inout类型的作用
inout 类型常出现于芯片外部引脚,为的是减少管腿个数,一个端口同时做输入和输出。
如32位半导体随机读写寄存器RAM,若分别使用input output,则仅数据管脚就有64位,且同一时间最多只有32位管脚处于有效状态。而使用inout类型,则缩减了32位数据管脚,使模块对外看起来更简洁。
inout类型的用法
那么,如何判断这个数据段代表输入还是输出呢?一般我们还需要另一个读写控制信号we,根据这个决定inout类型的数据段按读还是写解析。
因为inout类型既是input又是output类型,所以必须同时遵守输入输出的描述规定。这个规定也很好理解。因为硬件描述语言的=不是C语言的“覆盖”,而是用一根线将电路中的两个点链接起来。如果这个=表示的电线链接了两个寄存器类型,那么当其中一个reg改变时,另一个无法改变,则会产生冲突。所以用语法规定,盛装某电路模块输出信息的必须是wire类型。
那么,在向inout类型传入数据时(tb模块),必须遵守output的规定,使用线网型。(inout接口的另一端必须是wire)
//举个例子
//建模部分(模块描述)
module ModuleName(
input we;//读写控制信号 : 高电平代表写,低电平代表读
inout [31:0] inoutData;
);
//调用部分(实例化)
module ModeuleName_tb(); //为了看起来更简洁,省略begin end等
wire [31:0] inoutData;//传入inout接口的必须是wire型
reg we;//高电平代表写,低电平代表读
ModuleName ThisModuleName (.we(we), .inoutData(inoutData));
在we读写控制信号代表读,inout作为输出端口时,这样表述没有问题。但是,当we读写控制信号代表写,inout作为输入端口时,你大概率会碰到一个问题:
如何向inoutData中写入数据,使我能将我想要被调用的模块读到的数据传入呢
在调用模块里,inoutData是wire型,不能用行为级描述直接赋值。但是,你可能会想到,数据流描述方式
数据流描述方式:连续使用赋值语句(assign)对电路的逻辑功能进行描述。
连续赋值语句用于对wire型变量进行赋值,它由关键字assign开始,后面跟着由操作数和运算符组成的逻辑表达式。
连续赋值语句对inout类型数据赋值
在讨论这个问题之前,我们先讨论一下 线网(wire)数据类型的特征:
首先,线网类型相当于导线。这与C++语言中的变量(与Verilog中的reg寄存器类似)不同。线网没有存储功能,连接端改变,线网上的值立即改变。如上面的例子中是实例化语句,就相当于把inoutData导线接到了ModelName模块的接口上。
这样看似乎与寄存器类型没什么不同。但是,如果你想改变这个线网型变量呢?(如we=1写有效时赋值)直接赋值是不可以的,因为会
这就是硬件描述语言线网型的特点,它的值不能多次赋值覆盖改变,而是会产生冲突。
其次,与C++程序语言不同,硬件描述语言是要对应到门级电路上的。在C++程序中,可能有没有执行到的程序分支;但在Verilog语言中,每一句语言都会对应到电路被刻制在板子上。这就意味着,不管输入是什么样的,硬件描述语言的每一处都是确定的。 所以不存在,如果满足某个条件,电路如何连,否则采用另一种连法的情况,在实际中无法实现。(即不可能实现,读有效时连模块,写有效时连赋值寄存器)
正确用法的具体实现
inout 在具体实现上一般用三态门来实现。
既然硬件描述语言线网型的值不能多次赋值覆盖改变,还会产生冲突,那如何改变它的值呢?正确做法是将它与之前的连接“断开”,就不会产生冲突了。
assign inoutData = we ? Data_reg : 32'bz;
首先,在连续赋值语句中不能用if,只能用 ?:操作符
其次,设置一个寄存器类型的映像数据Data_reg,储存要给inoutData赋值的数据
最后,高阻抗z代表断开连接。
这样,inout类型的处理就完成了。测试模块中,在写有效时为它赋值映像数据域Data_reg数据,读有效时断开。在建模模块中则读有效时连接要输出的数据,写有效时断开。
如果这篇文章对您有帮助,请点个赞支持一下吧~码字不易,多谢鼓励