module asyn_fifo#( parameter DATA_WIDTH=8, parameter ADDR_WIDTH=3, //地址位宽为log2(deepth) parameter DATA_DEPTH=1<<ADDR_WIDTH ) ( input rst_n, //复位信号输入(应该对复位进行处理同步,个人认为,参考xilinx fifo IP同步stage) input rd_clk, //读取时钟 input wr_clk, //写时钟 input rd_en, //读取使能 input wr_en, //写使能 input [DATA_WIDTH-1:0] data_w, //写入数据 output reg [DATA_WIDTH-1:0]data_r, //读出数据 output reg full, //满标志 output reg empty //空标志 ); //定义DATA_DEPTH个数据宽度为DATA_WIDTH的数据 reg [DATA_WIDTH-1:0]mem[DATA_DEPTH-1:0]; //二进制格式下的写地址,二进制的地址宽度为3 写地址指针 reg [ADDR_WIDTH:0]wr_addr_bin; //二进制格式下的读地址,二进制的地址宽度为3(深度以2为底的对数) reg [ADDR_WIDTH:0]rd_addr_bin; //gray code 表示的地址 reg [ADDR_WIDTH:0]syn_wr_addr0_gray; //格雷码写地址0 reg [ADDR_WIDTH:0]syn_wr_addr1_gray; //格雷码写地址1 reg [ADDR_WIDTH:0]syn_wr_addr2_gray; //格雷码写地址2 reg [ADDR_WIDTH:0]syn_rd_addr0_gray; //格雷码读地址0 reg [ADDR_WIDTH:0]syn_rd_addr1_gray; //格雷码读地址1 reg [ADDR_WIDTH:0]syn_rd_addr2_gray; //格雷码读地址2 wire [ADDR_WIDTH-1:0] fifo_enter_addr; //fifo传入的地址 wire [ADDR_WIDTH-1:0]fifo_exit_addr; //fifo输出的地址 /* 关于为什么binary下的地址宽度大一位,是因为要转化为格雷码,作用是为了空满比较 */ wire [ADDR_WIDTH:0] wr_nextaddr_bin; wire [ADDR_WIDTH:0] rd_nextaddr_bin; wire [ADDR_WIDTH:0] wr_nextaddr_gray; wire [ADDR_WIDTH:0] rd_nextaddr_gray; wire asyn_full; //满标志信号,由于写入数据导致,属于写时钟域 wire asyn_empty; //空标志信号,由于读取数据导致,属于读时钟域 //fifo数据的写入和读出地址,格雷码的最高位表示第二轮 assign fifo_enter_addr = wr_addr_bin[ADDR_WIDTH-1:0]; //写入数据到fifo的写指针指向的地址 assign fifo_exit_addr = rd_addr_bin[ADDR_WIDTH-1:0]; //从fifo中读取数据指针指向的地址 //写入数据 always@(posedge wr_clk or negedge rst_n) if(~rst_n) begin //复位将fifo清零 integer i; for(i=0;i<DATA_DEPTH;i=i+1) mem[i]<={DATA_DEPTH{1'b0}}; end else if(wr_en&(~full)) //写使能且fifo未满 mem[fifo_enter_addr]<=data_w; //将数据写入到mem else mem[fifo_enter_addr]<=mem[fifo_enter_addr]; //数据读取 always@(posedge rd_clk or negedge rst_n) if(~rst_n) data_r<={DATA_DEPTH{1'b0}}; else data_r<=mem[fifo_exit_addr]; //fifo读写地址生成器,二进制转格雷码, //在fifo未满且写请求时,将写地址自动加一(binary) assign wr_nextaddr_bin= (wr_en&(~full)) ? fifo_enter_addr+1'b1: fifo_enter_addr; assign rd_nextaddr_bin= (rd_en&(~empty)) ? fifo_exit_addr+1'b1 : fifo_exit_addr ; //convert binary to gray code 就是二进制地址右移一位之后与原地址异或 assign wr_nextaddr_gray=(wr_nextaddr_bin>>1)^wr_nextaddr_bin; assign rd_nextaddr_gray=(rd_nextaddr_bin>>1)^rd_nextaddr_bin; always@(posedge wr_clk or negedge rst_n) if(~rst_n) begin wr_addr_bin<=0; syn_wr_addr0_gray<=0; end else begin wr_addr_bin<=wr_nextaddr_bin; //下一个地址赋值给写数据地址 syn_wr_addr0_gray<=wr_nextaddr_gray; //将下一个地址的格雷码赋值给syn_wr_addr0_gray进行同步寄存 end always@(posedge rd_clk or negedge rst_n) if(~rst_n) begin rd_addr_bin<=0; syn_rd_addr0_gray<=0; end else begin rd_addr_bin<=rd_nextaddr_bin; syn_rd_addr0_gray<=rd_nextaddr_gray; end /* 对格雷码进行同步: 1、将格雷码写地址同步到读时钟域,用于空判断 2、将格雷码读地址同步到写时钟域,用于满判断 */ always@(posedge rd_clk or negedge rst_n) if(~rst_n) begin syn_wr_addr1_gray<=0; syn_wr_addr2_gray<=0; end else begin syn_wr_addr1_gray<=syn_wr_addr0_gray; syn_wr_addr2_gray<=syn_wr_addr1_gray; end always@(posedge wr_clk or negedge rst_n ) if(~rst_n) begin syn_rd_addr1_gray<=0; syn_rd_addr2_gray<=0; end else begin syn_rd_addr1_gray<=syn_rd_addr0_gray; syn_rd_addr2_gray<=syn_rd_addr1_gray; end //将产生的同步后的地址进行比较 assign asyn_empty = ( rd_nextaddr_gray==syn_wr_addr2_gray ); //格雷码的高两位相反,其余位相同时,表示fifo满 assign asyn_full = ( wr_nextaddr_gray=={ ~{syn_rd_addr2_gray[ADDR_WIDTH:ADDR_WIDTH-1]},syn_rd_addr2_gray[ADDR_WIDTH-2:0] } ); always@(posedge wr_clk or negedge rst_n ) if(~rst_n ) full<=1'b0; else full<=asyn_full; always@(posedge rd_clk or negedge rst_n ) if(~rst_n) empty<=1'b1; else empty<=asyn_empty; endmodule