SV之其他

目录

 

Dynamic Casting

Package

file operations

Parameters and `define


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

 

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值