FPGA教程系列-通过FIFO实现延时与跨时钟域
本次实验主要实现标题里的两个功能,通过学习,这两个操作其实都是对写信号和读信号的操作,就将其归结为一次实验,没必要花更多的时间。
简单的描述下逻辑,延时的话,是在同一个时钟域下,设置一个计数器,当写数据的时候开始计数,计数到需要的延时后读信号开始读取数据,这样就可以进行延时的操作。
跨时钟域的话要考虑读和写的时钟,在这里简单的用读快写慢来进行实验,FIFO核中,有data count 的功能,其实原理很简单,当数据大于一个值后开始读,小于一个值后停止读就可以实现,当然,都是一些基本的操作,用于加深下FIFO的应用,并没有太多的实际用途,以后还是要具体情况具体分析,这里仅作为一个熟悉FIFO核的过程。
设置FIFO的IP核


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参数,就可以实现不同的延时。可以自行仿真观察一下。

2、不同时钟下的读取。

1万+

被折叠的 条评论
为什么被折叠?



