【FPGA基础】双向端⼝inout端⼝的使⽤指北
在查阅了各种书和帖⼦之后,总结了以下inout端⼝的使⽤注意事项。
(以下资料来源:
  《Xilinx FPGA开发实⽤教程第⼆版》
⾸先是⼀些要先理解的基本概念:
0、什么是双向端⼝inout端⼝
  顾名思义,双向端⼝既可以作为输⼊端⼝接收数据,也可以作为输出端⼝发出数据,它对数据的操作是双向的。双向端⼝在综合时是以三态门的形式存在的,其典型结构如图所⽰。
1、三态门
  在Xilinx的《XST User Guide》中给出了三态门的verilog描述
1// Tristate Description Using Concurrent Assignment
2
3// Combinatorial Always Block Can be Used too.
4
5module v_three_st_2 (T, I, O);
6
7input T, I;
8
define的基本用法9output O;
10
11assign O = (~T) ? I: 1’bZ;
12
13endmodule
  上述描述表明,当控制信号T=1时,管⼦被置为⾼阻态,输出O为⾼阻态;当控制信号T=0时,管⼦开通,输出O=输⼊I。(跟控制信号T是⾼有效或低有效有关)
2、⾼阻和悬空
  三态门中有⼀个状态是⾼阻。⾼阻,即可以认为是没有输出,作为输出端⼝⽽⾔,对下级电路没有任何影响。悬空是针对输⼊端⼝来说的,也就是说没有接输⼊。这也就意味着,实际上⾼阻和悬空是⼀个状态,在HDL语⾔⾥都表⽰为Z。
也就是说,⼀个输出端⼝在⾼阻态的时候,其状态是由于其相连的其他电路决定的,可以将其看作是输⼊。
  双向端⼝⽤作输出时,就和平常⼀样,但双向端⼝作输⼊引脚时需要将此引脚置为⾼阻态,这样其电平就可以由外部输⼊信号决定了(这是⾼阻态的特性)。
  在上述基本概念的基础上,讨论三个⽅⾯的问题:
⼀、双向端⼝的实现原理
  通过上⾯的基本概念应该已经清楚双向端⼝的实现原理了,只要搞清楚上⾯管⼦的开通状态与整个双向端⼝的对外特性之间的关系就⾏了。
  当上⾯的管⼦开通时,此时数据可以从上⾯的管⼦中通过,此时双向端⼝为输出端⼝,Device IO的赋值 from FPGA。
  当上⾯的管⼦被置为⾼阻态时,数据只能从下⾯的管⼦通过,此时双向端⼝为输⼊端⼝,Device IO的赋值 to FPGA。
  ⽽控制信号T与管⼦开关状态之间的关系下⾯结合代码来讲。
⼆、双向端⼝的Verilog实现
  在Verilog中实现双向端⼝,⾸先要明确inout端⼝的变量类型。inout端⼝只能被定义为net型变量,只能采⽤assign赋值语句,不能在always块内使⽤,详细解释可以看我的另⼀篇博客《》。要注意这⼀点与VHDL中双向端⼝是使⽤⽅法不同。
  接下来的代码展⽰了在输⼊情况下和输出情况下的赋值语句。
1module inout_def(clk,data_inout)
2input clk;
3inout data_inout;
4reg data_out;
5reg data_out_control;
6//define data_out
7
8//define data_out_control
9
10//assign data_inout
11assign data_inout=data_out_control?data_out:1'bz;
12
13//assign data_in
14wire data_in;
15assign data_in=(!data_out_control)&data_inout;
16
17endmodule
  控制信号data_out_control决定了双向端⼝data_inout的特性。当data_out_control=1时,data_inout作输出,此时将data_out的值赋给data_inout;当data_out_control=0时,data_inout作输⼊,将data_inout赋给data_in。
  不同需求的双向端⼝,它的assign赋值语句的写法也是不同的:
  当双向端⼝作输出时,assign data_inout=data_out_control?data_out:1'bz;
  当双向端⼝作输⼊时,要将三态门置为⾼阻态,也就是要有data_inout = 1'bz,此时控制信号等于0,所以有 assign data_in=
(!data_out_control)&data_inout;
三、双向端⼝的仿真
  编写测试模块时,对于inout类型的端⼝,需要定义成wire型变量,⽽其他输⼊端⼝都定义成reg型,这两者是有区别的。
  当上⾯的例⼦中的data_inout⽤作输⼊时,需要赋值给data_inout,其余情况可以断开。此时可以⽤assign语句实现:
assign data_inout = link ? data_in_t : 1'bz;
其中的link,data_in_t是reg型变量,在tb⽂件中赋值。
另外,可以设置⼀个输出端⼝观察data_inout⽤作输出的情况:
wire data_out;
assign data_out_t = (!link) ? data_inout : 1'bz;
  需要注意的是:当给data_inout赋值的时候(它作输⼊端⼝时),只能在原INOUT数据为⾼阻态时才可
以赋值,所以link信号即该INOUT数据为⾼阻态时的控制信号,也就是说link = !data_out_control;当不需要测试⽂件给data_inout数据赋值的时候,测试⽂件的data_inout接⼝因为⾼阻态,从⽽不影响源⽂件data_inout接⼝的其它操作。