- 添加CAN反馈和ID快照SPI命令文件 - 移除EXMC组件依赖,精简项目配置 - 添加RAM函数段链接脚本,优化Flash等待周期抖动 - 实现EscExmcBus类,提供16位并行总线访问LAN9252 - 实现EscLan9252驱动,支持寄存器和PDI内存访问 - 实现EthercatSlave类,采用三缓冲机制进行PDO数据交换 - 实现EthercatSync同步管理,支持SYNC0/SYNC1中断和定时器模式 - 添加ISR执行时间统计和诊断功能 - 分离周期路径和事件路径通知机制,避免RMW竞态
7.8 KiB
7.8 KiB
Tasks
-
Task 1: 阶段 A — EtherCAT 周期任务标志脱离 TaskManager(修复严重风险 #1)
- SubTask 1.1: 在
comm/ethercat_slave.h中声明static volatile uint8_t s_periodicSeq与static uint8_t s_processedSeq私有静态成员,并增加static bool hasPendingPeriodic()/static void markPeriodicProcessed()公有接口 - SubTask 1.2: 在
comm/ethercat_slave.cpp的periodicIsr()末尾移除TaskManager::set(1UL << TASK_ETHERCAT_PROCESS),改为s_periodicSeq++(uint8 自然回绕,原子单字节写) - SubTask 1.3: 实现
hasPendingPeriodic()返回s_periodicSeq != s_processedSeq,markPeriodicProcessed()执行s_processedSeq = s_periodicSeq - SubTask 1.4: 在
sys/task_runner.cpp的runPending()开头(TaskManager::takePending 之前或之后)增加:if (EthercatSlave::hasPendingPeriodic()) { EthercatSlave::markPeriodicProcessed(); taskEthercatProcess(); },该分支用#if CONFIG_ETHERCAT_ENABLE保护 - SubTask 1.5: 确认 ESC IRQ 路径(
comm/ethercat_sync.cpp::onEscIrqExti())仍调用TaskManager::set(1UL << TASK_ETHERCAT_PROCESS),BASEPRI 保护该路径,无改动 - SubTask 1.6: 在
comm/ethercat_slave.h头注释中说明"周期路径用 s_periodicSeq,事件路径用 TASK_ETHERCAT_PROCESS,两者独立"
- SubTask 1.1: 在
-
Task 2: 阶段 B — EXMC 总线访问权分离(修复严重风险 #2)
- SubTask 2.1: 在
comm/esc_lan9252.h中声明static uint16_t safeReadReg(uint16_t addr)/static void safeWriteReg(uint16_t addr, uint16_t val)/static void safeReadPdi(uint16_t offset, uint8_t *buf, size_t len)/static void safeWritePdi(uint16_t offset, const uint8_t *buf, size_t len)公有接口 - SubTask 2.2: 在
comm/esc_lan9252.cpp实现 safe 接口,内部用__disable_irq()/__set_PRIMASK()包裹(注意:这里用 PRIMASK 而非 BASEPRI,因为要阻塞 preempt=0 的 SYNC0),调用对应的无锁版本后恢复 - SubTask 2.3: 在
comm/esc_lan9252.h/comm/esc_exmc_bus.h头文件注释中明确标注:readReg/writeReg/readPdi/writePdi/read16/write16/readBlock/writeBlock为 "ISR-only,仅供 preempt=0 调用,无锁";safeReadReg/safeWriteReg/safeReadPdi/safeWritePdi为 "Thread-safe,主循环与 init 调用,PRIMASK 保护" - SubTask 2.4: 审查
comm/ethercat_slave.cpp的init()与process(),所有 EXMC 访问改用 safe 接口(init 阶段虽中断未使能但保持一致性;process 若有 EXMC 访问必须用 safe) - SubTask 2.5: 在
comm/ethercat_slave.cpp的periodicIsr()中确认仍调用无锁的readPdi/writePdi(因 ISR 为 preempt=0,天然原子) - SubTask 2.6: 代码审查确认无主循环路径误调用 ISR-only 接口
- SubTask 2.1: 在
-
Task 3: 阶段 C — PDO 三缓冲替换双缓冲(修复严重风险 #3 与 #4)
- SubTask 3.1: 在
comm/ethercat_slave.h中将s_inputs[2]/s_outputs[2]改为s_inputs[3]/s_outputs[3],成员名改为s_inputWriteIdx/s_outputReadIdx(替换原s_inputReadIndex/s_outputWriteIndex),类型volatile uint8_t - SubTask 3.2: 在
comm/ethercat_slave.h头注释中更新双缓冲说明为三缓冲说明,描述模 3 轮转规则 - SubTask 3.3: 在
comm/ethercat_slave.cpp的init()中初始化s_inputWriteIdx = 0/s_outputReadIdx = 0 - SubTask 3.4: 重写
periodicIsr()的缓冲管理:写入s_inputs[s_inputWriteIdx]→ 递增s_inputWriteIdx = (s_inputWriteIdx + 1) % 3;读取s_outputs[s_outputReadIdx]→ 递增s_outputReadIdx = (s_outputReadIdx + 1) % 3 - SubTask 3.5: 重写
getLatestInputs()返回&s_inputs[(s_inputWriteIdx + 2) % 3](最新完成且与 ISR 当前写入隔一个) - SubTask 3.6: 重写
setOutputs()写入s_outputs[(s_outputReadIdx + 1) % 3](与 ISR 当前读取隔一个),无需临界区 - SubTask 3.7: 移除原 XOR 翻转逻辑(
^1U),确认无残留引用 - SubTask 3.8: 在
comm/ethercat_slave.h注释中明确"三缓冲容忍主循环跨 1 个周期,主循环处理必须 < 2 × 周期时间"
- SubTask 3.1: 在
-
Task 4: 阶段 D — periodicIsr 执行时间测量与评估(修复高风险 #5)
- SubTask 4.1: 在
comm/ethercat_slave.h增加static volatile uint32_t s_isrMaxCycles/static volatile uint32_t s_isrAvgCycles/static volatile uint32_t s_isrCount诊断成员 - SubTask 4.2: 在
comm/ethercat_slave.cpp的periodicIsr()入口读DWT->CYCCNT存局部变量,出口再读,差值更新 max(原子比较)与 avg(滑动平均或累加) - SubTask 4.3: 在
sys/initial.cpp或comm/esc_lan9252.cpp::init()中使能 DWT:CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; - SubTask 4.4: 在
sys/task_runner.cpp的taskDiagnosticPrint()中增加 EtherCAT ISR 执行时间输出(受#if CONFIG_ETHERCAT_ENABLE保护),格式periodicIsr max=Xus avg=Yus(cycles / 180 转 µs) - SubTask 4.5: 在
sys/config.h增加CONFIG_ETHERCAT_ISR_MAX_US_THRESHOLD 5U宏,作为评估阈值 - SubTask 4.6: 在 spec.md 与代码注释中记录:若实测 > 5µs 需评估减小
CONFIG_ETHERCAT_PDO_SIZE或优化 EXMC 时序;若 ≤ 5µs 则接受现状并在注释中说明"preempt=0 阻塞窗口 ≤ 5µs,CAN/SPI 需容忍"
- SubTask 4.1: 在
-
Task 5: 阶段 E — .ramfunc 段落地与调用链 RAM 化(修复高风险 #6)
- SubTask 5.1: 定位项目链接脚本文件(查找
*.sct或 RTE 配置中的 linker script) - SubTask 5.2: 在链接脚本中新增
.ramfunc输出段,执行区(VMA)位于 SRAM,加载区(LMA)位于 Flash(参考.data段的 scatter 语法) - SubTask 5.3: 确认启动代码(
startup_gd32e50x_cl.s或system_gd32e50x.c)是否自动拷贝.ramfunc段;若否,增加拷贝逻辑(类似.data拷贝循环) - SubTask 5.4: 在
comm/ethercat_slave.cpp的periodicIsr()已有__attribute__((section(".ramfunc")))基础上增加noinline属性 - SubTask 5.5: 在
comm/esc_lan9252.cpp的readPdi()/writePdi()增加__attribute__((section(".ramfunc"), noinline)) - SubTask 5.6: 在
comm/esc_exmc_bus.cpp的read16()/write16()增加__attribute__((section(".ramfunc"), noinline))(注意:readBlock/writeBlock不在 ISR 调用链,不标记) - SubTask 5.7: 编译后查看 map 文件,确认
.ramfunc段的 VMA 在 SRAM 区域、LMA 在 Flash 区域 - SubTask 5.8: 若 DWT 测量(阶段 D)显示 Flash 执行已满足 <1µs 抖动,可在 spec 中记录降级方案(仅 periodicIsr 本体 RAM 化),但默认按完整 RAM 化实施
- SubTask 5.1: 定位项目链接脚本文件(查找
-
Task 6: 编译验证与回退(⚠️ 受阻于预存 EXMC 组件解析问题)
- SubTask 6.1: 临时将
sys/config.h中CONFIG_ETHERCAT_ENABLE改为 1,全量编译通过,无 error - SubTask 6.2: 检查无新增 warning(特别注意
.ramfunc段未定义或未拷贝的链接警告) - SubTask 6.3: 查看 map 文件确认
.ramfunc段在 SRAM、s_inputs[3]/s_outputs[3]在 SRAM、s_periodicSeq在 SRAM - SubTask 6.4: 编译验证完成后将
CONFIG_ETHERCAT_ENABLE恢复为 0 - SubTask 6.5: 确认
CONFIG_ETHERCAT_ENABLE=0时全量编译仍通过,功能与改造前等价 - SubTask 6.6: ⚠️ 阻塞项:
GD32E508_DCU_APP.cproject.yml第 68 行无条件引用Device:GD32E50x_StdPeripherals:EXMC组件,但已安装的 GD32E50x_DFP pack(1.9.0/1.3.2)pdsc 未注册 EXMC 组件,导致 csolution 组件解析失败,ENABLE=0 与 ENABLE=1 均无法构建。EXMC 驱动源文件(gd32e50x_exmc.h/.c)实际存在于 pack 中。需单独修复 cproject.yml(改为直接引用源文件)或更新 pack 后重新执行本任务
- SubTask 6.1: 临时将
Task Dependencies
- Task 1, 2, 3 相互独立,可并行实施(均集中在
comm/目录,但修改不同函数与成员) - Task 4 依赖 Task 1(periodicIsr 改造完成才能测量)
- Task 5 依赖 Task 1, 2, 3(调用链 RAM 化需在接口稳定后)
- Task 6 依赖 Task 1-5 全部完成