-
Notifications
You must be signed in to change notification settings - Fork 2
RK0 Profiling
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:
-
NDEBUGflag (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 (
-gflags ) so we can inspect symbols for the running test.
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;
}
}
}The results can be seen below:
After 5 runs:
There was no deviation at all.
TBP
Copyright (C) 2025 Antonio Giacomelli | www.kernel0.org

