目录
Dynamic Casting
如果在两种不同的数据类型之间赋值,这一般是不允许的,必须添加关键字$cast.$cast可以是任务或者函数,两者之间的差别在于函数$cast当赋值成功时会返回1;如果赋值失败,则返回0.语法格式如下:
function int $cast (targ_var, source_exp);//targ为目标类型,source为原始类型
task $cast (targ_var, source_exp);//如果赋值失败会报错
typedef enum { PENNY=1, FIVECENTS=5, DIME=10, QUARTER=25, DOLLAR=100 } Cents;
module tb;
Cents myCent;
initial begin
$cast (myCent, 10 + 5 + 10);//25为枚举中的一个元素
$display ("Money=%s", myCent.name());
end
endmodule
25为枚举类型Cents中的一个元素,所以赋值成功,结果如下:
如果将一个不是枚举元素的值赋给mycent,结果如下:
typedef enum { PENNY=1, FIVECENTS=5, DIME=10, QUARTER=25, DOLLAR=100 } Cents;
module tb;
Cents myCent;
initial begin
$cast (myCent, 75);
$display ("Money=%s", myCent.name());
end
endmodule
typedef enum { PENNY=1, FIVECENTS=5, DIME=10, QUARTER=25, DOLLAR=100 } Cents;
module tb;
Cents myCent;
initial begin
if ($cast (myCent, 75))
$display ("Cast passed");
else
$display ("Cast failed");
$display ("Money=%s", myCent.name());
end
endmodule
Package
package是一种封装了变量、方法、常量的包,以便在多个testbench中共享,可以通过关键字import导入.定义举例如下:
package my_pkg;
typedef enum bit [1:0] { RED, YELLOW, GREEN, RSVD } e_signal;
typedef struct { bit [3:0] signal_id;
bit active;
bit [1:0] timeout;
} e_sig_param;
function common ();
$display ("Called from somewhere");
endfunction
task run ( ... );
...
endtask
endpackage
导入包需要用到作用域操作符"::",举例:
// Import the package defined above to use e_signal
import my_pkg::*;//导入包,*表示包中的所有内容
class myClass;
e_signal my_sig;//定义枚举类型,e_signal为包中定义的变量类型
endclass
module tb;
myClass cls;
initial begin
cls = new ();
cls.my_sig = GREEN;
$display ("my_sig = %s", cls.my_sig.name());
common ();
end
endmodule
也可以只导入包中的某系变量:
import my_pkg::GREEN;
import my_pkg::e_signal;
import my_pkg::common;
当包中变量名与其他处定义的变量名冲突时,必须通过作用域操作符指明包中的变量,否则会被其他变量覆盖.看下例:
package my_pkg;
typedef enum bit { READ, WRITE } e_rd_wr;
endpackage
import my_pkg::*;
typedef enum bit { WRITE, READ } e_wr_rd;
module tb;
initial begin
e_wr_rd opc1 = READ;//两个枚举元素同名了,究竟是哪个?
e_rd_wr opc2 = READ;
$display ("READ1 = %0d READ2 = %0d ", opc1, opc2);
end
endmodule
结果:
可以看到包中的READ被包外的READ覆盖率,所以两个值都是1.再看下面:
package my_pkg;
typedef enum bit { READ, WRITE } e_rd_wr;
endpackage
import my_pkg::*;
typedef enum bit { WRITE, READ } e_wr_rd;
module tb;
initial begin
e_wr_rd opc1 = READ;
e_rd_wr opc2 = my_pkg::READ;//指明是包中变量
$display ("READ1 = %0d READ2 = %0d ", opc1, opc2);
end
endmodule
上面用作用域操作符指明了是包中的READ ,所以结果如下:
对于package,有下面几点需要注意:
- Packages can not contain any assign statement 包中不能包括任何assign语句
- Variable declaration assignments within the package shall occur before any initial, always, always_comb, always_latch, or always_ff blocks 变量的声明与赋值必须在过程块之前
- Items within packages cannot have hierarchical references. 包中的成员不能够存在层次引用
Nested modules
在verilog中不允许在一个module中定义另一个module,但是这种情况在SV中是允许的,这种内部的module称为Nested modules.其内部的成员称为局部成员,只能在module内部使用;但是外部module的成员可以在内部module中使用;
// Nested Module
//+++++++++++++++++++++++++++++++++++++++++++++++++
module nested_module();
//=================================================
// Module declration inside the module
//=================================================
module counter(input clk,enable,reset,
output logic [3:0] data);
always @ (posedge clk)
if (reset) data <= 0;
else if (enable) data ++;
endmodule
logic clk = 0;
always #1 clk++;
logic enable, reset;
wire [3:0] data;
counter U(clk,enable,reset,data);
initial begin
$monitor("@%0dns reset %b enable %b data %b",
$time,reset,enable,data);
reset <= 1;
#10 reset <= 0;
#1 enable <= 1;
#10 enable <= 0;
#4 $finish;
end
endmodule
file operations
打开和关闭文件
$fopen 打开文件并返回一个32bits的句柄,该句柄必须用于读取文件或者写入文件直至文件被关闭;
$fclose 关闭文件,任何文件在打开后都必须关闭;
module tb;
initial begin
// 定义fd用于保存句柄
int fd;
// 2. Open a file called "note.txt" in the current folder with a "read" permission
// If the file does not exist, then fd will be zero
fd = $fopen ("./note.txt", "r");
if (fd) $display("File was opened successfully : %0d", fd);
else $display("File was NOT opened successfully : %0d", fd);
// 2. Open a file called "note.txt" in the current folder with a "write" permission
// "fd" now points to the same file, but in write mode
fd = $fopen ("./note.txt", "w");
if (fd) $display("File was opened successfully : %0d", fd);
else $display("File was NOT opened successfully : %0d", fd);
// 3. Close the file descriptor
$fclose(fd);
end
endmodule
定义文件打开模式
文件的默认打开模式是"w",也可以由其他模式:
module tb;
initial begin
int fd_w, fd_r, fd_a, fd_wp, fd_rp, fd_ap;
fd_w = $fopen ("./todo.txt", "w"); // Open a new file in write mode and store file descriptor in fd_w
fd_r = $fopen ("./todo.txt", "r"); // Open in read mode
fd_a = $fopen ("./todo.txt", "a"); // Open in append mode
if (fd_w) $display("File was opened successfully : %0d", fd_w);
else $display("File was NOT opened successfully : %0d", fd_w);
if (fd_r) $display("File was opened successfully : %0d", fd_r);
else $display("File was NOT opened successfully : %0d", fd_r);
if (fd_a) $display("File was opened successfully : %0d", fd_a);
else $display("File was NOT opened successfully : %0d", fd_a);
// Close the file descriptor
$fclose(fd_w);
$fclose(fd_r);
$fclose(fd_a);
end
endmodule
将数据写入文件或从文件中读取数据
module tb;
int fd; // Variable for file descriptor handle
string line; // String value read from the file
initial begin
// 1. Lets first open a new file and write some contents into it
fd = $fopen ("trial", "w");//打开文件
// Write each index in the for loop to the file using $fdisplay
// File handle should be the first argument
for (int i = 0; i < 5; i++) begin
$fdisplay (fd, "Iteration = %0d", i);//写入文件
end
// Close this file handle
$fclose(fd);
// 2. Let us now read back the data we wrote in the previous step
fd = $fopen ("trial", "r");
// Use $fgets to read a single line into variable "line"
$fgets(line, fd);//获取文件的一行
$display ("Line read : %s", line);
// Get the next line and display
$fgets(line, fd);
$display ("Line read : %s", line);
// Close this file handle
$fclose(fd);
end
endmodule
上一个例子中利用$fgets()方法每次获取文件的一行,加入要获取整个文件这样写太过于复杂.可以利用$foef()方法,当到达文件的最后一行时返回true。看下面:
module tb;
int fd; // Variable for file descriptor handle
string line; // String value read from the file
initial begin
// 1. Lets first open a new file and write some contents into it
fd = $fopen ("trial", "w");
for (int i = 0; i < 5; i++) begin
$fdisplay (fd, "Iteration = %0d", i);
end
$fclose(fd);
// 2. Let us now read back the data we wrote in the previous step
fd = $fopen ("trial", "r");
while (!$feof(fd)) begin
$fgets(line, fd);
$display ("Line: %s", line);
end
// Close this file handle
$fclose(fd);
end
endmodule
module tb;
int fd; // Variable for file descriptor handle
int idx;
string str;
initial begin
// 1. Lets first open a new file and write some contents into it
fd = $fopen ("trial", "w");
for (int i = 0; i < 5; i++)
$fdisplay (fd, "Iteration = %0d", i);
$fclose(fd);
// 2. Let us now read back the data we wrote in the previous step
fd = $fopen ("trial", "r");
// fscanf returns the number of matches
while ($fscanf (fd, "%s = %0d", str, idx) == 2) begin
$display ("Line: %s = %0d", str, idx);
end
// Close this file handle
$fclose(fd);
end
endmodule
Parameters and `define
SV中存在两种定义常量的方法:parameter和`define;
(1)parameter
参数(parameter)必须定义在模块(module)内部,并且可以在实例化模块时修改;参数通常用来定义位宽和延时周期数.看下面例子:
module mem_model #(
parameter ADDR_WIDTH=8;
parameter DATA_WIDTH=32;)
(clk, addr, data);
input clk;
input [ADDR_WIDTH-1:0] addr;
output [DATA_WIDTH-1:0] data;
.....
.....
endmodule
参数可以在实例化时重新定义和修改,这用到关键字defparam,如下例:
//实例化使用默认参数
mem_model mem_1 (.clk(clk),
.addr(addr_1),
.data(data_1));
//实例化修改参数位宽
mem_model #(4,8) mem_2 (.clk(clk),
.addr(addr_2),
.data(data_2));
//实例化修改参数位宽 addr width = 32 and data width = 64.
mem_model #(32,64) mem_3 (.clk(clk),
.addr(addr_3),
.data(data_3));
//实例化修改参数位宽 data width = 64.
mem_model #(DATA_WIDTH=64) mem_4 (.clk(clk),
.addr(addr_3),
.data(data_3));
//用关键字defparam在实例化之前修改模块的参数
defparam hierarchical_path.mem_1.ADDR_WIDTH = 32;
(2)`define 宏
与parameter的不同在于,参数只在模块内部可见,而宏却是对所有文件都可见的.
一旦定义了一个宏,它就一直有效直至用另一个宏来改变当前宏的值或者使用关键字`undef来取消宏的定义;
`define WIDTH 8//定义宏
....
`undef WIDTH//取消宏定义
为了避免重复定义,可以使用`ifdef和`ifndef,它们可以使用类似if...else的结构.`ifdef是用来检查宏定义是否存在,而`ifndef是用来检查宏定义是否不存在。
`ifdef TYPE_1//如果已经定义了TYPE_1宏,则`define WIDTH 8;否则`define WIDTH 32
`define WIDTH 8
`else//else选项是可有可无的
`define WIDTH 32
`endif
`ifndef TYPE_1//如果没有定义TYPE_1宏,则`define WIDTH 8;如果定义了TYPE_1宏,则`define WIDTH 32
`define WIDTH 8
`else//else选项是可有可无的
`define WIDTH 32
`endif