Scheduling (Fixed Schedule, Prios, #1)

Starting Point

Here I have described a scheduling solution based on a fixed timing schedule. All processes are invoked based on FPGA-based timers. The scheduling algorithm goes round in the linked process ring, so all processes will be activated at some point.

Process Priorities

Process priorities are added using one ring of linked processes per priority level, as a direct extension of the existing concept.

  • The scheduling algorithm goes around in each ring separately. That is, each process in a priority ring will eventually be activated, if processes of its prio level are not locked out by higher prio processes.

  • Processes of a specific prio, which are ready based on timing or device signals, will be invoked before any lower ready prio processes. The other way around, lower prio processes can be locked out by higher prio ones, if the latters' run-times exceed their periods.

  • Priority and period are not connected. In particular, short periods do not automatically mean higher priority. The process designer can choose to set higher frequency processes at a higher prio, but does not have to.

  • There are three priority levels right now. Priorities are positive integers, including zero, with lower priority numbers meaning higher process priority.

The ring per prio is used to ensure that a process cannot lock out the other processes at the same prio. A simpler solution would be a single process list, sorted by priority and possibly process period.


The Oberon.Loop uses the following algorithm. It puts value on the lightweight detection of the ready conditions as signalled by the FPGA devices for timing and device signalling, to only do any further checking and processing upon such condition. Other approaches are feasible, of course, such as checking for process state first.

As we now have to ensure that no process of higher prio is pending (ready) before even considering the next lower one, we have to work through the whole higher level ring. Therefore, the ready bits from timing and device signals are read in one go, and the checks per process are done one this loaded data word. This also has the advantage that all process checks are based on the same time time basis.

Per one loop of the Loop maximally one process gets invoked, at which point the evaluation of the process readiness is stopped, and the dance starts anew with the next run of the Loop. This gives command and upload handling a chance to run. When a process was invoked, the pointer into the corresponding ring gets advanced, and the process evaluation will start from there with the next loop.

MODULE Oberon;

    procs: ARRAY NumPrios OF Process; (* process rings *)
    cp: Process;  (* current process *)

      res, prio: INTEGER;
      ch: CHAR;
      done: BOOLEAN;
      p, p0: Process;
    WriteLine("Starting scheduler");
      IF Console.Available() > 0 THEN
        (* command and upload handling *)
        (* load ready bits *)
        (* GC timing ... *)
        (* check the processes *)
        prio := 0; done := FALSE;
        WHILE ~done & (prio < NumPrios) DO
          IF procs[prio] # NIL THEN            
            p := procs[prio]; p0 := p;
              IF ProcDevSig.ProcessReady(p.pcNo) THEN (* device signals *)
                IF p.state = AwaitDevsigTo THEN
                p.retVal := OK;
                p.state := Active; cp := p; Coroutines.Transfer(loop, p.cor); cp := NIL;
                procs[prio] := procs[prio].next;
                done := TRUE;         
              ELSIF ProcTimers.ProcessReady(p.pcNo) THEN (* timing *)
                ProcTimers.SetPeriod(p.pcNo, p.period);
                p.retVal := OK;
                IF p.state = AwaitDevsigTo THEN
                  p.retVal := Timeout
                IF ~(p.state IN {Suspended, AwaitDevsig}) THEN
                  p.state := Active; cp := p; Coroutines.Transfer(loop, p.cor); cp := NIL;
                  procs[prio] := procs[prio].next;
                  done := TRUE
              p :=
            UNTIL done OR (p = p0)
  END Loop;

END Oberon.