SuperTinyKernel™ RTOS 1.05.3
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
stk_arch.h
Go to the documentation of this file.
1/*
2 * SuperTinyKernel(TM) RTOS: Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
3 *
4 * Source: https://github.com/SuperTinyKernel-RTOS
5 *
6 * Copyright (c) 2022-2026 Neutron Code Limited <stk@neutroncode.com>. All Rights Reserved.
7 * License: MIT License, see LICENSE for a full text.
8 */
9
10#ifndef STK_ARCH_H_
11#define STK_ARCH_H_
12
24
25// Architecture back-end selection.
26// Exactly one of the following macros must be defined by the build system (e.g. via -D compiler flag):
27// _STK_ARCH_ARM_CORTEX_M — ARM Cortex-M (M0/M0+/M3/M4/M7/M33/M55).
28// _STK_ARCH_RISC_V — RISC-V (RV32I/RV32E/RV64, with optional FPU).
29// _STK_ARCH_X86_WIN32 — x86/x64 on Windows (simulation/test use only).
30//
31// Defining more than one is not supported and will result in multiple conflicting definitions.
32// _STK_ARCH_DEFINED is set by whichever back-end is included; it can be tested by downstream
33// headers or build checks to verify that a valid architecture was selected.
34#ifdef _STK_ARCH_ARM_CORTEX_M
36#define _STK_ARCH_DEFINED
37#endif
38#ifdef _STK_ARCH_RISC_V
40#define _STK_ARCH_DEFINED
41#endif
42#ifdef _STK_ARCH_X86_WIN32
44#define _STK_ARCH_DEFINED
45#endif
46
47namespace stk {
48
63#define STK_KERNEL_PANIC(id) \
64 do { \
65 __stk_debug_break(); /* debug aid */ \
66 STK_PANIC_HANDLER(id); /* must not return */ \
67 } while (0)
68
80namespace hw {
81
93template <typename T>
95{
96 STK_STATIC_ASSERT(sizeof(Word) == sizeof(T *));
97 return reinterpret_cast<Word>(ptr);
98}
99
110template <typename T>
112{
113 STK_STATIC_ASSERT(sizeof(Word) == sizeof(T *));
114 return reinterpret_cast<T *>(value);
115}
116
123bool IsInsideISR();
124
125// Some architectures (e.g. RISC-V with the 'tp' register) can implement TLS access as a
126// single inline instruction. When the back-end header defines _STK_INLINE_TLS_DEFINED,
127// GetTls and SetTls are provided as inline functions there and the declarations below
128// are suppressed to avoid duplicate definitions.
129#if !_STK_INLINE_TLS_DEFINED
130
140
147void SetTls(Word tp);
148
149#endif // _STK_INLINE_TLS_DEFINED
150
157template <class _TyTls>
159{
161}
162
169template <class _TyTls>
170__stk_forceinline void SetTlsPtr(const _TyTls *tp)
171{
173}
174
202{
203public:
218 {
219 public:
223
227
228 private:
230 };
231
240 static void Enter();
241
250 static void Exit();
251
252private:
253 explicit CriticalSection() {}
255};
256
275{
276public:
281 {
284 };
285
288 explicit SpinLock() : m_lock(UNLOCKED)
289 {}
290
299 void Lock();
300
308 void Unlock();
309
317 bool TryLock();
318
325 bool IsLocked() const { return (m_lock == LOCKED); }
326
327protected:
329
330#ifdef _STK_ARCH_X86_WIN32
331 volatile long m_lock;
332#else
333 volatile bool m_lock __stk_aligned(8);
334#endif
335};
336
356template <typename T>
357__stk_forceinline T ReadVolatile64(volatile const T *addr)
358{
359 STK_STATIC_ASSERT_N(sz, sizeof(T) == 8); // only 64-bit types permitted
360 STK_STATIC_ASSERT_N(al, alignof(T) >= 4); // type must be at least 4-byte aligned
361
362 if (sizeof(void *) == 8) // 64-bit arch: aligned 64-bit load is inherently atomic
363 {
364 return (*addr);
365 }
366 else
367 {
368 // 32-bit arch: split the 64-bit address into two 32-bit halves.
369 // Writer always updates hi before lo (see WriteVolatile64), so if hi is
370 // the same before and after reading lo, no write straddled the two reads.
371 volatile const uint32_t *plo = &((volatile const uint32_t *)addr)[STK_ENDIAN_IDX_LO];
372 volatile const uint32_t *phi = &((volatile const uint32_t *)addr)[STK_ENDIAN_IDX_HI];
373
374 uint32_t hi, lo;
375 do
376 {
377 hi = (*phi);
378 __stk_full_memfence();
379
380 lo = (*plo);
381 __stk_full_memfence();
382 }
383 while (hi != (*phi)); // hi changed: a write occurred during the read; retry
384
385 return ((uint64_t)hi << 32) | lo;
386 }
387}
388
410template <typename T>
411__stk_forceinline void WriteVolatile64(volatile T *addr, T value)
412{
413 STK_STATIC_ASSERT_N(sz, sizeof(T) == 8); // only 64-bit types permitted
414 STK_STATIC_ASSERT_N(al, alignof(T) >= 4); // type must be at least 4-byte aligned
415
416 if (sizeof(void *) == 8) // 64-bit arch: aligned 64-bit store is inherently atomic
417 {
418 (*addr) = value;
419 }
420 else
421 {
422 volatile uint32_t *plo = &((volatile uint32_t *)addr)[STK_ENDIAN_IDX_LO];
423 volatile uint32_t *phi = &((volatile uint32_t *)addr)[STK_ENDIAN_IDX_HI];
424
425 // Write hi first: ReadVolatile64 reads hi twice and retries if it changed,
426 // so writing hi before lo ensures readers can detect a torn write.
427 (*phi) = (uint32_t)(value >> 32);
428 __stk_full_memfence();
429
430 (*plo) = (uint32_t)value;
431 }
432}
433
438{
444
449 static uint32_t GetFrequency();
450
455 static inline Ticks GetTimeUs()
456 {
457 uint32_t freq = GetFrequency();
458 STK_ASSERT(freq != 0); // guaranteed non-zero
459
460 return static_cast<Ticks>((GetCycles() * 1000000ULL) / freq);
461 }
462};
463
464} // namespace hw
465
467inline bool ISyncObject::Tick(Timeout elapsed_ticks)
468{
469 // note: ScopedCriticalSection usage
470 //
471 // Single-core: no critical section needed - Tick() runs inside the
472 // SysTick ISR which already executes with interrupts disabled, making
473 // re-entrancy impossible on the local core.
474 //
475 // Multi-core: critical section is required because the tick handler on
476 // each core may call Tick() concurrently for the same Semaphore instance,
477 // and ISyncObject::Tick() is not re-entrant.
478#if (STK_ARCH_CPU_COUNT > 1)
480#endif
481
482 IWaitObject *itr = static_cast<IWaitObject *>(m_wait_list.GetFirst());
483
484 while (itr != nullptr)
485 {
486 IWaitObject *next = static_cast<IWaitObject *>(itr->GetNext());
487
488 if (!itr->Tick(elapsed_ticks))
489 itr->Wake(true);
490
491 itr = next;
492 }
493
494 return !m_wait_list.IsEmpty();
495}
496
497} // namespace stk
498
499#ifndef STK_PANIC_HANDLER
509 #define STK_PANIC_HANDLER(id) STK_PANIC_HANDLER_DEFAULT(id)
510#endif
511
512#endif /* STK_ARCH_H_ */
Platform port for ARM Cortex-M.
Platform port for RISC-V.
Platform port for Windows Win32 (STK emulator).
void STK_PANIC_HANDLER_DEFAULT(stk::EKernelPanicId id)
Default panic handler: disable interrupts, record the id, and spin in a tight loop — a defined,...
Definition stktest.cpp:55
#define STK_STATIC_ASSERT_N(NAME, X)
Compile-time assertion with a user-defined name suffix.
Definition stk_defs.h:359
#define STK_ENDIAN_IDX_LO
Array index of the low 32-bit word when a 64-bit value is viewed as uint32_t[2].
Definition stk_defs.h:503
#define __stk_forceinline
Forces compiler to always inline the decorated function, regardless of optimisation level.
Definition stk_defs.h:104
#define STK_ENDIAN_IDX_HI
Array index of the high 32-bit word when a 64-bit value is viewed as uint32_t[2].
Definition stk_defs.h:502
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
Definition stk_defs.h:330
#define STK_STATIC_ASSERT(X)
Compile-time assertion. Produces a compilation error if X is false.
Definition stk_defs.h:367
Namespace of STK package.
uintptr_t Word
Native processor word type.
Definition stk_common.h:112
int64_t Ticks
Ticks value.
Definition stk_common.h:150
int32_t Timeout
Timeout time (ticks).
Definition stk_common.h:133
uint64_t Cycles
Cycles value.
Definition stk_common.h:155
EKernelPanicId
Identifies the source of a kernel panic.
Definition stk_common.h:52
Hardware Abstraction Layer (HAL) for architecture-specific operations.
__stk_forceinline T * WordToPtr(Word value) noexcept
Cast a CPU register-width integer back to a pointer.
Definition stk_arch.h:111
__stk_forceinline void WriteVolatile64(volatile T *addr, T value)
Atomically write a 64-bit volatile value.
Definition stk_arch.h:411
__stk_forceinline Word PtrToWord(T *ptr) noexcept
Cast a pointer to a CPU register-width integer.
Definition stk_arch.h:94
__stk_forceinline void SetTlsPtr(const _TyTls *tp)
Type-safe wrapper around SetTls() that stores a typed pointer as the raw TP value.
Definition stk_arch.h:170
void SetTls(Word tp)
Write raw thread-pointer (TP) register used as per-task TLS storage.
__stk_forceinline _TyTls * GetTlsPtr()
Type-safe wrapper around GetTls() that casts the raw TP value to a typed pointer.
Definition stk_arch.h:158
Word GetTls()
Read raw thread-pointer (TP) register used as per-task TLS storage.
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
Definition stktest.cpp:103
__stk_forceinline T ReadVolatile64(volatile const T *addr)
Atomically read a 64-bit volatile value.
Definition stk_arch.h:357
STK_NONCOPYABLE_CLASS(CriticalSection)
static void Exit()
Exit a critical section.
Definition stktest.cpp:78
static void Enter()
Enter a critical section.
Definition stktest.cpp:74
RAII guard that enters the critical section on construction and exits it on destruction.
Definition stk_arch.h:218
ScopedLock()
Enter the critical section.
Definition stk_arch.h:222
~ScopedLock()
Exit the critical section.
Definition stk_arch.h:226
bool TryLock()
Attempt to acquire SpinLock in a single non-blocking attempt.
SpinLock()
Construct a SpinLock (unlocked by default).
Definition stk_arch.h:288
EState
Internal lock state values.
Definition stk_arch.h:281
@ UNLOCKED
Lock is free and available for acquisition.
Definition stk_arch.h:282
@ LOCKED
Lock is held by a thread or core.
Definition stk_arch.h:283
STK_NONCOPYABLE_CLASS(SpinLock)
bool IsLocked() const
Sample current lock state.
Definition stk_arch.h:325
volatile bool m_lock __stk_aligned(8)
Lock state (see EState). 8-byte aligned to occupy its own cache line word and avoid false sharing on ...
void Lock()
Acquire SpinLock, blocking until it is available.
Definition stktest.cpp:85
void Unlock()
Release SpinLock, allowing another thread or core to acquire it.
Definition stktest.cpp:89
High-resolution clock for high-precision measurements.
Definition stk_arch.h:438
static uint32_t GetFrequency()
Get clock frequency.
static Ticks GetTimeUs()
Get elapsed time in microseconds.
Definition stk_arch.h:455
static Cycles GetCycles()
Get number of clock cycles elapsed.
Wait object.
Definition stk_common.h:212
virtual void Wake(bool timeout)=0
Wake task.
virtual bool Tick(Timeout elapsed_ticks)=0
Update wait object's waiting time.
IWaitObject::ListHeadType m_wait_list
tasks blocked on this object
Definition stk_common.h:373
virtual bool Tick(Timeout elapsed_ticks)
Called by kernel on every system tick to handle timeout logic of waiting tasks.
Definition stk_arch.h:467
DLEntryType * GetNext() const
Get the next entry in the list.