首页 / 资讯中心 / 文章详情

数字后仿核心原理与工业级实操指南

数字后仿核心原理与工业级实操指南 ★ FEATURED ARTICLE
1. 项目概述数字后仿不是“跑个波形”那么简单“数字后仿的流程”这六个字乍看平平无奇像教科书目录里的一行小字。但在我带过的二十多个数字IC项目里几乎每个流片失败的根因回溯最后都卡在后仿这个环节——不是没做而是做得不全、不深、不闭环。它绝不是前端RTL仿真通过后把网表和SDF文件往仿真器里一扔、点个run就完事的“过场戏”。真正的数字后仿是芯片物理实现与逻辑功能之间最后一道、也是最严苛的校验门。它要回答的问题非常具体当信号真正走过硅片上几微米宽的金属线、经过几十皮秒的RC延迟、被工艺角拉扯变形、被电源噪声扰动之后我的设计还能不能在100MHz下稳定采样计数器会不会在某个特定时序路径上多跳一拍FIFO会不会因为跨时钟域握手信号的毛刺而悄悄丢数据这些都不是RTL仿真能覆盖的。核心关键词“数字”“后仿”“SDF”“netlist”“Verilog”已经勾勒出它的技术骨架以综合/布局布线生成的门级网表netlist为输入注入从物理实现中提取的精确时序信息SDF在数字仿真器如VCS、Xcelium、Questa中进行带反标时序的逻辑功能验证。它不像前端仿真那样追求代码覆盖率而是死磕“时序-功能”的耦合效应。适合谁数字前端工程师必须懂它来理解时序收敛边界后端工程师靠它验证ECO修改是否引入新bug验证工程师用它补全UVM环境无法覆盖的物理层corner case而流片前的Sign-off评审后仿报告就是那张决定芯片命运的“体检单”。我见过太多团队把后仿当成形式主义结果tape-out后发现一个异步复位释放时序违例导致系统启动必死返工代价远超前期多花两周做完整后仿。2. 数字后仿的整体设计思路与方案选型逻辑2.1 为什么必须做后仿——从“理想逻辑”到“真实硅片”的鸿沟前端RTL仿真是基于理想时序模型的所有信号变化瞬间完成没有传播延迟没有线负载没有工艺偏差。这就像用CAD画好一张精密齿轮图纸但没考虑钢材热胀冷缩、机床加工误差、润滑油膜厚度变化。数字后仿要填的就是这张图纸和真实齿轮之间的鸿沟。这个鸿沟主要体现在三个维度第一是时序精度断层。RTL中一个assign a b c;语句在综合后可能变成一个由4个NAND门串联的组合逻辑链每级门有0.15ns的固有延迟连线还有0.08ns的RC延迟。如果前端只关心功能这个0.52ns的总延迟会被忽略但后仿必须把它标定出来否则在1.2GHz主频下这个路径就直接成了关键路径违例。SDF文件正是这个精度的载体它把每个门、每段线的延迟值按工艺角ff/ss/tt、温度-40℃/25℃/125℃、电压0.95V/1.0V/1.05V三维参数化地记录下来。第二是结构失真。RTL代码里一个always (posedge clk)块在综合后可能被拆解成多个寄存器簇中间插入了时钟门控单元Clock Gating Cell、扫描链多路器Scan Mux。这些结构在RTL里是不可见的但它们会改变信号到达寄存器D端的实际时间点。比如一个本该在clk上升沿采样的数据因为时钟门控单元引入了额外0.3ns延迟导致实际采样发生在clk上升沿之后0.3ns如果此时数据刚好在建立时间窗口边缘翻转就会发生亚稳态。后仿的网表netlist里包含了这些真实单元才能暴露这种结构级风险。第三是环境扰动建模缺失。RTL仿真默认电源稳定、地平面完美、信号无串扰。但真实芯片里当CPU集群突然全速运行电源网络压降IR Drop可达50mV导致附近模块的门延迟增加3%相邻高速SerDes通道的开关噪声SSN会耦合到ADC参考电压线上让采样阈值漂移。虽然完整电源完整性仿真PI和信号完整性仿真SI通常独立进行但后仿至少要集成最基础的电源噪声模型如通过SDF的TIMESCALE和DELAY注释模拟电压敏感延迟否则对低功耗设计的唤醒时序验证就是空中楼阁。提示很多新人误以为“综合后仿真”就是后仿这是致命误区。综合后仿真Synthesis Simulation用的是综合工具生成的门级网表但延迟是理想值或估算值如set_propagated_clock后的虚拟延迟没有SDF反标它只能验证综合是否改变了功能不能验证时序收敛。2.2 后仿流程的四大支柱网表、SDF、测试激励、仿真器一个健壮的后仿流程必须围绕四个核心要素构建闭环缺一不可网表Netlist这是后仿的“骨骼”。它必须是最终用于布局布线Place Route的同一份网表且需包含所有物理实现插入的单元。常见陷阱是用了综合网表而非PR网表——综合网表缺少填充单元Filler Cell、天线修复二极管Antenna Diode、ECO缓冲器ECO Buffer这些单元虽不改功能但会改变线负载和驱动能力影响时序。我曾遇到一个案例综合网表后仿通过但PR网表后仿在某条reset_n释放路径上出现setup违例原因是PR插入的filler cell增加了reset_n网络的电容负载使驱动单元输出延迟增大了0.18ns恰好踩在时序悬崖边上。SDFStandard Delay Format这是后仿的“血液”负责给骨骼注入生命。SDF文件本质是一个文本数据库用层次化结构描述每个实例instance的延迟。关键字段包括TIMESCALE 1ps定义时间单位必须与仿真器设置一致否则延迟值会错乱1000倍(CELLDELAYS (CELLTYPE AND2X1) (INSTANCE u_top/u_core/u_alu/u_and1) (DELAY (ABSOLUTE (IOPATH A Z (0.123 : 0.123 : 0.123) (0.145 : 0.145 : 0.145))))这里(0.123 : 0.123 : 0.123)是ff工艺角下的延迟(0.145 : 0.145 : 0.145)是ss工艺角下的延迟。注意SDF中的延迟是“绝对延迟”Absolute Delay即从输入引脚变化到输出引脚变化的总时间而非增量延迟Incremental Delay。这意味着仿真器必须能正确解析并应用这些值。测试激励Testbench这是后仿的“大脑”。它不能简单复用RTL测试激励必须做三重升级一是时钟源必须用真实PLL模型而非理想initial begin #10 forever #10 clk ~clk;因为PLL的抖动Jitter和相位噪声会直接影响时序裕量二是复位序列必须包含电源上电时序Power-on Reset Timing例如VDDA先于VDDD上电5ms否则模拟模块可能在数字模块未初始化时就输出无效数据三是激励频率必须覆盖芯片标称最高频及降频点如1.2GHz/800MHz/400MHz因为不同频率下时钟树skew和线延迟占比不同违例点会迁移。仿真器Digital Simulator这是后仿的“手术台”。VCS、Xcelium、Questa是三大主力。选型逻辑很务实VCS在大规模网表10M门上编译快、内存占用低适合回归测试Xcelium在调试体验上更优波形查看器支持SDF延迟着色Delay Coloring一眼就能看出哪段路径延迟最大Questa的UVM后仿集成度最高能无缝复用前端验证平台。但无论选谁都必须启用-sdf_cmd选项加载SDF并设置sdfverbose打开SDF解析日志——我亲眼见过一个项目因SDF路径写错仿真器静默跳过SDF加载全程用零延迟跑报告全是绿色结果流片后功能紊乱。2.3 流程设计的两大黄金原则分阶段验证与正交覆盖把后仿当成一个“大而全”的单次任务是效率最低的做法。我坚持采用“分阶段、正交覆盖”的双轨策略分阶段验证将后仿拆解为三个递进阶段每个阶段目标明确、资源可控。阶段一功能等价性验证Functional Equivalence。仅加载最小SDF如tt工艺角、25℃关闭所有时序检查notimingchecks目标是确认PR网表与RTL功能完全一致。这一步能快速捕获网表生成错误如综合工具bug导致的逻辑优化错误、ECO patch遗漏。我们曾用此法在2小时内定位到一个因Innovus版本升级导致的扫描链多路器插入错误避免了后续所有时序验证的浪费。阶段二时序签核验证Timing Sign-off。加载全角SDFff/ss/tt 温度 电压启用严格时序检查check_timing运行全速激励。这是最耗时的阶段但必须覆盖所有关键场景正常工作模式、低功耗模式clock gating开启、边界条件如FIFO半满时读写并发。重点不是“有没有违例”而是“违例是否可接受”。例如一个在ss工艺角下0.05ns的setup违例若对应路径是debug接口且永不启用可标记为waive但若在ff工艺角下也出现hold违例则必须修正。阶段三物理感知验证Physical-Aware Verification。引入更精细的模型如用sdfmax指定最大延迟路径分析或用sdfmin分析最小延迟路径用于hold检查。还可集成简易电源噪声模型通过SDF的CONDITIONAL语句模拟电压跌落对关键路径的影响。这一阶段已接近流片前的终极考验。正交覆盖确保验证空间被无死角扫描。我常用一个3x3矩阵来规划激励类型工艺角电压/温度全速功能激励ff1.05V/25℃边界压力激励ss0.95V/125℃低功耗状态激励tt1.0V/-40℃这个矩阵强制覆盖了最乐观、最悲观、最典型三种物理场景比随机跑几个SDF组合可靠得多。每次回归我都用脚本自动生成这9个case的运行列表确保不漏不重。3. 核心细节解析与实操要点从SDF加载到波形调试3.1 SDF文件的生成、校验与加载别让“假数据”毁掉整个流程SDF是后仿的生命线但它的生成和加载过程充满暗礁。我总结出一套“三查一验”法确保SDF真实可信。第一查生成源头是否权威。SDF必须由布局布线工具如Innovus、ICC2在完成最终版图Final GDS后用write_sdf命令导出。严禁使用综合工具Design Compiler生成的SDF因为综合阶段没有布线信息其线延迟Wire Delay是估算值如基于wireload model误差常达30%-50%。Innovus生成SDF的标准命令是# 在Innovus中执行 set_app_var sdf_write_version 3.0 set_app_var sdf_write_annotate true set_app_var sdf_write_include_cell_delay true set_app_var sdf_write_include_net_delay true set_app_var sdf_write_include_port_delay false write_sdf -version 3.0 -context verilog -output ./sdf/top_ff.sdf -corner ff关键参数解读-version 3.0确保兼容主流仿真器-context verilog指定SDF用于Verilog仿真-corner ff明确工艺角。set_app_var sdf_write_include_net_delay true是核心它告诉工具必须包含布线后的实际线延迟而非仅门延迟。第二查SDF内容是否完整。拿到SDF文件后绝不直接导入仿真器。先用grep做三道过滤grep -c CELLDELAYS top_ff.sdf确认CELLDELAYS段存在数量应与网表中门电路总数基本一致允许±5%因部分单元如filler无延迟grep -c INTERCONNECT top_ff.sdf确认INTERCONNECT段存在这是线延迟的标志数量应远大于CELLDELAYS一条线可能连接多个门head -20 top_ff.sdf | grep TIMESCALE确认TIMESCALE为1ps若为1ns则所有延迟值小1000倍后仿必然失败。我曾因跳过第二查用了一个不含INTERCONNECT的SDF因Innovus设置错误导致后仿波形看起来完美但所有线延迟为零实际芯片在长走线路径上严重违例。第三查SDF与网表的实例名是否严格匹配。SDF中每个INSTANCE的路径名必须与网表中module和instance的层次化命名完全一致包括大小写和下划线。常见坑是网表中顶层模块叫top_moduleSDF中写成TOP_MODULE或网表中子模块实例名为u_dutSDF中为u_DUT。仿真器加载时不会报错但会静默忽略所有不匹配的延迟相当于没加SDF。解决方案是用脚本预处理sed -i s/u_DUT/u_dut/g top_ff.sdf批量修正。一验SDF加载是否成功。在仿真器启动日志中必须看到类似SDF: Loading file ./sdf/top_ff.sdf和SDF: Loaded 124567 delays for 89234 instances的确认行。更保险的做法是在testbench中插入一个“延迟探针”// 在testbench顶层添加 initial begin $display(INFO: SDF loaded, checking delay on u_top.u_core.clk_buf.Z); // 此处可调用仿真器API读取特定引脚延迟但更简单的是观察波形 end然后在波形中放大观察一个已知长路径如u_top.u_core.clk_buf.Z的输出延迟对比SDF中该实例的IOPATH值若实测延迟与SDF标注值偏差5%则加载成功。注意SDF加载失败的静默性是最大风险。我建议在所有后仿脚本中加入-sdfverbose选项并用grep SDF.*error\|SDF.*warning sim.log自动检查日志任何非零匹配都触发告警。3.2 网表的准备与精简如何让百万门级后仿不卡死面对千万门级SoC直接用全芯片网表做后仿编译时间可能超24小时内存占用破100GB根本不可持续。我的经验是“三砍一保”砍掉无关模块用-top选项指定顶层但更重要的是在网表中删除与当前验证目标无关的IP。例如验证CPU子系统时可安全删除GPU、Display Controller、PCIe PHY的网表实例。方法是在网表文件中搜索module gpu_top将其从module top的实例化列表中整段删除并移除对应的module gpu_top定义。注意必须同步删除其所有端口连接否则网表语法错误。砍掉冗余逻辑综合工具常插入大量调试逻辑如JTAG TAP控制器、内部扫描链Scan Chain的旁路多路器。这些逻辑在功能上无用却占大量门数。用sed命令批量删除# 删除所有以scan_开头的实例化语句 sed -i /scan_/d top_netlist.v # 删除所有以SCAN_开头的module定义 sed -i /^module SCAN_/,\$d top_netlist.v砍掉未用端口网表中常有大量未连接的IO端口如pad_vddaux,pad_jtag_tdo。这些端口在SDF中仍有延迟模型徒增计算负担。用脚本遍历网表找出所有wire声明后从未在assign或instance中使用的信号将其连同module端口声明一并删除。一保关键时序路径必须保留。以上所有“砍”操作都必须以不破坏关键时序路径为前提。我用一个简单规则判断保留所有时钟域clock domain的根节点如clk_cpu,clk_axi及其扇出路径上的所有寄存器和组合逻辑。可用report_timing -from [get_pins clk_cpu] -to [all_fanout -flat -endpoints_only]在Innovus中先导出关键路径列表再对照网表确保这些路径完整。实测效果一个1200万门的AI加速器网表经此精简后门数降至380万编译时间从18小时缩短至2.3小时内存峰值从92GB降至28GB而关键路径覆盖率保持100%。精简不是偷懒而是让有限的算力聚焦在刀刃上。3.3 测试激励的改造从“功能正确”到“时序鲁棒”RTL测试激励的目标是“功能覆盖”后仿激励的目标是“时序鲁棒”。改造的核心是注入物理世界的不确定性。时钟模型升级将理想方波时钟替换为带抖动的PLL模型。一个实用的Verilog PLL模型如下// pll_model.v module pll_model ( input wire rst_n, input wire ref_clk, output reg clk_out ); real jitter_ps 0.0; // 基础抖动单位ps real period_ps 833.33; // 1.2GHz周期833.33ps real phase_error_ps 0.0; always (posedge ref_clk or negedge rst_n) begin if (!rst_n) begin clk_out 0; phase_error_ps 0.0; end else begin // 模拟PLL相位噪声高斯随机抖动 jitter_ps $random % 100 - 50; // ±50ps抖动 phase_error_ps phase_error_ps * 0.99 jitter_ps * 0.01; // 一阶滤波 // 计算下一个边沿时间 real next_edge $realtime period_ps phase_error_ps; // 等待到该时间点 #(next_edge - $realtime); clk_out ~clk_out; end end endmodule此模型引入了±50ps的随机抖动和一阶相位噪声滤波比固定周期更贴近真实PLL。在testbench中实例化它替代原来的initial时钟生成块。复位序列增强增加电源上电时序。真实芯片中模拟电源VDDA必须先于数字电源VDDD稳定否则ADC会输出乱码。在testbench中添加// power_on_sequence.v initial begin vdda_stable 0; vddd_stable 0; // VDDA先上电 #(10000); // 10us vdda_stable 1; // 等待VDDA稳定后VDDD再上电 #(5000); // 5us vddd_stable 1; // 复位信号在VDDD稳定后释放 #(1000); // 1us rst_n 1; end并在DUT的电源端口上添加assign vdda vdda_stable ? 1b1 : 1bz;让DUT感知电源状态。激励频率动态切换不再只跑单一频率。用一个frequency_controller模块在仿真中动态切换时钟频率// frequency_controller.v module frequency_controller ( input wire clk_ref, input wire [1:0] freq_sel, // 001.2G, 01800M, 10400M, 11200M output reg clk_out ); reg [31:0] counter; localparam FREQ_1P2G 32d41666666; // 1.2G周期0.833ns - counter max localparam FREQ_800M 32d62500000; // 800M周期1.25ns localparam FREQ_400M 32d125000000; // 400M周期2.5ns always (posedge clk_ref) begin case (freq_sel) 2b00: counter (counter FREQ_1P2G) ? 0 : counter 1; 2b01: counter (counter FREQ_800M) ? 0 : counter 1; 2b10: counter (counter FREQ_400M) ? 0 : counter 1; default: counter 0; endcase clk_out (counter 0) ? ~clk_out : clk_out; end endmodule这样一个testbench就能覆盖全频段无需为每个频率单独写激励。4. 实操过程与核心环节实现从零搭建一个可运行的后仿环境4.1 环境搭建VCS为例的完整配置清单以Synopsys VCS为仿真器搭建一个工业级后仿环境。这不是简单的vcs -sverilog top.v而是一套标准化的Makefile驱动流程。第一步创建项目目录结构project/ ├── src/ # RTL源码只读供参考 ├── netlist/ # PR生成的门级网表 │ ├── top_netlist.v │ └── macros/ # IP硬宏网表 ├── sdf/ # SDF时序文件 │ ├── top_ff.sdf │ ├── top_ss.sdf │ └── top_tt.sdf ├── tb/ # 测试激励 │ ├── tb_top.sv │ ├── pll_model.sv │ └── power_seq.sv ├── scripts/ # 自动化脚本 │ ├── compile.tcl # VCS编译脚本 │ ├── sim.tcl # 仿真运行脚本 │ └── run_all.sh # 一键运行脚本 └── logs/ # 日志输出第二步编写VCS编译脚本compile.tcl// scripts/compile.tcl // 编译选项兼顾速度与调试 defineVCS incdir./tb -sverilog -f ./scripts/filelist.f // 文件列表按依赖顺序排列 // 关键SDF加载选项 -sdf_cmd ./scripts/sdf_load.cmd // 优化选项 vclicfull vclicfast // 调试选项必须开启 dumpvarsall fsdball // 内存优化 memmax10g // 时间精度 timescale1ps/1ps第三步编写SDF加载命令文件sdf_load.cmd// scripts/sdf_load.cmd // 为每个SDF文件指定加载方式和作用范围 -sdf_cmd_file ./sdf/top_ff.sdf -sdf_corner ff -sdf_max 1.05 -sdf_min 0.95 -sdf_temp 25 -sdf_cmd_file ./sdf/top_ss.sdf -sdf_corner ss -sdf_max 0.95 -sdf_min 0.95 -sdf_temp 125 -sdf_cmd_file ./sdf/top_tt.sdf -sdf_corner tt -sdf_max 1.0 -sdf_min 1.0 -sdf_temp 25 // 关键指定SDF应用于哪个顶层模块 -sdf_top top第四步编写文件列表filelist.f// scripts/filelist.f // 严格按实例化层次排序先宏再网表最后testbench ./netlist/macros/ahb_bus.v ./netlist/macros/axi_dma.v ./netlist/top_netlist.v ./tb/pll_model.sv ./tb/power_seq.sv ./tb/tb_top.sv第五步编写一键运行脚本run_all.sh#!/bin/bash # scripts/run_all.sh # 参数corner (ff/ss/tt), freq (1200/800/400) CORNER${1:-ff} FREQ${2:-1200} echo Starting post-sim for corner$CORNER, freq$FREQ MHz # 清理旧编译 rm -rf simv* csrc/ *.log *.fsdb # 编译 vcs -f ./scripts/filelist.f -f ./scripts/compile.tcl -sdf_cmd ./scripts/sdf_load_${CORNER}.cmd -o simv_${CORNER}_${FREQ} compile_${CORNER}_${FREQ}.log 21 if [ $? -ne 0 ]; then echo ERROR: Compile failed for $CORNER $FREQ exit 1 fi # 运行仿真 ./simv_${CORNER}_${FREQ} sdfmax sdfmin notimingchecks fsdball vcdall trace UVM_VERBOSITYUVM_LOW sim_${CORNER}_${FREQ}.log 21 if [ $? -ne 0 ]; then echo ERROR: Simulation failed for $CORNER $FREQ exit 1 fi echo SUCCESS: Post-sim completed for $CORNER $FREQ第六步执行与监控# 运行ff工艺角、1.2GHz ./scripts/run_all.sh ff 1200 # 运行ss工艺角、125℃高温 ./scripts/run_all.sh ss 1200关键监控点compile.log中必须有SDF: Loaded XXX delayssim.log中必须有UVM_INFO ... [UVM]表示UVM框架启动且无SDF.*error字样。波形文件.fsdb大小应在100MB以上过小说明dumpvars未生效。4.2 关键环节实现如何精准定位一个时序违例后仿报告里一堆红色的setup/hold违例新手常手足无措。我的方法是“三步定位法”直击根因。第一步从报告抓关键路径。VCS的simv -gui启动后在Tcl Console中运行# 加载违例报告 read_saif -input ./saif/report.saif # 查看最差setup违例 report_timing -delay_type min_max -path_type setup -max_paths 1 -significant_digits 3报告会输出类似Startpoint: u_top.u_core.u_cpu.clk_gen.u_pll.clk_out (rising edge) Endpoint: u_top.u_core.u_cpu.u_regfile.reg[0].q (rising edge) Path Group: clk_cpu Path Type: max Delay: 832.15ps (data required time) ...记下Startpoint和Endpoint这是我们的猎物。第二步在波形中放大观察。在Verdi或SimVision中加载.fsdb波形搜索u_top.u_core.u_cpu.u_regfile.reg[0].q找到其D端输入信号通常是u_top.u_core.u_cpu.u_regfile.reg[0].d。放大到皮秒级观察clk_out上升沿时刻T_clkreg[0].d数据稳定时刻T_data_validreg[0].d数据变化时刻T_data_change计算T_data_valid - T_clk应 建立时间setup timeT_clk - T_data_change应 保持时间hold time。若不满足违例成立。第三步逆向追踪信号源。右键点击reg[0].d信号选择“Trace Source”工具会自动高亮其上游所有逻辑。重点看是否有长组合逻辑链如a b c d e这往往是关键路径是否有跨时钟域信号如clk_cpu域的信号未经同步器直接进入clk_axi域这是hold违例高发区是否有时钟门控单元其使能信号的毛刺可能导致时钟脉冲丢失。我曾定位到一个顽固的hold违例reg[0].d信号来自一个always (posedge clk_cpu)块但其输入data_in竟然是always (negedge clk_cpu)块的输出。负边沿采样在正边沿触发的寄存器上天然存在半个周期的保持时间缺口。解决方案是在data_in路径上插入两级同步器或重构逻辑统一用正边沿。实操心得不要迷信自动报告。我习惯在违例点附近手动添加$display打印// 在违例寄存器前添加 always (posedge clk_cpu) begin if (rst_n) begin reg_q 0; end else begin $display(TIME%0t: data_in%b, clk_cpu%b, $time, data_in, clk_cpu); reg_q data_in; end end这比波形更直观尤其对亚稳态问题$display能捕捉到data_in在clk_cpu边沿附近的不定态x值。4.3 从零开始的全流程演示一个8位RISC-V CPU的后仿以一个开源的8位RISC-V CPU如PicoRV32简化版为例走一遍完整后仿流程所有步骤均可在个人电脑上复现。Step 1获取并准备网表从GitHub克隆PicoRV32用Yosys综合yosys -p read_verilog picorv32.v; synth_ice40 -top picorv32; write_verilog -noattr picorv32_netlist.v用NextPNR布局布线生成SDF需安装SDF插件nextpnr-ice40 --json picorv32.json --pcf picorv32.pcf --asc picorv32.asc --sdf picorv32.sdfStep 2编写精简testbench// tb_picorv32.sv module tb_picorv32; logic clk, rst_n; logic [31:0] mem_addr; logic [7:0] mem_data; logic mem_we; // 时钟10MHz带±1ns抖动 initial begin clk 0; forever begin real jitter ($random % 2000 - 1000) / 1000.0; // ±1ns #(50.0 jitter) clk ~clk; end end // 复位 initial begin rst_n 0; #(100) rst_n 1; end // DUT实例化 picorv32 #( .IMEM_SIZE(1024), .DMEM_SIZE(1024) ) dut ( .clk(clk), .rst_n(rst_n), .mem_addr(mem_addr), .mem_data(mem_data), .mem_we(mem_we) ); // 内存模型简化 reg [7:0] mem [0:1023]; always (posedge clk) begin if (mem_we mem_addr 1024) mem[mem_addr] mem_data; end // 激励写入一条指令运行 initial begin // 写入addi x1, x0, 1 mem[0] 8h13; mem[1] 8h00; mem[2] 8h00; mem[3] 8h00; // 等待足够周期 repeat (100) (posedge clk); $finish; end endmoduleStep 3VCS编译与运行# 创建filelist.f echo ./picorv32_netlist.v filelist.f echo ./
阅读完成 · 觉得有帮助?
咨询建站