Skip to content

RK0 Profiling

Antonio Giacomelli edited this page Dec 23, 2025 · 31 revisions

Preemptive test

The following test was performed on an ARM Cortex-M4F @ 80MHz (Nucleo STM32F401RE)

It has been compiled as follows:

  OPT      := -O2
  DEBUG    :=  -DNDEBUG 
  CFLAGS_EXTRA := -g -fstack-usage
  ASFLAGS_EXTRA := -g -D__FPU_PRESENT=0 -DNDEBUG
  STRIPFLAGS :=

COMMON_WARN := -Wall -Wextra -Wsign-compare -Wsign-conversion -pedantic
CFLAGS  := -std=gnu11 $(MCU_FLAGS)  $(DEBUG) \
           $(COMMON_WARN) -ffunction-sections -fdata-sections \
           $(CFLAGS_EXTRA) $(OPT) $(INC_DIRS)

ASFLAGS := $(MCU_FLAGS) -x assembler-with-cpp $(ASFLAGS_EXTRA) -ffunction-sections -fdata-sections
LDFLAGS := -nostartfiles -T $(LINKER_SCRIPT) $(MCU_FLAGS) \
           -Wl,-Map=$(BUILD_DIR)/rk0_demo.map,--cref -Wl,--gc-sections \
           -specs=nano.specs -lc

What it means:

  • NDEBUG flag (error checking is disabled)
  • FPU is disabled
  • Stack overflow checking is disabled
  • Optmisation level: -O2 (adequate for release, not strictly optimised for speed)
  • Debug symbols were not stripped off (-g flags ) so we can inspect symbols for the running test.

Test bench code

This test is based on the ThreadX test-suit

Reproducing the description:

1.3. Preemptive Scheduling Test ? This test consists of 5 threads that each have a unique priority. In this test, all threads except the lowest priority thread are left in a suspended state. The lowest priority thread will resume the next highest priority thread. That thread will resume the next highest priority thread and so on until the highest priority thread executes. Each thread will increment its run count and then call thread suspend. Eventually the processing will return to the lowest priority thread, which is still in the middle of the thread resume call. Once processing returns to the lowest priority thread, it will increment its run counter and once again resume the next highest priority thread - starting the whole process over once again.

The ThreadX code is here: https://github.com/eclipse-threadx/threadx/blob/master/utility/benchmarks/thread_metric/tm_preemptive_scheduling_test.c

For RK0, the same is written is as follows:

#include <application.h>
#include <kapi.h>

/* Preemptive scheduling profiling */
/* Each run has 30 sec */
/* Criteria: counters will be at most +/-1 from the average */

 #define TM_TEST_DURATION   30000UL     /* ms (tick = 1ms) */
 #define TM_FLAG            0x01UL

 #define kSuspendSelf(timeout) \
     do { kTaskFlagsGet(TM_FLAG, RK_FLAGS_ANY, NULL, (timeout)); } while (0)

 #define kResumeTask(taskHandle) \
     do { kTaskFlagsSet((taskHandle), TM_FLAG); } while (0)

#define STACKSIZE 128

RK_DECLARE_TASK(task1Handle, Task1, stack1, STACKSIZE)
RK_DECLARE_TASK(task2Handle, Task2, stack2, STACKSIZE)
RK_DECLARE_TASK(task4Handle, Task4, stack4, STACKSIZE)
RK_DECLARE_TASK(task5Handle, Task5, stack5, STACKSIZE)
RK_DECLARE_TASK(task3Handle, Task3, stack3, STACKSIZE)
RK_DECLARE_TASK(task6Handle, Task6, stack6, STACKSIZE)

 /* counters */

volatile ULONG counter1 = 0UL;
volatile ULONG counter2 = 0UL;
volatile ULONG counter3 = 0UL;
volatile ULONG counter4 = 0UL;
volatile ULONG counter5 = 0UL;


 VOID kApplicationInit(VOID)
 {
     /* lowest priority */
     kCreateTask(&task1Handle, Task1, RK_NO_ARGS, "T0", stack1, STACKSIZE, 6, RK_PREEMPT);
     kCreateTask(&task2Handle, Task2, RK_NO_ARGS, "T1", stack2, STACKSIZE, 5, RK_PREEMPT);
     kCreateTask(&task3Handle, Task3, RK_NO_ARGS, "T2", stack3, STACKSIZE, 4, RK_PREEMPT);
     kCreateTask(&task4Handle, Task4, RK_NO_ARGS, "T3", stack4, STACKSIZE, 3, RK_PREEMPT);
     kCreateTask(&task5Handle, Task5, RK_NO_ARGS, "T4", stack5, STACKSIZE, 2, RK_PREEMPT);
     /* highest priority - reporting task */
     kCreateTask(&task6Handle, Task6, RK_NO_ARGS, "REP", stack6, STACKSIZE, 1, RK_PREEMPT);
 }

/* lowest priority counter task: does not suspend itself */
/* it should be preempted by higher priority tasks */
 VOID Task1(VOID *args)
 {
     RK_UNUSEARGS
     while (1)
     {
         kResumeTask(task2Handle);
         counter1++;
     }
 }

 /*  2..4: start suspended, resume, increment, self-suspend */
VOID Task2(VOID *args)
 {
     RK_UNUSEARGS

     kSuspendSelf(RK_WAIT_FOREVER);

     while (1)
     {
         kResumeTask(task3Handle);
         counter2++;
         kSuspendSelf(RK_WAIT_FOREVER);
     }
 }

 VOID Task3(VOID *args)
 {
     RK_UNUSEARGS

     kSuspendSelf(RK_WAIT_FOREVER);

     while (1)
     {
         kResumeTask(task4Handle);
         counter3++;
         kSuspendSelf(RK_WAIT_FOREVER);
     }
 }

 VOID Task4(VOID *args)
 {
     RK_UNUSEARGS;

     kSuspendSelf(RK_WAIT_FOREVER);

     while (1)
     {
         kResumeTask(task5Handle);
         counter4++;
         kSuspendSelf(RK_WAIT_FOREVER);
     }
 }
/* highest priority counter task: does not resume any task */
 VOID Task5(VOID *args)
 {
     RK_UNUSEARGS

     kSuspendSelf(RK_WAIT_FOREVER);

     while (1)
     {
         counter5++;
         kSuspendSelf(RK_WAIT_FOREVER);
     }
 }
 
 volatile ULONG error = 0UL; 
 volatile ULONG roundn = 0UL;
 volatile RK_TICK time0 = 0UL;
 volatile RK_TICK time1 = 0UL;
 volatile ULONG total = 0UL;
 volatile ULONG average = 0UL;


 /* REPORTING TASK: */
 /* Highest priority, starts, sleeps for 30000 ticks (30sec), interrupts and
evaluate */
  VOID Task6(VOID *args)
 {
     RK_UNUSEARGS

     while (1)
     {
         time0 = kTickGetMs();
         kSleep(TM_TEST_DURATION);
         time1 = kTickGetMs();
         total = counter1 + counter2 + counter3 + counter4 + counter5;
         average = total / 5;
         roundn += 1;

         /* check: each counter within +-1 of average */
         if ( ((LONG)counter1 < (LONG)average - 1) || ((LONG)counter1 > (LONG)average + 1) ||
              ((LONG)counter2 < (LONG)average - 1) || ((LONG)counter2 > (LONG)average + 1) ||
              ((LONG)counter3 < (LONG)average - 1) || ((LONG)counter3 > (LONG)average + 1) ||
              ((LONG)counter4 < (LONG)average - 1) || ((LONG)counter4 > (LONG)average + 1) ||
              ((LONG)counter5 < (LONG)average - 1) || ((LONG)counter5 > (LONG)average + 1) )
         {

             error += 1;
         }

     }
 }

Results

The results can be seen below:

Preemptive test results

After 5 runs:

150 secs test

There was no deviation at all.

Context Switch (Minimal) Latency (in cycles)

TBP

Clone this wiki locally