usedbytes / rp2040-serial-bootloader

A serial bootloader for the Raspberry Pi RP2040 (Pico)
86 stars 29 forks source link

Over flow error when I compiled this #1

Closed nachiketathakur closed 2 years ago

nachiketathakur commented 2 years ago

I have the pico development board and I ran this code. I am getting the error as shown below:

[build] c:/progra~2/gnuarm~1/102021~1.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: bootloader.elf section `.text' will not fit in region `FLASH'
[build] c:/progra~2/gnuarm~1/102021~1.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld.exe: region `FLASH' overflowed by 9244 bytes

What am I missing?

usedbytes commented 2 years ago

The error means that the compiled code ended up being too big, so it won't fit in to the 12 kB my linker script sets aside for the bootloader. That's strange because for me this builds to around 9 kB.

Have you made any modifications to the code? Could you also please share what version/revision of the pico-sdk you're using?

usedbytes commented 2 years ago

If you just want a quick fix, you could try this (expand the size allocated to the bootloader):

diff --git a/bootloader.ld b/bootloader.ld
index c82585d3ea70..128fa962c02f 100644
--- a/bootloader.ld
+++ b/bootloader.ld
@@ -21,10 +21,10 @@
     __stack (== StackTop)
 */

-/* Limit flash to 12k, so we can use 12-16k for the image header */
+/* Limit flash to 24k, so we can use 24-28k for the image header */
 MEMORY
 {
-    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 12k
+    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 24k
     RAM(rwx) : ORIGIN =  0x20000000, LENGTH = 256k
     SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
     SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
diff --git a/main.c b/main.c
index cd48ae231783..70cde4d67734 100644
--- a/main.c
+++ b/main.c
@@ -53,7 +53,7 @@
 #define RSP_OK   (('O' << 0) | ('K' << 8) | ('O' << 16) | ('K' << 24))
 #define RSP_ERR  (('E' << 0) | ('R' << 8) | ('R' << 16) | ('!' << 24))

-#define IMAGE_HEADER_OFFSET (12 * 1024)
+#define IMAGE_HEADER_OFFSET (24 * 1024)

 #define WRITE_ADDR_MIN (XIP_BASE + IMAGE_HEADER_OFFSET + FLASH_SECTOR_SIZE)
 #define ERASE_ADDR_MIN (XIP_BASE + IMAGE_HEADER_OFFSET)
nachiketathakur commented 2 years ago

Hi. Thanks for the reply. I am on Windows environment, my Makefile generator is MinGW-Make(mentioning this because in pico getting started guide the suggest generator is NMake for Windows). I am using the latest version of Pico SDK(release 1.3.0). And I have not made any additions to the main.c. I just ran it as it is. Also kindly note that I have flashed the *.UF2 file only. I hope that is what I am supposed to upload to the development board.

I had tried the additions in linker script before you wrote and it seemed to work, but I didn't understand the reason. Thanks for explaining about compiled code's size being too big.

After making changes to the bootloader.ld file, I made the following change in blink_noboot2.ld: FLASH(rx) : ORIGIN = 0x10000000 + 24k, LENGTH = 2048k - 24k After doing so, I compiled the code and flashed the *.UF2 file(of blink_noboot2.ld) into pico by mass storage mode. The led did blink but very quickly and only ONCE. I tried making changes to the while loop, just toggled the LED with the delay of 500ms but it didn't work. Why so? What am I missing? Do I have to make more changes to the .ld file?

usedbytes commented 2 years ago

FLASH(rx) : ORIGIN = 0x10000000 + 24k, LENGTH = 2048k - 24k

This should be 0x10000000 + 28k, LENGTH = 2048k - 28k (28k), an empty 4 kB page of flash is needed to store the information about the program. It has to be a full 4 kB because that's the smallest block that can be erased

usedbytes commented 2 years ago

Oh also I don't think you can upload blink_noboot.uf2 via mass storage, or at least I haven't tried that. I don't know if the UF2 bootloader supports writing code anywhere other than the start of flash.

You can flash blink_noboot2.elf (or .bin but you'll have to specify the address) over serial (after all that's the whole point of having the serial bootloader 😄). There's some go code for doing that here: https://github.com/usedbytes/serial-flash

Obviously this all needs to be much better documented, but this bootloader was just one part of a larger project with a looming deadline so I haven't put much effort into making it easy to reproduce. Sorry about that ☚ī¸

I'm guessing you found this project via my blog post, but if not it might have some useful info: https://blog.usedbytes.com/2021/12/pico-serial-bootloader/

nachiketathakur commented 2 years ago

Can you please tell me how the LED blink code worked out for you? In my case it just blinked once and that too very quickly which I think might be due to I uploaded the UF2 file. I think the blink's UF2 file did not upload to the Pico. When I flashed it, it just got reset and the bootloader's LED blinked. So yeah I need to understand how UF2 files are flashed and flash the .bin files to verify.

I have never worked with Go, so I think I will try uploading *.bin files using the picotool. And great job with the bootloader, by the way.

I could not find any information on why the binary size of boot loader is so big. Are you also on the same environment as me?

I got the reference from your project. I really liked your blog. Thank you for sharing with us. :-) 👍

usedbytes commented 2 years ago

If you're only seeing one brief flash then that will be the bootloader itself. It flashes the LED when it first starts up.

Thinking about it more, using blink_boot2.uf2 definitely can't work, because the UF2 loader won't know how to write the "SEAL" data. Picotool won't work directly either for the same reason.

You could hack the code up some more to skip this. If you always write your program (with picotool) to 0x10000000 + 28k, then you could make some changes like this, but note this will make the bootloader unconditionally try and run your program, even if it's not there.

diff --git a/main.c b/main.c
index cd48ae231783..658e204669fd 100644
--- a/main.c
+++ b/main.c
@@ -694,8 +694,8 @@ int main(void)

    struct image_header *hdr = (struct image_header *)(XIP_BASE + IMAGE_HEADER_OFFSET);

-   if (!should_stay_in_bootloader() && image_header_ok(hdr)) {
-       uint32_t vtor = *((uint32_t *)(XIP_BASE + IMAGE_HEADER_OFFSET));
+   if (!should_stay_in_bootloader()) {
+       uint32_t vtor = XIP_BASE + (1024 * 28);
        disable_interrupts();
        reset_peripherals();
        jump_to_vtor(vtor);

If you want to do it "properly" then you'd need to fill in this structure, and write it to flash just after the bootloader (i.e. at 24k for you), otherwise the image_header_ok(hdr) in my code won't pass, and it will stay in the bootloader:

struct image_header {
    uint32_t vtor;
    uint32_t size;
    uint32_t crc;
    uint8_t pad[FLASH_PAGE_SIZE - (3 * 4)];
};

The vtor is the address of the program (0x10000000 + 28k for you), size is the length of the .bin, rounded up to 512B, and crc is a CRC of the padded binary, using IEEE802.3.

This is how my bootloader checks that your program was flashed correctly, and how it knows where your program is so that it can jump to it.

I have to ask though... if you aren't going to upload code via serial, why do you want this bootloader at all?

nachiketathakur commented 2 years ago

Thanks for the suggestions.

I did not understand the:

Thinking about it more, using blink_boot2.uf2 definitely can't work, because the UF2 loader won't know how to write the "SEAL" data.

What is SEAL data? I did see your blog where you have mentioned about CMD_SEAL, but I cannot find any reference of such commands in the data sheet of RP2040. Can you please point me where I can read more about it?

I just want to receive the firmware from USB, but I could not find any projects or sample codes which explains how to update the firmware via USB. I need a boot loader which can update the firmware by the USB mode.

usedbytes commented 2 years ago

CMD_SEAL is implemented in my code, it's not something the RP2040 knows about.

Maybe I should wind back and summarize a bit:

In my use-case, my Pico is embedded in a project where I don't want to have to plug in USB every time I update the code. So, I wrote this serial bootloader so that I can upload code via the UART, and I have connected the UART to an ESP32 connected to WiFi, meaning I can update the code over WiFi.

Because I'm uploading code over WiFi (or even if I wasn't), it's possible that something goes wrong part-way through upload, which would potentially leave the Pico with a half-written program, which wouldn't work, and I'd have to connect the Pico to USB to recover it.

To avoid this, I implemented "CMD_SEAL" (which should perhaps be called "CMD_FINALIZE" instead). The programming sequence goes like this:

What CMD_SEAL does, is stores:

to a known place in flash (just after the bootloader code).

When the bootloader starts, it checks for a valid "SEAL", by reading the address and size, calculating a CRC of that region of flash, and then comparing the CRC it calculated to the CRC in the "SEAL". If they match, then it's safe to assume that the program was written correctly and the bootloader can safely jump to it. If the "SEAL" doesn't match or isn't valid, then the bootloader will stay in bootloader mode, allowing another attempt to download code.

This means that in the case of a partial or failed download or some random data corruption, the "SEAL" won't match, the Pico will stay in my bootloader, and I'll be able to re-program it without needing to connect to USB.

I'm still a little confused about what you're hoping to use this bootloader for - if you don't want to upload your code via UART, then why use it? And if you do want to upload your code via UART, why do you want to use picotool or UF2/USB boot?

I'm happy to try and build you a binary of my Go tool for programming over serial if that will help - are you using 64-bit Windows 10 on x86? I've never tried it on Windows but I think it should work.

nachiketathakur commented 2 years ago

Hello, Sorry for delayed response. Actually, my goal was to learn creating a custom boot loader for rp2040 and the method using which I am going to update the firmware is USB. I had several confusions. but they are all solved now. Thanks a lot for explaining implementation of CRC in your code.

By the way regarding the huge size of bootloader, did you create it in RELEASE MODE? I have created the binary in debug mode, may be that is the reason why it's size is so big.

usedbytes commented 2 years ago

Actually, my goal was to learn creating a custom boot loader for rp2040 and the method using which I am going to update the firmware is USB

Ah! OK, that makes sense :-) Thanks for clarifying

By the way regarding the huge size of bootloader, did you create it in RELEASE MODE? I have created the binary in debug mode, may be that is the reason why it's size is so big.

Yes that sounds likely

Edit: to be honest, even 12 kB is rather "huge" for a simple bootloader - I didn't focus at all on optimising the size, I just wanted to get something working as quickly/simply as I could.

nachiketathakur commented 2 years ago

I am using GCC 10.3.1 kit. Are you using the same?

usedbytes commented 2 years ago

I'm on AArch64 Linux, GCC 7.3.1

$ uname -a
Linux archer 5.4.51-v8+ #1333 SMP PREEMPT Mon Aug 10 16:58:35 BST 2020 aarch64 GNU/Linux
$ /usr/bin/arm-none-eabi-gcc --version
arm-none-eabi-gcc (15:7-2018-q2-6) 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907]
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
nachiketathakur commented 2 years ago

Great. Thank You for helping me, I really appreciate you for taking time to explain me things. We can close this issue now.