同步FIFO的设计,难点在于满指针和空指针的产生。同步的意思是读写时钟是同一个时钟,读写在相同时钟域下工作。
读出数据时,读请求有效,只要非空,从读指针处读出数据。
写入数据时,写请求有效,只要非满,将数据写入写指针处。
输入信号:时钟、复位,写请求,写数据,读请求,;
输出:读出数据,空信号,满信号。
写一个深度为8的,位宽为8的FIFO。
`timescale 1ns/1ps
module FIFO_syn(Clk,Rst_n,rd_valid,wr_valid,rddata,wrdata,empty,full);
input Clk,Rst_n,wr_valid,rd_valid;
input [7:0] wrdata;
output reg [7:0] rddata;
output reg empty,full;
reg [2:0] wrptr,rdptr;
reg [7:0] mem [0:7];
integer i;
//数据写入,写指针增加
always@(posedge Clk or negedge Rst_n) begin
if(!Rst_n) begin
for(i=0;i<=7;i=i+1)
mem[i] <= 0; //mem不能直接以mem=0初始化
wrptr <= 0;
end
else if (wr_valid&~full) begin
mem[wrptr] <= wrdata;
wrptr <= (wrptr == 3'd7)?(8'd0):(wrptr + 1'b1);
end
end
//数据读出,读指针增加
always@(posedge Clk or negedge Rst_n) begin
if(!Rst_n) begin
rddata <= 0;
rdptr <= 0;
end
else if (rd_valid&~empty) begin
rddata <= mem[rdptr];
rdptr <= (rdptr==3'd7)?8'd0:(rdptr + 1'b1);end
end
//空信号
always@(posedge Clk,negedge Rst_n) begin
if(!Rst_n)
empty <= 1'b1;
else if (wr_valid)
empty <= 1'b0;
else if (rd_valid&(wrptr==rdptr+1'b1))
empty <= 1'b1;
end
//满信号
always@(posedge Clk,negedge Rst_n) begin
if(!Rst_n)
full <= 1'b0;
else if (wr_valid&(rdptr==wrptr+1'b1))
full <= 1'b1;
else if (rd_valid)
full <= 1'b0;
end
endmodule
下面是仿真代码
`timescale 1ns/1ps
`define clk_period 20
module tb_FIFO_syn();
reg clk,rst_n,wr,rd;
reg [7:0] wrdata;
wire empty,full;
wire [7:0] rddata;
FIFO_syn fifos(.Clk(clk),.Rst_n(rst_n),.wr_valid(wr),.rd_valid(rd),.wrdata(wrdata),.rddata(rddata),.empty(empty),.full(full));
initial clk =1'b0;
always#(`clk_period/2) clk=~clk;
integer i;
initial begin
i=0;rst_n=0;wrdata=0;wr=0;rd=0;
#20.01;rst_n=1;
#20.01;rd=1;
//持续读和写
#(`clk_period*10) wr=1;wrdata=8'b0; //第一个写周期,读是读不出的。
for(i=0;i<=20;i=i+1)
#(20) wrdata=i+1;
rd=0;
//不读,写满
#(`clk_period*2) wr=1;wrdata=8'b0;
for(i=0;i<=20;i=i+1)
#(20) wrdata=i;
wr=0;
//不写,读空
#(`clk_period*2) rd=1;
#(`clk_period*20)
//不读,写满
rd=0;
#(`clk_period*2) wr=1;wrdata=8'b0;
for(i=0;i<=20;i=i+1)
#(20) wrdata=i;
wr=0;
end
initial
begin
$fsdbDumpMDA();
$fsdbDumpfile("fifosyn.fsdb");
$fsdbDumpvars;
$fsdbDumpon;
end
initial #5000 $finish;
endmodule