Files
DCU_APP/docs/compose/spec/lan9252-perf-audit.md
cbb 6afaacd03a feat(comm): LAN9252增加csrRead验证分级并实现PDO差分写功能
为提升EtherCAT通信性能,实现以下两项优化:

1. 对EscLan9252::csrRead方法增加verify参数,支持分级验证:
   - verify=true(默认):保持原有的双读比对+重试机制,用于SM/AL/邮箱等
     关键路径防止EXMC毛刺
   - verify=false:单读模式,用于诊断回读/MII busy轮询/init轮询等低风
     险场景,节省一半CSR间接访问时间

2. 实现PDO输入差分写ESC功能:
   - 将输入PDO分为三区:段1桥状态(0-31)、段2电机密排(32-543)、段3+5
     快照诊断(544-599)
   - 采用3-buffer冲刷协议:变化后连续写3拍确保3个缓冲区全部更新
   - 对内容稳定的段跳过写操作,显著减少PRAM字节搬运时间
   - 全段跳过时强制写末2字节完成SM事件应答

同时更新配置版本至1.6.25,添加相关配置选项和文档说明。
2026-09-12 20:10:52 +08:00

151 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
feature: lan9252-perf-audit
status: delivered
updated: 2026-02-14
branch: master
commits: <no-commit — analysis-only on dirty master WIP>
---
# LAN9252 使用效率审计
## Report
**What was built** — LAN9252 使用效率审计 + 后续 P1/P2 实现。结论:**DPRAM 用得满但是设计上的满,不算差;真正的速度瓶颈是 PRAM 字节搬运(约 0.44µs/B)**。1.6.25 落地:(1) 输入 PDO 三区差分写 ESC(段1 桥状态 / 段2 电机密排 / 段3+5 快照诊断,变化后 3-buffer 冲刷 3 拍才允许跳过);(2) csrRead 增加 verify 参数,SM/AL/邮箱保持双读,诊断/MII busy/init 轮询单读。
**Verification** — 本报告为静态代码审计,对照仓库内 DWT 实测注释与布局常量交叉验证;未在本回合重跑上机测量(DIFF-STATS 当前 `CONFIG_ECAT_DIFF_STATS=0`,写侧基线须置 0 重测才可对拍)。
**Journey log**
1. 先验怀疑「内存差」不成立:3-buffer 硬上限把 out+in 钉在 ≤1024B,当前 984B 已是极限附近。
2. 1.6.0 PRAM 批量弹 DWORD 因短读损坏回退——任何再加速不能走回未弄清语义的批量模式。
3. 工作区 master 上有未提交的 1.6.22–1.6.24(稀疏写 + pmDrive),审计不改动这些 WIP。
4. 1.6.25 已实现 P1 差分写(三区 + 3-buffer 冲刷)与 P2 csrRead verify 分级;上机对拍 sub10 待做。
---
## [S1] Problem
用户问题:
1. LAN9252 使用是否有提升效率/速度的优化空间?
2. 现在是不是内存空间使用比较差?
需要基于当前代码(含未提交 1.6.22 稀疏写)给出可执行的结论与优先级,而不是泛泛「还能优化」。
## [S2] Design / 分析结论
### S2.1 内存:不是“差”,是 3-buffer 硬上限下的近满设计
**ESC 片内 DPRAM 4KB 布局(LAN9252)**
| 区域 | 范围 | 占用 |
|------|------|------|
| 邮箱 SM0/SM1 | 0x1000–0x13FF | 2×256B 固定 |
| 过程数据 SM2(out)+SM3(in) | 0x1400–0x1FFF | 3072B 可用 |
| 3-buffer 实占 | (384+600)×3 = **2952B** | 余 **120B** |
推导见 `comm/ethercat_pdo_size.h`:`(out+in)×3 ≤ 3072` ⇒ **out+in ≤ 1024B 是硬上限**。当前 384+600=984B,已用到 96%。
若将来还要更大,只有两条路,都有代价:
- 邮箱压到 `MIN_MBX_SIZE(0x22)` → 上限约 1280B,拖慢 SDO/FoE
- 改 1-buffer → 上限 3072B,失去三缓冲并发保护
**结论:DPRAM 满是约束结果,不是浪费。段 2 密排(1.6.21)已经把空节点从有效载荷里挤掉。**
**MCU 侧 RAM**
- `aPdInputData` + `aPdOutputData` = 1024B(SSC 对象存储)
- `EthercatPdo::s_out/s_in/s_build` = 3×384~600B 级
- 段 2 影子 `s_motShadow` ≈ 512B 量级
- DIFF-STATS 自持 600B 快照(仅开启时)
相对 GD32E508 的 128KB SRAM 可忽略。**MCU 内存不是瓶颈。**
### S2.2 速度:热成本在 PRAM 搬运,不在“存不下”
**已实测的通道速率(仓库内 DWT 注释,0x2000 sub9/sub10)**
| 场景 | 耗时 | 占 1ms 周期 |
|------|------|-------------|
| 输入 640B 整包写(历史) | 296µs | 29.7% |
| 输入 496B 整包写(1.6.1) | 218.7µs | 21.9% |
| 输入 600B 整包写(1.6.15) | ≈265µs(600×0.441) | 26.5% |
| 满载 1024B PRAM(历史估算) | ≈218µs(更早时序) | — |
| 输出 384B 全量读(被探针跳过) | ≈190µs | 已优化 |
通道近似线性:**≈0.44–0.463 µs/B**。写多少、读多少,时间就按字节缩放。
**已落地的减法(正确方向)**
1. **输出按需读**(1.6.0,`ecatappl.c`):每拍先读 12B 头探针;seq 无变化则跳过 384B 全量读,只补读末字应答。
2. **段 2 差分填充**(1.6.21,`ethercat_can_bridge.cpp::fillMotorRound`):节点集合与各 seq 未变时整段复用影子,跳过 MotorState 重读与逐槽拷贝——省的是 MCU 侧填充,不直接省 PRAM 字节。
3. **稀疏写 ESC**(1.6.22,工作区未提交):`CONFIG_ECAT_SPARSE_ESC_WRITE=1` 时,密排 count 有效则只写「段1+段2有效区」与「段3+段5」,跳过空槽区。count=3 时约省 (9−3)×56 ≈ 336B PRAM 写 ≈ 150µs/拍。magic 不对/count 撑满自动回退整包。
4. **输入 PDO 瘦身**(1.6.1,历史):640→496 已证明“减字节=减时间”;1.6.15 为批量窗扩回 600 是容量权衡,不是速度回退。
**未做 / 被否决的加速**
| 尝试 | 结果 | 依据 |
|------|------|------|
| PRAM 批量弹 DWORD(用 AVAIL_CNT) | **已回退**(1.6.2) | 短读首 DWORD 新鲜、后续取陈旧项;窗口冻结 |
| EXMC 慢档时序试速 | 无效 | 当年失败根因是访问模型错误,与时序无关 |
| 压缩总线时序 | **不建议** | BUSLAT 1→4 是为温漂可靠(+3HCLK≈16.7ns/访问) |
### S2.3 仍有空间的优化点(按 ROI 排序)
#### P1 — 输入 PDO 真正的差分写 ESC
- **现状**:填充侧有段 2 影子;**写侧**仍是整包 600B 或稀疏两段,段 1/3/5 每拍全写。
- **前提**:开 `CONFIG_ECAT_DIFF_STATS=1` 采 0x2000 sub13..23(changedMax / runsMax / 各段变化字节),拿分布再定 A1(语义分区条件写)或 A2(块粒度差分,N 由 runsMax 定)。
- **预期**:若稳定工况下每拍变化字节数远小于 600,写侧可再省数十至上百 µs。
- **风险**:run 碎片会把一次大写退化成多次带 AVAIL 轮询的小写;必须设碎片上限,超限回退整段/整包。
- **验收**:`CONFIG_ECAT_DIFF_STATS=0` 对拍 sub10 与 load_perm;布局/SII/主站解码不变。
#### P2 — csrRead 双读校验分级
- **现状**:`EscLan9252::csrRead` 对每次事务读两遍 CSR_DATA 并比较(1.5.59),不一致整事务重跑一次。对 EXMC 毛刺防护有效,但 **CSR 路径访问次数近似翻倍**。
- **热路径调用方**:AL Event(每拍 1–2 次)、邮箱长度/SM 状态、看门狗状态、LED、诊断。
- **可行做法**:提供 `csrReadOnce`(或按寄存器关键级别开关);**几何/状态关键寄存器(SM、AL、邮箱)保持双读**;LED/诊断/低频路径可单读。
- **风险**:去掉防护的路径可能偶发读错;须限定白名单,不可一刀切。
- **预期**:CSR 间接访问(多步 HBI)减少约一半;相对 600B PRAM 写占比更小,属次优先。
#### P3 — 稀疏写收益标定(已实现,待验收)
- 工作区已实现 `sparseEscWritePlan` + `CONFIG_ECAT_SPARSE_ESC_WRITE=1`。
- **验收动作**:同工况开关 0/1 各测一轮 sub10;count 越小收益越大。
- 空洞区为 SM 缓冲上次残留,主站必须只按 count 迭代(1.6.21 已如此)。
#### 不建议再碰
- **EXMC 时序收紧**:BUSLAT=4 / DSET=20 为全温区可靠性的已知代价;已有“慢档无效、访问模型才是当年根因”的记录。除非板级时序重测给出明确裕量,否则不动。
- **恢复 PRAM 批量弹 DWORD**:短读语义未弄清前禁止。
- **改 1-buffer 扩 DPRAM**:失去三缓冲,并发读写会拿到半新半旧过程数据。
### S2.4 访问模型备忘(审计时反复踩过的坑)
```
主站帧
→ LAN9252 DPRAM (4KB)
邮箱 0x1000+ / PDO 0x1400+
← PDI (HBI Indexed)
HBI_IDX_0 / HBI_DATA_0 ← 仅系统 CSR + 访问端口
ECAT_CSR_CMD/DATA 0x304/0x300 ← 核心 CSR 0x0000–0x0FFF
PRAM FIFO 0x000–0x03C + 0x308–0x314 ← 过程 RAM 0x1000–0x1FFF
→ EXMC NE0 @ 0x60000000, 16-bit
```
核心 CSR **不能**直读 HBI 索引(1.5.37 之前全 0 的根因)。过程数据 **必须**走 PRAM FIFO。
## [S3] Out of Scope
- 不重生成 SII、不烧录、不改主站解码。
- 不审计主站侧解码或 CAN 桥竞态(另见 1.6.9–1.6.21 修复记录)。
- 不重新设计 PDO 布局或 EEPROM。
- 上机 A/B 对拍(T3/T6)待现场执行。
## Tasks
- [x] T1: 审计 DPRAM/MCU 内存占用与 PRAM 热路径 — acceptance: 本报告 S2.1/S2.2 有依据与数字 (covers: S2)
- [x] T2: 给出优化优先级与不做清单 — acceptance: S2.3 含 ROI/风险/验收 (covers: S2)
- [x] T4: (1.6.25)实现输入 PDO 差分写 — acceptance: 三区影子+3-buffer 冲刷;全跳过写末 2 字节;SII 不变;CONFIG_ECAT_DIFF_ESC_WRITE 门控 (covers: S2.3)
- [x] T5: (1.6.25)csrRead 分级单读 — acceptance: verify 参数;SM/AL 默认双读;MII busy/init/诊断回读单读 (covers: S2.3)
- [ ] T3: (后续)开 DIFF-STATS + 上机对拍 sub10 — acceptance: 记录写侧耗时 A/B(差分开/关)(covers: S2.3)
- [ ] T6: (遗留)验收工作区稀疏写 A/B — acceptance: CONFIG_ECAT_SPARSE_ESC_WRITE 0/1 同工况 sub10 对比入档 (covers: S2.3)