-
Notifications
You must be signed in to change notification settings - Fork 918
Add simple UART bootloader example #571
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
Open
will-v-pi
wants to merge
8
commits into
raspberrypi:develop
Choose a base branch
from
will-v-pi:uart-boot
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
bb03e7b
Add simple UART bootloader
will-v-pi 9ad8d9f
Hardcode baud rate, as it's fixed by the bootrom
will-v-pi 7c16777
Tidy up variable names, add assets on correct chars, and improve docu…
will-v-pi 170cdb3
Add verify, support Risc-V, and replace asserts on returned chars wit…
will-v-pi 5f9e265
Add note as to why bootloader should be absolute
will-v-pi 94167d6
Allow more startup time for booting device
will-v-pi 7cdeb52
Allow longer delays while running too
will-v-pi e817bae
review fixups
will-v-pi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
add_executable(uart_boot | ||
uart_boot.c | ||
) | ||
|
||
# pull in common dependencies | ||
target_link_libraries(uart_boot pico_stdlib hardware_flash) | ||
|
||
# add partition table | ||
pico_embed_pt_in_binary(uart_boot ${CMAKE_CURRENT_LIST_DIR}/uart-pt.json) | ||
|
||
# create absolute UF2, as it's a bootloader so shouldn't go in a partition | ||
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) | ||
|
||
|
||
# Create separate binary to be loaded onto other device | ||
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) | ||
|
||
# package uf2 in flash | ||
pico_package_uf2_output(uart_binary 0x10000000) | ||
lurch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# 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) | ||
lurch marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"version": [1, 0], | ||
"unpartitioned": { | ||
"families": ["absolute"], | ||
"permissions": { | ||
"secure": "rw", | ||
"nonsecure": "rw", | ||
"bootloader": "rw" | ||
} | ||
}, | ||
"partitions": [ | ||
{ | ||
"start": "128K", | ||
"size": "32K", | ||
kilograham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"families": ["rp2350-arm-s", "rp2350-riscv"], | ||
"permissions": { | ||
"secure": "rw", | ||
"nonsecure": "rw", | ||
"bootloader": "rw" | ||
} | ||
} | ||
] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
will-v-pi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#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); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#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 | ||
|
||
// 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; | ||
char in = 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)) { | ||
in = uart_getc(UART_ID); | ||
if (in != 'n') { | ||
printf("Incorrect response - resetting\n"); | ||
reset_chip(); | ||
return; | ||
} | ||
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)); | ||
kilograham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION)); | ||
assert(ret == 3); | ||
kilograham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
uint32_t location_and_permissions = buffer[1]; | ||
uint32_t start_addr = XIP_BASE + ((location_and_permissions & PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) * FLASH_SECTOR_SIZE; | ||
uint32_t end_addr = XIP_BASE + (((location_and_permissions & PICOBIN_PARTITION_LOCATION_LAST_SECTOR_BITS) >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) + 1) * FLASH_SECTOR_SIZE; | ||
printf("Start %08x, end %08x\n", start_addr, end_addr); | ||
|
||
free(buffer); | ||
|
||
printf("Writing binary\n"); | ||
uint32_t time_start = time_us_32(); | ||
uint32_t current_addr = start_addr; | ||
while (current_addr < end_addr) { | ||
uart_putc_raw(UART_ID, 'w'); | ||
char *buf = (char*)current_addr; | ||
int i; | ||
for (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; | ||
} | ||
in = uart_getc(UART_ID); | ||
if (in != 'w') { | ||
printf("Incorrect response - resetting\n"); | ||
reset_chip(); | ||
return; | ||
} | ||
current_addr += i; | ||
} | ||
|
||
uint32_t time_end = time_us_32(); | ||
printf("Write took %dus\n", time_end - time_start); | ||
printf("Write complete - resetting pointer\n"); | ||
|
||
uart_putc_raw(UART_ID, 'c'); | ||
if (!uart_is_readable_within_us(UART_ID, 500)) { | ||
// Detect hangs and reset the chip | ||
printf("Clear has hung - resetting\n"); | ||
reset_chip(); | ||
return; | ||
} | ||
in = uart_getc(UART_ID); | ||
printf("%c\n", in); | ||
if (in != 'c') { | ||
printf("Incorrect response - resetting\n"); | ||
reset_chip(); | ||
return; | ||
} | ||
|
||
printf("Verifying binary\n"); | ||
time_start = time_us_32(); | ||
current_addr = start_addr; | ||
while (current_addr < end_addr) { | ||
uart_putc_raw(UART_ID, 'r'); | ||
char *buf = (char*)current_addr; | ||
if (!uart_is_readable_within_us(UART_ID, 500)) { | ||
// Detect hangs and reset the chip | ||
printf("Verify has hung - resetting\n"); | ||
reset_chip(); | ||
return; | ||
} | ||
int i = 0; | ||
while (uart_is_readable_within_us(UART_ID, 10) && i < 32) { | ||
in = uart_getc(UART_ID); | ||
if (in != buf[i]) { | ||
printf("Verify has incorrect data at 0x%08x - resetting\n", current_addr - start_addr + SRAM_BASE); | ||
reset_chip(); | ||
return; | ||
} | ||
i++; | ||
} | ||
if (i != 32) { | ||
printf("Verify has incorrect data size - resetting\n"); | ||
reset_chip(); | ||
return; | ||
} | ||
in = uart_getc(UART_ID); | ||
if (in != 'r') { | ||
printf("Incorrect response - resetting\n"); | ||
reset_chip(); | ||
return; | ||
} | ||
current_addr += i; | ||
} | ||
|
||
time_end = time_us_32(); | ||
printf("Verify took %dus\n", time_end - time_start); | ||
printf("Verify 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; | ||
} | ||
in = uart_getc(UART_ID); | ||
printf("%c\n", in); | ||
if (in != 'x') { | ||
printf("Incorrect response - resetting\n"); | ||
reset_chip(); | ||
return; | ||
} | ||
} | ||
|
||
|
||
int main() | ||
{ | ||
stdio_init_all(); | ||
|
||
// Set up our UART for booting the other device | ||
uart_init(UART_ID, 1000000); | ||
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(); | ||
|
||
int attempts = 0; | ||
char splash[] = "RP2350"; | ||
char hello[] = "Hello, world"; | ||
|
||
while (true) { | ||
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, splash[0], sizeof(buf)); | ||
if (ptr && strncmp(ptr, splash, sizeof(splash) - 1) == 0) { | ||
printf("Splash found\n"); | ||
uart_boot(); | ||
attempts = 0; | ||
} else { | ||
ptr = memchr(buf, hello[0], sizeof(buf)); | ||
will-v-pi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (ptr && strncmp(ptr, hello, sizeof(hello) - 1) == 0) { | ||
printf("Device is running\n"); | ||
attempts = 0; | ||
} else { | ||
if (attempts > 3) { | ||
printf("Device not running - attempting reset\n"); | ||
reset_chip(); | ||
attempts = 0; | ||
} else { | ||
printf("Device not running - waiting\n"); | ||
attempts++; | ||
} | ||
} | ||
} | ||
sleep_ms(1000); | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.