SuperTinyKernel™ RTOS 1.06.x
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
47#ifndef STK_PANIC_HANDLER
57 #define STK_PANIC_HANDLER(id) STK_PANIC_HANDLER_DEFAULT(id)
58#endif
59
60namespace stk {
61
76{
77 __stk_debug_break(); // debug aid
78 STK_PANIC_HANDLER(id); // must not return
79}
80
92namespace hw {
93
105template <typename T>
106static constexpr Word PtrToWord(T *const ptr) noexcept
107{
108 STK_STATIC_ASSERT(sizeof(Word) == sizeof(T *));
109 return reinterpret_cast<Word>(ptr);
110}
111
122template <typename T>
123static constexpr T *WordToPtr(Word value) noexcept
124{
125 STK_STATIC_ASSERT(sizeof(Word) == sizeof(T *));
126 return reinterpret_cast<T *>(value);
127}
128
136
142
143// Some architectures (e.g. RISC-V with the 'tp' register) can implement TLS access as a
144// single inline instruction. When the back-end header defines STK_INLINE_TLS,
145// GetTls and SetTls are provided as inline functions there and the declarations below
146// are suppressed to avoid duplicate definitions.
147#if STK_TLS && !STK_INLINE_TLS
148
159Word GetTls();
160
169void SetTls(Word tp);
170
171#endif // STK_INLINE_TLS
172
173#if STK_TLS
182template <class _TyTls>
183__stk_forceinline _TyTls *GetTlsPtr()
184{
186}
187
196template <class _TyTls>
197__stk_forceinline void SetTlsPtr(const _TyTls *tp)
198{
200}
201#endif // STK_TLS
202
222template <typename T>
223static __stk_forceinline T ReadVolatile64(volatile const T *addr)
224{
225 STK_STATIC_ASSERT_N(sz, sizeof(T) == 8U); // only 64-bit types permitted
226 STK_STATIC_ASSERT_N(al, alignof(T) >= 4U); // type must be at least 4-byte aligned
229
230 if __stk_constexpr_cpp17 (sizeof(void *) == 8U) // 64-bit arch: aligned 64-bit load is inherently atomic
231 {
232 return (*addr);
233 }
234 else
235 {
236 // 32-bit arch: split the 64-bit address into two 32-bit halves;
237 // writer always updates hi before lo (see WriteVolatile64), so if hi is
238 // the same before and after reading lo, no write straddled the two reads.
239 #if STK_STRICT_COMPLIANCY
240 const Word p_base = hw::PtrToWord(addr);
241 volatile const uint32_t *const plo = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_LO * sizeof(uint32_t)));
242 volatile const uint32_t *const phi = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_HI * sizeof(uint32_t)));
243 #else
244 volatile const uint32_t *const p_base = reinterpret_cast<volatile const uint32_t *>(addr);
245 volatile const uint32_t *const plo = &p_base[STK_ENDIAN_IDX_LO];
246 volatile const uint32_t *const phi = &p_base[STK_ENDIAN_IDX_HI];
247 #endif
248
249 uint32_t hi, lo;
250 do
251 {
252 hi = (*phi);
253 __stk_full_memfence();
254
255 lo = (*plo);
256 __stk_full_memfence();
257 }
258 while (hi != (*phi)); // hi changed: a write occurred during the read; retry
259
260 const uint64_t result = (static_cast<uint64_t>(hi) << 32U) | static_cast<uint64_t>(lo);
261
262 return static_cast<T>(result);
263 }
264}
265
287template <typename T>
288static __stk_forceinline void WriteVolatile64(volatile T *addr, T value)
289{
290 STK_STATIC_ASSERT_N(sz, sizeof(T) == 8U); // only 64-bit types permitted
291 STK_STATIC_ASSERT_N(al, alignof(T) >= 4U); // type must be at least 4-byte aligned
294
295 if __stk_constexpr_cpp17 (sizeof(void *) == 8U) // 64-bit arch: aligned 64-bit store is inherently atomic
296 {
297 (*addr) = value;
298 }
299 else
300 {
301 #if STK_STRICT_COMPLIANCY
302 const Word p_base = hw::PtrToWord(addr);
303 volatile uint32_t *const plo = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_LO * sizeof(uint32_t)));
304 volatile uint32_t *const phi = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_HI * sizeof(uint32_t)));
305 #else
306 volatile uint32_t *const p_base = reinterpret_cast<volatile uint32_t *>(addr);
307 volatile uint32_t *const plo = &p_base[STK_ENDIAN_IDX_LO];
308 volatile uint32_t *const phi = &p_base[STK_ENDIAN_IDX_HI];
309 #endif
310
311 // write hi first: ReadVolatile64 reads hi twice and retries if it changed,
312 // so writing hi before lo ensures readers can detect a torn write.
313 (*phi) = static_cast<uint32_t>(static_cast<uint64_t>(value) >> 32U);
314 __stk_full_memfence();
315
316 (*plo) = static_cast<uint32_t>(value);
317 }
318}
319
347{
348public:
363 {
364 public:
368
372
373 private:
375 };
376
385 static void Enter();
386
395 static void Exit();
396
397private:
398 explicit CriticalSection() {}
400};
401
420{
421public:
426 {
429 };
430
433 explicit SpinLock() : m_lock(UNLOCKED)
434 {}
435
444 void Lock();
445
453 void Unlock();
454
462 bool TryLock();
463
470 bool IsLocked() const { return (m_lock == LOCKED); }
471
472protected:
474
475#ifdef _STK_ARCH_X86_WIN32
476 volatile long m_lock;
477#else
478 volatile bool m_lock __stk_aligned(8);
479#endif
480};
481
486{
492
497 static uint32_t GetFrequency();
498
504 {
505 Ticks ticks = 0LL;
506 const uint32_t freq = GetFrequency();
507
508 if (freq != 0U)
509 {
510 const Cycles cycles = GetCycles();
511 const Cycles ticksu = (cycles * 1000000ULL) / static_cast<Cycles>(freq);
512
513 ticks = static_cast<Ticks>(ticksu);
514 }
515
516 return ticks;
517 }
518};
519
520} // namespace hw
521
526static constexpr TId GetTidFromUserTask(const ITask *task) noexcept { return hw::PtrToWord(task); }
527
532static constexpr ITask *GetUserTaskFromTid(TId task_id) noexcept { return hw::WordToPtr<ITask>(task_id); }
533
534} // namespace stk
535
539#ifndef _STK_CUSTOM_MEMCPY
540static __stk_forceinline void STK_MEMCPY(void *const dest, const void *const src, const size_t size)
541{
542 using namespace stk;
543
544 if ((dest != nullptr) && (src != nullptr) && (size != 0U))
545 {
546 const Word dest_addr = hw::PtrToWord(dest);
547 const Word src_addr = hw::PtrToWord(src);
548
549 // fast path: check if destination, source, and size are all 4-byte aligned
550 // then copy data in 4-byte chunks
551 if (((dest_addr & 0x03U) == 0U) &&
552 ((src_addr & 0x03U) == 0U) &&
553 ((size & 0x03U) == 0U))
554 {
555 uint32_t *const p_d32 = static_cast<uint32_t *>(dest);
556 const uint32_t *const p_s32 = static_cast<const uint32_t *>(src);
557 const size_t words = (size >> 2U);
558
559 STK_UNUSED(std::copy_n(p_s32, words, p_d32));
560 }
561 // slow path
562 else
563 {
564 uint8_t *const p_d = static_cast<uint8_t *>(dest);
565 const uint8_t *const p_s = static_cast<const uint8_t *>(src);
566
567 STK_UNUSED(std::copy_n(p_s, size, p_d));
568 }
569 }
570}
571#endif
572
573#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,...
static __stk_forceinline void STK_MEMCPY(void *const dest, const void *const src, const size_t size)
A wrapper for a built-in memcpy, redefine to your own if required.
Definition stk_arch.h:540
#define STK_PANIC_HANDLER(id)
Definition stk_arch.h:57
#define STK_UNUSED(X)
Explicitly marks a variable as unused to suppress compiler warnings.
Definition stk_defs.h:608
#define STK_STATIC_ASSERT_N(NAME, X)
Compile-time assertion with a user-defined name suffix.
Definition stk_defs.h:438
#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:587
#define __stk_forceinline
Forces compiler to always inline the decorated function, regardless of optimisation level.
Definition stk_defs.h:175
#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:586
#define __stk_constexpr_cpp17
constexpr definition for C++17 and above.
Definition stk_defs.h:382
static void __stk_debug_break()
Definition stk_defs.h:372
#define STK_STATIC_ASSERT(X)
Compile-time assertion. Produces a compilation error if X is false.
Definition stk_defs.h:446
Namespace of STK package.
uintptr_t Word
Native processor word type.
Definition stk_common.h:136
static constexpr ITask * GetUserTaskFromTid(TId task_id) noexcept
Get task instance from its identifier.
Definition stk_arch.h:532
static __stk_forceinline void SetTls(Word tp)
Set thread-local storage (TLS).
int64_t Ticks
Ticks value.
Definition stk_common.h:151
static __stk_forceinline Word GetTls()
Get thread-local storage (TLS).
static __stk_forceinline void STK_KERNEL_PANIC(stk::EKernelPanicId id)
Called when the kernel detects an unrecoverable internal fault.
Definition stk_arch.h:75
static constexpr TId GetTidFromUserTask(const ITask *task) noexcept
Get task identifier from ITask instance.
Definition stk_arch.h:526
uint64_t Cycles
Cycles value.
Definition stk_common.h:161
Word TId
Task (thread) id.
Definition stk_common.h:141
EKernelPanicId
Identifies the source of a kernel panic.
Definition stk_common.h:64
Hardware Abstraction Layer (HAL) for architecture-specific operations.
static __stk_forceinline void WriteVolatile64(volatile T *addr, T value)
Atomically write a 64-bit volatile value.
Definition stk_arch.h:288
static constexpr T * WordToPtr(Word value) noexcept
Cast a CPU register-width integer back to a pointer.
Definition stk_arch.h:123
static constexpr Word PtrToWord(T *const ptr) noexcept
Cast a pointer to a CPU register-width integer.
Definition stk_arch.h:106
static __stk_forceinline T ReadVolatile64(volatile const T *addr)
Atomically read a 64-bit volatile value.
Definition stk_arch.h:223
bool IsContextPrivileged()
Check if caller is Privileged.
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
static void Enter()
Enter a critical section.
static void Exit()
Exit a critical section.
STK_NONCOPYABLE_CLASS(CriticalSection)
ScopedLock()
Enter the critical section.
Definition stk_arch.h:367
~ScopedLock()
Exit the critical section.
Definition stk_arch.h:371
bool TryLock()
Attempt to acquire SpinLock in a single non-blocking attempt.
SpinLock()
Construct a SpinLock (unlocked by default).
Definition stk_arch.h:433
EState
Internal lock state values.
Definition stk_arch.h:426
@ UNLOCKED
Lock is free and available for acquisition.
Definition stk_arch.h:427
@ LOCKED
Lock is held by a thread or core.
Definition stk_arch.h:428
STK_NONCOPYABLE_CLASS(SpinLock)
bool IsLocked() const
Sample current lock state.
Definition stk_arch.h:470
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.
void Unlock()
Release SpinLock, allowing another thread or core to acquire it.
High-resolution clock for high-precision measurements.
Definition stk_arch.h:486
static uint32_t GetFrequency()
Get clock frequency.
static __stk_forceinline Ticks GetTimeUs()
Get elapsed time in microseconds.
Definition stk_arch.h:503
static Cycles GetCycles()
Get number of clock cycles elapsed.
Interface for a user task.
Definition stk_common.h:670