Files
skills-win/rtl-ip-replace/scripts/ram_to_ip.py
T
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

106 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Replace SimpleDualPortRAM_generic (HDL Coder) with blk_mem_gen SDP instances.
Semantics of the original (must be preserved exactly):
- single clock, `enb` gates BOTH write and read registration
- read latency 1 cycle; write/read same address -> read returns OLD data (read-first)
=> blk_mem_gen: Simple Dual Port, common clock, Port-A write mode READ_FIRST,
ENA/ENB pins used, NO output register.
The wrapper keeps the module name/ports/params; a generate-if selects the
per-(AddrWidth,DataWidth) IP; unlisted combos fall back to the original
behavioral body (so simulation still works, and synthesis of an unlisted combo
is loudly visible in the utilization report instead of silently wrong).
Run AFTER rom_to_ip.py (needs rtl_ip/ copies), BEFORE namespace_ip.py.
"""
import argparse, json, os, re
WRAP = '''// regenerated by ram_to_ip.py -- blk_mem_gen SDP wrappers (read-first, 1-cycle, common clk)
`timescale 1 ns / 1 ns
module {mod}
(clk, enb, wr_din, wr_addr, wr_en, rd_addr, dout);
parameter integer AddrWidth = 1;
parameter integer DataWidth = 1;
input clk;
input enb;
input signed [DataWidth - 1:0] wr_din;
input [AddrWidth - 1:0] wr_addr;
input wr_en;
input [AddrWidth - 1:0] rd_addr;
output signed [DataWidth - 1:0] dout;
generate
{cases} begin: g_behav // fallback: original behavioral RAM (unlisted param combo)
reg [DataWidth - 1:0] ram [2**AddrWidth - 1:0];
reg [DataWidth - 1:0] data_int;
integer i;
initial begin
for (i=0; i<=2**AddrWidth - 1; i=i+1) ram[i] = 0;
data_int = 0;
end
always @(posedge clk)
if (enb == 1'b1) begin
if (wr_en == 1'b1) ram[wr_addr] <= wr_din;
data_int <= ram[rd_addr];
end
assign dout = data_int;
end
endgenerate
endmodule
'''
CASE = ''' if (AddrWidth == {aw} && DataWidth == {dw}) begin: g_a{aw}d{dw}
wire [{dwm}:0] doutb_i;
{ip} u_bram (
.clka(clk), .ena(enb), .wea(wr_en), .addra(wr_addr), .dina(wr_din),
.clkb(clk), .enb(enb), .addrb(rd_addr), .doutb(doutb_i));
assign dout = doutb_i;
end else
'''
def main():
ap = argparse.ArgumentParser()
ap.add_argument('hdlsrc')
ap.add_argument('outdir', help='FIXED output dir (e.g. <repo>/vivado/rtl_ip/<model>)')
ap.add_argument('--manifest', default=None)
a = ap.parse_args()
hdlsrc = os.path.abspath(a.hdlsrc)
rtl_ip = os.path.abspath(a.outdir)
man = json.load(open(a.manifest or os.path.join(rtl_ip, 'manifest.json')))
combos = sorted({(r['AddrWidth'], r['DataWidth']) for r in man['ram']
if r['AddrWidth'] and r['DataWidth']})
if not combos:
print('no SimpleDualPortRAM instantiations found'); return
ipman_p = os.path.join(rtl_ip, 'ip_manifest.json')
ipman = json.load(open(ipman_p)) if os.path.exists(ipman_p) else {'ips': []}
have = {ip['name'] for ip in ipman['ips']}
cases = ''
for aw, dw in combos:
ip = f'bram_sdp_a{aw}d{dw}'
cases += CASE.format(aw=aw, dw=dw, dwm=dw - 1, ip=ip)
if ip not in have:
ipman['ips'].append({'type': 'blk_mem_gen', 'name': ip, 'width': dw,
'depth': 2 ** aw, 'mode': 'sdp_readfirst_commonclk'})
# rewrite every SimpleDualPortRAM_*.v module file present in rtl_ip
n = 0
for fn in os.listdir(rtl_ip):
if re.match(r'SimpleDualPortRAM_\w+\.v$', fn):
mod = fn[:-2]
open(os.path.join(rtl_ip, fn), 'w').write(WRAP.format(mod=mod, cases=cases))
print('wrapped', mod, 'combos:', combos)
n += 1
json.dump(ipman, open(ipman_p, 'w'), indent=1)
with open(os.path.join(rtl_ip, 'ip_manifest.tcl'), 'w') as f:
for ip in ipman['ips']:
f.write('lappend ips {%s}\n' % ' '.join(f'{k} {{{v}}}' for k, v in ip.items()))
print(f'done: {n} RAM module(s) wrapped, {len(combos)} blk_mem_gen config(s)')
if __name__ == '__main__':
main()