芯片的许多外部引脚使用输入式来节省管脚。通常,当信号线用于双向数据传输(例如总线)时,使用 inout 类型。也就是说,端口同时用于输入和输出。inout 通常在特定实现中通过三态门实现。三态栅极的第三种状态是高阻抗'Z'。当输入端口不输出时,将三态栅极设置为高阻抗。这样,信号就不会因为两端同时输出而出错。
1. 使用 inout 类型数据,可以这样写:
- inout data_inout;
- input data_in;
- reg data_reg;
- reg link_data;
- assign data_inout = link_data? data_reg: 1'bz;
对于 data_reg,可以通过组合逻辑或顺序逻辑根据data_in进行分配。
通过控制 link_data 级的高、低功率,从而设置 data_inout 输出数据或处于高阻抗状态,如果处于高阻抗状态,此时作为输入端口可以通过使用 link_data 相关电路进行控制。
2. 写入测试模块,对于输入类型的端口,需要定义为 wire 类型变量,而其他输入端口都定义为 reg 类型,
两者是有区别的。
当将上面示例中的data_inout用作输入时,需要将其分配给data_inout,其余部分可以断开连接。
这时,可以使用 assign 语句来实现:assign data_inout = link?Data_in_t: 1'bz;
其中 link 和 data_in_t 是 reg 类型变量,在测试模块中分配值。
此外,可以设置输出端口以观察 data_inout 用作输出的情况:
并且在 RTL 中,inout 在顶部模块中输入使用 不要在子模块中使用 inout(tri)
换句话说,最好不要在内部模块中有 inout。如果确实需要它,请使用两个端口来实现它,然后在达到顶层时使用三态实现。原因是:如果一个非顶层模块中使用了一个双向端口,那么这个双向端口的上层必须连接到它。由于是双向端口,因此至少将一个输入端口和一个输出端口连接到上层的双向端口,并且将两个内部输出单元连接在一起,这在合成过程中经常出错。对于双向端口,我们可以将其理解为两个组件:输入组件和输出组件。此外,还需要一个控制信号来控制输出组件的输出时间。
- 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: 1'bz;
- endmodule
可以看出,此时 input_of_inout 和 output_of_inout 可以作为普通信号使用。
在仿真过程中,要注意双向端口的处理。如果直接连接到另一个模块的双向端口,就足以保证当一个模块输出时,另一个模块没有输出(处于高阻抗状态)。
如果在 ModelSim 中作为单独的模块进行仿真,那么当模块输出时,不能使用 force 命令将其设置为高阻抗,而是使用释放命令来释放总线。
源代码:
- module xx(data_inout , ........);
- inout data_inout;
- assign data_inout = (!link) ? datareg : 1'bz;
- endmodule
方法一:使用相反的控制信号输入端口,相当于在两个模块之间使用 inout 双向端口互连。这种方法应该注意,赋值语句只能放在 initial 和 always 块中。
- module test();
- wire data_inout;
- reg data_reg;
- reg link;
- initial begin
- ..........
- end
- assign data_inout = link ? data_reg : 1'bz;
- endmodule
方法2:使用force和reless语句,但这种方法不能准确反映双向端口的信号变化,但这种方法可以在块中反转。
- module test();
- wire data_inout;
- reg data_reg;
- reg link;
- initial begin
- .....
- end
- assign data_inout = link ? data_reg : 1'bz;
- endmodule
- 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时,测试平台驱动 inner_port 端口,然后检查 outer_port 端口输出的数据是否正确;当 out_en 端口为0时,测试台驱动 outer_port 端口,然后检查 inner_port 端口读取的数据是否正确。由于 inner_port 和 outer_port 端口都是双向端口,因此驱动方法与单向端口不同。
用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];
- inner_port_tb_reg=$random;
- outer_port_tb_reg=$random;
- end
- end
- 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;
- bidirection_io bidirection_io_inst(
- .inner_port (inner_port_tb_wire),
- .out_en (out_en_tb),
- .outer_port (outer_port_tb_wire)
- );
- endmodule