一、写在前面
本专栏为作者在 【数字IC手撕代码】 【数字IC笔试面经分享】 【数字IC工具解析】 以外开设的第四个独立专栏,旨在学习并提供有关Verilog硬件描述语言中非基础性的高阶语法特性知识,因本身专栏的独特定位,因此作者并不会涉及基础Verilog语言如阻塞式非阻塞赋值,过程块,数据类型等内容;同时受限于作者知识有限,本专栏也不会涉及System Verilog的相关内容,若按照IEEE的相关标准来看,本专栏将会聚焦Verilog-2005,即“IEEE Std 1364™-2005”以及之前的有关内容,提供相关的IC设计领域语法特性。以下为Verilog的进阶框图,有更多学习需求的读者可以检索相关英文标准进行学习。
二、什么是inout双向端口
在Verilog中,inout是一个双向接口(区别于input与output),也是一个可综合语句,广泛的应用于比如I2C等协议的设计中,声明inout端口,意味着数据既可以从主设备流向从设备,也可以从从设备流向主设备。
三、inout端口的综合
inout端口会被综合成为如下所示的三态门,高阻态的引入,有效的消除了电路中其他部分对于此门的影响,也解决了常规端口可能会出现的多驱动的问题,因此,三态门广泛的应用于总线互联的端口,也是我们今天所讨论的inout端口的综合结果
四、inout双向端口的要求
- inout端口默认为wire型,这意味着我们不能在always中对其进行赋值,而需要使用assign进行赋值
- 每一个inout端口都需要一个reg型的buffer来做缓冲器。
考虑这种情况:
当控制信号为真时,三态门开启,此时DataOut的输出通过双向端口传输到数据总线上。
但是DataIn和DataOut是直接相连的,如何保证DataOut的数据不会影响到与DataIn相连的电路呢?
解决这个问题的方法是将DataIn声明为reg类型,将reg类型变量复制到always进程块中,需要添加一个控制信号,由always敏感表监控,保证inout端口的输出不能直接贯通到Datain处。
在Verilog描述的实际过程中,往往容易忽略某个inout端口的reg语句。以 CPU 或 RAM 为例。RAM本身是作为内存用reg声明的,所以不需要这个reg缓冲区;而 CPU 模块的 inout 端口的 reg 语句经常被忽略,因为这东西看起来“多余”。这也是初学者在使用 inout 端口时最容易出错的地方。
- 我们没有办法同时对inout端口既写又读(即同一时刻,数据只能有一个方向),配合着三态门的综合结果,在接收数据的的时候我们使其保持高高阻态
- inout 端口不能独立存在
对于一个模块,inout 端口可以用作输入和输出。那么,连接到inout口的另一个模块是什么情况呢?显然,另一个模块也应该是一个inout端口,一个inout端口不能独立存在。但在实际编写 Verilog 代码的过程中,这点往往被忽略。
- 对inout的赋值需要使用一对信号来完成
如前所述,inout 端口不能独立存在。为了进一步考虑,当一个模块的inout端口作为输出时,那么另一个模块的inout端口必须作为输入;反之,当一个模块的inout口用作输入时,那么另一个模块的inout口一定是输出口。因此,两个inout端口的控制信号实际上是由一对信号控制的。
五、inout端口的赋值
5.1 设计文件的赋值
方法一:声明一个受control信号控制的inout型的myport,当control为1时,赋值为data,control为0时,赋值为高阻态,推荐这个方法
assign myport = control ? data : 'z;
方法二:虽然本专栏在探讨IEEE Verilog-2005的相关设计标准,但是都说到这里了,作者再补充以下System Verilog出现后,inout端口新的赋值方法,以下的赋值看起来会比较奇特,因为正常的寄存器不产生Z值,但是在System Verilog的编译条件下,这种方法也是可行的。
logic myport_reg; assign myport = myport_reg;] // in procedural code myport_reg <= data; // myport becomes an output myport_reg <= 'z; // myport becomes an input
5.2 仿真文件的赋值
testbench没有端口名称,因此我们没有办法在testbench中将其声明为inout端口,为了在testbench中体现inout,首先,我们需要将inout端口声明为wire型,例化的时候与设计文件连接,其次,我们要分别模拟input和output的行为,读取的时间高阻态,发送的时间有相对应的值,下文的案例为SRAM的testbench,其中Databus在设计文件中声明为了inout端口
module test; reg [9:0]AddressBus; reg Read,Write; wire [31:0]DataBus,TestOut; reg [31:0]TestIn; DRAM dram(DataBus,AddressBus,Read,Write); assign DataBus=(Write==1)?TestIn:32'bz; assign TestOut=(Read==1)?DataBus:32'bz; initial begin Write=1'b0;Read=1'b0; AddressBus=1'b0; TestIn=1'b0; #100 Write=1'b1; AddressBus=10'd4; TestIn=32'd64; #10 Write=1'b0; #10 Read=1'b1; AddressBus=10'd8; #30 $stop; end endmodule
六、更多资料
有更多学习需求的读者可以直接参考以下连接的文章,虽然是英文的,不过应该可以说将inout讲的非常清晰明了,作者的本篇博客也参考了很多下面的文章内容。
How to use the inout port in Verilog
七、往期【Verilog】高级教程文章
多维数组:灭霸打个响指的功夫,看懂Verilog多维数组
clog2系统函数: 关于Verilog自动计算位宽的系统函数$clog2,这些是你不得不知道的
UDP用户原语:玩转UDP用户原语,这篇文章就够了
$monitor系统函数:放学前的最后几分钟,看懂Verilog中的monitor系统函数
generate语句:一把王者的时间,学会Verilog中的generate语句
parameter常量:玩转parameter与localparameter,这篇文章就够了
inout双向端口:通俗易懂的带你解读inout双向端口
task与function区别:芯片人必会的task与function区别详解