Skip to content

Instantly share code, notes, and snippets.

@jonpovey
Created May 6, 2012 03:25
Show Gist options
  • Save jonpovey/2608343 to your computer and use it in GitHub Desktop.
Save jonpovey/2608343 to your computer and use it in GitHub Desktop.
Clarification of interrupt things in DCPU spec 1.7
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 jon@leetfighter.com/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