tinygo-org / tinygo

Go compiler for small places. Microcontrollers, WebAssembly (WASM/WASI), and command-line tools. Based on LLVM.
https://tinygo.org
Other
14.92k stars 880 forks source link

RP2040 2nd stage bootloader strategy #1934

Closed kenbell closed 3 years ago

kenbell commented 3 years ago

This issue is a placeholder to figure out the best approach for RP2040 2nd stage bootloaders...

I'm having a quick play with a RP2040 feather - current pico code doesn't boot. My best guess is the 2nd stage bootloader is not compatible. Feather uses a GigaDevice GD25Q64C (8MB). It looks like the situation with RP2040 stage2 boot loaders is 'complex'... :

The TinyGo current code is one of the Pico 2nd stages in binary form (converted to .data hex): https://github.com/tinygo-org/tinygo/blob/dev/targets/pico_boot_stage2.S

It seems like ideally:

  1. We'd include the source for the stage2 bootloader in TinyGo
  2. The code would be assembled by TinyGo, either at go install time, gen-device time or tinygo build
  3. There would be something like the Circuit Python approach of having a modular / conditional stage2 that supports many flash chips

The build strategy is 'complicated' by the need to include a checksum at the end of the stage 2 bootloader that is verified by the stage 1 bootloader in ROM, so just assembling a .S file to be linked into the application binary is not sufficient.

aykevl commented 3 years ago

Hmm. That really complicates things.

We could indeed use a database of flash chips, but I'm not sure that's worth the effort. I would be fine with specifying the parameters manually for each chip.

One possible solution would be the following:

There are possibly other ways to do it.

aykevl commented 3 years ago

I think writing the stage2 bootloader in assembly will be easier as you don't have to worry about C compiler optimizations getting in the way. A quick look at the CircuitPython version suggests it relies on specific compiler flags to compile correctly. This can be avoided by programming directly in assembly.

kenbell commented 3 years ago

Makes sense - i agree, assembler is probably the right approach. I'm not familiar with how the ESP32 binary format works, so i'll take a look at that.

kenbell commented 3 years ago

I've been thinking a bit. If we patch the uf2 only, then tinygo build -target pico -o blinky1.elf ./src/examples/blinky1 will have surprising results (especially if someone tries to use a debugger to flash the board).

I'm going to try out doing 2 linker passes:

aykevl commented 3 years ago

Yes, that is also an option. The debugger issue you mention may indeed be a problem.

Perhaps easier is modifying the ELF directly. This is already done in one place, here: https://github.com/tinygo-org/tinygo/blob/4e610a0ee72eba476b5b4102df62385ef7b73e39/builder/build.go#L980

In fact, it might be a good idea to do this for the ESP chips too for consistency.

kenbell commented 3 years ago

I've created a prototype of using 2 linker passes. It assembles the existing Pico 2nd stage from code and creates a binary image that boots. I tried to code it so that it could be extended in future for other use-cases where 2 linker passes might be needed, but it feels very specific to this case. Prototype is here: #1936

I'm going to also try prototyping the approach of patching the ELF directly - if we can have three things use that technique (auto stack-sizing, rp2040, ESP) that seems like it would be the better option.

kenbell commented 3 years ago

And a prototype of using binary patching: #1937

I tried to minimize redundant logic between modifyStackSizes and the rp2040 logic. I don't feel too strongly about it though (doesn't really simplify things much)

kenbell commented 3 years ago

To keep this thread updated, moving forwards with #1937 (patching binary after linking) and dropping #1936.

deadprogram commented 3 years ago

This has been released with v0.19.0 so now closing. Thanks everyone!