Files
bowei ea4541ee90 sync: SHANGHAI-U9 2026-07-27 三个 Simulink→RTL skill 转系统级 + 新增 simulink-rtl-redev
- simulink_rtl_dev / rtl-ip-replace: 从工程级 .claude/skills 迁到用户级并去工程耦合
  (模型名/Notes 文档依赖/工程实测基线移出; addpath 与脚本路径改用户级)
- rtl-ip-replace/scripts/gen_cmp_tb.py: Vivado 路径不再写死, 改读 VIVADO_PATH /
  XILINX_VIVADO (兼容 install root / bin / bin\vivado.bat 三种写法), 回落时提示
  去设 skills_env.md 的 VIVADO_PATH
- simulink-rtl-redev: 新增。原生模型改动后的仿真模型同步规范, 含两个实测过的工具
  check_sync.py (三条时序链新鲜度闸门) 与 measure_k.m (时域/频域/包络三估计器互证)
- sim-rtl-flow/SKILL.md: 加入 sync_sim:* 步骤, 引用上述三个 skill
2026-07-27 10:39:53 +08:00

238 lines
12 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.
---
name: simulink-rtl-redev
description: >
改动原生模型(xxx_h.slx)之后,如何保证仿真模型(xxx_hs.slx)同步、仿真结论仍然算数。
典型触发场景:Vivado 综合时序不满足要改流水、硬件调试发现问题要改 RTL、定点位宽调整。
核心:生成脚本不会把原生核的改动翻译成 m 代码,只改一侧 = RTL 已变而仿真还在验证旧行为。
提供两个工具:同步新鲜度闸门 check_sync.py(三条时序链,退出码即结论)、
延迟实测 measure_k.m(时域/频域/包络三个估计器互证,不一致返回 NaN 而非猜测值)。
另含改动分类、验证三步、环内/环外加流水的差别、六条禁令。
触发词:改了原生模型、同步仿真模型、xxx_h 和 xxx_hs 不一致、时序违例改流水、
硬件调试改RTL、仿真模型过时、等价闸门、cycle-true 同步、check_sync、measure_k、
测延迟、K 是多少、加流水延迟多少拍、原生模型 仿真模型 双轨、改完怎么验、
makehdl 之前要做什么。
规范全文见工程 Notes/Task16-3.md。适用于任何"原生模型+仿真模型"双轨结构的工程。
---
# simulink-rtl-redev — 原生模型改动后的仿真模型同步
## 这个 skill 解决什么
两个模型描述同一段算法:
| 记号 | 名称 | 内容 | 用途 |
|---|---|---|---|
| `xxx_h.slx` | 原生模型(-e) | 目标模块全部用可综合原生块搭建 | **生成 RTL** |
| `xxx_hs.slx` | 仿真模型(-x/-q) | 由原生模型派生,跑不动的**原生核**换成等价的 **m 实现** | **跑场景仿真** |
**核心原则:改了原生模型,必须同步改仿真模型,并重新验证两者行为一致。**
原因:从原生模型派生仿真模型的脚本只做"摘掉原生核、换上 m 实现、接线",
**它不会把原生核的改动翻译成 m 代码**。只改原生模型再重跑生成脚本,
仿真模型不会跟着变——它只是把同一份未改动的 m 实现重新包一遍。
后果:**RTL 已经变了,而仿真还在验证旧行为**。板上出问题时仿真给不出线索,
因为它测的根本不是同一个设计。
## 什么时候需要手工同步
只看改动落在哪里:
| 改动位置 | 是否需要手工同步 m 实现 |
|---|---|
| **原生核之内** | **需要**。这是两套实现的重叠区 |
| 原生核之外(其余链路) | 不需要。生成脚本原样继承,重生成即可 |
## Agent 路径:先跑闸门
任何时候怀疑"仿真结论还算不算数",第一件事是跑同步新鲜度闸门。
### 一次性:写 manifest
在工程里建一份 manifest(建议放 `Notes/sync_manifest_<model>.json`),
声明这个模型的源与派生产物。**`root` 相对 manifest 所在目录**,其余路径相对 `root`:
```json
{
"model": "<model>",
"root": "..",
"native_model": "matlab/<proj>/xxx_h.slx",
"build_scripts": ["matlab/<proj>/build_core.m"],
"gen_scripts": ["matlab/<proj>/make_sim.m"],
"mcode_impl": ["matlab/<proj>/core_mcode.m"],
"runtime_deps": ["matlab/<proj>/pscore.m", "matlab/<proj>/coef.mat"],
"sim_model": "matlab/<proj>/xxx_hs.slx",
"rtl_source_model": "matlab/<proj>/xxx_h_<Mod>_E.slx",
"gate_result": "Notes/gate_<model>.txt",
"gate_tools": ["matlab/<proj>/gate.m", "matlab/<proj>/cmp_core.m"]
}
```
`runtime_deps` 必填**运行时才从路径解析的 .m** 与运行时读取的数据文件
(`.mat`、系数表、数据字典)。改它们时模型本身一个字节都不变,漏列等于没查。
`rtl_source_model` 与 `gate_tools` 可省(省了就不查对应项)。
### 每次改完模型:跑闸门
```
cd <工程根>
python "%USERPROFILE%\.claude\skills\simulink-rtl-redev\check_sync.py" Notes\sync_manifest_<model>.json
```
Linux/macOS 把路径换成 `~/.claude/skills/simulink-rtl-redev/check_sync.py`。
**退出码即结论**:`0` 通过,`1` 有过时产物,`2` manifest 有问题(路径不存在等)。
可直接串进脚本做前置门禁。
选项:`--quiet` 不打印文件清单,`--json` 输出机器可读结果。
它查三条时序链:
```
链 1(派生新鲜):max(源文件) < min(派生产物)
链 2(闸门新鲜):max(派生产物) < 闸门结果
链 3(工具新鲜):max(闸门工具) < 闸门结果
```
**链 1 是最容易漏的那条。** 只查"闸门晚于全部源文件"会放行下面这种情况:
闸门确实在改完源之后跑的,但仿真模型停留在改动之前。本工具在真实工程上正是这样
抓出了一个已存在的漂移——链 2、链 3 都 PASS,只有链 1 FAIL。
## 同步流程
```
[1] 改原生实现(改构建脚本,不直接手改 .slx)
|
[2] 把同样的改动落到 m 实现 <-- 漏掉这步是本 skill 存在的理由
|
[3] 重新生成仿真模型
|
[4] 若改动带来延迟变化:先实测新增拍数 K
|
[5] 跑等价闸门 --> 原生核 与 m 实现 是否一致
|
[6] 跑短程冒烟 --> 链路是否还通
|
[7] 跑全场景全长 --> 对照基线,指标是否达标
|
[8] 从原生模型生成 RTL
```
任一步不过,回到 `[1]` 重走,不要从中间插入——仿真模型一旦重生成,后面的结果全部作废。
**`[4]` 必须在 `[5]` 之前**:延迟变了,闸门比对要带偏移,K 没测出来这一步做不了。
跑完 `[3]` 之后、进 `[5]` 之前,先用 `check_sync.py` 确认新鲜度,能省掉一次白跑的闸门。
## 三类改动的差别
| 类别 | 例 | m 实现要改吗 | 延迟 | 场景判据 |
|---|---|---|---|---|
| **不改延迟** | 资源共享、块重构、常量折叠 | 通常不用 | 不变 | 与基线**逐位相同** |
| **改延迟** | 插流水寄存器 | 补等量延迟 | +K | 吸收 K 后与基线**逐位相同** |
| **改定点** | 位宽、二进制点、饱和/舍入 | **必须改** | 不变 | 行为已变,**重新定性并建立新基线** |
- **延迟必须实测,不能按设计意图推算。** 用 `measure_k.m`(下节),至少两种互不依赖的方法测出一致值。
- **在反馈环内插流水,代价远大于环外。** 环外只是一个可被注入侧参数吸收的常数延迟;
环内会改变环路群延迟,必须重新定标环路参数(步长、移位、门限),并先在单元台验证
阶跃响应再上全链。**能搬到环外就搬到环外。**
## 实测新增延迟 K:`measure_k.m`
流程 `[4]`。B 类(加流水)改动必须先测 K 才能进 `[5]`——闸门的判据本身要用 K 作偏移。
```matlab
addpath(fullfile(getenv('USERPROFILE'),'.claude','skills','simulink-rtl-redev'));
[K, R] = measure_k(ref, dut); % ref/dut 为实数或复数 IQ 向量,同采样率
```
`ref` 是改动前(或参考抽头)的信号,`dut` 是改动后(滞后 K 拍)的信号。
返回 `K` = 认定的整数延迟,**方法不一致时返回 `NaN`**,不给猜测值。
`R` 里有各方法的分项结果。可选参数:`'MaxLag'`(4096)、`'OnsetTol'`(1e-3)、
`'FracTol'`(0.05)、`'Verbose'`(true)。**只用基础 MATLAB**,不依赖 Signal Processing Toolbox。
三个估计器,前两个域完全不同、始终可用,第三个仅在信号有静默前导时适用:
| 方法 | 域 | 说明 |
|---|---|---|
| 1 互相关 argmax | 时域 | 整数 |
| 2 互谱相位斜率 | **频域** | 分数;纯延迟使 `angle(Y·conj(X))` 对频率成直线,斜率即 −K |
| 3 起始点差 | 幅度包络 | 整数;阈值取各自**峰值**的比例,故 DUT 的噪声底不会误触发;连续激励信号判 `n/a` 而非失败 |
判据:方法 1 与 2 必须一致;方法 3 适用时也必须一致;插值峰距整数不超过 `FracTol`。
**为什么非要两种。** 实测:两路**完全无关**的信号,相关器照样给出 `K = 241`
(|rho| 仅 0.0198 的假峰)——单靠它会把一个纯噪声峰当成延迟。方法 2 给出 −26.350,
两者互不相认,工具才判 FAIL。这就是"至少两种互不依赖的方法"的全部理由。
实测过的六种情形:
| 情形 | 结果 |
|---|---|
| 干净整数延迟 40,复数 IQ | PASS,3 法一致 |
| 增益 0.37× + 全程噪声底 | PASS,2 法一致(方法 3 自动判 n/a) |
| 连续激励无静默段,延迟 17 | PASS,2 法一致 |
| **分数延迟 12.5 拍** | **FAIL**:方法 1 给 13、方法 2 给 12.500,且插值峰偏离整数 0.5 |
| **两路无关信号** | **FAIL**:241 vs −26.350 |
| 真延迟 40 + 3 阶非线性失真 | PASS,2 法一致(\|rho\| 0.989) |
分数延迟那一条尤其要留意:它说明改动**没有引入干净的整数流水延迟**(碰到了分数内插/重采样
路径)。这时不能四舍五入接着走,要回去查改的是不是环内。
## 等价闸门的语义(极易搞错)
闸门比对的是**原生核与 m 实现这两套实现**,不是"新行为与旧行为"。因此:
- 闸门对**所有类别**的改动都必须跑,判据都是"一致";
- 即使行为相对上一版变了,只要两侧按同样意图改完,**两套实现对新行为的描述仍必须一致**;
- 改完两侧仍不一致,说明两边理解有分歧——这正是闸门要抓的。
**不要以"行为变了"为由跳过闸门。** 跳过它就等于放弃了对双实现是否同步的唯一检查。
闸门的局限:它只证明"在所用激励下一致",不证明全状态空间等价。
状态机边界、饱和、复位时序这类角落必须补定向用例,不能指望随机激励覆盖到。
## 六条禁令
| # | 规矩 | 理由 |
|---|---|---|
| 1 | 仿真模型是**产物**,不能手改;要改就改生成脚本再重生成 | 手改必被下次重生成冲掉,git 上无法溯源 |
| 2 | 只改一侧的改动**视为未完成**,不得进入验证 | 两套实现已分叉 |
| 3 | 没跑过闸门,**不得跑场景仿真,也不得生成 RTL** | 前者测的可能是旧设计,后者错误直接进硅 |
| 4 | 旧结果**永不覆盖**,每次运行带唯一标签、落唯一目录 | 失败记录是"改动前后对比"的另一半;陈旧编译缓存会静默复现旧结果 |
| 5 | 判据不得为了通过而修改 | 一条带机理分析的 FAIL,价值高于一个来路不明的 PASS |
| 6 | 时序违例优先改模型,**禁止**用多周期豁免放宽慢角富余路径 | 布线器会吃满被放松的预算,表现为"报告通过、板上异常" |
## Gotchas
- **时间戳只是权宜之计。** `copy`/`touch`/版本签出都会扰动 mtime。
`check_sync.py` 因此只能作自查,不是证明。更可靠的做法是让生成脚本与闸门自动记录
所用源码的校验和,运行前比对不符即拒绝开跑——那属于工具建设,本 skill 不含。
- **"闸门比全部源文件新"不等于没问题。** 这正是链 1 存在的理由,见上文。
- **`runtime_deps` 漏列 = 没查。** 运行时解析的 .m 和 `.mat`/系数表改动时,
模型校验和纹丝不动。这类依赖必须显式声明,工具无法自动发现
(`load('x.mat')` 这种字符串依赖追不到)。
- **manifest 里每个声明的路径都必须存在**,否则 exit 2。这是故意的:
路径写错而静默跳过,比报错危险得多。
- **exit 2 与 exit 1 含义不同**:2 是 manifest 坏了(你的配置问题),
1 是真的有过时产物(你的工程问题)。串脚本时别把两者混为一谈。
## Troubleshooting
| 症状 | 原因 / 处置 |
|---|---|
| `ERROR: manifest refers to something that is missing -> <key>: <path>` | 该路径不存在。exit 2。检查 `root` 与相对路径;`root` 是相对 manifest 所在目录的 |
| 链 1 FAIL | 派生产物早于源。**先**把原生核改动落到 m 实现,**再**重生成,**再**重跑闸门。只重生成不同步 m 实现无效 |
| 链 2 FAIL | 闸门跑在当前派生产物之前 —— 重跑闸门 |
| 链 3 FAIL | 闸门工具在记录的通过之后改过 —— 重跑闸门 |
| 三条链全 PASS 但板上仍与仿真不符 | 新鲜度没问题,问题在别处:查闸门覆盖(定向用例是否覆盖到出问题的状态),或查 `runtime_deps` 是否漏列 |
| `ERROR: manifest declares no sources / no derived products` | manifest 缺 `native_model` 一类的键,或值是空列表 |
## 规范全文
本 skill 是 `Notes/Task16-3.md` 的可执行化。规范原文(含制定过程与十轮评审纠正的
逻辑错误)见该文档;本 skill 只承载执行路径与工具。