电子说
在TB当中,直接对DUT(Design under Test)的信号进行读写是很常见的操作。以一个简单的计数器例子为例:
定义一个模块名为a(取名比较随意),功能是带使能和异步复位的计数器,描述如下:
reg [31:0] cnt_r;
assign count = cnt_r;
always @ (posedge clk or posedge rst)
begin
if (rst)
cnt_r <= 'd0;
else if (en)
cnt_r <= count + 32'd1;
end
同时定义一个模块名为b,功能是带使能、置位和异步复位的计数器,描述如下:
reg [31:0] cnt_r;
assign count = cnt_r;
always @ (posedge clk or posedge rst)
begin
if (rst)
cnt_r <= 'd0;
else if (set)
cnt_r <= load_val;
else if (en)
cnt_r <= count + 32'd1;
end
同时创建一个wrapper将模块b例化,并在TB中完成简单的测试工作:
`include "macros.v"
module tb;
reg clk ;
reg rst ;
reg en ;
reg set ;
wire [31:0] a_cnt;
wire [31:0] b_cnt;
wire [31:0] b_cnt1;
initial begin
clk = 0;
rst = 1;
en = 0;
set = 0;
#100
rst = 0;
#100
en = 1;
#100
set = 1;
#20
set = 0;
#100
$finish;
end
always #10 clk = ~clk;
initial
forever begin
#20
$display("Time: %t A_CNT: %d B_CNT:%d", $time, `A_MODULE_INST.count, `B_MODULE_NAME.count);
end
a a_inst(
.clk(clk) ,
.rst(rst) ,
.en (en) ,
.count (a_cnt)
);
b_wrapper b_inst(
.clk(clk) ,
.rst(rst) ,
.set(set) ,
.load_val(32'd20),
.en(en) ,
.count (b_cnt)
);
`ifdef B_MULT
b_wrapper b_inst_1(
.clk(clk) ,
.rst(rst) ,
.set(set) ,
.load_val(32'd40),
.en(en) ,
.count (b_cnt1)
);
`endif
endmodule
a和b模块的XMR调用通过宏定义控制,在tb的display函数中使用。宏定义内容如下:
`define A_MODULE_INST tb.a_inst
`define B_MODULE_NAME b
//`define B_MULT
所以整体的层次结构如下:
可以看到,a模块的XMR方式是从顶层的tb开始,根据Instance name找到的例化的a模块;这种根据结构和Instance Name跨模块调用的方式叫做Hierarchical Reference, 这也是最常见的方式。
优缺点如下:
1.在Verilog的一个module中,Instance Name是唯一的,所以该种方式指向性很明确,支持精细化地控制同一个module例化的不同模块信号。
2.但如果层次结构发生变化,或者Instance Name改变,会爆出Cross Module Reference Error。
b模块的XMR方式则是直接定义了b模块的模块名,然后verilog开始从定义的顶层开始查找,没找到就进入下一层继续寻找,直到找到唯一的那个模块名匹配的为止:
它的优缺点与Hierarchical Reference正好相反:
1.不用担心层次结构和Instance Name的变化会影响跨模块调用。
2.使用该方式需要保证唯一性,不然也会爆出Cross Module Reference Error。
实验结果如下:
在未定义B_MULT时:
定义B_MULT时:
总结:
Hierarchical Reference方式严谨但无灵活性,操作上可实现精细化控制,支持多模块;Upwards Name Referencing方式灵活,但只支持单模块,应用场景限制高。
所以,这也是Hierarchical Reference应用广泛的原因吧,如果不是最近犯错,我可能都不会注意到另一种XMR方式。
全部0条评论
快来发表一下你的评论吧 !