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:
bowei
2026-07-27 10:39:53 +08:00
parent 6dddab8923
commit ea4541ee90
23 changed files with 3171 additions and 12 deletions
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""Gate for rule 2 of Task12-1: an m-code block inside an RTL-bound subtree is
acceptable ONLY if a specific Xilinx IP is named as its replacement AND the swap
is proven cycle-true by simulation.
python check_exemptions.py <hdl_audit.json> [<exemptions.json>]
Exit code 0 = the subtree may go to makehdl / synthesis:
every m-code block is either gone, or exempted with (a) a named Xilinx IP and
(b) an evidence file whose first line is PASS (rtl-ip-replace cmp_result.txt).
Exit code 1 = blocked, with the reason printed per block.
Evidence paths are resolved relative to the repo root (the directory containing
Notes/), or absolutely.
"""
import json, os, sys
REQUIRED = ('block', 'reason', 'xilinx_ip', 'cycle_true_evidence', 'approved_by', 'date')
def repo_root(start):
d = os.path.abspath(start)
while True:
if os.path.isdir(os.path.join(d, 'Notes')) or os.path.isdir(os.path.join(d, '.git')):
return d
nd = os.path.dirname(d)
if nd == d:
return os.path.abspath(start)
d = nd
def evidence_ok(path, roots):
tried = []
for r in ([''] if os.path.isabs(path) else roots):
p = path if os.path.isabs(path) else os.path.join(r, path)
tried.append(p)
if os.path.isfile(p):
first = open(p, errors='replace').readline().strip()
if not first.upper().startswith('PASS'):
return False, f'evidence is not a PASS: "{first}" ({p})'
return True, first
return False, 'evidence file not found: ' + ' | '.join(tried)
def main():
argv = [a for a in sys.argv[1:] if not a.startswith('--root=')]
root_opt = [a.split('=', 1)[1] for a in sys.argv[1:] if a.startswith('--root=')]
if not argv:
sys.exit(__doc__)
audit = json.load(open(argv[0], encoding='utf-8-sig'))
exf = argv[1] if len(argv) > 1 else audit.get('exemptions_file') or ''
roots = [r for r in (root_opt + [repo_root(os.getcwd()),
repo_root(os.path.dirname(os.path.abspath(argv[0])) or '.'),
os.getcwd()]) if r]
items = audit.get('items') or []
if isinstance(items, dict):
items = [items]
mcode = [i for i in items if i['severity'] in ('VIOLATION', 'EXEMPT')]
warns = [i for i in items if i['severity'] == 'WARN']
ex = []
if exf and os.path.isfile(exf):
raw = json.load(open(exf, encoding='utf-8-sig'))
ex = raw.get('exemptions', raw if isinstance(raw, list) else [])
by_block = {e.get('block'): e for e in ex}
print(f"target : {audit.get('target')}")
print(f"root : {roots[0]}")
print(f"audit : {len(items)} flagged block(s) "
f"({len(mcode)} m-code, {len(warns)} stateflow-warn)")
print(f"exempt : {exf or '(none)'}\n")
problems = []
for it in mcode:
blk = it['block']
e = by_block.get(blk)
if not e:
problems.append(f"{blk}\n m-code with no exemption entry -- rebuild it from "
f"native Simulink blocks, or file an exemption (rule 2).")
print(f" [BLOCKED ] {blk}")
continue
missing = [k for k in REQUIRED if not str(e.get(k, '')).strip()]
if missing:
problems.append(f"{blk}\n exemption is incomplete: missing {', '.join(missing)}")
print(f" [BLOCKED ] {blk} (incomplete exemption)")
continue
ok, info = evidence_ok(e['cycle_true_evidence'], roots)
if not ok:
problems.append(f"{blk}\n {info}")
print(f" [BLOCKED ] {blk} ({info})")
continue
print(f" [EXEMPT ] {blk}\n IP: {e['xilinx_ip']}\n"
f" cycle-true: {info} [{e['cycle_true_evidence']}]")
for it in warns:
print(f" [WARN ] {it['block']} ({it['kind']}) -- control-only Stateflow is tolerated; "
f"no datapath arithmetic in it")
stale = [b for b in by_block if b not in {i['block'] for i in items}]
for b in stale:
print(f" [STALE ] exemption for a block that is no longer m-code (or gone): {b}")
if problems:
print(f'\nGATE: BLOCKED -- {len(problems)} problem(s)\n')
for p in problems:
print(' * ' + p)
print('\nRule (Task12-1): an RTL-bound subsystem and everything under it must be built '
'from Simulink blocks that generate RTL directly; embedded m code is allowed only '
'against a named Xilinx IP, proven cycle-true.')
sys.exit(1)
print('\nGATE: PASS -- no undocumented m code in the RTL-bound subtree')
if __name__ == '__main__':
main()