How does ARM cortex handle PendSV Handler

6.5k Views Asked by At

I am creating a RTOS kernel and need to use the PendSV handler for context switching. I trigger the PendSV handler by doing : 0xE000ED04 = (0x1 << 28);. This sets the PendSVset register to 1, so theoretically, the handler should trigger. I do disable interrupts before triggering and enable after triggering. After the enabling PendSV should trigger. The priority is the lowest 0xFF and the systick handler priority is 0x00. I am not sure what is going on and why the pendsv handler is not running. I am using an TI-MSP432 controller and I figure maybe its the way the controller is handling the interrupt?

It is set in vectpending, vectpending is 001110 which is 14 for pendsv.

If anybody can help, I'd greatly appreciate it.

2

There are 2 best solutions below

1
On

I am assuming that you set PENDSVSET bit within Systick timer handler (from the information you gave in your comment). Since you set the PendSV priority lower than the priority of the Systick, so PendSV will pend until Systick interrupt returns. PendSV cannot interrupt Systick because it has lower priority. On returning from the Systick interrupt, PendSV will take hold through tail-chaining interrupt.

So no, PendSV interrupt will not be immediately generated when you set PENDSVSET bit within Systick timer handler as what you've expected. It will only interrupt on returning from the Systick interrupt.

3
On

Have you tried something simple like this (throwaway code experiments are crucial for bare-metal development, try without the rest of the project and all the over-complications)

.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word pendsv_handler
.word hang

.thumb_func
reset:
    bl notmain
    b hang
    
.thumb_func
hang:   b .
.align

...


.thumb_func
pendsv_handler:
    mov r4,#1
    bx lr
    
.thumb_func
.globl pendsv_test
pendsv_test:
    push {r4,lr}
    mov r4,#0
    str r1,[r0]
pendsv_loop:
    cmp r4,#0
    bne pendsv_loop
    mov r0,r4
    pop {r4,pc}

call with

#define ICSR 0xE000ED04

hexstring(0x12345678);
hexstring(pendsv_test(ICSR,1<<28));
hexstring(0x11111111);

and in my case that has some uart output:

12345678 
00000001 
11111111 

And can then move on to this:

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word pendsv_handler
.word hang



// ************** EXCEPTION HANDLER ***************
void pendsv_handler ( void )
{
    hexstring(GET32(ICSR));
}
// ************** EXCEPTION HANDLER ***************

int notmain ( void )
{
    clock_init();
    uart2_init();
    hexstring(0x12345678);
    PUT32(ICSR,1<<28);
    hexstring(0x11111111);
    return(0);
}

and see

12345678 
0000080E 
11111111 

which tells me that PendSV is cleared when it goes to the handler.

As far as priority goes, the docs say pendsv and svc are equal, so

stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word svc_handler
.word hang
.word hang
.word pendsv_handler
.word hang

and

// ************** EXCEPTION HANDLER ***************
void pendsv_handler ( void )
{
    hexstring(0x22222222);
    hexstring(GET32(ICSR));
    hexstring(0x22222222);
}
// ************** EXCEPTION HANDLER ***************

// ************** EXCEPTION HANDLER ***************
void svc_handler ( void )
{
    unsigned int ra;
    
    hexstring(GET32(ICSR));
    PUT32(ICSR,1<<28);
    for(ra=0;ra<20;ra++)
    {
        hexstring(GET32(ICSR));
    }
}
// ************** EXCEPTION HANDLER ***************

int notmain ( void )
{
    clock_init();
    uart2_init();
    hexstring(0x12345678);
    SVC();
    hexstring(0x11111111);
    return(0);
}

note:

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl GET32
GET32:
    ldr r0,[r0]
    bx lr

then we get

12345678 
0000080B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
1000E80B 
22222222 
0000080E 
22222222 
11111111

An eternity of time for the pendsv to fire but because the svc call is equal per the documentation then it cannot, until svc call returns. You can do this with interrupts as well, but the exceptions should be higher than the interrupts? Check the docs.

No reason why your experience should be different than mine on your cortex-m. If pendsv is not happening then divide and conquer. Divide the problem in half. Take the current code and start removing stuff working toward nothing. Do that for a bit. Start from almost nothing having the pendsv firing then start adding stuff, work toward the middle where you find what seems to cause it, understanding that even if you find it, that may not be IT. That is IT for the gutted code, you may have created another situation.

Reading and throwaway code experiments are well over 99% of your time with bare-metal. Writing of the final program is a very small percentage of your time.

Have you done these things to see how interrupt handlers or other things you are doing has affected pendsv success? What is the ICSR register showing you after you set pendsv?

How much time is in the systick or other equal level handlers relative to clock cycles left for pendsv, have you insured in your system level design that there are free clocks for the handlers to all have time.

If you want to do the thread swap in the systick handler then either just do it or use svc perhaps and not pendsv.