Booting a Teensy 4.x from scratch

I've had an interest in embedded development for quite a while now, but never really got into the whole Arduino thing. A few years ago I tried my hand at Arduino with a Teensy 4.0, but ultimately found that the Arduino framework is too limiting, so I wrote my own bootloader in Rust. It did boot, but it came with a massive list of issues, so I abandoned the hardware for years, and eventually lost it.

I "recently" bought a new Teensy 4.1, and even set a goal this time: Connecting two USB hosts to seamlessly transfer audio between the two. An output device on one host would be an input device on the other, and vice versa. I dubbed this project TinyAudioLink, and the source code is available on Github. While I still refuse to use the Arduino or RTOS framework, I made massive progress compared to before - let's get into some details.

Booting a Teensy 4.x

The NXP Kinetis Flash Loader

All Teensy 4.x devices are pre-loaded with the Kinetis Flashloader ROM, which handles pre-boot initialization and some other things. The documentation for it is partially wrong, but not enough to really hamper progress and it was quickly dealt with. The only important part was placing it at the start of flash, since there's no way to set its location in the ROM. On to the next thing, which on this device would be located 4KB into the Flash:

The IMXRT1060 Image Vector Table

I've struggled with this part quite a bit, even if it seemed easy at first. Implementing what the documentation provided was easy enough, and it seemingly worked - sometimes. Whenever i changed the function it would call it would randomly stop working, with no indication for why. It seems that even the official Teensyduino was having issues with that, so what gives?

With the documentation about the chip that I could find, I found zero hints as to what was going on. All I knew was that adding a few NOP instructions would randomly make or break the built binary. While there were version differences between what Teensyduino uses, 0x43 (4.3), and what I used, 0x41 (4.1), the structure itself was an exact match. Ultimately, I couldn't figure it out myself, and ended up searching... and found this post.

In it, the author describes a different way to build the Image Vector Table, one with points at the Interrupt Vector Table of the NVIC. This makes sense, since most of these chips don't boot from a Flash Loader, but from the Interrupt Vector Table instead. It should then automatically set up CM7_INIT_VTOR, Vector Table Offset and Stack Pointer for us - with no code required if the post is correct. As I couldn't think of another solution, I tried it...

... and it worked. Quite well in fact, to the point that it would no longer break randomly. No Stack Pointer manipulation, no additional NOP instructions, but I still had to keep the gnu::naked attribute for now. That is something I can fix with the Device Configuration Data element of the Image Vector Table

Where to go from now?

I wasn't expecting to get this far in just over a week, but I still have quite a way to go. The Floating Point Unit is still inactive, the caches are still disabled, the Memory Protection Unit still needs to be set up properly, etc. Overall this was more of an experiment of "What if?", and it was a success. My boot process is faster and smaller than Teensyduino, so it'll allow me to have a bit more code.

Perhaps TinyAudioLink will grow to the point of being a completely new framework, or perhaps this is all it'll ever be. Maybe someone will do the work and add support for a different board than the Teensy 4.x series. But it's all a thing of the future. For now I'm very happy to have something booting faster than the official Teensyduino package.

Until my ADHD reminds me I have a blog.

Comments for: Booting a Teensy 4.x from scratch