Verilog HDL基础篇—仿真基础
1. 常用系统任务
1.1 系统任务—文件操作
1.1.1 打开文件($fopen)
<文件句柄> = $fopen(“<文件名>”);
任务$fopen返回一个被称为多通道描述符的32位值,多通道描述符中只有一位被设置成1。
1.1.2 写文件($fwrite)
$fdisplay(<文件描述符>, p1, p2, ... ,pn); $fmonitor(<文件描述符>, p1, p2, ... ,pn);
p1, p2, ... , pn 可以变量、信号名或者带引号的字符串。例子:
`timescale 1 ns/ 1 ps `define DATA_DEBUG 1 `define LAST_TIME 300 `define DLY_1 1 module write_enable_signal#( parameter signal_WIDTH = 10, parameter FILENAME = "./data_new.txt" ); integer signal_FILE; reg signal_isNotFirstRise = 0; reg signal_isSimulationEnd = 0; reg signed[signal_WIDTH-1:0] signal_out; // 定义接口 reg clk = 0; reg enable = 1; initial begin signal_out = 0; #`DLY_1; signal_FILE = $fopen(FILENAME, "w"); if(signal_FILE == 0) begin $display("Error at opening file: %s", FILENAME); $stop; end else $display("Loading %s ...... ", FILENAME); end // clock initial begin repeat(22) #`DLY_1 clk = !clk; end always @(posedge clk) begin signal_isNotFirstRise <= #`DLY_1 1; end always @(posedge clk) begin if(signal_isNotFirstRise) begin if(enable) begin /*$fdisplay、$fwrite任务中%m选项方式可以显示任何级别的层次*/ $fwrite(signal_FILE,$time,"clock,%m,%d\n",signal_out); // $fdisplay(signal_FILE,"$time,%m,%d\n",signal_out); `ifdef DATA_DEBUG $display("Write data is %d", signal_out); `endif signal_out += 1; //每个时钟沿加1 end end end endmodule
运行结果如下:
1.1.3 读取文件($fscanf)
$fscanf(<文件描述符>, <读取格式>, <存储变量>)
该系统函数每次读取文件中的一行数据(注意:数据格式)。
`timescale 1 ns/ 1 ps `define DATA_DEBUG 1 `define LAST_TIME 300 `define DLY_1 1 module read_enable_signal#( parameter signal_WIDTH = 10, parameter FILENAME = "./data.txt" ); integer signal_FILE; reg signal_isNotFirstRise = 0; reg signal_isSimulationEnd = 0; reg signed[signal_WIDTH-1:0] signal_out; // 定义接口 reg clk = 0; reg enable = 1; initial begin signal_out = 0; #`DLY_1; signal_FILE = $fopen(FILENAME, "rb"); if(signal_FILE == 0) begin $display("Error at opening file: %s", FILENAME); $stop; end else $display("Loading %s ...... ", FILENAME); end // clock always begin #`DLY_1 clk = !clk; end always @(posedge clk) begin signal_isNotFirstRise <= #`DLY_1 1; end always @(posedge clk) if(signal_isNotFirstRise) begin if($feof(signal_FILE) != 0) begin signal_isSimulationEnd = 1; #`LAST_TIME; $finish(2); end else if(enable) begin if($fscanf(signal_FILE, "%d", signal_out) < 1) begin signal_isSimulationEnd = 1; #`LAST_TIME; `ifdef DATA_DEBUG $display("signal_isSimulationEnd = %d, Data format error!!", signal_isSimulationEnd); `endif $finish(2); end else begin `ifdef DATA_DEBUG $display("Data is %d", signal_out); `endif // end end end endmodule
对应 "./data.txt " 文件内容格式如下:
-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
运行结果如下:
1.1.4 关闭文件($fclose)
$fclose(<文件描述符>);
除此之外,还有 文件处理定位(fseek)、文件位置(ftell)、文件格式化(sformat/sprintf)等其它系统函数。
1.2 系统任务—$display 和 $write
$display(p1, p2,...,pn); $write(p1,p2,...,pn);
参数p1通常称为“格式控制”,参数p2至pn通常称为“输出系列”。$display会自动在输出后进行换行,$write则不会。
(1)输出格式和说明
输出格式 | 说明 |
%h或%H | 以十六进制形式输出 |
%d或%D | 以十进制形式输出 |
%o或%O | 以八进制形式输出 |
%b或%B | 以二进制形式输出 |
%c或%C | 以ASCII码字符形式输出 |
%v或%V | 输出网络型数据信号强度 |
%m或%M | 输出等级层次的名字 |
%s或%S | 以字符串的形式输出 |
%t或%T | 以当前的时间格式输出 |
%e或%E | 以指数的形式输出实型数 |
%f或%F | 以十进制的形式输出实型数 |
%g或%G | 以指数或十进制数的形式输出实型数 无论何种方式都以最短的结果输出 |
(2)特殊字符输出
序列 | 功能 |
\n | 换行 |
\t | 横向跳格(即跳到下一个输出区) |
\\ | 反斜杠字\ |
\” | 双引号字符” |
\o | 1~3位八进制数代表的字符 |
%% | 百分符号% |
1.3 系统任务—$readmemb 和 $readmemh
Verilog HDL中有两个系统任务 $readmemh 和 $readmemb,用来从文件中读取数据到存储器中。
$readmemb("<数据文件名>",<存储器名>) $readmemb("<数据文件名>",<存储器名>,<起始地址>) $readmemb("<数据文件名>",<存储器名>,<起始地址>,<结束地址>)
$readmemh("<数据文件名>",<存储器名>) $readmemh("<数据文件名>",<存储器名>,<起始地址>) $readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>)
文件 init_memb.txt 初始化数据,用@<地址> 在数据文件中指定地址,地址以16进制数说明,数据用空格符分割,数据可以包含x或Z。如下:
`timescale 1 ns/ 1 ps `define DLY_1 1 module readmemb_signal#( parameter FILENAME = "./init_memb.txt" ); integer signal_FILE; integer i = 0; reg[7:0] memory[0:7]; initial begin #`DLY_1; $readmemb(FILENAME, memory); for(i = 0; i < 8; i = i + 1) $display("Memory[%d] = %b", i, memory[i]); end endmodule
对应 "./init_memb.txt " 文件内容格式如下:
@001 00000001 00000010 00000100 00001000 @006 1111zzzz 00001111
运行结果如下:
1.4 系统任务—显示层次
通过$display、$write、$monitor、$strobe 等任务中的%m选项,可以显示任何级别的层级。
$fwrite(signal_FILE,$time,"clock,%m,%d\n",signal_out);
输出结果:
3clock,write_enable_signal, 0 5clock,write_enable_signal, 1 7clock,write_enable_signal, 2
1.5 系统任务—选通显示
由关键字$strobe完成,该语句总是在同时刻的其它赋值语句执行完成之后才执行。因此,$strobe提供了一种同步机制,可以确保所有在同一时刻沿赋值的其他语句在执行完毕后才能显示数据。如下:
`timescale 1 ns/ 1 ps `define DLY_1 1 module other_exp(); reg[7:0] a, b; reg[7:0] c, d; reg clk; initial begin b = 0; d = 0; clk = 0; end // clock initial begin repeat(20) #`DLY_1 clk = !clk; end always @(posedge clk) begin a = b; c = d; d += 1; b += 1; end always @(posedge clk) begin /* 注意:$strobe 和 $display 的区别 */ //$strobe("Displaying a = %d, c = %d", a, c); $display("Displaying a = %d, c = %d", a, c); end endmodule
(1) $strobe 运行结果如下:
(2) $display 运行结果如下:
1.6 系统任务—值变转存文件
值变存储文件(VCD)是一个ASCII文件,它包含仿真时间、范围与信号的定义、仿真运行过程中信号值得变化等信息。
设计中的所有信号或选定的信号集合在仿真过程中都可以被写入VCD文件。后处理工具可以把VCD文件作为输入并把层次信息、信号值和信号波形显示出来。
Verilog HDL提供了系统任务来选择要存储的模块实例或模块实例信号($dumpvars),选择VCD文件的名称($dumpfile),选择存储过程的起点和终点($dumpon, $dumpoff),选择生成检测点($dumppall)等。
... initial $monitor("At time %t, ocnt = %d", $time, ocnt); initial begin $dumpfile("counter_test.vcd"); $dumpvars(0, counter_test); end
2. 调试用系统任务
2.1 系统任务—$monitor
$monitor(p1, p2, ... ,pn); $monitor; $monitoron; $monitoroff;
$monitoron 和 monitoroff 任务的作用是通过打开和关闭监控标志来控制任务 $monitor 任务的启动和停止,方便程序员控制$monitor何时发生。
2.2. 系统函数—$time
返回一个64位的整数来表示当前仿真时刻值。该时刻以模块仿真时间尺度为基准。
2.3. 系统任务—$finish
$finish; $finish(n);
作用是结束仿真过程。此任务可以带参数表达式,参数值(0,1,2),默认值为1,几种特征信息如下:
0 不输出任何信息; 1 输出当前仿真时刻和位置; 2 输出当前仿真时刻、位置和仿真过程中所用memory及CPU时间的统计;
2.4. 系统任务—$stop
$stop; $stop(n);
作用是将EDA工具(如仿真器)设置为暂停模式,在仿真环境下给出一个交互式的命令提示符,将控制权交给用户。
此任务可以带参数表达式,参数值(0,1,2),值越大,输出信息越多。
参考来源:
[1] 《数字电路设计及Verilog HDL实现》 康磊 西安电子科技大学出版社
[2] 《Verilog数字系统设计教程》 夏宇闻 北京航空航天大学出版社
[3] 《通信IC设计》 李庆华 机械工业出版社