ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

高云FPGA仿真全流程:ModelSim Testbench编写与功能时序分析实战

高云FPGA仿真全流程:ModelSim Testbench编写与功能时序分析实战 1. 高云FPGA设计的“盲飞”问题仿真是第一道防线做FPGA开发这几年我最大的感触就是代码写得再漂亮不上板验证都是纸上谈兵但盲目上板验证比不验证还坑。尤其是用高云FPGA做项目时很多刚转过来的同行还带着单片机开发的习惯——写完代码直接综合、布线、下板一个LED没亮就拿着逻辑分析仪疯狂抓信号。这种路子不是不行但效率太低了而且有些问题你根本没法用示波器或者逻辑分析仪去抓比如内部一个跨时钟域的亚稳态、一个组合逻辑的毛刺、一个状态机跑飞之后的自恢复行为这些全靠上板去摸运气不好能折腾你一个星期。我最早接触高云FPGA是从GW1N系列开始的那时候项目急没怎么把仿真当回事觉得多一事不如少一事结果被现实狠狠教育了一课一个SPI从机接口的时序代码层面怎么看怎么对上板就是读不到正确数据。后来老老实实写了Testbench建模了主机时序一仿真就发现片选信号提前了半个时钟周期拉高就是这一个时序小问题让整个通信链路完全废掉。从那以后我就形成了一个习惯任何模块哪怕简单到一个计数器也要写Testbench跑一遍功能仿真再进综合。这个习惯一直保持到现在也成了我带新人时强调的第一条铁律。对于高云FPGA这种国产FPGA平台很多人有个误区觉得它的小型化IDE工具不如国外大厂成熟仿真工具链肯定也麻烦。实际上高云的Gowin Design Suite对ModelSim的支持做得相当到位从工程创建到仿真脚本生成基本是无缝衔接。而且ModelSim作为业界最经典的HDL仿真器资料多、教程多、解决问题的社区帖子也多哪怕你之前完全没用过半天时间也能把基本流程跑通。这篇内容我就把自己的实操经验完整分享出来从高云工程怎么建、怎么和ModelSim联动到Testbench怎么写、功能仿真怎么跑、时序分析要看什么再到新手最容易踩的坑怎么排查一条线全部打通。文章适合三类人看一是刚接触高云FPGA、还在用上板调错硬刚的初学者二是会用ModelSim但没和Gowin Design Suite联调过、每次仿真还要手动复制粘贴文件的老手三是准备系统学习FPGA仿真方法论、想提升代码可验证性的进阶开发者。不管你是哪种照着这篇文章走一遍高云FPGA的仿真之路就能顺顺当当铺好。2. 环境准备Gowin Design Suite与ModelSim的版本匹配要点先说环境。高云FPGA的官方IDE叫做Gowin Design Suite不同版本对应的FPGA型号支持范围不一样但仿真相关的配置逻辑大同小异。ModelSim这边我用的是ModelSim SE 10.5这个版本在Windows 10 64位系统上跑得非常稳装好以后直接用很少出幺蛾子。2.1 高云工程创建流程打开Gowin Design Suite新建工程时选择对应的芯片型号。以我常用的GW1NR-9为例在Device一栏选好系列和封装之后IDE会自动生成一个初始工程结构。这里有个小细节要注意工程路径和文件名尽量不要带中文也不要带空格。很多人不以为然但ModelSim对路径中的中文字符支持极差仿真跑到一半报错File not found或者莫名其妙的加载失败八成都是路径问题。用拼音或者英文命名工程是最稳妥的。工程建好后在Designer页面添加你的Verilog或者VHDL源文件。高云IDE对Verilog-2001语法支持最好SystemVerilog的部分特性也支持但如果你要写比较复杂的接口协议仿真模型建议在ModelSim里单独维护Testbench文件不要过度依赖IDE的模板生成功能。2.2 ModelSim路径配置关这是高云IDE和ModelSim联动的关键但也是我最想吐槽的一个点——官方文档写得不够醒目很多人直接忽略了。在Gowin Design Suite的菜单栏找到Tools - Options - EDA Tools里面会有一栏ModelSim相关的设置需要手动指定ModelSim的安装路径比如C:\modeltech64_10.5和可执行文件名vsim.exe。如果你不填后面在IDE里点击Run Simulation就会提示找不到仿真器或者弹出一个空的命令行窗口一闪而过根本没有仿真界面出现。配好路径之后高云IDE就能自动调用ModelSim了。更妙的是IDE会自动生成一个仿真脚本把当前工程里的所有RTL源文件、约束文件和仿真宏定义全部整理好自动编译、自动加载、自动打开波形窗口。这个能力其实被很多人低估了后面我会细说它到底帮你省了哪些事。2.3 需要额外安装的库文件高云的FPGA IP核像PLL、DDR控制器、SerDes这些在仿真时需要对应的仿真模型库。Gowin Design Suite安装目录下自带这些库文件通常存放在simlib文件夹里里面有GW1N、GW1NR、GW2A等系列对应的仿真模型。ModelSim默认不认这些库需要先编译一次。具体操作是在ModelSim环境里新建一个库比如gowin_lib然后用vlog命令将高云提供的.v文件编译进去。高云官方也提供了批处理脚本你可以在simlib文件夹里看到类似compile_modelsim.bat的文件直接双击运行就能自动编译全部器件库。这个步骤只要做一次以后所有高云工程的仿真都能复用不需要重复折腾。提示编译库文件时如果报错先看看是不是ModelSim版本太老、对Verilog-2001某些语法支持不到位。我遇到过用ModelSim 10.1编译高云新版本PLL模型报语法错误的情况升级到10.5之后一切正常属于版本越新越省心的典型场景。3. 手写Testbench的核心套路时钟、复位与激励生成很多人写Testbench就是随便生成一个时钟、给几个信号赋值能跑出波形就算完事。但真正的Testbench要达到自动验证的效果而不只是人工盯着波形看。我写Testbench有一套固定的思路这里拆开来讲。3.1 时钟生成的几种写法最基础的时钟生成是always块翻转timescale 1ns / 1ps module tb_top; reg clk; initial clk 0; always #10 clk ~clk; // 20ns周期50MHz endmodule这种写法简单但有个问题如果你要仿真不同频率的时钟比如系统时钟100MHz、SPI时钟10MHz就要写多个always块。我习惯用参数封装timescale 1ns / 1ps module tb_top; parameter SYS_CLK_PERIOD 10; // 100MHz parameter SPI_CLK_PERIOD 100; // 10MHz reg sys_clk; reg spi_clk; initial sys_clk 0; always #(SYS_CLK_PERIOD/2) sys_clk ~sys_clk; initial spi_clk 0; always #(SPI_CLK_PERIOD/2) spi_clk ~spi_clk; endmodule还有一个高级技巧是用可变的时钟频率模拟时钟抖动或者上电稳定过程这种在验证时钟模块时非常有用initial begin clk 0; // 模拟前100ns频率不稳 repeat(5) begin #8 clk 1; #12 clk 0; end // 之后稳定在20ns周期 forever #10 clk ~clk; end3.2 复位的正确释放姿势复位信号是Testbench里最常见的信号但新手经常随手一写就出问题。典型错误是这样的initial begin rst_n 1; #100 rst_n 0; #100 rst_n 1; end这种写法的问题在于复位释放的时刻和时钟沿没有对齐。如果被测试模块是时序逻辑复位释放后第一个时钟沿什么时候到来是不确定的可能导致初始化状态不明确。正确做法是把复位释放对齐到时钟沿initial begin rst_n 0; // 在时钟上升沿同步释放复位 (posedge clk); rst_n 1; end或者用几个周期的延时确保复位完全生效initial begin rst_n 0; repeat(5) (posedge clk); rst_n 1; end这个细节直接影响仿真结果的可复现性。我见过有人因为复位释放时机不固定同一份代码仿真两次波形不一样最后排查了半天才发现是Testbench写得不规范。3.3 任务封装把激励写成人话当接口信号多、时序复杂时直接用initial块里一条条赋值代码会变得特别长且难维护。我的做法是用task封装接口操作比如一个SPI读取操作task spi_read; input [7:0] addr; output [7:0] data; begin cs_n 0; // 发送地址字节 for (int i 7; i 0; i--) begin mosi addr[i]; (posedge sclk); end // 读取数据字节 for (int i 7; i 0; i--) begin (posedge sclk); data[i] miso; end cs_n 1; #100; end endtask这样在initial主流程里调用就非常清晰initial begin // 初始化 rst_n 0; cs_n 1; #200; rst_n 1; // 读取寄存器0x01 spi_read(8h01, read_data); // 等待中断 wait(irq 1); // 仿真结束 #1000; $finish; end这种封装方式的优势在于激励逻辑和时序细节分离Testbench的可读性和可维护性大幅提升。我经常和团队说一句话Testbench是给别人看的也是给三个月后的自己看的写清楚比写炫技重要。3.4 仿真结束的优雅收尾很多新手Testbench里不写$finish结果波形窗口自己跳闸。其实仿真结束有两种经典方式// 方式一固定时间结束 initial begin #100000; $finish; end // 方式二等待某个条件满足后结束 initial begin wait(test_done 1); #1000; $finish; end方式二会更灵活尤其是做复杂功能验证时可以在Testbench里设置一个test_done标志位所有用例跑完后再结束仿真。另外$stop也是好用的调试指令它和$finish的区别是$stop暂停仿真但不退出方便你在暂停点查看波形、调试信号状态$finish则是彻底结束仿真进程。调试阶段多用$stop回归测试阶段用$finish。4. 高云IDE自动生成仿真工程别忽略这个省力功能接下来说高云IDE和ModelSim的核心联动操作。这里我强烈建议第一次配置好路径之后每次仿真都直接走IDE的自动流程不要手动去ModelSim里建工程。原因很简单——高云IDE自动生成的仿真工程包含了所有编译选项、库映射和优化参数比手动配置靠谱得多而且换电脑、换工程后照样一键跑通。4.1 一键调用ModelSim的全流程在高云IDE中工程编译通过之后点击菜单栏的Simulation - ModelSim - Run Behavioral SimulationIDE会自动做下面这些事扫描工程中的RTL源文件按依赖关系排序检查是否有例化了高云IP核自动映射之前编译好的gowin_lib仿真库查找顶层Testbench文件通常以tb_开头或已在工程中标记为仿真顶层生成一个ModelSim DO脚本包含vlib、vlog、vsim、add wave等全部命令打开ModelSim窗口自动运行脚本编译、加载、打开波形。整个过程一气呵成。但有一个前提条件容易踩坑Testbench文件必须手动拖入高云工程中而且要在工程配置里指定为仿真顶层。很多人忽略了这个步骤点了按钮发现ModelSim打开是空白的或者编译报错找不到顶层模块。具体操作是在高云IDE的Designer页面右键Testbench文件选择Set as Top Simulation Module。这样IDE才知道这个文件是拿来仿真的。如果工程里没有Testbench文件就先手动新建一个添加进去。4.2 DO脚本里发生了什么为了让大家心里有底我把高云IDE生成的DO脚本核心内容拆解一下。它的逻辑其实和我手动写脚本的逻辑完全一致只不过IDE帮你做好了模板化处理vlib work vlib gowin_lib vmap gowin_lib C:/gowin/lib/gowin_lib vlog src/top.v vlog src/pll.v vlog src/ip/pll/pll_sim.v vlog sim/tb_top.v vsim -t 1ns work.tb_top add wave -position end sim:/tb_top/* run -all看到没有本质就这几步建库、编译、仿真、开波形。熟悉了这套逻辑之后你完全可以在ModelSim里手动执行这些命令遇到自动流程出问题时也能快速定位是编译报错还是仿真库的问题。4.3 手动关联高云IP仿真库如果你用的是高云的PLL、DDR、SerDes这类复杂IP仿真时必须加载对应的仿真模型。高云Design Suite在生成IP时会在IP目录下同时生成一个*_sim.v文件比如pll_sim.v这个文件就是IP的仿真模型充当的是行为级替身。正常自动流程下IDE会把这个文件加入编译列表。但如果你手动建ModelSim工程经常漏掉这一步导致仿真时出现Module pll not found之类的错误。解决办法就是手动把这几个IP仿真文件加进编译列表vlog src/ip/pll/pll_sim.v vlog src/ip/ddr/ddr_sim.v这些小文件的命名规律一般是ip名称_sim.v在对应IP的生成目录下都能找到。5. 功能仿真实操跑通第一个完整波形并读懂它环境配好、Testbench写好接下来就是真正跑仿真了。这一节我以最经典的一个分频器为例从写代码到看波形完整走一遍把中间容易卡壳的地方全部标注出来。5.1 一个可用于演示的设计代码假设我们在高云FPGA里要做一个8分频器代码很简单module divider_8 ( input wire clk, input wire rst_n, output reg clk_out ); reg [2:0] cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 3d0; clk_out 1b0; end else begin if (cnt 3d7) begin cnt 3d0; clk_out ~clk_out; end else begin cnt cnt 1b1; end end end endmodule这代码本身没有难度重点在于Testbench怎么配timescale 1ns / 1ps module tb_divider_8; reg clk; reg rst_n; wire clk_out; // 生成10ns周期的时钟100MHz initial clk 0; always #5 clk ~clk; // 分频器实例化 divider_8 uut ( .clk (clk), .rst_n (rst_n), .clk_out (clk_out) ); // 激励流程 initial begin rst_n 0; #100; rst_n 1; // 观察200个时钟周期 #2000; $display([SUCCESS] Simulation finished.); $finish; end endmodule5.2 在ModelSim中的操作步骤直接用高云IDE的自动流程点击Run Behavioral Simulation后ModelSim窗口打开波形窗口里应该能看到clk、rst_n、cnt、clk_out四个信号。如果波形窗口里没有信号只需要在ModelSim命令行手动执行add wave -position end sim:/tb_divider_8/*这个命令把Testbench顶层下所有信号都加到波形窗口。为了看到内部信号比如分频器内部的cnt可以加到被测试模块的信号add wave -position end sim:/tb_divider_8/uut/*然后按F9或者执行run -all让仿真跑完。之后按CtrlShiftF或者用Zoom Full按钮把波形缩放到完整视图就能看到分频器的完整行为。5.3 波形怎么看才对很多人把波形跑出来就完事了盯着看一眼好像有波形就觉得功能正确。我举个例子说明怎么看波形才算真正看懂了。以上面这个8分频器为例正确的行为应该是cnt从0到7循环计数clk_out每8个时钟周期翻转一次因此输出频率是输入频率的1/16因为8分频之后又翻转一次。等等这里其实有个常见的分频器陷阱——很多人以为cnt计数到7就翻转一次是8分频但输出占空比不是50%。要得到真正的50%占空比8分频应该是计数器计数到3时拉高、计数到7时拉低总共8个周期完成一个完整输出周期。而上面代码的运行结果是cnt计满一次输出翻转一次一个完整输出周期需要16个输入时钟所以实际上是16分频。仿真跑出来之后你先看cnt是不是从0到7循环再看clk_out是不是每8个clk翻转一次然后计算一下输出周期。如果你心里预期是8分频但实测是16分频说明代码逻辑和需求不匹配——这正是仿真能帮你提前发现的问题。5.4 常见异常波形红线、高阻态、未知态仿真中最常见的异常就是信号显示为红色在ModelSim中红色表示未知态X或者高阻态Z。新手看到红色第一反应是代码写错了其实大多数情况下是Testbench没初始化好。红色信号常见有三种来源没有给输入端赋初值reg类型信号在仿真开始时默认是X态如果Testbench里没初始化就参与运算输出就是X。复位时序不对复位结束后第一个时钟沿时序逻辑采样到的输入仍然处于X态所以输出也变成X。解决办法是延长复位时间确保复位释放前所有输入都有确定值。仿真时间不够有些信号需要几个时钟周期才稳定仿真结束得太早信号还没来得及初始化。还有一个经常被忽略的情况wire类型信号如果没连接好在波形窗口里会保持高阻态Z蓝色虚线。这种情况通常意味着模块实例化的端口连接有误比如端口名写错、位宽不匹配等。遇到蓝色信号第一反应应该是检查实例化代码而不是盯着RTL逻辑排查。6. 时序分析进阶从功能仿真到时序收敛的五大关键点功能仿真通过了是不是就能直接下板答案是能跑但不保证稳定。功能仿真验证的是逻辑行为跟实际电路的工作时序还有一层巨大的差异——信号在FPGA内部的走线延迟、组合逻辑的传播延迟、时钟网络的偏斜这些物理特性在功能仿真里是看不到的。这也是很多人仿真全通过、上板跑飞的根本原因。6.1 功能仿真与时序仿真的本质区别功能仿真又称行为仿真它只关心信号之间的逻辑关系所有延时都默认为0除了timescale里声明的最小时间单位。而时序仿真会把布局布线后的实际延时信息反标到仿真模型中每条路径的建立时间、保持时间都要满足要求不满足就报时序违规。对于高云FPGAIDE里自带的静态时序分析STA工具是时序收敛的关键。在完成布局布线后点击Timing Analysis按钮工具会生成一个详细报告列出每个时钟域内的关键路径和裕量。6.2 建立时间与保持时间高速设计的隐形天花板时序分析的物理基础是两个约束建立时间Setup Time和保持时间Hold Time。我用一个生活化的类比来解释想象你在火车站台上车。列车门关闭前你必须站在门内建立时间在时钟沿之前数据必须准备好而且列车启动后你也不能在车门口犹豫太久否则车门夹着你保持时间时钟沿之后数据必须保持稳定一段时间。数字电路中数据信号必须在时钟沿之前到达触发器的数据端并保持一段时间否则触发器采到的值就是不确定的。FPGA布局布线工具会根据你的时钟约束来判断这些时间是否满足。如果建立时间不够通常的解决办法是在关键路径中间插入寄存器流水线或者优化组合逻辑减少级数如果保持时间不够通常出现在时钟频率较低但逻辑路径极短的场景反而不太常出现在低速设计中。6.3 时序约束不写约束就没法谈收敛高云FPGA的时序约束文件是.cst格式你需要在工程中创建并添加。最基础的约束是时钟约束// 100MHz时钟约束 create_clock -name clk -period 10.0 [get_ports {clk}]如果你的设计里有多个时钟域还要分别约束。上板跑飞的一个常见原因就是没有写时钟约束工具默认按理想时钟布线实际跑到高频时时序自然崩了。除了时钟约束还有输入延迟约束和输出延迟约束它们描述的是外部器件相对时钟沿的数据建立/保持要求。这部分内容比较深我建议初学者先掌握时钟约束等做外部接口比如SDRAM、ADC采样时再深入学习set_input_delay和set_output_delay。6.4 看时序报告的正确姿势高云IDE时序分析报告打开后你会看到很多字段Slack裕量、Data Path、Clock Path、Arrival Time、Required Time等。我建议只听三件事Slack是否为正值。Slack为负说明时序不满足负数越大问题越严重。关键路径出现在哪个模块。报告中会把路径经过的所有器件列出你能看到信号从哪个寄存器出发、经过哪些组合逻辑、到哪个寄存器结束。如果一条路径上的组合逻辑级数特别多这往往就是性能瓶颈。时钟频率是否有余量。即使Slack为正也要看余量是否足够。设计余量太小比如只有0.1ns温度一变、电源一波动就可能变成负余量导致系统不稳定。6.5 从时序报告到代码优化的闭环这一步是仿真和时序分析衔接的关键。很多人拿到时序报告看到SLACK为负然后就开始盲目地到处改代码。我的建议是走一套标准化的排查顺序先看是哪条路径违例。报告里会标出起点寄存器和终点寄存器找到对应的代码位置。判断路径类型。如果是组合逻辑路径过长考虑在中间插入寄存器打一拍如果是跨时钟域路径检查是否做了同步处理高云IDE也提供了跨时钟域分析工具建议打开使用。检查是否伪路径。有些路径比如初始化信号、测试模式信号不需要满足时序要求可以通过设置伪路径约束把它排除set_false_path -from [get_pins {uut/rst_sync_reg/Q}]重新布局布线再看结果。改完代码或约束后重新综合、布局布线、再跑时序分析确认Slack变成正值。我见过很多人一看到时序余量负了就往代码里塞寄存器结果时钟路径和组合逻辑都变了过了但电路功能坏了。所以时序优化的核心原则是小步修改、每次只改一个变量、每次改完都重新验证功能。7. 高云FPGA中的常见仿真坑波形是红线、仿真不更新等排查记录最后这一节我把自己实际踩过的坑集中列出来有些问题我花了大半天才定位到原因写出来帮大家省点时间。7.1 波形全是红线三分钟排查法我第一次用高云自动流程跑仿真时波形窗口打开一片红。当时第一反应以为代码写错了后来才总结出排查套路。按顺序检查这三步90%的红线问题都能解决先看Testbench里的输入信号有没有初始化。所有reg类型信号在initial里赋初值否则默认是X态。这里有个小技巧初始化时把复位信号先拉低、再延时几百纳秒后拉高保证所有逻辑在复位期间完成初始化。再看被测试模块的端口连接。实例化时端口名和信号名千万不能打错高云IDE编译时有些连接错误不会报warning但仿真就是跑不对。我遇到过把clk连到rst_n上的低级错误代码在功能上完全讲不通但编译居然通过了波形乱得没法看。最后看时间尺度。timescale 1ns/1ps和timescale 1ps/1ps会直接影响仿真时间计算。如果你的Testbench里延时是#100在1ns尺度下是100ns在1ps尺度下只有100ps。这个差异在波形上表现出来就是信号变化的时间点看起来不对。7.2 Testbench更新了但仿真没变化这个坑我踩得特别深。有时候你修改了Testbench文件重新点仿真结果ModelSim加载的还是旧的Testbench。原因在于ModelSim的编译缓存它默认会复用旧的编译产物只有检测到文件时间戳变化才会重新编译。高云IDE的自动流程一般会自动处理这个但如果你手动在ModelSim里仿真就很容易遇到。解决办法是在重新仿真前强制清空work库vdel -all -lib work或者干脆删掉仿真目录下的work文件夹让它全量重新编译。另一个仿真没变化的原因是Testbench和RTL文件耦合更新但高云IDE只把RTL文件关联到了工程Testbench是外部文件。如果Testbench文件没有加进高云工程改了Testbench但IDE不认为有文件更新自然不会重新编译。解决办法是确认Testbench文件在工程中并且勾选了仿真相关配置。7.3 高云IP核仿真的特殊坑用高云PLL仿真时即使功能仿真能跑波形也可能出现一个诡异现象PLL输出时钟在仿真开始后很长一段时间内是未知态。这其实是正常的——PLL的仿真模型会模拟锁相过程需要一定的起振时间。你可以在Testbench里把这个时间预留出来或者直接查看PLL的locked信号等它拉高后再开始后续激励。// 等待PLL锁定 wait(uut.pll_inst.locked 1); #100; // 后续激励从这里开始还有一点高云PLL仿真模型会对输入时钟的质量做检查如果你的Testbench里时钟频率和PLL配置的目标不一致仿真可能会报错或者输出异常。所以Testbench里的时钟频率一定要和IP配置里的一致别随手生成一个频率就接上去。7.4 覆盖率统计的额外补充ModelSim提供了代码覆盖率分析功能在高云工程中也可以用。跑完仿真后在ModelSim命令行输入coverage report -file coverage.txt -byfile -details这个命令会生成每个源文件的覆盖率报告包括语句覆盖率、分支覆盖率、条件覆盖率、表达式覆盖率等维度。我通常在模块级验证最后阶段跑一次覆盖率看有没有盲区。分支覆盖率和条件覆盖率是重点看的两项如果某个分支从来没被走到那很可能就是你的Testbench漏了某种输入组合。覆盖率报告还能用文本拼接的方式合并多次仿真的结果方便做回归测试coverage add -file sim1.ucdb -file sim2.ucdb -file merged.ucdb回归测试时把多组激励跑完合并覆盖率数据就能看出整个验证是否充分。8. 我的实操习惯每次仿真前都做的三件小事内容讲到这里该说的技术点基本都覆盖了。最后分享几个我自己在实际项目中养成的习惯算是掏心窝子的经验。第一件小事每次仿真前先看一遍Testbench里的时间尺度timescale是否正确。因为模型、Testbench、IP仿真文件可能各自带时间尺度声明如果它们不一致仿真结果的时序逻辑可能和你预期完全不同。高云IDE自动流程里一般会用Testbench最顶层的时间尺度但如果你在ModelSim里手动添加了别的文件还是检查一下为妙。第二件小事仿真波形跑出来之后先用肉眼做一次冒烟测试确认复位、时钟、关键接口的初始状态都符合预期再去做复杂功能验证。就像写代码先跑个Hello World再写业务逻辑一样Testbench也要先跑通基础激励再扩展复杂用例。第三件小事把Testbench和RTL代码一起提交版本库。软件工程里单元测试和代码同仓库管理是基本规范FPGA工程也一样。很多团队RTL代码管得挺好但Testbench丢三落四结果项目换个人接手仿真环境还原不出来所有验证工作等于白做。高云工程文件不复杂把Testbench和高云IDE的仿真脚本也一并存好谁拿到工程都能立马开始仿真验证。有一次我在调试一个高云GW1N-1的UART通信模块时功能仿真和上板实测出现了对不上的奇怪现象仿真里数据收发完全正确但实际板子上波特率就差了一点导致高频通信误码。最终定位到是PLL配置的输出频率存在微小偏差通过查看PLL配置界面里的实际输出频率选项修正后问题解决。这个过程耗费了两天但如果在设计初期就对PLL输出做一次精确的时序仿真这个坑完全可以提前避开。仿真不是为了走流程而是为了在上板前就排除那些藏在细节里的问题。从这个角度来说花在仿真上的每一分钟都是值得的。
返回列表