博客
关于我
Verilog十大基本功3(testbench的设计 iout类型端口信号处理)
阅读量:221 次
发布时间:2019-02-28

本文共 6937 字,大约阅读时间需要 23 分钟。

需求说明:Verilog设计基础

内容       :testbench的设计 iout类型端口信号处理

来自       :时间的诗

3 testbench 的技巧

1)如果激励中有一些重复的项目,可以考虑将这些语句编写成一个 task,这样会给书写和仿真带来很大方便。例如,
    一个存储器的 testbench 的激励可以包含 write, read 等 task。
2)如果 DUT 中包含双向信号(inout),在编写 testbench 时要注意。需要一个 reg 变量来表示其输入,还需要一个 wire
    变量表示其输出。
3)如果 initial 块语句过于复杂,可以考虑将其分为互补相干的几个部分,用数个 initial 块来描述。在仿真时,这些
    initial 块会并发运行。这样方便阅读和修改。
4)每个 testbench 都最好包含$stop 语句,用以指明仿真何时结束。
5)加载测试向量时,避免在时钟的上下沿变化,比如数据最好在时钟上升沿之前变化,这也符合建立时间的要求。

4 一个简单的例子

DUT:

module counter (         clk,          reset,          enable,          count       );         input         clk;  input         reset;  input         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;  endendmodule
testbench:
module counter_tb;  reg        clk;  reg        reset;  reg        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 lineendmodule

5 双向端口

这个没用过,从网上找的,如果有问题,大家再讨论吧
芯片外部引脚很多都使用 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 RTLinout 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
很多读者反映仿真双向端口的时候遇到困难,这里介绍一下双向端口的仿真方法。一个典型的双向端口如图所示。
典型的双向端口可以用 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
仿真时需要验证双向端口能正确输出数据,以及正确读入数据,因此需要驱动 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 定义),因此驱动方法与单向端口有所不同。
用 Verilog 代码编写的 testbench 如下,其中使用了自动结果比较,随机化激励产生等技术。

`timescale 1ns/10psmodule 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

6. 高级用法

比如 PLI 之类。有需要的,大家再讨论
总体感觉, testbench 是个很难的事情, 这里讨论的只是一些最基本的东西。 真正有技术含量的是 testcase 的设计,设计
阶段合理层次设计以及模块划分等等,我没有做过很大的项目,所以这方面也没有办法提供更多的帮助。经验丰富的
大牛不妨出来讲讲经验, ^_^
testbench 用的较多的东西是:
1、输入数据文件的产生,一般由 matlab 产生,这方面经常涉及浮点到定点的转换、进制的转换的问题;
2、输入数据文件的输出文件的设置;
3、 VCD、 fsdb 等和其他 eda 软件接口的文件的输出;
4、一定范围内的随机数的产生
5、双向端口的仿真
6、与上层 dsp 等 cpu 接口的时序仿真。
....
panwest: testbench 设计的掌握的技术可多可少,模块级的 HDL 就可以搞定了:
激励产生:高级语言实现( C, C++, Java 等)
DUT: HDL 实现
参考单元:高级语言实现( C, C++, Java 等)
初始化:脚本语言( perl, Tcl/TK)
波形输出:一般脚本自动对比了
数据通过 PLI 进行联系
日后 systemVerilog, e 等语言完善后会减轻一些建立平台的工作量。

synopsys vera support verification.

bench : vera produce

DUT:

ref module: vera

monitor: vera
coverage analysis : VCS or vera
vera can load c, C++ file .
systemverilog 应该是以后的趋势
一些 random scenario 与 tlm base model 都可以用systemverilog 完成
而一个好的 framework 很重要现在有两各 framework 可以选择vmm, ovm
不过要用上述的 framework 需要很大的 oop 基础要花很长时间学习
关于 systemC 或者 system verilog,大家可以查找相关文档。

你可能感兴趣的文章
mysql 导入 sql 文件时 ERROR 1046 (3D000) no database selected 错误的解决
查看>>
mysql 导入导出大文件
查看>>
MySQL 导出数据
查看>>
mysql 将null转代为0
查看>>
mysql 常用
查看>>
MySQL 常用列类型
查看>>
mysql 常用命令
查看>>
Mysql 常见ALTER TABLE操作
查看>>
MySQL 常见的 9 种优化方法
查看>>
MySQL 常见的开放性问题
查看>>
Mysql 常见错误
查看>>
mysql 常见问题
查看>>
MYSQL 幻读(Phantom Problem)不可重复读
查看>>
mysql 往字段后面加字符串
查看>>
mysql 快照读 幻读_innodb当前读 与 快照读 and rr级别是否真正避免了幻读
查看>>
MySQL 快速创建千万级测试数据
查看>>
mysql 快速自增假数据, 新增假数据,mysql自增假数据
查看>>
MySql 手动执行主从备份
查看>>
Mysql 批量修改四种方式效率对比(一)
查看>>
Mysql 报错 Field 'id' doesn't have a default value
查看>>