FPGA教程系列-通过FIFO实现延时与跨时钟域

FPGA教程系列-通过FIFO实现延时与跨时钟域

本次实验主要实现标题里的两个功能,通过学习,这两个操作其实都是对写信号和读信号的操作,就将其归结为一次实验,没必要花更多的时间。

简单的描述下逻辑,延时的话,是在同一个时钟域下,设置一个计数器,当写数据的时候开始计数,计数到需要的延时后读信号开始读取数据,这样就可以进行延时的操作。

跨时钟域的话要考虑读和写的时钟,在这里简单的用读快写慢来进行实验,FIFO核中,有data count 的功能,其实原理很简单,当数据大于一个值后开始读,小于一个值后停止读就可以实现,当然,都是一些基本的操作,用于加深下FIFO的应用,并没有太多的实际用途,以后还是要具体情况具体分析,这里仅作为一个熟悉FIFO核的过程。

设置FIFO的IP核

image

image

Top文件编写

`timescale 1ns / 1ps

module fifo_top #(
    // --- Parameters for easy configuration ---
    parameter DELAY = 12'd10,
    parameter HIGH_WATER_MARK = 12'd4000,
    parameter LOW_WATER_MARK  = 12'd100
)(
    input  wire        i_clkw,
    input  wire        i_clkr,
    input  wire        i_rst,      
    input  wire [15:0] i_din,
    output wire [15:0] o_dout,
    output wire        o_full, 
    output wire        o_empty
);

    // --- Internal Signals ---
    reg          rd_en;
    wire [11:0]  rd_data_count;
    wire         fifo_empty;
    wire         fifo_full;
    // --- FIX: Declared missing wires ---
    wire [11:0]  wr_data_count; // Assuming 14-bit width to match rd_data_count
    wire         wr_rst_busy;
    wire         rd_rst_busy;
    
    // --- IMPROVEMENT: Use $clog2 for precise counter width ---
    localparam CNT_WIDTH = $clog2(DELAY);
    reg [CNT_WIDTH-1:0] cnt;

    // --- Read Enable Control Logic ---
    always @(posedge i_clkr) begin
        if (i_rst) begin
            rd_en <= 1'b0;
            cnt   <= {CNT_WIDTH{1'b0}}; // Use parameterized width for reset
        end else begin
            if (fifo_empty) begin
                rd_en <= 1'b0;
            end else if (rd_data_count >= HIGH_WATER_MARK) begin
                // --- FIX: Corrected delay logic ---
                // If delay hasn't finished, keep counting
                if (cnt < DELAY - 1) begin
                    cnt   <= cnt + 1'b1;
                    rd_en <= 1'b0;
                end else begin // Delay finished, enable reading
                    cnt   <= cnt; // Hold counter
                    rd_en <= 1'b1;
                end
            end else if (rd_data_count <= LOW_WATER_MARK) begin
                // --- FIX: Reset counter when low watermark is hit ---
                rd_en <= 1'b0;
                cnt   <= {CNT_WIDTH{1'b0}};
            end else begin
                // Hold the current state
                rd_en <= rd_en;
            end
        end
    end

    // --- FIFO Instantiation ---
    fifo_generator_0 fifo_generator_u (
        .rst            (i_rst),          
        .wr_clk         (i_clkw),         
        .rd_clk         (i_clkr),         
        .din            (i_din),          
        .wr_en          (!fifo_full),     
        .rd_en          (rd_en),          
        .dout           (o_dout),         
        .full           (fifo_full),      
        .empty          (fifo_empty),     
        .rd_data_count  (rd_data_count),  
        .wr_data_count  (wr_data_count),  
        .wr_rst_busy    (wr_rst_busy),    
        .rd_rst_busy    (rd_rst_busy)     
    );
    
    // --- Assign outputs ---
    assign o_full  = fifo_full;
    assign o_empty = fifo_empty;

endmodule

Testbench编写

`timescale 1ns / 1ps

module fifo_top_tb;
    reg         i_clkw;
    reg         i_clkr;
    reg         i_rst;
    reg  [15:0] i_din;
    
    wire [15:0] o_dout;
    wire        o_full;
    wire        o_empty;
 
    // Instantiate the Unit Under Test (UUT)
    fifo_top #(
        .DELAY(10),
        .HIGH_WATER_MARK(4000),
        .LOW_WATER_MARK(10)
    ) uut (
        .i_clkw   (i_clkr),
        .i_clkr   (i_clkr),
        .i_rst    (i_rst),
        .i_din    (i_din),
        .o_dout   (o_dout),
        .o_full   (o_full),
        .o_empty  (o_empty)
    );

    // --- Test Logic ---
    reg [15:0] expected_data; // Register to hold expected output
    
    initial begin
        // Initialize Inputs
        i_clkw = 1'b1;
        i_clkr = 1'b1;
        i_rst  = 1'b1;
        i_din  = 16'b0;
        expected_data = 16'b0;

        // Wait for reset to be released
        #100;
        i_rst = 1'b0;
        
        // Wait for a bit then start checking
        #1000;
    end
    // --- Stimulus Generation ---
    // Generate write-side data
    always @(posedge i_clkw or posedge i_rst) begin
        if (i_rst) begin
            i_din <= 16'b0;
        end else if (!o_full) begin // Only drive data when FIFO is not full
            if (i_din == 16'd3999)
                i_din <= 16'b0; 
            else
                i_din <= i_din + 16'd1;
        end
    end

    // --- Clock Generation ---
    always #20 i_clkw = ~i_clkw; // 25 MHz
    always #6  i_clkr = ~i_clkr; // ~83.33 MHz

endmodule

Tips:

照搬的话,可能会有点问题,代码中的应该仅仅是同时钟域下延时的仿真。

仿真

1、同时钟域下的延时仿真,通过改变DELAY参数,就可以实现不同的延时。可以自行仿真观察一下。

image

2、不同时钟下的读取。

image

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值