Created
May 6, 2012 03:25
-
-
Save jonpovey/2608343 to your computer and use it in GitHub Desktop.
Clarification of interrupt things in DCPU spec 1.7
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Clarification of interrupt things in DCPU spec 1.7 | |
A couple of questions, and suggestions. | |
=== EXECUTIVE SUMMARY ========================================================== | |
1. Is hardware still notified about triggering of interrupts it has sent to | |
DCPU? It looks like that feature has been removed (good). | |
2. Can you clarify that if there are interrupts on queue, after RFI the | |
interrupt handler is immediately called again? This is the right way to do it | |
but line 174 of spec 1.7 seems to suggest otherwise: | |
"The DCPU-16 will perform at most one interrupt between each instruction." | |
I think that line is left over from an older spec and wants to be removed. | |
If in doubt see section "Rate-limited interrupts?" below. | |
3. What happens if INT is called with interrupt queueing enabled? | |
There is uncertainty, we think it should queue the interrupt. | |
See "Async INT" section of this doc if that is not the case. | |
4. Please tweak IAQ to swap current queueing status with its argument | |
instead of just read it. For literals, behaviour would be unchanged. | |
Reasoning behind this is discussed more later in this doc. | |
5. There is some confusing use of terms like "queue" and "trigger" in the spec | |
to mean different things. Fixing them involves some clunky phrases like | |
"disable interrupt dequeueing", yuck (but see Bonus Round). | |
6. If IA = 0, are interrupts dropped on arrival, before they go on the queue? | |
I have assumed that they are and the queue is always empty if IA = 0, if that | |
is wrong then my recommended specs below need rewording. | |
An intermediate revision to address only the above, with minimal instruction set | |
change (Note the clunky descriptions are improved in the Bonus Round shortly): | |
https://raw.github.com/jonpovey/das/irq-spec/dcpu-16.txt | |
or as a diff against Notch's 1.7: | |
https://github.com/jonpovey/das/compare/master...irq-spec#diff-0 | |
=== Bonus Round ================================================================ | |
None of these Bonus Round items are needed for extra functionality. The | |
important stuff is above, we can do everything we need if IAQ swaps state with | |
its argument. The following just improve spec clarity and consistency, and (7) | |
gives slightly more efficient and readable code - but does involve more spec | |
churn. | |
7. Change IAQ to IQH (Interrupt Queue Hold). Because it is specified that | |
multiple interrupts arriving at the same time go onto a queue, it is | |
confusing to talk about "enabling and disabling queueing". Putting things | |
on the queue is always enabled, it's taking them off that we want to control. | |
This is a name-only change; behaviour is the same. Also specify a name, IH, | |
for the register that IQH works on so that it's clearer and more concise to | |
talk about. | |
8. Instead of having IQH swap its argument with IH, replace IQH with IHG and | |
IHS (Interrupt Hold Get/Set) for simpler test and nest code. | |
This end up with IH/IHG/IHS which parallels IA/IAG/IAS, tidy. | |
A suggested revision that includes Bonus Round stuff is here: | |
https://raw.github.com/jonpovey/das/irq-extreme/dcpu-16.txt | |
or as a diff against Notch's 1.7: | |
https://github.com/jonpovey/das/compare/irq-extreme#diff-0 | |
The rest of this document is discussion of the above suggestions. I will use | |
terminology assuming the Bonus Round has been implemented, otherwise replace | |
"IH" with "interrupt queueing", "IHS" with "IAQ", etc. | |
=== Rate-limited interrupts? =================================================== | |
If you think that forcing at least one non-interrupt instruction to execute | |
between interrupts is a good idea, read this. Otherwise, skip it. | |
1. INT handler is occasionaly delayed even if dequeueing is enabled: | |
If one or more hardware interrupts arrive just before you call INT, a | |
hardware interrupt will be handled before the next instruction instead of the | |
software interrupt handler running like you expected; so you can't rely on any | |
results being there any time soon. This will happen rarely but sometimes. | |
Nightmare heisenbugs. This could be fixed by a spec change to say INT jumps the | |
queue, but multiple INT would have problems; see section "Async INT" further on. | |
2. It doesn't help avoid denial of service: | |
Assuming the interrupt handler takes 10 cycles (for example), here are the | |
scenarios, I will use "user code" to mean regular non-interupt processing: | |
A. Interrupts come in at a rate of less than 1 per 10 cycles on average. You can | |
handle them fast enough, and user code progresses anyway (doesn't have to be | |
forced by design). | |
B. Interrupts come in at 1 interrupt per 10 cycles or faster. You can't handle | |
them fast enough, your queue fills and you HCF, whether your user code | |
progresses 1 instruction in between or not. In fact if the interrupt rate | |
were exactly the same as the interrupt processing time, forcing a user | |
instruction would be the cause of the queue filling. Without a forced user | |
instruction you would survive, spending all your time handling interrupts | |
flat out. | |
Because of the above I would argue that requiring user code to advance 1 | |
instruction per interrupt is pointless, and leads to problems described above, | |
as well as possible side-effects when IA transitions from 0, for more see: | |
http://www.reddit.com/r/dcpu16/comments/t5wu5/foo/c4jxzkv | |
The right way to fix huge interrupt load is to either set up your hardware and | |
software correctly to not generate a flood of interrupts, or write your | |
interrupt handler defensively so it can detect handling too many interrupts in a | |
row and disable either the offending source or interrupts all together. | |
If some hardware is generating interrupts really really fast then a well-written | |
interrupt handler won't save you anyway, you're going to catch fire. | |
Tough titties, unless the "catch fire on queue overrun" was changed to setting | |
a flag somewhere - but I kind of like the catching fire thing. It'll be pretty | |
clear if you have an interrupt overload bug. DCPU doesn't need to be designed | |
like a failsafe industrial microcontroller. | |
=== Async INT ================================================================== | |
I think INT is normally expected to be used with interrupt queue hold disabled | |
(IH = 0). Considering what might happen if IH is nonzero: | |
If INT is specified to immediately run the interrupt handler regardless of IH, | |
this causes a problem if the handler ends with RFI as the queue will be | |
released inappropriately. As IH is always set at entry to interrupt handler | |
(which is good), it's not even possible to do an ugly workaround like | |
detecting IH state on entry and doing a manual POP PC return to avoid RFI. | |
We think that INT should be queued in this scenario. If the programmer is using | |
INT expecting results to be available immediately, they need to take care not to | |
call those interrupts when the queue is on hold. This should not trip up | |
beginners as doing things under IAQ/IHS is advanced use. Naively calling INT | |
inside an IAQ/IHS protected section and having it silently break open your | |
critical section is a far nastier source of heisenbugs waiting to happen. | |
Another option is to wallpaper the whole issue by specifying that INT when | |
IH is nonzero causes HCF. However async INT is not completely useless: | |
- You could use it to test chained interrupts work properly: | |
IHS 1 | |
INT 1 | |
INT 2 | |
INT 3 | |
IHS 0 ; release interrupts | |
; test INT 3's work is complete, and that interrupts fired in correct order | |
- Deliberately trigger queue overflow HCF, for fun, for evil, or to test your | |
emulator: | |
IHS 1 | |
INT 1 | |
SUB PC, 2 | |
- Use interrupts as a queue mechanism: You could write a processing loop section | |
that ran with IH set and called INT x to queue different events to itself for | |
later processing. If the processing loop was time-critical, then extra reason | |
for running with IH set. This would be using the interrupt queue as a kind of | |
free extra memory without having to use memory for your own event queue and | |
pointers. | |
- Other uses that devious future hackers may think up | |
=== What you can do with IHG / IAQ value-swap ================================== | |
IHG or tweaking IAQ to do value-swap would allow detection of interrupt hold | |
context, and support nested interrupt queue hold/release. | |
Detection is nice because you can write generic routines which behave | |
differently depending on context, for example an operation that requires | |
hardware interaction can't wait for hardware interrupt but can queue the | |
action instead. Another example is for optimisation, a routine can choose to | |
queue data for later processing if interrupts are held; less efficient, but | |
returns faster to reduce interrupt latency. | |
Nested hold/release is nice, you can write routines that have critical sections | |
they need to protect with IH = 1, and call those routines with IH in any state | |
and the routines will Do The Right Thing, i.e. not set IH = 0 when they | |
shouldn't. Written appropriately (e.g. using IH as a nesting counter or saving | |
IH on the stack) they can call other such functions recursively and IH will | |
only be set zero when the last layer of nesting is unraveled. | |
=== Background discussion ====================================================== | |
http://www.reddit.com/r/dcpu16/comments/t5wu5/ | |
Notch, thanks for your patience with us interrupt-lovers. | |
=== Postscript ================================================================= | |
If you made it this far: | |
Someone suggested I mention a discussion about IFA/IFU and IFG/IFL instructions: | |
http://www.reddit.com/r/dcpu16/comments/sxjvd/ | |
-- | |
This doc by [email protected]/blueshift/cheese_magnet | |
With thanks to hellige, Zgwortz-Steve, Toqu, AgentME and others. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment