
www.Usenet.com
| <-- __Chronological__ --> | <-- __Thread__ --> |
Nick Maclaren <[EMAIL PROTECTED]> wrote:
+---------------
| My solution to that was an architectural requirement for a 'yield'
| instruction to be called every (say) M instructions or N memory
| references, whichever comes first, and to abort the process if it
| failed to do so. Dead easy to implement.
+---------------
Ahhh... Sutherland's "Wheel of Reincarnation" strikes again... ;-} ;-}
In 1972, at Digital Communications Associates, I designed the kernel
for a realtime embedded operating system for a networking node which was
based on *precisely* that notion: The system ran with interrupts *OFF*,
and the programmers were required to insert "@YIELD" macros[1] every
so often[2] in the code. The "@YIELD" macro was logically a no-op[3]
unless some I/O event had occurred that needed to run a task of higher
priority than the current task. Worked like a charm!!
Despite the fact that the @YIELDs were inserted manually, since the rules
were very simple[2] and compliance was easy to verify by inspection, we
had almost no bugs due to misplaced @YIELDs. [Lots of *other* bugs, but
not those.]
Actually, inserting the @YIELDs manually provided a significant coding
advantage: one was guaranteed that an interrupt *wouldn't* occur anywhere
else, and thus one didn't have to worry about critical sections when
manipulating shared data or using extended CPU register state (e.g.,
the "MQ" register, etc.) -- everything between sucessive calls to @YIELD
was a critical section. Conversely, @YIELD didn't have to save/restore
anything but the PC and the VM page table base register[5], so it ran
faster, too.
The resulting product had *great* performance (for the time), actually
quite a bit faster than DEC's own PDP-11-based network processor... ;-}
-Rob
[1] The platform was a DEC PDP-8. The code was written in assembler,
with a pre-pass of the code through the "8BAL" macro processor.
[8BAL macros started with "@", hence the "@YIELD" above.]
[2] The system was handling a large number of asynchronous and synchronous
serial ports without DMA, which required character-at-a-time service
within a small latency to avoid overflowing (or for output, underrunning)
some fairly small FIFOs, so we chose 200 cycles as the absolute maximum
between @YIELDs. Also, to avoid accidents, we also required that any
loop should contain at least one @YIELD in its body (even short ones).
[On a PDP-8, instructions (other than I/O) would take 1-5 cycles,
depending on mem-ref or not, indirection or not, and RMW or not
(all known at the time of coding). The coding style was such that
subroutines were usually less than ~50 instructions anyway. So the
rule of "at least one @YIELD per 200 cycles or loop, whichever comes
first" was easy to follow.]
[3] Well, actually, since on the PDP-8 enabling/disabling interrupts was
a *lot* cheaper than even calling a simple subroutine -- not to mention
polling several I/O devices, the @YIELD macro actually expanded into
the following three instructions:
ION ; turn interrupts on
CLA ; Clear the AC (delay slot for ION)
IOF ; turn interrupts off again
which had the side-effect of clearing the accumulator. But that
was o.k., since one needed quite a few CLAs anyway in PDP-8 code,
so @YIELD was documented to do that. If no new interrupt request
had been asserted since the last @YIELD, the above took only 3 cycles,
which was the same as a single "ISZ <mem_loc>".[4]
[4] "Increment memory location and Skip next instruction if result is Zero".
[5] Oh, yeah, we also built an add-on board for the PDP-8 that added a
*small* amount of virtual memory to the system (8 pages of 8 words
each), which was used to alleviate the problem of having 15 bits of
address space on a machine with only 12-bit pointers (and no index
registers)! But that's a story for another time...
-----
Rob Warnock <[EMAIL PROTECTED]>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
| <-- __Chronological__ --> | <-- __Thread__ --> |