SuperTinyKernel™ RTOS 1.08.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
13#include "stk_defs.h"
14
26
27// Architecture back-end selection.
28// Exactly one of the following macros must be defined by the build system (e.g. via -D compiler flag):
29// _STK_ARCH_ARM_CORTEX_M - ARM Cortex-M (M0/M0+/M3/M4/M7/M33/M55).
30// _STK_ARCH_RISC_V - RISC-V (RV32I/RV32E/RV64, with optional FPU).
31// _STK_ARCH_X86_WIN32 - x86/x64 on Windows (simulation/test use only).
32//
33// Defining more than one is not supported and will result in multiple conflicting definitions.
34// _STK_ARCH_DEFINED is set by whichever back-end is included; it can be tested by downstream
35// headers or build checks to verify that a valid architecture was selected.
36#ifdef _STK_ARCH_ARM_CORTEX_M
38 #define _STK_ARCH_DEFINED
39#endif
40#ifdef _STK_ARCH_RISC_V
42 #define _STK_ARCH_DEFINED
43#endif
44#ifdef _STK_ARCH_X86_WIN32
46 #define _STK_ARCH_DEFINED
47#endif
48
49#ifndef STK_PANIC_HANDLER
59 #define STK_PANIC_HANDLER(id) STK_PANIC_HANDLER_DEFAULT(id)
60#endif
61
62// =============================================================================
63#if STK_MPU
64// =============================================================================
65
74#define STK_MPU_SHARED_DATA_SECTION __attribute__((section(".stk_mpu_shared_data")))
75
84#define STK_MPU_SHARED_CODE_SECTION __attribute__((section(".stk_mpu_shared_code")))
85
101#define STK_MPU_SHARED_BSS_SECTION __attribute__((section(".stk_mpu_shared_bss")))
102
117#define STK_MPU_KERNEL_DATA_SECTION __attribute__((section(".stk_mpu_kernel_data")))
118
132#define STK_MPU_KERNEL_CODE_SECTION __attribute__((section(".stk_mpu_kernel_code")))
133
153#define STK_MPU_KERNEL_BSS_SECTION __attribute__((section(".stk_mpu_kernel_bss")))
154
155#else
156
157#define STK_MPU_SHARED_DATA_SECTION
158#define STK_MPU_SHARED_CODE_SECTION
159#define STK_MPU_SHARED_BSS_SECTION
160#define STK_MPU_KERNEL_DATA_SECTION
161#define STK_MPU_KERNEL_CODE_SECTION
162#define STK_MPU_KERNEL_BSS_SECTION
163
164// =============================================================================
165#endif // STK_MPU
166// =============================================================================
167
168namespace stk {
169
184{
185 __stk_debug_break(); // debug aid
186 STK_PANIC_HANDLER(id); // must not return
187}
188
200namespace hw {
201
213template <typename T>
214static constexpr Word PtrToWord(T *const ptr) noexcept
215{
216 STK_STATIC_ASSERT(sizeof(Word) == sizeof(T *));
217 return reinterpret_cast<Word>(ptr);
218}
219
230template <typename T>
231static constexpr T *WordToPtr(Word value) noexcept
232{
233 STK_STATIC_ASSERT(sizeof(Word) == sizeof(T *));
234 return reinterpret_cast<T *>(value);
235}
236
244
250
251// Some architectures (e.g. RISC-V with the 'tp' register) can implement TLS access as a
252// single inline instruction. When the back-end header defines STK_INLINE_TLS,
253// GetTls and SetTls are provided as inline functions there and the declarations below
254// are suppressed to avoid duplicate definitions.
255#if STK_TLS && !STK_INLINE_TLS
256
267Word GetTls();
268
277void SetTls(Word tp);
278
279#endif // STK_INLINE_TLS
280
281#if STK_TLS
290template <class _TyTls>
291__stk_forceinline _TyTls *GetTlsPtr()
292{
293 return hw::WordToPtr<_TyTls>(GetTls());
294}
295
304template <class _TyTls>
305__stk_forceinline void SetTlsPtr(const _TyTls *tp)
306{
307 SetTls(hw::PtrToWord(tp));
308}
309#endif // STK_TLS
310
330template <typename T>
331static __stk_forceinline T ReadVolatile64(volatile const T *addr)
332{
333 STK_STATIC_ASSERT_N(sz, sizeof(T) == 8U); // only 64-bit types permitted
334 STK_STATIC_ASSERT_N(al, alignof(T) >= 4U); // type must be at least 4-byte aligned
337
338 if __stk_constexpr_cpp17 (sizeof(void *) == 8U) // 64-bit arch: aligned 64-bit load is inherently atomic
339 {
340 return (*addr);
341 }
342 else
343 {
344 // 32-bit arch: split the 64-bit address into two 32-bit halves;
345 // writer always updates hi before lo (see WriteVolatile64), so if hi is
346 // the same before and after reading lo, no write straddled the two reads.
347 #if STK_STRICT_COMPLIANCY
348 const Word p_base = hw::PtrToWord(addr);
349 volatile const uint32_t *const plo = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_LO * sizeof(uint32_t)));
350 volatile const uint32_t *const phi = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_HI * sizeof(uint32_t)));
351 #else
352 volatile const uint32_t *const p_base = reinterpret_cast<volatile const uint32_t *>(addr);
353 volatile const uint32_t *const plo = &p_base[STK_ENDIAN_IDX_LO];
354 volatile const uint32_t *const phi = &p_base[STK_ENDIAN_IDX_HI];
355 #endif
356
357 uint32_t hi, lo;
358 do
359 {
360 hi = (*phi);
361 __stk_full_memfence();
362
363 lo = (*plo);
364 __stk_full_memfence();
365 }
366 while (hi != (*phi)); // hi changed: a write occurred during the read; retry
367
368 const uint64_t result = (static_cast<uint64_t>(hi) << 32U) | static_cast<uint64_t>(lo);
369
370 return static_cast<T>(result);
371 }
372}
373
395template <typename T>
396static __stk_forceinline void WriteVolatile64(volatile T *addr, T value)
397{
398 STK_STATIC_ASSERT_N(sz, sizeof(T) == 8U); // only 64-bit types permitted
399 STK_STATIC_ASSERT_N(al, alignof(T) >= 4U); // type must be at least 4-byte aligned
402
403 if __stk_constexpr_cpp17 (sizeof(void *) == 8U) // 64-bit arch: aligned 64-bit store is inherently atomic
404 {
405 (*addr) = value;
406 }
407 else
408 {
409 #if STK_STRICT_COMPLIANCY
410 const Word p_base = hw::PtrToWord(addr);
411 volatile uint32_t *const plo = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_LO * sizeof(uint32_t)));
412 volatile uint32_t *const phi = hw::WordToPtr<uint32_t>(p_base + (STK_ENDIAN_IDX_HI * sizeof(uint32_t)));
413 #else
414 volatile uint32_t *const p_base = reinterpret_cast<volatile uint32_t *>(addr);
415 volatile uint32_t *const plo = &p_base[STK_ENDIAN_IDX_LO];
416 volatile uint32_t *const phi = &p_base[STK_ENDIAN_IDX_HI];
417 #endif
418
419 // write hi first: ReadVolatile64 reads hi twice and retries if it changed,
420 // so writing hi before lo ensures readers can detect a torn write.
421 (*phi) = static_cast<uint32_t>(static_cast<uint64_t>(value) >> 32U);
422 __stk_full_memfence();
423
424 (*plo) = static_cast<uint32_t>(value);
425 }
426}
427
456{
457public:
461 enum ESessionFlags : uint8_t
462 {
464 SESSION_FLAG_NPRIV = (1U << 0),
465 };
466
471 typedef uint8_t Session;
472
479
494 {
495 public:
499 {}
500
507
508 private:
511 };
512
527
537 static void Exit(const Session ses = DEFAULT_SESSION);
538
539private:
541
545 {}
546};
547
570{
571public:
576 {
579 };
580
594 {
595 public:
598 explicit ScopedLock(SpinLock &sl) : m_sl(sl)
599 {
600 sl.Lock();
601 }
602
606 {
607 m_sl.Unlock();
608 }
609
610 private:
613 };
614
618 {}
619
628 void Lock();
629
637 void Unlock();
638
646 bool TryLock();
647
654 bool IsLocked() const { return (m_lock == LOCKED); }
655
656protected:
658
659#ifdef _STK_ARCH_X86_WIN32
660 volatile long m_lock;
661#else
662 volatile bool m_lock __stk_aligned(8);
663#endif
664};
665
675{
681
686 static uint32_t GetFrequency();
687
693 {
694 Ticks ticks = 0LL;
695 const uint32_t freq = GetFrequency();
696
697 if (freq != 0U)
698 {
699 const Cycles cycles = GetCycles();
700 const Cycles ticksu = (cycles * 1000000ULL) / static_cast<Cycles>(freq);
701
702 ticks = static_cast<Ticks>(ticksu);
703 }
704
705 return ticks;
706 }
707};
708
709} // namespace hw
710
715#ifndef _STK_CORTEX_M_TRUSTZONE_NON_SECURE
716static constexpr TId GetTidFromUserTask(const ITask *task) noexcept { return hw::PtrToWord(task); }
717#else
718TId GetTidFromUserTask(const ITask *task);
719#endif
720
725static constexpr ITask *GetUserTaskFromTid(TId task_id) noexcept { return hw::WordToPtr<ITask>(task_id); }
726
727} // namespace stk
728
730#ifndef _STK_CUSTOM_MEMCPY
731static inline void STK_MEMCPY(void *const dest, const void *const src, const size_t size)
732{
733 using namespace stk;
734
735 if ((dest != nullptr) && (src != nullptr) && (size != 0U))
736 {
737 const Word dest_addr = hw::PtrToWord(dest);
738 const Word src_addr = hw::PtrToWord(src);
739
740 // fast path: check if destination, source, and size are all 4-byte aligned
741 // then copy data in 4-byte chunks
742 if (((dest_addr & 0x03U) == 0U) &&
743 ((src_addr & 0x03U) == 0U) &&
744 ((size & 0x03U) == 0U))
745 {
746 uint32_t *const p_d32 = static_cast<uint32_t *>(dest);
747 const uint32_t *const p_s32 = static_cast<const uint32_t *>(src);
748 const size_t words = (size >> 2U);
749
750 STK_UNUSED(std::copy_n(p_s32, words, p_d32));
751 }
752 // slow path
753 else
754 {
755 uint8_t *const p_d = static_cast<uint8_t *>(dest);
756 const uint8_t *const p_s = static_cast<const uint8_t *>(src);
757
758 STK_UNUSED(std::copy_n(p_s, size, p_d));
759 }
760 }
761}
762#endif
763
765#ifndef _STK_CUSTOM_MEMSET
766static inline void STK_MEMSET(void *const dest, const uint8_t value, const size_t size)
767{
768 using namespace stk;
769
770 if ((dest != nullptr) && (size != 0U))
771 {
772 const Word dest_addr = hw::PtrToWord(dest);
773
774 // fast path: destination and size are 4-byte aligned, fill in 4-byte chunks
775 if (((dest_addr & 0x03U) == 0U) &&
776 ((size & 0x03U) == 0U))
777 {
778 uint32_t *const p_d32 = static_cast<uint32_t *>(dest);
779 const uint32_t word = static_cast<uint32_t>(value) * 0x01010101U;
780 const size_t words = (size >> 2U);
781
782 STK_UNUSED(std::fill_n(p_d32, words, word));
783 }
784 // slow path
785 else
786 {
787 uint8_t *const p_d = static_cast<uint8_t *>(dest);
788
789 STK_UNUSED(std::fill_n(p_d, size, value));
790 }
791 }
792}
793#endif
794
795#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 void STK_MEMCPY(void *const dest, const void *const src, const size_t size)
Implementation of STK_MEMCPY.
Definition stk_arch.h:731
static void STK_MEMSET(void *const dest, const uint8_t value, const size_t size)
Implementation of STK_MEMSET.
Definition stk_arch.h:766
#define STK_PANIC_HANDLER(id)
Definition stk_arch.h:59
Compiler and platform low-level definitions for STK.
#define STK_UNUSED(X)
Explicitly marks a variable as unused to suppress compiler warnings.
Definition stk_defs.h:715
#define STK_STATIC_ASSERT_N(NAME, X)
Compile-time assertion with a user-defined name suffix.
Definition stk_defs.h:545
#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:694
#define __stk_forceinline
Forces compiler to always inline the decorated function, regardless of optimisation level.
Definition stk_defs.h:277
#define __stk_aligned(x)
Specifies minimum alignment in bytes for the decorated variable or struct member (data instance prefi...
Definition stk_defs.h:289
#define STK_NONCOPYABLE_CLASS(TYPE)
Disables copy construction and assignment for a class.
Definition stk_defs.h:708
#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:693
#define __stk_constexpr_cpp17
constexpr definition for C++17 and above.
Definition stk_defs.h:489
static void __stk_debug_break()
Definition stk_defs.h:479
#define STK_STATIC_ASSERT(X)
Compile-time assertion. Produces a compilation error if X is false.
Definition stk_defs.h:553
Namespace of STK package.
uintptr_t Word
Native processor word type.
Definition stk_common.h:143
static constexpr ITask * GetUserTaskFromTid(TId task_id) noexcept
Get task instance from its identifier.
Definition stk_arch.h:725
int64_t Ticks
Ticks value.
Definition stk_common.h:158
EKernelPanicId
Identifies the source of a kernel panic.
Definition stk_common.h:58
static void STK_KERNEL_PANIC(stk::EKernelPanicId id)
Called when the kernel detects an unrecoverable internal fault.
Definition stk_arch.h:183
static constexpr TId GetTidFromUserTask(const ITask *task) noexcept
Get task identifier from ITask instance.
Definition stk_arch.h:716
uint64_t Cycles
Cycles value.
Definition stk_common.h:168
Word TId
Task (thread) id.
Definition stk_common.h:148
bool IsPrivilegedContext()
Check if caller context is Privileged.
static constexpr T * WordToPtr(Word value) noexcept
Cast a CPU register-width integer back to a pointer.
Definition stk_arch.h:231
static void WriteVolatile64(volatile T *addr, T value)
Atomically write a 64-bit volatile value.
Definition stk_arch.h:396
static constexpr Word PtrToWord(T *const ptr) noexcept
Cast a pointer to a CPU register-width integer.
Definition stk_arch.h:214
static T ReadVolatile64(volatile const T *addr)
Atomically read a 64-bit volatile value.
Definition stk_arch.h:331
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
static constexpr Session DEFAULT_SESSION
Default session value passed to Enter()/Exit() when the caller does not need to force a specific hand...
Definition stk_arch.h:478
CriticalSection()
Protected constructor (instantiation is prohibited).
Definition stk_arch.h:544
static Session Enter(const Session ses=DEFAULT_SESSION)
Enter a critical section.
uint8_t Session
Opaque session token returned by Enter() and consumed by Exit().
Definition stk_arch.h:471
CriticalSection(const CriticalSection &)=delete
static void Exit(const Session ses=DEFAULT_SESSION)
Exit a critical section.
ESessionFlags
Collection of session flags.
Definition stk_arch.h:462
@ SESSION_FLAG_NPRIV
Calling context is non-Privileged.
Definition stk_arch.h:464
ScopedLock()
Enter the critical section.
Definition stk_arch.h:498
CriticalSection::Session m_ses
Definition stk_arch.h:510
~ScopedLock()
Exit the critical section.
Definition stk_arch.h:503
bool TryLock()
Attempt to acquire SpinLock in a single non-blocking attempt.
SpinLock()
Construct a SpinLock (unlocked by default).
Definition stk_arch.h:617
EState
Internal lock state values.
Definition stk_arch.h:576
@ UNLOCKED
Lock is free and available for acquisition.
Definition stk_arch.h:577
@ LOCKED
Lock is held by a thread or core.
Definition stk_arch.h:578
bool IsLocked() const
Sample current lock state.
Definition stk_arch.h:654
volatile bool m_lock
Lock state (see EState). 8-byte aligned to occupy its own cache line word and avoid false sharing on ...
Definition stk_arch.h:662
void Lock()
Acquire SpinLock, blocking until it is available.
void Unlock()
Release SpinLock, allowing another thread or core to acquire it.
ScopedLock(SpinLock &sl)
Lock a spin lock instance.
Definition stk_arch.h:598
~ScopedLock()
Unlock a spin lock instance.
Definition stk_arch.h:605
High-resolution clock for high-precision measurements.
Definition stk_arch.h:675
static uint32_t GetFrequency()
Get clock frequency.
static Ticks GetTimeUs()
Get elapsed time in microseconds.
Definition stk_arch.h:692
static Cycles GetCycles()
Get number of clock cycles elapsed.
Interface for a user task.
Definition stk_common.h:755