通过参数传递改变在被引用子模块中已定义的参数,顶层模块和子模块中都定义parameter型常数

前段时间在FPGA上用Verilog写了一个多端口以太网的数额分发模块,因为每个网口需要单独的MAC地址和IP地址,为了便利中期修改,在规划中应用parameter来定义那多少个地址和多少总线的位宽等常量。 
顿时的做法是,顶层模块和子模块中都定义parameter型常数,在顶层模块引用子模块时,通过参数传递改变在被引用子模块中已定义的参数,实现在顶层模块统一管理参数的效能,代码如下

前段时间在FPGA上用Verilog写了一个多端口以太网的多寡分发模块,因为每个网口需要单独的MAC地址和IP地址,为了便利后期修改,在统筹中应用parameter来定义这一个地址和数据总线的位宽等常量。 
旋即的做法是,顶层模块和子模块中都定义parameter型常数,在顶层模块引用子模块时,通过参数传递改变在被引用子模块中已定义的参数,实现在顶层模块统一管理参数的功效,代码如下

testbench

 1 module top(); // 顶层模块
 2 parameter eth1_ip_addr = {8'd192, 8'd168, 8'd100, 8'd1}; // 以太网1 IP地址 192.168.100.1
 3 parameter eth2_ip_addr = {8'd192, 8'd168, 8'd100, 8'd2}; // 以太网2 IP地址 192.168.100.2
 4 
 5 // 子模块1例化
 6 sub1
 7 #(  .ip_addr(eth1_ip_addr) // 参数传递
 8 )
 9 sub1_inst( ); //端口映射
10 
11 // 子模块2例化
12 sub2
13 #(  .ip_addr(eth2_ip_addr) // 参数传递
14 )
15 sub2_inst( ); //端口映射
 1 module top(); // 顶层模块
 2 parameter eth1_ip_addr = {8'd192, 8'd168, 8'd100, 8'd1}; // 以太网1 IP地址 192.168.100.1
 3 parameter eth2_ip_addr = {8'd192, 8'd168, 8'd100, 8'd2}; // 以太网2 IP地址 192.168.100.2
 4 
 5 // 子模块1例化
 6 sub1
 7 #(  .ip_addr(eth1_ip_addr) // 参数传递
 8 )
 9 sub1_inst( ); //端口映射
10 
11 // 子模块2例化
12 sub2
13 #(  .ip_addr(eth2_ip_addr) // 参数传递
14 )
15 sub2_inst( ); //端口映射
  1. 鼓舞的发出

子模块1和2分别实现以太网1和2的收发功用,ip_addr为各自的地面IP地址,通过参数传递将eth1_ip_addr和eth2_ip_addr的值分别传递给五个子模块的ip_addr常数。 
而是上述方法的老毛病在于: 

子模块1和2独家实现以太网1和2的收发效能,ip_addr为独家的本土IP地址,通过参数传递将eth1_ip_addr和eth2_ip_addr的值分别传递给四个子模块的ip_addr常数。 
只是上述办法的弱点在于: 

对于testbench而言,端口应当和被测试的module一一对应。端口分为input,output和inout类型发生刺激信号的时候,input对应的端口应当表明为reg,
output对应的端口声明为wire,inout端口比较特别,下边专门讲解。

  1. 当子模块的参数较多,并且要再三实例化时,代码会显得相比臃肿 
    2.
    倘若工程的层系有4层,某个参数只在第4层的模块使用到,为了贯彻将参数从顶层传递到第4层,第2层和第3层模块的参数列表中必须含有该参数。 
    解决上述问题有两种办法: 
    1. 施用defparam命令在顶层模块对子模块中的参数重定义,不过该办法同样存在一个题材:改变一度实例化后的模块中的参数,必须用英文小数点(.)表示层次逻辑关系,如下所示

    1 module top;
    2 reg clk;
    3 reg [0:4] in1;
    4 reg [0:9] in2;
    5 wire [0:4] o1;
    6 wire [0:9] o2;
    7 // 子模块实例化
    8 vdff m1 (o1, in1, clk);
    9 vdff m2 (o2, in2, clk);
    10 endmodule
    11 // 子模块定义
    12 module vdff (out, in, clk);
    13 parameter size = 1, delay = 1;
    14 input [0:size-1] in;
    15 input clk;
    16 output [0:size-1] out;
    17 reg [0:size-1] out;
    18
    19 always @(posedge clk)
    20 # delay out = in;
    21 endmodule
    22 // 参数重定义模块
    23 module annotate;
    24 defparam
    25 top.m1.size = 5, // size参数的层次化表示
    26 top.m1.delay = 10,
    27 top.m2.size = 10,
    28 top.m2.delay = 20;
    29 endmodule

  1. 当子模块的参数较多,并且要再三实例化时,代码会显得较为臃肿 
    2.
    一旦工程的层系有4层,某个参数只在第4层的模块使用到,为了贯彻将参数从顶层传递到第4层,第2层和第3层模块的参数列表中必须带有该参数。 
    解决上述问题有两种办法: 
    1. 接纳defparam命令在顶层模块对子模块中的参数重定义,不过该办法一致存在一个题材:改变一度实例化后的模块中的参数,必须用英文小数点(.)表示层次逻辑关系,如下所示

    1 module top;
    2 reg clk;
    3 reg [0:4] in1;
    4 reg [0:9] in2;
    5 wire [0:4] o1;
    6 wire [0:9] o2;
    7 // 子模块实例化
    8 vdff m1 (o1, in1, clk);
    9 vdff m2 (o2, in2, clk);
    10 endmodule
    11 // 子模块定义
    12 module vdff (out, in, clk);
    13 parameter size = 1, delay = 1;
    14 input [0:size-1] in;
    15 input clk;
    16 output [0:size-1] out;
    17 reg [0:size-1] out;
    18
    19 always @(posedge clk)
    20 # delay out = in;
    21 endmodule
    22 // 参数重定义模块
    23 module annotate;
    24 defparam
    25 top.m1.size = 5, // size参数的层次化表示
    26 top.m1.delay = 10,
    27 top.m2.size = 10,
    28 top.m2.delay = 20;
    29 endmodule

1)直接赋值。

现实应用格局见http://www.cnblogs.com/hechengfei/p/4116667.html 
2. 使用`inculude预处理命令 
日前在STM官网上找M95xxx多重的EEPROM资料时,看到该芯片的Verilog
Testbench模块,如下图所示

切实采用形式见http://www.cnblogs.com/hechengfei/p/4116667.html 
2. 使用`inculude预处理命令 
多年来在STM官网上找M95xxx雨后春笋的EEPROM资料时,看到该芯片的Verilog
Testbench模块,如下图所示

一般用initial块给信号赋初值,initial块执行五回,always或者forever表示由事件激发反复实践。

图片 1

图片 2

举例,一个module

 

 

module exam();

M95XXX_Macro_Mux.v:定义M95xxx多样芯片的AC参数 
M95XXX_Parameters.v:定义Memory大小、有效地址位数和Page大小等参数 
M95xxx_Testbench.v:链接 M95xxx_Driver.v和M95xxx_Memory.v 
M95xxx_Driver.v:模拟M95xxx_Memory.v文件依照SPI接口的读写行为 
M95xxx_Memory.v:M95xxx EEPROM的行为级描述模型 
在上述文件中,M95XXX_Macro_Mux.v和M95XXX_Parameters.v定义了全局参数,被另外文件通过’include命令包含到文件内。具体使用方法如下 
M95XXX_Parameters.v 参数定义文件

M95XXX_Macro_Mux.v:定义M95xxx系列芯片的AC参数 
M95XXX_Parameters.v:定义Memory大小、有效地址位数和Page大小等参数 
M95xxx_Testbench.v:链接 M95xxx_Driver.v和M95xxx_Memory.v 
M95xxx_Driver.v:模拟M95xxx_Memory.v文件遵照SPI接口的读写行为 
M95xxx_Memory.v:M95xxx EEPROM的行为级描述模型 
在上述文件中,M95XXX_Macro_Mux.v和M95XXX_Parameters.v定义了大局参数,被其他文件通过’include命令包含到文件内。具体使用方法如下 
M95XXX_Parameters.v 参数定义文件

reg rst_n;

1 `define MEM_ADDR_BITS       11              //memory address bits
2 `define PAGE_ADDR_BITS      6               //page address bits
1 `define MEM_ADDR_BITS       11              //memory address bits
2 `define PAGE_ADDR_BITS      6               //page address bits

reg clk;

M95xxx_Driver.v   驱动文件

M95xxx_Driver.v   驱动文件

reg data;

`include "M95XXX_Parameters.v"
reg[`MEM_ADDR_BITS-1:0] memory_address;
reg[`PAGE_ADDR_BITS-1:0] page_address;
`include "M95XXX_Parameters.v"
reg[`MEM_ADDR_BITS-1:0] memory_address;
reg[`PAGE_ADDR_BITS-1:0] page_address;

initial

可以看来要调用参数文件中参数时,需要: 
1. 使用'include指令包含参数文件 
2. 使用反引号+参数名的艺术来调用该参数

可以看来要调用参数文件中参数时,需要: 
1. 使用'include一声令下包含参数文件 
2. 使用反引号+参数名的点子来调用该参数

begin



clk=1’b0;

 

 

rst=1’b1;

STM MP95xxx体系EEPROM Verilog Testbench模型的链接地址: 
https://gitee.com/hombeen/codes/y9rb0g7ej385sipqozvla95

STM MP95xxx种类EEPROM Verilog Testbench模型的链接地址: 
https://gitee.com/hombeen/codes/y9rb0g7ej385sipqozvla95

#10

rst=1’b0;

#500

rst=1’b1;

end

always

begin

#10

clk=~clk;

end

世家应该注意到有个#标记,该符号的意趣是指延迟相应的时刻单位。该时间单位由timscale决定.一般在testbench的起首定义时间单位和仿真
精度,比如`timescale
1ns/1ps,前边一个是意味着时间单位,前面一个意味仿真时间精度。以地点的事例而言,一个时钟周期是20个单位,也就是20ns。而仿真时间精度的概
念就是,你能看到1.001ns时对应的信号值,而假若timescale
1ns/1ns,1.001ns时候的值就不可能看出。对于一个计划而言,时间刻度应该统一,假设计划文本和testbench里面的流年刻度不一致,仿真
器默认以testbench为准。一个较好的不二法门是写一个global.v文件,然后用include的模式,可以避免那一个题材。

对此频繁实践的操作,可写成task,然后调用,比如

task load_count;

input [3:0] load_value;

begin

@(negedge clk_50);

$display($time, ” << Loading the counter with %h >>”,
load_value);

load_l = 1’b0;

count_in = load_value;

@(negedge clk_50);

load_l = 1’b1;

end

endtask //of load_count

initial

begin

load_count(4’hA);   // 调用task

end

此外像forever,for,function等等语句用法类似,即使不肯定都能综合,但是用在testbench里面很方便,大家可以自动查阅参考文档

2) 文件输入

有时,需要大量的数额输入,直接赋值的话相比较麻烦,可以先生成多少,再将数据读入到寄存器中,需要时取出即可。用
$readmemb系统任务从文本文件中读取二进制向量(可以涵盖输入激励和出口期望值)。$readmemh
用于读取十六进制文件。例如:

reg [7:0]   mem[1:256]   //   a 8-bit, 256-word 定义存储器mem

initial   $readmemh ( “E:/readhex/mem.dat”, mem ) //
将.dat文件读入寄存器mem中

initial   $readmemh ( “E:/readhex/mem.dat”, mem, 128, 1 ) //
参数为寄存器加载数据的地点始终

2.   查看仿真结果

对此简易的module来说,要在modelsim的仿真窗口里面看波形,就用add wave
..命令

譬如,testbench的顶层module名叫tb,要看时钟信号,就用add wave tb.clk

要翻看所有信号的时候,就用 add wave /*

自然,也得以在workspace下的sim窗口里面右键单击instance来添加波形

对此复杂的虚假,免不了要记录波形和数目到文件之中去。

1)波形文件记录

广大的波浪文件一般有两种,vcd和fsdb,debussy是个很好的工具,辅助fsdb,所以最好是modelsim+debussy的咬合

默认状况下,modelsim不认得fsdb,所以需要先装debussy,再生成fsdb文件。

$dumpfile和$dumpvar是verilog语言中的三个系统任务,可以调用这多少个系列任务来创建和将点名音信导入VCD文件.

对于fsdb文件来说,对应的命令是fsdbDumpfile,dumpfsdbvars

(什么是VCD文件?
答:VCD文件是在对规划开展的虚伪过程中,记录各个信号取值变化情状的音讯记录文件。EDA工具通过读取VCD格式的文件,彰显图形化的虚假波形,所以,能够把VCD文件简单地说是波形记录文件.)上面分别讲述它们的用法并举例表达之。

$dumpfile系统任务:为所要创制的VCD文件指定文件名。

举例来说(”//”符号后的内容为注释文字):

initial

$dumpfile (“myfile.dump”);
//指定VCD文件的名字为myfile.dump,仿真音信将记录到此文件

$dumpvar系统任务:指定需要记录到VCD文件中的信号,可以指定某一模块层次上的有所信号,也足以独自指定某一个信号。

典型语法为$dumpvar(level, module_name);
参数level为一个平头,用于指定层次数,参数module则指定要记录的模块。整句的意味就是,对于指定的模块,包括其下相继层次(层次数由
level指定)的信号,都亟待记录到VCD文件中去。

举例:

initial

$dumpvar (0, top);
//指定层次数为0,则top模块及其上边各层次的兼具信号将被记录

initial

$dumpvar (1, top); //记录模块实例top以下一层的信号

//层次数为1,即记录top模块这一层次的信号

//对于top模块中调用的更深层次的模块实例,则不记录其信号变化

initial

$dumpvar (2, top); //记录模块实例top以下两层的信号

//即top模块及其下一层的信号将被记录

尽管模块top中蕴藏有子模块module1,而我辈期待记录top.module1模块以下两层的信号,则语法举例如下:

initial

$dumpvar (2, top.module1); //模块实例top.module1及其下一层的信号将被记录

一经模块top包含信号signal1和signal2(注意是变量而不是子模块),
如我们希望只记录这多个信号,则语法举例如下:

initial

$dumpvar (0, top.signal1, top.signal2);
//即使指定了层次数,但层次数是不影响单独指定的信号的

//即指定层次数和单身指定的信号无关

俺们甚至足以在同一个$dumpvar的调用中,同时指定某些层次上的拥有信号和某个单独的信号,即便模块top包含信号signal1,同时涵盖有子模
块module1,假使大家不光希望记录signal1以此独立的信号,而且还愿意记录子模块module1以下三层的享有信号,则语法举例如下:

initial

$dumpvar (3, top.signal1, top.module1); //指定层次数和单独指定的信号无关

//所以层次数3只听从于模块top.module1, 而与信号

top.signal1无关

下面这些事例和底下的口舌是同样的:

initial

begin

$dumpvar (0, top.signal1);

$dumpvar (3, top.module1);

end

$dumpvar的专门用法(不带任何参数):

initial

$dumpvar; //无参数,表示计划中的所有信号都将被记录

末段,我们将$dumpfile和$dumpvar这五个连串任务的运用方法在底下的例子中概括表达,假如我们有一个规划实例,名为
i_design,此计划中蕴藏模块module1,模块module1下边还有不少层次,大家愿意对这多少个规划开展虚伪,并将仿真过程中模块
module1及其以下有所层次中所有信号的扭转情况,记录存储到名为mydesign.dump的VCD文件中去,则例示如下:

initial

begin

$dumpfile (“mydesign.dump”); //指定VCD文件名为mydesign.dump

$dumpvar (0, i_design.module1);
//记录i_design.module1模块及其下边层次中装有模块的所有信号

end

对此生成fsdb文件而言,也是类似的

initial

begin

$fsdbDumpfile(“tb_xxx.fsdb”);

$fsdbDumpvars(0,tb_xxx);

end

2)文件输出结果

integer out_file;   // out_file 是一个文书讲述,需要定义为 integer类型

out_file = $fopen ( ” cpu.data ” ); // cpu.data
是索要开辟的文书,也就是最终的输出文本

计划中的信号值可以经过$fmonitor, $fdisplay,$fwrite

中间$fmonitor只要有浮动就直接记录,$fdisplay和$fwrite需要接触条件才记录

例子:

initial begin

$fmonitor(file_id, “%m: %t in1=%d o1=%h”, $time, in1, o1);

end

always@(a or b)

begin

$fwrite(file_id,”At time%t a=%b b=%b”,$realtime,a,b);

end

3 testbench的技巧

1).假如点燃中有局部再次的连串,能够设想将这一个讲话编写成一个task,这样会给书写和虚假带来很大方便。例如,一个存储器的testbench的激发能够涵盖write,read等task。

2).假若DUT中涵盖双向信号(inout),在编辑testbench时要注意。需要一个reg变量来代表其输入,还索要一个wire变量表示其出口。

3).假使initial块语句过于复杂,可以设想将其分成互补相干的几个部分,用数个initial块来讲述。在虚假时,这多少个initial块会并发运行。这样方便阅读和改动。

4).每个testbench都最好包含$stop语句,用以指明仿真什么时候截止。

5).加载测试向量时,制止在时钟的上下沿变化,比如数据最好在时钟上升沿此前生成,这也顺应建顿时间的渴求。

4.一个简单易行的例证

module counter (clk, reset, enable, count);

input clk, reset, enable;

output [3:0] count;

reg [3:0] count;

always @ (posedge clk)

if (reset == 1’b1) begin

count <= 0;

end else if ( enable == 1’b1) begin

count <= count + 1;

end

endmodule

testbench

module counter_tb;

reg clk, reset, enable;

wire [3:0] count;

counter U0 (

.clk (clk),

.reset   (reset),

.enable (enable),

.count   (count)

);

initial begin

clk = 0;

reset = 0;

enable = 0;

end

always

#5   clk =   ! clk;

initial   begin

$dumpfile (“counter.vcd”);

$dumpvars;

end

initial   begin

$display(“\t\ttime,\tclk,\treset,\tenable,\tcount”);

$monitor(“‰d,\t‰b,\t‰b,\t‰b,\t‰d”,$time, clk,reset,enable,count);

end

initial

#100   $finish;

//Rest of testbench code after this line

endmodule

5   双向端口

本条自己没用过,完全是从网上google的,假设有问题,我们再探讨吗

芯片外部引脚很多都采取inout类型的,为的是节省管腿。一般信号线用做总线等双向数据传输的时候就要用到INOUT类型了。就是一个端口还要做输入和
输出。
inout在切实实现上一般用三态门来实现。三态门的第两个情景就是高阻’Z’。当inout端口不出口时,将三态门置高阻。这样信号就不会因为双方同时
输出而失误了,更详尽的始末可以搜索一下三态门tri-state的资料.

1 使用inout类型数据,可以用如下写法:

inout data_inout;

input data_in;

reg data_reg;//data_inout的画面寄存器

reg link_data;

assign data_inout=link_data?data_reg:1’bz;//link_data控制三态门

//对于data_reg,可以经过结合逻辑或者时序逻辑遵照data_in对其赋值.通过控制link_data的高低电平,从而设置data_inout是出口数据或者处于高阻态,假设处在高阻态,则此时作为输入端口使用.link_data能够由此有关电路来控制.

2
编写测试模块时,对于inout类型的端口,需要定义成wire类型变量,而任何输入端口都定义成reg类型,这五头是有分其它.

当上边例子中的data_inout用作输入时,需要赋值给data_inout,另外情状可以断开.此时可以用assign语句实现:assign
data_inout=link?data_in_t:1’bz;其中的link
,data_in_t是reg类型变量,在测试模块中赋值.

此外,能够设置一个出口端口观察data_inout用作输出的情事:

Wire data_out;

Assign data_out_t=(!link)?data_inout:1’bz;

else,in RTL

inout use in top module(PAD)

dont use inout(tri) in sub module

也就是说,在中间模块最好不用现身inout,假诺真的需要,那么用两个port实现,到顶层的时候再用三态实现。理由是:在非顶层模块用双向口的话,该
双向口必然有它的上层跟它不断。既然是双向口,则上层至少有一个输入口和一个输出口联到该双向口上,则发出四个里面输出单元连接到一块的意况出现,这样在
综合时频繁会出错。

对双向口,大家可以将其知道为2个轻重:一个输入分量,一个出口分量。此外还索要一个操纵信号控制输出分量啥时候输出。此时,我们就可以很容易地对双向端口建模。

例子:

CODE:

module dual_port (

….

inout_pin,

….

);

inout inout_pin;

wire inout_pin;

wire input_of_inout;

wire output_of_inout;

wire out_en;

assign input_of_inout = inout_pin;

assign inout_pin = out_en ? output_of_inout : 高阻;

endmodule

可见,此时input_of_inout和output_of_inout就可以视作普通信号使用了。

在虚假的时候,需要专注双向口的处理。假设是直接与其它一个模块的双向口连接,那么只要保证一个模块在出口的时候,其它一个模块没有出口(处于高阻态)就足以了。

假如是在ModelSim中作为单身的模块仿真,那么在模块输出的时候,无法应用force命令将其设为高阻态,而是接纳release命令将总线释放掉

有的是初学者在写testbench举办虚伪和认证的时候,被inout双向口难住了。仿真器老是指示错误不可能举办。下边是自我个人对inout端口写
testbench仿真的一对总计,并举例进行求证。在这里先要表明一下inout口在testbench中要定义为wire型变量。

先如果有一源代码为:

module xx(data_inout , ……..);

inout data_inout;

……………………

assign data_inout=(! link)?datareg:1’bz;

endmodule

措施一:使用相反控制信号inout口,等于五个模块之间用inout双向口互连。这种艺术要留心assign
语句只可以放在initial和always块内。

module test();

wire data_inout;

reg data_reg;

reg link;

initial begin

……….

end

assign data_inout=link?data_reg:1’bz;

endmodule

措施二:使用force和release语句,但这种艺术不可以可靠反映双向端口的信号变化,但这种情势可以反在块内。

module test();

wire data_inout;

reg data_reg;

reg link;

#xx;        //延时

force data_inout=1’bx;           //强制作为输入端口

……………

#xx;

release data_inout;       //释放输入端口

endmodule

洋洋读者反映仿真双向端口的时候碰到困难,这里介绍一下双向端口的假冒伪劣艺术。一个典型的双向端口如图1所示。

其中inner_port与芯片里面任何逻辑相连,outer_port为芯片外部管脚,out_en用于控制双向端口的趋向,out_en为1时,端口为出口方向,out_en为0时,端口为输入方向。

用Verilog语言描述如下:

module bidirection_io(inner_port,out_en,outer_port);

input out_en;

inout[7:0] inner_port;

inout[7:0] outer_port;

assign outer_port=(out_en==1)?inner_port:8’hzz;

assign inner_port=(out_en==0)?outer_port:8’hzz;

endmodule

用VHDL语言描述双向端口如下:

library ieee;

use IEEE.STD_LOGIC_1164.ALL;

entity bidirection_io is

port ( inner_port : inout std_logic_vector(7 downto 0);

out_en : in std_logic;

outer_port : inout std_logic_vector(7 downto 0) );

end bidirection_io;

architecture behavioral of bidirection_io is

begin

outer_port<=inner_port when out_en=’1′ else (OTHERS=>’Z’);

inner_port<=outer_port when out_en=’0′ else (OTHERS=>’Z’);

end behavioral;

虚假时索要表明双向端口能科学输出数据,以及科学读入数据,因而需要驱动out_en端口,当out_en端口为1时,testbench驱动
inner_port端口,然后检查outer_port端口输出的数据是否科学;当out_en端口为0时,testbench驱动
outer_port端口,然后检查inner_port端口读入的数码是否科学。由于inner_port和outer_port端口都是双向端口(在
VHDL和Verilog语言中都用inout定义),因而使得方法与单向端口有所不同。

表达该双向端口的testbench结构如图2所示。

这是一个self-checking
testbench,可以活动检查仿真结果是否正确,并在Modelsim控制台上打印出指示信息。图中Monitor完成信号采样、结果机关相比较的职能。

testbench的干活经过为

1)out_en=1时,双向端口处于输出状态,testbench给inner_port_tb_reg信号赋值,然后读取outer_port_tb_wire的值,假使两者如出一辙,双向端口工作正常。

2)out_en=0时,双向端口处于输如状态,testbench给outer_port_tb_reg信号赋值,然后读取inner_port_tb_wire的值,假设两者如出一辙,双向端口工作正常化。

用Verilog代码编写的testbench如下,其中使用了活动结果相比,随机化激励暴发等技术。

`timescale 1ns/10ps

module tb();

reg[7:0] inner_port_tb_reg;

wire[7:0] inner_port_tb_wire;

reg[7:0] outer_port_tb_reg;

wire[7:0] outer_port_tb_wire;

reg out_en_tb;

integer i;

initial

begin

out_en_tb=0;

inner_port_tb_reg=0;

outer_port_tb_reg=0;

i=0;

repeat(20)

begin

#50

i=$random;

out_en_tb=i[0]; //randomize out_en_tb

inner_port_tb_reg=$random; //randomize data

outer_port_tb_reg=$random;

end

end

//**** drive the ports connecting to bidirction_io

assign
inner_port_tb_wire=(out_en_tb==1)?inner_port_tb_reg:8’hzz;

assign
outer_port_tb_wire=(out_en_tb==0)?outer_port_tb_reg:8’hzz;

//instatiate the bidirction_io module

bidirection_io
bidirection_io_inst(.inner_port(inner_port_tb_wire),

.out_en(out_en_tb),

.outer_port(outer_port_tb_wire));

//***** monitor ******

always@(out_en_tb,inner_port_tb_wire,outer_port_tb_wire)

begin

#1;

if(outer_port_tb_wire===inner_port_tb_wire)

begin

$display(“\n **** time=%t ****”,$time);

$display(“OK! out_en=%d”,out_en_tb);

$display(“OK! outer_port_tb_wire=%d,inner_port_tb_wire=%d”,

outer_port_tb_wire,inner_port_tb_wire);

end

else

begin

$display(“\n **** time=%t ****”,$time);

$display(“ERROR! out_en=%d”,out_en_tb);

$display(“ERROR! outer_port_tb_wire != inner_port_tb_wire” );

$display(“ERROR! outer_port_tb_wire=%d, inner_port_tb_wire=%d”,

outer_port_tb_wire,inner_port_tb_wire);

end

end

endmodule

  1. 尖端用法

比如pli之类的东西,我也没用过。。。有需要的,大家再议论

总体感觉,testbench是个很难的业务,这里商量的只是有的最主旨的东西。真正有技术含量的是testcase的设计,设计阶段合理层次设计以及模
块分割等等,我从未做过很大的类型,所以这方面也从不办法提供更多的帮忙。经验充裕的大牛不妨出来讲讲经验,^_^

相关文章