FPGA三态门使用介绍

本文介绍了三态门的概念及其在FPGA中的应用,主要用于多个设备分时驱动同一信号线的情况,避免短路风险。在Verilog中,通过声明信号为inout类型来实现三态门功能。文中还提供了测试模块inout_if.v的代码示例,展示了如何根据控制信号切换不同设备对信号线的驱动,并通过波形图验证了三态门的正确工作。
AI助手已提取文章相关产品:

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

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

FPGA中设定一个信号为三态门,在Verilog中,就是设定该信号的类型为inout。
其等效的电路结构如图所示。
这里写图片描述
图中的sda信号声明为inout,当ENB为高电平HI时,sda由data_o驱动。
当ENB为低电平LO时,ENB控制的门电路处于高阻状态,相当于切断了data_o和sda之间的通路,对于连接到sda信号的其他设备而言,相当于根本不存在data_o这个驱动源。
这里写图片描述
此时,该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

编译并调用波形查看器的结果如图所示。
这里写图片描述

从图中可以看出,当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分频;

您可能感兴趣的与本文相关内容

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值