当前位置:首页 > EDA > 电子设计自动化
[导读]FPGA验证最费人的是手写testbench:功能点一多,定向用例、边界用例、随机回归全靠手工,既慢又容易漏。用Python做“向量生成—testbench渲染—仿真调用—结果比对—报告汇总”闭环,可把几百个组合用例一键跑完,适合加法器、FIFO、SPI、滤波器、AXI片段等中小规模数字逻辑。


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省时且可复现。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭