fix(ssc): 根治邮箱楔死(1.5.59): 几何常量化消灭建立路径, 守护字节序修复

- ecat_def.h: 新增 MBX_SM0/1_START/SIZE 构建期常量(=SII 真值 0x1000/0x1200 各 256B,
  与 gen_eeprom.py/DCU_ESI.xml 三方一致, 改 SII 邮箱尺寸必须同步)
- mailbox.c: MBX_StartMailboxHandler 常量直达缓存, CSR 单次间接读降为交叉校验
  (失配打 [ECAT][MBX] WARN)。根因: INIT→PREOP 单次 CSR 读错值(0x1204/0x1208,
  BUSLAT 余量外的 EXMC 毛刺)被缓存成 u16EscAddrSendMbx → 响应写错址 → 断电才清;
  常量化后错值无进入缓存的路径, 运行期内存踩踏也会在每次 INIT→PREOP 被覆写
- esc_lan9252.cpp: csrRead 双读 CSR_DATA 一致校验+整事务重试(CSR_DATA 为锁存
  寄存器非 FIFO, 连读必一致; 不一致即本拍总线毛刺)。瞬态错读曾致 CheckSmSettings
  以 0x0016/0x001F 打断转换
- dc_app.c: 修复守护/心跳 SWAPWORD 字节序 bug——ESC 寄存器 PDI/CSR 读即主机序
  (与 GetSyncMan/mailbox 裸读一致), 交换使 sm0Len=0x0001<8 恒短路, 1.5.52~1.5.58
  地址失配守护从未生效过; 现降级为运行期内存踩踏兜底并真正可用; ReadAlDiag 同修,
  期间串口日志的 al/evt 均为字节交换错值, 历史日志需交换重读
- task_runner.cpp: 守护注释同步
- 新增 _syntax_check.bat + _syntaxcheck_stubs: 无 AC6 环境用 VS2022 cl /Zs 验 C 文件

真机验收: 140 次 op/start 全成功(40 轮 DC 4ms + 100 轮 DC/free-run 交替),
每轮进 OP、SDO 0x2000:3/4 全成功, 无 0x0016/0x001D/0x001B。
This commit is contained in:
cbb
2026-09-05 17:38:35 +08:00
parent f9ef1b8da2
commit 2ebd4ff1d2
7 changed files with 112 additions and 31 deletions
+24 -20
View File
@@ -374,13 +374,18 @@ void APPL_Application(void)
* AL 诊断快照 — task_runner 5s 心跳用 (C 链接, C++ 侧 extern "C" 声明)
* [19:16]=AL 状态(0x0130 低 4 位) [31:24]=AL 错误码(0x0130 高 8 位)
* [15:0] =AL 事件寄存器(0x0220, bit0=主站 AL 控制请求待处理)
*
* 1.5.59 字节序修正:ESC 寄存器经 PDI/CSR 读出即主机序(LAN9252 CSR 引擎按
* 16-bit 主机总线给出寄存器"值";线上大端只存在于网络侧与 PRAM 原始帧数据),
* 与 SSC 自身 GetSyncMan/mailbox 的裸读约定一致。1.5.48~1.5.58 这里误加了
* SWAPWORD,心跳打印的 al/evt 全是字节交换后的错值——那段时期的串口日志
* (如 evt=0x0400)需按字节交换重新解读后才能当证据用。
*---------------------------------------------------------------------------*/
UINT32 EcatDbg_ReadAlDiag(void)
{
UINT16 alStatus = 0U;
HW_EscReadWord(alStatus, ESC_AL_STATUS_OFFSET);
alStatus = SWAPWORD(alStatus);
return ((UINT32)alStatus << 16) | (UINT32)SWAPWORD(HW_GetALEventRegister());
return ((UINT32)alStatus << 16) | (UINT32)HW_GetALEventRegister();
}
/*---------------------------------------------------------------------------
@@ -420,30 +425,29 @@ UINT32 EcatDbg_GuardAleventMask(void)
return 0xFFFFU; /* 状态不适用:返回哨兵值,调用方不打印 */
}
/* (0) 邮箱 SM 地址/长度失配自愈(2026-09-03 03:00 根治,1.5.52):
/* (0) 邮箱 SM 地址/长度失配兜底(1.5.52 引入,1.5.59 修复字节序并降级为兜底):
*
* 主站侧探针(楔死态)实锤的证据链:
* 历史楔死(1.5.51 及更早,断电才清)的实锤证据链:
* - ESC/SSC 邮箱处理全程活着(注入合法 CoE 请求后 200ms 内 SM1 出响应);
* - 但响应帧头出现在 SM1 start+8 而非 start(且逐会话累积:+4→+8,断电才清零);
* - 网络侧(FPRD)读 SM0/SM1 寄存器 start 恒正确(0x1000/0x1200);
* => SSC 的 u16EscAddrSendMbx(Init→PreOP 时经 GetSyncMan/CSR 间接读)拿到了错值,
* 主站按协议从 0x1200 读 → 帧头永远错位 → SOEM 报 WkcError/MailboxError,
* 且预清理重枚举(重跑 StartMailboxHandler)重读仍错 → 死锁,只有断电恢复。
* 自愈:AL>=PREOP 时用寄存器真值(0x0800/0x0808)校验 SSC 缓存的 start/len,
* 失配即写回 + WARN(旧值/新值一并打印,下次串口复现可确凿定位 CSR 读错还是
* 变量污染——两种成因此写回均有效,写回后下一个邮箱事务即恢复)。
* 变量仅在 Stop/StartMailboxHandler(INIT<->PREOP 迁移,主循环上下文)被写,
* 守护同样在主循环——无并发风险。 */
* => SSC 缓存的 u16EscAddrSendMbx(Init→PreOP 时经 GetSyncMan/CSR 间接读)拿到了错值,
* 主站按协议从 0x1200 读 → 帧头永远错位 → SOEM 报 WkcError/MailboxError。
* 1.5.59 根治:mailbox.c 的 MBX_StartMailboxHandler 改用构建期常量直达
* (ecat_def.h:MBX_SM0/1_START/SIZE),CSR 错值从此没有进入缓存的路径——
* 楔死无建立点,本守护降级为"运行期内存踩踏"的最后兜底:AL>=PREOP 时用
* 寄存器真值校验 SSC 缓存的 start/len,失配即写回 + WARN。
*
* ★ 1.5.59 字节序修复:ESC 寄存器经 PDI/CSR 读出即主机序(与 SSC 自身
* GetSyncMan/mailbox 路径一致)。1.5.52~1.5.58 这里误加了 SWAPWORD——
* sm0Len 被换成 0x0001 < 8,下面的失配判定恒短路,守护自上线起从未真正
* 执行过("自愈"名存实亡,楔死实际靠重枚举重读碰运气恢复)。 */
if (al >= 2U && bMbxRunning) {
UINT16 sm0Start = 0U, sm0Len = 0U, sm1Start = 0U, sm1Len = 0U;
HW_EscReadWord(sm0Start, ESC_SYNCMAN_REG_OFFSET);
HW_EscReadWord(sm0Len, ESC_SYNCMAN_REG_OFFSET + 2U);
HW_EscReadWord(sm1Start, ESC_SYNCMAN_REG_OFFSET + SIZEOF_SM_REGISTER);
HW_EscReadWord(sm1Len, ESC_SYNCMAN_REG_OFFSET + SIZEOF_SM_REGISTER + 2U);
sm0Start = SWAPWORD(sm0Start);
sm0Len = SWAPWORD(sm0Len);
sm1Start = SWAPWORD(sm1Start);
sm1Len = SWAPWORD(sm1Len);
if (sm0Len >= 8U && sm1Len >= 8U
&& (u16EscAddrReceiveMbx != sm0Start || u16EscAddrSendMbx != sm1Start
|| u16ReceiveMbxSize != sm0Len || u16SendMbxSize != sm1Len)) {
@@ -464,16 +468,17 @@ UINT32 EcatDbg_GuardAleventMask(void)
return 0xFFFFU; /* 以下仅 SAFEOP+ 适用 */
}
evt = SWAPWORD(HW_GetALEventRegister());
/* 1.5.59:PDI 寄存器读即主机序,不再 SWAPWORD——1.5.48~1.5.58 交换出的
* evt/actual 是错值,0x204 缺位判定恒真(每 250ms 盲写一次 mask,值凑巧
* 正确故未暴露),SM2 事件判定则对在错位上。详见上方字节序修复说明。 */
evt = HW_GetALEventRegister();
HW_EscReadWord(actual, ESC_AL_EVENTMASK_OFFSET);
actual = SWAPWORD(actual);
/* (1) 0x204 缺位自愈 */
if ((UINT16)(actual & u16ALEventMask) != u16ALEventMask) {
const UINT16 before = actual;
HW_SetALEventMask(u16ALEventMask);
HW_EscReadWord(actual, ESC_AL_EVENTMASK_OFFSET);
actual = SWAPWORD(actual);
printf("[ECAT][AEM] WARN: AL Event Mask lost bits, actual=0x%04X expect=0x%04X -> restored 0x%04X\r\n",
(unsigned)before, (unsigned)u16ALEventMask, (unsigned)actual);
}
@@ -482,7 +487,6 @@ UINT32 EcatDbg_GuardAleventMask(void)
* (OP 态下 LRW 每周期置位/清除 bit10 属正常,不能误报) */
if (al == 4U /* SAFEOP */ && (evt & 0x0400U) != 0U && bEcatFirstOutputsReceived == FALSE) {
HW_EscReadWord(wdState, ESC_PD_WD_STATE);
wdState = SWAPWORD(wdState);
bEcatFirstOutputsReceived = TRUE;
HW_EscReadWord(dummy, nEscAddrOutputData);
HW_EscReadWord(dummy, (UINT16)(nEscAddrOutputData + nPdOutputSize - 2U));
+23
View File
@@ -708,6 +708,29 @@
#define MAX_MBX_READ_ADDRESS 0x1FFF
#endif
/* 1.5.59 根治邮箱楔死:本板邮箱几何构建期常量。
* 取值 = gen_eeprom.py 的 MBX_RX_OFFSET/SIZE(0x1000/256) 与 MBX_TX_OFFSET/SIZE
* (0x1200/256),即 DCU_ESI.xml <Sm> 与 ESC 上电自 SII 加载的寄存器真值;主站
* config_init 也只是照抄同一份 SII。MBX_StartMailboxHandler 以此直达缓存,
* 不再信任单次 CSR 间接读(错值曾以 0x1204/0x1208 进入缓存 → 所有 SDO 响应
* 写错址 → 断电才解的楔死),CSR 读仅作交叉校验(mailbox.c 打 WARN)。
* ★ 改 SII 邮箱几何必须同步改这里(gen_eeprom.py 顶部注释同样有约束)。★ */
#ifndef MBX_SM0_START
#define MBX_SM0_START 0x1000
#endif
#ifndef MBX_SM0_SIZE
#define MBX_SM0_SIZE 0x0100
#endif
#ifndef MBX_SM1_START
#define MBX_SM1_START 0x1200
#endif
#ifndef MBX_SM1_SIZE
#define MBX_SM1_SIZE 0x0100
#endif
/*-----------------------------------------------------------------------------------------
------
------ PDO Timing (DC sync parameters, unit: ns)