前言
本实验简单地使用 fifo 跨时钟域传输多 bit 数据,以 100M 的时钟依次向 fifo 中写入 1-200 这 200 个数,然后再以 200M 的时钟从 fifo 中把数据读出,验证数据是否写入,然后继续写入 1-200 这 200 个数,然后在继续读出 200 个数,如此这样读写循环。让我们初步理解 fifo 的时序。
创建FIFO IP核
1.在 IP Catalog 中搜索fifo,找到FIFO Generator,双击打开
2.配置 fifo ip 核的基本设置,选择Independent Clocks Block RAM
3.配置 ip 核的接口的模式以及位宽
4.将 fifo 写入数据和读出数据的端口都选上
5.本实验用涉及到跨时钟用到了 pll,pll 主要配置如下图
程序设计
Verilog代码
// 定义FIFO测试模块
module fifo_test(
input clk, // 时钟信号输入
input rst_n // 复位信号输入,低电平有效
);
// 定义最大计数器值
localparam CNT_MAX = 200 - 1;
// 声明内部信号
wire rst; // 复位信号,高电平有效
wire fifo_rst; // FIFO复位信号
wire wr_clk; // 写时钟信号
wire rd_clk; // 读时钟信号
reg wr_en; // 写使能信号
wire[7:0]din; // 数据输入端口
reg[7:0]wr_cnt; // 写计数器
wire empty; // FIFO空信号
wire[7:0]wr_data_count; // 写数据计数
wire wr_rst_busy; // 写复位忙信号
reg rd_en; // 读使能信号
wire[7:0]dout; // 数据输出端口
reg[7:0]rd_cnt; // 读计数器
wire full; // FIFO满信号
wire[7:0]rd_data_count; // 读数据计数
wire rd_rst_busy; // 读复位忙信号
// 复位信号处理
assign fifo_rst = wr_rst_busy | rd_rst_busy; // FIFO复位信号由写复位忙或读复位忙信号产生
assign rst = ~rst_n; // 复位信号,取反rst_n得到
// 实例化时钟模块,产生写时钟和读时钟
clock inst_clock(
.clk_out1(wr_clk),
.clk_out2(rd_clk),
.reset(rst),
.clk_in1(clk)
);
// 写时钟域的写使能信号生成
always @(posedge wr_clk or posedge fifo_rst) begin
if (fifo_rst)
wr_en <= 1'b0; // FIFO复位时,写使能置为0
else if (wr_cnt == CNT_MAX)
wr_en <= 1'b0; // 写计数器达到最大值时,写使能置为0
else if (empty)
wr_en <= 1'b1; // FIFO为空时,写使能置为1
end
// 写计数器逻辑
always @(posedge wr_clk or posedge fifo_rst) begin
if (fifo_rst)
wr_cnt <= 'd0; // FIFO复位时,写计数器清零
else if (wr_en) begin
if (wr_cnt == CNT_MAX)
wr_cnt <= 'd0; // 写计数器达到最大值时,计数器清零
else
wr_cnt <= wr_cnt + 1'b1; // 写使能时,计数器加1
end
else
wr_cnt <= 'd0; // 非写使能时,计数器清零
end
// 数据输入端口连接写计数器加1
assign din = wr_cnt + 1'b1;
// 实例化FIFO模块
dc_fifo dc_fifo(
.wr_clk(wr_clk),
.rd_clk(rd_clk),
.din(din),
.wr_en(wr_en),
.rd_en(rd_en),
.dout(dout),
.full(full),
.empty(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)
);
// 读时钟域的读使能信号生成
always @(posedge rd_clk or posedge fifo_rst) begin
if (fifo_rst)
rd_en <= 1'b0; // FIFO复位时,读使能置为0
else if (rd_data_count == 200)
rd_en <= 1'b1; // 读数据计数达到200时,读使能置为1
else if (rd_cnt == CNT_MAX)
rd_en <= 1'b0; // 读计数器达到最大值时,读使能置为0
else
rd_en <= rd_en; // 保持当前读使能状态
end
// 读计数器逻辑
always @(posedge rd_clk or posedge fifo_rst) begin
if (fifo_rst)
rd_cnt <= 'd0; // FIFO复位时,读计数器清零
else if (rd_en) begin
if (rd_cnt == CNT_MAX)
rd_cnt <= 'd0; // 读计数器达到最大值时,计数器清零
else
rd_cnt <= rd_cnt + 1'b1; // 读使能时,计数器加1
end
else
rd_cnt <= 'd0; // 非读使能时,计数器清零
end
endmodule
仿真代码
`timescale 1ns / 1ps
module tb_fifo_test();
reg clk;
reg rst_n;
fifo_test fifo_test(
.clk(clk),
.rst_n(rst_n)
);
initial begin
clk=1;
forever begin
#10 clk=~clk;
end
end
initial begin
rst_n=0;
repeat(20) @(posedge clk);
rst_n=1;
end
endmodule
仿真结果
向 fifo 中以 100M 的时钟写入 1-200 这 200 个数据;从 fifo 中以 200M 的时钟读出这 200 个数