ISR strangeness
Seairth
Posts: 2,474
in Propeller 2
Chip,
This one is for you. Trying to track down an issue that i described elsewhere, I ended up with the following code:
At this point, it's meaningless code, except that is seems to expose an issue.
If you run this code as it is, you will get one LED sequence. If you comment line mentioned in the code above, you will get a different sequence (ignoring the LED that's being toggled by the commented-out line). You will notice that the ISR has a long-running REP, which is only there to force the issue/bug. However, this should not impact the observed change in behavior of commenting out the annotated line.
This has been very elusive to pin down, so this test code is probably not as concise as it could be. But hopefully it's enough for you to spot the problem.
This one is for you. Trying to track down an issue that i described elsewhere, I ended up with the following code:
con sys_clk = 50_000_000 baud_rate = 115_200 rx_pin = 63 edg_int = 5 dat orgh org 0 or dirb, #$FF mov ijmp1, #rx_isr setedg #(%10_000000 | rx_pin) setint1 #edg_int rx_loop waitint notb outb, #6 ' COMMENT THIS INSTRUCTION TO SEE DIFFERENT INTERRUPT BEHAVIOR tjz rx_buff, #rx_loop mov rx_buff, #0 notb outb, #7 jmp #rx_loop '-------------------------------------------------------------------------------------------------- rx_isr setb outb, c add c, #1 tjnz rx_buff, #.ret mov rx_buff, #1 rep @.endrep, #10 ' make sure to run for much longer than we should waitx full_bit_time ' in the ISR. At least one more edg event will .endrep ' have occurred before the ISR completes. .ret reti1 '-------------------------------------------------------------------------------------------------- full_bit_time long sys_clk / baud_rate half_bit_time long (sys_clk / baud_rate) >> 1 c long 0 rx_buff res 1 rx_cnt res 1
At this point, it's meaningless code, except that is seems to expose an issue.
If you run this code as it is, you will get one LED sequence. If you comment line mentioned in the code above, you will get a different sequence (ignoring the LED that's being toggled by the commented-out line). You will notice that the ISR has a long-running REP, which is only there to force the issue/bug. However, this should not impact the observed change in behavior of commenting out the annotated line.
This has been very elusive to pin down, so this test code is probably not as concise as it could be. But hopefully it's enough for you to spot the problem.
Comments
Edit: that question is just an aside. When I replaced the rep in the above code with a djnz, it made no difference to the behavior I describe above. At least, none that I noticed...
Chip's documentation says:
I took that to mean there was an implied STALLI/ALLOWI wrapped around these.
There is no automatic STALLI/ALLOWI going on. Those only happen if you execute them in your code.
WAITINT is waiting for an interrupt. The next instruction is already in the pipeline. WAITINT stops waiting when an interrupt occurs. The next instruction executes, while the interrupt CALLD is being injected into the pipeline. The next instruction that executes is CALLD.
So, the instruction after WAITINT always executes before the interrupt branch occurs.
Doh! Of course!
That was exactly my problem. Both in this code and in the code in FDS demo with interrupts.
Thanks for spotting that!
Thanks for reminding me, by the way. I really should go read that document before I ask any more questions.
Well, the doc's never said anything about WAITCNT executing the next instruction before the interrupt branch. I'll add that today.
No, that's not true. The only reason I needed a NOP was because the next instruction (TJZ, in this case) that followed was dependent on a state change in an ISR. Except for this case (so far), a spacer instruction is not necessary.
Let me think about this.
I'd need to cancel the instruction in the pipe and inhibit the program counter from advancing, so that the return address is correct. It may be just a few gates. I'll look at it today.
I don't think this is necessary. Documentation of this particular case should be good enough.
If it turns out to be simple enough to do, it would save the need to explain. As instructions go, it is maybe the oddest.
Please don't do it. Instead, could you make the assembler automatically add a NOP unless the user explicitly specifies otherwise?
Are you saying that you WANT that behavior? Right now, you could follow WAITCNT with a STALLI, some code, then ALLOWI. That would make it possible to always execute some bit of code before the interrupt branch occurs.
If you could magically snap your fingers and have it either way, which would you prefer? I'm thinking that to make it work as people would expect it to is pretty simple - don't increment the PC on WAITINT and cancel the next instruction already in the pipe.
'No surprises' is always best, but there does seem to be an Assembler level work around possible here ?
A safe WAITINT would insert a NOP and there could be a Queued (WAIQINT ?) that trims the NOP, for experienced users who are very tight on code space, or know that the next opcode does not expect any INT updated information.
That STALLI trick is clever! I'm not sure how it could be used, but that is a unique and interesting feature!
Leave things as they are.
Idea: If you were to add a GETINTS instruction that non-destructively returned the 16 event flags and 3 interrupt flags, that STALLI trick might have all sorts of uses!
Edit: there would also be room in GETINTS to return the three 4-bit interrupt configuration values.
Wow! We actually already have that in the form of SETBRK during a debug interrupt. I could easily make it always available through another instruction. It would just be a few logic gates, no mux's.
Also, we should determine three more interrupt events to fill slots 13..16. Those would be freebies, too.
Chip, I edited my last post with an extension to the idea. Did you see it?
Yes, it's already in there!
We'd need to reduce the INTx state reporting to 1 bit each (inactive vs. waiting/executing), in order to fit 3 more event trap bits.
Would this be useful, or just an invitation to madness? My friend Walter Banks said that some features are akin to a cannon pointed at the users' feet.
Possibly also a poll for a rare, but more important case ?
Some ideas:
- hardware stack full (i.e. pushing again would overflow)
- hardware stack empty (i.e. popping again would underflow)
- PTRx out of range (again, for stack)
- cog X reached debug breakpoint (can already be done using trigger longs and debug ISR)
- more timers
Yes! That's what I was kind of thinking.
I still like the idea of an out-of-bound HUB fetch interrupt, which would allow SW DLL type memory management, and make the 512k less of a hard ceiling.
Large Serial Flash is very cheap, and it is getting faster to access.
eg P1 type speed running from external flash may be possible.
It is common to frequently run a stack with no-entries.
A trap on actual underflow/overflow would be more useful
opens the door to some external memory management.
Enjoy!
Mike
No. It would only trigger immediately after the pop that removed the last entry from the stack. Likewise, the stack-full interrupt would only trigger immediately after the push that filled the last entry.
A trap on actual underflow/overflow would probably require radically changing how interrupts work. None of the other interrupts retry the instruction that caused them.
I'm not sure where retry comes from ?
To me, limit checks are just against some number, so do not vary greatly in logic.
A trap that fires on an actual fault, is more useful than one that fires on normally running code.
I guess a workaround is to prefill the stack just to avoid the Trap in normal code, but that consumes 1 stack level.