Unix on Teensy 3.5

onre

Member
First, a disclaimer: I'm doing this for fun, not because it made any particular sense.

For background, I've dabbled with embedded systems as a hobby for some time. I've mostly made things that work together with my modular synthesizer setup. Lately I've been working on a one-hand chording keyboard for general text input and ended up writing a small multi-tasking library for it.

While writing that code, I remembered how I've read about the RetroBSD project, where an individual ported 2.11-BSD to the PIC32MX7 family of microcontrollers. I thought about how the PIC32MX7 and the Kinetis-K family aren't that different in concept and capacity, the biggest difference being the processor core architecture, which is MIPS on PIC32MX7 and ARM on Kinetis-K. Turns out that another individual had already ported RetroBSD to STM32F4, which is another Cortex-M based MCU family, very similar to Kinetis-K. Their work is called DiscoBSD, and, well, I had this long weekend which I spent mostly pasting things together from DiscoBSD and Teensyduino init code and writing the missing bits.

It gets this far now:

Code:
20241120 20:15:41    connected to /dev/ttyACM1    press C-z q to exit


DiscoBSD 2.3-current (TEENSY35) #92 561: Wed Nov 20 20:13:48 2024
     esp@tieteislaskin:/sys/mk64/teensy35
cpu: MK64FX512, 120 MHz, bus 60 MHz
oscillator: oscillating
usbuart0: USB, console
sd0: port sdio0
sd0: type SDHC, size 7822336 kbytes
sd0a: partition type b7, sector 2, size 204800 kbytes
sd0b: partition type b8, sector 409602, size 2048 kbytes
sd0c: partition type b7, sector 413698, size 204800 kbytes
phys mem  = 255 kbytes
user mem  = 191 kbytes
root dev  = (0,1)
swap dev  = (0,2)
root size = 204800 kbytes
swap size = 2048 kbytes
...and eternal silence falls, as of now. This is the point of first fork() where one branch becomes swapper (pid 0) and another becomes init (pid 1).

In case you want to take a look or play with it yourself, it's all here. Another disclaimer: it's a mess, I need to clean up both the tree and the commit history if I ever get this to actually run processes. It should more or less build with the Teensy toolchain. Set ARM_GCC_PREFIX to $HOME/.arduino15/packages/teensy/tools/teensy-compile/11.3.1/arm/bin/arm-none-eabi, then run make MACHINE=mk64 MACHINE_ARCH=arm to build a ton of things. unix.hex should appear in sys/mk64/teensy35 and is flashable with the Teensy loader.

I'll post here if I get any farther.
 
Okay, we're definitely multitasking now.

Code:
20241123 03:32:07    connected to /dev/ttyACM0    press C-z q to exit


DiscoBSD 2.3-current (TEENSY35) #109 576: Sat Nov 23 03:31:54 EET 2024
     esp@tieteislaskin:/sys/mk64/teensy35
cpu: MK64FX512, 120 MHz, bus 60 MHz, MPU enabled, 12 regions
mpu: region 0: 00000000-ffffffff, RGDAAC0 0361f7df
oscillator: oscillating
uartusb0: USB, console
sd0: port sdio0
sd0: type SDHC, size 7822336 kbytes
sd0a: partition type b7, sector 2, size 204800 kbytes
sd0b: partition type b8, sector 409602, size 2048 kbytes
sd0c: partition type b7, sector 413698, size 204800 kbytes
phys mem  = 255 kbytes
user mem  = 191 kbytes
root dev  = (0,1)
swap dev  = (0,2)
root size = 204800 kbytes
swap size = 2048 kbytes
syscall! @ 0x00001d19
syscall! @ 0x00004b05
syscall! @ 0x00002611
syscall! @ 0x000025ad
syscall! @ 0x000034a1
syscall! @ 0x000034a1
syscall! @ 0x000034a1
syscall! @ 0x000034a1
syscall! @ 0x000034a1
syscall! @ 0x000034a1
syscall! @ 0x000036cd
syscall! @ 0x000036cd
syscall! @ 0x0000c28d
syscall! @ 0x00001a29
syscall! @ 0x000034a1
syscall! @ 0x000034a1
Those are entry point addresses in flash, 1d18 = execv, 4b04 = gettimeofday, 2610 = getuid, 25ac = getpid, 34a0 = sigaction, 36cc = sigprocmask, c28c = open, 1a28 = close... it is actually running something, even though I can't see the actual process output. Need to take another look at that console "driver" I cobbled together. I also (obviously) needed a root device of some sort, so I took the Teensy fork of the SdFat library, did a quick n dirty "C port" of the SD card access part of it and to my amazement it actually worked on the first try. Honestly, I know very little C++ and my method can be described as "remove everything that does not look like C".

I spent considerably more time trying to figure out what is corrupting my first process's u structure. The answer is: nothing. The code has relied on readily zeroed memory for almost four decades. So, a quick memset() addition to the early startup code to avoid accidentally zeroing the stack, too, and suddenly it seems like we're running multiple processes, simultaneously, on a single computer! What is this sorcery?🧙‍♂️

Joking aside, I've yet to see a character being printed from the userspace. At this rate we'll get there soon enough, though. Getting to this point this quickly did wonders for motivation. A week ago my knowledge of ARM processor specifics was of the level "um, I believe it is a RISC design" so it's been a rather deep dive. Unix is familiar, though. I think I need to buy a 4.x from the sale and get this going on that as well. TBH this does not look like a bad platform for certain sorts of projects.

Maybe solder a MAX3232 to every UART on board and have it run six terminals at once?
 
Code:
DiscoBSD 2.3-current (TEENSY35) #2 588: Fri Nov 29 11:17:32 EET 2024
     esp@tieteislaskin:/sys/mk64/teensy35
cpu: Kinetis K64, 120 MHz, bus 60 MHz
mpu: enabled, 12 regions
mpu: region 0: 0x00000000-0xffffffff flags 0x0061f7df
oscillator: oscillating
uart1: found, 64 tx/64 rx bytes FIFO, console
sd0: port sdio0
sd0: type SDHC, size 7822336 kbytes
sd0a: partition type b7, sector 2, size 204800 kbytes
sd0b: partition type b8, sector 409602, size 2048 kbytes
sd0c: partition type b7, sector 413698, size 204800 kbytes
phys mem  = 255 kbytes
user mem  = 191 kbytes
root dev  = (0,1)
swap dev  = (0,2)
root size = 204800 kbytes
swap size = 2048 kbytes
alive for = 1833 ms
init: starting /bin/sh
erase ^?, kill ^U, intr ^C
# ps aux
USER       PID NICE SZ TTY  TIME COMMAND
root         1   0  37 0    0:00  (-)
# ls -l /
total 12
lrw-rw-r--  1 root           13 Nov 29 01:02 .profile -> root/.profile
drwxrwxr-x  2 root         1024 Nov 29 01:02 bin
drwxrwxr-x  2 root         1024 Nov 29 01:02 etc
drwxrwxr-x  2 root         1024 Nov 29 01:02 home
drwxrwxr-x  2 root         1024 Nov 29 01:02 root
drwxrwxr-x  2 root         1024 Nov 29 01:02 sbin
drwxrwxr-x  9 root         1024 Nov 29 01:02 usr
drwxrwxr-x  6 root         1024 Nov 29 01:02 var
#

I can't be sure, but I'd guess this makes me the first person to run Unix on a Teensy?

Anyway, going to spend this evening cleaning up the tree and then see what bites me next. The UART driver needs improving at least - as Paul remarks in a Teensyduino serial1.c comment, the Kinetis UART is a bit weird.

So, what do we next? Does a middle-aged BSD need a USB stack? Framebuffer driver? Something else? Given the quality of Teensyduino code, it's rather trivial to get all that going - the bigger challenge is coming up with a nice way of how to represent all that in the userspace. I've long fancied for a /dev/ugpio which would allow the user to configure any bunch of GPIO pins in a more or less freeform manner, be it async bitbanging, interrupt-driven I/O or whatever is required by the problem at hand, and then just do file I/O on them.

Someone asked me whether I'm debugging this with JTAG or OpenOCD+gdb. I'm not sure whether or not to send the attached photo to them.
 

Attachments

  • teensy_bsd_debug.jpg
    teensy_bsd_debug.jpg
    90.2 KB · Views: 36
Now we are booting to multi-user mode, need to hook up another UART. Repository needs attention, I've been hacking together horrible code to get to this point, general idea being "I'll clean it later". There's a slight glitch in the UART driver still - I have hooked up a button to the board so I can manually trigger interrupts for debugging - turned out that pushing the button is required to get it to go multiuser. I think the culprit is in my uartopen(), need to look at how it is supposed to be done.

The two weird printouts are the debug strings printed by the interrupt handler, showing current systick count and swapin/swapout activity from last five seconds.

I'll post the rest of the source and the build artifacts once I get that ironed out, in case someone else wants to play with it too.
 

Attachments

  • teensy_bsd.png
    teensy_bsd.png
    70.3 KB · Views: 29
For the less knowledgeable folks here, could you explain the advantages of running Unix on the Teensy as opposed to other existing task management libs, or RTOS?
 
For the less knowledgeable folks here, could you explain the advantages of running Unix on the Teensy as opposed to other existing task management libs, or RTOS?
I'm not sure there are any, unless you're already familiar with it. This is more of an exercise in futility than anything else - though I have to admit that it does perform way better than I expected.
 
I'm not sure there are any, unless you're already familiar with it. This is more of an exercise in futility than anything else - though I have to admit that it does perform way better than I expected.
I'm not familiar with it, that's why I was asking! :)

What do you mean exactly when you say it performed better than expected? In what way? And in comparison to what?


I'm asking as I am genuinely intrigued about his
 
Wikipedia does a good job explaining the history of Unix
it was one of the first comprehensive operating system for computers, and the fore-runner for DOS, Windows, Linux
So not a small compact RTOS, the real-deal
So the notion that it can run on a T3.5 is ... astonishing !
 
I wouldn't call it "the real-deal" without an MMU, which the Teensy simply isn't capable of.
Then almost none of the '70s Unixes are "real deal" either. Ditto for all of the PC Unix ports of the early '80s etc.

If your "real deal" criteria is demand paging, then the real Unix history starts at 3BSD.
 
I'm not familiar with it, that's why I was asking! :)

What do you mean exactly when you say it performed better than expected? In what way? And in comparison to what?


I'm asking as I am genuinely intrigued about his
I was (and am) surprised by the I/O speed and the overall snappiness of it. I haven't benchmarked the raw SD card access speed, but even when writing a single large file to the filesystem (read: with the filesystem layer making things slower) I'm getting 6-7 megabytes per second.When I'm running commands over a 115200 bps serial link, the system responds immediately and in general feels usable, even fast.

Also, I presumed that the whole-process swapping as implemented by 2BSD would make the system feel sluggish to use, but the aforementioned I/O speed comes to rescue here. The executables (programs you can run) are rather tiny and if it weren't for the LEDs indicating things, I would not know swapping is happening.

I've written a crude GPIO driver which presents the digital pins to the OS as files. It feels a bit weird to control GPIO by writing quick shell scripts, or even just running commands like echo 1 > /dev/gpioc4 to change the status of a pin. For a lot of what I do at my workbench, this thing is already a rather useful tool. Also I'm feeling mild curiosity about writing an interrupt-driven device driver for the ADC, maybe even using DMA.
 
Didn't start a new adventure yet, but continued an old one a bit... went and did some reading and configuring - now the memory protection unit, or MPU to friends, is working for the money, enforcing the user/supervisor split as it should. Now you can't just run a program that zero-fills memory from 0x1fff0000 to 0x1fffffff and have the system crash instantaneously - what happens instead is that you get a bus fault signal and a dump of relevant registers is printed on the system console as the system keeps on going.
 
Wikipedia does a good job explaining the history of Unix
What I like about the history is that IIRC the name Unix was chosen to indicate singe user single task operating system to run on a small computer. contrary to the mainframes at that time. And BTW Linux is Linus Thorwalds Unix (kernel) implementation coming slightly after A. Tannenbaum's Minix. that aimed to make Unix 'Open source'.
For the interested, here is A Tannenbaums story about Unix,Minix and Linux: https://web.archive.org/web/20100818183310/http://www.cs.vu.nl/~ast/brown/
 
Last edited:
Back
Top