2026-09-05 20:07:22 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @file cpu_load.h
|
|
|
|
|
|
* @brief CPU 负载率测量(1.5.63 定义收窄,机制沿用 1.5.61)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 度量定义:**中断 + 非延迟处理段(任务派发 / OTA 中继等请求驱动工作)占墙钟
|
|
|
|
|
|
* 时间的比值**,1s 滚动窗口,分辨率 0.1%(permille)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 为什么不计入主循环常开轮询:本架构在 EtherCAT 实时模式(CONFIG_MAIN_LOOP_WFI_ENABLE=0)
|
|
|
|
|
|
* 下主循环空闲时是 NOP 忙等,且 runPending() 每圈无条件跑 SSC MainLoop 轮询与各
|
|
|
|
|
|
* poll() 检查——把这部分计入"busy"的话负载率恒 ≈99%,失去参考价值(1.5.61 实机
|
|
|
|
|
|
* 实测 99.4%,其中绝大部分是自由运转的轮询而非在处理事件)。收窄后的定义回答
|
|
|
|
|
|
* 的问题变成"CPU 有多少时间在响应事件",随真实负载(CAN 流量、OTA 中继、
|
|
|
|
|
|
* DC 模式 Sync0 过程数据搬运)增长;常开轮询成本与空转时间不计入。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 计入项(全走同一深度计数器求**区间并集**,嵌套不重不漏):
|
|
|
|
|
|
* - 全部挂钩 ISR:TIMER2(100µs 调度)、SysTick、EXTI0/1/2(EtherCAT Sync0/PDI/
|
|
|
|
|
|
* Sync1)、TIMER0(DC 定时档)、CAN0-2 RX+TX、SPI0+EXTI10_15、USART0。
|
|
|
|
|
|
* DC 模式下 Sync0_Isr 的 EXMC 过程数据搬运由此计入——这是大头,不能漏。
|
2026-09-20 22:58:09 +08:00
|
|
|
|
* - 主循环非延迟段:TaskManager 派发任务、BOOT、OTA relay、新鲜度救援、
|
|
|
|
|
|
* free-run 下 **1ms 节流** 的过程数据应用(有 CpuLoad 挂钩)。
|
|
|
|
|
|
* - PDI/Sync0 ISR 全程(含 SM 类型下每拍 Process/映射)——负载随 PDO Hz 升。
|
|
|
|
|
|
* 不计入:MainLoop/SSC/poll 空转。
|
|
|
|
|
|
* 性能约束:PDI 热路径不得「凡 AL 事件就 Process/InputMapping」——8kHz 时
|
|
|
|
|
|
* 单拍超 ~120µs 墙会掉速;输出用 12B 探针,process 兜底读带新鲜度门。
|
2026-09-05 20:07:22 +08:00
|
|
|
|
*
|
|
|
|
|
|
* 并发协议(无关中断、无临界区):enter/exit 用 LDREX/STREX 原子改深度,
|
|
|
|
|
|
* 任一优先级可抢占任意上下文而不破坏计数——STREX 失败方重试时会看到已修正的
|
|
|
|
|
|
* 深度并补上时间戳。已知残余误差:exit 在读时间戳与递减深度之间被高优先级
|
|
|
|
|
|
* 中断插入时,该次中断时长漏计(几 cycle 窗口,对 0.1% 分辨率可忽略)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @note DWT 已由 EscLan9252::init 使能;init() 幂等兜底,在主循环启动前调用。
|
|
|
|
|
|
*/
|
|
|
|
|
|
#ifndef SYS_CPU_LOAD_H
|
|
|
|
|
|
#define SYS_CPU_LOAD_H
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
2026-09-20 22:58:09 +08:00
|
|
|
|
#ifdef __cplusplus
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
#endif
|
|
|
|
|
|
/** C 侧挂钩(ecatappl.c / dc_app.c)。与 beginWork/endWork 同一深度计数器。 */
|
|
|
|
|
|
void CpuLoad_Enter(void);
|
|
|
|
|
|
void CpuLoad_Leave(void);
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
2026-09-05 20:07:22 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief CPU 负载率测量。
|
|
|
|
|
|
* @note 全静态类,与 SysTickTimer 同风格。
|
|
|
|
|
|
*/
|
|
|
|
|
|
class CpuLoad {
|
|
|
|
|
|
public:
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 使能 DWT CYCCNT 并复位统计状态。
|
|
|
|
|
|
* @note 幂等;须在主循环启动前调用一次。
|
|
|
|
|
|
*/
|
|
|
|
|
|
static void init();
|
|
|
|
|
|
|
|
|
|
|
|
/** @brief 主循环圈起点:进入"busy"区间。与 endWork() 成对,每圈各一次。 */
|
|
|
|
|
|
static void beginWork();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 主循环圈终点:退出"busy"区间。
|
|
|
|
|
|
* @note 与 beginWork() 成对;窗口滚动不在这里(begin/end 可能整圈不执行),
|
|
|
|
|
|
* 见 tick()。
|
|
|
|
|
|
*/
|
|
|
|
|
|
static void endWork();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 主循环每圈无条件调用:检查 1s 窗口是否滚动。
|
|
|
|
|
|
* @note 只做一次 CYCCNT 读 + 比较(命中才进 flash 侧结算函数),滚动不再
|
|
|
|
|
|
* 依赖"本圈恰好有挂钩段执行",空闲系统 seq 也会按秒推进。
|
|
|
|
|
|
*/
|
|
|
|
|
|
static void tick();
|
|
|
|
|
|
|
|
|
|
|
|
/** @brief 中断入口挂钩:与 isrExit() 成对,包住 ISR 主体。 */
|
|
|
|
|
|
static void isrEnter();
|
|
|
|
|
|
|
|
|
|
|
|
/** @brief 中断出口挂钩。 */
|
|
|
|
|
|
static void isrExit();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 最近一个完整窗口的负载率。
|
|
|
|
|
|
* @return 0..1000,单位 0.1%(1000=满载);窗口未滚动过时为 0。
|
|
|
|
|
|
*/
|
|
|
|
|
|
static uint16_t loadPermille() { return s_loadPerm; }
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 已完成的统计窗口数(自 init 起,单调递增回绕)。
|
|
|
|
|
|
* @note 随负载率一起进 EtherCAT 输入 PDO 段 5,主站据此判断数据是否仍在刷新。
|
|
|
|
|
|
*/
|
|
|
|
|
|
static uint32_t windowSeq() { return s_seq; }
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
/* 静态成员开成 public:cpu_load.cpp 里 .ramfunc 的文件级内联热路径
|
|
|
|
|
|
* (cpuLoadEnter/cpuLoadLeave/cpuLoadRollWindow) 直接触碰它们,
|
|
|
|
|
|
* 经成员函数绕一道反而多一层调用。勿在模块外写这些状态。 */
|
|
|
|
|
|
|
|
|
|
|
|
static volatile uint32_t s_depth; /**< busy 区间嵌套深度 */
|
|
|
|
|
|
static volatile uint32_t s_stamp; /**< 深度 0→1 时刻的 CYCCNT 快照 */
|
|
|
|
|
|
static volatile uint32_t s_busy; /**< 当前窗口内累计 busy 周期数 */
|
|
|
|
|
|
static volatile uint32_t s_winStart; /**< 当前窗口起点 CYCCNT */
|
|
|
|
|
|
static volatile uint16_t s_loadPerm; /**< 最近一个完整窗口的负载率(0.1%) */
|
|
|
|
|
|
static volatile uint32_t s_seq; /**< 完成滚动的窗口计数 */
|
|
|
|
|
|
};
|
2026-09-20 22:58:09 +08:00
|
|
|
|
#endif /* __cplusplus */
|
2026-09-05 20:07:22 +08:00
|
|
|
|
|
|
|
|
|
|
#endif /* SYS_CPU_LOAD_H */
|