-
Notifications
You must be signed in to change notification settings - Fork 983
Description
I've been using TinyGo to develop firmware for a custom board based on the ATSAMD51. Overall it's been an awesome experience.
Some of the changes necessary have required forking TinyGo to get everything working right. I wanted to summarize those points here and see if there is interest in resolving them and get any guidance on the direction for each.
- The biggest immediate difference for custom boards is one cannot make assumptions about pin assignments or SERCOMs and the various things that use them (UART, SPI and I2C buses or anything else).
- This means that UARTs, SPIs, etc. need to be fully definable in user code. This is not terribly hard to do, but there are little things like the interrupt handler func for the UART on SAM microcontrollers is not exported (
handleInterrupt
). - It also means
print()
and friends would need to write to something which is user-definable (I'm thinking an instance of an interface with a Write and/or WriteByte method), so after the appropriate setup one could do something like "machine.DefaultPrinter = &myCustomUART". And by default it could just not send anywhere or maybe panic with a "must set DefaultPrinter before print()ing" message. Perhaps this behavior could correspond to a command line option like-serial=custom
- I had to modify the linker configuration in order to accommodate different bootloader sizes. Perhaps I missed something on this, but I could not find a way to reference a linker script from a target JSON file that lived outside of TinyGo's source tree. (As an interesting side note on this, I was able to write a working bootloader in TinyGo. The only tricky part is the actual jump to user code, but it's just a few asm instructions needed. Would this be worth writing up as an article somewhere so other people could do this if they wanted? It would be a good place to document the linker script changes needed also.)
- I did run into a bug deep inside the init code that triggered when using a TinyGo bootloader, I think it might be related to a SAMD51 hardware issue. There were a couple cases where a setting was made and then it would spin in a for loop waiting for a flag to clear, and this would work correctly the first time but not the second time. I ended up hacking in a "if this option is already set to this value, don't do it again" change for this. I can dig up the details separately.
- The rest of the startup code seems very reasonable and useful even for custom boards. I ran into very few issues and most things Just Worked.
- And then separately but related, I ended up having to build tooling to expose more information about the stack and heap. E.g. the stack overflow checking code doesn't fire right away, since it waits for the canary value to be overwritten and only checks for that at certain points, assuming the program didn't crash before then. So I wrote something I could drop into various places to check the free stack space and blorp out a warning if it's below a certain watermark. Similarly on the heap I needed a way to get a picture of what the memory usage was so a made something to dump out various stats as well as an ASCII-art map of the memory usage (256KB can be represented in a 64x16 grid of hex letters one for each 256 bytes - it makes for a nice succinct visual summary showing what's used/free/fragmented). I'm not entirely sure what the best path forward is with stuff like this. It's obviously very specific to these internal implementation details and so not portable and only useful in limited scenarios, but at the same time while trying to make a complex program within the constraints of an embedded system, I found this sort of visibility vital when debugging various issues. So if there is a package where this sort of thing could live I think it could be a useful contribution. And unfortunately these things require at least some access to the
runtime
internals, so it can't be (entirely) a third party package. - If there were a way to get and print the PC value for panics or other fatal errors (not sure if it's available in the LR register or the stack), and some generally available tooling to trace this back to a file:line, that would be very useful. I realize microcontroller code too constrained to include debug info, but the static memory layout also means it's pretty easy get a program counter and find where it's coming from. I was able to do this in some cases where it was in an infinite loop by halting with openocd to get the PC, and then using
objdump -S
on an ELF output from TinyGo to find which function it was in (and a rough guess from the asm what the corresponding code was). It would be great if this process were simpler. I realize debugging it's own thing, but I think this mapping of the PC back to a file:line could be really useful.
So overall my question is: do these seem like things I should do changes and PRs for and try to get things to the point where custom boards (at least starting with those based on SAM chips) are supported out the box? And also some of the debug tooling mentioned above? If so, I'll see about setting aside some time to clean up the changes I made into merge-able PRs and originate any other discussion about the design of some of these things where needed.