一般博客会这样介绍三态控制写法:
inout sda_1,
wire inout_control_1;
wire sda_in_1;
wire sda_out_1;
assign sda_1 = (inout_control_1)?sda_out_1:1'bz;
assign sda_in_1 = sda_1;
插叙:悬空和高阻的区别
高阻,即可以认为是没有输出,作为输出端口而言,对下级电路没有任何影响。悬空是针对输入端口来说的,也就是说没有接输入。这也就意味着,实际上高阻和悬空是一个状态,在HDL语言里都表示为Z。
这里不谈inout接口是什么,只描述设计和仿真文件的写法。
经验:
设计中,顶层再做三态控制 方便插逻辑分析仪 而且inout需要和inout进行握手 用wire和reg分别模拟输入输出行为方便 控制信号在顶层出现
具体就是在ad驱动小模块里露出输入输出接口和输出信号 分别使用 在顶层再三态控制
驱动模块:
output reg inout_control,//
input sda_in,
output reg sda_out,//data_inout
顶层:
assign sda_1 = (inout_control_1)?sda_out_1:1'bz;
assign sda_in_1 = sda_1;
inout端口不能独立存在,连接到inout的另一个模块也应该是inout端口如前所述,inout 端口不能独立存在。为了进一步考虑,当一个模块的inout端口作为输出时,那么另一个模块的inout端口必须作为输入;
反之,当一个模块的inout口用作输入时,那么另一个模块的inout口一定是输出口。因此,两个inout端口的控制信号实际上是由一对信号控制的。(控制信号在仿真时也作为端口传递 比如用读写信号分别控制行为)
以下借鉴两篇博客:
通俗易懂的带你解读inout双向端口【Verilog高级教程】_verilog inout-CSDN博客
设计文件写法
data_inout是双向接口 对于输入和输出的行为,定义reg型变量data_out来描述输出结果,wire型变量data_in来观察输出结果(注意data_in的条件限制了!data_out_control),控制信号为1时,data_in为0.
module inout_def(clk,data_inout)
input clk;
inout data_inout;
reg data_out;
reg data_out_control;
//define data_out
//define data_out_control
//assign data_inout
assign data_inout=data_out_control?data_out:1'bz;
//assign data_in
wire data_in;
assign data_in=(!data_out_control)&data_inout;
endmodule
inout接口仿真
编写测试模块时,对于inout类型的端口,需要定义成wire型变量。
当上面的例子中的data_inout用作输入时,需要赋值给data_inout,其余情况可以断开。此时可以用assign语句实现:
assign data_inout = link ? data_in_t : 1'bz;
其中的link,data_in_t是reg型变量,在tb文件中赋值(可以是自己在仿真之中实现)。另外,可以设置一个输出端口观察data_inout用作输出的情况:
经测试有一些问题,修改如下:
-
数据位宽不为1位时,比如8bit,也要相应写成8’bz
-
assign data_in=(!data_out_control)&data_inout;这个写法只适用于1bit情况(这个输出的相当于是一个逻辑值而不是data_inout向量 只会是1bit),用以下代码:
assign w_data_in= (!i_data_out_control) ? io_data_inout : 8'bz;//有问题 只出现0 1 0 1
仿真中对输出信号进行观察的写法:
wire data_out_t;
assign data_out_t = (!link) ? data_inout : 1'bz;
需要注意的是:当给data_inout赋值的时候(它作输入端口时),只能在原INOUT数据为高阻态时才可以赋值,所以link信号即该INOUT数据为高阻态时的控制信号,也就是说
link = !data_out_control;
仿真的三态门控制信号和设计文件相反。因为仿真时,需要对inout进行赋值的时候是产生激励的时候,对reg型赋值,对应设计文件中的(data_out也相应赋值成reg类型)
当不需要测试文件给data_inout数据赋值的时候,测试文件的data_inout接口因为高阻态,从而不影响源文件data_inout接口的其它操作。
自己写的版本:
src
module inout_interface(
input i_clk ,
input i_rst ,
input i_data_out_control ,
inout [7:0] io_data_inout
);
//define data_out
reg [7:0] r_data_out;
//define i_data_out_control
always @(posedge i_clk or posedge i_rst)
if (i_rst)
begin
r_data_out <= 0;
end
else if(i_data_out_control)
begin
r_data_out <= r_data_out + 1;
end
else
r_data_out <= r_data_out;
//assign io_data_inout
assign io_data_inout = i_data_out_control ? r_data_out : 8'bz;
//assign data_in
wire [7:0] w_data_in;
// assign data_in= (!i_data_out_control) & io_data_inout ;//有问题 只出现0 1 0 1
assign w_data_in= (!i_data_out_control) ? io_data_inout : 8'bz;//有问题 只出现0 1 0 1
endmodule
tb
`timescale 1ns / 1ps
`define P_CLK_PERIOD 10 //100Mhz SYS_CLK
module tb_inout_interface();
reg r_clk;
reg r_rst;
reg r_link;
wire [7:0] w_data_inout;
reg [7:0] r_data_in_t;
wire [7:0] w_data_out_t;
inout_interface inout_interface_inst0(
.i_clk (r_clk) ,
.i_rst (r_rst) ,
.i_data_out_control (!r_link) ,
.io_data_inout (w_data_inout)
);
assign w_data_inout = r_link ? r_data_in_t : 8'bz; //写
assign w_data_out_t = (!r_link) ? w_data_inout : 8'bz; //读
initial
r_clk = 1;
always #(`P_CLK_PERIOD/2) r_clk = ~r_clk;
initial
begin
r_rst = 1;
r_link = 0;
#20;
r_rst = 0;
#200;
r_link = 1;
#2000;
r_link = 0;
#2000;
$stop;
end
always @(posedge r_clk or posedge r_rst)
if (r_rst)
begin
r_data_in_t <= 0;
end
else if(r_link)
begin
r_data_in_t <= r_data_in_t + 1;
end
else
begin
r_data_in_t <= r_data_in_t;
end
endmodule
读信号拉高时(即这里的i_data_out_control为低,r_link为高时) inout端口作输入用 读取sim的激励信号r_data_in_t
写信号拉高(即这里的i_data_out_control为高,r_link为低时) inout端口作输出用 此时用于将设计文件中的输出量r_data_out输出
读信号有效时,w_data_out_t为z,但对应设计文件的输出还是有一个值(20),但对于w_data_out_t我们把它写死
assign w_data_out_t = (!r_link) ? w_data_inout : 8'bz; //观察输出
同理写信号有效时的r_data_in有值,但是设计文件中观察到的是8’bz,注意这个z必须要对应他们正确的位宽,是8位就是8’bz.
assign w_data_in= (!i_data_out_control) ? io_data_inout : 8'bz;