Verilog十大基本功2(testbench的设计 文件读取和写入操作 源代码)
来源:互联网 发布:手机脱壳软件 编辑:程序博客网 时间:2024/06/05 15:49
需求说明:Verilog设计基础
内容 :testbench的设计 读取文件 写入文件
来自 :时间的诗
十大基本功之 testbench
1. 激励的产生
对于 testbench 而言,端口应当和被测试的 module 一一对应。
端口分为 input,output 和 inout 类型产生激励信号的时候,
input 对应的端口应当申明为 reg,
output 对应的端口申明为 wire,
inout 端口比较特殊,下面专门讲解。
1)直接赋值
一般用 initial 块给信号赋初值,initial 块执行一次,always 或者 forever 表示由事件激发反复执行。
举例,一个 module
`timescale 1ns/1psmodule exam(); reg rst_n; reg clk; reg data; initial begin clk = 1'b0; rst = 1'b1; #10 rst = 1'b0; #500 rst = 1'b1; end always begin #10 clk = ~clk; end endmodule
大家应该注意到有个#符号,该符号的意思是指延迟相应的时间单位。该时间单位由 timscale 决定.
一般在 testbench 的开头定义时间单位和仿真精度,比如`timescale 1ns/1ps
前面一个是代表时间单位,后面一个代表仿真时间精度。
以上面的例子而言,一个时钟周期是 20 个单位,也就是 20ns。
而仿真时间精度的概念就是,你能看到 1.001ns 时对应的信号值,
而假如 timescale 1ns/1ns,1.001ns 时候的值就无法看到。
对于一个设计而言,时间刻度应该统一,如果设计文件和 testbench 里面的时间刻度不一致,
仿真器默认以 testbench 为准。
一个较好的办法是写一个 global.v 文件,然后用 include 的办法,可以防止这个问题。
对于反复执行的操作,可写成 task,然后调用,比如
task load_count; input [3:0] load_value; begin @(negedge clk_50); $display($time, " << Loading the counter with %h >>", load_value); load_l = 1’b0; count_in = load_value; @(negedge clk_50); load_l = 1’b1; end endtask //of load_count initial begin load_count(4’hA); // 调用 task end
其他像 forever,for,function 等等语句用法类似,虽然不一定都能综合,但是用在 testbench 里面很方
便,大家可以自行查阅参考文档
2) 文件输入
有时候,需要大量的数据输入,直接赋值的话比较繁琐,可以先生成数据,再将数据读入到寄存器中,
需要时取出即可。
用 $readmemb 系统任务从文本文件中读取二进制向量(可以包含输入激励和输出期望值)。
$readmemh 用于读取十六进制文件。例如:
reg [7:0] mem[256:1] // a 8-bit, 256-word 定义存储器 meminitial $readmemh ( "E:/readhex/mem.dat", mem ) // 将.dat 文件读入寄存器 mem 中initial $readmemh ( "E:/readhex/mem.dat", mem, 128, 1 ) // 参数为寄存器加载数据的地址始终
文件调入和打印中,我们 $fread $fwrite 用的更多一些, 大家可以自行查阅参考文档
2. 查看仿真结果
对于简单的 module 来说,要在 modelsim 的仿真窗口里面看波形,就用 add wave ..命令
比如, testbench 的顶层 module 名叫 tb,要看时钟信号,就用 add wave tb.clk
要查看所有信号的时候,就用 add wave /*
当然,也可以在 workspace 下的 sim 窗口里面右键单击 instance 来添加波形
对于复杂的仿真,免不了要记录波形和数据到文件里面去。
1)波形文件记录(这部分暂时我没用到呢!)
常见的波形文件一般有两种, vcd 和 fsdb, debussy 是个很好的工具,支持 fsdb,所以最好是 modelsim+debussy 的组合默认情况下, modelsim 不认识 fsdb,所以需要先装 debussy,再生成 fsdb 文件。
$dumpfile 和$dumpvar 是 verilog 语言中的两个系统任务, 可以调用这两个系统任务来创建和将指定信息导入 VCD 文件.
对于 fsdb 文件来说,对应的命令是 fsdbDumpfile,dumpfsdbvars
(什么是 VCD 文件? 答: VCD 文件是在对设计进行的仿真过程中,记录各种信号取值变化情况的信息记录文件。 EDA
工具通过读取 VCD 格式的文件,显示图形化的仿真波形,所以,可以把 VCD 文件简单地视为波形记录文件.)下面分别
描述它们的用法并举例说明之。
$dumpfile 系统任务:为所要创建的 VCD 文件指定文件名。举例( "//"符号后的内容为注释文字):initial$dumpfile ("myfile.dump"); //指定 VCD 文件的名字为 myfile.dump,仿真信息将记录到此文件$dumpvar 系统任务:指定需要记录到 VCD 文件中的信号,可以指定某一模块层次上的所有信号,也可以单独指定某一个信号。典型语法为$dumpvar(level, module_name); 参数 level 为一个整数, 用于指定层次数, 参数 module 则指定要记录的模块。整句的意思就是,对于指定的模块,包括其下各个层次(层次数由 level 指定)的信号,都需要记录到 VCD 文件中去。举例: initial $dumpvar (0, top); //指定层次数为 0,则 top 模块及其下面各层次的所有信号将被记录 initial $dumpvar (1, top); //记录模块实例 top 以下一层的信号 //层次数为 1,即记录 top 模块这一层次的信号 //对于 top 模块中调用的更深层次的模块实例,则不记录其信号变化 initial $dumpvar (2, top); //记录模块实例 top 以下两层的信号 //即 top 模块及其下一层的信号将被记录 假设模块 top 中包含有子模块 module1,而我们希望记录 top.module1 模块以下两层的信号,则语法举例如下: initial $dumpvar (2, top.module1); //模块实例 top.module1 及其下一层的信号将被记录 假设模块 top 包含信号 signal1 和 signal2(注意是变量而不是子模块), 如我们希望只记录这两个信号,则语法举例如下: initial $dumpvar (0, top.signal1, top.signal2); //虽然指定了层次数,但层次数是不影响单独指定的信号的
//即指定层次数和单独指定的信号无关
我们甚至可以在同一个$dumpvar 的调用中,同时指定某些层次上的所有信号和某个单独的信号,假设模块 top 包含信
号 signal1,同时包含有子模 块 module1,如果我们不但希望记录 signal1 这个独立的信号,而且还希望记录子模块 module1
以下三层的所有信号,则语法举例如下:
initial $dumpvar (3, top.signal1, top.module1); //指定层次数和单独指定的信号无关 //所以层次数 3 只作用于模块 top.module1, 而与信号 top.signal1 无关
上面这个例子和下面的语句是等效的:
initial begin $dumpvar (0, top.signal1); $dumpvar (3, top.module1); end $dumpvar 的特别用法(不带任何参数): initial $dumpvar; //无参数,表示设计中的所有信号都将被记录
最后,我们将$dumpfile 和$dumpvar 这两个系统任务的使用方法在下面的例子中综合说明,假设我们有一个设计实例,
名为 i_design,此设计中包含模块 module1,模块 module1 下面还有很多层次,我们希望对这个设计进行仿真,并将仿
真过程中模块 module1 及其以下所有层次中所有信号的变化情况,记录存储到名为 mydesign.dump 的 VCD 文件中去,
则例示如下:
initial begin $dumpfile ("mydesign.dump"); //指定 VCD 文件名为 mydesign.dump $dumpvar (0, i_design.module1); //记录 i_design.module1 模块及其下面层次中所有模块的所有信号 end
对于生成 fsdb 文件而言,也是类似的
initial begin $fsdbDumpfile("tb_xxx.fsdb"); $fsdbDumpvars(0,tb_xxx); end
2)文件输出结果
integer out_file; // out_file 是一个文件描述,需要定义为 integer 类型out_file = $fopen ( " cpu.data " ); // cpu.data 是需要打开的文件,也就是最终的输出文本
设计中的信号值可以通过$fmonitor, $fdisplay,$fwrite
其中$fmonitor 只要有变化就一直记录, $fdisplay 和$fwrite 需要触发条件才记录
例子:
initial begin $fmonitor(file_id, "%m: %t in1=%d o1=%h", $time, in1, o1); end always@(a or b) begin $fwrite(file_id,"At time%t a=%b b=%b",$realtime,a,b); end
3 参考“A Verilog HDL Test Bench Primier.pdf”
1) DUT(Design Under Test)部分
//-------------------------------------------------// File: count16.v// Purpose: Verilog Simulation Example//-------------------------------------------------`timescale 1 ns / 100 psmodule count16 ( clk, rst_n, load_l, enable_l, cnt_in, oe_l, count, count_tri ); input clk; input rst_n; input load_l; input enable_l; input [3:0] cnt_in; input oe_l; output [3:0] count; output [3:0] count_tri; reg [3:0] count; // tri-state buffers assign count_tri = (!oe_l) ? count : 4'bZZZZ; // synchronous 4 bit counter always @ (posedge clk or negedge rst_n) if (!rst_n) begin count <= 4'd0; end else begin if (!load_l) begin count <= cnt_in; end else if (!enable_l) begin count <= count + 1; end end endmodule //of count16
2) Test Bench
//-------------------------------------------------// File: tb.v// Purpose: Verilog Simulation Example// Test Bench//-----------------------------------------------------------`timescale 1ns / 100psmodule tb (); //--------------------------------------------------------- // inputs to the DUT are reg type reg clk_50; reg rst_n; reg load_l; reg enable_l; reg [3:0] count_in; reg oe_l; //-------------------------------------------------------- // outputs from the DUT are wire type wire [3:0] cnt_out; wire [3:0] count_tri; //---------------------------------------------------------- // create a 50Mhz clock always #10 clk_50 = ~clk_50; // every ten nanoseconds invert //----------------------------------------------------------- // initial blocks are sequential and start at time 0 initial begin $display($time, " << Starting the Simulation >>"); clk_50 = 1'd0; // at time 0 rst_n = 0; // reset is active enable_l = 1'd1; // disabled load_l = 1'd1; // disabled count_in = 4'h0; oe_l = 4'b0; // enabled #20 rst_n = 1'd1; // at time 20 release reset $display($time, " << Coming out of reset >>"); @(negedge clk_50); // wait till the negedge of // clk_50 then continue load_count(4'hA); // call the load_count task @(negedge clk_50); $display($time, " << Turning ON the count enable >>"); enable_l = 1'b0; // turn ON enable // let the simulation run, // the counter should roll wait (cnt_out == 4'b0001); // wait until the count // equals 1 then continue $display($time, " << count = %d - Turning OFF the count enable >>",cnt_out); enable_l = 1'b1; #40; // let the simulation run for 40ns // the counter shouldn't count $display($time, " << Turning OFF the OE >>"); oe_l = 1'b1; // disable OE, the outputs of // count_tri should go high Z. #20; $display($time, " << Simulation Complete >>"); $stop; // stop the simulation end //-------------------------------------------------------------- // This initial block runs concurrently with the other // blocks in the design and starts at time 0 initial begin // $monitor will print whenever a signal changes // in the design $monitor( $time, " clk_50=%b, rst_n=%b, enable_l=%b, load_l=%b, count_in=%h, cnt_out=%h, oe_l=%b, count_tri=%h", clk_50, rst_n, enable_l, load_l, count_in, cnt_out, oe_l, count_tri ); end //-------------------------------------------------------------- // The load_count task loads the counter with the value passed task load_count; input [3:0] load_value; begin @(negedge clk_50); $display($time, " << Loading the counter with %h >>", load_value); load_l = 1'b0; count_in = load_value; @(negedge clk_50); load_l = 1'b1; end endtask //of load_count //--------------------------------------------------------- // instantiate the Device Under Test (DUT) // using named instantiation count16 count16_m0 ( .clk (clk_50), .rst_n (rst_n), .load_l (load_l), .cnt_in (count_in), .enable_l (enable_l), .oe_l (oe_l), .count (cnt_out), .count_tri (count_tri) ); //--------------------------------------------------------- // read and write data reg [7:0] mem[10:1];//read data from file initial $readmemh ("F:/IC/prj/testbench/prj0/data/mem.dat", mem ); // 将.dat 文件读入寄存器 mem 中 //设计中的信号值可以通过$fmonitor, $fdisplay,$fwrite //其中$fmonitor 只要有变化就一直记录, $fdisplay 和$fwrite 需要触发条件才记录 integer file_out; // out_file 是一个文件描述,需要定义为 integer 类型 initial file_out = $fopen("F:/IC/prj/testbench/prj0/data/wr_mem.dat", "w"); // wr_mem.dat 是需要打开的文件,也就是最终的输出文本 always @(posedge clk_50) if (/*tb.count16_m0.*/enable_l == 1'd0) begin $fwrite (file_out, "%h\n", cnt_out[3:0]);// $fdisplay(file_out, "%h", cnt_out[3:0]); end endmodule //of cnt16_tb
3) sim.do文件
#Time: 2016-07-26 #By : times_poem quit -sim cd F:/IC/prj/testbench/prj0 if [file exists work] { vdel -all } vlib work vlog ./*.v vlog ./src/*.v vsim -t ps -novopt work.tb log -r /* do wave.do run -all
1 0
- Verilog十大基本功2(testbench的设计 文件读取和写入操作 源代码)
- Verilog中testbench的设计,文件读取和写入操作
- Verilog十大基本功3(testbench的设计 iout类型端口信号处理)
- 十大基本功之testbench
- Verilog十大基本功1(流水线设计Pipeline Design)
- Verilog十大基本功1(流水线设计Pipeline Design)
- Verilog十大基本功4 (FPGA四大设计要点)
- c#大文件读取和写入数据库(带进度条的源代码)
- c#大文件读取和写入数据库(带进度条的源代码)
- c#大文件读取和写入数据库(带进度条的源代码)
- c#大文件读取和写入数据库(带进度条的源代码)
- c#大文件读取和写入数据库(带进度条的源代码)
- c#大文件读取和写入数据库(带进度条的源代码)
- c#大文件读取和写入数据库(带进度条的源代码)
- verilog testbench中 文本读写的操作
- Verilog testbench的写法之输入输出文件
- 文件操作(读取和写入)
- Verilog的testbench入门
- 蓝绿发布的整个部署过程
- FAE面试后的自我反思
- 从苏宁电器到卡巴斯基第18篇:曲折考研路(中)
- Hello Scala! 在IntelliJ IDEA 2016.2环境下的第一个Scala程序
- Android开发笔记(一百一十一)聊天室中的Socket通信
- Verilog十大基本功2(testbench的设计 文件读取和写入操作 源代码)
- [树状数组] poj 2481 Cows
- win7的svn客户端查看日志时,出现:there has been a problem contacting the server
- Firebird(全功能的,免维护的数据库,能够管理多个独立的数据库) V2.1.3 英文特别版
- Web 网站的基本工作原理
- select 获取选择的值
- Rx处理服务器请求、缓存的完美封装
- linux下如何查看某软件是否已安装
- QL的四种连接-左外连接、右外连接、内连接、全连接