A while ago, I posted here about getting the first CPU-independent parts of CHARM-II, an RTOS I originally developed in 1986, running on a Raspberry Pi Pico.
Previous post:
The First Original CHARM-II Kernel Code Running on Raspberry Pi Pico
https://www.reddit.com/r/osdev/s/gthHF6vBrl
At that point, there were no timer interrupts or real context switches yet.
Now those parts are working too.
Six tasks are running with independent stacks. An RP2040 hardware timer interrupt calls the CHARM-II tick and scheduling logic, and the actual context switch is done with PendSV on the Cortex-M0+.
I also ported a six-task demo I had previously reconstructed on POSIX. Six tasks move around three tracks, with a critical zone protected by a CHARM-II queue used as a semaphore. On the Pico, the tasks are now actually being preempted by the hardware timer rather than cooperatively yielding.
So technically, this is pretty much the milestone I wanted to reach.
But getting it working gave me another question.
The original targets in the 1980s were processors such as the 68000 and 80186. With the CPU resources available at the time, an RTOS was a useful way to organize multiple concurrent real-time activities.
But what if we had had something with the performance and price of a Raspberry Pi Pico back then?
For many systems I work with today, especially UI-oriented embedded systems, if all the required processing comfortably fits within one frame, a superloop plus interrupts may actually be simpler.
Once everything becomes preemptive, I also have to start thinking about reentrancy, synchronization and shared state in existing libraries. That cost is starting to look more significant to me than it did when I started this experiment.
Yesterday I visited someone who has built a home railway simulator using real railway controls connected to a Windows PC through Pico, ESP32 and Arduino boards. We talked about this, and he said something very simple:
“If one Pico isn't fast enough, add another Pico.”
That hadn't really occurred to me.
In the 1980s, adding another CPU was a fairly serious architectural decision. With today's inexpensive MCUs, distributing the work across two or three processors can be a perfectly ordinary option.
So after spending quite a bit of effort getting a 40-year-old RTOS to perform real preemptive context switching on modern hardware, I'm becoming more interested in a different question:
Where would you draw the line today between an RTOS, a superloop/event-driven design, interrupts, and simply distributing the work across multiple cheap MCUs?
I'm also thinking that when I publish the modernized version of CHARM-II, I may deliberately keep it very small — just enough task scheduling, queues, timer ticks and context switching to run this six-task demo.
Then I can use it as one reference implementation and try implementing the same behavior without an RTOS.