Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cmake-test-generic-stm32.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
matrix:
include:
- name: "Configure CMake (Board: STM32F407DISC1, CPU: Arm Cortex-M4)"
cmake_flags: -DVENDOR_STM32=ON -DTARGET_CORTEX_M4=ON -DTARGET_CPU_FAMILY=STM32F407xx
cmake_flags: -DVENDOR_STM32=ON -DTARGET_CORTEX_M4=ON -DTARGET_CPU_FAMILY=STM32F407xx -DENABLE_TLS=ON
cpu: cortex-m4
board: STM32F4-Discovery

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cmake-test-generic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v6

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

- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel 4
Expand Down
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,17 @@ if (BUILD_DOC)
endif()

# Declare tickles mode if enabled (see stk_config.h.in)
if (MODE_TICKLESS)
if (ENABLE_TICKLESS)
message(STATUS "* tickless mode")
set(STK_TICKLESS_IDLE 1)
endif()

# Declare tickles mode if enabled (see stk_config.h.in)
if (ENABLE_TLS)
message(STATUS "* enable TLS")
set(STK_TLS 1)
endif()

# Create stk_config.h (note: must be defined after inclusion of the projects to catch value of _STK_DEVICE_INC)
if (WIN32)
set(_STK_ARCH_X86_WIN32 TRUE)
Expand Down
79 changes: 41 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,55 +571,54 @@ Compatible with:
Below example toggles RGB LEDs on a development board. Each LED is controlled by its own thread, switching at 1s intervals:

```cpp
#include <stk_config.h>
#include <stk.h>
#include "example.h"
#include <sync/stk_sync_eventflags.h>

static volatile uint8_t g_TaskSwitch = 0;
using namespace bsp;

enum { TASK_STACK_SIZE = 256 };

static const uint32_t FLAGS_ALL[] = {
(1U << LED_RED),
(1U << LED_ORANGE),
(1U << LED_GREEN),
(1U << LED_BLUE)
};
static stk::sync::EventFlags g_TaskFlags(FLAGS_ALL[LED_RED]);
static stk::Ticks g_Timeline = 0;

template <stk::EAccessMode _AccessMode>
class MyTask : public stk::Task<256, _AccessMode>
class MyTask : public stk::Task<TASK_STACK_SIZE, _AccessMode>
{
uint8_t m_taskId;
uint8_t m_task_id;
uint32_t m_my_flag;
uint32_t m_next_flag;

public:
MyTask(uint8_t taskId) : m_taskId(taskId)
MyTask(uint8_t task_id) : m_task_id(task_id), m_my_flag(FLAGS_ALL[task_id]),
m_next_flag(FLAGS_ALL[(task_id + 1) % LED_MAX])
{}

private:
void Run()
void Run() override
{
uint8_t task_id = m_taskId;
const stk::Timeout period = stk::GetTicksFromMs(1000);
g_Timeline = stk::GetTicks();

while (true)
{
if (g_TaskSwitch != task_id)
{
stk::Sleep(10);
uint32_t result = g_TaskFlags.Wait(m_my_flag, stk::sync::EventFlags::OPT_WAIT_ANY);
if (stk::sync::EventFlags::IsError(result))
continue;
}

switch (task_id)
{
case 0:
LED_SET_STATE(LED_RED, true);
LED_SET_STATE(LED_GREEN, false);
LED_SET_STATE(LED_BLUE, false);
break;
case 1:
LED_SET_STATE(LED_RED, false);
LED_SET_STATE(LED_GREEN, true);
LED_SET_STATE(LED_BLUE, false);
break;
case 2:
LED_SET_STATE(LED_RED, false);
LED_SET_STATE(LED_GREEN, false);
LED_SET_STATE(LED_BLUE, true);
break;
stk::hw::CriticalSection::ScopedLock __guard;
Led::SwitchOnExclusive(static_cast<LedId>(m_task_id));
}

stk::Sleep(1000);
g_TaskSwitch = (task_id + 1) % 3;
stk::SleepUntil(g_Timeline += period);

g_TaskFlags.Set(m_next_flag);
}
}
};
Expand All @@ -628,23 +627,27 @@ void RunExample()
{
using namespace stk;

LED_INIT(LED_RED, false);
LED_INIT(LED_GREEN, false);
LED_INIT(LED_BLUE, false);
Led::InitAll(false);

const uint8_t KernelMode = KERNEL_STATIC | KERNEL_SYNC | (STK_TICKLESS_IDLE ? KERNEL_TICKLESS : 0);

static Kernel<KernelMode, 4, SwitchStrategyRR, PlatformDefault> kernel;

static Kernel<KERNEL_STATIC, 3, SwitchStrategyRoundRobin, PlatformDefault> kernel;
static MyTask<ACCESS_PRIVILEGED> task1(0), task2(1), task3(2);
static MyTask<ACCESS_PRIVILEGED> task1(LED_RED);
static MyTask<ACCESS_PRIVILEGED> task2(LED_ORANGE);
static MyTask<ACCESS_PRIVILEGED> task3(LED_GREEN);
static MyTask<ACCESS_PRIVILEGED> task4(LED_BLUE);

kernel.Initialize(PERIODICITY_DEFAULT);
kernel.Initialize();

kernel.AddTask(&task1);
kernel.AddTask(&task2);
kernel.AddTask(&task3);
kernel.AddTask(&task4);

kernel.Start();

assert(false);
while (true);
STK_ASSERT(false);
}
```

Expand Down
112 changes: 49 additions & 63 deletions build/example/blinky/example.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SuperTinyKernel™ (STK): Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
* SuperTinyKernel(TM) RTOS: Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
*
* Source: https://github.com/SuperTinyKernel-RTOS
*
Expand All @@ -9,112 +9,97 @@

#include <stk_config.h>
#include <stk.h>
#include <sync/stk_sync_eventflags.h>
#include "example.h"

using namespace bsp;

// R2350 requires larger stack due to stack-memory heavy SDK API
#ifdef _PICO_H
enum { TASK_STACK_SIZE = 1024 };
#else
enum { TASK_STACK_SIZE = 256 };
#endif

static volatile uint8_t g_TaskSwitch = 0;
// One flag bit per LED task; task 0 (RED) goes first
static const uint32_t FLAGS_ALL[] = {
(1U << LED_RED),
(1U << LED_ORANGE),
(1U << LED_GREEN),
(1U << LED_BLUE)
};

// Start with the RED task's flag set so it runs first
static stk::sync::EventFlags g_TaskFlags(FLAGS_ALL[LED_RED]);

// Timeline for a precise LED switching
static stk::Ticks g_Timeline = 0;

// Task's core (thread)
template <stk::EAccessMode _AccessMode>
class MyTask : public stk::Task<TASK_STACK_SIZE, _AccessMode>
{
uint8_t m_task_id;
const char *m_name;
uint8_t m_task_id;
uint32_t m_my_flag;
uint32_t m_next_flag;

public:
MyTask(uint8_t task_id, const char *name) : m_task_id(task_id), m_name(name)
MyTask(uint8_t task_id) : m_task_id(task_id), m_my_flag(FLAGS_ALL[task_id]),
m_next_flag(FLAGS_ALL[(task_id + 1) % LED_MAX])
{}

size_t GetId() const { return m_task_id; }
const char *GetName() const { return m_name; }

private:
void Run() override
{
uint8_t task_id = m_task_id;
stk::Ticks ts = 0;
// we switch LEDs with 1s period
const stk::Timeout period = stk::GetTicksFromMs(1000);

// get a start of the timeline
g_Timeline = stk::GetTicks();

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

// change active LED
{
stk::hw::CriticalSection::ScopedLock __guard;

SwitchOnLED(task_id);
Led::SwitchOnExclusive(static_cast<LedId>(m_task_id));
}

ts = stk::hw::HiResClock::GetTimeUs();

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

stk::Cycles diff = stk::hw::HiResClock::GetTimeUs() - ts;
(void)diff;

g_TaskSwitch = (task_id + 1) % 3;
}
}

static void SwitchOnLED(uint8_t task_id)
{
switch (task_id)
{
case 0:
Led::Set(Led::RED, true);
Led::Set(Led::GREEN, false);
Led::Set(Led::BLUE, false);
break;
case 1:
Led::Set(Led::RED, false);
Led::Set(Led::GREEN, true);
Led::Set(Led::BLUE, false);
break;
case 2:
Led::Set(Led::RED, false);
Led::Set(Led::GREEN, false);
Led::Set(Led::BLUE, true);
break;
// hand off to the next task
g_TaskFlags.Set(m_next_flag);
}
}
};

static void InitLeds()
{
Led::Init(Led::RED, false);
Led::Init(Led::GREEN, false);
Led::Init(Led::BLUE, false);
}

void RunExample()
{
using namespace stk;

InitLeds();
Led::InitAll(false);

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

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

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

// init scheduling kernel
kernel.Initialize();
Expand All @@ -123,6 +108,7 @@ void RunExample()
kernel.AddTask(&task1);
kernel.AddTask(&task2);
kernel.AddTask(&task3);
kernel.AddTask(&task4);

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