fix(comm): 回退LAN9252批量读写优化并修复多个CAN-EtherCAT桥竞态问题
回退1.6.0引入的LAN9252 PRAM批量读写优化,因其AVAIL_CNT计数器对短读存在语义问题, 导致邮箱短读和探针传输时出现FIFO陈旧数据损坏问题。 同时修复EtherCAT-CAN桥中的多个竞态条件: - 修复消费者在fillRxBatch中直接访问共享ring buffer导致的数据混淆问题 - 修复生产者-消费者间tail指针发布时机不当造成的槽位覆盖竞态 - 修复WIN窗口基址更新位置错误导致电机状态窗口被锁定的问题 - 修复buildInput中断上下文与主循环间的数据竞争和内存撕裂问题 - 添加STATUS帧过滤机制减少批量区压力并解决队列饱和问题 此外还增加了多个诊断计数器用于监控撕裂、溢出和拼接错误,并调整了 输入PDO段布局和大小参数以支持新的48字节STATUS格式。 这些修改解决了上位机观察到的帧标签错误、电机状态丢失和数据拼接问题。
This commit is contained in:
@@ -369,14 +369,13 @@ void EscLan9252::pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
|
||||
* 首 DWORD 有效字节从 (addr&3) 起(表 12-7),末 DWORD 尾部无效(表 12-8)。
|
||||
* 别名口 0x00-0x1C 功能相同,轮换寻址即官方突发用法;每读满一个 DWORD 弹一项。
|
||||
*
|
||||
* 1.6.0 批量搬运(负载优化):旧实现每搬一个 DWORD 都重新轮询一次 CMD 的
|
||||
* AVAIL 位——每次轮询是 setHbiIndex+读低/高半字共 3 次 EXMC 事务,数据本身
|
||||
* 又 3 次,单周期 1024B 过程数据因此跑 ~1600 次 EXMC 访问,是 GD32 侧 CPU
|
||||
* 占用的大头。DS 12.13.6:ECAT_PRAM_RD_CMD bits[12:8] = PRAM_READ_AVAIL_CNT,
|
||||
* 指示"无需再查状态即可继续读 FIFO 的次数"(≤16,每次整 DWORD 读自减)。
|
||||
* 轮询命中时顺手取该计数,一口气读 min(CNT, 8[别名窗口一程], 剩余 DWORD)个,
|
||||
* 轮询开销从"每 DWORD 一次"降到"每批一次"。字节装配/lead 逻辑逐字节不变,
|
||||
* 任意 (addr,len) 组合的搬运结果与旧实现完全一致。 */
|
||||
* 1.6.2 回退逐 DWORD 轮询,弹前必查 AVAIL 位。1.6.0 曾信 CMD 的
|
||||
* AVAIL_CNT(bits[12:8])一次弹一批:实测 12B 短读(1.6.0 输出探针,主站窗口
|
||||
* 请求 WIN@8 靠它送达)首 DWORD 新鲜、后续 DWORD 取到 FIFO 陈旧项,窗口
|
||||
* 基址冻结,槽位 7..26 的电机状态因此永不上报(仅 seq 沿触发的 384B 全量读
|
||||
* 路径能拿到新 WIN)。批量模式 1.6.0 上机对拍本来就是零收益,384/496B 长读
|
||||
* 未见损坏,但邮箱短读/探针这类小传输只验证过"逐 DWORD 查 AVAIL"的旧模式
|
||||
* ——在弄清该计数器对短读的精确语义之前,不要恢复批量弹 DWORD。 */
|
||||
if (len == 0U) {
|
||||
return;
|
||||
}
|
||||
@@ -386,31 +385,18 @@ void EscLan9252::pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
|
||||
uint16_t alias = 0U;
|
||||
while (done < len) {
|
||||
uint32_t n = POLL_MAX;
|
||||
uint32_t cmd;
|
||||
while (((cmd = readReg32(ECAT_PRAM_RD_CMD)) & PRAM_AVAIL) == 0U && --n != 0U) {}
|
||||
while ((readReg32(ECAT_PRAM_RD_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
|
||||
if (n == 0U) {
|
||||
break; /* 芯片异常:避免死等,交出残缺数据 */
|
||||
}
|
||||
uint32_t batch = (cmd >> 8) & 0x1FU;
|
||||
if (batch == 0U || batch > 8U) {
|
||||
batch = 1U; /* 计数字段异常时退回单拍,等价旧实现 */
|
||||
}
|
||||
/* 本批以"吃完全部剩余字节"为上限(lead 只在首 DWORD 出现) */
|
||||
const uint32_t lead0 = (done == 0U) ? (uint32_t)(addr & 3U) : 0U;
|
||||
const uint32_t needDW = ((uint32_t)(len - done) + lead0 + 3U) / 4U;
|
||||
if (batch > needDW) {
|
||||
batch = needDW;
|
||||
}
|
||||
while (batch-- != 0U) {
|
||||
const uint32_t dw = readReg32((uint16_t)(ECAT_PRAM_RD_DATA + alias));
|
||||
alias = (uint16_t)((alias + 4U) & 0x1FU);
|
||||
const uint8_t lead = (done == 0U) ? (uint8_t)(addr & 3U) : 0U;
|
||||
const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
|
||||
for (uint8_t i = 0U; i < take; ++i) {
|
||||
buf[done + i] = (uint8_t)(dw >> (8U * (lead + i)));
|
||||
}
|
||||
done = (uint16_t)(done + take);
|
||||
const uint32_t dw = readReg32((uint16_t)(ECAT_PRAM_RD_DATA + alias));
|
||||
alias = (uint16_t)((alias + 4U) & 0x1FU);
|
||||
const uint8_t lead = (done == 0U) ? (uint8_t)(addr & 3U) : 0U;
|
||||
const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
|
||||
for (uint8_t i = 0U; i < take; ++i) {
|
||||
buf[done + i] = (uint8_t)(dw >> (8U * (lead + i)));
|
||||
}
|
||||
done = (uint16_t)(done + take);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,9 +404,8 @@ void EscLan9252::pramWrite(uint16_t addr, const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
/* DS 12.11.4:写 ADDR_LEN 再置 BUSY 启动;首 DWORD 有效字节从 (addr&3) 起,
|
||||
* 引擎只取有效字节;别名口 0x20-0x3C 轮换;写满一个 DWORD 推一项。
|
||||
* 1.6.0 批量搬运:与 pramRead 同理,DS 12.13.7 bits[12:8] =
|
||||
* PRAM_WRITE_AVAIL_CNT 指示"无需再查状态即可继续写 FIFO 的次数",
|
||||
* 轮询从"每 DWORD 一次"降到"每批一次",单周期 EXMC 事务约减半。 */
|
||||
* 1.6.2 与 pramRead 同步回退逐 DWORD 轮询(推前必查 AVAIL 位):1.6.0 批量
|
||||
* 模式实测零收益,且与短读损坏同源(见 pramRead 注),邮箱小 PDU 写同类风险。 */
|
||||
if (len == 0U) {
|
||||
return;
|
||||
}
|
||||
@@ -430,31 +415,19 @@ void EscLan9252::pramWrite(uint16_t addr, const uint8_t *buf, uint16_t len)
|
||||
uint16_t alias = 0U;
|
||||
while (done < len) {
|
||||
uint32_t n = POLL_MAX;
|
||||
uint32_t cmd;
|
||||
while (((cmd = readReg32(ECAT_PRAM_WR_CMD)) & PRAM_AVAIL) == 0U && --n != 0U) {}
|
||||
while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
|
||||
if (n == 0U) {
|
||||
break; /* 芯片异常:放弃剩余字节 */
|
||||
}
|
||||
uint32_t batch = (cmd >> 8) & 0x1FU;
|
||||
if (batch == 0U || batch > 8U) {
|
||||
batch = 1U; /* 计数字段异常时退回单拍,等价旧实现 */
|
||||
}
|
||||
const uint32_t lead0 = (uint32_t)((uint16_t)(addr + done) & 3U);
|
||||
const uint32_t needDW = ((uint32_t)(len - done) + lead0 + 3U) / 4U;
|
||||
if (batch > needDW) {
|
||||
batch = needDW;
|
||||
}
|
||||
while (batch-- != 0U) {
|
||||
const uint8_t lead = (done == 0U) ? (uint8_t)((uint16_t)(addr + done) & 3U) : 0U;
|
||||
const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
|
||||
uint32_t dw = 0U;
|
||||
for (uint8_t i = 0U; i < take; ++i) {
|
||||
dw |= (uint32_t)buf[done + i] << (8U * (lead + i));
|
||||
}
|
||||
writeReg32((uint16_t)(ECAT_PRAM_WR_DATA + alias), dw);
|
||||
alias = (uint16_t)((alias + 4U) & 0x1FU);
|
||||
done = (uint16_t)(done + take);
|
||||
const uint8_t lead = (done == 0U) ? (uint8_t)((uint16_t)(addr + done) & 3U) : 0U;
|
||||
const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
|
||||
uint32_t dw = 0U;
|
||||
for (uint8_t i = 0U; i < take; ++i) {
|
||||
dw |= (uint32_t)buf[done + i] << (8U * (lead + i));
|
||||
}
|
||||
writeReg32((uint16_t)(ECAT_PRAM_WR_DATA + alias), dw);
|
||||
alias = (uint16_t)((alias + 4U) & 0x1FU);
|
||||
done = (uint16_t)(done + take);
|
||||
}
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_BUSY) != 0U && --n != 0U) {}
|
||||
|
||||
Reference in New Issue
Block a user