std1.0.0-beta-7 SafeC docs

Source hal/cortex_m.sc

View in the repository

// SafeC Standard Library — Cortex-M HAL Implementation
#pragma once
#include <std/hal/cortex_m.h>

// MMIO read/write helpers for 32-bit registers.
namespace std {

// Definitions of the globals cortex_m.h declares extern.
struct Nvic nvic;
struct Scb scb;
struct SysTick systick;


static unsigned int cm_read32_(unsigned long addr) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)addr;
        return *p;
    }
    return (unsigned int)0;
}

static void cm_write32_(unsigned long addr, unsigned int val) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)addr;
        *p = val;
    }
}

// ── NVIC ──────────────────────────────────────────────────────────────────────
// NVIC_ISER0: base + 0x000  (32 IRQs per word)
// NVIC_ICER0: base + 0x080
// NVIC_ISPR0: base + 0x100
// NVIC_ICPR0: base + 0x180
// NVIC_IPR0:  base + 0x300  (8-bit priority per IRQ)


inline void nvic_init() {
    unsafe { nvic.base = (&volatile unsigned int)NVIC_BASE; }
}

inline void Nvic::enable(unsigned int irq) {
    unsigned long reg = (unsigned long)self.base + (unsigned long)0 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
    cm_write32_(reg, (unsigned int)1 << (irq % (unsigned int)32));
}

inline void Nvic::disable(unsigned int irq) {
    unsigned long reg = (unsigned long)self.base + (unsigned long)0x80 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
    cm_write32_(reg, (unsigned int)1 << (irq % (unsigned int)32));
}

inline int Nvic::is_pending(unsigned int irq) const {
    unsigned long reg = (unsigned long)self.base + (unsigned long)0x100 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
    unsigned int val = cm_read32_(reg);
    if ((val >> (irq % (unsigned int)32)) & (unsigned int)1) { return 1; }
    return 0;
}

inline void Nvic::clear_pending(unsigned int irq) {
    unsigned long reg = (unsigned long)self.base + (unsigned long)0x180 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
    cm_write32_(reg, (unsigned int)1 << (irq % (unsigned int)32));
}

inline void Nvic::set_priority(unsigned int irq, unsigned char priority) {
    unsigned long reg = (unsigned long)self.base + (unsigned long)0x300 + (unsigned long)irq;
    unsafe {
        volatile unsigned char* p = (volatile unsigned char*)reg;
        *p = priority;
    }
}

inline unsigned char Nvic::get_priority(unsigned int irq) const {
    unsigned long reg = (unsigned long)self.base + (unsigned long)0x300 + (unsigned long)irq;
    unsafe {
        volatile unsigned char* p = (volatile unsigned char*)reg;
        return *p;
    }
    return (unsigned char)0;
}

// ── SysTick ───────────────────────────────────────────────────────────────────
// SYST_CSR  = base + 0x0
// SYST_RVR  = base + 0x4
// SYST_CVR  = base + 0x8


inline void systick_init() {
    unsafe { systick.base = (&volatile unsigned int)SYSTICK_BASE; }
}

inline void SysTick::start(unsigned int reload, int use_core_clock) {
    unsigned long base = (unsigned long)self.base;
    cm_write32_(base + (unsigned long)4, reload & (unsigned int)0x00FFFFFF);
    cm_write32_(base + (unsigned long)8, (unsigned int)0);  // clear CVR
    unsigned int csr = (unsigned int)0x3;  // TICKINT | ENABLE
    if (use_core_clock != 0) { csr = csr | (unsigned int)0x4; }
    cm_write32_(base, csr);
}

inline void SysTick::stop() {
    unsigned long base = (unsigned long)self.base;
    cm_write32_(base, (unsigned int)0);
}

inline unsigned int SysTick::read() const {
    return cm_read32_((unsigned long)self.base + (unsigned long)8);
}

inline int SysTick::flag_set() const {
    unsigned int csr = cm_read32_((unsigned long)self.base);
    if ((csr >> 16) & (unsigned int)1) { return 1; }
    return 0;
}

// ── SCB ───────────────────────────────────────────────────────────────────────
// SCB: CPUID=+0, ICSR=+4, VTOR=+8, AIRCR=+0xC, SCR=+0x10, CCR=+0x14, SHCSR=+0x24


inline void scb_init() {
    unsafe { scb.base = (&volatile unsigned int)SCB_BASE; }
}

inline void Scb::system_reset() {
    unsafe {
        unsigned long base = (unsigned long)self.base;
        unsigned int val = (unsigned int)0x05FA0004;  // VECTKEY | SYSRESETREQ
        cm_write32_(base + (unsigned long)0xC, val);
        asm volatile ("dsb");
        while (1) {}
    }
}

inline void Scb::enable_fault(unsigned int fault_mask) {
    unsigned long reg = (unsigned long)self.base + (unsigned long)0x24;
    unsigned int val = cm_read32_(reg);
    cm_write32_(reg, val | fault_mask);
}

inline void Scb::disable_fault(unsigned int fault_mask) {
    unsigned long reg = (unsigned long)self.base + (unsigned long)0x24;
    unsigned int val = cm_read32_(reg);
    cm_write32_(reg, val & ~fault_mask);
}

inline void Scb::set_vtor(unsigned int addr) {
    cm_write32_((unsigned long)self.base + (unsigned long)8, addr);
}

inline int Scb::in_handler_mode() const {
    unsigned int icsr = cm_read32_((unsigned long)self.base + (unsigned long)4);
    if ((icsr & (unsigned int)0x1FF) != (unsigned int)0) { return 1; }
    return 0;
}

inline void Scb::set_psp(unsigned int addr) {
    unsafe { asm volatile ("msr psp, %0" : : "r"(addr)); }
}

inline void Scb::set_msp(unsigned int addr) {
    unsafe { asm volatile ("msr msp, %0" : : "r"(addr)); }
}

} // namespace std