FPGA 双向口的使用及Verilog实现

FPGA的双向口在FPGA的设计应用中使用及其广泛,如I2C接口中的SDA,3线制的SPI接口中的数据线,传统控制总线中的数据总线,以及内存的访问DDR3/DDR4的数据总线等都是双向访问的。双向访问涉及到的概念比较多,如三态的概念,高阻的概念,输入、输出引脚合并,输入输出分时复用等概念,因此初学者往往比较迷惑,觉得无所适从,本文从底层基本原理入手,揭示双向口的机理,并用Verilog程序开发为例一步步引导大家如何使用双向口(inout)的使用与开发。

  1. 双向口涉及的基本模型
    • 三态门

为了描述方便,这里给两个命名tri0和tri1(tri是三态门(tri-state的缩略写法,其实在Verilog语法中有两个模型与之对应,分别为bufif0,bufif1。图1,2中的oe在传统的三总线结构中,通常对应OE(读)或WE(写)。

      • tri0

bufif0是三态门模型,其例化格式如下:

bufif0 tri0 (out, in, oe); //tri0是bufif0的例化名。

其电路形态形态如图1:

 

FPGA 双向口的使用及Verilog实现

图1 bufif0

 

在这两个模型中,oe端决定输出的形态,在tri0的模型中,如果oe为’0’, out就得到out0(out0是FPGA内部逻辑产生的值)的值,最终输出到端口PAD上。如果 oe为’1’,此时三态门的输出为高阻状态,在Verilog 描述中用’Z’表示,即三态门与外界是断开状态,如图2所示。

FPGA 双向口的使用及Verilog实现

图2

图1,2中的oe在传统的三总线结构中,通常对应OE(读)或WE(写)。

      • tri1

bufif1是另一种三态门模型,其例化格式如下:

bufif1 tri1 (out, in, oe); //tri1是bufif1的例化名。

其电路形态形态如图3:

FPGA 双向口的使用及Verilog实现

图3 buffif1

在这两个模型中,oe端决定输出的形态,在tri1的模型中,如果oe为’1’, out就得到out0(out0是FPGA内部逻辑产生的值)的值,最终输出到端口PAD上。如果 oe为’0’,此时三态门的输出为高阻状态,在Verilog 描述中用’Z’表示,即三态门与外界是断开状态,如图4所示。

FPGA 双向口的使用及Verilog实现

图4

图3,4中的oe在传统的三总线结构中,通常对应OE#(读)或WE#(写)。

  • 输入、输出在双向口合并

FPGA的I/O基本上都支持双向数据操作,但是由于对外输出端口只有一个,因此需要在端口处合并。

    • 利用tri0合并,如图5,

FPGA 双向口的使用及Verilog实现

图5

在图5中,输出流向从FPGA内部逻辑out0–>out–>PAD;输入流向PAD–>in–>FPGA 内部逻辑。

    • 利用tri1合并,如图6

FPGA 双向口的使用及Verilog实现

图6

在图6中,输出流向从FPGA内部逻辑–>out0–>out–>PAD;输入流向PAD–>in–>FPGA 内部逻辑。

    • 输入、输出分时复用。

从图5,6可以看出,由于PAD 共享输入、输出。一般在推拉驱动模型中,三态门的输出能力相对较强,考虑到如果接到FPGA外部器件有同样的接口,应该严格控制他们的时序关系,以免发生短路。如图7,

FPGA 双向口的使用及Verilog实现

图7

在图7中如果a,b两个器件同时输出(两个器件的oe都为’1’),如果恰好一个器件输出为高,一个为低,则会引起短路现象。因此要严格控制时序,保证a,b两个器件避开由内部逻辑同时驱动输出的情况。只有在两个器件oe一个为高,另一个为低,或者两个器件的oe都为低的时候,两个器件的端口才能连在一起。

  • 分时复用的实现
    • 推拉结构

在传统的工业控制总线中,分为主从模式。一般MCU或FPGA为MASTER,SRAM 、EPROM等器件为从模式。在这种模式下,FPGA生成控制信号oe, 同时取反后接到对方的OE端上。如图8:

 

FPGA 双向口的使用及Verilog实现

图8

图8中,由于oe是由一个主器件控制,因此实现推拉模式,即主器件输入,从器件输出;主器件输出,从器件输入。从输出角度看,在输出的时段内,高低电平直接输出,这一点不同后面要介绍的漏极开路(OD)带上拉电阻的结构。

推拉MOS管模型如图9:

FPGA 双向口的使用及Verilog实现

图9

例1:Master 模式单线控制双向接口(Verilog)。

门级描述:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 module bidir_gate (     input            clk,     input            rst,     inout      [3:0] a,     output           noe,     output reg [3:0] in_val  );   reg [3:0] counta, countb;   assign noe = ~counta[3]; bufif1 tri1_0(a[0], countb[0], ~noe); bufif1 tri1_1(a[1], countb[1], ~noe); bufif1 tri1_2(a[2], countb[2], ~noe); bufif1 tri1_3(a[3], countb[3], ~noe);   always@(posedge clk or posedge rst) if(rst) begin     counta <= 0;     countb <= 0; end else begin     counta <= counta + 1;       if(counta == 15)         countb <= countb + 1;       if(noe)         in_val <= a; end endmodule

RTL描述

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 module bidir_RTL (     input            clk,     input            rst,     inout      [3:0] a,     output           noe,     output reg [3:0] in_val  );     reg [3:0] counta, countb;   assign noe = ~counta[3]; assign  a  = (!noe) ? countb : 4'bZZZZ;   //双向口输出     always@(posedge clk or posedge rst) if(rst) begin     counta <= 0;     in_val <= 0; end else begin     counta <= counta + 1;       if(counta==15)         countb <= countb + 1;       if(noe)         in_val <= a; //获得输入,可以给FPGA内部其它模块使用 end   endmodule

在RTL的描述中可以看出,在Verilog中直接使用高阻4’bZZZZ就起到了三态门的效果,因此应习惯这种使用方法。

例2:Master 模式双向结构(Verilog RTL), 结构模式参看例1。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 module bidir_gate (     input            clk,     input            rst,     inout      [3:0] a,     output           noe,     output           nwe,     output reg [3:0] in_val  );     reg [3:0] counta, countb;   assign noe = ~counta[3]; assign nwe = counta[3];   //采用tri0模型 assign a   = ~nwe ? countb : 4'bZZZZ;   always@(posedge clk or posedge rst) if(rst) begin     counta <= 0; end else begin     counta <= counta + 1;       if(counta==15)         countb <= countb + 1;      if(~noe) in_val <= a;   end       endmodule
      • 上拉电阻结构(OD结构)

上拉电阻结构适合总线模型,如I2C总线,485总线等多master多slave的结构。在上拉电阻的结构中,双向口一般不需要读(noe)、写(nwe)控制接口配合。但需要协议配合实现。以I2C 为例,在主、从的结构中都采用OD的方式如图10,因此多个器件的输出端可以直接通过连线接在一起。但在由于输出没有推拉结构,在期望高电平输出时,由于MOS管关闭,实际输出为也为高阻,因此需要在总线上结上拉电阻,以保证在输出阶段且输出为高电平时,可以得到确保的高电平状态,如图11,

FPGA 双向口的使用及Verilog实现

图10 OD结构

FPGA 双向口的使用及Verilog实现

图11

在OD(或OC)的设计中,Verilog描述在输入、低电平输出时与推拉结构一致,只有在输出高电平时不同。在输出高电平时要确保真正输出的是高阻。I2C总线接口就是标准的OD结构,在SDA,SDL都要加上拉电阻,如图12

FPGA 双向口的使用及Verilog实现

图12

由于inout信号一般只在端口使用,因此在FPGA的内部逻辑(内部模块)将会把inout(双向口)变换成input, output类型进行传递, 具体的使用见例3.

例3:I2C接口Verilog描述。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 module I2C_intereface (     inout        SCL,     inout        SDA,     input  [7:0] datain,     output [7:0] dataout );     wire    SCL_in; wire    SDA_in;   wire    SCL_out; wire    SDA_out;   assign  SCL     = SCL_out ? 1’bZ : 1’b0;   //这里处理方式与推拉结构不同。 assign  SCL_in  = SCL; assign  SDA     = SDA_out ? 1’bZ : 1’b0; assign  SDA_in  = SDA;   I2C  I2C_inst (     .SCL_in  (SCL_in),     .SDA_in  (SDA_in),       .SCL_out (SCL_out),     .SDA_out (SDA_out),       .datain  (datain),     .dataout (dataout) )     endmodule

上面程序中,输入直接赋值给SDA_in,是全阶段赋值,即把输出阶段也给输入端赋值,因此在I2C 的程序中,何时使用输入的值,应有严格定时。关于I2C的时序描述请参照I2C部分内容。

上一篇:基于FPGA与MCU通信的SPI协议设计


下一篇:【电子技术】【2005.12】用于Xilinx FPGA的BitMaT位流操作工具