最清晰最全的inout类型使用方法
例如,ddr_dqs是inout类型的,在代码中尽量这么写:
assign ddr_dqs = ddr_dqs_write_en ? 4'dZ : ddr_dqs_write;
inout 在具体实现上一般用三态门来实现。三态门的第三个状态就是高阻
'Z'。当 inout 端口不输出时,将三态门置高阻。
就遵从以上这种写法就行了,不要怀疑。不要纠结:
sdram_out_en是1的时候,ddr_dqs作为输入,当ddr_dqs_write_en为0时候,ddr_dqs作为输出。
接下来讲讲为什么尽量那么写:组合起来有4种写法:
(1)assign ddr_dqs = ddr_dqs_write_en ? ddr_dqs_write : 4'dZ;
1 output 0 input
(2)assign ddr_dqs = (!ddr_dqs_write_en) ? ddr_dqs_write : 4'dZ;
1 input 0 output
(3)assign ddr_dqs = (!ddr_dqs_write_en) ? 4'dZ : ddr_dqs_write;
1 output 0 input
(4)assign ddr_dqs = ddr_dqs_write_en ? 4'dZ : ddr_dqs_write;
1 input 0 output
通过撰写代码实际仿真可知:四种写法生成的三态门控制器一模一样:
这时候就比较纠结了,
(1)(3)ddr_dqs_write_en置1的时候inout作为输入,(2)(4)ddr_dqs_write_en置1为输出,三态门一模一样,那么问题来了,到底置1的时候输出还是应该输入呢?
然后你去****一搜,比如比较经典的两个回答:
https://www.cnblogs.com/fimwest/p/7860465.html
https://www.eefocus.com/ilove314/blog/11-09/231507_10e01.html
两个讲解的都很清澈明了,但是:
明显第一个链接:使能信号置1位输入,第2个链接:使能信号置1为输出!!!
f......uck!!!
这些链接都只讲了皮毛,没有想到我说的问题。
这就很坑了。但是呢,实际上以上4种写法暗藏玄机:
虽然三态门长得一样,但是使能端T来的信号是不一样的:
(1)(3)使能信号出来的时候,经过了一个元器件:
而(2)(4)使能信号直接到达了三态门的T端:
然后通过查看原语:
ug471中:T为1,不使能Output buffer,而不是图中的O端口(这官方文档也是够坑的,真容易让人产生误解),所以此时IO端口作为输入,数据通过O端口给到FPGA code中。同理,也就是T为0的时候,不使能input buffer,而不是I端口,那么FPGA code从I端口输入信号,IO端口作为输出。
这也就是说:到达三态门的T端信号为1,inout是输入,为0,inout为输出。
这就和(2)(4)写法一致了,那么是不是说(1)(3)就错了呢?
也不是!
通过实际上板调试发现:(1)(3)的确使能信号为1做了输出
(2)(4)的确使能信号为0做了输出
蓝框里面:下面一条线为使能信号,为0的时候,上面数值改变了,做了输出
所以猜测多了元器件LUT1为取反功能:虽然你在代码里面写的是1,但是通过LUT1那么一个器件到达三态门的T端时候变成了0,这样就和原语规定的一致了。
继续上板抓取,果然,经过LUT1那个器件的信号取了反。
为什么说(1)(3)的使能信号变化了呢,你可以再看实际仿真中生成的三态门:
使能信号为1时候,你想把inout作为输出,但是为1的时候,根据原语,output buffer不使能了,那么数据只能通过io端口进来,从o端口出去,那么inout就是输入了,和你的预期不同,所以说实际上vivado生成电路的时候已经给你把使能值取反了。
至此,也就是4种写法都可以,但是为了和原语保持一致,避免产生歧义,就使用第4种写法就行了:
assign ddr_dqs = ddr_dqs_write_en ? 4'dZ : ddr_dqs_write;
别杠!!!就这么写,就行了。
如果代码中不将input类型用“? :”撰写,此处在代码中只用到输入,那么:
如图,ila链接为蓝色线,input类型生成的电路图只是一个输入电路而已。
不过有几个其他小问题:
以上都是添加ila 执行布线后的电路,但实际上不添加ila后,就乱了,比如(1)的LUT1在综合后有,布线后就没有了,能力不够,不知道为什么。
还有,没添加ila后,(4)在综合的时候居然有了LUT1这个东西,可能操作有问题吧。。。。