[comp.realtime] The 8051MX 8-bit multi-tasking microprocessor

markh@csd4.csd.uwm.edu (Mark William Hopkins) (05/20/91)

The 8051MX: a real-time 8-bit multitasking controller.

(0) OVERVIEW
   In case you've never heard of the 8051MX, don't feel stumped.  It doesn't
exist, I just made it up! :) Hence this article: a new chip proposal.
   The 8051MX is an upwardly compatible extension of the 8051 8-bit controller
that incorporates my real-time multitasking kernel.  This kernel is so damn
simple, and so small, that it seems to make perfect sense to see it implemented
in hardware.  The difference, however, is that in hardware it is possible to
check for conflicts between processes, whereas the software kernel cannot
effectively do that.
   The best way to describe this architecture is: A Signal-Driven Threaded
Multi-Processor.

(1) ARCHITECTURE
   These are the primitive operations of the MX extension of the 8051 chip:

                             pause #Sig
                             resume #Sig

where "Sig" is a "signal-descriptor" used to mediate task-switching.  There are
16 in all, but the first several should be thought of as being associated with
hardware interrupt flags.  So compatible assemblers will have options that
enable the automatic generation of the appropriate interrupt-handlers and
perhaps interrupt-initialization code on reset.

   First, "pause #Sig" will freeze the current process, placing it in a queue
associated with this particular signal, Sig.  The chip can be designed to have
a queue capacity of only 1 (for exclusive access), or a larger limited number
(for chaining processes onto a signal), if feasible.  This process will be
frozen until the corresponding "resume #Sig" is called, and then it resumes
right after where the pause was called.

   When the process "resume"-ed either returns or pauses again, then control
returns at the point after the "resume" was called.  Therefore, resume is an
indirect call of sorts.

                             spawn DPTR

sets up a stack segment for the process pointed to by the DPTR register and
then initiates it.  Spawn "returns" when this process executes a pause or a
return, and the process ID is returned in a new special function register.
Setting a flag will allow the user to define the address for the "spawn"-ed
process to exit to, else a default exit routine "_Exit" is carried out on
return.

There are a preset number of process ID's available (8), numbered from 0 and
up, and if there is no available process, the value -1 (ff, in hex) is returned.

                              exit #PID

causes the process indicated by the process ID (PID) to abort, if it is
currently active.  Therefore, next time this process is "resume"-ed, it will
jump immediately to its predefined exit address.

                               getpid

sets the special function register mentioned above to the current process ID.

                             status #PID

returns the status of the process indicated into a special function register.

   A Priority Stack is used to enable task preemption in a way that is
consistent with the interrupt priority structure.  Both pause and exit will
pop the next process to go to off this stack, and both resume and spawn
push the current process onto the stack.

   The stack, and internal registers are all initialized on reset.  Exiting
from the main process is equivalent to jumping to an infininte idle loop,
and puts the chip into idle mode.  Effectively, the chip is ALWAYS considered
idle, with "main" being spawned on a reset.  Interrupt-handlers, of course,
are still active even after the main process exits, so that processes that are
in the background are still in effect.

   All the new instructions are implemented with the reserved A5 opcode (which
is all you have left on this chip :)).

(2) EXAMPLE
   Suppose you have a keypad hooked up to the chip, with external interrupt 0
used to flag a key-press, and the data from the key-pad going into port 2.
Using the multi-tasking feature above, reading a key will be as simple as this:

GetKey:
   pause #SIG_IE0
   mov A, P2
ret

where the user or assembler defines SIG_IE0 and sets up the appropriate
interrupt-handler:

ORG 3
   resume SIG_IE0
reti

Hey!  It's that easy, but now you don't have to worry about manually
threading all your processes and idle-loops together.  The hardware does
it for you!  So you can concentrate on just writing straight-line code
without the nasty asynchronism rearing its ugly head.

Of course, you still have to worry about initializing the interrupt mode and
enabling it.

(3) COMMAND SHELL
   Most development 8051 chips will come with a BASIC interpreter masked
into internal ROM.  This interpreter effectively implements a jump table
for commands, and intercepts interrupts with its own set of interrupt
handlers and "vectors" user interrupt-handlers out to some other fixed
address.

   But now, you have the potential to have an extended BASIC interpreter that
effectively behaves like a command shell.  There are no longer any user-defined
interrupt handler addresses, as all interrupts now are tied to given "signals".
The programmer only needs to use the "pause" statement on that signal to create
an interrupt handler in his or her application.  No need to worry about
vectoring to the appropriate address.
   Secondly, the interpreter now has a library of routines that can be called
in a relatively easy way by using the "resume" statement with the appropriate
signal ... much the same way that library functions are called in the IBM PC
using the 8086's INT command.

   The pause, resume, spawn, etc, are all defined at the user level in the BASIC
as well.

   Thus, you now have a command shell to a small real-time operating system
that is capable of multi-processing.