feat!: 1.6.0 DWT 剖分探针 + 1.6.1 输入 PDO 瘦身 640→496(待重烧验收)

1.6.0 剖分探针(上一轮真机已验证, 随本轮一并入库):
- ecatappl.c: 0x2000 sub5..12 DWT 周期累加器(PDI_Isr/输入映射/640B 写细分)
- ethercat_pdo.cpp: 输出按需全量读(12B 探针判 seq 沿, 省每周期 ~190µs 全量读)
- 段5 诊断区(负载率+seq)随 PDO 上报, 主站 /api/app/load 轮询

1.6.1 输入 PDO 瘦身 640→496(62×64bit; 0..359 与 1.5.61 逐字节兼容):
- 依据: DWT 实测 640B 写恒 296µs/拍且与 SYNC0 相位无关(主站 dc_bench 五形态
  对拍极差 0.04%), PRAM ≈0.463µs/B 线性; 真机 6086 帧 DLC 仅 24/32
- 批量接收条目 8B 头+64B 数据 → 8B+32B(DLC 真值保留, 截断打 flags bit1,
  环形队列仍存全量 64B); 段5 诊断区 632→484
- 预期写侧 −67µs/拍(→~230µs, 占空 29.7%→23%); LRW 帧 1024→880B
- 五处一致: ethercat_pdo_size.h(真源) / dc_app.c(62 条目+映射初始化器) /
  gen_eeprom.py(PD_IN_SIZE, nEntry 自动推导) / DCU_ESI.xml(SM3 496+62 条目) /
  bridge.h(kEcatRxBatchDataMax=32, 断言更新)
- 主站 tools/ecat decode_input 按 Ibytes 双布局自适应, 固件/EEPROM 与主站可
  任意先后升级; 混烧(只烧一侧)会被 SM 长度校验拦下(AL 0x001A)
- AC6 编译 0 警告; 真机验收待: 烧 axf + ecat.bat flash + 断电冷启动
  (流程与复验判据见 tools/ecat/MIGRATION.md 1.6.1 节)
This commit is contained in:
cbb
2026-09-06 00:19:18 +08:00
parent 70daa4b0c6
commit 9f1c2d25dd
11 changed files with 315 additions and 185 deletions
+56 -19
View File
@@ -367,7 +367,16 @@ void EscLan9252::pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
{
/* DS 12.11.3:写 ADDR_LEN 再置 BUSY 启动;引擎按 DWORD 向下对齐填充读 FIFO,
* 首 DWORD 有效字节从 (addr&3) 起(表 12-7),末 DWORD 尾部无效(表 12-8)。
* 别名口 0x00-0x1C 功能相同,轮换寻址即官方突发用法;每读满一个 DWORD 弹一项。 */
* 别名口 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) 组合的搬运结果与旧实现完全一致。 */
if (len == 0U) {
return;
}
@@ -377,25 +386,41 @@ void EscLan9252::pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
uint16_t alias = 0U;
while (done < len) {
uint32_t n = POLL_MAX;
while ((readReg32(ECAT_PRAM_RD_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
uint32_t cmd;
while (((cmd = readReg32(ECAT_PRAM_RD_CMD)) & PRAM_AVAIL) == 0U && --n != 0U) {}
if (n == 0U) {
break; /* 芯片异常:避免死等,交出残缺数据 */
}
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)));
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);
}
done = (uint16_t)(done + take);
}
}
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 推一项。 */
* 引擎只取有效字节;别名口 0x20-0x3C 轮换;写满一个 DWORD 推一项。
* 1.6.0 批量搬运:与 pramRead 同理,DS 12.13.7 bits[12:8] =
* PRAM_WRITE_AVAIL_CNT 指示"无需再查状态即可继续写 FIFO 的次数",
* 轮询从"每 DWORD 一次"降到"每批一次",单周期 EXMC 事务约减半。 */
if (len == 0U) {
return;
}
@@ -405,19 +430,31 @@ 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;
while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
uint32_t cmd;
while (((cmd = readReg32(ECAT_PRAM_WR_CMD)) & PRAM_AVAIL) == 0U && --n != 0U) {}
if (n == 0U) {
break; /* 芯片异常:放弃剩余字节 */
}
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));
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);
}
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) {}