RP2040 baremetal starter project - Page 1
Forum section: electronics / general

author: ataradov
author_rank: Super Contributor
timestamp: December 31, 2021, 10:26:15 pm
content: I've got a few RPi Pico boards when they were released. Well, now their time has come. I decided to have a closer look at them and I made my typical bare-metal starter project that I make for every device I work with. The code can be found herehttps://github.com/ataradov/mcu-starter-projects/tree/master/rp2040I did not use their header files or SDK, since they are just horrible, IMO. I generated my own header file from SVD file using SVDConv utility from ARM. The result is not perfect by any means, but it is at least usable.Initial loader is built as a part of the project itself. The project is made so that initial loader transfers the application from the flash into SRAM and entire application is linked in SRAM. Flash remains accessible in XIP mode. It is never used by the compiler generated code, but you can store any data there and read it manually.The loader uses plain SPI mode to not mess with the QSPI settings in the different flash devices. I have a "clean" version of the QSPI initialization for the flash on RPi Pico if anyone wants that. But I found that QSPI mode does not actually increase performance that much to bother with it for generic code. I tested with the main code running from SRAM and doing CRC32 calculation on the entire 2 MB flash. So the access pattern is linear (in 32-bit words) with minor data processing in-between. QSPI was 1.6 times faster that SPI in that test. Cache did not matter that much here (as it is constantly trashed by long linear access). Cache does matter A LOT if you also run from that flash. The test was ~30 times slower in that case. But this project runs entirely from SRAM, so this is not an issue.Also note that if you are using the flash in the main application, then you probably need to adjust XIP_SSI->BAUDR, since SPI clock would be 60 MHz with 120 MHz core clock, and 0x03 command only goes up to 50MHz. If you are using flash a lot, you may get better performance by dropping CPU clock to 100 MHz and leaving the divider as is.The project also includes a tool to generate UF2 file with correct CRC32 for the bootloader section. It can also just update the binary file itself if you have some other way to place files into the flash. There are probably other tools like this out there. I have not really looked.When I have more time I'll have a look at using both cores and USB, as this is the only not trivial peripheral there.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 01, 2022, 02:03:45 am
content: I've written my own build environment as well, but using the SDK files so far.Quote from: ataradov on December 31, 2021, 10:26:15 pmWhat did you find horrible about them?

author: ataradov
author_rank: Super Contributor
timestamp: January 01, 2022, 02:24:43 am
content: Quote from: SiliconWizard on January 01, 2022, 02:03:45 amA bunch of unconventional stuff. Starting with peripheral definitions in lower case. Things split into a million files for no reason (Atmel does the same and I hate it too).Lack of bit-field unions is not great either. I prefer let the compiler do the math rather than do a bunch of bit-ops when I need to change the value of a multi-bit field in a register.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 01, 2022, 02:39:03 am
content: Quote from: ataradov on January 01, 2022, 02:24:43 amOh, yeah. Not my style either, but that was definitely not a deal-breaker either. I've seen worse in terms of bad style.Quote from: ataradov on January 01, 2022, 02:24:43 amOK, this one is horribly annoying. The NXP SDK is structured exactly in the same way. It's largely due to their use of CMake, which promotes this heavily hierarchical structure of sources files. It's not just that there are many source files - it's also that they are each in their own subdirectory!! The alleged benefit is that one can fine-tune compiler options for each and every file, because each is associated with its own CMake file! The practical benefit of that in this kind of case is almost exactly zero.Quote from: ataradov on January 01, 2022, 02:24:43 amYeah. Well, these days, compilers are indeed largely smart enough to group operations when you use bit fields, so bit masks have no advantage for performance reasons. But... bit fields are not portable. That would be the only rationale for not using bit fields. Making it compiler-proof.Which in itself is a questionable issue since those definitions are entirely target-dependent, and, the main two compilers that are commonly used for ARM Cortex dev, GCC and Clang, both treat bit fields the same way. I've never used armcc - and I don't think it's even currently supported by the Pico SDK - but I think it does treat bit fields also in the same way as GCC...

author: ataradov
author_rank: Super Contributor
timestamp: January 01, 2022, 02:45:56 am
content: Quote from: SiliconWizard on January 01, 2022, 02:39:03 amI don't care. They are portable among all reasonable compilers. And I don't use goofy compilers, so they are pretty much guaranteed to work for me. In fact, not being compatible with leading compilers is the issue of the compiler not the code. If you take seriously all the stuff that is missing from the "official" standard, you will live a sad life.There are a whole bunch of other attributes and stuff that pretty much ties this code to GCC/Clang and I realistically see no reason for any other compilers to exist or use them.Also, new (v6) ARMCC is just Clang, so it will work the same way as GCC.

author: westfw
author_rank: Super Contributor
timestamp: January 02, 2022, 08:02:39 am
content: QuoteQuoteYeah. With interdependencies on the directory structure.I tried setting up an Atmel-style top-level "io.h" file that included all of the struct and register definitions so I wouldn't have to have a hundred "-I" options on the compile line, and it does work, because some file that ought to be rp2040 specific (address_mapped.h: "Low-level types and (atomic) accessors for memory-mapped hardware registers") ends up including pico.h (which is theoretically a board-specific definition file) and "hardware/regs/addressmap.h"Essentially, they've mixed up their chip definitions with their library and SDK definitions, and pretty much required a specific (and unusual) build environment :-(Did we mention yet that the file paths are redundantly redundant? Like ".../rp2040/hardware_regs/include/hardware/regs/adc.h" ?One of the early problems that cropped up was that the micropython (one of their preferred tools) build on windows ended up generating compile commands that exceeded window's maximum command length. (Yeah, bad for windows having a limited command line length. But it's about 8k bytes, which ... is pretty ridiculous to exceed!)I'm not sure that it's "awful", but it's surely ... annoying.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 02, 2022, 05:08:26 pm
content: Quote from: westfw on January 02, 2022, 08:02:39 amThat's part of the heavy hiearchical structure of the files and directories. Not only does every .c file lives in its own subdirectory, but each also has its own 'include' subdirectory with the corresponding header file(s), and as you showed here, the include subdir can have also subdirectories. All this is structured to accomodate a certain way of using CMake, as I said. As I got it, it allows them to "define" each feature support as a kind of independent "module", making adding one easier and independant of the rest. The idea could be interesting at first sight, but it just ends up as a huge mess.Note: to be fair, it probably makes the life of the maintainers of the SDK easier (as long as they master this structure), as adding "features" is probably a bit easier. And, for users, it's alright too as long as you use CMake, and use it as instructed. It also makes it possible and easy to cherry-pick the SDK features you want, instead of having to build and link against the whole shebang. So, that's very modular. But for people using other build tools, or using CMake in a different way, that's pure hell. So, you're stuck with their way of doing things. Or, you have a lot of annoying work to do. I did write my own Makefiles from this SDK, but it needs to deal with a lot of directories, and that's pretty annoying indeed.

author: westfw
author_rank: Super Contributor
timestamp: January 02, 2022, 10:23:50 pm
content: QuoteI'm not sure I'd mind so much if it was just the (SDK) *code* that was structured this way.But having the (theoretically codeless) device definitions set up like this is not good.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 02, 2022, 10:26:26 pm
content: They probably did this out of consistency, but if all you want to use are those definition files, you can extract them, put them in one single directory and use them like that.

author: ataradov
author_rank: Super Contributor
timestamp: January 02, 2022, 11:40:59 pm
content: Quote from: SiliconWizard on January 02, 2022, 10:26:26 pmYou would have to do some editing/post-processing too, since they all use #include with long paths.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 03, 2022, 12:23:25 am
content: Quote from: ataradov on January 02, 2022, 11:40:59 pmYeah, the include directives use relative paths, consistent with this frantic hierarchical scheme.So yeah, if you're only going to use those definitions headers, better generate your own from SVD, as you did.

author: ataradov
author_rank: Super Contributor
timestamp: January 05, 2022, 04:08:00 am
content: I've added RP2040 as a platform to Free-DAP, which required adding USB (HID and CDC) -https://github.com/ataradov/free-dap/tree/master/platform/rp2040. So now I have a clean USB stack. But to be fair, their own simple bare metal USB example is also pretty clean and useful.I also added pre-built UF2 image that can turn any Raspberry Pi Pico into a CMSIS-DAP debugger.Next I want to move the DAP part to the second core, mostly just to see how the multi-core thing works. And then implement SWD (and may be JTAG) low level transfers with PIO controllers. Again, mostly to have a practical thing to be used as a learning tool.But the more I work with this chip, the more I like it. I ordered 50 pcs to have some in stock to be used in random projects.

author: Rudolph Riedel
author_rank: Regular Contributor
timestamp: January 05, 2022, 03:57:15 pm
content: I have not checked in a while since I quickly lost interested in the RP2040 again.But the includes, yes, I was surprised that there were no CMSIS parts included.So instead of Systick_Handler() it was isr_systick() and there was no SysTick_Config().

author: don_sam17
author_rank: Newbie
timestamp: April 27, 2023, 02:32:43 pm
content: Hi Alex. I have been using your setup to generate uf2 files for some bare-metal applications. Now I'm facing an issue, when i upload the generated elf file it doesn't work. it drops down to usb boot after uploading elf file. Please help me out. It is kind of urgent for the project I'm doing.

author: ataradov
author_rank: Super Contributor
timestamp: April 27, 2023, 03:55:05 pm
content: Binary file needs to contain the CRC, there is no easy way to add that CRC into the ELF file directly. You can do tricks like converting ELF to BIN, adding the CRC and converting it back to ELF.Bin2uf2 tool has "-u" flag that would simply update the CRC in the supplied BIN file, which then can be converted into ELF. But at that point you might as well program the BIN file.

author: brucehoult
author_rank: Super Contributor
timestamp: April 28, 2023, 01:35:21 am
content: I finally ordered a Pico this week. Partly because it's an interesting chip/board, but mostly because someone has implemented the WCH 1-wire debug protocol on Pico and it uses normal OpenOCD not a patched one.https://github.com/aappleby/PicoRVDCommenting so this thread is in my "your posts" list. As you were, gentlemen.