std1.0.0-beta-7 SafeC docs

Source interrupt/clock.sc

View in the repository

// SafeC Standard Library — System Clock Configuration
// Generic MMIO layout (loosely based on STM32 RCC):
//   base + 0x00 = clock control register (source select, PLL enable)
//   base + 0x04 = PLL configuration (multiplier, divider)
//   base + 0x08 = clock config register (AHB/APB prescalers)
//   base + 0x0C = status register (PLL ready, clock ready)
#pragma once
#include <std/interrupt/clock.h>

namespace std {

inline struct ClockConfig ClockConfig::new(&volatile unsigned int base) {
    struct ClockConfig c;
    c.base     = base;
    c.source   = 0; // HSI default
    c.sys_freq = (unsigned int)8000000; // 8 MHz HSI typical
    c.ahb_div  = (unsigned int)1;
    c.apb1_div = (unsigned int)1;
    c.apb2_div = (unsigned int)1;
    return c;
}

inline void ClockConfig::set_source(int source) {
    self.source = source;
    unsafe {
        unsigned int* ctrl = (unsigned int*)self.base;
        unsigned int val   = volatile_load(ctrl);
        val = val & ~(unsigned int)3;
        val = val | ((unsigned int)source & (unsigned int)3);
        volatile_store(ctrl, val);

        // Wait for clock switch to complete
        unsigned int* status = (unsigned int*)((unsigned long)self.base + (unsigned long)12);
        int timeout = 100000;
        while (timeout > 0) {
            unsigned int st = volatile_load(status);
            if (((st >> 2) & (unsigned int)3) == ((unsigned int)source & (unsigned int)3)) {
                return;
            }
            timeout = timeout - 1;
        }
    }
}

int ClockConfig::configure_pll(unsigned int mul, unsigned int div) {
    if (div == (unsigned int)0) { return 0; }
    unsafe {
        unsigned int* ctrl = (unsigned int*)self.base;
        unsigned int val   = volatile_load(ctrl);
        val = val & ~((unsigned int)1 << 24); // disable PLL
        volatile_store(ctrl, val);

        unsigned int* pll_cfg = (unsigned int*)((unsigned long)self.base + (unsigned long)4);
        unsigned int cfg = (mul & (unsigned int)0xFF) | ((div & (unsigned int)0xFF) << 8);
        volatile_store(pll_cfg, cfg);

        val = val | ((unsigned int)1 << 24); // enable PLL
        volatile_store(ctrl, val);

        // Wait for PLL ready
        unsigned int* status = (unsigned int*)((unsigned long)self.base + (unsigned long)12);
        int timeout = 100000;
        while (timeout > 0) {
            unsigned int st = volatile_load(status);
            if ((st & ((unsigned int)1 << 25)) != (unsigned int)0) {
                self.sys_freq = (self.sys_freq * mul) / div;
                return 1;
            }
            timeout = timeout - 1;
        }
    }
    return 0;
}

inline void ClockConfig::set_ahb_div(unsigned int div) {
    self.ahb_div = div;
    unsafe {
        unsigned int* cfg = (unsigned int*)((unsigned long)self.base + (unsigned long)8);
        unsigned int val  = volatile_load(cfg);
        val = val & ~((unsigned int)0xF << 4);
        val = val | ((div & (unsigned int)0xF) << 4);
        volatile_store(cfg, val);
    }
}

inline void ClockConfig::set_apb1_div(unsigned int div) {
    self.apb1_div = div;
    unsafe {
        unsigned int* cfg = (unsigned int*)((unsigned long)self.base + (unsigned long)8);
        unsigned int val  = volatile_load(cfg);
        val = val & ~((unsigned int)0x7 << 8);
        val = val | ((div & (unsigned int)0x7) << 8);
        volatile_store(cfg, val);
    }
}

inline void ClockConfig::set_apb2_div(unsigned int div) {
    self.apb2_div = div;
    unsafe {
        unsigned int* cfg = (unsigned int*)((unsigned long)self.base + (unsigned long)8);
        unsigned int val  = volatile_load(cfg);
        val = val & ~((unsigned int)0x7 << 11);
        val = val | ((div & (unsigned int)0x7) << 11);
        volatile_store(cfg, val);
    }
}

inline unsigned int ClockConfig::get_freq() const {
    return self.sys_freq / self.ahb_div;
}

} // namespace std