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
137// Some architectures (e.g. RISC-V with the 'tp' register) can implement TLS access as a
138// single inline instruction. When the back-end header defines STK_INLINE_TLS,
139// GetTls and SetTls are provided as inline functions there and the declarations below
140// are suppressed to avoid duplicate definitions.
141#if STK_TLS && !STK_INLINE_TLS
142
153Word GetTls();
154
163void SetTls(Word tp);
164
165#endif // STK_INLINE_TLS
166
167#if STK_TLS
176template <class _TyTls>
177__stk_forceinline _TyTls *GetTlsPtr()
178{
180}
181
190template <class _TyTls>
191__stk_forceinline void SetTlsPtr(const _TyTls *tp)
192{
194}
195#endif // STK_TLS
196
224{
225public:
240 {
241 public:
245
249
250 private:
252 };
253
262 static void Enter();
263
272 static void Exit();
273
274private:
275 explicit CriticalSection() {}
277};
278
297{
298public:
303 {
306 };
307
310 explicit SpinLock() : m_lock(UNLOCKED)
311 {}
312
321 void Lock();
322
330 void Unlock();
331
339 bool TryLock();
340
347 bool IsLocked() const { return (m_lock == LOCKED); }
348
349protected:
351
352#ifdef _STK_ARCH_X86_WIN32
353 volatile long m_lock;
354#else
355 volatile bool m_lock __stk_aligned(8);
356#endif
357};
358
378template <typename T>
379static __stk_forceinline T ReadVolatile64(volatile const T *addr)
380{
381 STK_STATIC_ASSERT_N(sz, sizeof(T) == 8U); // only 64-bit types permitted
382 STK_STATIC_ASSERT_N(al, alignof(T) >= 4U); // type must be at least 4-byte aligned
385
386 if __stk_constexpr_cpp17 (sizeof(void *) == 8U) // 64-bit arch: aligned 64-bit load is inherently atomic
387 {
388 return (*addr);
389 }
390 else
391 {
392 // 32-bit arch: split the 64-bit address into two 32-bit halves;
393 // writer always updates hi before lo (see WriteVolatile64), so if hi is
394 // the same before and after reading lo, no write straddled the two reads.
395 #if STK_STRICT_COMPLIANCY
396 const Word p_base = hw::PtrToWord(addr);
397 volatile const uint32_t *const plo = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_LO * sizeof(uint32_t)));
398 volatile const uint32_t *const phi = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_HI * sizeof(uint32_t)));
399 #else
400 volatile const uint32_t *const p_base = reinterpret_cast<volatile const uint32_t *>(addr);
401 volatile const uint32_t *const plo = &p_base[STK_ENDIAN_IDX_LO];
402 volatile const uint32_t *const phi = &p_base[STK_ENDIAN_IDX_HI];
403 #endif
404
405 uint32_t hi, lo;
406 do
407 {
408 hi = (*phi);
409 __stk_full_memfence();
410
411 lo = (*plo);
412 __stk_full_memfence();
413 }
414 while (hi != (*phi)); // hi changed: a write occurred during the read; retry
415
416 const uint64_t result = (static_cast<uint64_t>(hi) << 32U) | static_cast<uint64_t>(lo);
417
418 return static_cast<T>(result);
419 }
420}
421
443template <typename T>
444static __stk_forceinline void WriteVolatile64(volatile T *addr, T value)
445{
446 STK_STATIC_ASSERT_N(sz, sizeof(T) == 8U); // only 64-bit types permitted
447 STK_STATIC_ASSERT_N(al, alignof(T) >= 4U); // type must be at least 4-byte aligned
450
451 if __stk_constexpr_cpp17 (sizeof(void *) == 8U) // 64-bit arch: aligned 64-bit store is inherently atomic
452 {
453 (*addr) = value;
454 }
455 else
456 {
457 #if STK_STRICT_COMPLIANCY
458 const Word p_base = hw::PtrToWord(addr);
459 volatile uint32_t *const plo = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_LO * sizeof(uint32_t)));
460 volatile uint32_t *const phi = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_HI * sizeof(uint32_t)));
461 #else
462 volatile uint32_t *const p_base = reinterpret_cast<volatile uint32_t *>(addr);
463 volatile uint32_t *const plo = &p_base[STK_ENDIAN_IDX_LO];
464 volatile uint32_t *const phi = &p_base[STK_ENDIAN_IDX_HI];
465 #endif
466
467 // write hi first: ReadVolatile64 reads hi twice and retries if it changed,
468 // so writing hi before lo ensures readers can detect a torn write.
469 (*phi) = static_cast<uint32_t>(static_cast<uint64_t>(value) >> 32U);
470 __stk_full_memfence();
471
472 (*plo) = static_cast<uint32_t>(value);
473 }
474}
475
480{
486
491 static uint32_t GetFrequency();
492
498 {
499 Ticks ticks = 0LL;
500 const uint32_t freq = GetFrequency();
501
502 if (freq != 0U)
503 {
504 const Cycles cycles = GetCycles();
505 const Cycles ticksu = (cycles * 1000000ULL) / static_cast<Cycles>(freq);
506
507 ticks = static_cast<Ticks>(ticksu);
508 }
509
510 return ticks;
511 }
512};
513
514} // namespace hw
515
520static constexpr TId GetTidFromUserTask(const ITask *task) noexcept { return hw::PtrToWord(task); }
521
526static constexpr ITask *GetUserTaskFromTid(TId task_id) noexcept { return hw::WordToPtr<ITask>(task_id); }
527
528} // namespace stk
529
533#ifndef _STK_CUSTOM_MEMCPY
534static __stk_forceinline void STK_MEMCPY(void *const dest, const void *const src, const size_t size)
535{
536 using namespace stk;
537
538 if ((dest != nullptr) && (src != nullptr) && (size != 0U))
539 {
540 const Word dest_addr = hw::PtrToWord(dest);
541 const Word src_addr = hw::PtrToWord(src);
542
543 // fast path: check if destination, source, and size are all 4-byte aligned
544 // then copy data in 4-byte chunks
545 if (((dest_addr & 0x03U) == 0U) &&
546 ((src_addr & 0x03U) == 0U) &&
547 ((size & 0x03U) == 0U))
548 {
549 uint32_t *const p_d32 = static_cast<uint32_t *>(dest);
550 const uint32_t *const p_s32 = static_cast<const uint32_t *>(src);
551 const size_t words = (size >> 2U);
552
553 STK_UNUSED(std::copy_n(p_s32, words, p_d32));
554 }
555 // slow path
556 else
557 {
558 uint8_t *const p_d = static_cast<uint8_t *>(dest);
559 const uint8_t *const p_s = static_cast<const uint8_t *>(src);
560
561 STK_UNUSED(std::copy_n(p_s, size, p_d));
562 }
563 }
564}
565#endif
566
567#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:534
#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:115
static constexpr ITask * GetUserTaskFromTid(TId task_id) noexcept
Get task instance from its identifier.
Definition stk_arch.h:526
static __stk_forceinline void SetTls(Word tp)
Set thread-local storage (TLS).
int64_t Ticks
Ticks value.
Definition stk_common.h:130
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:520
uint64_t Cycles
Cycles value.
Definition stk_common.h:140
Word TId
Task (thread) id.
Definition stk_common.h:120
EKernelPanicId
Identifies the source of a kernel panic.
Definition stk_common.h:53
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:444
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:379
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:244
~ScopedLock()
Exit the critical section.
Definition stk_arch.h:248
bool TryLock()
Attempt to acquire SpinLock in a single non-blocking attempt.
SpinLock()
Construct a SpinLock (unlocked by default).
Definition stk_arch.h:310
EState
Internal lock state values.
Definition stk_arch.h:303
@ UNLOCKED
Lock is free and available for acquisition.
Definition stk_arch.h:304
@ LOCKED
Lock is held by a thread or core.
Definition stk_arch.h:305
STK_NONCOPYABLE_CLASS(SpinLock)
bool IsLocked() const
Sample current lock state.
Definition stk_arch.h:347
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:480
static uint32_t GetFrequency()
Get clock frequency.
static __stk_forceinline Ticks GetTimeUs()
Get elapsed time in microseconds.
Definition stk_arch.h:497
static Cycles GetCycles()
Get number of clock cycles elapsed.
Interface for a user task.
Definition stk_common.h:599