Verilog之跨时钟域设计-FIFO

简介

  相信能看到此篇文字的朋友,对FIFO的作用已经不陌生了。
  FIFO 的英文全称是 First In First Out,即先进先出。 常被用于数据的缓存,或者高速异步数据的交互也即所谓的跨时钟域信号传递。看到这有人要问了,这与我之前写的RAM有什么区别呢。
  它与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址。

分类

  • 根据 FIFO 工作的时钟域,可以将 FIFO 分为同步 FIFO 和异步 FIFO。
  • 同步 FIFO 是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。
  • 异步 FIFO 是指读写时钟不一致,读写时钟是互相独立的。

用途

  • 同步 FIFO 常用于同步时钟的数据缓存, 异步 FIFO 常用于跨时钟域的数据信号的传递

思路

在这里插入图片描述

FIFO结构图
  我们首先来了解一下FIFO的常见参数:
  • FIFO 的宽度: FIFO 一次读写操作的数据位 N
  • FIFO 的深度: FIFO 可以存储多少个宽度为 N 位的数据
  • 空标志: empty。 FIFO 已空时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从 FIFO中读出数据而造成无效数据的读出。
  • 将空标志: almost_ empty。 FIFO 即将被读空。
  • 满标志: full。 FIFO 已满时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的写操作继续向 FIFO 中写数据而造成溢出。
  • 将满标志: almost_full。 FIFO 即将被写满。
  • 读时钟:读 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
  • 写时钟:写 FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
    “almost_ empty”和“almost_full”这两个信号分别被看作“empty”和“full”的警告信号,他们距离真正的空(empty)和满(full)都一个时钟的延时

代码

  • 本设计中,采用的是Xilinx的一个8bit*256的异步双口FIFO。

IP_FIFO,顶层文件:

module ip_fifo(
    input sys_clk,
    input sys_rstn
    );
    
wire        fifo_wr_en;    //FIFO写使能信号  
wire        fifo_rd_en;    //FIFO读使能信号?    
wire  [7:0] fifo_din;      //fifo输入信号线?       
wire  [7:0] fifo_dout;     //fifo输出信号线?
wire        almost_full;   //fifo将满    
wire        almost_empty;  //fifo将空
wire        fifo_full;     //fifo满?    
wire        fifo_empty;    //fifo空?    
wire  [7:0] fifo_wr_data_count; // FIFO写时钟域计数?
wire  [7:0] fifo_rd_data_count; // FIFO读时钟域计数 
    
//例化FIFO核
fifo_generator_0 
u_fifo_generator_0 (
  .wr_clk              (sys_clk),                // input wire wr_clk
  .rd_clk              (sys_clk),                // input wire rd_clk
  
  .din                 (fifo_din),               // input wire [7 : 0] din
  .dout                (fifo_dout),              // output wire [7 : 0] dout
  .wr_en               (fifo_wr_en),             // input wire wr_en
  .rd_en               (fifo_rd_en),             // input wire rd_en
  
  .full                (fifo_full),              // output wire full
  .empty               (fifo_empty),             // output wire empty
  .almost_full         (almost_full),            // output wire almost_full
  .almost_empty        (almost_empty),           // output wire almost_empty
  
  .rd_data_count       (fifo_rd_data_count),     // output wire [7 : 0] rd_data_count
  .wr_data_count       (fifo_wr_data_count)      // output wire [7 : 0] wr_data_count
);    
    
//例化写FIFO模块
fifo_wr
u_fifo_wr (
    .clk               (sys_clk),
    .rstn              (sys_rstn),
    
    .fifo_wr_en        (fifo_wr_en),//fifo写请求
    .fifo_wr_data      (fifo_din),//fifo写数据
    .almost_empty      (almost_empty), //fifo将空信号
    .almost_full       (almost_full) //fifo将满信号
);

//例化读FIFO模块
fifo_rd
u_fifo_rd(
    .clk               (sys_clk),
    .rst_n             (sys_rstn),
    
    .fifo_rd_en        (fifo_rd_en),   //fifo读请求
    .fifo_rd_data      (fifo_dout),    //fifo读数据
    .almost_empty      (almost_empty), //fifo将空信号
    .almost_full       (almost_full)   //fifo读满信号
);
    
endmodule

fifo_wr_control:写控制

module fifo_wr(
  input wire clk,              
  input wire rstn,             
    
  input  wire      almost_empty,      //将空信号
  input  wire      almost_full,       //将满信号
  output reg       fifo_wr_en,        //FIFO写使能
  output reg [7:0] fifo_wr_data       //FIFO写入数据
    );

//reg define    
reg [1:0] state;              //动作状态    
reg       almost_empty_d0;    //almost_empty延迟1拍
reg       almost_empty_d1;    //almost_empty延迟2拍    
reg [3:0] delay_cnt;            //延迟计数器  
    
//main code    

//将fifo空信号同步到写时钟域中
always@(posedge clk) begin
    if(!rstn) begin 
        almost_empty_d0 <= 1'b0;
        almost_empty_d1 <= 1'b0;
    end
    else begin
        almost_empty_d0 <= almost_empty;
        almost_empty_d1 <= almost_empty_d0;
    end        
end    
    
//读出FIFO的数据    
always@(posedge clk) begin
    if(!rstn) begin
        fifo_wr_en <= 1'b0;
        fifo_wr_data <= 8'd0;
        state      <= 2'd0;
        delay_cnt  <= 4'd0;
    end
    else begin
        case(state)
            2'd0:begin
                if(almost_empty_d1) begin    //检测到FIFO将要被读空
                    state <= 2'd1;           //就进入到延时状态
                end
                else begin
                    state <= state;
                end
            end
            2'd1: begin
                if(delay_cnt == 4'd10) begin    //延时10拍
                    delay_cnt <= 4'd0;    //FIFO IP核内部信号的更新存在延时,等待10拍
                    state <= 2'd2;        //开始写操作
                    fifo_wr_en <= 1'b1;   //打开写使能
                end
                else begin
                    delay_cnt <= delay_cnt + 4'd1;
                end
            end
            2'd2: begin
                if(almost_full) begin  //等待FIFO将被写满(下一拍就会满)
                    fifo_wr_en <= 1'b0;    //关闭写使能
                    fifo_wr_data <= 8'd0;    
                    state        <= 2'd0;    //回到第一个状态
                end
                else begin                    //如果FIFO没有被写满
                    fifo_wr_en    <= 1'b1;    //则持续打开写使能
                    fifo_wr_data  <= fifo_wr_data + 1'd1;    //且写数据持续累加
                end        
            end
         default: state <= 2'd0;   
         endcase   
    end
end

endmodule

fifo_rd_control:读FIFO控制

module fifo_rd(
    input  wire  clk,      // 时钟信号
    input  wire  rst_n,    // 复位信号
    
    input  [7:0] fifo_rd_data,    //从FIFO读出的数据
    input        almost_full,  //FIFO将满信号
    input        almost_empty, //FIFO将空信号
    output  reg  fifo_rd_en    //FIFO读使能
    );
    
    //reg define
    reg [1:0] state;    //fsm
    reg       almost_full_d0;    //almost_full延迟一拍
    reg       almost_full_d1;    //almost_full延迟两拍
    reg [3:0] delay_cnt;         //延迟计数器
    
//--------------------main--------------------------//
//因为FIFO_full是写钟域的所以要将其同步到读时钟域中
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        almost_full_d0 <= 1'b0;
        almost_full_d1 <= 1'b1;
    end
    else begin
        almost_full_d0 <= almost_full;
        almost_full_d1 <= almost_full_d0;
    end
end

//读出FIFO的数据
always@(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        fifo_rd_en <= 1'b0;
        state <= 2'd0;
        delay_cnt <= 4'd0;
    end
    else begin
        case(state)
            2'd0: begin
                if(almost_full_d1) begin   //检测到FIFO被写满
                    state <= 2'd1;         //检测到进入延时状态
                end
                else begin
                    state <= state;
                end
            end
            2'd1: begin
                if(delay_cnt == 4'd10) begin  //延迟10拍,等待FIFO IP核内部更新
                    delay_cnt <= 4'd10;
                    state <= 2'd2;
                end
                else begin
                    delay_cnt <= delay_cnt + 4'd1;
                end
            end
            2'd2: begin
                if(almost_empty) begin  //检测到FIFO将被读空
                    fifo_rd_en <= 1'b0;  //Read enable close
                    state <= 2'd0;    //fsm into idle
                end
                else begin    //FIFO not empty
                    fifo_rd_en <= 1'b1;    //fifo read enbale 
                end    
            end
            default: begin
                state <= 2'd0;   //defalut is idle
            end
        endcase
    end
end

endmodule

Testbench:

module tb_fifo();
    
    //input
    
    reg system_clk;
    reg sys_rst_n;
    
//UUT
ip_fifo
u_ip_fifo(
    .sys_clk    (system_clk),
    .sys_rstn   (sys_rst_n)
);
    
always begin
    #20 
    system_clk = ~system_clk;
end
    
initial begin
    //input
    sys_rst_n = 1'b0;
    system_clk = 1'b0;
    #100;
    sys_rst_n = 1'b1;
end 
    
endmodule

仿真波形图

写时序:

在这里插入图片描述

写时序

读时序:

在这里插入图片描述

读时序

总体时序:

在这里插入图片描述

总体时序
  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值