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:
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
/* syntax-check stub only: SSC C files never touch device registers directly */
|
||||
@@ -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 低位,硬件处理字节对齐。 */
|
||||
* 有效数据总在 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -133,6 +133,8 @@ V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h<br>
|
||||
|
||||
#include "ecatcoe.h"
|
||||
|
||||
#include <stdio.h> /* 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)))
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user