diff --git a/Makefile b/Makefile index 09977ccd..e2981789 100644 --- a/Makefile +++ b/Makefile @@ -358,6 +358,8 @@ smoketest: @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=pico examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=feather-rp2040 examples/blinky1 + @$(MD5SUM) test.hex # test pwm $(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm @$(MD5SUM) test.hex diff --git a/README.md b/README.md index 21941e96..ae3a8b52 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ See the [getting started instructions](https://tinygo.org/getting-started/) for You can compile TinyGo programs for microcontrollers, WebAssembly and Linux. -The following 62 microcontroller boards are currently supported: +The following 63 microcontroller boards are currently supported: * [Adafruit Circuit Playground Bluefruit](https://www.adafruit.com/product/4333) * [Adafruit Circuit Playground Express](https://www.adafruit.com/product/3333) @@ -52,6 +52,7 @@ The following 62 microcontroller boards are currently supported: * [Adafruit Feather M4](https://www.adafruit.com/product/3857) * [Adafruit Feather M4 CAN](https://www.adafruit.com/product/4759) * [Adafruit Feather nRF52840 Express](https://www.adafruit.com/product/4062) +* [Adafruit Feather RP2040](https://www.adafruit.com/product/4884) * [Adafruit Feather STM32F405 Express](https://www.adafruit.com/product/4382) * [Adafruit Grand Central M4](https://www.adafruit.com/product/4064) * [Adafruit ItsyBitsy M0](https://www.adafruit.com/product/3727) diff --git a/src/machine/board_feather_rp2040.go b/src/machine/board_feather_rp2040.go new file mode 100644 index 00000000..7894c5cf --- /dev/null +++ b/src/machine/board_feather_rp2040.go @@ -0,0 +1,10 @@ +// +build feather_rp2040 + +package machine + +const ( + LED = GPIO13 + + // Onboard crystal oscillator frequency, in MHz. + xoscFreq = 12 // MHz +) diff --git a/targets/feather-rp2040-boot-stage2.S b/targets/feather-rp2040-boot-stage2.S new file mode 100644 index 00000000..e6e694a4 --- /dev/null +++ b/targets/feather-rp2040-boot-stage2.S @@ -0,0 +1,17 @@ +// Adafruit Feather RP2040 Stage 2 Bootloader + +// +// This file defines the parameters specific to the flash-chip found +// on the Adafruit Feather RP2040. The generic implementation is in +// rp2040-boot-stage2.S +// + +#define BOARD_PICO_FLASH_SPI_CLKDIV 2 +#define BOARD_CMD_READ 0xe7 +#define BOARD_QUAD_OK 1 +#define BOARD_QUAD_ENABLE_STATUS_BYTE 2 +#define BOARD_QUAD_ENABLE_BIT_MASK 2 +#define BOARD_SPLIT_STATUS_WRITE 1 +#define BOARD_WAIT_CYCLES 2 + +#include "rp2040-boot-stage2.S" \ No newline at end of file diff --git a/targets/feather-rp2040.json b/targets/feather-rp2040.json new file mode 100644 index 00000000..45f6b60b --- /dev/null +++ b/targets/feather-rp2040.json @@ -0,0 +1,10 @@ +{ + "inherits": [ + "rp2040" + ], + "build-tags": ["feather_rp2040"], + "linkerscript": "targets/feather-rp2040.ld", + "extra-files": [ + "targets/feather-rp2040-boot-stage2.S" + ] +} diff --git a/targets/feather-rp2040.ld b/targets/feather-rp2040.ld new file mode 100644 index 00000000..d97942f5 --- /dev/null +++ b/targets/feather-rp2040.ld @@ -0,0 +1,10 @@ + +MEMORY +{ + /* Reserve exactly 256 bytes at start of flash for second stage bootloader */ + BOOT2_TEXT (rx) : ORIGIN = 0x10000000, LENGTH = 256 + FLASH_TEXT (rx) : ORIGIN = 0x10000000 + 256, LENGTH = 8192K - 256 + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256k +} + +INCLUDE "targets/rp2040.ld" \ No newline at end of file diff --git a/targets/pico-boot-stage2.S b/targets/pico-boot-stage2.S new file mode 100644 index 00000000..88dd5517 --- /dev/null +++ b/targets/pico-boot-stage2.S @@ -0,0 +1,17 @@ +// Raspberry Pi Pico Stage 2 Bootloader + +// +// This file defines the parameters specific to the flash-chip found +// on the official Pico boards. The generic implementation is in +// rp2040-boot-stage2.S +// + +#define BOARD_PICO_FLASH_SPI_CLKDIV 2 +#define BOARD_CMD_READ 0xeb +#define BOARD_QUAD_OK 1 +#define BOARD_QUAD_ENABLE_STATUS_BYTE 2 +#define BOARD_QUAD_ENABLE_BIT_MASK 2 +#define BOARD_SPLIT_STATUS_WRITE 0 +#define BOARD_WAIT_CYCLES 4 + +#include "rp2040-boot-stage2.S" \ No newline at end of file diff --git a/targets/pico.json b/targets/pico.json index e8e5a06b..011d5110 100644 --- a/targets/pico.json +++ b/targets/pico.json @@ -5,6 +5,6 @@ "build-tags": ["pico"], "linkerscript": "targets/pico.ld", "extra-files": [ - "targets/pico_boot_stage2.S" + "targets/pico-boot-stage2.S" ] } diff --git a/targets/pico_boot_stage2.S b/targets/rp2040-boot-stage2.S similarity index 82% rename from targets/pico_boot_stage2.S rename to targets/rp2040-boot-stage2.S index 274845b1..8a16b644 100644 --- a/targets/pico_boot_stage2.S +++ b/targets/rp2040-boot-stage2.S @@ -1,11 +1,21 @@ // -// Implementation of Pico stage 2 boot loader. This code is for the Winbond W25Q080 -// (as found in the Pico) from the official Pico SDK. +// Implementation of RP2040 stage 2 boot loader. This code is derived from the +// Winbond W25Q080 implementation (as found in the Pico) in the official Pico SDK. // // This implementation has been made 'stand-alone' by including necessary code / // symbols from the included files in the reference implementation directly into -// the source. Care has been taken to preserve ordering and it has been verified -// the generated binary is byte-for-byte identical to the reference code binary. +// the source. It has also been modified to include the conditional logic from +// the CircuitPython implementation that supports additional flash chips. The +// CiruitPython source is here: +// https://github.com/adafruit/circuitpython/blob/main/ports/raspberrypi/stage2.c.jinja +// +// This file cannot be assembled directly, instead assemble the board-specific file +// (such as pico-boot-stage2.S) which defines the parameters specific to the flash +// chip included on that board. +// +// Care has been taken to preserve ordering and it has been verified the generated +// binary is byte-for-byte identical to the reference code binary when assembled for +// the Pico. // // Note: the stage 2 boot loader must be 256 bytes in length and have a checksum // present. In TinyGo, the linker script is responsible for allocating 256 bytes @@ -19,10 +29,6 @@ // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/boot_stage2/boot2_w25q080.S // -// Board Parameters -#define PICO_FLASH_SPI_CLKDIV 2 - - // ---------------------------------------------------------------------------- // Second stage boot code @@ -59,7 +65,8 @@ #define CMD_WRITE_ENABLE 0x06 #define CMD_READ_STATUS 0x05 #define CMD_READ_STATUS2 0x35 -#define CMD_WRITE_STATUS 0x01 +#define CMD_WRITE_STATUS1 0x01 +#define CMD_WRITE_STATUS2 0x31 #define SREG_DATA 0x02 // Enable quad-SPI mode #define XIP_BASE 0x10000000 @@ -123,18 +130,32 @@ // 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 +#define PICO_FLASH_SPI_CLKDIV BOARD_PICO_FLASH_SPI_CLKDIV #if PICO_FLASH_SPI_CLKDIV & 1 #error PICO_FLASH_SPI_CLKDIV must be even #endif +#if BOARD_QUAD_OK==1 // Define interface width: single/dual/quad IO -#define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_QUAD +#define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_QUAD +#define TRANSACTION_TYPE SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_2C2A +// 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 +#define INSTRUCTION_LENGTH SSI_SPI_CTRLR0_INST_L_VALUE_NONE +#define READ_INSTRUCTION MODE_CONTINUOUS_READ +#define ADDR_L 8 // 6 for address, 2 for mode +#else +#define FRAME_FORMAT SSI_CTRLR0_SPI_FRF_VALUE_STD +#define TRANSACTION_TYPE SSI_SPI_CTRLR0_TRANS_TYPE_VALUE_1C1A +#define INSTRUCTION_LENGTH SSI_SPI_CTRLR0_INST_L_VALUE_8B +#define READ_INSTRUCTION BOARD_CMD_READ +#define ADDR_L 6 // * 4 = 24 +#endif -// For W25Q080 this is the "Read data fast quad IO" instruction: -#define CMD_READ 0xeb +// The flash-chip specific read isntruction +#define CMD_READ BOARD_CMD_READ // "Mode bits" are 8 special bits sent immediately after // the address bits in a "Read Data Fast Quad I/O" command sequence. @@ -142,13 +163,10 @@ // next read does not require the 0xeb instruction prefix. #define MODE_CONTINUOUS_READ 0xa0 -// 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 +#define WAIT_CYCLES BOARD_WAIT_CYCLES + // If defined, we will read status reg, compare to SREG_DATA, and overwrite // with our value if the SR doesn't match. @@ -184,10 +202,14 @@ _stage2_boot: ldr r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET] movs r1, #PADS_QSPI_GPIO_QSPI_SD0_SCHMITT_BITS bics r0, r1 +#if BOARD_QUAD_OK==1 str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD0_OFFSET] +#endif str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD1_OFFSET] +#if BOARD_QUAD_OK==1 str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD2_OFFSET] str r0, [r3, #PADS_QSPI_GPIO_QSPI_SD3_OFFSET] +#endif ldr r3, =XIP_SSI_BASE @@ -225,9 +247,15 @@ program_sregs: str r1, [r3, #SSI_SSIENR_OFFSET] // Check whether SR needs updating +#if BOARD_QUAD_OK==1 +# if BOARD_QUAD_ENABLE_STATUS_BYTE==1 + movs r0, #CMD_READ_STATUS1 +# elif BOARD_QUAD_ENABLE_STATUS_BYTE==2 movs r0, #CMD_READ_STATUS2 +# endif + bl read_flash_sreg - movs r2, #SREG_DATA + movs r2, #BOARD_QUAD_ENABLE_BIT_MASK cmp r0, r2 beq skip_sreg_programming @@ -240,17 +268,37 @@ program_sregs: ldr r1, [r3, #SSI_DR0_OFFSET] // Send status write command followed by data bytes - movs r1, #CMD_WRITE_STATUS +# if BOARD_SPLIT_STATUS_WRITE==1 +# if BOARD_QUAD_ENABLE_STATUS_BYTE==1 + movs r1, #CMD_WRITE_STATUS1 +# elif BOARD_QUAD_ENABLE_STATUS_BYTE==2 + movs r1, #CMD_WRITE_STATUS2 +# endif 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] + +# else + movs r1, #CMD_WRITE_STATUS1 + str r1, [r3, #SSI_DR0_OFFSET] +# if BOARD_QUAD_ENABLE_STATUS_BYTE==2 movs r0, #0 str r0, [r3, #SSI_DR0_OFFSET] +# endif str r2, [r3, #SSI_DR0_OFFSET] bl wait_ssi_ready ldr r1, [r3, #SSI_DR0_OFFSET] ldr r1, [r3, #SSI_DR0_OFFSET] +# if BOARD_QUAD_ENABLE_STATUS_BYTE==2 ldr r1, [r3, #SSI_DR0_OFFSET] +# endif +# endif // Poll status register for write completion 1: movs r0, #CMD_READ_STATUS @@ -258,6 +306,7 @@ program_sregs: movs r1, #1 tst r0, r1 bne 1b +#endif skip_sreg_programming: @@ -286,6 +335,7 @@ dummy_read: movs r1, #0x0 // NDF=0 (single 32b read) str r1, [r3, #SSI_CTRLR1_OFFSET] +#if BOARD_QUAD_OK==1 #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 */ \ @@ -315,6 +365,7 @@ dummy_read: movs r1, #0 str r1, [r3, #SSI_SSIENR_OFFSET] // Disable SSI (and clear FIFO) to allow further config +#endif // Note that the INST_L field is used to select what XIP data gets pushed into // the TX FIFO: @@ -322,13 +373,13 @@ dummy_read: // 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 */ \ + (READ_INSTRUCTION /* 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 */ \ + (INSTRUCTION_LENGTH /* 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) */ \ + (TRANSACTION_TYPE /* 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)