std1.0.0-beta-7 SafeC docs

Source hal/riscv.sc

View in the repository

// SafeC Standard Library — RISC-V HAL Implementation
#pragma once
#include <std/hal/riscv.h>

// ── CSR accessors ─────────────────────────────────────────────────────────────

namespace std {

// Definitions of the globals riscv.h declares extern.
struct Clint clint;
struct Plic plic;


inline unsigned long rv_csr_read_mstatus() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, mstatus" : "=r"(v)); }
    return v;
}
inline void rv_csr_write_mstatus(unsigned long val) {
    unsafe { asm volatile ("csrw mstatus, %0" : : "r"(val)); }
}
inline unsigned long rv_csr_read_mie() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, mie" : "=r"(v)); }
    return v;
}
inline void rv_csr_write_mie(unsigned long val) {
    unsafe { asm volatile ("csrw mie, %0" : : "r"(val)); }
}
inline unsigned long rv_csr_read_mip() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, mip" : "=r"(v)); }
    return v;
}
inline unsigned long rv_csr_read_mcause() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, mcause" : "=r"(v)); }
    return v;
}
inline unsigned long rv_csr_read_mepc() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, mepc" : "=r"(v)); }
    return v;
}
inline void rv_csr_write_mepc(unsigned long val) {
    unsafe { asm volatile ("csrw mepc, %0" : : "r"(val)); }
}
inline unsigned long rv_csr_read_mtvec() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, mtvec" : "=r"(v)); }
    return v;
}
inline void rv_csr_write_mtvec(unsigned long val) {
    unsafe { asm volatile ("csrw mtvec, %0" : : "r"(val)); }
}
inline unsigned long rv_csr_read_time() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, time" : "=r"(v)); }
    return v;
}
inline unsigned long rv_csr_read_cycle() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, cycle" : "=r"(v)); }
    return v;
}
inline unsigned long rv_csr_read_instret() {
    unsigned long v = (unsigned long)0;
    unsafe { asm volatile ("csrr %0, instret" : "=r"(v)); }
    return v;
}

inline void rv_global_irq_enable() {
    // Set MIE bit (bit 3) in mstatus.
    unsafe { asm volatile ("csrsi mstatus, 0x8"); }
}

inline void rv_global_irq_disable() {
    unsafe { asm volatile ("csrci mstatus, 0x8"); }
}

// ── CLINT ─────────────────────────────────────────────────────────────────────
// Layout: msip[0..MAX_HARTS] @ +0, mtimecmp[hart] @ +0x4000, mtime @ +0xBFF8


inline void clint_init(unsigned long base_addr) {
    clint.base = base_addr;
}

inline void Clint::set_msip(unsigned int hart_id) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)(self.base + (unsigned long)hart_id * (unsigned long)4);
        *p = (unsigned int)1;
    }
}

inline void Clint::clear_msip(unsigned int hart_id) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)(self.base + (unsigned long)hart_id * (unsigned long)4);
        *p = (unsigned int)0;
    }
}

inline void Clint::set_mtimecmp(unsigned long long cmp) {
    // Split into lo/hi to avoid non-atomic 64-bit write on 32-bit cores.
    unsafe {
        volatile unsigned int* lo = (volatile unsigned int*)(self.base + (unsigned long)0x4000);
        volatile unsigned int* hi = (volatile unsigned int*)(self.base + (unsigned long)0x4004);
        *lo = (unsigned int)0xFFFFFFFF;  // prevent spurious match
        *hi = (unsigned int)((unsigned long long)cmp >> 32);
        *lo = (unsigned int)(cmp & (unsigned long long)0xFFFFFFFF);
    }
}

inline unsigned long long Clint::read_mtime() const {
    unsafe {
        volatile unsigned int* lo = (volatile unsigned int*)(self.base + (unsigned long)0xBFF8);
        volatile unsigned int* hi = (volatile unsigned int*)(self.base + (unsigned long)0xBFFC);
        unsigned int l = (unsigned int)0;
        unsigned int h = (unsigned int)0;
        // Re-read if hi changed during lo read (rollover guard).
        do {
            h = *hi;
            l = *lo;
        } while (*hi != h);
        return ((unsigned long long)h << 32) | (unsigned long long)l;
    }
    return (unsigned long long)0;
}

inline void Clint::schedule(unsigned long delta) {
    unsigned long long now = self.read_mtime();
    self.set_mtimecmp(now + (unsigned long long)delta);
}

// ── PLIC ──────────────────────────────────────────────────────────────────────
// Priority: base + irq*4
// Enable (ctx0): base + 0x2000
// Threshold (ctx0): base + 0x200000
// Claim/complete (ctx0): base + 0x200004


inline void plic_init(unsigned long base_addr) {
    plic.base = base_addr;
}

inline void Plic::set_priority(unsigned int irq, unsigned int priority) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)(self.base + (unsigned long)irq * (unsigned long)4);
        *p = priority & (unsigned int)7;
    }
}

inline void Plic::enable(unsigned int irq) {
    unsafe {
        unsigned long reg = self.base + (unsigned long)0x2000 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
        volatile unsigned int* p = (volatile unsigned int*)reg;
        *p = *p | ((unsigned int)1 << (irq % (unsigned int)32));
    }
}

inline void Plic::disable(unsigned int irq) {
    unsafe {
        unsigned long reg = self.base + (unsigned long)0x2000 + (unsigned long)(irq / (unsigned int)32) * (unsigned long)4;
        volatile unsigned int* p = (volatile unsigned int*)reg;
        *p = *p & ~((unsigned int)1 << (irq % (unsigned int)32));
    }
}

inline void Plic::set_threshold(unsigned int threshold) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)(self.base + (unsigned long)0x200000);
        *p = threshold;
    }
}

inline unsigned int Plic::claim() {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)(self.base + (unsigned long)0x200004);
        return *p;
    }
    return (unsigned int)0;
}

inline void Plic::complete(unsigned int irq) {
    unsafe {
        volatile unsigned int* p = (volatile unsigned int*)(self.base + (unsigned long)0x200004);
        *p = irq;
    }
}

} // namespace std