Skip to content

Commit 130593e

Browse files
committed
Add simple UART bootloader
The sample binary flashes the LED and prints Hello, world back over the UART interface
1 parent da4e50d commit 130593e

File tree

6 files changed

+275
-0
lines changed

6 files changed

+275
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ App|Description
4444
App|Description
4545
---|---
4646
[enc_bootloader](bootloaders/encrypted) | A bootloader which decrypts binaries from flash into SRAM. See the separate [README](bootloaders/encrypted/README.md) for more information
47+
[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
4748

4849
### Clocks
4950

bootloaders/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
add_subdirectory_exclude_platforms(uart host rp2040)
2+
13
if (TARGET pico_mbedtls)
24
add_subdirectory_exclude_platforms(encrypted host rp2040 rp2350-riscv)
35
else()

bootloaders/uart/CMakeLists.txt

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
add_executable(uart_boot
2+
uart_boot.c
3+
)
4+
5+
# pull in common dependencies
6+
target_link_libraries(uart_boot pico_stdlib hardware_flash)
7+
8+
pico_embed_pt_in_binary(uart_boot ${CMAKE_CURRENT_LIST_DIR}/uart-pt.json)
9+
pico_set_uf2_family(uart_boot "absolute")
10+
11+
# create map/bin/hex file etc.
12+
pico_add_extra_outputs(uart_boot)
13+
14+
# add url via pico_set_program_url
15+
example_auto_set_url(uart_boot)
16+
17+
18+
add_executable(uart_binary
19+
uart_binary.c
20+
)
21+
22+
# pull in common dependencies
23+
target_link_libraries(uart_binary pico_stdlib)
24+
25+
pico_set_binary_type(uart_binary no_flash)
26+
pico_package_uf2_output(uart_binary 0x10000000)
27+
28+
# create map/bin/hex/uf2 file etc.
29+
pico_add_extra_outputs(uart_binary)
30+
31+
# call pico_set_program_url to set path to example on github, so users can find the source for an example via picotool
32+
example_auto_set_url(uart_binary)

bootloaders/uart/uart-pt.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"version": [1, 0],
3+
"unpartitioned": {
4+
"families": ["absolute"],
5+
"permissions": {
6+
"secure": "rw",
7+
"nonsecure": "rw",
8+
"bootloader": "rw"
9+
}
10+
},
11+
"partitions": [
12+
{
13+
"start": "128K",
14+
"size": "32K",
15+
"families": ["rp2350-arm-s"],
16+
"permissions": {
17+
"secure": "rw",
18+
"nonsecure": "rw",
19+
"bootloader": "rw"
20+
}
21+
}
22+
]
23+
}

bootloaders/uart/uart_binary.c

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include "pico/stdlib.h"
8+
#include "hardware/uart.h"
9+
#include "hardware/structs/pads_qspi.h"
10+
#include "hardware/structs/io_qspi.h"
11+
12+
#ifndef LED_DELAY_MS
13+
#define LED_DELAY_MS 500
14+
#endif
15+
16+
// Initialize the GPIO for the LED
17+
void pico_led_init(void) {
18+
#ifdef PICO_DEFAULT_LED_PIN
19+
// A device like Pico that uses a GPIO for the LED will define PICO_DEFAULT_LED_PIN
20+
// so we can use normal GPIO functionality to turn the led on and off
21+
gpio_init(PICO_DEFAULT_LED_PIN);
22+
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
23+
#endif
24+
}
25+
26+
// Turn the LED on or off
27+
void pico_set_led(bool led_on) {
28+
#if defined(PICO_DEFAULT_LED_PIN)
29+
// Just set the GPIO on or off
30+
gpio_put(PICO_DEFAULT_LED_PIN, led_on);
31+
#endif
32+
}
33+
34+
// Set function for QSPI GPIO pin
35+
void qspi_gpio_set_function(uint gpio, gpio_function_t fn) {
36+
// Set input enable on, output disable off
37+
hw_write_masked(&pads_qspi_hw->io[gpio],
38+
PADS_QSPI_GPIO_QSPI_SD2_IE_BITS,
39+
PADS_QSPI_GPIO_QSPI_SD2_IE_BITS | PADS_QSPI_GPIO_QSPI_SD2_OD_BITS
40+
);
41+
// Zero all fields apart from fsel; we want this IO to do what the peripheral tells it.
42+
// This doesn't affect e.g. pullup/pulldown, as these are in pad controls.
43+
io_qspi_hw->io[gpio].ctrl = fn << IO_QSPI_GPIO_QSPI_SD2_CTRL_FUNCSEL_LSB;
44+
45+
// Remove pad isolation now that the correct peripheral is in control of the pad
46+
hw_clear_bits(&pads_qspi_hw->io[gpio], PADS_QSPI_GPIO_QSPI_SD2_ISO_BITS);
47+
}
48+
49+
int main() {
50+
pico_led_init();
51+
52+
// SD2 is QSPI GPIO 3, SD3 is QSPI GPIO 4
53+
qspi_gpio_set_function(3, GPIO_FUNC_UART_AUX);
54+
qspi_gpio_set_function(4, GPIO_FUNC_UART_AUX);
55+
56+
uart_init(uart0, 1000000);
57+
58+
while (true) {
59+
uart_puts(uart0, "Hello, world\n");
60+
pico_set_led(true);
61+
sleep_ms(LED_DELAY_MS);
62+
pico_set_led(false);
63+
sleep_ms(LED_DELAY_MS);
64+
}
65+
}

bootloaders/uart/uart_boot.c

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include "pico/stdlib.h"
4+
#include "hardware/uart.h"
5+
#include "pico/bootrom.h"
6+
#include "boot/picobin.h"
7+
#include "hardware/flash.h"
8+
9+
// UART defines for uart boot
10+
#define UART_ID uart1
11+
#define BAUD_RATE 1000000
12+
13+
// Use pins 4 and 5 for uart boot
14+
#define UART_TX_PIN 4
15+
#define UART_RX_PIN 5
16+
17+
// Use pin 3 for the RUN pin on the other chip
18+
#define RUN_PIN 3
19+
20+
21+
void reset_chip() {
22+
// Toggle run pin
23+
gpio_put(RUN_PIN, false);
24+
sleep_ms(1);
25+
gpio_put(RUN_PIN, true);
26+
}
27+
28+
29+
void uart_boot() {
30+
uint knocks = 0;
31+
while (true) {
32+
// Send the knock sequence
33+
uart_putc_raw(UART_ID, 0x56);
34+
uart_putc_raw(UART_ID, 0xff);
35+
uart_putc_raw(UART_ID, 0x8b);
36+
uart_putc_raw(UART_ID, 0xe4);
37+
uart_putc_raw(UART_ID, 'n');
38+
39+
if (uart_is_readable_within_us(UART_ID, 1000)) {
40+
char in = uart_getc(UART_ID);
41+
printf("%c\n", in);
42+
break;
43+
} else {
44+
if (knocks > 10) {
45+
printf("No response - resetting\n");
46+
reset_chip();
47+
return;
48+
}
49+
printf("No response - knocking again\n");
50+
knocks++;
51+
}
52+
}
53+
54+
printf("Boot starting\n");
55+
56+
// Get partition location in flash
57+
const int buf_words = (16 * 4) + 1; // maximum of 16 partitions, each with maximum of 4 words returned, plus 1
58+
uint32_t* buffer = malloc(buf_words * 4);
59+
60+
int ret = rom_get_partition_table_info(buffer, buf_words, PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION | (0 << 24));
61+
assert(buffer[0] == (PT_INFO_PARTITION_LOCATION_AND_FLAGS | PT_INFO_SINGLE_PARTITION));
62+
assert(ret == 3);
63+
64+
uint32_t location_and_permissions = buffer[1];
65+
uint32_t saddr = XIP_BASE + ((location_and_permissions >> PICOBIN_PARTITION_LOCATION_FIRST_SECTOR_LSB) & 0x1fffu) * FLASH_SECTOR_SIZE;
66+
uint32_t eaddr = XIP_BASE + (((location_and_permissions >> PICOBIN_PARTITION_LOCATION_LAST_SECTOR_LSB) & 0x1fffu) + 1) * FLASH_SECTOR_SIZE;
67+
printf("Start %08x, end %08x\n", saddr, eaddr);
68+
69+
free(buffer);
70+
71+
printf("Writing binary\n");
72+
uint32_t tstart = time_us_32();
73+
uint32_t caddr = saddr;
74+
while (caddr < eaddr) {
75+
uart_putc_raw(UART_ID, 'w');
76+
char *buf = (char*)caddr;
77+
for (int i=0; i < 32; i++) {
78+
uart_putc_raw(UART_ID, buf[i]);
79+
}
80+
if (!uart_is_readable_within_us(UART_ID, 500)) {
81+
// Detect hangs and reset the chip
82+
printf("Write has hung - resetting\n");
83+
reset_chip();
84+
return;
85+
}
86+
char in = uart_getc(UART_ID);
87+
printf("%c\n", in);
88+
caddr += 32;
89+
}
90+
91+
uint32_t tend = time_us_32();
92+
printf("Write took %dus\n", tend - tstart);
93+
printf("Write complete - executing\n");
94+
uart_putc_raw(UART_ID, 'x');
95+
if (!uart_is_readable_within_us(UART_ID, 500)) {
96+
// Detect hangs and reset the chip
97+
printf("Execute has hung - resetting\n");
98+
reset_chip();
99+
return;
100+
}
101+
char in = uart_getc(UART_ID);
102+
printf("%c\n", in);
103+
}
104+
105+
106+
int main()
107+
{
108+
stdio_init_all();
109+
110+
// Set up our UART for booting the other device
111+
uart_init(UART_ID, BAUD_RATE);
112+
gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
113+
gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
114+
115+
// Set up run pin
116+
gpio_init(RUN_PIN);
117+
gpio_set_dir(RUN_PIN, GPIO_OUT);
118+
119+
// Reset chip
120+
reset_chip();
121+
122+
123+
while (true) {
124+
char splash[] = "RP2350";
125+
char hello[] = "Hello";
126+
char buf[500] = {0};
127+
int i = 0;
128+
while (uart_is_readable(UART_ID) && i < sizeof(buf)) {
129+
char in = uart_getc(UART_ID);
130+
printf("%c", in);
131+
buf[i] = in;
132+
i++;
133+
}
134+
if (i > 0) {
135+
printf(" ...Read done\n");
136+
}
137+
char *ptr = memchr(buf, 'R', sizeof(buf));
138+
if (ptr && strncmp(ptr, splash, sizeof(splash) - 1) == 0) {
139+
printf("Splash found\n");
140+
uart_boot();
141+
} else {
142+
ptr = memchr(buf, 'H', sizeof(buf));
143+
if (ptr && strncmp(ptr, hello, sizeof(hello) - 1) == 0) {
144+
printf("Device is running\n");
145+
} else {
146+
printf("Device not running - attempting reset\n");
147+
reset_chip();
148+
}
149+
}
150+
sleep_ms(1000);
151+
}
152+
}

0 commit comments

Comments
 (0)