跳转到主要内容

FPGA触发器使用经验详解(二)

judy 提交于

文章来源:FPGA入门到精通

核心设计建议详解(4-7)
建议4:多位宽跨时钟域使用FIFO
解释:多位宽信号跨时钟域时,简单的同步器无法保证所有位同时稳定,可能产生亚稳态或数据错乱。FIFO通过握手或格雷码指针确保数据完整传输。

示例(异步FIFO接口):

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
// 实例化一个异步FIFO进行跨时钟域数据传输
async_fifo #(
    .DATA_WIDTH(16),
    .FIFO_DEPTH(32)
) u_cdc_fifo (
    .wr_clk  (clk_a),
    .wr_en   (data_valid_a),
    .din     (data_a),
    .rd_clk  (clk_b),
    .rd_en   (ready_b),
    .dout    (data_b),
    .full    (fifo_full),
    .empty   (fifo_empty)
);

建议5:控制集管理——不超过15%
解释:控制集定义为(时钟信号 + 时钟使能 + 复位信号)的组合。Xilinx FPGA中,每个Slice的触发器共享控制集。过多控制集会大幅降低资源利用率,甚至导致布线失败。

控制集估算参考:

. 理想:控制集数量 < 总触发器数的7.5%
. 警戒线:7.5% ~ 15%
. 必须优化:> 15%

优化方法对比:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
// 优化前:每个模块有不同的复位和使能,产生多个控制集
module bad_control_set (
    input clk, rst1, rst2, ce1, ce2,
    input [3:0] d1, d2,
    output reg [3:0] q1, q2
);
    always @(posedge clk) begin
        if (rst1) q1 <= 0;      // 控制集 {clk, 无ce, rst1}
        else if (ce1) q1 <= d1;
    end

    always @(posedge clk) begin
        if (rst2) q2 <= 0;      // 控制集 {clk, 无ce, rst2} — 不同
        else if (ce2) q2 <= d2;
    end
endmodule
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
// 优化后:统一复位和使能
module good_control_set (
    input clk, rst, ce,
    input [3:0] d1, d2,
    output reg [3:0] q1, q2
);
    always @(posedge clk) begin
        if (rst) begin           // 共享控制集
            q1 <= 0;
            q2 <= 0;
        end else if (ce) begin
            q1 <= d1;
            q2 <= d2;
        end
    end
endmodule

建议6:优先选择同步复位
解释:同步复位能被时钟滤除毛刺,时序分析更简单,且能充分利用Xilinx FPGA中触发器SR端口的特性。异步复位虽然响应快,但容易因复位释放的时序问题导致亚稳态。

同步复位示例:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
module sync_reset_dff (
    input clk,
    input rst,      // 同步复位
    input d,
    output reg q
);
    always @(posedge clk) begin
        if (rst)
            q <= 1'b0;
        else
            q <= d;
    end
endmodule

建议7:异步复位必须同步释放
解释:如果必须使用异步复位,需要将复位信号通过两级同步器同步到目标时钟域,并展宽到足够长度,确保所有触发器在同一时钟边沿退出复位状态。

异步复位同步释放电路:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
module async_reset_sync_release (
    input clk,
    input async_rst_n,      // 异步复位输入
    output reg sync_rst_n    // 同步后的复位输出
);
    reg rst_meta;

    always @(posedge clk or negedge async_rst_n) begin
        if (!async_rst_n) begin
            rst_meta <= 1'b0;
            sync_rst_n <= 1'b0;
        end else begin
            rst_meta <= 1'b1;
            sync_rst_n <= rst_meta;
        end
    end
endmodule

使用同步后复位的逻辑模块:

ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line
module user_logic (
    input clk,
    input async_rst_n,
    input d,
    output reg q
);
    wire sync_rst_n;

    async_reset_sync_release u_sync (
        .clk(clk),
        .async_rst_n(async_rst_n),
        .sync_rst_n(sync_rst_n)
    );

    always @(posedge clk or negedge sync_rst_n) begin
        if (!sync_rst_n)
            q <= 1'b0;
        else
            q <= d;
    end
endmodule