三态门详解

来源:互联网 发布:picturebox 网络图片 编辑:程序博客网 时间:2024/05/18 03:39

在FPGA中三态门比较常见,因为FPGA是做为一个高速处理的器件,免不了要进行输入输出数据,常规的输入和输出是分开的两个接口要不停的切换比较麻烦,在FPGA中用的双向口一般都是用三态门来作为输入和输出的,这样优点是只要一个接口就可以输入输出比较节约逻辑资源,但缺点是三态门的处理没有常规两个I/O的方便,这里我们来看看怎样使用三态门,下图是三态门的结构。




当sda_en为高时SDA作为输出口,输出sda_out的数据,当sda_en为低时三态门是处于高阻态,这时三态门是作为输入口使用,这时输入的数据为SDA的数据。

 

下面是用vivado 写的一段程序,当做为输出时将我们产生的clk_out的频率输出,代码如下。

module stm
    (
    i_clk,
    i_rst_n,
    SDA
    );
   
input i_clk;
input i_rst_n;
inout SDA;

reg sda_en;
reg sda_out;
reg [9:0] cnt;
reg clk_out;

always@(posedge i_clk or negedge i_rst_n)
      if(i_rst_n==1'b0)
           cnt <= 10'd0;
      else if(cnt==10'd999)
           cnt <= 10'd0;
      else
           cnt <= cnt + 1'b1;
          
always@(posedge i_clk or negedge i_rst_n)
      if(i_rst_n==1'b0)
           clk_out <= 1'b0;
      else if(cnt==10'd999)
           clk_out <= ~clk_out;
            
          

always@(posedge i_clk or negedge i_rst_n)
      if(i_rst_n==1'b0)begin
           sda_out <= 1'b0;
           sda_en  <= 1'b1;
           end
      else begin
           sda_out <= clk_out;
           sda_en  <= 1'b1;   //这里为1表示作为输出口使用
           end

assign SDA = (sda_en)? sda_out:1'bz;

endmodule

 

这个仿真脚本是随意编写的一个,如果你有更好的脚本可以使用自己编写的,我使用的vivado 仿真脚本如下:

module test_tb();

reg         i_clk;
reg         i_rst_n;

wire SDA;


stm u1
    (
     .i_clk(i_clk),
     .i_rst_n(i_rst_n),
     .SDA(SDA)
    );

 
      
                  initial begin
                       i_clk = 0;
                       i_rst_n = 1;
                       #10;
                       i_rst_n =   1'b0;
                       #120;
                       i_rst_n =   1'b1;
                   end
                  
                  always #40   i_clk   =   ~i_clk;
    
endmodule

 

下图是仿真时产生的波形,可以清楚的看到当sda_en为高时,我们输出SDA是等于clk_out的,所以可以看出这时这个三态门是作为输出口使用的。(当然这使能信号sda_en是高输出还是低输出,可以自己去设置我现在写的程序是高输出低输入,也可以高输入低输出只要将程序改一下就可以了,具体自己可以去尝试。)


 

上图是三态门作为输出口使用的,下面是三态门作为输入使用的代码如下:

module stm
    (
    i_clk,
    i_rst_n,
    SDA
    );
   
input i_clk;
input i_rst_n;
inout SDA;

reg sda_en;
reg sda_out;
reg [9:0] cnt;
reg clk_out;

always@(posedge i_clk or negedge i_rst_n)
      if(i_rst_n==1'b0)
           cnt <= 10'd0;
      else if(cnt==10'd999)
           cnt <= 10'd0;
      else
           cnt <= cnt + 1'b1;
          
always@(posedge i_clk or negedge i_rst_n)
      if(i_rst_n==1'b0)
           clk_out <= 1'b0;
      else if(cnt==10'd999)
           clk_out <= ~clk_out;
            
          

always@(posedge i_clk or negedge i_rst_n)
      if(i_rst_n==1'b0)begin
           sda_out <= 1'b0;
           sda_en  <= 1'b1;
           end
      else begin
           sda_out <= clk_out;
           sda_en  <= 1'b0;     //这里为0表示作为输入口使用
           end

assign SDA = (sda_en)? sda_out:1'bz;

endmodule


当做为输入口时,我在vivado仿真脚本里添加了一个输入时钟clk_out,为了加以区别,输入口的clk_out这个的频率是计数到499就翻转而作为输出口的clk_out是计数到999才翻转,所以输人口的clk_out的频率是输出口的clk_out 的频率的两倍,代码如下:

module test_tb();

reg         i_clk;
reg         i_rst_n;

wire SDA;


stm u1
    (
     .i_clk(i_clk),
     .i_rst_n(i_rst_n),
     .SDA(SDA)
    );


reg [9:0] cnt;
reg clk_out;

always@(posedge i_clk or negedge i_rst_n)
      if(i_rst_n==1'b0)
           cnt <= 10'd0;
      else if(cnt==10'd499)
           cnt <= 10'd0;
      else
           cnt <= cnt + 1'b1;
          
always@(posedge i_clk or negedge i_rst_n)
      if(i_rst_n==1'b0)
           clk_out <= 1'b0;
      else if(cnt==10'd499)
           clk_out <= ~clk_out;


assign SDA = clk_out;

 
      
                  initial begin
                       i_clk = 0;
                       i_rst_n = 1;
                       #10;
                       i_rst_n =   1'b0;
                       #120;
                       i_rst_n =   1'b1;
                   end
                  
                  always #40   i_clk   =   ~i_clk;
    
endmodule


 

SDA作为输入口时,仿真波形如下,大家可以看到当sda_en为低时,即使sda_out上面有我们之前产生的clk_out计数到999的那个频率,但SDA依然是clk_out计数到499时的频率,所以可以看出这时SDA是作为输入口使用的。


 

 

功能描述如下图:

         sda_en为1时作为输出,为0时为高阻作为输入



最后建议大家用我的程序仿真时,如果作为输出口使用仿真程序请用作为输出口仿真脚本,作为输入口使用时请使用作为输入口的仿真脚本,这两个脚本不是通用的。
 

原创粉丝点击