This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Saving Registers for Interrupts

Hi,

We have a project using the C5515 and CCS6.

We're using timer 0 interrupt to implement a simple CPU scheduler with a few tasks.

We have the following function that handles the interrupt:

interrupt void scheduler_isr()

Unfortunately, after some troubleshooting we found that not all registers were being saved by the interrupt handler from the compiler.

We had to insert the following code into the assembly file that was generated to force the registers to be saved:

PSHBOTH XAR4
PSHBOTH XAR5 ; added
PSHBOTH XAR6 ; added
PSHBOTH XAR7 ; added
PSHBOTH XDP ; added
PSH T2, T3 ; added

Afterwards everything worked great.

We also have the registers being popped off the stack on the return of the ISR.

Is there a way to force the compiler to include the above registers for saving/restoring context in an ISR?

Thanks

  • The compiler generated code for an interrupt function will automatically save all of the CPU registers if they are used, or if a function is called. Does your scheduler_isr function call another function? What compiler options are you using? What version of the compiler are you using? (The compiler version is not the same as the CCS version).
  • Hi,
    I did do some reading in the compiler manual, and from what I understood, I too thought that if at least one function was called in an ISR, all CPU registers would be saved.

    Below is the code in the timer ISR function:

    interrupt void scheduler_isr(void)
    {
    if (CPU_TIMINT_AGGR & TIM0FLAG)
    {
    // Now clear the timer 0 bit in the timer aggregation register by writing a 1 to it.
    // Note that this will leave the other timer bits alone.
    CPU_TIMINT_AGGR = TIM0FLAG;

    // Timer 0 (Scheduler) has expired. Handle it.
    schedule_timer_isr();
    }
    }

    We're using the TI v4.4.1 for the compiler.
    I had to get the C5515 to execute out of flash, so the following options were set:
    - ptrdiff_size = 32
    - all gotos/calls are encoded with 24-bit offset
    - huge memory model is being used
    - rts55h.lib is used for Runtime Support library

    Currently we are not using any optimizations.

    Since the call the schedule_timer_isr() is conditional, could the compiler potentially not save the mentioned registers in the ISR?

    Thanks!
  • The fact that the call is conditional should not matter to the compiler.
    Is schedule_timer_isr an interrupt function? If so, you can't call it directly like that because it won't properly restore the RETA register. You must instead arrange to raise an interrupt. I'm sorry I don't have the documentation in front of me, but there should be an intrinsic to raise a specific interrupt.
  • Thanks for the reply!

    schedule_timer_isr() is not declared as an interrupt function (I know the name is misleading).

    it is just static void schedule_timer_isr(void), and below is the code for it to fill in some gaps here:

    void schedule_timer_isr(void)

    {

    update_timer_tick();

    get_next_task(); // eventually calls context_switch()

    }

    However, your note about the RETA instruction is interesting.

    In the area of code where I swap tasks, I had to explicitly save the RETA register myself because the system wasn't running correctly.

    Below is the code I used to do so:

     

    static void context_switch(void)

    {

    // allocate space on the stack for the RETA value by

    // declaring the variable uint32 temp_ra;

    // This variable is located at SP (with zero offset).

    // Adding in local variables to this method may crash

    // the application!!! Take special care when modifying

    // this area of code.

    uint32 temp_ra;

    // move the RETA register to temp_ra (SP with zero offset)

    asm(" MOV RETA, dbl(*SP(#00h))");

    // get and save the critical count for the current task

    task[running_task].critical_count = get_critical_count();

    // Save running task's stack pointers

    // TWEAK: Use processor SP and SSP registers rather than memory map

    task[running_task].data_stack.uint[1] = SP;

    task[running_task].system_stack.uint[1] = SSP;

    // save the return address to the task control block.

    // The C compiler complains because in its eyes temp_ra

    // was never assigned a value. In actuality temp_ra was

    // assigned a value with the below instruction

    // asm(" MOV RETA, dbl(*SP(#00h))");

    task[running_task].return_address = temp_ra;

    // The new task is now the running task

    running_task = next_task;

    // Load the new task's stack pointers and return to it

    // TWEAK: Use processor SP and SSP registers rather than memory map

    SP = task[running_task].data_stack.uint[1];

    SSP = task[running_task].system_stack.uint[1];

    // the critical count of the new task

    set_critical_count(task[running_task].critical_count);

    // now setup the task so that the new RETA value will be used

    temp_ra = task[running_task].return_address;

    // now move the actual value of temp_ra to the RETA register

    asm(" MOV dbl(*SP(#00h)), RETA");

    }

    Maybe there is something else going on here, so I will take a deeper look into it.

    Thanks!

  • I need to start by mentioning the danger of using inline assembly code in a C function. The TI compiler doesn't understand what is in those asm statements, and there is no guarantee that (for instance) temp_ra is at exactly SP(#0). If you are able to get a C version of the function with asm statements to do what you need, then fine, but you are on your own.

    As you've noticed, in addition to saving the usual SOE (callee-saved) registers, you have to save and restore additional state registers like SP, SSP, and RETA, but you might also consider other control registers (depending on the operation of the rest of your program). A full context save and restore is a lot of registers on C55x. There are some assembly code examples in the CPU manual using repeat loops to accomplish context save and restore.

    It may be that function context_switch is confusing the compiler. I assume identifier get_critical_count is a macro or is otherwise inlined, and I suspect the function context_switch does not appear to the compiler to call any functions. In this case, it won't save all of the SOE (callee-saved) registers. Given that this function is declared static, it is obviously in the same source file as the function which calls it, and in that case the compiler won't bother saving and restoring registers in the caller that context_switch doesn't use. This can trickle up to function scheduler_isr. In order to figure out whether this is affecting scheduler_isr, I would need a more complete test case which demonstrates the problem. If that's not feasible, you might be able to get around the problem by putting each function in its own source file so that the compiler can't see them at the point of use and over-optimize the register save and restore.