Reply by Niklas Holsti January 16, 20122012-01-16
A small correction to my own post:

On 12-01-16 01:11 , Niklas Holsti wrote:

> To test if interrupts can take effect after the instruction "bit set > IMASK",
I meant to write, 'after the instruction AFTER the "bit set IMASK"', but I omitted the second "after". Oops. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .
Reply by Niklas Holsti January 15, 20122012-01-15
On 12-01-15 22:24 , alb wrote:
> On 1/15/2012 3:17 PM, Niklas Holsti wrote: >> "Effect latency" means that the "bit set IMASK" does not take effect at >> once, but only after a delay -- in this case, one cycle, which is >> usually one instruction. See the table in the 21020 UM in section >> 3.1.2.1. The UM also says in section E.2: >> >> "A write to any system register except USTAT1 or USTAT2 has one cycle of >> latency before any changes are effective." >> >> This means that an interrupt may be handled after the "bit set IMASK" >> instruction, and perhaps even after the next instruction -- this is not >> entirely clear (to me) from the UM. >>
...
>> >> Suppose that foo() should increment a counter atomically. If the >> assembly code is like this: >> >> bit set IMASK ... // mask interrupts >> load counter into register >> increment register >> store register into counter >> bit clear IMASK ... // unmask interrupts >> >> then the 1-cycle effect latency means that foo() may be interrupted >> between the "bit set" and the "load counter", which is harmless because >> the counter is not yet touched, but perhaps foo() can be interrupted >> also after the "load counter", which would break the atomicity. Putting >> at least one "nop" after "bit set" gives the new IMASK time to take >> effect before the atomic region starts. >> > > Hence the NOPs guarantee that if any interrupt is occurring it will > interrupt a nop operation
I would say, it interrupts "at a nop"; it does not interrupt the nop itself.
> rather than my "uninterruptable" part of the > code.
Exactly. The essential point is that the interrupt occurs before the first instruction of the uninterruptible part.
> But if I then understood correctly the UM I should be only worried > about the latency in system register write operation, no other latency > is involved.
For interrupt masking, in order to create uninterruptible code regions, I agree that the "effect latency" is the only important latency. But for computing the interrupt response time, you must of course consider other latencies, too.
> If this is the case one NOP will serve the purpose.
I think so, since the "effect latency" for IMASK is one cycle. But one more NOP will not do much harm. ...
>> The same effect latency applies to unmasking, but it usually does no >> harm, since it only delays the handling of any pending interrupt when it >> is unmasked. In unusual cases, after unmasking an interrupt you may need >> to be sure that any pending interrupt is actually handled, before the >> code does something else, and then you have to take the effect latency >> into account also for unmasking, and add a "nop" after the "bit clear >> IMASK". > > Given the aforementioned latency, any part of code can be executed after > the interrupt enabling and is guaranteed that pending interrupt will be > served. > > n : bit clr IMASK > n+1: bit set IMASK > n+2: nop > > in this extreme case were interrupts are enabled and then disabled right > after there's still one cycle (n+1) were pending interrupt are served, > since IMASK will only be set in n+2.
That would be my guess, but I would not trust it for such an extreme case, without testing.
>> Note that I haven't tested any of this, but my understanding of "effect >> latency" seems to match that of Glen and Mac. >> > > Now I'm curious, how would you test this if you were to write the code?
To test if interrupts can take effect after the instruction "bit set IMASK", I would write an interrupt handler that sets a variable "flag" to 1, and another function that is called repeatedly and has this form: bit set IMASK ... // mask interrupt store zero in flag load flag if flag is not zero report that an interrupt can occur after "store zero in flag" bit clear IMASK ... // unmask interrupt I would let the code run for a while, while feeding a large number of interrupts into it. I would expect some reports from the next-to-last line, above, when an interrupt is handled between "store zero in flag" and "if flag is not zero". To test that the "nop" after "bit set IMASK" works, I would run the same code, modified to have a nop between "bit set IMASK" and "store zero in flag". This time, I would expect no reports from the next-to-last line. I don't see any easy way to test if the code can be interrupted immediately after "bit set IMASK" -- the tests that I can think of cannot separate an interrupt immediately before this insruction, from one immediately after. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .
Reply by alb January 15, 20122012-01-15
On 1/15/2012 3:17 PM, Niklas Holsti wrote:
> "Effect latency" means that the "bit set IMASK" does not take effect at > once, but only after a delay -- in this case, one cycle, which is > usually one instruction. See the table in the 21020 UM in section > 3.1.2.1. The UM also says in section E.2: > > "A write to any system register except USTAT1 or USTAT2 has one cycle of > latency before any changes are effective." > > This means that an interrupt may be handled after the "bit set IMASK" > instruction, and perhaps even after the next instruction -- this is not > entirely clear (to me) from the UM. >
Thanks for the explanation, it seems to me clearer now.
>> This is how I would have set a >> function to be uninterruptable: >> >> foo() { >> DISABLE_INTERRUPTS(); >> // do foo stuff here >> ... >> ENABLE_INTERRUPTS(); >> } > > Suppose that foo() should increment a counter atomically. If the > assembly code is like this: > > bit set IMASK ... // mask interrupts > load counter into register > increment register > store register into counter > bit clear IMASK ... // unmask interrupts > > then the 1-cycle effect latency means that foo() may be interrupted > between the "bit set" and the "load counter", which is harmless because > the counter is not yet touched, but perhaps foo() can be interrupted > also after the "load counter", which would break the atomicity. Putting > at least one "nop" after "bit set" gives the new IMASK time to take > effect before the atomic region starts. >
Hence the NOPs guarantee that if any interrupt is occurring it will interrupt a nop operation rather than my "uninterruptable" part of the code. But if I then understood correctly the UM I should be only worried about the latency in system register write operation, no other latency is involved. If this is the case one NOP will serve the purpose.
>> >> Interrupt latency is documented in the document as follows: >> >>> 3.6.1 Interrupt Latency >>> The ADSP-21020 responds to interrupts in three stages: synchronization >>> and latching (1 cycle), recognition (1 cycle), and branching to the >>> interrupt >>> vector (2 cycles). See Figure 3.10 on the next page. If an interrupt >>> is forced >>> in software by a write to a bit in IRPTL, it is recognized in the >>> following >>> cycle, and the two cycles of branching to the interrupt vector follow >>> that. >> >> Therefore, when I DISABLE_INTERRUPTS I would *assume* that as soon as >> IMASK is set in cycle n, in n+1 the hardware will still latch the >> interrupt but not recognize and not branch to the interrupt vector. > > The effect latency on writing to IMASK means that this assumption is > wrong, and the interrupt is not effectively masked until cycle n+2 or n+3. > >> On the contrary, when I ENABLE_INTERRUPT, I would *assume* that as soon >> as IMASK is clear in cycle n, in n+1 the hardware will latch the >> interrupt, n+2 will recognize and n+3 will branch to interrupt vector. > > The same effect latency applies to unmasking, but it usually does no > harm, since it only delays the handling of any pending interrupt when it > is unmasked. In unusual cases, after unmasking an interrupt you may need > to be sure that any pending interrupt is actually handled, before the > code does something else, and then you have to take the effect latency > into account also for unmasking, and add a "nop" after the "bit clear > IMASK".
Given the aforementioned latency, any part of code can be executed after the interrupt enabling and is guaranteed that pending interrupt will be served. n : bit clr IMASK n+1: bit set IMASK n+2: nop in this extreme case were interrupts are enabled and then disabled right after there's still one cycle (n+1) were pending interrupt are served, since IMASK will only be set in n+2.
> > Note that I haven't tested any of this, but my understanding of "effect > latency" seems to match that of Glen and Mac. >
Now I'm curious, how would you test this if you were to write the code?
Reply by Niklas Holsti January 15, 20122012-01-15
On 12-01-15 13:24 , alb wrote:
> On 1/13/2012 10:22 PM, glen herrmannsfeldt wrote: >> In comp.dsp Mac Decman<dearman.mark@gmail.com> wrote: >> >>>> On 1/11/2012 11:45 PM, Mac Decman wrote: >> [snip] >>>>> #define ENABLE_INTERRUPTS() asm("bit set IMASK SOMEBITS; nop; nop;") >> >>>> Why the nops? I was just about doing something similar, but with no nops. >> >>> Not sure how many it takes on your processor. But I believe IMASK >>> like MODE1 has an effect latancy of one or two. >>
....
> > Uhm, I guess I did not understood that.
"Effect latency" means that the "bit set IMASK" does not take effect at once, but only after a delay -- in this case, one cycle, which is usually one instruction. See the table in the 21020 UM in section 3.1.2.1. The UM also says in section E.2: "A write to any system register except USTAT1 or USTAT2 has one cycle of latency before any changes are effective." This means that an interrupt may be handled after the "bit set IMASK" instruction, and perhaps even after the next instruction -- this is not entirely clear (to me) from the UM.
> This is how I would have set a > function to be uninterruptable: > > foo() { > DISABLE_INTERRUPTS(); > // do foo stuff here > ... > ENABLE_INTERRUPTS(); > }
Suppose that foo() should increment a counter atomically. If the assembly code is like this: bit set IMASK ... // mask interrupts load counter into register increment register store register into counter bit clear IMASK ... // unmask interrupts then the 1-cycle effect latency means that foo() may be interrupted between the "bit set" and the "load counter", which is harmless because the counter is not yet touched, but perhaps foo() can be interrupted also after the "load counter", which would break the atomicity. Putting at least one "nop" after "bit set" gives the new IMASK time to take effect before the atomic region starts.
> > Interrupt latency is documented in the document as follows: > >> 3.6.1 Interrupt Latency >> The ADSP-21020 responds to interrupts in three stages: synchronization >> and latching (1 cycle), recognition (1 cycle), and branching to the interrupt >> vector (2 cycles). See Figure 3.10 on the next page. If an interrupt is forced >> in software by a write to a bit in IRPTL, it is recognized in the following >> cycle, and the two cycles of branching to the interrupt vector follow that. > > Therefore, when I DISABLE_INTERRUPTS I would *assume* that as soon as > IMASK is set in cycle n, in n+1 the hardware will still latch the > interrupt but not recognize and not branch to the interrupt vector.
The effect latency on writing to IMASK means that this assumption is wrong, and the interrupt is not effectively masked until cycle n+2 or n+3.
> On the contrary, when I ENABLE_INTERRUPT, I would *assume* that as soon > as IMASK is clear in cycle n, in n+1 the hardware will latch the > interrupt, n+2 will recognize and n+3 will branch to interrupt vector.
The same effect latency applies to unmasking, but it usually does no harm, since it only delays the handling of any pending interrupt when it is unmasked. In unusual cases, after unmasking an interrupt you may need to be sure that any pending interrupt is actually handled, before the code does something else, and then you have to take the effect latency into account also for unmasking, and add a "nop" after the "bit clear IMASK". Note that I haven't tested any of this, but my understanding of "effect latency" seems to match that of Glen and Mac. -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .
Reply by alb January 15, 20122012-01-15
On 1/13/2012 10:22 PM, glen herrmannsfeldt wrote:
> In comp.dsp Mac Decman <dearman.mark@gmail.com> wrote: > >>> On 1/11/2012 11:45 PM, Mac Decman wrote: > [snip] >>>> #define ENABLE_INTERRUPTS() asm("bit set IMASK SOMEBITS; nop; nop;") > >>> Why the nops? I was just about doing something similar, but with no nops. > >> Not sure how many it takes on your processor. But I believe IMASK >> like MODE1 has an effect latancy of one or two. > > If I remember, for some processors that is intentional. You want to be > able to enable interrupts before returning from the interrupt > routine, but not until after the return. (I might be remembering the > 8080.) If it comes too soon, you will overflow the stack!
Uhm, I guess I did not understood that. This is how I would have set a function to be uninterruptable: foo() { DISABLE_INTERRUPTS(); // do foo stuff here ... ENABLE_INTERRUPTS(); } Interrupt latency is documented in the document as follows:
> 3.6.1 Interrupt Latency > The ADSP-21020 responds to interrupts in three stages: synchronization > and latching (1 cycle), recognition (1 cycle), and branching to the interrupt > vector (2 cycles). See Figure 3.10 on the next page. If an interrupt is forced > in software by a write to a bit in IRPTL, it is recognized in the following > cycle, and the two cycles of branching to the interrupt vector follow that.
Therefore, when I DISABLE_INTERRUPTS I would *assume* that as soon as IMASK is set in cycle n, in n+1 the hardware will still latch the interrupt but not recognize and not branch to the interrupt vector. On the contrary, when I ENABLE_INTERRUPT, I would *assume* that as soon as IMASK is clear in cycle n, in n+1 the hardware will latch the interrupt, n+2 will recognize and n+3 will branch to interrupt vector. Again quoting the book:
> To process an interrupt, the program sequencer performs the following > actions: > 1. Outputs the appropriate interrupt vector on the program memory > address. > 2. Pushes the current PC value (return address) on the PC stack. > 3. If the interrupt is either an external interrupt (IRQ3-0) or the internal > timer interrupt, the program sequencer pushes the current ASTAT and > MODE1 registers on the status stack. > 4. Alters the interrupt mask pointer (IMASKP) to reflect the current > interrupt nesting state. The nesting mode (NESTM) bit in the MODE1 > register determines whether all interrupts or only lower priority > interrupts are masked during the service routine.
Given the fact that I'm dealing with IRQ3-0 only, why would I care about the stack? I feel I'm missing something here.
> > In the disable case, if you want to be sure, you would need the NOP. > > For the enable case, though, it would seem to me that it wouldn't > cause much of a problem. Unless you specifically needed to be > interrupted, a delay of a few insructions shouldn't be a problem. >
Uhm, I think I have not understood you clearly. Are you talking about enabling interrupts at the end of the interrupt service routine? In this case the two nops will need to be executed before the RTI instruction, hence no matter how many nops you put you may overflow the stack, since another interrupt may be coming before you returned from the current one. Am I totally off? I need to say that my understanding is still very hazy, so it might be I completely missed your point. I will definitely need to understand these details better before implementing anything.
> -- glen >
Reply by lang...@fonz.dk January 13, 20122012-01-13
On 11 Jan., 23:45, Mac Decman <dearman.m...@gmail.com> wrote:
> On Wed, 11 Jan 2012 12:23:27 +0100, Alessandro Basili > > > > <alessandro.bas...@cern.ch> wrote: > >On 1/11/2012 9:52 AM, Mac Decman wrote: > >[...] > > [snip] > > >It's not clear yet to me whether the interrupt are latched (IRPTL > >updated) if interrupts are disabled (IRPTEN=3D0). By the way, the target > >is an ADSP21020. > [snip] > > >> Mark DeArman > > For example > #define ENABLE_INTERRUPTS() =A0 asm("bit set IMASK SOMEBITS; nop; nop;") > #define DISABLE_INTERRUPTS() =A0asm("bit clr IMASK SOMEBITS; nop; nop;") > > Mark DeArman
that will eventually break, it assumes that imask is always the same "somebits", you need to set imask to what it was before you changed it, or you might be enabling/diabling interrupts that code elsewhere has disabled/enabled I haven't used that dsp, but I'm quite sure that the irpten bit is the right way to do it, just remember not to enable interrupts unless they were enabled when you disabled them -Lasse
Reply by glen herrmannsfeldt January 13, 20122012-01-13
In comp.dsp Mac Decman <dearman.mark@gmail.com> wrote:

>>On 1/11/2012 11:45 PM, Mac Decman wrote:
[snip]
>>> #define ENABLE_INTERRUPTS() asm("bit set IMASK SOMEBITS; nop; nop;")
>>Why the nops? I was just about doing something similar, but with no nops.
> Not sure how many it takes on your processor. But I believe IMASK > like MODE1 has an effect latancy of one or two.
If I remember, for some processors that is intentional. You want to be able to enable interrupts before returning from the interrupt routine, but not until after the return. (I might be remembering the 8080.) If it comes too soon, you will overflow the stack! In the disable case, if you want to be sure, you would need the NOP. For the enable case, though, it would seem to me that it wouldn't cause much of a problem. Unless you specifically needed to be interrupted, a delay of a few insructions shouldn't be a problem. -- glen
Reply by Mac Decman January 13, 20122012-01-13
On Fri, 13 Jan 2012 13:55:36 +0100, alb <alessandro.basili@cern.ch>
wrote:

>On 1/11/2012 11:45 PM, Mac Decman wrote: >[snip] >> For example >> #define ENABLE_INTERRUPTS() asm("bit set IMASK SOMEBITS; nop; nop;") >> #define DISABLE_INTERRUPTS() asm("bit clr IMASK SOMEBITS; nop; nop;") >> > >Why the nops? I was just about doing something similar, but with no nops. >
Not sure how many it takes on your processor. But I believe IMASK like MODE1 has an effect latancy of one or two. Mark DeArman
Reply by Mac Decman January 13, 20122012-01-13
On Fri, 13 Jan 2012 12:41:08 +0100, alb <alessandro.basili@cern.ch>
wrote:

>I never asked "what is the pragma to >disable interrupts yet continue to latch them".
Haha, Sorry I guess that is what I kept reading when I read your post.
> >> The Analog Devices parts do not latch Interrupts when IRPTEN=0 or >> atleast don't respond to them. That is on the same page of the >> manual. >>
Yes it was not on the same page. Infact, I'm not sure where it is. But if memory serves when IRPTEN=0 the sequencer clears the latch register on the next cycle. Mark DeArman
Reply by alb January 13, 20122012-01-13
On 1/11/2012 11:45 PM, Mac Decman wrote:
[snip]
> For example > #define ENABLE_INTERRUPTS() asm("bit set IMASK SOMEBITS; nop; nop;") > #define DISABLE_INTERRUPTS() asm("bit clr IMASK SOMEBITS; nop; nop;") >
Why the nops? I was just about doing something similar, but with no nops.
> Mark DeArman