FPGA三态门使用介绍

三态门指的是门电路的输出有3种状态:高电平,低电平和高阻态。

当两个以上的设备分时驱动同一根信号线时,就需要用到三态门。
任意一个时刻,只能有一个设备驱动信号,其他设备需要设定为高阻态。
否则,如果两个设备同时驱动同一信号,一个设备输出高电平,一个设备输出低电平,对于推挽输出来说,两个设备间相当于上拉管和下拉管直接短路,瞬时大电流会将设备烧毁,造成严重后果。

FPGA中设定一个信号为三态门,在Verilog中,就是设定该信号的类型为inout。
其等效的电路结构如图所示。
FPGA三态门使用介绍_第1张图片
图中的sda信号声明为inout,当ENB为高电平HI时,sda由data_o驱动。
当ENB为低电平LO时,ENB控制的门电路处于高阻状态,相当于切断了data_o和sda之间的通路,对于连接到sda信号的其他设备而言,相当于根本不存在data_o这个驱动源。
FPGA三态门使用介绍_第2张图片
此时,该FPGA可从sda管脚读取外部数据。

对于FPGA而言,即便只有一个外设与FPGA相连,如果要通过同一根信号既能读取FPGA数据又能向FPGA写入数据,那么该信号线就需要定义为三态门。

为了测试FGPA上的三态门,写了一个小模块inout_if.v,代码如下:

`timescale 1ns/10ps

module inout_if #(
    parameter     INOUTIF_ID_NUM = 0
    ) (
    input           rst_n,
    input           i_clk,
    input   [1:0]   rd_cs,
    input           rd_en,
    inout           sda
    );

reg     [7:0]   cnt;
reg             clk_out;

assign  sda = ((rd_cs == INOUTIF_ID_NUM) & rd_en)   ? clk_out : 1'bz;

always @(posedge i_clk or negedge rst_n) begin
    if (rst_n == 1'b0) begin
        // reset
        cnt         <= 8'h0;
        clk_out     <= 1'b0;
    end
    else begin
        cnt         <= cnt + 1'b1;
        if (cnt == 2**INOUTIF_ID_NUM) begin
            cnt     <= 8'h0;
            clk_out <= ~clk_out;
        end
    end
end

endmodule

式中用INOUTIF_ID_NUM表示该模块的ID号,在testbench中可以通过指定1和2来实现模块识别。
模块的功能是对输入时钟分频2^INOUTIF_ID_NUM。
顶层模块tb_inout_if.v代码如下:

`timescale 1ns/10ps

module tb_inout_if ();

wire            tb_clk;
wire            tb_rst_n;
wire            tb_sda;
wire    [1:0]   tb_rd_cs;
wire            tb_rd_en;

reg     [1:0]   rd_cs;
reg             rd_en;

assign  tb_rd_cs    = rd_cs;
assign  tb_rd_en    = rd_en;
assign  tb_sda      = (tb_rd_en == 1'b0) ? tb_clk : 1'bz;

// genrate clock
clk_rst clk_rst_inst(
    .i_clk  (tb_clk    ),
    .i_rst_n(tb_rst_n  )
);

// user logic
inout_if #(
    .INOUTIF_ID_NUM (1)
    )
    inout_if_inst0(
    .rst_n  (tb_rst_n   ),
    .i_clk  (tb_clk     ),
    .rd_cs  (tb_rd_cs   ),
    .rd_en  (tb_rd_en   ),
    .sda    (tb_sda     )
    );


inout_if #(
    .INOUTIF_ID_NUM (2)
    )
    inout_if_inst1(
    .rst_n  (tb_rst_n   ),
    .i_clk  (tb_clk     ),
    .rd_cs  (tb_rd_cs   ),
    .rd_en  (tb_rd_en   ),
    .sda    (tb_sda     )
    );

initial
begin
    rd_en       <= 1'b0;
    rd_cs       <= 2'b00;
    #1000 rd_en  <= 1'b1;
    rd_cs        <= 2'b01;
    #1000 rd_cs  <= 2'b10;
    #1000 rd_cs  <= 2'b00;
    rd_en        <= 1'b0;
end

// dump fsdb file for debussy
initial
begin
  $fsdbDumpfile("wave.fsdb");
  $fsdbDumpvars;
end

endmodule

辅助时钟和复位信号模块clk_rst.v的代码如下:

`timescale 1ns/10ps

module clk_rst(
  output reg i_clk,
  output reg i_rst_n
);

parameter CLK_PERIOD = 20;
parameter MULT_RATIO = 10;
parameter RESET_TIME = MULT_RATIO * CLK_PERIOD + 1;

initial
begin
  i_rst_n <= 1'b0;
  #RESET_TIME i_rst_n <= 1'b1;
end

initial
begin
  i_clk <= 1'b0;
  forever
    #(CLK_PERIOD / 2) i_clk <= ~i_clk;
end

endmodule

编译并调用波形查看器的结果如图所示。
FPGA三态门使用介绍_第3张图片

从图中可以看出,当tb_rd_cs为0时,sda由tb_inout_if.v中tb_clk直接驱动;
当tb_rd_cs为1时,sda的信号改为由inout_if_inst0驱动,sda的时钟频率是tb_clk的2*2^1分频;
当tb_rd_cs为2时,sda的信号改为由inout_if_inst1驱动,sda的时钟频率是tb_clk的2*2^2分频;

你可能感兴趣的:(fpga,硬件设计)