/** * @file cpu_load.cpp * @brief CPU 负载率测量实现(1.5.61,度量定义与并发协议见 cpu_load.h)。 * * 热路径(beginWork/endWork/isrEnter/isrExit/tick)放进 .ramfunc——它们会被包进 * EXTI0(Sync0, preempt=0) 等延迟敏感的 ISR 包装,与 EthercatSync::onSync0Exti * 同款 section 属性,避免 flash 等待态给 Sync0 入口添抖动。1s 一次的窗口 * 结算(含 64 位除法)留在普通 flash 函数里。 */ #include "sys/cpu_load.h" #include "RTE_Components.h" #include CMSIS_device_header volatile uint32_t CpuLoad::s_depth = 0U; volatile uint32_t CpuLoad::s_stamp = 0U; volatile uint32_t CpuLoad::s_busy = 0U; volatile uint32_t CpuLoad::s_winStart = 0U; volatile uint16_t CpuLoad::s_loadPerm = 0U; volatile uint32_t CpuLoad::s_seq = 0U; /** @brief 区间并集的共享进入点。0→1 者负责盖时间戳;STREX 失败方重试时深度 * 已被抢占者修正,不会再重复盖戳。 */ __attribute__((always_inline)) static inline void cpuLoadEnter() { uint32_t d; do { d = __LDREXW(&CpuLoad::s_depth); if (d == 0U) { CpuLoad::s_stamp = DWT->CYCCNT; __DMB(); } } while (__STREXW(d + 1U, &CpuLoad::s_depth) != 0U); __DMB(); } /** @brief 区间并集的共享退出点。1→0 者把区间累进 s_busy(同样走独占访问, * 防"ISR 恰好插在主循环退出的读-改-写之间"丢累加)。 */ __attribute__((always_inline)) static inline void cpuLoadLeave() { const uint32_t end = DWT->CYCCNT; const uint32_t start = CpuLoad::s_stamp; uint32_t d; do { d = __LDREXW(&CpuLoad::s_depth); } while (__STREXW(d - 1U, &CpuLoad::s_depth) != 0U); if (d == 1U) { const uint32_t delta = end - start; uint32_t b; do { b = __LDREXW(&CpuLoad::s_busy); } while (__STREXW(b + delta, &CpuLoad::s_busy) != 0U); } } void CpuLoad::init() { /* DWT 已由 EscLan9252::init 使能;这里幂等兜底(万一 EtherCAT 初始化被裁剪)。 */ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; s_depth = 0U; s_busy = 0U; s_winStart = DWT->CYCCNT; s_loadPerm = 0U; s_seq = 0U; } __attribute__((section(".ramfunc"), noinline)) void CpuLoad::beginWork() { cpuLoadEnter(); } __attribute__((section(".ramfunc"), noinline)) void CpuLoad::isrEnter() { cpuLoadEnter(); } __attribute__((section(".ramfunc"), noinline)) void CpuLoad::isrExit() { cpuLoadLeave(); } /** * @brief 窗口结算(普通 flash 函数,1s 才进一次)。 * @note s_busy 用独占访问换出:窗口边界上 ISR 的在途累加要么算进本窗口、 * 要么算进下窗口,不丢。 */ static void cpuLoadRollWindow(uint32_t now) { const uint32_t elapsed = now - CpuLoad::s_winStart; /* 调用方已保证 >= 1s */ uint32_t busy; do { busy = __LDREXW(&CpuLoad::s_busy); } while (__STREXW(0U, &CpuLoad::s_busy) != 0U); CpuLoad::s_winStart = now; uint32_t perm = (uint32_t)(((uint64_t)busy * 1000ULL) / (uint64_t)elapsed); if (perm > 1000U) { perm = 1000U; } CpuLoad::s_loadPerm = (uint16_t)perm; CpuLoad::s_seq++; } __attribute__((section(".ramfunc"), noinline)) void CpuLoad::endWork() { cpuLoadLeave(); } __attribute__((section(".ramfunc"), noinline)) void CpuLoad::tick() { const uint32_t now = DWT->CYCCNT; if ((uint32_t)(now - s_winStart) >= SystemCoreClock) { cpuLoadRollWindow(now); } } /* C 侧挂钩:供 ecatappl.c / dc_app.c 计入 EtherCAT 过程数据路径。 * 与 beginWork/endWork 共用深度计数器,嵌套不重不漏。 */ extern "C" void CpuLoad_Enter(void) { CpuLoad::beginWork(); } extern "C" void CpuLoad_Leave(void) { CpuLoad::endWork(); }