Starting Point
Here I have described the time-based scheduling of processes, based on one FPGA timer per process. The timer starts when the process is installed via Oberon.InstallProc
, so there is no inherent synchronisation between the timers, and thus between processes. If a set of processes is installed together, they are pretty well in sync within the one millisecond precision of the timers and the scheduler, but a process that is installed later will not be, as its timing starts at an arbitrary point in time, compare to the first set.
Timing Schedule
I have now implemented process timers based on a different approach, namely a global timing schedule. This schedule defines a set of slots, such as every 5 ms or every 50 ms, into which the processes can be installed. As with the previous solution, the timers and the process ready checks are realised in the FPGA.
- There are eight possible slots, or periods.
- The period values are set from software. This is done in Oberon.mod upon system start, based on eight period values defined as constants. This also resets all timers to be in sync.
- Right now, the max. period is limited to 16 bits, ie. 65535 milliseconds. This can easily be expanded to 24 bits with the current concept.
- The delay facility remains, with the same semantics.
- The API of Oberon.mod remains more or less unchanged, with the main difference that in lieu of specifying a period in milliseconds for a process, now a timer number is given.
- The Oberon.Loop is unchanged, as it only checks for the ready bit, independent how it is generated by the hardware.
- One of the periods must be suitable for the garbage collector, which is also triggered by a process timer (the standard one second at the moment).
Scheduling
- The scheduling is cooperative, without preemption.
- All processes have the same priority, independent of their period.
- Processes scheduled for the same time compete for the time slot, and will be delayed with respect to the nominal start time of the slot, depending on the run-time of the other processes scheduled for the same time.
- The Oberon.Loop goes around the linked ring of processes to check if they are ready with each cycle, so the order of the process invocations per time slot is random.
If the order of invocations between two processes is relevant, an explicit synchronisation mechanism should be used, eg. signals. This also applies to the previous solution.
ProcTimers
The correspondingly adapted ProcTimers.mod, ie. the driver for the process timers in the FPGA:
MODULE ProcTimers;
CONST
(* ... *)
CancelDelayCtrl = ORD({0}); (* control data *)
SetTimerCtrl = ORD({1});
ResetTimersCtrl = ORD({2});
Timers = {0 .. 7};
PROCEDURE* SetPeriod*(pn: INTEGER; timerNo: INTEGER);
BEGIN
ASSERT(timerNo IN Timers);
SYSTEM.PUT(PeriodAdr, LSL(pn, ProcNumShift) + timerNo)
END SetPeriod;
(* as before *)
PROCEDURE* cfgTimer(timerNo, period: INTEGER);
BEGIN
SYSTEM.PUT(CtrlAdr, LSL(period, 16) + LSL(timerNo, 8) + SetTimerCtrl)
END cfgTimer;
PROCEDURE* resetTimers;
BEGIN
SYSTEM.PUT(CtrlAdr, ResetTimersCtrl)
END resetTimers;
PROCEDURE Init*(p0, p1, p2, p3, p4, p5, p6, p7: INTEGER);
BEGIN
cfgTimer(0, p0);
cfgTimer(1, p1);
(* ... *)
cfgTimer(7, p7);
resetTimers
END Init;
END ProcTimers.