- fpga_flash: 新增 build_zynq_boot.py / program_zynq_qspi.py(bootgen + program_flash, 含备份、SHA-256/容量/marker 守卫);vivado_env.py 增 resolve_sdk_dir(SDK_ROOT/SDK_PATH); SKILL.md 补 Zynq 烧写流程,示例去掉写死的 D:\Xilinx\SDK\2018.1,改由 skills_env.md 解析。 - rtl-ip-replace: gen_ip.tcl / namespace_ip.py 更新。 - sim-rtl-flow: SKILL.md 与 run_sim_rtl_flow.py 更新。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
66 lines
2.9 KiB
Python
66 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Suffix every module in rtl_ip/ with _IP so original and IP-based RTL can
|
|
coexist in one xsim `work` library for the dual-DUT cycle-true comparison.
|
|
|
|
- renames `module X` -> `module X_IP` for every module DECLARED in rtl_ip/*.v
|
|
- rewrites instantiations of those modules accordingly (word-boundary match)
|
|
- IP core instances (dist_mem_gen/blk_mem_gen/... names from ip_manifest) are
|
|
left untouched -- they are not declared here.
|
|
|
|
Run LAST, after rom_to_ip.py / ram_to_ip.py / any hand edits (CORDIC, FIFO).
|
|
Idempotent: a name already ending in _IP is never suffixed twice.
|
|
|
|
INCREMENTAL RUNS -- THE BUG THIS DOCSTRING USED TO DESCRIBE AS A FEATURE.
|
|
The rename set used to be "modules declared here whose name does NOT already end
|
|
in _IP", i.e. it treated an already-suffixed declaration as nothing left to do.
|
|
That is wrong the moment incr_plan.py restores previously-rewritten files and
|
|
drops ONE freshly regenerated file beside them, which is the normal incremental
|
|
case:
|
|
|
|
EncAddr.v restored from the last run -> declares EncAddr_IP
|
|
DpdTrain.v freshly generated -> declares DpdTrain, instantiates EncAddr
|
|
|
|
`EncAddr` was no longer in the rename set, so the new DpdTrain.v kept its bare
|
|
`EncAddr u_EncAddr (...)` while the file that defines it had been renamed away.
|
|
The delivery then elaborated only because the dual-DUT bench compiled the GOLDEN
|
|
tree into the same work library, where a bare `EncAddr` still exists; synthesis,
|
|
which sees the delivery alone, failed with "module 'EncAddr' not found".
|
|
|
|
The rename set is therefore built from the BASE NAME of every declaration --
|
|
X_IP counts as X -- so a regenerated file is namespaced against the whole
|
|
delivery, not just against the part of it that has not been namespaced yet.
|
|
"""
|
|
import argparse, os, re
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument('rtl_ip_dir')
|
|
a = ap.parse_args()
|
|
d = os.path.abspath(a.rtl_ip_dir)
|
|
files = [os.path.join(d, f) for f in os.listdir(d) if f.endswith('.v')]
|
|
|
|
mods, already = [], []
|
|
for p in files:
|
|
for m in re.finditer(r'^\s*module\s+(\w+)', open(p, encoding='utf-8', errors='replace').read(), re.M):
|
|
n = m.group(1)
|
|
if n.endswith('_IP'):
|
|
already.append(n[:-3])
|
|
mods.append(n[:-3]) # base name: a restored file still OWNS this name
|
|
else:
|
|
mods.append(n)
|
|
mods = sorted(set(mods), key=len, reverse=True)
|
|
print('modules:', mods)
|
|
if already:
|
|
print('already namespaced (kept in the rename set as base names):',
|
|
sorted(set(already)))
|
|
|
|
for p in files:
|
|
txt = open(p, encoding='utf-8', errors='replace').read()
|
|
for mod in mods:
|
|
txt = re.sub(r'\b%s\b(?!_IP)' % re.escape(mod), mod + '_IP', txt)
|
|
open(p, 'w', encoding='utf-8').write(txt)
|
|
print(f'suffixed {len(mods)} modules across {len(files)} files')
|
|
|
|
if __name__ == '__main__':
|
|
main()
|