testbench常用语句 很详细相当实用
内容
与可综合Verilog代码所不同的是,testbench Verilog是在计算机主机上的仿真器中执行的。testbench Verilog的许多构造与C语言相似,我们可在代码中包括复杂的语言结构和顺序语句的算法。
1 always块和initial块
Verilog有两种进程语句:always块和initial块。always块内的进程语句,可用来模拟抽象的电路。
出于模拟的目的,always块可以包括:用以指定与不同结构之间的传播延迟等同的时序结构;或等待指定事件的时序结构。敏感列表有时可忽略。比方说,我们用下面的代码片段来模拟时钟信号,该信号每20个时间单位在0~1间变换一次,且永远执行下去。
always begin clk=1; #20; clk=0; #20); end |
initial块内也有进程语句,但是仅在仿真之初被执行。其简单语法如下:
initial begin 进程语句; end |
initail块常用于设置变量的初始值。注意,initial块不可被综合。
2 进程语句
进程语句应用于initial块、always块、function和task之中。最常用的进程语句为:
· 阻塞赋值
· 非阻塞赋值
· if表达式
· case表达式
· 循环表达式
Verilog支持的循环结构有:for、while、repeat和forever。for循环的简单语法为:
for([initial_assignment]; [end_condition]; [step_assignment]) begin [procedural_statements;] end |
举个例子,我们可以使用下面的语句来清除16位寄存器文件的内容:
integer i; . . . for(i=0; i<16; i=i+1) reg_file[i]=0; |
注意当循环体内只有一条语句的话,begin和end限定词可以略去。
while循环的简单语法如下:
while([end_condition]) begin [procedural_statements;] end |
循环体内的语句连续重复执行,直到达到指定的终止条件[end_condition]为止。比方说上面的清寄存器文件的操作可以使用while循环来描述:
integer i; . . . while(i<16) begin reg_file[i]=0; i=i+1; en |
repeat循环的简单语法如下:
repeat([number]) begin [procedual_statements;] end |
循环体内的语句被重复执行指定数次,该数可通过[number]来指定。比方说,我们可以将上面的操作替换为repeat循环:
integer i; . . . repeat(16) begin [procedural_statements;] end |
forever循环,正如其名,重复执行其主体直至仿真结束位置。循环体内常包括一定的时序控制结构,以致周期性推迟执行。比方说,我们换一种方式来描述时钟信号,该信号每10个时间单位翻转一次,且永远运行下去。
initial begin clk=1'b0; forever #10 clk=~clk; end |
3 时序控制
在testbench中,必须指定不同信号有效和无效或等待某事件或条件的时间。有三种时序控制结构:
· 时延控制:#[delay_time]
· 事件控制:@([event], [event], …]
· 等待语句:wait([boolean_expression])
此外还有一个编译器指令,`timescale,也与时序规范有关。
4 时延控制
时延控制使用#符号来指示,其后为延迟的时间单位数值。
如果时延控制放置在左手边,那么整条语句的执行都会被延迟。比方说,
. . . #10 a=1'b0; #5 y=a|b; . . . |
假设当前时间为t,上面的语句表示,a于t+10时刻得到0值;又过了5个时间单位后(即于t+15时刻)a|b表达式被计算,其结果被赋给y。
如果实验控制被放置在右手边,那么表达式将会被立即运算,但是延迟后再赋给左手边。如:
. . . #10 a=1'b0; y=#5 a|b; . . . |
a于t+10时刻得到0值;a|b表达式被立即运算(即在t+10时刻),但其结果却在t+15时刻才赋给y。
一般情况下,我们使用时延控制生成激励的方式来替代传播延迟的模拟。下面的格式使得代码显得更加直观。
. . . a=1'b0;// a gets 0 #10; // the 0 value lasts 10 time units a-1'b1;// a changes to 10 #5; // the 1 value lasts 5 time units a=1'b0;// a changes to 0 #20 // the 0 value lasts 20 time units . . . |
…
5 事件控制
事件控制使用@符号来指示,其后为敏感列表,用于指定所需事件。其使用与always块内的事件类似。事件即敏感列表中的信号改变其值(信号跳变)的时刻。可加入posedge和negedge关键字以指定所需的跳变边沿(上升沿和下降沿)。在testbench中,直到指定事件发生,语句才可跳过延迟,继续执行。事件控制的一个常见应用为:使用时钟信号来同步激励的生成。比方说,下面的代码片段中,en信号被激活持续一个时钟周期。
localparam delta=1; . . . @(posedgeclk);// wait for the rising edge of clk #delta; // wait for delta to avoid hold=time violation en=1'b1; // assert en to 1 @(posedgeclk);// wait for the next rising edge of clk #delta; // wait for delta to avoid hold-time violation en=1'b0; // assert en to 0 |
换一种方式,我们可以在时钟信号的下降沿断言或解除断言en。
. . . @(negedgeclk)// wait for the falling edge of clk en=1'b1; // assert en to 1 @(negedgeclk)// wait for the next falling edge of clk en=1'b0; . . . |
6 等待语句
wait语句用以等待指定条件。其简单语法如下:
wait[boolean_expression] |
直到[boolean_expression]被计算为真,后面语句才可跳过延迟,继续执行。比方说,我们可以这样写代码:
wait(state==READ && mem_ready==1'b1) [statement_to_get_data]; |
我们也可以使用wait语句来延迟执行。比方说,我们可以等计数器数到15才激活某信号:
. . . wait(counter==4'b1111);// wait until counter is 15 . . . // continue |
wait语句有时很想事件控制。后者是等待某信号的跳变边沿,而前者是等待指定条件,有时可理解为电平敏感。
7 timescale指令
编译器指令用以控制编译和预处理verilog代码,他们通过重音符号(`)来指明。重音符号常位于键盘的左上角。与时间有关的指令是`timescale指令
`timescale [time_unit] / [time_precision]
time_unit指定计时和延时的测量单位,time_precision则是指定仿真器的精度。
比方说,指令
`timescale10ns/1ns |
则说明仿真单位为10ns,精度为1ns。当指定如下代码中的延时,
#5 y = a & b; |
表明实际上的延时为50ns(即5*10ns)。
也可以指定小数形式的单位延时,比方说
#5.12345 y = a & b; |
则说明实际延时为51.2345ns。因为精度是1ns, 所以在仿真中就取整为51ns。精度越少,仿真的准确性越高,但是会减慢仿真的速度。
time_unit和time_precision的数字部分可以为1、10和100,时间单元可以是s(秒)、ms(毫秒)、us(微秒)、ns(纳秒)和ps(皮秒)。
8 系统控制函数和任务
Verilog有一组预定义的系统函数,以$打头,执行与系统相关的操作,如仿真控制、文件读取等。下面我们讲一下一些常用的函数和任务。
数据类型转换函数
$unsigned和$signed函数执行介于无符号数和有符号数类型之间的转换。
仿真时间函数
仿真时间函数返回当前的仿真时间,如$time、$stime和$realtime函数分别以64位整数、32位整数和实数的形式返回时间。
仿真控制任务
有两种仿真控制函数:$finish和$stop。其中,$finish任务用于终止仿真并跳出仿真器;$stop任务则用于中止仿真。在Modelsim中,$stop任务则是返回到交互模式。在开发流程中,我们有时会停在Modelsim环境中,来进一步编辑或测试波形,因此代码中使用的是$stop。
显示任务
在Modelsim中,仿真的结果可以以波形的形式显示,也可以以文本的形式显示。四种主要的显示任务有$display、$write、$strobe和$monitor,它们语法类似。在Modelsim中,文本是在控制面板显示的。
$display的语法与C语言中的打印函数类似。其简单语法为:
$display([format_string], [argument], [argument], ...); |
如:
$display("at %d; signal x = %b", $time, x); |
其结果的形式如下:
at 5100; signal x = 00110001 |
最常用的转移符号有%d、%b、%o、%h、%c、%s和%g,对应分别为十进制、二进制、八进制、十六进制、字符、字符串和实数。
$write任务几乎和$diplay等同,除了其执行之后并不跳到下一行显示。而是一直显示在当前位置。显示下一行字符\n,必须手动添加,以创建一个行中断。
Verilog可结合time step的概念来塑造仿真延时。每个time step中可以发生很多活动。$strobe与$display任务类似。代替立即执行的是,$strobe任务是在当前仿真的time step的结尾执行的。它可以规避由于竞争冒险造成的不匹配的数据显示。
$monitor任务是非常通用的命令。鉴于$displat、$write、$strobe任务是在一旦它们被执行的情况下才显示文本,$monitor任务则是当其参数发生变化时即显示文本。$monitor任务提供了简单的富有弹性的方式来跟踪仿真。比方说,我们可以在testbench中添加如下的代码:
initial begin $display("time test_in0 test_in1 test_out"); $monitot("%d %b %b %b", $time, test_in0, test_in1, test_out); end |
Modelsim的控制面板中显示的文本仿真结果如下(示例):
time test_in0 test_in1 test_out 0 00 00 1 200 01 00 0 400 01 11 0 . . . |
文件I/O系统函数和任务
Veirlog提供一组用于访问外部数据文件的函数和任务。文件可以通过$fopen和$fclose函数来打开和关闭。$fopen的语法为:
[mcd_names] = $fopen("[file_name]"); |
$fopen函数返回一个与文件相关的32位的多通道描述子。这个描述子我们可以认为是一个32位的标志,它代表一个文件(亦即一个通道)。最低位LSB保留,用以只是标准输出(console)。当使用函数调用的文件被成功打开,则返回的描述子的值得某位会被置一。例如,0…0010表示打开第一个文件,0…0100表示打开第二个文件,依次类推。若函数的返回值为0,则表示文件未能成功打开。
一旦某个文件被打开,我们就可以向其内写入数据。可用的四种显示系统任务为:$fdisplay,$fwrite,$fstrobe和$fmonitor。这些任务的用法类似于先前的$display等,除了其第一参数为描述子以外。
$fdisplay([mcd_name], [format_string], ...); |
下面给出一个简单的代码片段。
integer log_file, both_file; localparam con_file =32'h0000_0001;// console initialbegin log_file = $fopenZ("my_log"); if(log_file == 0) $display("Fail to open log file");// write console both_file = log_file | con_file; // write to both console and log_file $fdisplay(both_file,"Simulation started"); . . . // write to log_file only $fdisplay(log_file, ...); . . . // write to $fdisplay(both_file,"Simulation ended"); $fclose(log_file); end |
注意我们可以通过对多个描述子进行位运算来创建一个描述子,比方说both_file变量。当both_file被使用时,就可以同时对console和log_file进行操作。
有两个任务可以从文件中载入数据,分别为:$readmemb,$readmemh。这些任务假设外置文件中存储了memory-array的内容,然后读出这些内容存到一个变量中。$readmemb,$readmemh所假设的文件格式分别为二进制和十六进制,相应地,它们的语法格式为:
$readmemb("[file_name]", [mem_variable]); $readmemh("{file_name]", [mem_variable]); |
下面的片段描述如何载入一个8x4的存储阵列:
reg[3:0] v_mem [0:7]; . . . $readmemb("vector.txt", v_mem); |
vector.txt应该包含八个4bit的使用空格分割的二进制数据。
有了文件操作函数和任务,就可以使用外部文件来指定测试模型,以及记录仿真结果。下面给出一个案例。
`timescale1ns/1ns moduleeq2_file_tb; // signal declaration reg[1:0] test_in0, test_in1; wiretest_out; integer log_file, console_file, out_file; reg[3:0] v_mem [0:7]; integer i; // instantiate the circuit under test eq2_sop eq2_sop_inst ( .a(test_in0), .b(test_in1), .aeqb(test_out) ); initialbegin // setup output fil log_file = $fopen("eqlog.txt" if(!log_file) $fdisplay("Cannot open log file"); console_file =32'h0000_0001; out_file = log_file | console_file; // read test vector #readmemb("vector.txt", v_mem); // test generator iterating throught 8 pattens for(i=0; i<8; i=i+1)begin {test_in0, test_in1} = v_mem[i]; end // stop simulation $fclose(log_file); $stop; end // text display initialbegin $fdisplay(out_file," time test_in0 test_in1 test_out"); $fdisplay(out_file," (a) (b) (aeqb)"); $fmoitor(out_file," d %b %b %b", $time, test_in0, test_in1, test_out); end endmodule |
指定的test pattern为4位二进制格式,存储在vector.txt中。文件内容为:
1 2 3 4 5 6 7 8 |
00_00 01_00 01_11 10_10 10_00 11_11 11_01 00_10 |
注意:“_”只起连接数字,分隔开容易区分位数的作用,和verilog其他地方的用法一致。
上面的文件被载入到二维的v_mem变量中。仿真的结果被写入console和log_file中。log_file的内容为:
time test_in0 test_in1 test_out (a) (b) (aeqb) 0 00 00 1 200 01 00 0 400 01 11 0 600 10 10 1 800 10 11 0 1000 11 00 1 1200 11 01 0 1400 00 10 0 |
log_file为一般的文本文件,可使用其他文本编辑器编辑。
9 用户自定义函数和任务
待续
转载自:http://blog.sina.com.cn/s/blog_78699cbf01016mvt.html
testbench常用语句 很详细相当实用的更多相关文章
- 很详细、很移动的Linux makefile教程:介绍,总述,书写规则,书写命令,使用变量,使用条件推断,使用函数,Make 的运行,隐含规则 使用make更新函数库文件 后序
很详细.很移动的Linux makefile 教程 内容如下: Makefile 介绍 Makefile 总述 书写规则 书写命令 使用变量 使用条件推断 使用函数 make 的运行 隐含规则 使用m ...
- JS正则表达式大全(整理详细且实用)
JS正则表达式大全(整理详细且实用).需要的朋友可以过来参考下,希望对大家有所帮助!! 正则表达式中的特殊字符 字符 含意 \ 做为转意,即通常在"\"后面的字符不按原来意义解释, ...
- C++内存管理(超长,例子很详细,排版很好)
[导语] 内存管理是C++最令人切齿痛恨的问题,也是C++最有争议的问题,C++高手从中获得了更好的性能,更大的自由,C++菜鸟的收获则是一遍一遍的检查代码和对C++的痛恨,但内存管理在C++中无处不 ...
- mysqldump的常用语句及各参数详解
mysqldump的常用语句及各参数详解 分类: MySQL 2011-01-11 17:55 1368人阅读 评论(0) 收藏 举报 数据库mysql服务器tableinsertdatabase m ...
- ETL讲解(很详细!!!)
ETL讲解(很详细!!!) ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程,目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供分析依据. ETL是BI项目重要 ...
- MySQL 常用语句大全
MySQL 常用语句大全 一.连接 MySQL 格式: mysql -h 主机地址 -u 用户名 -p 用户密码 1.例 1:连接到本机上的 MYSQL. 首先在打开 DOS 窗口,然后进入目录 my ...
- 转载自鸿燕藏锋-ETL讲解(很详细!!!)
ETL讲解(很详细!!!) ETL讲解(很详细!!!) ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程,目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供 ...
- 转:Java多线程学习(总结很详细!!!)
Java多线程学习(总结很详细!!!) 此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢? 本文主要讲java中多线程 ...
- 转:ETL讲解(很详细!!!)
ETL讲解(很详细!!!) ETL是将业务系统的数据经过抽取.清洗转换之后加载到数据仓库的过程,目的是将企业中的分散.零乱.标准不统一的数据整合到一起,为企业的决策提供分析依据. ETL是BI项目重要 ...
随机推荐
- android触控,先了解MotionEvent
MotionEvent源代码可以在ocs看到,当然你也可以在SDK中下载源代码,或者其他地方,如: https://github.com/CyanogenMod/android_frameworks_ ...
- windows installer服务无法启动,无法打开任何msi文件
如果不成功就在"依存关系"中找是否有其他的文件没有启用. 启用"remote procedure call(rpc)" 启用"workstation& ...
- Android Studio关于USB device not found的解决的方法
Android Studio关于USB device not found的解决的方法 我们使用Android Studio进行Android开发时.当我们使用真机进行调试时.非常可能会出现USB de ...
- 媒体类型(MIME类型)
# encoding=utf-8 #python 2.7.10 #xiaodeng #HTTP权威指南 #HTTP协议:超文本传输协议是在万维网上进行通信时所使用的协议方案. #媒体类型: #HTTP ...
- Redis学习(7)-通用命令
keys pattern: 获取所有与pattern匹配的key,返回所有与该key匹配的keys. 通配符: *表示任意一个或多个字符串. ?表示一个字符. 例如: 查询所有的key:keys * ...
- HDUOJ---Hamming Distance(4712)
Hamming Distance Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others) ...
- Linux命令-服务管理命令:chkconfig
chkconfig --list 查看服务自启动状态列表,等同于查看服务列表 设置某一个服务为自启动服务: chkconfig 服务名 on 修改服务的启动级别为3,,5 查看某一个服务时候已经运行了 ...
- Linux命令-终止进程命令:killall
强制杀死所有进程,注意它后面跟着是进程名而不是进程号 killall - httpd 杀死apache所有进程pstree -p | grep httpd 查看apache进程就没有了service ...
- Apache Storm使用
Apache Storm 是 Apache 基金会的开源的分布式实时计算系统.与 Hadoop 的批处理相类似,Storm 可以对大量的数据流进行可靠的实时处理,这一过程也称为“流式处理”,是分布式大 ...
- mvc中Action前HttpPost的作用
本文导读:在ASP.NET MVC框架中,为了限制某个action只接受HttpPost的请求,对于HttpGet的请求则提示404找不到页面,可以在action的方法前面加上[HttpPost]属性 ...