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
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
---
|
||||
name: rtl-ip-replace
|
||||
description: >
|
||||
把 MATLAB/Simulink HDL Coder 生成的 RTL 二次加工:查找表(ROM)→ Vivado dist_mem_gen/blk_mem_gen IP、
|
||||
RAM(SimpleDualPortRAM_generic 等)→ blk_mem_gen IP、CORDIC → Xilinx CORDIC IP、FIFO(reFIFO 等)→
|
||||
fifo_generator IP、滤波器(**仅固定抽头**的 FIR/抽取/内插;LMS 自适应、Farrow、运行时参考抽头
|
||||
的相关器不在替换范畴)→ fir_compiler / cic_compiler IP;改写后的 RTL 只存放在
|
||||
固定目录 vivado/rtl_ip/<model>/(含 PROVENANCE.log 溯源与 ip/ 子目录),每次重跑先整体备份到
|
||||
backup_<时间戳>/(含上次原始 RTL src_orig/ 与上次改写后 RTL,作为增量基线);与原始生成 RTL 做
|
||||
cycle-true 双 DUT 对比仿真,PASS 后才作为 vivado 综合输入,vivado 工程绝不允许直接引用 matlab hdlsrc。
|
||||
触发词:IP替换、ROM IP、RAM IP、CORDIC IP、FIFO IP、滤波器 IP、FIR IP、查找表替换、rtl_ip、
|
||||
cycle-true 对比、二次修改rtl、makehdl 后处理、增量替换。
|
||||
每次 regen_all/makehdl 重新生成 RTL 后都要重跑本流程(默认走增量)。适用于任何 HDL Coder→Vivado 工程。
|
||||
---
|
||||
|
||||
# rtl-ip-replace — HDL Coder RTL 的 Vivado IP 化 + cycle-true 验证
|
||||
|
||||
> 系统级 skill,适用于任何 HDL Coder→Vivado 工程。
|
||||
> 文中的模型名、模块名一律是**占位示例**;各工程的需求规格、实测清单与"暂不实施"名单,
|
||||
> 记在该工程自己的任务书与 Notes 下,不写进本 skill。
|
||||
|
||||
## 〇、安装位置与环境依赖
|
||||
|
||||
脚本位于 `~/.claude/skills/rtl-ip-replace/scripts/`
|
||||
(Windows:`%USERPROFILE%\.claude\skills\rtl-ip-replace\scripts\`)。下文的 `scripts/` 均指此处。
|
||||
|
||||
**Vivado 路径来自环境变量,不写死**:`gen_cmp_tb.py` 生成 `run_cmp.bat` 时按
|
||||
`VIVADO_PATH` → `XILINX_VIVADO` 的顺序取安装根,三种写法都接受——
|
||||
安装根、其 `bin\`、或 `bin\vivado.bat`。都没设时回落到内置默认值并打印 WARNING。
|
||||
换机器/换版本只需改环境变量。
|
||||
|
||||
**实施对象(`<model>`)不绑定任何默认值,必须由用户在每次调用时明确指定**
|
||||
(如 "在 `<model>` 上实施");未指定时先问用户,不得自行猜测。
|
||||
"查找表"指**固定内容**的表(sin、cos、log、atan 等常量表);
|
||||
运行时写入的数据表不属于替换范围。
|
||||
|
||||
**滤波器替换范围(用户指令 2026-07-24)**:**只有固定抽头(常量系数)的滤波器才适用
|
||||
fir_compiler/cic_compiler 替换**。以下一律**不替换**:
|
||||
- Farrow/多项式内插器(系数是 mu 等运行时变量的多项式,如 TxPreOffset);
|
||||
- LMS 等自适应滤波器(逐样本更新权值,如 EqCore);
|
||||
- 参考抽头为运行时捕获序列的相关器(如 RxAlignCore 的 refdl 相关)。
|
||||
扫描器只把**能识别到的**这类结构报为 `adaptive_fir_candidate`/`fir_block_candidate`(仅供盘点,
|
||||
不进替换清单):抽头寄存器组 + 延迟线 + 乘法(EqCore 的 LMS 就是这样被认出来的);
|
||||
Farrow(TxPreOffset)与相关器(RxAlignCore)没有抽头寄存器组,扫描器不一定报得出来——
|
||||
**它们由本规则排除,不依赖扫描器识别**。`fir_to_ip.py` 只处理 `fir_coef_bank`(常量系数),
|
||||
自适应类一律只打印 INVENTORY ONLY,不生成 IP。
|
||||
**"暂不实施"名单**:工程可以把某些模型/子系统登记为暂不替换(典型:激励源、上游 OFDM 源),
|
||||
名单写在该工程的任务书里,**待用户明示后才可对其运行本 skill**。本 skill 不内置任何名单。
|
||||
|
||||
**双向调整原则(用户指令 2026-07-23)**:替换不必单向迁就原 RTL——**允许修改 MATLAB/Simulink
|
||||
源设计来适配 Vivado IP 核**(位宽、量化、接口节奏、算法结构),只要**仿真性能不受影响**
|
||||
(收敛行为、MER 等系统指标不劣化,须用模型仿真复验)。位级逐拍等价做不到时
|
||||
(典型:CORDIC 的内部舍入),按此原则改源头或按消费点契约放宽,并在 Task12/Result
|
||||
文档显式记录差异及性能复验结论。
|
||||
|
||||
## ★ 强制目录规定(用户指令 2026-07-23,严格执行)
|
||||
|
||||
1. **固定目录**:脚本处理后的 RTL 一律存放于 `vivado/rtl_ip/<model>/`。
|
||||
**Vivado 工程只允许使用该目录下的 RTL,绝对不允许直接引用 `matlab/**/hdlsrc/**` 的自动生成代码**
|
||||
(对比仿真编译原始代码除外——那只进 xsim,不进综合)。
|
||||
2. **溯源 log**:固定目录下必须有 `PROVENANCE.log`,写明本次修改所依据的原始 MATLAB RTL 的**绝对路径**、
|
||||
每个源文件的 sha1/mtime(由 `ipflow_common.py` 自动生成维护)。
|
||||
3. **IP 子目录**:所有生成的 Xilinx IP(.xci 及产物)放在固定目录的 `ip/` 子目录。
|
||||
4. **备份先行**:每次重跑本 skill,第一步必须把固定目录现有全部内容移入 `backup_<YYYYMMDD_HHMMSS>/`
|
||||
子目录,然后全新生成(`backup_outdir.py` 完成,backup_* 目录本身不重复嵌套)。
|
||||
5. 接入综合前 lint:`.xpr` 中不得再有指向 hdlsrc 的**综合**源(检查:grep hdlsrc *.xpr)。
|
||||
|
||||
## 适用范围(HDL Coder 25.2 生成代码的习惯用法,实测归纳)
|
||||
|
||||
| 结构 | 生成代码特征 | 目标 IP | cycle-true 约束 |
|
||||
|---|---|---|---|
|
||||
| 常量查找表 (ROM) | `wire signed [W-1:0] <tbl> [0:D-1];` + D 条 `assign <tbl>[i] = W'sb...;` + 组合读 `<tbl>[expr]` | `dist_mem_gen`(默认,异步读,天然 cycle-true) | 零改动等价 |
|
||||
| **isempty-init 表** ★ | 常量表 `P` + `T_1[i]=P[i]` 拷贝 generate + `T_reg_reg` 寄存器阵列(复位清 0,首个 `enb&&s` 拍整表载入常量,此后保持)+ `TMP = s ? T_1 : T` 组合 mux。**这是 13 万 FF 毒源的本体**(sinT/cosT 4096×16) | `dist_mem_gen` + 1 个 `__ipinited` 标志 FF | 读点改写:`T[e] → inited ? ROM[e] : 0`;`TMP[e] → (s\|\|inited) ? ROM[e] : 0`,逐拍等价(复现"载入前读 0") |
|
||||
| RAM | `SimpleDualPortRAM_generic`(单时钟 SDP,enb 使能,**读旧值 read-first**,读延迟 1 拍) | `blk_mem_gen` SDP,写口 **Read-First**,无输出寄存器 | 碰撞语义必须 Read-First **且 `CONFIG.Assume_Synchronous_Clk true`(C_COMMON_CLK=1)——异步双钟假设下模型不保证碰撞读旧值,实测 dlock 提交后 tx_al 发散 17640 拍** |
|
||||
| CORDIC | 内联 FSM:`cx/cy/cz` 迭代寄存器 + `atanT` 表,多拍出一次结果 | `cordic`(arctan/rotate) | **无法逐拍等价**;契约=消费点等价:IP 延迟 ≤ 原 FSM 出结果拍数,IP 早出的结果寄存保持到原提交拍再输出 |
|
||||
| FIFO | `reFIFO` 等:reg 数组 + wr/rd 指针 | `fifo_generator` | 读延迟(standard=1拍/FWFT=0拍)与 empty/full 翻转拍必须逐拍一致;TB 同时比数据和 flag |
|
||||
| 滤波器(**仅固定抽头**) | **常量**抽头数组 + delay line + 乘加链(成型/内插 FIR 等) | 定系数→`fir_compiler`;大抽取/内插比→`cic_compiler`。**抽头非常量(LMS/Farrow/运行时参考)不在替换范畴** | 系数整数值逐个相同、Full Precision 输出 + 外部按原 RTL 方式截位、延迟差用固定移位寄存器补平后**逐拍 0 失配**;`tvalid` 与原输出有效拍一致 |
|
||||
|
||||
注意:`reg` 数组小表(w_out/refdl/t0 等 5~18 深)是真正的运行时数据,**保留原状**,不替换。
|
||||
|
||||
## 流程(0~5 步,顺序执行)
|
||||
|
||||
设 `HDLSRC` = 该工程 HDL Coder 的输出目录(形如 `matlab/<工程>/hdlsrc/<model>`),
|
||||
`OUT = vivado/rtl_ip/<model>`(固定目录,见上节规定),脚本在 `~/.claude/skills/rtl-ip-replace/scripts/`。
|
||||
|
||||
### 0. 备份(强制第一步)
|
||||
```
|
||||
python scripts/backup_outdir.py <OUT>
|
||||
```
|
||||
备份后的 `backup_<ts>/` 同时含**上次的原始 MATLAB RTL**(`src_orig/`)与**上次改写后的 RTL**,
|
||||
即下一步增量判定的基线。
|
||||
|
||||
### 0.5 增量判定(Task12 规定 13~16;要全量复核时加 `--full`)
|
||||
```
|
||||
python scripts/incr_plan.py plan <HDLSRC> <OUT> --apply
|
||||
```
|
||||
- 把新 hdlsrc 与基线 `src_orig/` 逐文件比 sha1:`same` 的文件**直接取回上次改写后的 .v**;
|
||||
- 打印 `ALL MATLAB sources unchanged` 时,**第 2 步(transform)整步可跳过**;
|
||||
- 同时把本次源码快照写进 `<OUT>/src_orig/`(下次的基线),计划落 `<OUT>/incr_plan.json`。
|
||||
|
||||
### 1. 扫描盘点
|
||||
```
|
||||
python scripts/rtl_ip_scan.py <HDLSRC> <OUT>
|
||||
```
|
||||
manifest 落在 `<OUT>/manifest.json`。人工 review:const_rom 里"唯一读点"若其实是
|
||||
`T_1[gv]=P[gv]` 的 generate 拷贝,该表属于 isempty-init 惯用法,**必须走 initrom_to_ip.py,
|
||||
不得用 rom_to_ip.py 直接改**(会把 ROM 塞进 genvar 循环,语义错误)。
|
||||
|
||||
### 2. 生成替换 RTL(另存到 <OUT>,不动原始文件)
|
||||
```
|
||||
python scripts/initrom_to_ip.py <HDLSRC> <OUT> # isempty-init 表(sinT/cosT/atanT)
|
||||
python scripts/rom_to_ip.py <HDLSRC> <OUT> # 剩余纯常量 ROM
|
||||
python scripts/ram_to_ip.py <HDLSRC> <OUT> # SimpleDualPortRAM → blk_mem_gen
|
||||
python scripts/fir_to_ip.py <HDLSRC> <OUT> [--rate N ...] # 定系数 FIR → .coe + fir_compiler 登记
|
||||
# + FIR_PROPOSAL_*.md(不改 RTL)
|
||||
#(CORDIC/FIFO/滤波器手术引导式手改 <OUT> 里的副本,见下方契约;所需 IP 手工追加进 ip_manifest)
|
||||
python scripts/namespace_ip.py <OUT> # 最后统一加 _IP 后缀(幂等)
|
||||
```
|
||||
产物:改写后 `*.v`、去重 `*.coe`、`ip_manifest.json/.tcl`、`PROVENANCE.log`、`src_orig/`(自动)。
|
||||
|
||||
### 2.5 增量结算(决定第 3、4 步能不能省)
|
||||
```
|
||||
python scripts/incr_plan.py finalize <HDLSRC> <OUT>
|
||||
```
|
||||
生成 `TRANSFORM.diff`(src_orig → 改写后的 removed/added 行,行号已归一化)并与基线比对,输出两个决策:
|
||||
- `ip_reuse: YES` — `ip_manifest.tcl` + 全部 `.coe` 与基线逐字节相同,基线 `ip/` 已复制回来,**跳过第 3 步**;
|
||||
- `sim_inherit: YES` — 还满足 `TRANSFORM.diff` 与基线相同、scripts_sha1 未变、文件集未增删、
|
||||
基线 `cmp_result.txt` 为 PASS ⇒ **跳过第 4 步**,PASS 继承并写入 `PROVENANCE.log`。
|
||||
任一条不满足即打印 blocker 并要求实跑。**接入综合出板前的最后一次必须 `--full` 全量复核。**
|
||||
|
||||
### 3. 生成 Vivado IP(固定目录的 ip/ 子目录) — `ip_reuse: YES` 时跳过
|
||||
```
|
||||
vivado -mode batch -source scripts/gen_ip.tcl -tclargs <OUT>/ip_manifest.tcl <OUT>/ip
|
||||
```
|
||||
|
||||
### 4. cycle-true 对比仿真(闸门,不 PASS 不准上综合) — `sim_inherit: YES` 时跳过
|
||||
```
|
||||
python scripts/gen_cmp_tb.py <top_module> <HDLSRC> --rtl-ip <OUT>
|
||||
<OUT>/cmp/run_cmp.bat
|
||||
```
|
||||
- 双 DUT:原始 `<top>`(从 HDLSRC 编译,仅用于仿真)vs `<top>_IP`(从 <OUT> 编译);
|
||||
复位释放后逐拍比较全部输出;CORDIC 输出用 `--compare-at OUT=done信号` 限定提交拍。
|
||||
- 激励:默认 LFSR;控制密集顶层优先复用 HDL Coder `<top>_tb.v` 向量。
|
||||
- xelab 库:`-L dist_mem_gen_v8_0 -L blk_mem_gen_v8_4 -L fifo_generator_v13_2 -L cordic_v6_0_14
|
||||
-L fir_compiler_v7_2_11 -L cic_compiler_v4_0_13 -L xbip_utils_v3_0_9 -L unisims_ver`
|
||||
+ IP 仿真网表(`--ip-simdir <OUT>/ip`)。
|
||||
- 判据:`cmp_result.txt` 首行 **PASS(0 失配)**;FAIL 时禁止接入综合。
|
||||
|
||||
### 5. 接入综合
|
||||
- 工程源:`<OUT>/*.v` + `<OUT>/ip/**/*.xci`,顶层实例化 `<top>_IP`;
|
||||
**移除工程中所有 hdlsrc 综合源引用**(规定 §1/§5)。
|
||||
- **共享模块名检查(实测踩坑)**:移除被转换模型的 hdlsrc 源前,grep 其余未转换模型是否
|
||||
实例化**同名**模块(HDL Coder 对多个模型会生成同名的通用模块,如 `SimpleDualPortRAM_generic`,
|
||||
工程里可能一直借用另一个模型目录下的那一份)——是则把**该模型自己目录下的同名 .v**
|
||||
加进工程,否则综合报 `module not found`。
|
||||
- isempty-init 表 IP 化后,`vivado/constrs/mc_paths.tcl` 的 ROM 多周期豁免段(`MC rom=`)删除。
|
||||
- 重新综合前全清 `.runs/.cache`(残留 `__synthesis_is_running__` 标记会死锁调度)。
|
||||
|
||||
## CORDIC 替换契约(引导式)
|
||||
|
||||
1. 圈出迭代锥:`cx/cy/cz` 寄存器、`atanT`(isempty-init 表)、迭代计数、结果提交点
|
||||
(本项目:相关峰种子 candR/candI → phi 种子,1 iter/4 clk)。
|
||||
2. 包成边界模块 `cordic_kernel_IP(clk, en, start, x_in, y_in, done, phase_out)`,内部用
|
||||
`cordic` IP(Translate/arctan,流水延迟 L ≤ 原 FSM 提交拍数)。
|
||||
3. `done`/`phase_out` 按原 FSM 提交拍对齐(早出结果寄存保持)→ 外部逐拍等价。
|
||||
4. TB 在提交拍断言相等;量化差须靠 IP 位宽/舍入配置调平至 0 或显式记录 ≤1 LSB。
|
||||
|
||||
## FIFO 替换契约(引导式)
|
||||
|
||||
1. 原模块 `rd_en` 后 1 拍出数 = **Standard**;当拍出数 = **FWFT**。
|
||||
2. `empty/full/count` 翻转拍逐拍对比;IP 的 `valid/underflow` 不接原逻辑。
|
||||
3. 深度向上取 2 的幂;复位保持 ≥ 双方要求最大值,双方 ready 后才开始比对。
|
||||
|
||||
## 滤波器替换契约(引导式,需求 4b)
|
||||
|
||||
1. **先分类(判定门槛就是"抽头是不是常量")**:
|
||||
- **抽头固定**(成型/抽取/内插/信道化,系数是模型里的常量)→ `fir_compiler`(系数进 .coe);
|
||||
抽取/内插比很大的级联积分梳状结构 → `cic_compiler`。**只有这一类进替换清单。**
|
||||
- **抽头非常量** —— LMS/自适应均衡器(EqCore)、Farrow/多项式内插器(TxPreOffset)、
|
||||
参考抽头为运行时捕获序列的相关器(RxAlignCore)—— **一律不替换,保留原 RTL**。
|
||||
不要用 Reloadable Coefficients 去凑:那是低速重载通道(一整帧系数经 AXI-Stream 灌入),
|
||||
表达不了逐样本更新的权值,也表达不了每拍随 mu 变化的 Farrow 系数;硬套只会既破坏
|
||||
cycle-true 又拿不到面积收益。
|
||||
(`fir_to_ip.py` 的 `--reload` 只用于**多组固定系数之间切换**的场合,不是自适应的出口。)
|
||||
2. 系数取 HDL Coder 常量表的**定点整数值**原样写 .coe,不让 IP 再量化。
|
||||
3. 输出用 **Full Precision**,截位放到 IP 外面按原 RTL 的移位/饱和方式做,**不开 IP 内建 rounding**。
|
||||
4. 延迟对齐:IP 延迟 L 与原 L0 之差用固定移位寄存器补平;L > L0 只能按双向调整原则改 Simulink 源,
|
||||
并复验 MER/收敛不劣化。
|
||||
5. 节奏:输入非满拍时用 `s_axis_data_tvalid`;`m_axis_data_tvalid` 翻转拍与原输出有效拍逐拍一致。
|
||||
6. 验收:对齐后 TB 逐拍 0 失配。
|
||||
|
||||
**IP 侧硬事实(2026-07-24 在本机 Vivado 2018.1 实测,已写进 gen_ip.tcl / fir_to_ip.py)**:
|
||||
|
||||
- `fir_compiler` 7.2 合法区间:`Coefficient_Width∈[8,49]`、`Data_Width∈[2,47]`、
|
||||
`Data_Fractional_Bits∈[0,16]`。HDL Coder 的 full-precision 抽头(如 OFDM 源的 51 位)
|
||||
**超限**,`fir_to_ip.py` 会拒绝登记该 IP 并写方案,要求先按双向调整原则回 Simulink 重新量化。
|
||||
- **二进制点一律填 0**(`Coefficient_Fractional_Bits=0`、`Data_Fractional_Bits=0`):core 做整数
|
||||
MAC,小数位只影响 GUI;填 0 与生成 RTL 的整数语义一致,又避开上面的 0..16 限制。
|
||||
- 定拍关键项:`RateSpecification=Input_Sample_Period` + `SamplePeriod=1`(禁折叠)、
|
||||
`Has_ACLKEN=true`(接 `enb`)、`M_DATA_Has_TREADY=false`、`S_DATA_Has_FIFO=false`。
|
||||
- `cic_compiler` 4.0 的同名参数**取值不同**:`RateSpecification` 只认
|
||||
`Frequency_Specification | Sample_Period`(**没有** `Input_Sample_Period`,填错直接报
|
||||
IP_Flow 19-3461)。
|
||||
- 对比仿真需要 `-L fir_compiler_v7_2_11 -L cic_compiler_v4_0_13`(gen_cmp_tb.py 已加)。
|
||||
|
||||
## 新工程接入本 skill
|
||||
|
||||
1. 在工程任务书里写明**实施对象清单**与**"暂不实施"名单**(§适用范围);
|
||||
2. 跑一次全量扫描(第 1 步),把结果作为**基线清单**记进工程 Notes——
|
||||
各类结构各几处(isempty-init 表 / RAM / CORDIC / FIFO / 定系数滤波器),
|
||||
以及被报为 `adaptive_fir_candidate` 但按范围规定**保留 RTL** 的那些;
|
||||
3. 确认 `VIVADO_PATH` 已指向本机 Vivado(§〇),否则 `run_cmp.bat` 跑不起来;
|
||||
4. 首次运行没有 `src_orig/`/`TRANSFORM.diff` 时会自动退回全量,并顺带把增量基线补齐;
|
||||
此后每次 `makehdl` 之后仍从第 0 步(备份)重跑,但走增量。
|
||||
|
||||
**典型收益点**:isempty-init 常量表(sin/cos 一类)在 HDL Coder 里会展开成 FF 阵列 + 巨型读 mux,
|
||||
数万到十几万 FF,是时序与布局的头号毒源;IP 化从根上解决。接入后优先扫这一类。
|
||||
Reference in New Issue
Block a user