芯片外部引腳很多都使用inout類型的,為的是節省管腿。一般信號線用做總線等雙向數據傳輸的時候就要用到INOUT類型了。就是一個端口同時做輸入和輸出。
inout在具體實現上一般用三態門來實現。三態門的第三個狀態就是高阻'Z'。當inout端口不輸出時,將三態門置高阻。
這樣信號就不會因為兩端同時輸出而出錯了
1 使用inout類型數據,可以用如下寫法:
inout data_inout;
input data_in;
reg data_reg;//data_inout的映象寄存器
reg link_data;
assign data_inout=link_data?data_reg:1’bz;//link_data控制三態門
//對于data_reg,可以通過組合邏輯或者時序邏輯根據data_in對其賦值.通過控制link_data的高低電平,從而設置data_inout是輸出數據還是處于高阻態,如果處于高阻態,則此時當作輸入端口使用.link_data可以通過相關電路來控制.
2 編寫測試模塊時,對于inout類型的端口,需要定義成wire類型變量,而其它輸入端口都定義成reg類型,這兩者是有區別的.
當上面例子中的data_inout用作輸入時,需要賦值給data_inout,其余情況可以斷開.此時可以用assign語句實現:assign data_inout=link?data_in_t:1’bz;其中的link ,data_in_t是reg類型變量,在測試模塊中賦值.
另外,可以設置一個輸出端口觀察data_inout用作輸出的情況:
Wire data_out_t;
Assign data_out_t=(!link)?data_inout:1’bz;
3 else,in RTL
inout use in top module(PAD)
dont use inout(tri) in sub module
也就是說,在內部模塊最好不要出現inout,如果確實需要,那么用兩個port實現,到頂層的時候再用三態實現。理由是:在非頂層模塊用雙向口的話,該雙向口必然有它的上層跟它相連。既然是雙向口,則上層至少有一個輸入口和一個輸出口聯到該雙向口上,則發生兩個內部輸出單元連接到一起的情況出現,這樣在綜合時往往會出錯。
對雙向口,我們可以將其理解為2個分量:一個輸入分量,一個輸出分量。另外還需要一個控制信號控制輸出分量何時輸出。此時,我們就可以很容易地對雙向端口建模。
例子:
CODE:
module dual_port (
....
inout_pin,
....
);
inout inout_pin;
wire inout_pin;
wire input_of_inout;
wire output_of_inout;
wire out_en;
assign input_of_inout = inout_pin;
assign inout_pin = out_en ? output_of_inout : 高阻;//問題,如果out_en為假的話,inout_pin為高
//阻,那input_of_inout呢?
//如果out_en為真,那么input_of_inout豈不
//是也等于inout_pin?怎么體現是輸入呢?
endmodule
可見,此時input_of_inout和output_of_inout就可以當作普通信號使用了。
4.仿真(o(∩_∩)o...哈哈,這才是我想要看的)
在仿真的時候,需要注意雙向口的處理。如果是直接與另外一個模塊的雙向口連接,那么只要保證一個模塊在輸出的時候,另外一個模塊沒有輸出(處于高阻態)就可以了。
如果是在ModelSim中作為單獨的模塊仿真,那么在模塊輸出的時候,不能使用force命令將其設為高阻態,而是使用release命令將總線釋放掉
很多初學者在寫testbench進行仿真和驗證的時候,被inout雙向口難住了。仿真器老是提示錯誤不能進行。下面是我個人對inout端口寫testbench仿真的一些總結,并舉例進行說明。在這里先要說明一下inout口在testbench中要定義為wire型變量。
先假設有一源代碼為:
module xx(data_inout , ........);
inout data_inout;
........................
assign data_inout=(! link)?datareg:1'bz;
endmodule
方法一:使用相反控制信號inout口,等于兩個模塊之間用inout雙向口互連。這種方法要注意assign 語句只能放在initial和always塊內。
module test();
wire data_inout;
reg data_reg;
reg link;
initial begin
..........
end
assign data_inout=link?data_reg:1'bz;
endmodule
方法二:使用force和release語句,但這種方法不能準確反映雙向端口的信號變化,但這種方法可以反在塊內。
module test();
wire data_inout;
reg data_reg;
reg link;
#xx; //延時
force data_inout=1'bx; //強制作為輸入端口
...............
#xx;
release data_inout; //釋放輸入端口
endmodule
仿真
很多讀者反映仿真雙向端口的時候遇到困難,這里介紹一下雙向端口的仿真方法。一個典型的雙向端口如圖1所示。
其中inner_port與芯片內部其他邏輯相連,outer_port為芯片外部管腳,out_en用于控制雙向端口的方向,out_en為1時,端口為輸出方向,out_en為0時,端口為輸入方向。
用Verilog語言描述如下:
module bidirection_io(inner_port,out_en,outer_port);
input out_en;
inout[7:0] inner_port;
inout[7:0] outer_port;
assign outer_port=(out_en==1)?inner_port:8'hzz;
assign inner_port=(out_en==0)?outer_port:8'hzz;
endmodule
用VHDL語言描述雙向端口如下:
library ieee;
use IEEE.STD_LOGIC_1164.ALL;
entity bidirection_io is
port ( inner_port : inout std_logic_vector(7 downto 0);
out_en : in std_logic;
outer_port : inout std_logic_vector(7 downto 0) );
end bidirection_io;
architecture behavioral of bidirection_io is
begin
outer_port<=inner_port when out_en='1' else (OTHERS=>'Z');
inner_port<=outer_port when out_en='0' else (OTHERS=>'Z');
end behavioral;
仿真時需要驗證雙向端口能正確輸出數據,以及正確讀入數據,因此需要驅動out_en端口,當out_en端口為1時,testbench驅動inner_port端口,然后檢查outer_port端口輸出的數據是否正確;當out_en端口為0時,testbench驅動outer_port端口,然后檢查inner_port端口讀入的數據是否正確。由于inner_port和outer_port端口都是雙向端口(在VHDL和Verilog語言中都用inout定義),因此驅動方法與單向端口有所不同。
驗證該雙向端口的testbench結構如圖2所示。
這是一個self-checking testbench,可以自動檢查仿真結果是否正確,并在Modelsim控制臺上打印出提示信息。圖中Monitor完成信號采樣、結果自動比較的功能。
testbench的工作過程為
1)out_en=1時,雙向端口處于輸出狀態,testbench給inner_port_tb_reg信號賦值,然后讀取outer_port_tb_wire的值,如果兩者一致,雙向端口工作正常。
2)out_en=0時,雙向端口處于輸如狀態,testbench給outer_port_tb_reg信號賦值,然后讀取inner_port_tb_wire的值,如果兩者一致,雙向端口工作正常。
用Verilog代碼編寫的testbench如下,其中使用了自動結果比較,隨機化激勵產生等技術。
`timescale 1ns/10ps
module tb();
reg[7:0] inner_port_tb_reg;
wire[7:0] inner_port_tb_wire;
reg[7:0] outer_port_tb_reg;
wire[7:0] outer_port_tb_wire;
reg out_en_tb;
integer i;
initial
begin
out_en_tb=0;
inner_port_tb_reg=0;
outer_port_tb_reg=0;
i=0;
repeat(20)
begin
#50
i=random;outentb=i[0];//randomizeoutentbinnerporttbreg=random;outentb=i[0];//randomizeoutentbinnerporttbreg=random; //randomize data
outer_port_tb_reg=random;endend//****drivetheportsconnectingtobidirction_ioassigninner_port_tb_wire=(out_en_tb==1)?inner_port_tb_reg:8'hzz;assignouter_port_tb_wire=(out_en_tb==0)?outer_port_tb_reg:8'hzz;//最不懂的就是這兒了,估計也是最重要的地兒//instatiatethebidirction_iomodulebidirection_iobidirection_io_inst(.inner_port(inner_port_tb_wire),.out_en(out_en_tb),.outer_port(outer_port_tb_wire));//*****monitor******always@(out_en_tb,inner_port_tb_wire,outer_port_tb_wire)begin#1;if(outer_port_tb_wire===inner_port_tb_wire)beginrandom;endend//****drivetheportsconnectingtobidirction_ioassigninner_port_tb_wire=(out_en_tb==1)?inner_port_tb_reg:8'hzz;assignouter_port_tb_wire=(out_en_tb==0)?outer_port_tb_reg:8'hzz;//最不懂的就是這兒了,估計也是最重要的地兒//instatiatethebidirction_iomodulebidirection_iobidirection_io_inst(.inner_port(inner_port_tb_wire),.out_en(out_en_tb),.outer_port(outer_port_tb_wire));//*****monitor******always@(out_en_tb,inner_port_tb_wire,outer_port_tb_wire)begin#1;if(outer_port_tb_wire===inner_port_tb_wire)begindisplay(" **** time=%t ****",time);time);display("OK! out_en=%d",out_en_tb);
display("OK!outerporttbwire=display("OK!outerporttbwire=display(" **** time=%t ****",time);time);display("ERROR! out_en=%d",out_en_tb);
display("ERROR!outerporttbwire!=innerporttbwire");display("ERROR!outerporttbwire!=innerporttbwire");display("ERROR! outer_port_tb_wire=%d, inner_port_tb_wire=%d",
outer_port_tb_wire,inner_port_tb_wire);
end
end
endmodule
審核編輯:劉清
-
VHDL語言
+關注
關注
1文章
113瀏覽量
17988 -
RTL
+關注
關注
1文章
385瀏覽量
59701 -
MODELSIM仿真
+關注
關注
0文章
15瀏覽量
7278 -
Verilog語言
+關注
關注
0文章
113瀏覽量
8214
原文標題:Verilog inout 雙向口使用和仿真
文章出處:【微信號:zhuyandz,微信公眾號:FPGA之家】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論