Skip to content

Instantly share code, notes, and snippets.

@jonpovey
Created May 6, 2012 03:25
Show Gist options
  • Select an option

  • Save jonpovey/2608343 to your computer and use it in GitHub Desktop.

Select an option

Save jonpovey/2608343 to your computer and use it in GitHub Desktop.

Revisions

  1. jonpovey revised this gist May 7, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion spec_1.7_irqs.txt
    Original file line number Diff line number Diff line change
    @@ -26,7 +26,7 @@ A couple of questions, and suggestions.
    "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 IA is only checked when dequeueing interrupts, if that
    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
  2. jonpovey revised this gist May 7, 2012. 1 changed file with 6 additions and 2 deletions.
    8 changes: 6 additions & 2 deletions spec_1.7_irqs.txt
    Original file line number Diff line number Diff line change
    @@ -25,6 +25,10 @@ A couple of questions, and suggestions.
    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 IA is only checked when dequeueing interrupts, 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
    @@ -39,15 +43,15 @@ 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.

    6. Change IAQ to IQH (Interrupt Queue Hold). Because it is specified that
    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.

    7. Instead of having IQH swap its argument with IH, replace IQH with IHG and
    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.

  3. jonpovey revised this gist May 6, 2012. 1 changed file with 45 additions and 30 deletions.
    75 changes: 45 additions & 30 deletions spec_1.7_irqs.txt
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@ A couple of questions, and suggestions.
    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 "No easy escape from DoS" below.
    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.
    @@ -22,12 +22,14 @@ A couple of questions, and suggestions.
    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. Some changes suggested:
    to mean different things. Fixing them involves some clunky phrases like
    "disable interrupt dequeueing", yuck (but see Bonus Round).

    A suggested revision to address all of the above is here:
    [ url ]
    or as a diff:
    [ url ]
    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 ================================================================

    @@ -42,31 +44,40 @@ churn.
    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 (ex IAQ) works on so that it's clearer and more
    concise to talk about.
    for the register that IQH works on so that it's clearer and more concise to
    talk about.

    7. 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 with all of the above is here:
    [ url ]
    or as a diff:
    [ url ]
    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.

    === No easy escape from DoS ====================================================
    === Rate-limited interrupts? ===================================================

    If you think that forcing at least one non-interrupt instruction to execute
    between interrupts helps avoid denial of service due to interrupt load, read
    this. Otherwise, skip it.
    between interrupts is a good idea, read this. Otherwise, skip it.

    Assuming non-interrupt code could progress 1 instruction per interrupt, and
    interrupt handler takes 10 cycles (for example). Here are some scenarios, I will
    use "user code" to mean regular non-interupt processing:
    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
    @@ -81,23 +92,21 @@ B. Interrupts come in at 1 interrupt per 10 cycles or faster. You can't handle
    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 discussed here:
    http://www.reddit.com/r/dcpu16/comments/t5wu5/foo/c4jxzkv
    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 something is capable of 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. Which might be desirable but that adds a little more
    complexity and state to worry about...

    Plus I kind of like the catching fire thing. It'll be pretty clear if you have
    an interrupt overload bug. This doesn't need to be designed like a failsafe
    industrial microcontroller.
    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 ==================================================================

    @@ -168,6 +177,12 @@ 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 and others.
    With thanks to hellige, Zgwortz-Steve, Toqu, AgentME and others.
  4. jonpovey revised this gist May 6, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion spec_1.7_irqs.txt
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,7 @@ A couple of questions, and suggestions.

    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, if needed.
    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. Some changes suggested:
  5. jonpovey created this gist May 6, 2012.
    173 changes: 173 additions & 0 deletions spec_1.7_irqs.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,173 @@
    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 "No easy escape from DoS" 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, if needed.

    5. There is some confusing use of terms like "queue" and "trigger" in the spec
    to mean different things. Some changes suggested:

    A suggested revision to address all of the above is here:
    [ url ]
    or as a diff:
    [ url ]

    === 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.

    6. 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 (ex IAQ) works on so that it's clearer and more
    concise to talk about.

    7. 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 with all of the above is here:
    [ url ]
    or as a diff:
    [ url ]

    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.

    === No easy escape from DoS ====================================================

    If you think that forcing at least one non-interrupt instruction to execute
    between interrupts helps avoid denial of service due to interrupt load, read
    this. Otherwise, skip it.

    Assuming non-interrupt code could progress 1 instruction per interrupt, and
    interrupt handler takes 10 cycles (for example). Here are some 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 discussed here:
    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 something is capable of 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. Which might be desirable but that adds a little more
    complexity and state to worry about...

    Plus I kind of like the catching fire thing. It'll be pretty clear if you have
    an interrupt overload bug. This 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.

    --
    This doc by [email protected]/blueshift/cheese_magnet
    With thanks to hellige, Zgwortz-Steve, Toqu and others.