用Python自动生成FPGA测试用例:批量完成数字逻辑的仿真验证
FPGA验证最费人的是手写testbench:功能点一多,定向用例、边界用例、随机回归全靠手工,既慢又容易漏。用Python做“向量生成—testbench渲染—仿真调用—结果比对—报告汇总”闭环,可把几百个组合用例一键跑完,适合加法器、FIFO、SPI、滤波器、AXI片段等中小规模数字逻辑。
一、向量生成与参数组合
先用Python按位宽、模式、边界条件产出激励,存CSV或十六进制文本,Verilog侧用readmemh/fscanf读取,避免把随机逻辑写进RTL。
import csv, random, itertools
def gen_vectors(width=8, n_random=200):
cases=[]
mask=(1<<width)-1
# 定向:全0、全1、异号、单bit
cases+=[(0,0),(mask,mask),(1,0),(0,1),(mask,1),(1,mask)]
# 边界:加和刚好不溢出/刚好溢出
cases+=[(mask-1,1),(mask,1),(mask>>1,mask>>1)]
# 随机
for _ in range(n_random):
cases.append((random.randint(0,mask),random.randint(0,mask)))
# 参数组合:不同saturation模式
out=[]
for sat in [0,1]:
for a,b in cases:
out.append((sat,a,b))
return out
with open('stim.csv','w',newline='') as f:
w=csv.writer(f); w.writerow(['sat','a','b'])
for row in gen_vectors(8,200): w.writerow(row)
print('total cases:',len(gen_vectors(8,200)))
sat=0表示普通加、截位;sat=1表示饱和到位宽最大值。这样定向加随机再加模式交叉,能覆盖溢出、符号扩展、全0全1等易错点。
二、自动渲染Testbench并跑仿真
Python不直接写复杂Verilog,只生成激励文件和轻量比对脚本;仿真器负责编译运行,Python负责调度与判错。以Icarus为例:
import subprocess, csv
def build_tb():
with open('stim.csv') as f:
rows=list(csv.reader(f))[1:]
lines=[]
for sat,a,b in rows:
lines.append(f"#(sat,{sat}) (clk,1,0,{a},{b});")
tb=f"""
module tb;
reg clk; reg [7:0] a,b; reg sat; wire [8:0] y;
adder8 u(.clk(clk),.sat(sat),.a(a),.b(b),.y(y));
initial begin
clk=0; $dumpfile("sim.vcd"); $dumpvars;
// stim injected below
$finish;
end
always #5 clk=~clk;
endmodule
"""
# 实际项目把stim按时间驱动写进initial,这里仅示意
open('tb_adder_auto.v','w').write(tb)
def run_one():
c=subprocess.run(['iverilog','-o','sim.vvp','adder8.v','tb_adder_auto.v'],
capture_output=True,text=True)
if c.returncode!=0:
print('COMPILE_FAIL'); return False
r=subprocess.run(['vvp','sim.vvp'],capture_output=True,text=True)
# 参考模型比对
for line in r.stdout.splitlines():
if 'MISMATCH' in line:
print('CASE_FAIL:',line); return False
return True
build_tb(); print('PASS' if run_one() else 'FAIL')
adder8的testbench在initial里按CSV驱动a/b/sat,每个用例算完调用Python参考模型:py=(a+b)若sat则min(max(...)),把期望写回$display,仿真日志出现MISMATCH即判失败。这样不用人工看波形。
三、批量回归与报告
多配置用itertools做笛卡尔积,例如位宽8/16/32、sat0/1、随机种子多组,生成全部tb后串行或并行subprocess。汇总写成JSON/HTML:
import itertools, subprocess, json
report={"total":0,"fail":0,"detail":[]}
for width in [8,16,32]:
for sat in [0,1]:
cmd=['python','gen_run.py',str(width),str(sat)]
ret=subprocess.run(cmd,capture_output=True,text=True)
report["total"]+=1
if 'FAIL' in ret.stdout:
report["fail"]+=1
report["detail"].append((width,sat,ret.stdout[-200:]))
json.dump(report,open('regression.json','w'),indent=2)
print('fail/total:',report["fail"],'/',report["total"])
接CI时,每次git提交跑regression.json,失败直接阻断合并;随机种子固定可复现,换种子跑压力。
四、进阶用法
cocotb写纯Python testbench,用async驱动时钟、复位、AXI/SPI BFM,assert直接比对,适合不想到处生成Verilog的工程;
随机激励配约束:位宽、有效字节、背压概率分别抽,避免无意义的全空间穷举;
边界优先:FIFO满写、空读、同时读写,加法溢出、符号翻转,状态机每个转移至少一条定向;
覆盖率:Icarus/Verilator可出代码覆盖,Vivado/ModelSim出分支与FSM覆盖,Python解析报告,未覆盖分支反向补向量;
大批量用Verilator编译成可执行,比解释型仿真快很多;厂商仿真用命令行批处理,不靠GUI点按钮。
按“Python出向量—模板出tb—仿真器跑DUT—参考模型比对—回归报告”落地,手动20个用例可扩展到数百个,边界和随机组合不漏;后期接cocotb或CI,数字逻辑改动后一分钟出验证结论,比纯手写testbench省时且可复现。





