verilog FPGA 同步FIFO设计及仿真
一、核心思想
FIFO即First In First Out,是一种数据缓冲方式。同步FIFO的读写端在同一时钟下
二、时序
1、写操作
当写入一个数据,写有效拉高,写地址加一,数据存量加一
2、读数据
当读出一个数据,读有效拉高,读地址加一,数据存量减一
3、存数据
利用寄存器组来实现数据的存储
四、设计难点
1、计数器与一般的计数器不同,在这里没有用add_cnt 和end_cnt,因为计数器本身会有减的情况
2、寄存器数组置零,在这里首次使用for循环来对其循环置零
五、代码实现
fifo_sync.v
module fifo_sync#(parameter dwidth = 8,awidth = 8)(//fifo 宽度为8,深度为2^8=256
input clk ,
input rst_n ,
input wr_req ,
input [dwidth-1:0] wr_data ,
input rd_req ,
output [dwidth-1:0] rd_data ,
output reg[awidth-1:0] cnt ,//数据量
output full ,
output empty
);
reg [awidth-1:0] wr_addr;//写地址
wire add_wr_addr;
wire end_wr_addr;
reg [awidth-1:0] rd_addr;//读地址
wire add_rd_addr;
wire end_rd_addr;
reg [dwidth-1:0] fifo_ram[{(awidth){1'b1}}:0];//寄存器组
integer i;
//cnt 当读信号有效,cnt-1,当写信号有效,cnt+1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 1'b0;
end
else if(rd_req &&wr_req)begin//当同时读写cnt不变
cnt <= cnt;
end
else if(wr_req && cnt<{(awidth){1'b1}})begin//当写信号有效且非满cnt加一
cnt <= cnt+1;
end
else if(rd_req && cnt>0)begin//当读信号有效且非空cnt减一
cnt <= cnt-1;
end
else
cnt <= cnt;
end
//wr_addr
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_addr <= 0;
end
else if(add_wr_addr)begin
if(end_wr_addr)
wr_addr <= 0;
else
wr_addr <= wr_addr+1;
end
end
assign add_wr_addr = wr_req;
assign end_wr_addr = add_wr_addr && wr_addr == {(awidth){1'b1}};
//rd_addr
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_addr <= 0;
end
else if(add_rd_addr)begin
if(end_rd_addr)
rd_addr <= 0;
else
rd_addr <= rd_addr+1;
end
end
assign add_rd_addr = rd_req;
assign end_rd_addr = add_rd_addr && rd_addr == {(awidth){1'b1}};
//empty
assign empty=(cnt!=0)?0:1;
//full
assign full = (cnt == {(awidth){1'b1}})?1:0;
//wr_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
for(i=0;i<={(awidth){1'b1}};i=i+1)begin
fifo_ram[i] <= {(dwidth){1'b0}};
end
end
else begin
fifo_ram[wr_addr] <= wr_data;
end
end
//rd_data
assign rd_data = fifo_ram[rd_addr];
endmodule
fifo_sync_tb.v
`timescale 1ns/1ps
module fifo_sync_tb();
reg tb_clk ;
reg tb_rst_n ;
reg tb_wr_req ;
reg [7:0] tb_wr_data ;
reg tb_rd_req ;
wire [7:0] tb_rd_data ;
wire [4:0] tb_cnt ;
wire tb_full ;
wire tb_empty ;
fifo_sync u_fifo_sync(
.clk (tb_clk ),
.rst_n (tb_rst_n ),
.wr_req (tb_wr_req ),
.wr_data (tb_wr_data ),
.rd_req (tb_rd_req ),
.rd_data (tb_rd_data ),
.cnt (tb_cnt ),//数据量
.full (tb_full ),
.empty (tb_empty )
);
defparam u_fifo_sync.dwidth = 8,
u_fifo_sync.awidth = 8;
parameter CLOCK_CYCLE=20;
initial tb_clk = 1'b0;//初始化时钟
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
integer i=0,j=0;
initial begin
tb_rst_n = 1'b1;
tb_wr_req = 1'b0;
tb_rd_req = 1'b0;
tb_wr_data = 8'd0;
#(20*CLOCK_CYCLE);
tb_rst_n = 1'b0;
#(20*CLOCK_CYCLE);
tb_rst_n = 1'b1;
repeat(40)begin
for(j=0;j<20;j = j+1)begin
i = {$random}%30;
#2;
tb_wr_req = {$random};
tb_rd_req = {$random};
tb_wr_data = {$random}%255;
#(CLOCK_CYCLE*i);
end
end
$stop;
end
endmodule