理解
来源特权同学-https://www.eefocus.com/ilove314/blog/11-09/231507_10e01.html
inout用法浅析
有感于之前IIC通信中第一次使用verilog的inout端口,早就想写点什么。有些人可能会认为所谓的inout端口FPGA会自己处理,你要它做INPUT的时候从它读数据,你要它OUTPUT的时候给它赋值就行。问题可不是这么简单!
我先送上一个表格吧,正好今天在一本书上看到了。这个表格表示的是在同等驱动强度下,两个驱动源驱动的wire型和tri型变量的真值表。
![](https://files.mdnice.com/user/17442/4e985ea4-1698-48f1-844f-18bf9a02bed6.png)
发现问题了吧!如果某时刻inout口有输入,此时你又正好要拿这个inout口做输出那么冲突是在所难免的,会出现什么样的结果可以参考上面的表。另外看这个表,你就应该明白双向口该怎么处理了吧。下面是一种典型的用法:
inout io_data; //inout口
reg out_data; //需要输出的数据
reg io_link; //inout口方向控制
assign io_data = io_link ? out_data:1'bz; //这个是关键
当inout的口要做输入时,记得一定要把它置为高阻态z,此时你就可以把inout口当作平常的input口用了。
测试
下面我们用三种方法去实现inout,先说明一下,第一种方法的结果与其他两种方法不一样,估计有问题,不推荐使用。
第一种方法和第二种方法的区别是inout作输入时的写法不一样。第三种方法针对Xilinx FPGA,直接使用原语IOBUF。
代码如下,综合工具Vivado 2015.3,器件选择k7325tffg900-2。
module inout_def(
input clk,
input z,
inout dinout,
input z2,
inout dinout2,
input z3,
inout dinout3,
output reg led_r1,
output reg led_r2,
output reg led_r3
);
reg dout = 0;
wire din;
assign dinout = z?1'bz:dout;
assign din = z?dinout:1'bz;
always @(posedge clk)
begin
if(din)
led_r1 <= 1;
else
led_r1 <= 0;
end
reg dout2 = 0;
wire din2;
assign dinout2 = z2?1'bz:dout2;
assign din2 = dinout2;
always @(posedge clk)
begin
if(din2)
led_r2 <= 1;
else
led_r2 <= 0;
end
reg dout3 = 0;
wire din3;
IOBUF IOBUF(
.I(dout3),
.O(din3),
.T(z3),
.IO(dinout3)
);
always @(posedge clk)
begin
if(din3)
led_r3 <= 1;
else
led_r3 <= 0;
end
endmodule
对代码进行综合,结果如下图所示,可以看到,第二种方法和第三种方法综合出来的结果是相同的,第一种方法的结果与其他两种不同。所以inout作输入时直接assign就行了。
放大IOBUF可以看到,IOBUF实际上由一个OBUFT和一个IBUF组成。
IBUF是输入缓冲器,这个不多说。OBUFT是三态输出缓冲器,其结构和真值表如下图所示,可以看到,当T为1时,输出是高阻态。当T为0时,输出与输入结果相同。
所谓高阻态,百度百科是这样说的:电路分析时高阻态可做开路理解。你可以把它看作输出(输入)电阻非常大。它的极限状态可以认为悬空(开路)。也就是说理论上高阻态不是悬空,它是对地或对电源电阻极大的状态。而实际应用上与引脚的悬空几乎是一样的。
我们想象一下,OBUFT为高阻态时相当于开路,那dinout3和IBUF组成的通路和一般的输入通路岂不是完全相同,所以此时可以当Input来用。
下面进行implementation,我们看dinout3实现的结果,可以看到IOBUF的IBUF和OBUFT以及相应的联结关系。OBUFT的TRI对应的是z3。IBUF的OUT对应的是led_r3_reg。
最后,考虑到代码的通用性,在使用inout端口的时候还是推荐用第二种方法。
原文:FPGA 的逻辑
作者:碎碎思
相关文章推荐