- 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
111 lines
4.4 KiB
Python
111 lines
4.4 KiB
Python
"""Shared helpers: source copying + PROVENANCE.log (mandatory traceability).
|
|
|
|
RULE (user directive 2026-07-23): script-processed RTL lives ONLY in the fixed
|
|
directory <repo>/vivado/rtl_ip/<model>/ ; the Vivado project may reference RTL
|
|
ONLY from there, NEVER from the MATLAB hdlsrc directly. PROVENANCE.log in the
|
|
fixed directory records the absolute path of the original MATLAB RTL.
|
|
Generated Xilinx IPs live in <fixed_dir>/ip/.
|
|
"""
|
|
import difflib, hashlib, os, re, shutil, time
|
|
|
|
SRC_ORIG = 'src_orig' # verbatim snapshot of the MATLAB RTL this run is based on
|
|
TRANSFORM_DIFF = 'TRANSFORM.diff'
|
|
|
|
|
|
def src_files(d):
|
|
return [fn for fn in sorted(os.listdir(d))
|
|
if fn.endswith('.v') and not fn.endswith('_tb.v')]
|
|
|
|
|
|
def sha1_file(p):
|
|
return hashlib.sha1(open(p, 'rb').read()).hexdigest()
|
|
|
|
|
|
def copy_sources(hdlsrc, outdir, force=False):
|
|
os.makedirs(outdir, exist_ok=True)
|
|
copied = []
|
|
for fn in src_files(hdlsrc):
|
|
dst = os.path.join(outdir, fn)
|
|
if force or not os.path.exists(dst):
|
|
shutil.copy(os.path.join(hdlsrc, fn), dst)
|
|
copied.append(fn)
|
|
snapshot_sources(hdlsrc, outdir)
|
|
write_provenance(outdir, hdlsrc)
|
|
return copied
|
|
|
|
|
|
def snapshot_sources(hdlsrc, outdir):
|
|
"""Task12 rule 14: keep the ORIGINAL MATLAB RTL this run is based on, so the
|
|
next run can diff against it and reuse unchanged work (incremental mode)."""
|
|
d = os.path.join(outdir, SRC_ORIG)
|
|
os.makedirs(d, exist_ok=True)
|
|
for fn in src_files(hdlsrc):
|
|
dst = os.path.join(d, fn)
|
|
if not os.path.exists(dst) or sha1_file(dst) != sha1_file(os.path.join(hdlsrc, fn)):
|
|
shutil.copy(os.path.join(hdlsrc, fn), dst)
|
|
return d
|
|
|
|
|
|
_HUNK = re.compile(r'^@@ -\d+(,\d+)? \+\d+(,\d+)? @@')
|
|
|
|
|
|
def write_transform_diff(outdir):
|
|
"""src_orig/*.v -> rewritten *.v, as removed/added lines only (n=0) with hunk
|
|
line numbers normalised away: two runs whose scripts performed the SAME
|
|
rewrite produce byte-identical files, even if the MATLAB source changed
|
|
elsewhere (that is exactly the class-B case of Task12 §5.2)."""
|
|
orig = os.path.join(outdir, SRC_ORIG)
|
|
out = []
|
|
for fn in src_files(orig):
|
|
mod = os.path.join(outdir, fn)
|
|
if not os.path.exists(mod):
|
|
out.append(f'=== {fn}: MISSING in outdir')
|
|
continue
|
|
a = open(os.path.join(orig, fn), errors='replace').read().splitlines()
|
|
b = open(mod, errors='replace').read().splitlines()
|
|
d = [_HUNK.sub('@@', ln) for ln in
|
|
difflib.unified_diff(a, b, fromfile=f'orig/{fn}', tofile=f'ip/{fn}',
|
|
n=0, lineterm='')]
|
|
if d:
|
|
out.extend(d)
|
|
p = os.path.join(outdir, TRANSFORM_DIFF)
|
|
open(p, 'w').write('\n'.join(out) + '\n')
|
|
return p
|
|
|
|
|
|
def scripts_sha1(script_dir=None):
|
|
"""Fingerprint of the skill itself -- changed scripts forbid inheriting a
|
|
previous cycle-true PASS (Task12 §5.4/5.5)."""
|
|
d = script_dir or os.path.dirname(os.path.abspath(__file__))
|
|
h = hashlib.sha1()
|
|
for fn in sorted(os.listdir(d)):
|
|
if fn.endswith(('.py', '.tcl')):
|
|
h.update(fn.encode())
|
|
h.update(open(os.path.join(d, fn), 'rb').read())
|
|
return h.hexdigest()[:12]
|
|
|
|
def write_provenance(outdir, hdlsrc):
|
|
hdlsrc = os.path.abspath(hdlsrc)
|
|
lines = ['# PROVENANCE.log -- generated by rtl-ip-replace, do not edit',
|
|
'# RULE: the Vivado project must use RTL from THIS directory only;',
|
|
'# direct use of the MATLAB-generated hdlsrc below is FORBIDDEN.',
|
|
'source_hdlsrc: ' + hdlsrc,
|
|
'updated_at: ' + time.strftime('%Y-%m-%d %H:%M:%S'),
|
|
'ip_subdir: ip/',
|
|
'files:']
|
|
for fn in src_files(hdlsrc):
|
|
p = os.path.join(hdlsrc, fn)
|
|
h = sha1_file(p)[:12]
|
|
mt = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(os.path.getmtime(p)))
|
|
lines.append(f' {fn} src_sha1={h} src_mtime={mt}')
|
|
lines.append('src_snapshot: ' + SRC_ORIG + '/ # original MATLAB RTL kept for the next incremental run')
|
|
lines.append('scripts_sha1: ' + scripts_sha1())
|
|
open(os.path.join(outdir, 'PROVENANCE.log'), 'w').write('\n'.join(lines) + '\n')
|
|
|
|
|
|
def append_provenance(outdir, lines):
|
|
"""Append the incremental-run block (Task12 §5.6). Called last, after the
|
|
transform scripts have rewritten PROVENANCE.log."""
|
|
with open(os.path.join(outdir, 'PROVENANCE.log'), 'a') as f:
|
|
f.write('\n'.join(lines) + '\n')
|