Hi. Sorry for my poor English.
I'm using 9s12e64 MCU and trying to write my own bootloader (it does not use interrupts) IDE CW5.1 + processor expert
Bootoader placed at address 0xe000-0xffff. Secondary interrupt table placed at 0xdf80-0xdfff Bootloader works fine, gets firmware, jumps to main application. Main app works. But there is on issue: I can't use interrupts in my main application. when interrupt occurs, MCU "hangs" (LED stops blinking) I've read AN2153, looked through AN2153SW and CW & HC12D60A VECTOR RELOCATION
Can anyone help me?
Here is some pieces of my code, where can be mistake:
// Events.c (bootloader)
#pragma CODE_SEG __NEAR_SEG NON_BANKED
void extVpwmesdn (void) { asm JMP [0xDF88,pcr]; }
void extVvreglvi (void) { asm JMP [0xDF8A,pcr]; }
....
#pragma CODE_SEG DEFAULT
// Vectors.c (bootloader)
static const tIsrFunc _InterruptVectorTable[] @0xFF80U = { /* Interrupt vector table */
/*lint -restore Enable MISRA rule (1.1) checking. */
/* ISR name No. Address Pri Name Description */
&Cpu_Interrupt, /* 0x3F 0xFF80 1 ivVReserved63 unused by PE */
....
&extVpwmesdn, /* 0x3B 0xFF88 1 ivVpwmesdn used by PE */
......
// interrupt, that does not works properly in main app:
#pragma CODE_SEG __NEAR_SEG NON_BANKED
ISR(FC1_Interrupt)
{
TIM2_TFLG1 = 0x80U; /* Reset interrupt request flag */
FC1_OnInterrupt(); /* Invoke user event */
}
#pragma CODE_SEG FC1_CODE
Attached screenshots of PE CPU settings for main app
Hi, Everyone. I think, my problem has been found.
When interrupt occurs, MCU serves interrupt in main firmware and hangs somewhere after RTI instruction.
So, some instruction corrupts stack. Here is disassembled code of interrupt in bootloader:
LDAA 0x30
PSHA
JMP [0xFF04, PC]
PULA
STAA 0x30
RTI
additional operations with stack - because of my compiler settings =)
Thank you all for the conversation.
Best regards
Slava,
probably you enabled compiler option in bootloader Code Generation->PPAGE register is used for paging (-CpPPAGE). When this is on, compiler saves/restores PPAGE on entry/exit form ISR. And this is what breaks your code. JMP doesn't return and PULA is never executed, stack destroyed. Either
It seems you are jumping to a nonexistent memory location.
void extVpwmesdn (void) { asm JMP [0xDF88,pcr]; }
That instruction will add the value in pcr to 0xDF88 and use it as reference to get the address to jump to.
I believe that you need to use x or y index register and use them to get to the correct address.
for example:
void extVpwmesdn (void)
{
asm LDX #8;
asm JMP [0xDF80,x];
}
I am not familiar with this processor, but you get the idea, x should be the reference to the X index register.
I just realized that in instruction:
JMP [0xDF80,x];
0xDF80 is an offset. I don't know how the processor would handle it, but it may be a signed number to be able to offset forward and back, so you may need to use:
void extVpwmesdn (void)
{
asm LDX #0xDF80;
asm JMP [8,x];
}
As I said, I am not familiar with this processor. pcr is what the OP posted in his code which I assumed it was the PC, but I could be wrong
There is two JMP instructions with indexed-indirect addressing:
JMP [D,xysp]
JMP [oprx16,xysp]
according to the datasheet:
[oprx16,xysp] --- Pointer to operand is found at 16-bit constant offset from X, Y, SP, or PC(16-bit offset in two extension bytes), which means that for "JMP [0xDF88,pcr]; " you have pcr(PC?) + 0xDF88 as the location it gets address from.
[D,xysp] --- Pointer to operand is found at X, Y, SP, or PC plus the value in D
Yes, you are wrong. ,PC and ,pcr are different things. CW treats first as constant offset to PC, not taking into account instruction length, while second computes right offset to specified address and takes into account instruction length as well. Example, just try it in simulator or in real HW:
// routine to call
void near pcrcallee(void) {
asm NOP
}
// vector at 0xDF88
void (*near vect)(void) @ 0xDF88 = pcrcallee;
// right caller
void near caller(void){
asm JMP [0xDF88,pcr];
}
// wrong caller
void near caller2(void){
asm JMP [0xDF88,PC];
}
void main(void) {
caller();
caller2();
If I understand you correctly pcr is PC + instruction length and this needs to be used to calculate offset correctly.
The instruction JMP [0xDF88, pcr] then offsets pcr by 0xDF88 which means the address is relative.
However his vector is at absolute address 0xDF88 so it would be getting the address from the wrong location.
Oh I see, I misunderstood your explanation. I checked in the manual and found the explanation somewhat hidden in the example for MIN and MAX instructions. Apparently they didn't think it was important enough to put it in the "Addressing Modes" or "Instruction Set" sections.
The “,PCR” notation is also new for the CPU12. This notation indicates the
programmer wants an appropriate offset from the PC reference to the
memory location (HILIMIT or LOWLIMIT in this example), and then to
assemble this instruction into a PC-relative indexed MIN or MAX instruction.
So, using the JMP[address,pcr] notation makes the assembler/compiler calculate the offset and replace the instruction with a proper JMP[oprx16,PC] instruction.
Thank everyone for replies.
vector table placed in .s19 file at necessary address (starts at 0xdf80, ends at 0xdfff). It is also present in *.map file.
main app works fine without bootloader (if I placed interrupts to 0xff80). In this case *.s19 file is not changed (only vector table address). So my mistake probably somewhere in bootloader. The worst thing: assembler language is some kind of magic for me :smileyhappy:
may be I have to attach my bootloader project?
Hi Viacheslav,
Thank you for your check.
My personal experiences with assembler programming are also limited.
Please try to replace asm JMP [0xDF88,pcr]; by asm JMP 0xDF88;
I hope it helps you.
Have a great day,
Radek
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Hi Edward,
You are right.
I tested that approach in different situation – it was on S12Z and jump instruction was part of an application and not in the bootloader. That may be also some different type of workaround. The interface between bootloader and application will be just specific addresses where JMP instructions are located.
In application code something like:
#pragma CODE_SEG __NEAR_SEG NON_BANKED
/* ISR prototype */
typedef void (* tIsrFunc)(void);
typedef struct
{
byte jmp_instruction;
tIsrFunc address;
} InterruptTableEntry;
#define _VECTOR(v) {0x06U, &v}
InterruptTableEntry _InterruptVectorTable[INT_VECTOR_TABLE_SIZE] @INT_VECTOR_TABLE_ADDR_IN_FLASH = { /* Interrupt vector table */
_VECTOR(Unimplemented_ISR),//0x80
//…
//_VECTOR(Unimplemented_ISR),//
_VECTOR(API_ISR),//0x100
//….
_VECTOR(Unimplemented_ISR),//0x
};
#pragma CODE_SEG DEFAULT
That should create a table with fields containing JMP instruction and direct address to the application ISRs. The complication and advantage simultaneously are that calculating addresses for bootloader vector are not so straightforward (3 bytes per record). The advantage is that this vector table may be placed anywhere in nonbanked flash and order or number of implemented ISR records is also not limited (you may skip Reserved or unused interrupts) since it is not referenced by interrupt hardware directly.
In extreme case, this may be also done directly in linker file. Example for one interrupt vector.
ROM_JMP = READ_ONLY 0xC000 TO 0xC000 FILL 0x06; //asm JMP instruction
ROM_APP_VECTOR = READ_ONLY 0xC001 TO 0xC002; //space for vector address - see VECTOR ADDRESS ...
ROM_C000 = READ_ONLY 0xC003 TO 0xDF7F;
VECTOR ADDRESS 0xC001 API_ISR
The vector for API in bootloader should be filled by address 0xC000 in this case.
I hope it helps you.
Have a great day,
Radek
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Hi Viacheslav,
The old S12 devices, unfortunately, do not contains IVBR register for shifting vector base address.
I suppose that I understood to your construction, but I am not exactly sure why it do not work.
The main problem is that application interrupt vector table belongs to flash occupied by the bootloader.
Your implementation contains ISRs in bootloader code with simple jumps per application vector table.
I am not sure with your jump command like asm JMP [0xDF88,pcr];
This command use indexed addressing.
I suppose, that when address 0xDF88 contains directly vector to application ISR, you should use extended addressing like asm JMP #DF88;.
The JMP opcode for extended addressing is 0x06 and 0x05 for indexed addressing. Please check generated extVpwmesdn() code.
Edward,
The PE defines ISR() as #define ISR(x) __interrupt void x(void)
So, #pragma TRAP_PROC should not be necessary. The interrupt keyword is used instead of it.
I hope it helps you.
Have a great day,
Radek
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
Radek,
OK, __interrupt is fine as well.
JMP is also right. It takes vector at DF88 and jumps where it points to. (BTW #adr denotes immediate addressing as opposed to extended adr. There’s no immediate addressing choice for JMP, only extended).
Perhaps linker optimized application vectors table out? Simulator or debugger should help
Regards,
Edward
Hi Edward,
You are right with JMP address. My fault.
About optimization)
good point. It might be next potential root cause.
Viacheslav,
Linker optimize out nonreferenced constants as unused. Please check generated map file in bootloader and application projects.
You may place constant name into ENTRIES section in prm linker file for avoiding that optimization.
I hope it helps you.
Have a great day,
Radek
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
#pragma TRAP_PROC is missing, which would make your ISR exit with RTI instruction.
#pragma CODE_SEG __NEAR_SEG NON_BANKED
#pragma TRAP_PROC
ISR(FC1_Interrupt)
{
TIM2_TFLG1 = 0x80U; /* Reset interrupt request flag */
FC1_OnInterrupt(); /* Invoke user event */
}
#pragma CODE_SEG FC1_CODE