FreeRTOS v11.1 R5F MPU Error

What is the minimum number of MPU regions that your boot code need? It is currently using 8 but is that the minimum required?

Something like this - https://www.ti.com/product/TMS570LC4357.

I will try to check the minimum number of MPU regions the boot code need.
It seems FreeRTOS v11.1 CR5 port only support 12 or 16 MPU regions, is it right?
The hardware support total 16 MPU regions, maybe i can provide 12 to FreeRTOS and 4 to boot code.

The document can be downloaded from below url:
https://documentation-service.arm.com/static/5f04288cdbdee951c1cd8969?token=

I have change MPU regions which were need by boot code to 4(12-15), and

#define configTOTAL_MPU_REGIONS                                12U

The latest boot.S has been uploaded to

code

After above update, below line can pass.

CPS #SVC_MODE

But the code stuck in below line now:

portRESTORE_CONTEXT

The fucntion’s code is:

/*
 * void vPortStartFirstTask( void );
 */
.align 4
.global vPortStartFirstTask
.type vPortStartFirstTask, %function
vPortStartFirstTask:
    /* This function is called from System Mode to start the FreeRTOS-Kernel.
     * As described in the portRESTORE_CONTEXT macro, portRESTORE_CONTEXT cannot
     * be called from the System mode. We, therefore, switch to the Supervisor
     * mode before calling portRESTORE_CONTEXT. */
    CPS #SVC_MODE
    portRESTORE_CONTEXT

appreciate for your advice.

Can you look into the disassembly and find the exact instruction? If you would like to have a debug session to debug this together, please drop me a DM.

/* Restore the context of a FreeRTOS Task. */
.macro portRESTORE_CONTEXT
    /* Load the pointer to the current task's Task Control Block (TCB). */
    LDR     LR, =pxCurrentTCB   /* LR = &( pxCurrentTCB ). */
    LDR     LR, [LR]            /* LR = pxCurrentTCB. */
    ADD     R1, LR, #0x4        /* R1 now points to the xMPUSettings in TCB. */
    LDR     LR, [LR]            /* LR = pxTopOfStack i.e. the address where to restore the task context from. */

    /* When creating a loop label in a macro it has to be a numeric label.
     * for( R5 = portFIRST_CONFIGURABLE_REGION ; R5 <= portNUM_CONFIGURABLE_REGIONS ; R5++ ) */
    MOV     R5, #portFIRST_CONFIGURABLE_REGION

    123:
        LDMIA   R1!, { R2-R4 }  /* R2 = ulRegionSize, R3 = ulRegionAttribute, R4 = ulRegionBaseAddress. */

        MCR     p15, #0, R5, c6, c2, #0 /* MPU Region Number Register. */
        MCR     p15, #0, R4, c6, c1, #0 /* MPU Region Base Address Register. */
        MCR     p15, #0, R3, c6, c1, #4 /* MPU Region Access Control Register. */
        MCR     p15, #0, R2, c6, c1, #2 /* MPU Region Size and Enable Register. */

        ADD     R5, R5, #1
        CMP     R5, #portNUM_CONFIGURABLE_REGIONS
        BLE     123b

    LDR     R1, =ulCriticalNesting /* R1 = &( ulCriticalNesting ). */
    LDM     LR!, { R2 }            /* R2 = Stored ulCriticalNesting. */
    STR     R2, [R1]               /* Restore ulCriticalNesting. */

#if ( portENABLE_FPU == 1 )
    LDM     LR!, { R1 }     /* R1 = Stored FPSCR.  */
    VMSR    FPSCR, R1       /* Restore FPSCR. */
    VLDM   LR!, { D0-D15 }  /* Restore D0-D15. */
#endif /* portENABLE_FPU*/


    /* LDM (User registers) - In a PL1 mode other than System mode, LDM (User
     * registers) loads multiple User mode registers from consecutive memory
     * locations using an address from a base register. The registers loaded
     * cannot include the PC. The processor reads the base register value
     * normally, using the current mode to determine the correct Banked version
     * of the register. This instruction cannot writeback to the base register.
     *
     *  The following can be derived from the above description:
     * - The macro portRESTORE_CONTEXT MUST be called from a PL1 mode other than
     *   the System mode.
     * - Base register LR of the current mode will be used which contains the
     *   location to restore the context from.
     * - It will restore R0-R14 of User mode i.e. SP(R13) and LR(R14) of User
     *   mode will be restored.
     */
    LDM     LR, { R0-R14 }^
    ADD     LR, LR, #60 /* R0-R14 - Total 155 register, each 4 byte wide. */

    RFE     LR  /* Restore PC and CPSR from the context. */

.endm

RFE LR

The above line stuck. @aggarg

What is the value of SP and LR before executing RFE LR? What are the 2 words stored at the address in register LR?

    LDR     r12, =0xB7000000
    LDR R13, [LR]
    STR     r13, [r12]

    LDR R13, [LR, #4]
    STR     r13, [r12, #4]

    RFE     LR  /* Restore PC and CPSR from the context. */

Sf:/$ dump 0xB7000000 0x10
ADDR 0xb7000000: 0x0510769c
ADDR 0xb7000004: 0x0000001f
ADDR 0xb7000008: 0x00000000
ADDR 0xb700000c: 0x00000000
ADDR 0xb7000010: 0x00000000
ADDR 0xb7000014: 0x00000000
ADDR 0xb7000018: 0x00000000
ADDR 0xb700001c: 0x00000000
ADDR 0xb7000020: 0x00000000
ADDR 0xb7000024: 0x00000000
ADDR 0xb7000028: 0x00000000
ADDR 0xb700002c: 0x00000000
ADDR 0xb7000030: 0x00000000
ADDR 0xb7000034: 0x00000000
ADDR 0xb7000038: 0x00000000
ADDR 0xb700003c: 0x00000000

Please check, thank you .

This code looks different than what you shared in the previous post. Where did you take it?

What is address 0xB7000000? Is it the value contained in LR register?

0xB7000000 is just a address used to debug and dump the LR value.
The real 2 words of LR are:
0x0510769c and 0x0000001f .

    LDR     r12, =0xB7000000
    LDR R13, [LR]
    STR     r13, [r12]

    LDR R13, [LR, #4]
    STR     r13, [r12, #4]

above code snippets do that save LR value to 0xB7000000.

The whole code:

/* Restore the context of a FreeRTOS Task. */
.macro portRESTORE_CONTEXT
    /* Load the pointer to the current task's Task Control Block (TCB). */
    LDR     LR, =pxCurrentTCB   /* LR = &( pxCurrentTCB ). */
    LDR     LR, [LR]            /* LR = pxCurrentTCB. */
    ADD     R1, LR, #0x4        /* R1 now points to the xMPUSettings in TCB. */
    LDR     LR, [LR]            /* LR = pxTopOfStack i.e. the address where to restore the task context from. */

    /* When creating a loop label in a macro it has to be a numeric label.
     * for( R5 = portFIRST_CONFIGURABLE_REGION ; R5 <= portNUM_CONFIGURABLE_REGIONS ; R5++ ) */
    MOV     R5, #portFIRST_CONFIGURABLE_REGION

    123:
        LDMIA   R1!, { R2-R4 }  /* R2 = ulRegionSize, R3 = ulRegionAttribute, R4 = ulRegionBaseAddress. */

        MCR     p15, #0, R5, c6, c2, #0 /* MPU Region Number Register. */
        MCR     p15, #0, R4, c6, c1, #0 /* MPU Region Base Address Register. */
        MCR     p15, #0, R3, c6, c1, #4 /* MPU Region Access Control Register. */
        MCR     p15, #0, R2, c6, c1, #2 /* MPU Region Size and Enable Register. */

        ADD     R5, R5, #1
        CMP     R5, #portNUM_CONFIGURABLE_REGIONS
        BLE     123b

    LDR     R1, =ulCriticalNesting /* R1 = &( ulCriticalNesting ). */
    LDM     LR!, { R2 }            /* R2 = Stored ulCriticalNesting. */
    STR     R2, [R1]               /* Restore ulCriticalNesting. */

#if ( portENABLE_FPU == 1 )
    LDM     LR!, { R1 }     /* R1 = Stored FPSCR.  */
    VMSR    FPSCR, R1       /* Restore FPSCR. */
    VLDM   LR!, { D0-D15 }  /* Restore D0-D15. */
#endif /* portENABLE_FPU*/


    /* LDM (User registers) - In a PL1 mode other than System mode, LDM (User
     * registers) loads multiple User mode registers from consecutive memory
     * locations using an address from a base register. The registers loaded
     * cannot include the PC. The processor reads the base register value
     * normally, using the current mode to determine the correct Banked version
     * of the register. This instruction cannot writeback to the base register.
     *
     *  The following can be derived from the above description:
     * - The macro portRESTORE_CONTEXT MUST be called from a PL1 mode other than
     *   the System mode.
     * - Base register LR of the current mode will be used which contains the
     *   location to restore the context from.
     * - It will restore R0-R14 of User mode i.e. SP(R13) and LR(R14) of User
     *   mode will be restored.
     */
    LDM     LR, { R0-R14 }^
    ADD     LR, LR, #60 /* R0-R14 - Total 155 register, each 4 byte wide. */

    LDR     r12, =0xB7000000
    LDR R13, [LR]
    STR     r13, [r12]

    LDR R13, [LR, #4]
    STR     r13, [r12, #4]

    RFE     LR  /* Restore PC and CPSR from the context. */

.endm

Thanks.

This indicates that we are trying to start a privileged task.

Does this look like a valid code section address? Can you examine the code at that address and see if it looks reasonable?

In the code you added, you are clobbering R13 which is SP. Use some other general purpose register and check the SP value also to see if looks reasonable.

update code as below:

    LDR     r12, =0xB7000000
    LDR     r11, [LR]
    STR     r11, [r12]

    LDR     r11, [LR, #4]
    STR     r11, [r12, #4]

    RFE     LR  /* Restore PC and CPSR from the context. */

dump result:
Sf:/$ dump 0xB7000000 0x10
ADDR 0xb7000000: 0x0510769c
ADDR 0xb7000004: 0x0000001f

All that seems reasonable. The RFE should take the control to timer task. Not sure why that is not happening. Just for testing, can you update your boot code to use MPU regions 8-16 so that you do not need to comment out any MPU region configuration in the boot code? Leave configTOTAL_MPU_REGIONS as 12.

I think the region ID should be from 8-15, right?
I have done the testing follow your advice.
But it’s failed.
The code stuck when call vPortStartFirstTask function, It’s not even executed inside this function.

Yes, you are right.

I am out of ideas here. We can have a debug session together and try to debug. I’d also suggest to reach out to your hardware vendor.

thank you.

Could you tell me how to setup a debug session, and which software need to install for it?

Send me a DM with your email and I will setup one.

Some update:
The function xTaskCreateRestricted which used to create task stuck,
further function pvPortMalloc called in prvCreateRestrictedTask stuck.
We use in heap_4.c.

We had a debug session and we concluded that the OP’s initial assertion that the code is stuck in RFE LR is not correct. The OP incorrectly concluded that from the observation that vPortStartFirstTask was not returning. In reality, this function is not meant to return and the control successfully reaches the first task (timer task in this case).

Please try to figure out where in pvPortMalloc is it stuck.

Both xTaskCreateRestricted and pvPortMalloc have been called successed.
But vTaskFunction can not be scheduled:

void vTaskFunction(void* p) {
    int* pp = 0xb7000001c;
    int a = 0x1111;
    *pp = a;
    for (;;)
    {
        *pp = a++;
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}