资源描述
转载:verilog双向端口的使用2009-05-23 16:19 芯片外部引脚很多都使用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:lbz;/link_data 控制三态门/对于data_reg,可以通过组合逻辑或者时序逻辑根据data_in对其赋值.通过 控制link_data的高低电平,从而设置data_inout是输出数据还是处于高阻态, 如果处于高阻态,则此时当作输入端口使用.link_data可以通过相关电路来控 制.2编写测试模块时,对于inout类型的端口,需要定义成wire类型变量,而其它 输入端口都定义成reg类型,这两者是有区别的.当上面例子中的data_inout用作输入时,需要赋值给data_inout,其余情况可 以断开此时可以用assign语句实现:assigndata_inout=link?data_in_t:lbz;其中的 link ,data_in_t 是 reg 类型变量, 在测试模块中赋值.另外,可以设置一个输出端口观察data_inout用作输出的情况:Wire data_out;Assign data_out_t=(!link)?data_inout:1bz;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:1bz;endmodule 方法一:使用相反控制信号inout 口,等于两个模块之间用inout双向口互连。 这种方法要注意 assign 语句只能放在 initial 和 always 块内。 module test();wire data_inout;reg data_reg;reg link;initial begin endassign data_inout=link?data_reg:1bz;endmodule方法二:使用 force 和 release 语句,但这种方法不能准确反映双向端口的信 号变化,但这种方法可以反在块内。module test();wire data_inout;reg data_reg;reg link;#xx;/延时force data_inout=1bx;/强制作为输入端口#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;inout7:0 inner_port;inout7:0 outer_port;assign outer_port=(out_en=1)?inner_port:8hzz;assign inner_port=(out_en=0)?outer_port:8hzz;endmodule用 VHDL 语言描述双向端口如下:library ieee;use IEEE.STD_LOGIC_1164.ALL;entity bidirection_io isport ( 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 beginouter_portZ); inner_portZ);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 test bench,可以自动检查仿真结果是否正确,并在 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 lns/10psmodule tb();reg7:0 inner_port_tb_reg;wire7:0 inner_port_tb_wire;reg7:0 outer_port_tb_reg;wire7:0 outer_port_tb_wire;reg out_en_tb;integer i;initialbeginout_en_tb=0;inner_port_tb_reg=0; outer_port_tb_reg=0;i=0;repeat(20)begin#50i=$random;out_en_tb=i0; /randomize out_en_tb inner_port_tb_reg=$random; /randomize data outer_port_tb_reg=$random;endend/* drive the ports connecting to bidirction_ioassign inner_port_tb_wire=(out_en_tb=1)?inner_port_tb_reg:8hzz; assign outer_port_tb_wire=(out_en_tb=0)?outer_port_tb_reg:8hzz;/instatiate the bidirction_io modulebidirection_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);endelsebegin$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);endend
展开阅读全文