Tasks w/ same Priority Interrupt even with time slice off

dnewby wrote on Tuesday, December 01, 2015:

I’ve configured FRTOS V8.2.0 with time slice off. I have a main task. The main task creates another task with the same priority. Here’s my psuedo code.

void vTask1(task_param_t pvParameters)
{
    printf("TASK1\n\r");
    for(;;);
}

void vMainTask( task_param_t pvParameters )
{
    TaskCreate(TASK1);
    printf("MAIN TASK\n\r");
    for(;;);
}

int main(void)
{
    TaskCreate(MAIN_TASK);
    vTaskStartScheduler();
    loop();
    return 0;
}

The output is:

MAIN TASK
TASK1

I do not expect this to happen. These two tasks have the same priority, and with configUSE_TIME_SLICING set to 0, vTask1() shouldn’t run (to my knowledge) without vMainTask blocking for some reason - which it doesnt.

rtel wrote on Tuesday, December 01, 2015:

I just tried this, albeit with FreeRTOS V8.2.3, and found that only “MAIN TASK” was printed out.

What other tasks and/or interrupts do you have in your system? You will get a switch between the two equal priority tasks if a task that has a higher priority runs in the mean time, or if a context switch occurs in an interrupt. For example, if vMainTask and vTask1 have priority 1, and vMainTask is running when a priority 2 task (lets call it task X) enters the Ready state, then the scheduler will start running task X as it has the highest priority. Then, when task X re-enters the Blocked state, the scheduler will select vTask1. So the execution sequence would be vMainTask -> X -> vTask1.

When I tried this, and found it behaved as expected, I didn’t have any other tasks or interrupt running.

Regards.

dnewby wrote on Wednesday, December 02, 2015:

My sys tick interrupt occurs and it switches to task1 afterwards. Is there a configuration to set so that the same task runs before and after a context switch even if there are other tasks with the same priority?

Also, if I change my tasks to print ‘1’ or ‘2’ respectively in the task loop, it prints '1’s until the systick interrupt, then '2’s, as you have said would happen. However, it never switches back to '1’s on the next systick interrupt. Why does it only switch tasks on the first occurence of the interrupt?

rtel wrote on Wednesday, December 02, 2015:

If you have two tasks that have the same priority, and no other tasks in
the Ready state, then:

With configUSE_TIME_SLICING set to 1 a context switch will occur on each
tick interrupt. This context switching on the tick interrupt is the
time slicing, and the behaviour that setting configUSE_TIME_SLICING
should suppress. Therefore…

With configUSE_TIME_SLICING set to 0 a context switch should not occur
on a tick interrupt, but only when a higher priority task enters the
Ready state, or when an interrupt other than the tick interrupt requests
a context switch.

…if you are getting a context switch on each tick interrupt even when
configUSE_TIME_SLICING is 0, and when there are no other tasks an no
other interrupts (as I think you are reporting) then something is wrong.

I have just tried this again, this time on a Cortex-M, with the simple code:

volatile uint32_t t1 = 0, t2 = 0;

void task1( void * pv )
{
     for( ;; )
     {
         t1++;
     }
}

void task2( void * pv )
{
     for( ;; )
     {
         t2++;
     }
}

int main( void )
{
     prvSetupHardware();
     xTaskCreate( task1, "t1", configMINIMAL_STACK_SIZE, NULL, 1, NULL );
     xTaskCreate( task2, "t2", configMINIMAL_STACK_SIZE, NULL, 1, NULL );
     vTaskStartScheduler();
}

With configUSE_TIME_SLICING set to 1, both t1 and t2 incremented. With
configUSE_TIME_SLICING set to 0, only t1 incremented and t2 remained at
zero.

Questions:

  • Which port are you using?
  • Do you have a tick hook defined that could be requesting a context switch?

Regards.

dnewby wrote on Wednesday, December 02, 2015:

Something must be wrong then. I tested similar code as you have above, and my t1 counter increments to 47842, systick interrupts, then t2 increments indefinitely. (This is with configUSE_TIME_SLICING set to 0).

I’m using Freescale’s KSDK v1.3.0 that includes freeRTOS (freescale.com/ksdk)

The port.c file included reports:
/*-----------------------------------------------------------

  • FreeRTOS for 56800EX port by Richy Ye in Jan. 2013.
    ----------------------------------------------------------/

I’m using the TWR-K60D100M development board from Freescale.
I haven’t created a tick hook.

rtel wrote on Wednesday, December 02, 2015:

Is this GCC? If so, could you please email me the tasks.c, port.c and
portmarco.h file so I can diff them. If it is not GCC there may also be
a portasm assembler file. You can send them to r dot barry -AT-
freertos.org.

dnewby wrote on Wednesday, December 02, 2015:

Sent.

rtel wrote on Wednesday, December 02, 2015:

Hmm, so are you using a Cortex-M device? TWK-K60D100M would seem to indicate yes, but 56800EX would seem to indicate no - unless the 56800EX comment in the file is just for that port, as the port.c seems to cover a lot of different usage scenarios.

Although the tasks.c file does contain some minor edits, there are none that would effect the time slicing behaviour. Unfortunately the port.c file is, as mentioned above, very specifc to Freescale, and so cannot be diffed with anything in the FreeRTOS distrobution with the same version number (it contains code for multiple chips and multiple compilers, for example). A quick look at the Cortex-M code in that file doesn’t immediately highlight anything that could change this behaviour though.

dnewby wrote on Wednesday, December 02, 2015:

Yes, the TWR-K60D100M uses a cortex m4. Is there anything else that I could try to help debug this problem?

rtel wrote on Wednesday, December 02, 2015:

First a couple of questions:

  1. It looks like you are only getting one yield. Therefore, there is a strong possibility the yield is already pending before the scheduler is started. Maybe that could be the issue? Although if that were the case I would still expect only one of the variables to increment - it would just be the other variable.

  2. Is there any library code, or code generated by the processor expert stuff, that is executing before main() is called? Maybe that is doing something?

On to your question:

If you tried my very simple example, that just creates two tasks that do nothing other than increment a variable, and still had the problem - then that proves the yield is not coming from the application code (no drivers or IO functions are being called). Therefore the yield must be coming directly or indirectly from an interrupt. Again, if you are using my very simple example, then the only interrupt that should be running is the tick interrupt, so somehow it must be coming from there. I would therefore suggest putting a break point in the tick interrupt handler (which is in the port.c file you sent me) and step through that code. You are looking for something calling taskYIELD(), portYIELD(), or otherwise setting the PendSV bit in the NVIC INT CTRL register.

Make double sure tick hook and idle hook functions are not defined, then manually delete all object files (rather than relying on a clean build doing this for you) before re-building from scratch.

dnewby wrote on Wednesday, December 02, 2015:

Digging a little deeper, I see the following:

In the vPortTickHandler/SysTick_Handler, it calls xTaskIncrementTick() which returns TRUE. It looks like the return value for xTaskIncrementTick() is whether or not a switch is required. It looks at the delayed list, and is seems as though there’s a task with the name “Tmr Svc” that it switches to. This task is created in xTimerCreateTimerTask() if configUSE_TIMERS is set to 1. Since the timer priority is greater than the task, it takes control. This must cause the task to be put on the back of the ready queue, and allow the other task to run.

Does that make sense?

rtel wrote on Wednesday, December 02, 2015:

Makes sense although if the priority of the timer task is above the priority of your tasks I would expect it to run first, then not again, unless a software timer is actually being created. Will look at this. My tests were also configured to have a timer task.

dnewby wrote on Wednesday, December 02, 2015:

I checked, and it does run first, then again at the first sys tick, then never again.

dnewby wrote on Tuesday, January 12, 2016:

Any updates with this? I have added definitions for the trace macros, and here is the output:

start frtos_main
//creating a test queue
traceQUEUE_CREATE
//creating producer task
traceTASK_CREATE PRODUCER
traceMOVED_TASK_TO_READY_STATE PRODUCER
//creating consumer task
traceTASK_CREATE CONSUMER
traceMOVED_TASK_TO_READY_STATE CONSUMER
//vTaskStartScheduler
traceTASK_CREATE IDLE
traceMOVED_TASK_TO_READY_STATE IDLE
traceQUEUE_CREATE
traceTASK_CREATE Tmr Svc
traceMOVED_TASK_TO_READY_STATE Tmr Svc

start prvTimerTask
traceTASK_DELAY_UNTIL
traceTASK_SWITCHED_OUT Tmr Svc
traceTASK_SWITCHED_IN PRODUCER

start producerTask
//just an infinite loop
traceTASK_INCREMENT_TICK 0
traceMOVED_TASK_TO_READY_STATE Tmr Svc
traceTASK_SWITCHED_OUT PRODUCER
traceTASK_SWITCHED_IN Tmr Svc
traceQUEUE_RECEIVE_FAILED 536841760
traceTASK_DELAY_UNTIL
traceTASK_SWITCHED_OUT Tmr Svc
traceTASK_SWITCHED_IN CONSUMER
start consumerTask

traceTIMER_CREATE is defined, but it is never observed. Is there any other reason the Tmr Svc task would run on the first tick if no timer has been created?

rtel wrote on Tuesday, January 12, 2016:

When I execute the code previously posted, the timer task only runs
once, so I’m afraid I don’t know why it runs twice in your project. I
suggest placing a break point in vTaskSwitchContext() within tasks.c,
and inspect the pxCurrentTCB->pcTaskName variable when the break point
is hit. When you see it it the timer service task for the second time,
step out of the function using the debugger to end up in the timer task
and see what it is doing (and why).

dnewby wrote on Tuesday, January 12, 2016:

On the first run of prvProcessTimerOrBlockTask() in the timer service task, I get:
xTimeNow = 0.
xNextExpireTime = 0.
xListWasEmpty = 1.

Then it executes
vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) );
vQueueWaitForMessageRestricted( VALID_PTR, 0 - 0 );

xTaskResumeAll() = FALSE, so then we call portYIELD_WITHIN_API()

On the second call, we come back from the vPortYieldFromISR() function.
We then run prvProcessReceivedCommands().
Reading from the queue fails (queue is empty) and we exit this function.

Upon reentry to the prvProcessTimerOrBlockTask() function, the only logic difference I see is
vQueueWaitForMessageRestricted( VALID_PTR, ( 0 - 1 ) );

Since the parameters for this function are:
( QueueHandle_t xQueue, TickType_t xTicksToWait )

It seems that xTicksToWait = 0 for the first occurence, and thus happens “immediately” and the second occurence has an xTicksToWait = 0xFFFFFFFF, or “infinite” wait.

This seems to be the inherent problem. When you test this, what are your values for xTicksToWait?

rtel wrote on Tuesday, January 12, 2016:

On the first run of prvProcessTimerOrBlockTask() in the timer service
task, I get:
xTimeNow = 0.
xNextExpireTime = 0.
xListWasEmpty = 1.

So far the same.

Then it executes
vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime -
xTimeNow ) );
vQueueWaitForMessageRestricted( VALID_PTR, 0 - 0 );

This is where our code bases differ. In V8.2.3 there is a third
parameter which is “wait indefinitely”, which in my case is set to
pdTRUE, hence it blocks indefinitely to wait for a command to arrive.

The change to the code was made to allow timers to be supported better
in lower power tickless applications.

dnewby wrote on Tuesday, January 12, 2016:

Do you know if this was an issue with 8.2.0? Just trying to make sure there isn’t anything else wrong with the way the code is executing.

rtel wrote on Tuesday, January 12, 2016:

I’m not aware of any issues - I think the timers work just fine in the
version you have - you are just seeing the very fine detail of the start
up sequence. The changes in the latest version were made specifically
for low power systems, to enable a tickless low power application to
enter a very deep and extended sleep even when software timers are used

  • hence the new ‘block indefinitely’ parameter.