SV之其他

    xiaoxiao2023-11-15  141

    目录

     

    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 = READ2 = ", 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 = READ2 = ", 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("@ ns 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 : ", fd); else $display("File was NOT opened successfully : ", 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 : ", fd); else $display("File was NOT opened successfully : ", 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 : ", fd_w); else $display("File was NOT opened successfully : ", fd_w); if (fd_r) $display("File was opened successfully : ", fd_r); else $display("File was NOT opened successfully : ", fd_r); if (fd_a) $display("File was opened successfully : ", fd_a); else $display("File was NOT opened successfully : ", 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 = ", 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 = ", 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 = ", 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 = ", str, idx) == 2) begin $display ("Line: %s = ", 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

     

    最新回复(0)