资讯动态

数字前端集成脚本:Python驱动的Verilog/SystemVerilog自动化实践

发布时间:2026/9/25 12:07:29 来源:尧图企业网站定制
1. “集成脚本”不是功能模块而是数字前端工程师的呼吸节奏“集成脚本”这四个字在IC设计流程里从来就不是某个工具菜单里的选项也不是GitHub上能一键clone的开源项目。它是一组被反复修改、深夜调试、凌晨提交、又被下个版本推翻重写的Python文件散落在每个数字前端工程师的/project/scripts/目录深处。我见过最老的集成脚本是2012年用Python 2.6写的里面还混着几行os.system(vlog defineDEBUG ...)也见过最新版——用argparse封装了17个参数、支持YAML配置、自动校验UVM testbench层级依赖的现代脚本。但它们干的同一件事把Verilog/SystemVerilog代码、testbench、约束文件、仿真脚本、覆盖率收集逻辑像搭乐高一样严丝合缝地拼成一个可编译、可仿真、可回归、可交付的完整验证环境。你搜“集成脚本”刷出来的全是“Verilog”“SystemVerilog”“Python”“openpyxl”——这不是巧合。这四词精准勾勒出它的血缘图谱Verilog和SystemVerilog是它的骨与肉要集成的对象Python是它的神经与血液实现集成的载体openpyxl是它的眼睛与手读写Excel格式的IP配置表、寄存器定义、测试用例矩阵。它不处理算法不优化时序不写RTL但它决定了整个设计流程能不能跑起来、跑得稳不稳、跑得快不快。一个写得糙的集成脚本能让团队每天多花2小时在环境搭建和路径错误上一个写得精的集成脚本能把回归测试从4小时压缩到35分钟还能自动生成带超链接的覆盖率报告HTML。它解决的不是“能不能做”而是“要不要重做”。比如当验证经理说“这个IP要加AXI-Lite接口所有testcase都要重跑”老手不会立刻打开VCS命令行而是先改gen_top.py里的接口生成逻辑再跑一遍run_regression.py——脚本跑完结果已出报告已发。新手则可能手动改了12个testbench里的include路径漏掉1个仿真卡死在第37个case排查3小时才发现是tb_top.sv里少加了一行bind语法。这就是集成脚本存在的全部意义把人从重复劳动中解放出来把注意力聚焦在真正需要思考的地方——逻辑正确性、边界条件、协议鲁棒性。所以别把它当成“辅助工具”。它是数字前端流程的中枢神经系统。今天这篇文章不讲抽象概念不列API文档只拆解我在5个SoC项目里亲手打磨、踩坑、重构过的4类核心集成脚本IP级自动拼接脚本、跨平台仿真环境生成器、Excel驱动的寄存器验证自动化、以及基于SystemVerilog bind语法的动态验证注入框架。每一段代码都来自真实项目现场每一个坑我都亲手填过三次以上。2. IP级自动拼接脚本让top.v不再靠手工CtrlC/V生成2.1 为什么必须自动化一个真实的“拼错top”事故去年Q3我们交付一款图像处理IP给客户。交付包里包含RTL、testbench、文档还有那个至关重要的top.v——顶层模块它把所有子模块DMA控制器、图像缩放器、色彩空间转换器、AXI桥接器按信号连接关系串起来。交付前最后一轮回归所有case全绿。客户拿到后一跑仿真直接报错UUT.dma_if.req未驱动。我们复现发现top.v里DMA模块实例化语句漏掉了.req(dma_req)这一行连线。查Git记录是某位同事在合并分支时手工编辑top.v复制粘贴时删掉了一行而该行恰好在diff视图的折叠区域外Code Review没发现。修复只花了2分钟但重走客户沟通、补签NDA、更新交付包耗了整整5个工作日。这件事让我彻底放弃手工维护top.v。IP模块越来越多接口越来越复杂AXI4、ACE、CHI、TileLink手工拼接的错误率呈指数上升。自动化不是为了炫技而是为了消除这种低级但致命的错误。2.2 核心原理用Python解析SV文件提取端口信息再按规则生成连接自动化拼接的核心不是写个模板填空而是理解SystemVerilog语法结构精准提取每个模块的端口定义并根据预设的连接规则如命名约定、总线类型匹配自动生成连线。我们不用正则表达式硬匹配因为module my_ip #(parameter WIDTH32) (input logic clk, rst_n, output logic [WIDTH-1:0] data);这种写法正则很难可靠解析。我们用pyparsing库构建一个轻量级SV端口解析器from pyparsing import * import re # 定义SV端口语法元素 direction oneOf(input output inout) type_spec oneOf(logic reg wire) | Word(alphas _) # 支持自定义类型 array_spec Optional(Suppress([) SkipTo(]) Suppress(])) port_name Word(alphas _0123456789) port_def Group(direction Optional(type_spec) array_spec port_name) def parse_module_ports(sv_content): 解析SV文件中的module端口声明 # 先定位module声明块 module_start Keyword(module) Word(alphas _0123456789) Suppress(() module_end Suppress();) # 提取端口部分直到第一个分号或endmodule port_section module_start SkipTo(module_end) module_end try: parsed port_section.parseString(sv_content) # 简化实际项目中会用更健壮的AST解析此处为示意 ports [] for line in sv_content.split(\n): if re.match(r^\s*(input|output|inout), line): # 基础解析提取方向、类型、名称 parts re.split(r\s, line.strip().rstrip(,;)) if len(parts) 2: direction parts[0] name parts[-1].rstrip(,) # 过滤掉数组声明等 name re.sub(r\[.*?\], , name).strip() ports.append({dir: direction, name: name}) return ports except Exception as e: print(fParse error in {sv_content[:100]}: {e}) return []提示生产环境强烈建议使用verilog-parser或sv2v等成熟解析器pyparsing仅用于快速原型验证。关键不是解析器多强大而是确保端口信息100%准确——一个漏掉的clk信号会导致整个仿真停摆。2.3 实战gen_top.py——从Excel配置表生成top.v我们的IP集成流程核心输入是一份ip_integration.xlsx由架构师维护。Sheet名为IP_LIST结构如下IP_NAMEINST_NAMERTL_PATHCLK_DOMAINRST_POLARITYCONNECT_TOdma_ctrlu_dma./rtl/dma_ctrl.svsys_clkactive_lowtopscaleru_scaler./rtl/scaler.svpix_clkactive_highu_dmaCONNECT_TO列定义了模块间的连接关系。gen_top.py读取此表执行三步操作收集所有IP的端口信息遍历RTL_PATH调用parse_module_ports()获取每个IP的输入/输出端口列表。构建连接图谱根据CONNECT_TO列确定哪些信号需要跨模块连接。例如u_dma的pix_clk_out要连到u_scaler的clk。生成top.v按标准模板填充重点是自动生成.port_name(signal_name)连线。关键逻辑如下def generate_top_module(ip_list_df): top_lines [] top_lines.append(timescale 1ns / 1ps) top_lines.append(module top () # 1. 生成顶层端口声明所有IP的输入端口汇总去重 all_inputs set() all_outputs set() for _, row in ip_list_df.iterrows(): ports parse_module_ports(open(row[RTL_PATH]).read()) for p in ports: if p[dir] input: all_inputs.add(p[name]) elif p[dir] output: all_outputs.add(p[name]) # 按字母序排列保证每次生成顺序一致便于diff for port in sorted(all_inputs): top_lines.append(f input logic {port},) for i, port in enumerate(sorted(all_outputs)): sep if i len(all_outputs)-1 else , top_lines.append(f output logic {port}{sep}) top_lines.append();) # 2. 生成内部信号线声明所有IP的输出端口作为wire internal_wires [] for _, row in ip_list_df.iterrows(): ports parse_module_ports(open(row[RTL_PATH]).read()) for p in ports: if p[dir] output: internal_wires.append(fwire {p[name]};) top_lines.extend(internal_wires) top_lines.append() # 3. 生成模块实例化语句核心自动连线 for _, row in ip_list_df.iterrows(): inst_name row[INST_NAME] ip_name row[IP_NAME] ports parse_module_ports(open(row[RTL_PATH]).read()) top_lines.append(f {ip_name} {inst_name} () # 遍历该IP所有端口生成连线 for i, p in enumerate(ports): # 规则1顶层端口直接映射 if p[name] in all_inputs or p[name] in all_outputs: conn_sig p[name] # 规则2内部信号查找上游提供者 else: # 在ip_list_df中找谁的输出端口是p[name] provider ip_list_df[ip_list_df.apply( lambda r: any(pp[name] p[name] and pp[dir]output for pp in parse_module_ports(open(r[RTL_PATH]).read())), axis1)] if not provider.empty: conn_sig f{provider.iloc[0][INST_NAME]}.{p[name]} else: # 默认连接到顶层同名信号或报错 conn_sig p[name] sep if i len(ports)-1 else , top_lines.append(f .{p[name]}({conn_sig}){sep}) top_lines.append(f );) top_lines.append(endmodule) return \n.join(top_lines) # 主函数 if __name__ __main__: df pd.read_excel(ip_integration.xlsx, sheet_nameIP_LIST) top_v generate_top_module(df) with open(top.v, w) as f: f.write(top_v) print(top.v generated successfully!)注意这段代码的关键不在语法而在连接规则的可配置性。CONNECT_TO列就是规则引擎。当需要新增一个u_i2c模块并让它接收u_dma的i2c_scl信号时只需在Excel里加一行gen_top.py自动搞定所有连线无需碰任何一行RTL代码。这才是集成脚本的价值——把设计决策谁连谁和代码生成怎么连彻底分离。2.4 踩坑实录bind语法与自动拼接的冲突点SystemVerilog的bind语法bind dut dut_wrapper u_dut_wrapper;常用于将验证组件如coverage collector、assertion checker动态绑定到DUT上。问题来了gen_top.py生成的top.v里如果DUT实例名是u_dut而bind语句写在dut_wrapper.sv里目标是u_dut那没问题。但如果gen_top.py因为配置变更把实例名从u_dut改成了u_dut_v2bind语句就失效了且仿真器不会报错只是coverage不收集。解决方案bind语句不写死实例名改用通配符或参数化。我们在dut_wrapper.sv里这样写// dut_wrapper.sv bind dut dut_wrapper #(.DUT_INST_NAME(u_dut)) u_dut_wrapper; // 或者更灵活的 bind dut dut_wrapper u_dut_wrapper; // 然后在dut_wrapper内部用$root.top.u_dut来引用需确保路径唯一但更好的方案是让gen_top.py在生成top.v的同时也生成一个bind_config.sv内容为// bind_config.sv - 自动生成 define DUT_INST_NAME u_dut define DMA_INST_NAME u_dma然后在dut_wrapper.sv里bind dut dut_wrapper #(.DUT_INST_NAME(DUT_INST_NAME)) u_dut_wrapper;这样bind语句和实例名永远同步。这个细节是无数个深夜debug后才悟出的——集成脚本不仅要生成代码还要为后续的验证扩展留好接口。3. 跨平台仿真环境生成器告别vcs -full64 -sverilog ...的手动敲命令3.1 为什么需要“环境生成器”一次VCS vs Questa的兼容性灾难我们曾同时用VCS和Questa进行仿真。VCS要求-sverilog开关启用SV特性Questa默认开启加了反而报错VCS的覆盖率开关是vcscoverallQuesta是-cover bcestfVCS的波形dump是vcsdumpvarsQuesta是-wave。更麻烦的是不同版本的工具参数还略有差异。一份run_sim.sh脚本在VCS上跑得好好的换到Questa上要么编译失败要么仿真卡死要么覆盖率不收集。工程师的本能反应是写两套脚本。但很快发现当新加一个defineDEBUG时要改两处当升级Questa版本发现-cover参数变了又要改当客户要求用Cadence Xcelium又得加第三套……脚本数量爆炸维护成本远超收益。3.2 核心设计抽象出“仿真任务”用YAML定义Python生成具体命令我们摒弃了“为每个工具写脚本”的思路转而定义一个与工具无关的仿真任务模型。核心是sim_config.yaml# sim_config.yaml tool: vcs version: 2023.06 top_module: top testbench: tb_top.sv rtl_files: - ./rtl/top.v - ./rtl/dma_ctrl.sv - ./rtl/scaler.sv defines: - DEBUG - SIMULATION includes: - ./include/ compile_options: - -sverilog - -full64 - incdir./include sim_options: - vcscoverall - vcsdumpvars - vcslicwaitgen_sim_script.py读取此YAML根据tool字段调用对应的模板生成器import yaml import jinja2 def load_config(config_path): with open(config_path) as f: return yaml.safe_load(f) def render_vcs_script(config): template_str #!/bin/bash # Generated by gen_sim_script.py for VCS {{ config.version }} set -e VCS_CMDvcs -sverilog -full64 \\ {% for def in config.defines %}-define{{ def }} \\ {% endfor %} {% for inc in config.includes %}incdir{{ inc }} \\ {% endfor %} {% for opt in config.compile_options %}{{ opt }} \\ {% endfor %} -top {{ config.top_module }} \\ {% for file in config.rtl_files %}{{ file }} \\ {% endfor %} {{ config.testbench }} echo Compiling with: $VCS_CMD eval $VCS_CMD SIM_CMD./simv \\ {% for opt in config.sim_options %}{{ opt }} \\ {% endfor %} UVM_TESTNAME{{ config.get(test_name, base_test) }} echo Running simulation: $SIM_CMD eval $SIM_CMD template jinja2.Template(template_str) return template.render(configconfig) def main(): config load_config(sim_config.yaml) if config[tool] vcs: script render_vcs_script(config) elif config[tool] questa: script render_questa_script(config) # 类似逻辑 else: raise ValueError(fUnsupported tool: {config[tool]}) with open(run_sim.sh, w) as f: f.write(script) print(run_sim.sh generated!) if __name__ __main__: main()关键洞察真正的可移植性不在于脚本本身兼容所有工具而在于把“做什么”编译、仿真、收集coverage和“怎么做”VCS命令、Questa命令彻底解耦。YAML是“做什么”的声明Python模板是“怎么做”的实现。当需要支持Xcelium时只需新增render_xcelium_script()函数无需改动YAML或现有逻辑。3.3 实战run_regression.py——从Excel用例表驱动批量仿真回归测试的痛点是testcase太多手动跑效率低且容易漏。我们用testcases.xlsx管理所有caseCASE_IDTEST_NAMETOP_MODULETB_FILERUN_TIMEEXPECTED_RESULTPRIORITYTC001basic_dmatoptb_dma.sv1000000PASSHIGHTC002burst_modetoptb_dma.sv2000000PASSMEDIUMrun_regression.py读取此表对每个case临时生成sim_config.yaml覆盖top_module,testbench,sim_options中的UVM_TESTNAME调用gen_sim_script.py生成run_sim.sh执行bash run_sim.sh解析sim.log提取UVM_REPORT_SERVER的PASS/FAIL信息将结果写回Excel的ACTUAL_RESULT列并标红失败项。核心代码片段def run_single_case(case_row): # 1. 生成临时sim_config temp_config { tool: vcs, top_module: case_row[TOP_MODULE], testbench: case_row[TB_FILE], rtl_files: get_rtl_files(), # 从全局配置读取 defines: [DEBUG], sim_options: [ fUVM_TESTNAME{case_row[TEST_NAME]}, UVM_VERBOSITYUVM_LOW, fUVM_MAX_QUIT_COUNT{case_row.get(MAX_FAIL, 1)} ] } with open(temp_sim.yaml, w) as f: yaml.dump(temp_config, f) # 2. 生成脚本 subprocess.run([python, gen_sim_script.py, temp_sim.yaml]) # 3. 执行 result subprocess.run([bash, run_sim.sh], capture_outputTrue, textTrue, timeoutcase_row[RUN_TIME]//1000) # 4. 解析日志 log_content result.stdout result.stderr if UVM_FATAL in log_content or UVM_ERROR in log_content: return FAIL elif TEST PASSED in log_content: return PASS else: return UNKNOWN # 主循环 df pd.read_excel(testcases.xlsx) results [] for _, row in df.iterrows(): try: res run_single_case(row) results.append(res) print(f{row[CASE_ID]}: {res}) except subprocess.TimeoutExpired: results.append(TIMEOUT) except Exception as e: results.append(fERROR: {e}) df[ACTUAL_RESULT] results df.to_excel(testcases_result.xlsx, indexFalse)经验之谈run_regression.py最大的价值不是“自动跑”而是统一了回归测试的入口和出口。以前A工程师跑case用vcs -guiB工程师用qrun -guiC工程师用irun -gui结果分散在各自终端里无法汇总。现在所有人只运行python run_regression.py --case TC001结果自动落库、自动邮件通知、自动触发CI。集成脚本最终是流程标准化的基石。4. Excel驱动的寄存器验证自动化让reg_model不再手写4.1 为什么寄存器验证是痛点一个“寄存器地址写反”的代价寄存器验证是UVM验证中最枯燥也最容易出错的部分。我们曾有一个PCIe IP寄存器映射表有128个地址每个地址有多个bit field。验证工程师手写reg_model类定义每个寄存器的uvm_reg_field。上线前最后一轮测试发现BAR0配置失败。查了两天发现reg_model里BAR0的地址是32h0000_0010而Spec里是32h0000_0008——写反了。修复很简单但影响是所有基于BAR0的testcase都无效需要重跑耽误了交付窗口。根源在于寄存器定义Spec、RTL实现reg_file.sv、验证模型reg_model.sv三者之间没有单一可信源。Spec是Word/PDFRTL是SV代码reg_model是SV代码三者靠人工对齐必然出错。4.2 核心方案用openpyxl读取Excel寄存器表自动生成reg_model我们强制规定寄存器定义的唯一可信源是regs_spec.xlsx。Sheet名为REG_MAP结构如下ADDR_OFFSETREG_NAMEWIDTHRESET_VALACCESSDESCRIPTIONFIELD_NAMEBIT_HIGHBIT_LOWFIELD_DESC0x00CTRL3232h0000_0000RWControl RegisterEN00Enable bit0x00CTRL3232h0000_0000RWControl RegisterMODE31Mode select0x04STATUS3232h0000_0000ROStatus RegisterFULL00FIFO full flaggen_reg_model.py读取此表生成my_ip_reg_block.svfrom openpyxl import load_workbook def generate_reg_block(xlsx_path, block_name): wb load_workbook(xlsx_path) ws wb[REG_MAP] # 按ADDR_OFFSET分组每个地址一个寄存器 regs {} for row in ws.iter_rows(min_row2, values_onlyTrue): if row[0] is None: # 跳过空行 continue addr row[0] if addr not in regs: regs[addr] { name: row[1], width: row[2], reset: row[3], access: row[4], desc: row[5], fields: [] } # 添加field if row[6]: # FIELD_NAME非空 regs[addr][fields].append({ name: row[6], hi: row[7], lo: row[8], desc: row[9] }) # 生成SV代码 lines [] lines.append(// Auto-generated by gen_reg_model.py) lines.append(class block_name _reg_block extends uvm_reg_block;) lines.append( uvm_object_utils( block_name _reg_block)) lines.append() # 声明寄存器变量 for addr, reg in regs.items(): lines.append(f rand {reg[name].lower()}_reg_t {reg[name].lower()};) lines.append() lines.append( virtual function void build();) lines.append( super.build();) # 创建寄存器 for addr, reg in regs.items(): lines.append(f {reg[name].lower()} {reg[name].lower()}_reg_t::type_id::create(\{reg[name].lower()}\);) lines.append(f {reg[name].lower()}.configure(this, null, \\);) lines.append(f {reg[name].lower()}.build();) lines.append(f add_reg({reg[name].lower()}, h{addr:x}, \{reg[access]}\);) lines.append( endfunction) lines.append() lines.append(endclass) # 生成每个寄存器类 for addr, reg in regs.items(): lines.append(fclass {reg[name].lower()}_reg_t extends uvm_reg;) lines.append(f uvm_object_utils({reg[name].lower()}_reg_t)) lines.append() # 声明fields for f in reg[fields]: lines.append(f rand uvm_reg_field {f[name].lower()};) lines.append() lines.append( virtual function void build();) lines.append( super.build();) for f in reg[fields]: lines.append(f {f[name].lower()} uvm_reg_field::type_id::create(\{f[name].lower()}\);) lines.append(f {f[name].lower()}.configure(this, {f[hi]}-{f[lo]}1, {f[lo]}, \{reg[access]}\, 0, h{reg[reset] ((1({f[hi]}-{f[lo]}1))-1):x}, 0, 1, 0);) lines.append( endfunction) lines.append() lines.append(endclass) return \n.join(lines) # 主函数 if __name__ __main__: code generate_reg_block(regs_spec.xlsx, my_ip) with open(my_ip_reg_block.sv, w) as f: f.write(code) print(Reg model generated!)注意openpyxl在这里不是“读Excel”而是建立寄存器定义的权威数据管道。Spec工程师改Excelgen_reg_model.py一跑RTL团队拿到新的reg_model.sv验证团队立刻能用RTL团队也能据此检查自己的reg_file.sv是否匹配。数据源头的统一比任何流程规范都有效。4.3 进阶用openpyxl离线生成测试用例矩阵寄存器验证不仅要测单个寄存器读写更要测组合场景。比如CTRL.EN1时STATUS.FULL才有效。手写testcase太慢。我们用第二张SheetTEST_MATRIXTEST_IDREG_WRITE_SEQREG_READ_SEQEXPECTED_BEHAVIORCOMMENTT001CTRL0x1, STATUS0x0STATUSSTATUS.FULL0Reset stateT002CTRL0x1STATUSSTATUS.FULL1 (after FIFO fill)Functional checkgen_testcase.py读取此表生成UVM testcasedef generate_testcase(test_row): test_id test_row[TEST_ID] write_seq test_row[REG_WRITE_SEQ] # 如 CTRL0x1, STATUS0x0 read_seq test_row[REG_READ_SEQ] # 如 STATUS # 解析write_seq writes [] for item in write_seq.split(,): reg_val item.strip().split() if len(reg_val) 2: reg_name reg_val[0].strip() val reg_val[1].strip() writes.append((reg_name, val)) # 生成testcase lines [] lines.append(fclass {test_id}_test extends base_test;) lines.append( uvm_component_utils( test_id _test)) lines.append() lines.append( virtual task run_phase(uvm_phase phase);) lines.append( super.run_phase(phase);) lines.append( phase.raise_objection(this);) # 执行写操作 for reg_name, val in writes: lines.append(f uvm_config_db#(int)::set(null, \*\, \{reg_name}_val\, {val});) # 启动sequence lines.append( my_sequence seq my_sequence::type_id::create(\seq\);) lines.append( seq.start(m_sequencer);) lines.append( phase.drop_objection(this);) lines.append( endtask) lines.append(endclass) return \n.join(lines)实操心得openpyxl离线安装pip install openpyxl-3.1.2-py3-none-any.whl在无网的服务器上很关键。但更重要的是Excel不是玩具是工程数据的载体。我们给regs_spec.xlsx加了密码保护设置了编辑权限所有修改留痕。一个看似简单的Excel成了整个寄存器验证流程的“宪法”。5. 基于SystemVerilog bind语法的动态验证注入框架让覆盖率收集不侵入RTL5.1 传统覆盖率收集的困境改RTL还是改验证覆盖率收集传统做法是在RTL里加covergroup。比如在dma_ctrl.sv里covergroup cg_dma_status (posedge clk); option.per_instance 1; coverpoint status { bins idle {IDLE}; bins busy {BUSY}; bins done {DONE}; } endgroup问题来了covergroup是RTL的一部分属于设计代码。验证团队想加一个新coverpoint必须提CR给设计团队走代码评审、回归测试流程周期长。更糟的是covergroup会影响综合即使ifdef保护也可能被误开。另一种做法在testbench里用$cover系统函数但需要手动在每个敏感点插入易漏且与RTL耦合深。5.2 破局用bind语法把覆盖率收集器“挂”在DUT上零侵入bind语法允许在不修改原模块的情况下将新模块“绑定”到其作用域。我们创建一个通用的coverage_collector.sv// coverage_collector.sv class coverage_collector extends uvm_component; uvm_component_utils(coverage_collector) covergroup cg_status (posedge clk); option.per_instance 1; coverpoint status { bins idle {IDLE}; bins busy {BUSY}; bins done {DONE}; } endgroup function new(string name, uvm_component parent); super.new(name, parent); cg_status new(); endfunction virtual function void build_phase(uvm_phase phase); super.build_phase(phase); // 从config_db获取DUT的handle if (!uvm_config_db::get(this, *, dut_handle, dut_h)) begin uvm_fatal(NO_DUT, DUT handle not found in config_db) end endfunction virtual task run_phase(uvm_phase phase); forever (posedge dut_h.clk) begin cg_status.sample(); end endtask endclass然后在tb_top.sv里用bind将其注入// tb_top.sv module tb_top; import uvm_pkg::*; include uvm_macros.svh dut uut (.clk(clk), .rst_n(rst_n), ...); // 动态绑定覆盖率收集器 bind dut coverage_collector u_cov_collector ( .clk(uut.clk), .status(uut.status) ); initial begin uvm_config_db::set(null, u_cov_collector, dut_handle, uut); end endmodule关键点bind语句中的dut是被绑定的模块名即uut的类型dutcoverage_collector是待绑定的类u_cov_collector是实例名。验证代码完全独立于RTLRTL工程师甚至不知道它的存在。5.3 集成脚本赋能gen_bind_script.py——从Excel自动生成bind语句bind语句虽短但当DUT有10个子模块每个都需要不同collector时手写易错。我们用bind_config.xlsx管理MODULE_TYPECOLLECTOR_CLASSINSTANCE_NAMECLK_SIGNALSTATUS_SIGNALENABLEdma_ctrldma_coverage_collectoru_dma_covuut.clkuut.status1scalerscaler_coverage_collectoru_scaler_covuut.pix_clkuut.state

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑