General porting procedures

If it is the first time “FillTask” is getting scheduled, you need to check the pxPortInitialiseStack function. If not, you need to check you context saving code.

Thank you for your help!

I have got “Filtask” at first and I have got the following :

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
	*pxTopOfStack = (StackType_t) 0xEFACABCDDEADBEEF;
	pxTopOfStack--;

	*pxTopOfStack = (StackType_t) 0x1234567890876543;       /* Word to which the stack pointer will be left pointing after context restore. */
	pxTopOfStack--;

	/* create a space for a full context */
	pxTopOfStack -= CTX_SIZE/8; /* CTX SIZE HERE*/

	/* fill up some initial values for us to kick in */
	*( pxTopOfStack + CTX_CAUSE/8 ) = (StackType_t) mips_getcr();
	*( pxTopOfStack + CTX_STATUS/8 ) = (StackType_t) (mips32_get_c0(C0_STATUS) | portINITIAL_SR);
	*( pxTopOfStack + CTX_EPC/8 ) = (StackType_t) pxCode;
	*( pxTopOfStack + CTX_RA/8 ) = (StackType_t) portTASK_RETURN_ADDRESS;
	*( pxTopOfStack + CTX_A0/8 ) = (StackType_t) pvParameters; /* Parameters to pass in. */

	/* Save GP register value in the context */
	asm volatile ("sd $gp, %0" : "=m" (*( pxTopOfStack + CTX_GP/8 )));

	return pxTopOfStack;
}

according
https://www.freertos.org/FreeRTOS_Support_Forum_Archive/December_2017/freertos_understanding_portable_code_a0ef28abj.html
pxPortInitialiseStack and portSave/ResoreContex have to keep the same order of data initializing and storing/restoring respectively
But I dont understand how to realize what and how I can point to stack of task ?, - is it the uxSavedTaskStackPointer?

And can you explain, please, the difference between data blocks that creates xTaskCreate (stack of task and task data structures) and task frame and pxCurrentTCB?

Sorry, I get confused

Let me try to explain the concepts. Let us make the following assumptions -

  1. The hardware has 4 general purpose registers, namely R0,R1,R2 and R3, which need to be stored as part of the context.
  2. The hardware uses register EPC to determine where to return from ISR.

Context Save

  1. Task1 is running and the context switch interrupt happens.

  2. The task’s stack and registers look like the following when the context switch ISR starts.

             Task1' Stack

              +-------+
Original SP   |       |
              |       |
              +-------+
              |       |
              |       |
              +-------+
              |       |
              |       |
              +-------+
              |       |
              |       |
              +-------+
R0 = 0x00
R1 = 0x01
R2 = 0x02
R3 = 0x03
EPC = Task1 PC.
  1. Registers R0-R3 and EPC are stored on the task’s stack.
             Task1' Stack

              +-------+
Original SP   |  R0   |
              | 0x00  |
              +-------+
              |  R1   |
              | 0x01  |
              +-------+
              |  R2   |
              | 0x02  |
              +-------+
              |  R3   |
              | 0x03  |
              +-------+
              | Task1 |
Adjusted SP   |  PC   |
              +-------+
  1. Adjusted SP (i.e. SP after storing registers) is stored as the first member of TCB.

Context Restore

  1. Task2 is selected as the task to be scheduled next.

  2. Read the first member of the Task2’s TCB which is the adjusted SP for Task2.

  3. The task2’s stack looks like the following -

             Task2' Stack

              +-------+
Original SP   |  R0   |
              | 0x10  |
              +-------+
              |  R1   |
              | 0x11  |
              +-------+
              |  R2   |
              | 0x12  |
              +-------+
              |  R3   |
              | 0x13  |
              +-------+
              | Task2 |
Adjusted SP   |  PC   |
              +-------+
  1. Read R0-R3 and EPC from the stack and program them. Adjust the SP after reading values.
             Task2' Stack

              +-------+
Original SP   |       |
              |       |
              +-------+
              |       |
              |       |
              +-------+
              |       |
              |       |
              +-------+
              |       |
              |       |
              +-------+

R0 = 0x10
R1 = 0x11
R2 = 0x12
R3 = 0x13
EPC = Task2 PC.
  1. The ISR will return to Task 2 as that is the value stored in EPC.

pxPortInitialiseStack

When each task is created, you need to initialize the stack with a frame such that the context restore operation will start the task.

             New Task's Stack

              +-------+
Original SP   |  R0   |
              |       |
              +-------+
              |  R1   |
              |       |
              +-------+
              |  R2   |
              |       |
              +-------+
              |  R3   |
              |       |
              +-------+
              | Task  |
Adjusted SP   |  PC   |
              +-------+

Thank you very much for your detailed answer!

When each task is created, you need to initialize the stack with a frame such that the context restore operation will start the task

.
Sorry I can not imagine how to create frame(for pxPortInitialiseStack) from context restore operation, and example for PIC32MZ do not do it obviously

It does it here - https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/main/portable/MPLAB/PIC32MZ/port.c#L174

Thank you fro your replay!
Yes this is:

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
    /* Ensure 8 byte alignment is maintained when leaving this function. */
    pxTopOfStack--;
    pxTopOfStack--;

    *pxTopOfStack = (StackType_t) 0xDEADBEEF;
    pxTopOfStack--;

    *pxTopOfStack = (StackType_t) 0x12345678;   /* Word to which the stack pointer will be left pointing after context restore. */
    pxTopOfStack--;

    *pxTopOfStack = (StackType_t) _CP0_GET_CAUSE();
    pxTopOfStack--;

    *pxTopOfStack = (StackType_t) portINITIAL_SR;/* CP0_STATUS */
    pxTopOfStack--;

    *pxTopOfStack = (StackType_t) pxCode;       /* CP0_EPC */
    pxTopOfStack--;

    *pxTopOfStack = (StackType_t) 0x00000000;   /* DSPControl */
    pxTopOfStack -= 7;                          /* Includes space for AC1 - AC3. */

    *pxTopOfStack = (StackType_t) portTASK_RETURN_ADDRESS;  /* ra */
    pxTopOfStack -= 15;

    *pxTopOfStack = (StackType_t) pvParameters; /* Parameters to pass in. */
    pxTopOfStack -= 15;

    *pxTopOfStack = (StackType_t) pdFALSE; /*by default disable FPU context save on parts with FPU */

    return pxTopOfStack;
}

and I didnt see the link to :

.macro  portSAVE_CONTEXT

    /* Make room for the context. First save the current status so it can be
    manipulated, and the cause and EPC registers so their original values are
    captured. */
    mfc0        k0, _CP0_CAUSE
    addiu       sp, sp, -portCONTEXT_SIZE

    #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
        /* Test if we are already using the system stack. Only tasks may use the
        FPU so if we are already in a nested interrupt then the FPU context does
        not require saving. */
        la          k1, uxInterruptNesting
        lw          k1, 0(k1)
        bne         k1, zero, 2f
        nop

        /* Test if the current task needs the FPU context saving. */
        la          k1, ulTaskHasFPUContext
        lw          k1, 0(k1)
        beq         k1, zero, 1f
        nop

        /* Adjust the stack to account for the additional FPU context.*/
        addiu       sp, sp, -portFPU_CONTEXT_SIZE

    1:
        /* Save the ulTaskHasFPUContext flag. */
        sw          k1, portTASK_HAS_FPU_STACK_LOCATION(sp)

    2:
    #endif

    mfc0        k1, _CP0_STATUS

    /* Also save s7, s6 and s5 so they can be used.  Any nesting interrupts
    should maintain the values of these registers across the ISR. */
    sw          s7, 48(sp)
    sw          s6, 44(sp)
    sw          s5, 40(sp)
    sw          k1, portSTATUS_STACK_LOCATION(sp)

    /* Prepare to enable interrupts above the current priority. */
    srl         k0, k0, 0xa
    ins         k1, k0, 10, 7
    srl         k0, k0, 0x7 /* This copies the MSB of the IPL, but it would be an error if it was set anyway. */
    ins         k1, k0, 18, 1
    ins         k1, zero, 1, 4

    /* s5 is used as the frame pointer. */
    add         s5, zero, sp

    /* Check the nesting count value. */
    la          k0, uxInterruptNesting
    lw          s6, (k0)

    /* If the nesting count is 0 then swap to the the system stack, otherwise
    the system stack is already being used. */
    bne         s6, zero, 1f
    nop

    /* Swap to the system stack. */
    la          sp, xISRStackTop
    lw          sp, (sp)

    /* Increment and save the nesting count. */
1:  addiu       s6, s6, 1
    sw          s6, 0(k0)

    /* s6 holds the EPC value, this is saved after interrupts are re-enabled. */
    mfc0        s6, _CP0_EPC

    /* Re-enable interrupts. */
    mtc0        k1, _CP0_STATUS

    /* Save the context into the space just created.  s6 is saved again
    here as it now contains the EPC value.  No other s registers need be
    saved. */
    sw          ra, 120(s5)
    sw          s8, 116(s5)
    sw          t9, 112(s5)
    sw          t8, 108(s5)
    sw          t7, 104(s5)
    sw          t6, 100(s5)
    sw          t5, 96(s5)
    sw          t4, 92(s5)
    sw          t3, 88(s5)
    sw          t2, 84(s5)
    sw          t1, 80(s5)
    sw          t0, 76(s5)
    sw          a3, 72(s5)
    sw          a2, 68(s5)
    sw          a1, 64(s5)
    sw          a0, 60(s5)
    sw          v1, 56(s5)
    sw          v0, 52(s5)
    sw          s6, portEPC_STACK_LOCATION(s5)
    sw          $1, 16(s5)

    /* Save the AC0, AC1, AC2, AC3 registers from the DSP.  s6 is used as a
    scratch register. */
    mfhi        s6, $ac1
    sw          s6, 128(s5)
    mflo        s6, $ac1
    sw          s6, 124(s5)

    mfhi        s6, $ac2
    sw          s6, 136(s5)
    mflo        s6, $ac2
    sw          s6, 132(s5)

    mfhi        s6, $ac3
    sw          s6, 144(s5)
    mflo        s6, $ac3
    sw          s6, 140(s5)

    /* Save the DSP Control register */
    rddsp       s6
    sw          s6, 148(s5)

    /* ac0 is done separately to match the MX port. */
    mfhi        s6, $ac0
    sw          s6, 12(s5)
    mflo        s6, $ac0
    sw          s6, 8(s5)

    /* Save the FPU context if the nesting count was zero. */
    #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
        la          s6, uxInterruptNesting
        lw          s6, 0(s6)
        addiu       s6, s6, -1
        bne         s6, zero, 1f
        nop

        /* Test if the current task needs the FPU context saving. */
        lw          s6, portTASK_HAS_FPU_STACK_LOCATION(s5)
        beq         s6, zero, 1f
        nop

        /* Save the FPU registers. */
        portSAVE_FPU_REGS ( portCONTEXT_SIZE + 8 ), s5

        /* Save the FPU status register */
        cfc1        s6, $f31
        sw          s6, (portCONTEXT_SIZE + portFPCSR_STACK_LOCATION)(s5)

        1:
    #endif

    /* Update the task stack pointer value if nesting is zero. */
    la          s6, uxInterruptNesting
    lw          s6, (s6)
    addiu       s6, s6, -1
    bne         s6, zero, 1f
    nop

    /* Save the stack pointer. */
    la          s6, uxSavedTaskStackPointer
    sw          s5, (s6)
1:
    .endm

/******************************************************************/
.macro  portRESTORE_CONTEXT

    /* Restore the stack pointer from the TCB.  This is only done if the
    nesting count is 1. */
    la          s6, uxInterruptNesting
    lw          s6, (s6)
    addiu       s6, s6, -1
    bne         s6, zero, 1f
    nop
    la          s6, uxSavedTaskStackPointer
    lw          s5, (s6)

    #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
        /* Restore the FPU context if required. */
        lw          s6, portTASK_HAS_FPU_STACK_LOCATION(s5)
        beq         s6, zero, 1f
        nop

        /* Restore the FPU registers. */
        portLOAD_FPU_REGS   ( portCONTEXT_SIZE + 8 ), s5

        /* Restore the FPU status register. */
        lw          s6, ( portCONTEXT_SIZE + portFPCSR_STACK_LOCATION )(s5)
        ctc1        s6, $f31
    #endif

1:

    /* Restore the context. */
    lw          s6, 128(s5)
    mthi        s6, $ac1
    lw          s6, 124(s5)
    mtlo        s6, $ac1

    lw          s6, 136(s5)
    mthi        s6, $ac2
    lw          s6, 132(s5)
    mtlo        s6, $ac2

    lw          s6, 144(s5)
    mthi        s6, $ac3
    lw          s6, 140(s5)
    mtlo        s6, $ac3

    /* Restore DSPControl. */
    lw          s6, 148(s5)
    wrdsp       s6

    lw          s6, 8(s5)
    mtlo        s6, $ac0
    lw          s6, 12(s5)
    mthi        s6, $ac0
    lw          $1, 16(s5)

    /* s6 is loaded as it was used as a scratch register and therefore saved
    as part of the interrupt context. */
    lw          s7, 48(s5)
    lw          s6, 44(s5)
    lw          v0, 52(s5)
    lw          v1, 56(s5)
    lw          a0, 60(s5)
    lw          a1, 64(s5)
    lw          a2, 68(s5)
    lw          a3, 72(s5)
    lw          t0, 76(s5)
    lw          t1, 80(s5)
    lw          t2, 84(s5)
    lw          t3, 88(s5)
    lw          t4, 92(s5)
    lw          t5, 96(s5)
    lw          t6, 100(s5)
    lw          t7, 104(s5)
    lw          t8, 108(s5)
    lw          t9, 112(s5)
    lw          s8, 116(s5)
    lw          ra, 120(s5)

    /* Protect access to the k registers, and others. */
    di
    ehb

    /* Decrement the nesting count. */
    la          k0, uxInterruptNesting
    lw          k1, (k0)
    addiu       k1, k1, -1
    sw          k1, 0(k0)

    #if ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )
        /* If the nesting count is now zero then the FPU context may be restored. */
        bne         k1, zero, 1f
        nop

        /* Restore the value of ulTaskHasFPUContext */
        la          k0, ulTaskHasFPUContext
        lw          k1, 0(s5)
        sw          k1, 0(k0)

        /* If the task does not have an FPU context then adjust the stack normally. */
        beq         k1, zero, 1f
        nop

        /* Restore the STATUS and EPC registers */
        lw          k0, portSTATUS_STACK_LOCATION(s5)
        lw          k1, portEPC_STACK_LOCATION(s5)

        /* Leave the stack in its original state.  First load sp from s5, then
        restore s5 from the stack. */
        add         sp, zero, s5
        lw          s5, 40(sp)

        /* Adjust the stack pointer to remove the FPU context */
        addiu       sp, sp, portFPU_CONTEXT_SIZE
        beq         zero, zero, 2f
        nop

        1:  /* Restore the STATUS and EPC registers */
        lw          k0, portSTATUS_STACK_LOCATION(s5)
        lw          k1, portEPC_STACK_LOCATION(s5)

        /* Leave the stack in its original state.  First load sp from s5, then
        restore s5 from the stack. */
        add         sp, zero, s5
        lw          s5, 40(sp)

        2:  /* Adjust the stack pointer */
        addiu       sp, sp, portCONTEXT_SIZE

    #else

        /* Restore the frame when there is no hardware FP support. */
        lw          k0, portSTATUS_STACK_LOCATION(s5)
        lw          k1, portEPC_STACK_LOCATION(s5)

        /* Leave the stack in its original state.  First load sp from s5, then
        restore s5 from the stack. */
        add         sp, zero, s5
        lw          s5, 40(sp)

        addiu       sp, sp, portCONTEXT_SIZE

    #endif // ( __mips_hard_float == 1 ) && ( configUSE_TASK_FPU_SUPPORT == 1 )

    mtc0        k0, _CP0_STATUS
    mtc0        k1, _CP0_EPC
    ehb
    eret
    nop

    .endm

And also I didnt find pcCode initialization in stack initialization and I didnt find link between pxCode and EPC
I calculate the following locations according pic’s paradigm:

#define portCONTEXT_SIZE                312
#define portSTATUS_STACK_LOCATION       288
#define portEPC_STACK_LOCATION          304
#define			_cv0 48
#define			_cv1 56
#define			_ca0 64
#define			_ca1 72
#define			_ca2 80
#define			_ca3 88
#define			_ct0 96
#define			_ct1 104
#define			_ct2 112
#define			_ct3 120
#define			_ct4 128
#define			_ct5 136
#define			_ct6 144
#define			_ct7 152
#define			_ct8 160
#define			_ct9 168
#define			_cs0 176
#define			_cs1 184
#define			_cs2 192
#define			_cs3 200
#define			_cs4 208
#define			_cs5 216
#define			_cs6 224
#define			_cs7 232
#define			_ck0 240
#define			_ck1 248

//	#define			gp, 256(s5)
//	#define			sp, 264(s5)
//	#define			fp, 272(s5)
#define			_cra 280

And I dont uderstand how to create correct frame for this locations

It is the following line:

 *pxTopOfStack = (StackType_t) pxCode;

What I one of my previous post was a simplified example explaining the concept. The exact implementation will very much depend on your hardware. You will need to understand what registers need to be stored as part of context for your hardware architecture.

Thank you for your replay!
As I understand in case of FreeRTOS switching context I should not use shadow register sets?

And does switching contex use global pointer(gp)
I have noticed what when I do not restore it in ISR of timertick handler or yield handler switching context doesnt occure

As I have said before, I am not familiar with this architecture. You will need to understand your architecture to find which registers need to be stored as part of context.

Thank you for your replay and support!
Finally I ported, and now Im trying to test PIC32 blink example.
And in process I found some misundestanding.
I have 25 MHz clock and I use 2kHz period of ticktimer interrupt
And I see what instead of 200 ms period of led blinking I have 100 ms.
I use the following defines:

#define configCPU_CLOCK_HZ                          25000000
#define configTICK_RATE_HZ                          1000

And the question is what, - Does configTICK_RATE_HZ have to eqal to ticktimer interrupt rate?
And on what has to base software timer?

The defines configCPU_CLOCK_HZ and configTICK_RATE_HZ are not use by the main part of FreeRTOS for anything. They can be used by the port layer to configure the system (the ratio being the divider to setup for the tick timer) and the configTICK_RATE_HZ is used by the convince macros to convert time in ms to time in ticks.

If you configured the hardware to generate ticks interrupts at 2 kHz, but defined the value for the macro to be 1 kHz, then all times will be off by a factor of 2,

High tick rates will increase the system overhead, as you go into the tick interrupt more often and the system needs to check if it is going to run the scheduler more often.

The demos use a 1 kHz tick to stress test the port, normally the tick is slower than that, I often use a 100 Hz tick as 10 ms resolution in time outs and delays is fine for my applications.

Thank you!
Now I understand!