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 -
- The hardware has 4 general purpose registers, namely R0,R1,R2 and R3, which need to be stored as part of the context.
- The hardware uses register EPC to determine where to return from ISR.
Context Save
-
Task1 is running and the context switch interrupt happens.
-
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.
- 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 |
+-------+
- Adjusted SP (i.e. SP after storing registers) is stored as the first member of TCB.
Context Restore
-
Task2 is selected as the task to be scheduled next.
-
Read the first member of the Task2’s TCB which is the adjusted SP for Task2.
-
The task2’s stack looks like the following -
Task2' Stack
+-------+
Original SP | R0 |
| 0x10 |
+-------+
| R1 |
| 0x11 |
+-------+
| R2 |
| 0x12 |
+-------+
| R3 |
| 0x13 |
+-------+
| Task2 |
Adjusted SP | PC |
+-------+
- 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.
- 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!