rt 0.20.1

A real-time operating system capable of full preemption
Documentation
#include <rt/arch/dcb.h>
#include <rt/arch/fp.h>
#include <rt/arch/mpu.h>
#include <rt/arch/nvic.h>
#include <rt/arch/scb.h>
#include <rt/arch/semihosting.h>
#include <rt/arch/syscall.h>
#include <rt/arch/systick.h>

#include <rt/log.h>
#include <rt/panic.h>
#include <rt/stack.h>
#include <rt/start.h>
#include <rt/tick.h>
#include <rt/trap.h>

static RT_STACK(stack, 512);

extern char __rx_region__[], __rx_region_size__[];
extern char __priv_rw_region__[], __priv_rw_region_size__[];
extern char __rw_region__[], __rw_region_size__[];

void start(void);

__attribute__((noreturn)) void rt_panic(const char *msg)
{
    semihosting_write0(msg);
    semihosting_write0("\n");
    semihosting_exception(ADP_STOPPED_OS_SPECIFIC);
}

__attribute__((noreturn)) void rt_trap(void)
{
    semihosting_exit(0);
}

__attribute__((noreturn)) static void nmi_handler(void)
{
    semihosting_write0("nmi\n");
    semihosting_exception(ADP_STOPPED_FIQ);
}

__attribute__((noreturn)) static void hardfault_handler(void)
{
    semihosting_write0("hardfault\n");
    semihosting_exception(ADP_STOPPED_INTERNAL_ERROR);
}

__attribute__((noreturn)) static void memmanagefault_handler(void)
{
    semihosting_write0("memmanagefault\n");
    semihosting_exception(ADP_STOPPED_ADDRESS_EXCEPTION);
}

__attribute__((noreturn)) static void busfault_handler(void)
{
    semihosting_write0("busfault\n");
    semihosting_exception(ADP_STOPPED_DATA_ABORT);
}

__attribute__((noreturn)) static void usagefault_handler(void)
{
    semihosting_write0("usagefault\n");
    semihosting_exception(ADP_STOPPED_UNDEFINED_INSTR);
}

__attribute__((noreturn)) static void securefault_handler(void)
{
    semihosting_write0("securefault\n");
    semihosting_exception(ADP_STOPPED_RUNTIME_ERROR_UNKNOWN);
}

__attribute__((noreturn)) static void debugmonitor_handler(void)
{
    semihosting_write0("debugmonitor\n");
    semihosting_exception(ADP_STOPPED_BREAKPOINT);
}

__attribute__((noreturn)) static void spurious_interrupt_handler(void)
{
    semihosting_write0("spurious interrupt\n");
    semihosting_exception(ADP_STOPPED_IRQ);
}

#define NUM_INTERRUPTS 96

__attribute__((aligned(512), section(".vector")))
const struct nvic_vector vector = {
    .sp_init = &stack[sizeof stack],
    .reset = start,
    .nmi = nmi_handler,
    .hardfault = hardfault_handler,
    .memmanagefault = memmanagefault_handler,
    .busfault = busfault_handler,
    .usagefault = usagefault_handler,
    .securefault = securefault_handler,
    .svcall = rt_svcall_handler,
    .debugmonitor = debugmonitor_handler,
    .pendsv = rt_pendsv_handler,
    .systick = rt_tick_advance,
    .interrupt_handlers =
        {
            [0 ... NUM_INTERRUPTS - 1] = spurious_interrupt_handler,
        },
};

void init(void);
void init(void)
{
    SCB->vtor = &vector;
    __asm__("dsb; isb" ::: "memory");

#if RT_ARM_FP
    SCB->cpacr |= SCB_CPACR_ENABLE_FPU;
#endif

    rt_mpu_attr_init();
    rt_mpu_attr_set(0, RT_MPU_ATTR_WB_RWALLOC);
    rt_mpu_attr_set(1, RT_MPU_ATTR_DEVICE_NGNRNE);

    // Configure static MPU regions (regions 0-3, task regions use 4-7)
    // Region 0: Code + RO data (execute, read-only)
    rt_mpu_region_set(0, (uintptr_t)__rx_region__, (size_t)__rx_region_size__,
                      RT_MPU_ATTR_RO | RT_MPU_ATTR_INDEX(0) |
                          RT_MPU_ATTR_ENABLE);

    // Region 1: Privileged RW (kernel stack + data)
    rt_mpu_region_set(1, (uintptr_t)__priv_rw_region__,
                      (size_t)__priv_rw_region_size__,
                      RT_MPU_ATTR_RW_PRIV | RT_MPU_ATTR_XN | RT_MPU_ATTR_PXN |
                          RT_MPU_ATTR_INDEX(0) | RT_MPU_ATTR_ENABLE);

    // Region 2: Privileged RW for the PPB.
    rt_mpu_region_set(2, 0xE0000000UL, 0x20000000UL,
                      RT_MPU_ATTR_RW_PRIV | RT_MPU_ATTR_XN | RT_MPU_ATTR_PXN |
                          RT_MPU_ATTR_INDEX(1) | RT_MPU_ATTR_ENABLE);

    // Region 3: General RW data (.data, .bss)
    rt_mpu_region_set(3, (uintptr_t)__rw_region__, (size_t)__rw_region_size__,
                      RT_MPU_ATTR_RW | RT_MPU_ATTR_XN | RT_MPU_ATTR_PXN |
                          RT_MPU_ATTR_INDEX(0) | RT_MPU_ATTR_ENABLE);

    /* If per-task MPU regions are not enabled, give user RW permissions to all
     * task stacks so unprivileged tasks can access their own stack. */
#if !RT_MPU_TASK_REGIONS_ENABLE
    extern char __task_stack_region__[], __task_stack_region_size__[];
    rt_mpu_region_set(4, (uintptr_t)__task_stack_region__,
                      (size_t)__task_stack_region_size__,
                      RT_MPU_ATTR_RW | RT_MPU_ATTR_XN | RT_MPU_ATTR_PXN |
                          RT_MPU_ATTR_INDEX(0) | RT_MPU_ATTR_ENABLE);
#endif

    /* Use the privileged default region for kernel access to task stacks. The
     * Armv8 MPU does not allow overlapping MPU regions, so priv_rw must not
     * contain task stacks. */
    rt_mpu_privileged_default_enable();
    rt_mpu_enable();

    SCB->shpr1.securefault = NVIC_PRIORITY_LOWEST;
    SCB->shpr2.svcall = NVIC_PRIORITY_LOWEST;
    SCB->shpr3.pendsv = NVIC_PRIORITY_LOWEST;
    SCB->shpr3.systick = NVIC_PRIORITY_LOWEST - NVIC_PRIORITY_STEP;

    SCB->shcsr |= (SCB_SHCSR_SECUREFAULTENA | SCB_SHCSR_USAGEFAULTENA |
                   SCB_SHCSR_BUSFAULTENA | SCB_SHCSR_MEMFAULTENA);
    DCB->demcr |= DCB_DEMCR_TRCENA | DCB_DEMCR_MON_EN;

    // Configure SysTick to 1kHz assuming a 32MHz sysclock.
    SYSTICK->rvr = 32000U - 1U;
    SYSTICK->cvr = 0;
    SYSTICK->csr =
        SYSTICK_CSR_CLKSOURCE | SYSTICK_CSR_TICKINT | SYSTICK_CSR_ENABLE;
}

#if RT_LOG_ENABLE
// Provide rt/log.h stubs so the build succeeds.
void rt_logf(const char *format, ...)
{
    (void)format;
}

void rt_log_flush(void)
{
}
#endif // RT_LOG_ENABLE