I'm currently working on a time critical design using normal interrupt and fast interrupt, I need to write my own interrupt handler because I used assembly code and C code in interrupt, and currently pragma interrupt only handle the CPU register that C code used on my project.
I know CW provide 3 kind of interrupt handle pragma.
- interrupt on : only handle C code, can only generate rti at last
- interrupt saveall on: save too much register, can only generate rti at last
- interrupt fast on : only handle C code, can only generate frtid at last
I can use neither these 3 kind interrupt handler.
I tried to write my own interrupt handler, but CW always determine my handler as a subroutine, it will modify stack and CPU register that I don't need at the top and the end.
my code:
inline void FastISRRegSave (void)
{
asm(
adda #<2,sp
move.l a2,x:(sp)+
move.l a10,x:(sp)+
move.l b2,x:(sp)+
move.l b10,x:(sp)+
move.l c2,x:(sp)+
move.l c10,x:(sp)+
move.l d2,x:(sp)+
move.l d10,x:(sp)+
move.l x0,x:(sp)+
bfset #$ffff,m01
);
}
inline void FastISRRegRestore (void)
{
asm(
move.l x:(sp)-,x0
move.l x:(sp)-,d
move.l x:(sp)-,d2
move.l x:(sp)-,c
move.l x:(sp)-,c2
move.l x:(sp)-,b
move.l x:(sp)-,b2
move.l x:(sp)-,a
move.l x:(sp)-,a2
frtid
nop
nop
);
}
compiler code:
0x00000000 FivINT_eFlexPWMA_CMP2:
0x00000000 0x827B adda #0x000002,SP
0x00000001 0xD22B move.l C10,X:(SP)+
0x00000002 0xDD3F move.l R5,X:(SP)
; 290: FastISRRegSave();
; 291:
0x00000003 0x827B adda #0x000002,SP
0x00000004 0xE500 move.l A2,X:(SP)+
0x00000005 0xD02B move.l A10,X:(SP)+
0x00000006 0xE501 move.l B2,X:(SP)+
0x00000007 0xD12B move.l B10,X:(SP)+
0x00000008 0xE502 move.l C2,X:(SP)+
0x00000009 0xD22B move.l C10,X:(SP)+
0x0000000A 0xE503 move.l D2,X:(SP)+
0x0000000B 0xD32B move.l D10,X:(SP)+
0x0000000C 0xE504 move.l X0,X:(SP)+
0x0000000D 0x835AFFFF bfset #0xffff,M01
FastISRRegRestore();
0x0000013B 0xE514 move.l X:(SP)-,X0
0x0000013C 0xF33B move.l X:(SP)-,D
0x0000013D 0xE513 move.l X:(SP)-,D2
0x0000013E 0xF23B move.l X:(SP)-,C
0x0000013F 0xE512 move.l X:(SP)-,C2
0x00000140 0xF13B move.l X:(SP)-,B
0x00000141 0xE511 move.l X:(SP)-,B2
0x00000142 0xF03B move.l X:(SP)-,A
0x00000143 0xE510 move.l X:(SP)-,A2
0x00000144 0xE71A frtid
0x00000145 0xE700 nop
0x00000146 0xE700 nop
0x00000147 0xFD3B move.l X:(SP)-,R5
0x00000148 0xF23B move.l X:(SP)-,C
0x00000149 0xE708 rts
Is there any way to make DSC compiler do not generate these code?
If you use the latest compiler, "#pragma interrupt" and its variants also handle cpu registers inside inline code
(previous versions had some quirks especially with Y,Y1,Y0, you had to save and restore Y0 and Y1 to be sure Y was saved and restored properly).
What they NOT handle is what happens in function called by the ISR, that's why if speed is not your main concern it is simpler to use
"#pragma interrupt alignsp saveall".
If instead you want to improve on that, use "#pragma interrupt" or "#pragma interrupt alignsp" for the ISR function and use "#pragma interrupt called" on each of the functions called by ISR (this way functions "interrupt called" will autosave the registers they use).
If you want to fully control ISR prolog/epilog and autosaves, code it in a separate .asm file.
If you have super-critical ISR where you want optimize to the last clock cycle, remember that "fast interrupts" use shadow registers (swapped on fast interrupt entry and later swapped back when executing frts/frtsd) so if you code the ISR in assembly and only use the swapped registers, you don't have to save them.
You can use the same trick in a normal ISR by calling SWAP SWADOWS on entry and then SWAP SHADOWS again before rts/rtsd.
See "9.3.2.2 Fast Interrupt Processing", SWAP SHADOWS instruction description in "DSP56800E and DSP56800EX Reference Manual" ( DSP56800ERM.pdf ) automatically installed by Codewarrior for MCU or downloadable from NXP webside.
On 56800E cores SWAP SHADOWS swaps R0, R1, N, and M01 registers with their shadow copies.
On 56800EX cores it swaps R0,R1,R2,R3,R4,R5, N, N3, and M01 registers with their shadow copies.
ALL THIS IN 1 CLOCK CYCLE !!!!!!
This is very useful if you code your ISR in pure assembly (i.e. in an .asm file) because you don't have to save/restore lots of pointer registers (and M01 if modulo addressing is used in your code or in something you link with your code).
Lastly, if you really need maximum performance and you know nothing else except your code uses fast interrupts and/or SWAP SHADOWS, you can use the shadow registers like sort of "global interrupt variables".
That is:
1)
at startup (and with interrupts disabled) you initialize the shadow registers to known values (pointers to structures you use inside ISR, etc. etc. even counters or generic integer values "mapped into address registers" if you don't need more than 24bits for them).
That is:
(initializations required before setting shadow registers)
....
SWAP SHADOWS
(initialize shadow registers)
SWAP SHADOWS
....
(continue with startup initializations)
2a) You code a single fast ISR in pure assembly, knowing that on entry R1-R5,N,N3,M01 (on 56800EX) have the values set at startup or "saved" by frti/frtid last time you executed the fast ISR after startup.
OR
2b) You code a singled fast ISR and/or one or more generic ISRs in pure assembly with each of them "owning" different shadow registers (or sharing pointers to common data structures).
For example if you have multiple ISR that just move data to/from I/O interfaces from/to ring buffers of the same size, you can have up to two separate ISR using two different ringbuffers of same size (one using shadow R0 and the other using shadow R1) and up to 3 other ISR using R2-R5,N,N3 to do the same with buffers managed by (pointer,size) or (pointer,top_address) register couples, or up to 7 ISR each with its own private "ISR frame pointer" autoloaded and autosaved to shadow registers (or even sharing them if they point to common data structures used by your ISRs).