Skip to content

Commit 1f28b23

Browse files
authored
Improve all examples by employing sync primitives.
Additionally: - Fix tickless mode of ARM Cortex-M arch: SysTick value exceeded 24-bit limit on MCUs with high frequency resulting in a wrong sleep duration. - Document requirement for -ffixed-r9 compiler flag for ARM Cortex-M if using stk::SetTls/stk::GetTls. - Make TLS optional via STK_TLS macro. - Add tick/time/timout types to C interface.
2 parents 5d7676e + b95f85e commit 1f28b23

65 files changed

Lines changed: 1618 additions & 686 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/cmake-test-generic-stm32.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
matrix:
1616
include:
1717
- name: "Configure CMake (Board: STM32F407DISC1, CPU: Arm Cortex-M4)"
18-
cmake_flags: -DVENDOR_STM32=ON -DTARGET_CORTEX_M4=ON -DTARGET_CPU_FAMILY=STM32F407xx
18+
cmake_flags: -DVENDOR_STM32=ON -DTARGET_CORTEX_M4=ON -DTARGET_CPU_FAMILY=STM32F407xx -DENABLE_TLS=ON
1919
cpu: cortex-m4
2020
board: STM32F4-Discovery
2121

.github/workflows/cmake-test-generic.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- uses: actions/checkout@v6
1818

1919
- name: Configure CMake
20-
run: cmake -G "Unix Makefiles" -B ${{github.workspace}}/build -DBUILD_LIB=ON -DBUILD_TESTS=ON -DTEST_GENERIC=ON
20+
run: cmake -G "Unix Makefiles" -B ${{github.workspace}}/build -DBUILD_LIB=ON -DBUILD_TESTS=ON -DTEST_GENERIC=ON -DENABLE_TLS=ON
2121

2222
- name: Build
2323
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel 4

CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,17 @@ if (BUILD_DOC)
8585
endif()
8686

8787
# Declare tickles mode if enabled (see stk_config.h.in)
88-
if (MODE_TICKLESS)
88+
if (ENABLE_TICKLESS)
8989
message(STATUS "* tickless mode")
9090
set(STK_TICKLESS_IDLE 1)
9191
endif()
9292

93+
# Declare tickles mode if enabled (see stk_config.h.in)
94+
if (ENABLE_TLS)
95+
message(STATUS "* enable TLS")
96+
set(STK_TLS 1)
97+
endif()
98+
9399
# Create stk_config.h (note: must be defined after inclusion of the projects to catch value of _STK_DEVICE_INC)
94100
if (WIN32)
95101
set(_STK_ARCH_X86_WIN32 TRUE)

README.md

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -571,55 +571,54 @@ Compatible with:
571571
Below example toggles RGB LEDs on a development board. Each LED is controlled by its own thread, switching at 1s intervals:
572572

573573
```cpp
574-
#include <stk_config.h>
575574
#include <stk.h>
576-
#include "example.h"
575+
#include <sync/stk_sync_eventflags.h>
577576

578-
static volatile uint8_t g_TaskSwitch = 0;
577+
using namespace bsp;
578+
579+
enum { TASK_STACK_SIZE = 256 };
580+
581+
static const uint32_t FLAGS_ALL[] = {
582+
(1U << LED_RED),
583+
(1U << LED_ORANGE),
584+
(1U << LED_GREEN),
585+
(1U << LED_BLUE)
586+
};
587+
static stk::sync::EventFlags g_TaskFlags(FLAGS_ALL[LED_RED]);
588+
static stk::Ticks g_Timeline = 0;
579589

580590
template <stk::EAccessMode _AccessMode>
581-
class MyTask : public stk::Task<256, _AccessMode>
591+
class MyTask : public stk::Task<TASK_STACK_SIZE, _AccessMode>
582592
{
583-
uint8_t m_taskId;
593+
uint8_t m_task_id;
594+
uint32_t m_my_flag;
595+
uint32_t m_next_flag;
584596

585597
public:
586-
MyTask(uint8_t taskId) : m_taskId(taskId)
598+
MyTask(uint8_t task_id) : m_task_id(task_id), m_my_flag(FLAGS_ALL[task_id]),
599+
m_next_flag(FLAGS_ALL[(task_id + 1) % LED_MAX])
587600
{}
588601

589602
private:
590-
void Run()
603+
void Run() override
591604
{
592-
uint8_t task_id = m_taskId;
605+
const stk::Timeout period = stk::GetTicksFromMs(1000);
606+
g_Timeline = stk::GetTicks();
593607

594608
while (true)
595609
{
596-
if (g_TaskSwitch != task_id)
597-
{
598-
stk::Sleep(10);
610+
uint32_t result = g_TaskFlags.Wait(m_my_flag, stk::sync::EventFlags::OPT_WAIT_ANY);
611+
if (stk::sync::EventFlags::IsError(result))
599612
continue;
600-
}
601613

602-
switch (task_id)
603614
{
604-
case 0:
605-
LED_SET_STATE(LED_RED, true);
606-
LED_SET_STATE(LED_GREEN, false);
607-
LED_SET_STATE(LED_BLUE, false);
608-
break;
609-
case 1:
610-
LED_SET_STATE(LED_RED, false);
611-
LED_SET_STATE(LED_GREEN, true);
612-
LED_SET_STATE(LED_BLUE, false);
613-
break;
614-
case 2:
615-
LED_SET_STATE(LED_RED, false);
616-
LED_SET_STATE(LED_GREEN, false);
617-
LED_SET_STATE(LED_BLUE, true);
618-
break;
615+
stk::hw::CriticalSection::ScopedLock __guard;
616+
Led::SwitchOnExclusive(static_cast<LedId>(m_task_id));
619617
}
620618

621-
stk::Sleep(1000);
622-
g_TaskSwitch = (task_id + 1) % 3;
619+
stk::SleepUntil(g_Timeline += period);
620+
621+
g_TaskFlags.Set(m_next_flag);
623622
}
624623
}
625624
};
@@ -628,23 +627,27 @@ void RunExample()
628627
{
629628
using namespace stk;
630629

631-
LED_INIT(LED_RED, false);
632-
LED_INIT(LED_GREEN, false);
633-
LED_INIT(LED_BLUE, false);
630+
Led::InitAll(false);
631+
632+
const uint8_t KernelMode = KERNEL_STATIC | KERNEL_SYNC | (STK_TICKLESS_IDLE ? KERNEL_TICKLESS : 0);
633+
634+
static Kernel<KernelMode, 4, SwitchStrategyRR, PlatformDefault> kernel;
634635

635-
static Kernel<KERNEL_STATIC, 3, SwitchStrategyRoundRobin, PlatformDefault> kernel;
636-
static MyTask<ACCESS_PRIVILEGED> task1(0), task2(1), task3(2);
636+
static MyTask<ACCESS_PRIVILEGED> task1(LED_RED);
637+
static MyTask<ACCESS_PRIVILEGED> task2(LED_ORANGE);
638+
static MyTask<ACCESS_PRIVILEGED> task3(LED_GREEN);
639+
static MyTask<ACCESS_PRIVILEGED> task4(LED_BLUE);
637640

638-
kernel.Initialize(PERIODICITY_DEFAULT);
641+
kernel.Initialize();
639642

640643
kernel.AddTask(&task1);
641644
kernel.AddTask(&task2);
642645
kernel.AddTask(&task3);
646+
kernel.AddTask(&task4);
643647

644648
kernel.Start();
645649

646-
assert(false);
647-
while (true);
650+
STK_ASSERT(false);
648651
}
649652
```
650653

build/example/blinky/example.cpp

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SuperTinyKernel™ (STK): Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
2+
* SuperTinyKernel(TM) RTOS: Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
33
*
44
* Source: https://github.com/SuperTinyKernel-RTOS
55
*
@@ -9,112 +9,97 @@
99

1010
#include <stk_config.h>
1111
#include <stk.h>
12+
#include <sync/stk_sync_eventflags.h>
1213
#include "example.h"
1314

15+
using namespace bsp;
16+
1417
// R2350 requires larger stack due to stack-memory heavy SDK API
1518
#ifdef _PICO_H
1619
enum { TASK_STACK_SIZE = 1024 };
1720
#else
1821
enum { TASK_STACK_SIZE = 256 };
1922
#endif
2023

21-
static volatile uint8_t g_TaskSwitch = 0;
24+
// One flag bit per LED task; task 0 (RED) goes first
25+
static const uint32_t FLAGS_ALL[] = {
26+
(1U << LED_RED),
27+
(1U << LED_ORANGE),
28+
(1U << LED_GREEN),
29+
(1U << LED_BLUE)
30+
};
31+
32+
// Start with the RED task's flag set so it runs first
33+
static stk::sync::EventFlags g_TaskFlags(FLAGS_ALL[LED_RED]);
34+
35+
// Timeline for a precise LED switching
36+
static stk::Ticks g_Timeline = 0;
2237

2338
// Task's core (thread)
2439
template <stk::EAccessMode _AccessMode>
2540
class MyTask : public stk::Task<TASK_STACK_SIZE, _AccessMode>
2641
{
27-
uint8_t m_task_id;
28-
const char *m_name;
42+
uint8_t m_task_id;
43+
uint32_t m_my_flag;
44+
uint32_t m_next_flag;
2945

3046
public:
31-
MyTask(uint8_t task_id, const char *name) : m_task_id(task_id), m_name(name)
47+
MyTask(uint8_t task_id) : m_task_id(task_id), m_my_flag(FLAGS_ALL[task_id]),
48+
m_next_flag(FLAGS_ALL[(task_id + 1) % LED_MAX])
3249
{}
3350

34-
size_t GetId() const { return m_task_id; }
35-
const char *GetName() const { return m_name; }
36-
3751
private:
3852
void Run() override
3953
{
40-
uint8_t task_id = m_task_id;
41-
stk::Ticks ts = 0;
54+
// we switch LEDs with 1s period
55+
const stk::Timeout period = stk::GetTicksFromMs(1000);
56+
57+
// get a start of the timeline
58+
g_Timeline = stk::GetTicks();
4259

4360
while (true)
4461
{
45-
if (g_TaskSwitch != task_id)
46-
{
47-
// to avoid hot loop and excessive CPU usage sleep 10ms while waiting for the own turn,
48-
// if scheduler does not have active threads then it will fall into a sleep mode which is
49-
// saving the consumed power
50-
stk::Sleep(100);
62+
// block until this task's flag is set; auto-cleared on return
63+
uint32_t result = g_TaskFlags.Wait(m_my_flag, stk::sync::EventFlags::OPT_WAIT_ANY);
64+
if (stk::sync::EventFlags::IsError(result))
5165
continue;
52-
}
5366

67+
// change active LED
5468
{
5569
stk::hw::CriticalSection::ScopedLock __guard;
56-
57-
SwitchOnLED(task_id);
70+
Led::SwitchOnExclusive(static_cast<LedId>(m_task_id));
5871
}
5972

60-
ts = stk::hw::HiResClock::GetTimeUs();
61-
62-
// sleep 1s and delegate work to another task switching another LED
63-
stk::Sleep(1000);
73+
// sleep 1s drift-free and then delegate work to the next task
74+
// we could use simple stk::Sleep() but due to other work around Sleep call we
75+
// will get a time drift, STK allows to sleep until exact timestamp making it
76+
// possible precise sleeping with 1 tick precision, you could also use
77+
// time::TimerHost for timer-related tasks (see related 'timer' example)
78+
stk::SleepUntil(g_Timeline += period);
6479

65-
stk::Cycles diff = stk::hw::HiResClock::GetTimeUs() - ts;
66-
(void)diff;
67-
68-
g_TaskSwitch = (task_id + 1) % 3;
69-
}
70-
}
71-
72-
static void SwitchOnLED(uint8_t task_id)
73-
{
74-
switch (task_id)
75-
{
76-
case 0:
77-
Led::Set(Led::RED, true);
78-
Led::Set(Led::GREEN, false);
79-
Led::Set(Led::BLUE, false);
80-
break;
81-
case 1:
82-
Led::Set(Led::RED, false);
83-
Led::Set(Led::GREEN, true);
84-
Led::Set(Led::BLUE, false);
85-
break;
86-
case 2:
87-
Led::Set(Led::RED, false);
88-
Led::Set(Led::GREEN, false);
89-
Led::Set(Led::BLUE, true);
90-
break;
80+
// hand off to the next task
81+
g_TaskFlags.Set(m_next_flag);
9182
}
9283
}
9384
};
9485

95-
static void InitLeds()
96-
{
97-
Led::Init(Led::RED, false);
98-
Led::Init(Led::GREEN, false);
99-
Led::Init(Led::BLUE, false);
100-
}
101-
10286
void RunExample()
10387
{
10488
using namespace stk;
10589

106-
InitLeds();
90+
Led::InitAll(false);
10791

108-
// operating in Static mode (tasks never exit) and if config requested also in tickless (STK_TICKLESS_IDLE=1) mode
109-
const uint8_t KernelMode = KERNEL_STATIC | (STK_TICKLESS_IDLE ? KERNEL_TICKLESS : 0);
92+
// operating in Static + Sync mode (EventFlags requires KERNEL_SYNC) and optionally tickless
93+
const uint8_t KernelMode = KERNEL_STATIC | KERNEL_SYNC | (STK_TICKLESS_IDLE ? KERNEL_TICKLESS : 0);
11094

11195
// allocate scheduling kernel for 3 threads (tasks) with Round-Robin scheduling strategy
112-
static Kernel<KernelMode, 3, SwitchStrategyRR, PlatformDefault> kernel;
96+
static Kernel<KernelMode, 4, SwitchStrategyRR, PlatformDefault> kernel;
11397

11498
// note: using ACCESS_PRIVILEGED as Cortex-M3+ may not allow writing to GPIO from a less secure user thread
115-
static MyTask<ACCESS_PRIVILEGED> task1(0, "LED-red");
116-
static MyTask<ACCESS_PRIVILEGED> task2(1, "LED-grn");
117-
static MyTask<ACCESS_PRIVILEGED> task3(2, "LED-blu");
99+
static MyTask<ACCESS_PRIVILEGED> task1(LED_RED);
100+
static MyTask<ACCESS_PRIVILEGED> task2(LED_ORANGE);
101+
static MyTask<ACCESS_PRIVILEGED> task3(LED_GREEN);
102+
static MyTask<ACCESS_PRIVILEGED> task4(LED_BLUE);
118103

119104
// init scheduling kernel
120105
kernel.Initialize();
@@ -123,6 +108,7 @@ void RunExample()
123108
kernel.AddTask(&task1);
124109
kernel.AddTask(&task2);
125110
kernel.AddTask(&task3);
111+
kernel.AddTask(&task4);
126112

127113
// start scheduler (it will start threads added by AddTask), execution in main() will be blocked on this line
128114
kernel.Start();

0 commit comments

Comments
 (0)