It’s important to note and point out, again, that Oberon and Embedded Project Oberon were not designed to be a real-time operating system. Consequently, the following is by no means to be read as criticism, but as starting point to define extensions and changes, to experiment and explore.
Embedded Project Oberon represents an opportunity. Its clear and lean structure invite experimentation, with the potential to result in something of practical use in real-world controller projects. It’s a time-tested and robust base, understandable down to the bits and logic gates for anyone.
Embedded Project Oberon as Real-time System
Let’s review Oberon against the basic requirements. Note that this implicitly includes the RISC5 FPGA-implementation, as the two are tightly linked on the lowest levels – or turned around, when later discussing to extend Oberon, the opportunities of the FPGA need to be included in the considerations as well.
Oberon tasks can be used to implement control processes. However, the timing of tasks is not strict. When the Oberon Loop finds a task to be due to run, based on a comparison with a milliseconds timer and the task’s due time value, the next scheduled run is set at a new due time, computed as current time plus the configured fixed period for the task. That is, tasks are not forced into a strict scheduling regime, accounting for slack.
Oberon tasks can be used to avoid busy waiting, to a degree. Tasks can only be activated based on time passed, and the scheduling period is fixed. It is obvious that this was deemed good enough for the goals and purpose of the Oberon system, probably because the perception of time passed for a human user is much different than for control processes. 100, or even 500, milliseconds is nothing for a human, but an eternity for a control process.
However, there are no facilities that allow scheduling a task other than periodically, say, upon a buffer empty condition.
If you check out the file access code in Kernel.mod you’ll see that the underlying SPI driver is completely based on busy waiting. We’ll need to assess what this means for Oberon RTS. Changing the file access and management procedures in the kernel is not something I would want to venture into without very – and I mean very – good reasons.
Run-time Error Protection and Recovery
The Oberon compiler inserts a defined set of run-time checks, which result in traps if violated. The trap handler in System.mod prints a message, and calls Oberon.Reset. If the trap was caused by a task, it is removed from the task list, hence not invoked again by the Loop. Oberon.Reset also resets the Loop, resetting the stack pointer to its startup value. Because commands and tasks are just procedures, this approach is reasonable and sufficient for a system that assumes that a user is present, operating the machine.
Other than the above, the operating system does not provide any facilities for error protection and recovery. Of course, commands and tasks can implement additional features on an individual basis.
The Oberon system does not have features to allow to start a non-system task or execute a command at startup. Oberon.mod starts the garbage collector task at startup. Also, if a module is being imported by a system module, it could start one or more tasks at startup, eg. a file server, but Embedded Project Oberon does not include such module.
The book Project Oberon differentiates between the RISC5 processor proper, and its environment. However, the term RISC5 processor is often being used for both the CPU itself as well as the whole system on a chip, which also includes the peripherals.
For clarity, we’ll use the term RISC5 processor to mean the system on a chip, including any extensions of the original design, and use RISC5 CPU to mean, well, the RISC5 CPU.