Skip to content

Add support for Arduino Nano RP2040 Connect #425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/boards/include/boards/arduino_nano_rp2040_connect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

// -----------------------------------------------------
// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO
// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
// -----------------------------------------------------

#ifndef _BOARDS_ARDUINO_NANO_RP2040_CONNECT_H
#define _BOARDS_ARDUINO_NANO_RP2040_CONNECT_H

// For board detection
#define ARDUINO_NANO_RP2040_CONNECT

//------------- UART -------------//
#ifndef PICO_DEFAULT_UART
#define PICO_DEFAULT_UART 0
#endif

#ifndef PICO_DEFAULT_UART_TX_PIN
#define PICO_DEFAULT_UART_TX_PIN 0
#endif

#ifndef PICO_DEFAULT_UART_RX_PIN
#define PICO_DEFAULT_UART_RX_PIN 1
#endif

//------------- LED -------------//
#ifndef PICO_DEFAULT_LED_PIN
#define PICO_DEFAULT_LED_PIN 6
#endif
// no PICO_DEFAULT_WS2812_PIN

//------------- I2C -------------//
#ifndef PICO_DEFAULT_I2C
#define PICO_DEFAULT_I2C 0
#endif

#ifndef PICO_DEFAULT_I2C_SDA_PIN
#define PICO_DEFAULT_I2C_SDA_PIN 12
#endif

#ifndef PICO_DEFAULT_I2C_SCL_PIN
#define PICO_DEFAULT_I2C_SCL_PIN 13
#endif

//------------- SPI -------------//
#ifndef PICO_DEFAULT_SPI
#define PICO_DEFAULT_SPI 0
#endif

#ifndef PICO_DEFAULT_SPI_TX_PIN
#define PICO_DEFAULT_SPI_TX_PIN 7
#endif

#ifndef PICO_DEFAULT_SPI_RX_PIN
#define PICO_DEFAULT_SPI_RX_PIN 4
#endif

#ifndef PICO_DEFAULT_SPI_SCK_PIN
#define PICO_DEFAULT_SPI_SCK_PIN 6
#endif

//------------- FLASH -------------//

#define PICO_BOOT_STAGE2_CHOOSE_AT25SF128A 1

#ifndef PICO_FLASH_SPI_CLKDIV
#define PICO_FLASH_SPI_CLKDIV 2
#endif

#ifndef PICO_FLASH_SIZE_BYTES
#define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024)
#endif

// All boards have B1 RP2040
#ifndef PICO_FLOAT_SUPPORT_ROM_V1
#define PICO_FLOAT_SUPPORT_ROM_V1 0
#endif

#ifndef PICO_DOUBLE_SUPPORT_ROM_V1
#define PICO_DOUBLE_SUPPORT_ROM_V1 0
#endif

#endif
285 changes: 285 additions & 0 deletions src/rp2_common/boot_stage2/boot2_at25sf128a.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
// ----------------------------------------------------------------------------
// Second stage boot code
// Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd.
// SPDX-License-Identifier: BSD-3-Clause
//
// Device: Adesto AT25SF128A
// Based on W25Q080 code: main difference is the QE bit is being set
// via command 0x31
//
// Description: Configures AT25SF128A to run in Quad I/O continuous read XIP mode
//
// Details: * Check status register 2 to determine if QSPI mode is enabled,
// and perform an SR2 programming cycle if necessary.
// * Use SSI to perform a dummy 0xEB read command, with the mode
// continuation bits set, so that the flash will not require
// 0xEB instruction prefix on subsequent reads.
// * Configure SSI to write address, mode bits, but no instruction.
// SSI + flash are now jointly in a state where continuous reads
// can take place.
// * Jump to exit pointer passed in via lr. Bootrom passes null,
// in which case this code uses a default 256 byte flash offset
//
// Building: * This code must be position-independent, and use stack only
// * The code will be padded to a size of 256 bytes, including a
// 4-byte checksum. Therefore code size cannot exceed 252 bytes.
// ----------------------------------------------------------------------------

#include "pico/asm_helper.S"
#include "hardware/regs/addressmap.h"
#include "hardware/regs/ssi.h"
#include "hardware/regs/pads_qspi.h"

// ----------------------------------------------------------------------------
// Config section
// ----------------------------------------------------------------------------
// It should be possible to support most flash devices by modifying this section

// The serial flash interface will run at clk_sys/PICO_FLASH_SPI_CLKDIV.
// This must be a positive, even integer.
// The bootrom is very conservative with SPI frequency, but here we should be
// as aggressive as possible.

#ifndef PICO_FLASH_SPI_CLKDIV
#define PICO_FLASH_SPI_CLKDIV 4
#endif
#if PICO_FLASH_SPI_CLKDIV & 1
#error PICO_FLASH_SPI_CLKDIV must be even
#endif

// Define interface width: single/dual/quad IO
#define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_QUAD

// For W25Q080 this is the "Read data fast quad IO" instruction:
#define CMD_READ 0xeb

// "Mode bits" are 8 special bits sent immediately after
// the address bits in a "Read Data Fast Quad I/O" command sequence.
// On W25Q080, the four LSBs are don't care, and if MSBs == 0xa, the
// next read does not require the 0xeb instruction prefix.
#define MODE_CONTINUOUS_READ 0x20

// The number of address + mode bits, divided by 4 (always 4, not function of
// interface width).
#define ADDR_L 8

// How many clocks of Hi-Z following the mode bits. For W25Q080, 4 dummy cycles
// are required.
#define WAIT_CYCLES 4

// If defined, we will read status reg, compare to SREG_DATA, and overwrite
// with our value if the SR doesn't match.
// We do a two-byte write to SR1 (01h cmd) rather than a one-byte write to
// SR2 (31h cmd) as the latter command isn't supported by WX25Q080.
// This isn't great because it will remove block protections.
// A better solution is to use a volatile SR write if your device supports it.
#define PROGRAM_STATUS_REG

#define CMD_WRITE_ENABLE 0x06
#define CMD_READ_STATUS 0x05
#define CMD_READ_STATUS2 0x35
#define CMD_WRITE_STATUS 0x01
#define CMD_WRITE_STATUS2 0x31
#define SREG_DATA 0x02 // Enable quad-SPI mode

// ----------------------------------------------------------------------------
// Start of 2nd Stage Boot Code
// ----------------------------------------------------------------------------

.syntax unified
.cpu cortex-m0plus
.thumb

.section .text

// The exit point is passed in lr. If entered from bootrom, this will be the
// flash address immediately following this second stage (0x10000100).
// Otherwise it will be a return address -- second stage being called as a
// function by user code, after copying out of XIP region. r3 holds SSI base,
// r0...2 used as temporaries. Other GPRs not used.
.global _stage2_boot
.type _stage2_boot,%function
.thumb_func
_stage2_boot:
push {lr}

// Set pad configuration:
// - SCLK 8mA drive, no slew limiting
// - SDx disable input Schmitt to reduce delay

ldr r3, =PADS_QSPI_BASE
movs r0, #(2 << PADS_QSPI_GPIO_QSPI_SCLK_DRIVE_LSB | PADS_QSPI_GPIO_QSPI_SCLK_SLEWFAST_BITS)
str r0, [r3, #PADS_QSPI_GPIO_QSPI_SCLK_OFFSET]
ldr r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET]
movs r1, #PADS_QSPI_GPIO_QSPI_SD0_SCHMITT_BITS
bics r0, r1
str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET]
str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD1_OFFSET]
str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD2_OFFSET]
str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD3_OFFSET]

ldr r3, =XIP_SSI_BASE

// Disable SSI to allow further config
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET]

// Set baud rate
movs r1, #PICO_FLASH_SPI_CLKDIV
str r1, [r3, #SSI_BAUDR_OFFSET]

// Set 1-cycle sample delay. If PICO_FLASH_SPI_CLKDIV == 2 then this means,
// if the flash launches data on SCLK posedge, we capture it at the time that
// the next SCLK posedge is launched. This is shortly before that posedge
// arrives at the flash, so data hold time should be ok. For
// PICO_FLASH_SPI_CLKDIV > 2 this pretty much has no effect.

movs r1, #1
movs r2, #SSI_RX_SAMPLE_DLY_OFFSET // == 0xf0 so need 8 bits of offset significance
str r1, [r3, r2]


// On QSPI parts we usually need a 01h SR-write command to enable QSPI mode
// (i.e. turn WPn and HOLDn into IO2/IO3)
#ifdef PROGRAM_STATUS_REG
program_sregs:
#define CTRL0_SPI_TXRX \
(7 << SSI_CTRLR0_DFS_32_LSB) | /* 8 bits per data frame */ \
(SSI_CTRLR0_TMOD_VALUE_TX_AND_RX << SSI_CTRLR0_TMOD_LSB)

ldr r1, =(CTRL0_SPI_TXRX)
str r1, [r3, #SSI_CTRLR0_OFFSET]

// Enable SSI and select slave 0
movs r1, #1
str r1, [r3, #SSI_SSIENR_OFFSET]

// Check whether SR needs updating
movs r0, #CMD_READ_STATUS2
bl read_flash_sreg
movs r2, #SREG_DATA
cmp r0, r2
beq skip_sreg_programming

// Send write enable command
movs r1, #CMD_WRITE_ENABLE
str r1, [r3, #SSI_DR0_OFFSET]

// Poll for completion and discard RX
bl wait_ssi_ready
ldr r1, [r3, #SSI_DR0_OFFSET]

// Send status write command followed by data bytes
movs r1, #CMD_WRITE_STATUS2
str r1, [r3, #SSI_DR0_OFFSET]
str r2, [r3, #SSI_DR0_OFFSET]

bl wait_ssi_ready
ldr r1, [r3, #SSI_DR0_OFFSET]
ldr r1, [r3, #SSI_DR0_OFFSET]
ldr r1, [r3, #SSI_DR0_OFFSET]

// Poll status register for write completion
1:
movs r0, #CMD_READ_STATUS
bl read_flash_sreg
movs r1, #1
tst r0, r1
bne 1b

skip_sreg_programming:

// Disable SSI again so that it can be reconfigured
movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET]
#endif

// Currently the flash expects an 8 bit serial command prefix on every
// transfer, which is a waste of cycles. Perform a dummy Fast Read Quad I/O
// command, with mode bits set such that the flash will not expect a serial
// command prefix on *subsequent* transfers. We don't care about the results
// of the read, the important part is the mode bits.

dummy_read:
#define CTRLR0_ENTER_XIP \
(FRAME_FORMAT /* Quad I/O mode */ \
<< SSI_CTRLR0_SPI_FRF_LSB) | \
(31 << SSI_CTRLR0_DFS_32_LSB) | /* 32 data bits */ \
(SSI_CTRLR0_TMOD_VALUE_EEPROM_READ /* Send INST/ADDR, Receive Data */ \
<< SSI_CTRLR0_TMOD_LSB)

ldr r1, =(CTRLR0_ENTER_XIP)
str r1, [r3, #SSI_CTRLR0_OFFSET]

movs r1, #0x0 // NDF=0 (single 32b read)
str r1, [r3, #SSI_CTRLR1_OFFSET]

#define SPI_CTRLR0_ENTER_XIP \
(ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Address + mode bits */ \
(WAIT_CYCLES << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z dummy clocks following address + mode */ \
(SSI_SPI_CTRLR0_INST_L_VALUE_8B \
<< SSI_SPI_CTRLR0_INST_L_LSB) | /* 8-bit instruction */ \
(SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C2A /* Send Command in serial mode then address in Quad I/O mode */ \
<< SSI_SPI_CTRLR0_TRANS_TYPE_LSB)

ldr r1, =(SPI_CTRLR0_ENTER_XIP)
ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET) // SPI_CTRL0 Register
str r1, [r0]

movs r1, #1 // Re-enable SSI
str r1, [r3, #SSI_SSIENR_OFFSET]

movs r1, #CMD_READ
str r1, [r3, #SSI_DR0_OFFSET] // Push SPI command into TX FIFO
movs r1, #MODE_CONTINUOUS_READ // 32-bit: 24 address bits (we don't care, so 0) and M[7:4]=1010
str r1, [r3, #SSI_DR0_OFFSET] // Push Address into TX FIFO - this will trigger the transaction

// Poll for completion
bl wait_ssi_ready

// The flash is in a state where we can blast addresses in parallel, and get
// parallel data back. Now configure the SSI to translate XIP bus accesses
// into QSPI transfers of this form.

movs r1, #0
str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI (and clear FIFO) to allow further config

// Note that the INST_L field is used to select what XIP data gets pushed into
// the TX FIFO:
// INST_L_0_BITS {ADDR[23:0],XIP_CMD[7:0]} Load "mode bits" into XIP_CMD
// Anything else {XIP_CMD[7:0],ADDR[23:0]} Load SPI command into XIP_CMD
configure_ssi:
#define SPI_CTRLR0_XIP \
(MODE_CONTINUOUS_READ /* Mode bits to keep flash in continuous read mode */ \
<< SSI_SPI_CTRLR0_XIP_CMD_LSB) | \
(ADDR_L << SSI_SPI_CTRLR0_ADDR_L_LSB) | /* Total number of address + mode bits */ \
(WAIT_CYCLES << SSI_SPI_CTRLR0_WAIT_CYCLES_LSB) | /* Hi-Z dummy clocks following address + mode */ \
(SSI_SPI_CTRLR0_INST_L_VALUE_NONE /* Do not send a command, instead send XIP_CMD as mode bits after address */ \
<< SSI_SPI_CTRLR0_INST_L_LSB) | \
(SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_2C2A /* Send Address in Quad I/O mode (and Command but that is zero bits long) */ \
<< SSI_SPI_CTRLR0_TRANS_TYPE_LSB)

ldr r1, =(SPI_CTRLR0_XIP)
ldr r0, =(XIP_SSI_BASE + SSI_SPI_CTRLR0_OFFSET)
str r1, [r0]

movs r1, #1
str r1, [r3, #SSI_SSIENR_OFFSET] // Re-enable SSI

// Bus accesses to the XIP window will now be transparently serviced by the
// external flash on cache miss. We are ready to run code from flash.

// Pull in standard exit routine
#include "boot2_helpers/exit_from_boot2.S"

// Common functions
#include "boot2_helpers/wait_ssi_ready.S"
#ifdef PROGRAM_STATUS_REG
#include "boot2_helpers/read_flash_sreg.S"
#endif

.global literals
literals:
.ltorg

.end
Loading