diff --git a/README.md b/README.md index d6809a1fc..eeb3d85e1 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ App|Description App|Description ---|--- [enc_bootloader](bootloaders/encrypted) | A bootloader which decrypts binaries from flash into SRAM. See the separate [README](bootloaders/encrypted/README.md) for more information +[uart_boot](bootloaders/uart) | A bootloader which boots a separate RP2350 using the UART boot interface. See section 5.8 in the datasheet for more details, including the wiring requirements ### Clocks diff --git a/bootloaders/CMakeLists.txt b/bootloaders/CMakeLists.txt index c4e886b5e..b8148217c 100644 --- a/bootloaders/CMakeLists.txt +++ b/bootloaders/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory_exclude_platforms(uart host rp2040) + if (TARGET pico_mbedtls) add_subdirectory_exclude_platforms(encrypted host rp2040 rp2350-riscv) else() diff --git a/bootloaders/uart/CMakeLists.txt b/bootloaders/uart/CMakeLists.txt new file mode 100644 index 000000000..cc0e38caa --- /dev/null +++ b/bootloaders/uart/CMakeLists.txt @@ -0,0 +1,32 @@ +add_executable(uart_boot + uart_boot.c + ) + +# pull in common dependencies +target_link_libraries(uart_boot pico_stdlib hardware_flash) + +pico_embed_pt_in_binary(uart_boot ${CMAKE_CURRENT_LIST_DIR}/uart-pt.json) +pico_set_uf2_family(uart_boot "absolute") + +# create map/bin/hex file etc. +pico_add_extra_outputs(uart_boot) + +# add url via pico_set_program_url +example_auto_set_url(uart_boot) + + +add_executable(uart_binary + uart_binary.c + ) + +# pull in common dependencies +target_link_libraries(uart_binary pico_stdlib) + +pico_set_binary_type(uart_binary no_flash) +pico_package_uf2_output(uart_binary 0x10000000) + +# create map/bin/hex/uf2 file etc. +pico_add_extra_outputs(uart_binary) + +# call pico_set_program_url to set path to example on github, so users can find the source for an example via picotool +example_auto_set_url(uart_binary) diff --git a/bootloaders/uart/uart-pt.json b/bootloaders/uart/uart-pt.json new file mode 100644 index 000000000..d6ce01299 --- /dev/null +++ b/bootloaders/uart/uart-pt.json @@ -0,0 +1,23 @@ +{ + "version": [1, 0], + "unpartitioned": { + "families": ["absolute"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + }, + "partitions": [ + { + "start": "128K", + "size": "32K", + "families": ["rp2350-arm-s"], + "permissions": { + "secure": "rw", + "nonsecure": "rw", + "bootloader": "rw" + } + } + ] +} \ No newline at end of file diff --git a/bootloaders/uart/uart_binary.c b/bootloaders/uart/uart_binary.c new file mode 100644 index 000000000..e3f2f535b --- /dev/null +++ b/bootloaders/uart/uart_binary.c @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "pico/stdlib.h" +#include "hardware/uart.h" +#include "hardware/structs/pads_qspi.h" +#include "hardware/structs/io_qspi.h" + +#ifndef LED_DELAY_MS +#define LED_DELAY_MS 500 +#endif + +// Initialize the GPIO for the LED +void pico_led_init(void) { +#ifdef PICO_DEFAULT_LED_PIN + // A device like Pico that uses a GPIO for the LED will define PICO_DEFAULT_LED_PIN + // so we can use normal GPIO functionality to turn the led on and off + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); +#endif +} + +// Turn the LED on or off +void pico_set_led(bool led_on) { +#if defined(PICO_DEFAULT_LED_PIN) + // Just set the GPIO on or off + gpio_put(PICO_DEFAULT_LED_PIN, led_on); +#endif +} + +// Set function for QSPI GPIO pin +void qspi_gpio_set_function(uint gpio, gpio_function_t fn) { + // Set input enable on, output disable off + hw_write_masked(&pads_qspi_hw->io[gpio], + PADS_QSPI_GPIO_QSPI_SD2_IE_BITS, + PADS_QSPI_GPIO_QSPI_SD2_IE_BITS | PADS_QSPI_GPIO_QSPI_SD2_OD_BITS + ); + // Zero all fields apart from fsel; we want this IO to do what the peripheral tells it. + // This doesn't affect e.g. pullup/pulldown, as these are in pad controls. + io_qspi_hw->io[gpio].ctrl = fn << IO_QSPI_GPIO_QSPI_SD2_CTRL_FUNCSEL_LSB; + + // Remove pad isolation now that the correct peripheral is in control of the pad + hw_clear_bits(&pads_qspi_hw->io[gpio], PADS_QSPI_GPIO_QSPI_SD2_ISO_BITS); +} + +int main() { + pico_led_init(); + + // SD2 is QSPI GPIO 3, SD3 is QSPI GPIO 4 + qspi_gpio_set_function(3, GPIO_FUNC_UART_AUX); + qspi_gpio_set_function(4, GPIO_FUNC_UART_AUX); + + uart_init(uart0, 1000000); + + while (true) { + uart_puts(uart0, "Hello, world\n"); + pico_set_led(true); + sleep_ms(LED_DELAY_MS); + pico_set_led(false); + sleep_ms(LED_DELAY_MS); + } +} diff --git a/bootloaders/uart/uart_boot.c b/bootloaders/uart/uart_boot.c new file mode 100644 index 000000000..caeed6113 --- /dev/null +++ b/bootloaders/uart/uart_boot.c @@ -0,0 +1,152 @@ +#include +#include +#include "pico/stdlib.h" +#include "hardware/uart.h" +#include "pico/bootrom.h" +#include "boot/picobin.h" +#include "hardware/flash.h" + +// UART defines for uart boot +#define UART_ID uart1 +#define BAUD_RATE 1000000 + +// Use pins 4 and 5 for uart boot +#define UART_TX_PIN 4 +#define UART_RX_PIN 5 + +// Use pin 3 for the RUN pin on the other chip +#define RUN_PIN 3 + + +void reset_chip() { + // Toggle run pin + gpio_put(RUN_PIN, false); + sleep_ms(1); + gpio_put(RUN_PIN, true); +} + + +void uart_boot() { + uint knocks = 0; + while (true) { + // Send the knock sequence + uart_putc_raw(UART_ID, 0x56); + uart_putc_raw(UART_ID, 0xff); + uart_putc_raw(UART_ID, 0x8b); + uart_putc_raw(UART_ID, 0xe4); + uart_putc_raw(UART_ID, 'n'); + + if (uart_is_readable_within_us(UART_ID, 1000)) { + char in = uart_getc(UART_ID); + printf("%c\n", in); + break; + } else { + if (knocks > 10) { + printf("No response - resetting\n"); + reset_chip(); + return; + } + printf("No response - knocking again\n"); + knocks++; + } + } + + printf("Boot starting\n"); + + // Get partition location in flash + const int buf_words = (16 * 4) + 1; // maximum of 16 partitions, each with maximum of 4 words returned, plus 1 + uint32_t* buffer = malloc(buf_words * 4); + + int ret = rom_get_partition_table_info(buffer, buf_words, PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (0 << 24)); + assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION)); + assert(ret == 3); + + uint32_t location_and_permissions = buffer[1]; + uint32_t saddr = XIP_BASE + ((location_and_permissions >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) & 0x1fffu) * FLASH_SECTOR_SIZE; + uint32_t eaddr = XIP_BASE + (((location_and_permissions >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & 0x1fffu) + 1) * FLASH_SECTOR_SIZE; + printf("Start %08x, end %08x\n", saddr, eaddr); + + free(buffer); + + printf("Writing binary\n"); + uint32_t tstart = time_us_32(); + uint32_t caddr = saddr; + while (caddr < eaddr) { + uart_putc_raw(UART_ID, 'w'); + char *buf = (char*)caddr; + for (int i=0; i < 32; i++) { + uart_putc_raw(UART_ID, buf[i]); + } + if (!uart_is_readable_within_us(UART_ID, 500)) { + // Detect hangs and reset the chip + printf("Write has hung - resetting\n"); + reset_chip(); + return; + } + char in = uart_getc(UART_ID); + printf("%c\n", in); + caddr += 32; + } + + uint32_t tend = time_us_32(); + printf("Write took %dus\n", tend - tstart); + printf("Write complete - executing\n"); + uart_putc_raw(UART_ID, 'x'); + if (!uart_is_readable_within_us(UART_ID, 500)) { + // Detect hangs and reset the chip + printf("Execute has hung - resetting\n"); + reset_chip(); + return; + } + char in = uart_getc(UART_ID); + printf("%c\n", in); +} + + +int main() +{ + stdio_init_all(); + + // Set up our UART for booting the other device + uart_init(UART_ID, BAUD_RATE); + gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); + gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); + + // Set up run pin + gpio_init(RUN_PIN); + gpio_set_dir(RUN_PIN, GPIO_OUT); + + // Reset chip + reset_chip(); + + + while (true) { + char splash[] = "RP2350"; + char hello[] = "Hello"; + char buf[500] = {0}; + int i = 0; + while (uart_is_readable(UART_ID) && i < sizeof(buf)) { + char in = uart_getc(UART_ID); + printf("%c", in); + buf[i] = in; + i++; + } + if (i > 0) { + printf(" ...Read done\n"); + } + char *ptr = memchr(buf, 'R', sizeof(buf)); + if (ptr && strncmp(ptr, splash, sizeof(splash) - 1) == 0) { + printf("Splash found\n"); + uart_boot(); + } else { + ptr = memchr(buf, 'H', sizeof(buf)); + if (ptr && strncmp(ptr, hello, sizeof(hello) - 1) == 0) { + printf("Device is running\n"); + } else { + printf("Device not running - attempting reset\n"); + reset_chip(); + } + } + sleep_ms(1000); + } +}