HCS12 Interrupts (bootloader and main firmware)

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

HCS12 Interrupts (bootloader and main firmware)

2,434 Views
viacheslavbala1
Contributor II

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

Tags (1)
18 Replies

1,873 Views
viacheslavbala1
Contributor II

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

0 Kudos

1,873 Views
kef2
Senior Contributor IV

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

  • disable that option. I guess you don't need it at all
  • Remove interrupt keyword from your bootloader ISR's. Since they only jump and never return, RTS instead of RTI at the end will be fine, but compiler won't push/pull PPAGE.  

1,873 Views
Ray_V
Contributor V

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.

0 Kudos

1,873 Views
Ray_V
Contributor V

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];

}

0 Kudos

1,873 Views
kef2
Senior Contributor IV

What is pcr? It is not PC register, it‘s PC plus offset to compensate current instruction length, so that JMP jumps to address stored at DF88.

Regards,

Edward

0 Kudos

1,873 Views
Ray_V
Contributor V

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

0 Kudos

1,873 Views
kef2
Senior Contributor IV

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();

0 Kudos

1,873 Views
Ray_V
Contributor V

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.

0 Kudos

1,873 Views
kef2
Senior Contributor IV

No, JMP just jumps to vector at 0xDF88. Nothing wrong here.

0 Kudos

1,873 Views
Ray_V
Contributor V

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.

0 Kudos

1,873 Views
viacheslavbala1
Contributor II

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? 

main_app_abs_and_map.png

0 Kudos

1,873 Views
RadekS
NXP Employee
NXP Employee

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!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

1,873 Views
kef2
Senior Contributor IV

Radek,

JMP 0xDF88 will jump to routine at DF88. As I understand at DF88 should be vector table entry

0 Kudos

1,872 Views
RadekS
NXP Employee
NXP Employee

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!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

1,873 Views
RadekS
NXP Employee
NXP Employee

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!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

1,873 Views
kef2
Senior Contributor IV

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

0 Kudos

1,872 Views
RadekS
NXP Employee
NXP Employee

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!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

1,873 Views
kef2
Senior Contributor IV

#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

0 Kudos