本次设计主要介绍异步FIFO中读写指针和格雷码的原理及其实现,最后会有代码和仿真文件
一、异步FIFO的重要参数及其作用
异步FIFO主要用作跨时钟域的数据缓存。
二、设计要点
异步FIFO设计中,最重要的就是空满判断,格雷码是现在使用最多用于判断空满的一种码制,虽然都知道用格雷码,那为什么要用格雷码?
还有就是为什么读写指针要比FIFO_DEPTH多一位,以及格雷码在这之中起到的重要作用,这个在说完格雷码之后会有解释。
格雷码:因1953年公开的弗兰克·格雷专利 “Pulse Code Communication”而得名。
格雷码是一种安全码,因为相邻的格雷码只有一位不同,和二进制不同,二进制一般相邻的都有多位不同。格雷码在传输中,因为相邻只有一位不同,所以其误码率比二进制低得多。
在同步时,出现亚稳态的概率也比二进制低。
二进制转换为格雷码

Verilog代码描述:gray = binary ^ (binary >> 1)

注:以上两幅关于格雷码的图片均不是原创,不知道出处,若有侵权,请联系删除
1、首先是读写指针为什么可以多一位,对读写地址有没有影响
2、其次是为什么要用格雷码
3、空满信号的同步
因为空信号是对读操作有影响,所以,将空信号在rd_clk下同步(多采用两级寄存器)
因为满信号是对写操作有影响,所以,将满信号在wr_clk下同步(多采用两级寄存器)
三、源代码及仿真
1、源代码
`timescale 1ns / 1ns module asynchronous_fifo #( parameter FIFO_WIDTH = 8, parameter FIFO_DEPTH = 8 )( input wr_clk, //写时钟 input rd_clk, //读时钟 input wr_en, //写使能 input rd_en, //读使能 input wr_rst_n, //写复位 input rd_rst_n, //读复位 input [FIFO_WIDTH-1:0] wr_data, //写入的数据 output reg [FIFO_WIDTH-1:0] rd_data, //输出的数据 output reg wr_full, output reg rd_empty, output wr_fifo_en, output rd_fifo_en, output reg [$clog2(FIFO_DEPTH):0] wr_gray_g2, // 写指针 output reg [$clog2(FIFO_DEPTH):0] rd_gray_g2 ); // reg define reg [$clog2(FIFO_DEPTH):0] wr_pointer = 0; // 写指针 reg [$clog2(FIFO_DEPTH):0] rd_pointer = 0; // 读指针 reg [$clog2(FIFO_DEPTH):0] wr_gray_g1; // 写格雷码,用格雷码来判断空满 reg [$clog2(FIFO_DEPTH):0] rd_gray_g1; wire [$clog2(FIFO_DEPTH):0] wr_gray; // 写格雷码,用格雷码来判断空满 wire [$clog2(FIFO_DEPTH):0] rd_gray; // 读格雷码,用格雷码来判断空满 wire [$clog2(FIFO_DEPTH)-1:0] wr_addr; wire [$clog2(FIFO_DEPTH)-1:0] rd_addr; wire full_1; wire empty_1; //ram define //(*ram_style = "distributed"*) reg [FIFO_WIDTH - 1 : 0] fifo_buffer [FIFO_DEPTH - 1:0]; //寄存器组当FIFO reg [FIFO_WIDTH - 1 : 0] fifo_buffer [FIFO_DEPTH - 1:0]; //寄存器组当FIFO assign wr_fifo_en = wr_en && !full_1; assign rd_fifo_en = rd_en && !empty_1; assign wr_gray = wr_pointer^(wr_pointer>>1); assign rd_gray = rd_pointer^(rd_pointer>>1); assign wr_addr = wr_pointer[$clog2(FIFO_DEPTH)-1:0]; assign rd_addr = rd_pointer[$clog2(FIFO_DEPTH)-1:0]; assign full_1 = ({~rd_gray_g2[$clog2(FIFO_DEPTH):$clog2(FIFO_DEPTH)-1], rd_gray_g2[$clog2(FIFO_DEPTH)-2:0]} == wr_gray)?1:0; assign empty_1 = (wr_gray_g2 == rd_gray)?1:0; //将rd_gray在写时钟域与wr_gray比较,如果wr_gray与rd_gray高两位相反,低位相等,则写满 always@(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) begin rd_gray_g1 <= 0; rd_gray_g2 <= 0; end else begin rd_gray_g1 <= rd_gray; rd_gray_g2 <= rd_gray_g1; end end always@(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) wr_full <= 1'b0; else if(wr_full && wr_en) wr_full <= 1'b1; else wr_full <= full_1; end //将wr_gray在读时钟域与rd_gray比较,相等为FIFO空 always@(posedge rd_clk or negedge rd_rst_n) begin if(!rd_rst_n) begin wr_gray_g1 <= 0; wr_gray_g2 <= 0; end else begin wr_gray_g1 <= wr_gray; wr_gray_g2 <= wr_gray_g1; end end always@(posedge rd_clk or negedge rd_rst_n) begin if(!rd_rst_n) rd_empty <= 1'b0; else if(rd_empty && rd_en) rd_empty <= 1'b1; else rd_empty <= empty_1; end // 写数据指针计数 always@(posedge wr_clk or negedge wr_rst_n) begin if(!wr_rst_n) begin fifo_buffer[wr_addr] <= 'bz; wr_pointer <= 0; end else if(wr_fifo_en) begin fifo_buffer[wr_addr] <= wr_data; wr_pointer <= wr_pointer + 1'b1; end else begin fifo_buffer[wr_addr] <= fifo_buffer[wr_addr]; wr_pointer <= wr_pointer; end end // 读数据指针计数 always@(posedge rd_clk or negedge rd_rst_n) begin if(!rd_rst_n) begin rd_data <= 'bz; rd_pointer <= 0; end else if(rd_fifo_en) begin rd_data <= fifo_buffer[rd_addr]; rd_pointer <= rd_pointer + 1'b1; end else begin rd_data <= fifo_buffer[rd_addr]; rd_pointer <= rd_pointer; end end endmodule
2、仿真文件
改变clk_period_wr和clk_period_rd就可以实现读写快慢切换
`timescale 1ns / 1ns `define clk_period_wr 50 `define clk_period_rd 20 module asynchronous_fifo_tb(); parameter FIFO_WIDTH = 8; parameter FIFO_DEPTH = 16; reg wr_clk; reg rd_clk; reg wr_en; reg rd_en; reg wr_rst_n; reg rd_rst_n; reg [FIFO_WIDTH-1:0] wr_data; wire [FIFO_WIDTH-1:0] rd_data; wire wr_full; wire rd_empty; wire wr_fifo_en; wire rd_fifo_en; wire [$clog2(FIFO_DEPTH):0] wr_gray_g2; wire [$clog2(FIFO_DEPTH):0] rd_gray_g2; assign wr_fifo_en = wr_en && !wr_full; assign rd_fifo_en = rd_en && !rd_empty; initial begin wr_clk = 0; forever begin #(`clk_period_wr/2) wr_clk = ~wr_clk; end end initial begin rd_clk = 0; forever begin #(`clk_period_rd/2) rd_clk = ~rd_clk; end end initial begin wr_rst_n = 1; rd_rst_n = 1; wr_en = 0; rd_en = 0; #5; wr_rst_n = 0; rd_rst_n = 0; #5; wr_rst_n = 1; rd_rst_n = 1; end initial begin //第一段仿真 @(negedge wr_clk) wr_en = 1;wr_data = $random; repeat(7) begin @(negedge wr_clk) wr_data = $random; end @(negedge wr_clk) wr_en = 0; @(negedge rd_clk) rd_en = 1; repeat(8) begin @(negedge rd_clk); end @(negedge rd_clk) rd_en = 0; # 150 //第二段仿真 @(negedge wr_clk) wr_en = 1; wr_data = $random; repeat(18) begin @(negedge wr_clk) wr_data = $random; end @(negedge wr_clk) wr_en = 0; @(negedge rd_clk) rd_en = 1; repeat(18) begin @(negedge rd_clk); end rd_en = 0; // 第三段仿真 repeat(9) begin @(negedge wr_clk) wr_data = $random;wr_en = 1; end rd_en = 1; repeat(19) begin @(negedge wr_clk) wr_data = $random; end @(negedge wr_clk) wr_en = 0; @(negedge rd_clk) rd_en = 0; #20 $finish; end asynchronous_fifo #( .FIFO_WIDTH (FIFO_WIDTH), .FIFO_DEPTH (FIFO_DEPTH) ) asynchronous_fifo ( .wr_clk (wr_clk), .rd_clk (rd_clk), .wr_rst_n (wr_rst_n), .rd_rst_n (rd_rst_n), .wr_en (wr_fifo_en), .rd_en (rd_fifo_en), .wr_data (wr_data), .wr_full (wr_full), .rd_empty (rd_empty), .rd_data (rd_data) ); endmodule
3、仿真截图
读比写快



写比读快



这个代码也还有些问题,希望大家多多包容并且指出错误或者可以改善的地方,一起进步。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/229050.html原文链接:https://javaforall.net
