diff --git a/GD32E508_DCU_APP/_syntax_check.bat b/GD32E508_DCU_APP/_syntax_check.bat new file mode 100644 index 0000000..6565ec5 --- /dev/null +++ b/GD32E508_DCU_APP/_syntax_check.bat @@ -0,0 +1,11 @@ +@echo off +call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" >nul 2>&1 +cd /d "G:\Robot_v2\DCU\DCU_APP\GD32E508_DCU_APP" +set PACKED=/D"STRUCT_PACKED_START=" /D"STRUCT_PACKED_END=" /D"MBX_STRUCT_PACKED_START=" /D"MBX_STRUCT_PACKED_END=" /D"OBJ_STRUCT_PACKED_START=" /D"OBJ_STRUCT_PACKED_END=" +set INC=/I. /Icomm\ssc\inc /Icomm\ssc\port /IRTE\_Debug_GD32E508VE /I_syntaxcheck_stubs +echo === mailbox.c === +cl /nologo /Zs /W0 /TC /std:c11 /utf-8 /Zp1 %PACKED% %INC% comm\ssc\src\mailbox.c +if errorlevel 1 (echo MAILBOX_FAIL) else (echo MAILBOX_OK) +echo === dc_app.c === +cl /nologo /Zs /W0 /TC /std:c11 /utf-8 /Zp1 %PACKED% %INC% comm\ssc\port\dc_app.c +if errorlevel 1 (echo DC_APP_FAIL) else (echo DC_APP_OK) diff --git a/GD32E508_DCU_APP/_syntaxcheck_stubs/gd32e50x.h b/GD32E508_DCU_APP/_syntaxcheck_stubs/gd32e50x.h new file mode 100644 index 0000000..ea5fbea --- /dev/null +++ b/GD32E508_DCU_APP/_syntaxcheck_stubs/gd32e50x.h @@ -0,0 +1 @@ +/* syntax-check stub only: SSC C files never touch device registers directly */ diff --git a/GD32E508_DCU_APP/comm/esc_lan9252.cpp b/GD32E508_DCU_APP/comm/esc_lan9252.cpp index b501c3b..f331d13 100644 --- a/GD32E508_DCU_APP/comm/esc_lan9252.cpp +++ b/GD32E508_DCU_APP/comm/esc_lan9252.cpp @@ -329,12 +329,26 @@ void EscLan9252::enableEcatIrqOutput() uint32_t EscLan9252::csrRead(uint16_t addr, uint8_t size) { /* DS 12.11.1:写 CSR_CMD(BUSY|R_nW|SIZE|ADDR) 启动,busy 自清后读 CSR_DATA; - * 有效数据总在 CSR_DATA 低位,硬件处理字节对齐。 */ - uint32_t n = POLL_MAX; - while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {} - writeReg32(ECAT_CSR_CMD_REG, CSR_BUSY | CSR_RNW | ((uint32_t)size << 16) | addr); - n = POLL_MAX; - while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {} + * 有效数据总在 CSR_DATA 低位,硬件处理字节对齐。 + * 1.5.59 加固:CSR_DATA 是锁存寄存器(非 FIFO),命令不变时连读两次必一致; + * 不一致即本拍 EXMC 毛刺——历史上这类错读曾让 GetSyncMan 把 0x1204/0x1208 + * 缓存成邮箱几何(→SSC 邮箱楔死,根治见 mailbox.c 构建期常量),或让 + * CheckSmSettings 以 0x0016/0x001F 瞬态打断状态转换。对错读整事务重跑一次; + * 两轮仍不一致则交出末拍(与旧行为一致的兜底,邮箱几何另有常量兜底)。 */ + for (uint8_t attempt = 0U; attempt < 2U; ++attempt) + { + uint32_t n = POLL_MAX; + while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {} + writeReg32(ECAT_CSR_CMD_REG, CSR_BUSY | CSR_RNW | ((uint32_t)size << 16) | addr); + n = POLL_MAX; + while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {} + const uint32_t d1 = readReg32(ECAT_CSR_DATA_REG); + const uint32_t d2 = readReg32(ECAT_CSR_DATA_REG); + if (d1 == d2) + { + return d1; + } + } return readReg32(ECAT_CSR_DATA_REG); } diff --git a/GD32E508_DCU_APP/comm/ssc/port/dc_app.c b/GD32E508_DCU_APP/comm/ssc/port/dc_app.c index b9ae79e..d7ab8ce 100644 --- a/GD32E508_DCU_APP/comm/ssc/port/dc_app.c +++ b/GD32E508_DCU_APP/comm/ssc/port/dc_app.c @@ -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)); diff --git a/GD32E508_DCU_APP/comm/ssc/port/ecat_def.h b/GD32E508_DCU_APP/comm/ssc/port/ecat_def.h index 01a13bf..f34c59c 100644 --- a/GD32E508_DCU_APP/comm/ssc/port/ecat_def.h +++ b/GD32E508_DCU_APP/comm/ssc/port/ecat_def.h @@ -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 与 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) diff --git a/GD32E508_DCU_APP/comm/ssc/src/mailbox.c b/GD32E508_DCU_APP/comm/ssc/src/mailbox.c index d2aba90..79d6837 100644 --- a/GD32E508_DCU_APP/comm/ssc/src/mailbox.c +++ b/GD32E508_DCU_APP/comm/ssc/src/mailbox.c @@ -133,6 +133,8 @@ V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h
#include "ecatcoe.h" +#include /* printf: SM 几何交叉校验 WARN(1.5.59,见 MBX_StartMailboxHandler) */ + /*-------------------------------------------------------------------------------------- ------ ------ internal Types and Defines @@ -282,24 +284,47 @@ void MBX_Init(void) UINT16 MBX_StartMailboxHandler(void) { UINT16 result = 0; - /* get address of the receive mailbox sync manager (SM0) */ + /* 1.5.59 根治"邮箱楔死"(响应帧头落在 SM1 start+4/+8、逐会话漂移,断电才清): + * 根因链 = INIT→PREOP 时经 GetSyncMan/CSR 单次间接读(EXMC HBI)把 SM0/SM1 + * 几何缓存进 u16EscAddr* 与 u16*MbxSize 等变量;总线毛刺给出的"合法"错值 + * (0x1204/0x1208 均为偶地址且在检查范围内)即被缓存,此后所有 SDO 响应写错址, + * 主站按协议从 0x1200 读永远错位,重枚举重读仍错 → 只有断电能解。本板邮箱 + * 几何是 SII 固定值(gen_eeprom.py 的 MBX_RX/TX_OFFSET/SIZE,ESC 上电自行 + * 加载,主站 config_init 也只是照抄),改为构建期常量(ecat_def.h: + * MBX_SM0/1_START/SIZE)直达缓存,CSR 读降级为交叉校验:失配打 WARN(说明 + * ESC 寄存器被非常规改写,需查)但缓存仍取常量——CSR 错值从此没有进入 + * 缓存的路径,楔死无建立点;运行期内存踩踏也会在每次 INIT→PREOP 被常量覆写。 + * 注意:ESC 寄存器经 PDI/CSR 读出即主机序(与 GetSyncMan 既有路径一致,勿加 + * SWAPWORD;PRAM 帧数据才是线上大端,那边交换是对的)。 */ /*ECATCHANGE_START(V5.11) HW1*/ TSYNCMAN ESCMEM * pSyncMan = (TSYNCMAN ESCMEM *)GetSyncMan(MAILBOX_WRITE); /*ECATCHANGE_END(V5.11) HW1*/ + if (pSyncMan->PhysicalStartAddress != MBX_SM0_START || pSyncMan->Length != MBX_SM0_SIZE) + { + printf("[ECAT][MBX] WARN: SM0 reg 0x%04X/%u != build-time 0x%04X/%u -> keep build-time\r\n", + (unsigned)pSyncMan->PhysicalStartAddress, (unsigned)pSyncMan->Length, + (unsigned)MBX_SM0_START, (unsigned)MBX_SM0_SIZE); + } /* store size of the receive mailbox */ - u16ReceiveMbxSize = pSyncMan->Length; + u16ReceiveMbxSize = MBX_SM0_SIZE; /* store the address of the receive mailbox */ - u16EscAddrReceiveMbx = pSyncMan->PhysicalStartAddress; + u16EscAddrReceiveMbx = MBX_SM0_START; /* get address of the send mailbox sync manager (SM1) */ /*ECATCHANGE_START(V5.11) HW1*/ pSyncMan =(TSYNCMAN ESCMEM *) GetSyncMan(MAILBOX_READ); /*ECATCHANGE_END(V5.11) HW1*/ + if (pSyncMan->PhysicalStartAddress != MBX_SM1_START || pSyncMan->Length != MBX_SM1_SIZE) + { + printf("[ECAT][MBX] WARN: SM1 reg 0x%04X/%u != build-time 0x%04X/%u -> keep build-time\r\n", + (unsigned)pSyncMan->PhysicalStartAddress, (unsigned)pSyncMan->Length, + (unsigned)MBX_SM1_START, (unsigned)MBX_SM1_SIZE); + } /* store the size of the send mailbox */ - u16SendMbxSize = pSyncMan->Length; + u16SendMbxSize = MBX_SM1_SIZE; /* store the address of the send mailbox */ - u16EscAddrSendMbx = pSyncMan->PhysicalStartAddress; + u16EscAddrSendMbx = MBX_SM1_START; // HBu 02.05.06: it should be checked if there are overlaps in the sync manager areas if ((u16EscAddrReceiveMbx + u16ReceiveMbxSize) > u16EscAddrSendMbx && (u16EscAddrReceiveMbx < (u16EscAddrSendMbx + u16SendMbxSize))) diff --git a/GD32E508_DCU_APP/sys/task_runner.cpp b/GD32E508_DCU_APP/sys/task_runner.cpp index 2752046..66b276a 100644 --- a/GD32E508_DCU_APP/sys/task_runner.cpp +++ b/GD32E508_DCU_APP/sys/task_runner.cpp @@ -295,6 +295,9 @@ extern "C" uint32_t EcatDbg_GuardAleventMask(void); * 1.5.52:周期 1s→250ms——新加的邮箱地址自愈(1.5.52 根治 SSC 侧 * u16EscAddrSendMbx 错位楔死,详见 dc_app.c)必须快过主站 SDO 超时 * (~700ms),否则自愈永远落在主站放弃之后,形同虚设。 + * 1.5.59:邮箱楔死已根治(mailbox.c 几何常量化,CSR 错值无进入缓存路径), + * 本守护降级为运行期内存踩踏兜底;且修复了 1.5.52~1.5.58 的 SWAPWORD + * 字节序 bug——那段时期本守护的失配判定恒短路,实际从未生效过。 */ static void ethercatAleMaskGuard() {