目录
1 双端口RAM设计
1.1 FIFO中的数据存储
大量数据的存放不是使用reg,而是使用mem去存储。
fifo使用双端口RAM存储数据:即输入端口和输出端口是独立的。
1.2 双端口RAM的verilog实现
下面为双端口RAM的verilog实现:
module dual_port_ram#(
parameter DEPTH = 16,
parameter WIDTH = 8
)(
input wclk ,
input wenc ,
input [$clog2(DEPTH)-1:0] waddr ,
input [WIDTH-1:0] wdata ,
input rclk ,
input renc ,
input [$clog2(DEPTH)-1:0] raddr ,
output reg [WIDTH-1:0] rdata
);
reg [WIDTH-1:0] ram_mem [0:DEPTH-1];
always_ff@(posedge wclk)begin
if(wenc)
ram_mem[waddr] <= wdata;
end
always_ff@(posedge rclk)begin
if(renc)
rdata <= ram_mem[raddr];
end
endmodule
下面为双端口RAM的testbench:(是使用vcs进行仿真的,如果使用vivado或者其他工具仿真的话,稍微修改下就好~)
`timescale 1ns/1ps
module tb_dual_port_ram();
parameter DEPTH = 16;
parameter WIDTH = 8;
reg wclk ;
reg wenc ;
reg [$clog2(DEPTH)-1:0] waddr ;
reg [WIDTH-1:0] wdata ;
reg rclk ;
reg renc ;
reg [$clog2(DEPTH)-1:0] raddr ;
wire [WIDTH-1:0] rdata;
initial begin
wclk = 0;
forever #10 wclk = ~wclk;
end
initial begin
rclk = 0;
forever #10 rclk = ~rclk;
end
initial begin
wenc = 0;
waddr = 0;
wdata = 0;
renc = 0;
raddr = 0;
#40;
wenc = 1;
repeat(16)begin
waddr = waddr + 1;
wdata = wdata + 1;
#20;
end
#40;
renc = 1;
repeat(16)begin
raddr = raddr + 1;
#20;
end
end
dual_port_ram#(
.DEPTH(DEPTH),
.WIDTH(WIDTH)
)u_dual_port_ram(
.wclk (wclk ),
.wenc (wenc ),
.waddr (waddr ),
.wdata (wdata ),
.rclk (rclk ),
.renc (renc ),
.raddr (raddr ),
.rdata (rdata )
);
initial begin
$fsdbDumpfile("tb_dual_port_ram.fsdb");
$fsdbDumpvars(0);
$fsdbDumpMDA(0,tb_dual_port_ram);
#1000;
$finish;
end
endmodule
2 同步FIFO设计
2.1 FIFO空满信号的产生
FIFO的原则:empty不能读,full不能写
FIFO设计的关键就是设计empty和full信号
空满信号的产生通常用两种方法:
方法一:直接比较读写指针,(读写指针追赶的一个过程)
读指针表示将要读取数据的地址
写指针表示将要写入数据的地址
写操作使得读写指针相等
满信号的产生:fifo_full=(read_ptr == write_ptr+1) && write
读操作使得读写指针相等
空信号的产生:fifo_empty=(write_ptr == read_ptr +1) && read
方法二:使用counter计数器来进行比较
写操作时,counter+1;
读操作时,counter-1;
读写同时进行的时候,counter不变
比较counter与ram的深度来产生空满信号
本次设计使用方法二实现
2.2 同步FIFO的verilog实现
下面为同步FIFO的verilog代码:
module sync_fifo#(
parameter DEPTH = 16,
parameter WIDTH = 8
)(
input clk ,
input rst_n ,
input winc ,
input [WIDTH-1:0] wdata ,
input rinc ,
input [WIDTH-1:0] rdata ,
output wfull ,
output rempty
);
reg [$clog2(DEPTH)-1:0] write_ptr;
reg [$clog2(DEPTH)-1:0] read_ptr;
wire wen_vld;
wire ren_vld;
reg [$clog2(DEPTH):0] data_count;
//generate write pointer
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
write_ptr <= 0;
end
else if(winc && !wfull)begin
write_ptr <= write_ptr + 1;
end
end
//generate read pointer
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
read_ptr <= 0;
else if(rinc && !rempty)begin
read_ptr <= read_ptr + 1;
end
end
//generate data counter
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
data_count <= 0;
else if(winc && !wfull && !rinc)
data_count <= data_count + 1;
else if(rinc && !rempty && !winc)
data_count <= data_count - 1;
end
//generate full && empty
assign wfull = (data_count == DEPTH) ? 1 : 0;
assign rempty = (data_count == 0) ? 1 : 0;
assign wen_vld = winc && (!wfull);
assign ren_vld = rinc && (!rempty);
dual_port_ram#(
.DEPTH(DEPTH),
.WIDTH(WIDTH)
)u_dual_port_ram(
.wclk (clk ),
.wenc (wen_vld ),
.waddr (write_ptr ),
.wdata (wdata ),
.rclk (clk ),
.renc (ren_vld ),
.raddr (read_ptr ),
.rdata (rdata )
);
endmodule
下面为同步FIFO的testbench:(是使用vcs进行仿真的,如果使用vivado或者其他工具仿真的话,稍微修改下就好~)
`timescale 1ns/1ps
module tb_sync_fifo();
parameter DEPTH = 16;
parameter WIDTH = 8;
reg clk ;
reg rst_n ;
reg winc ;
reg [WIDTH-1:0] wdata ;
reg rinc ;
wire [WIDTH-1:0]rdata ;
wire wfull ;
wire rempty ;
initial begin
clk = 0;
forever #10 clk = ~clk;
end
initial begin
rst_n = 0;
#1000;
rst_n = 1;
end
initial begin
winc = 0;
rinc = 0;
#1000;
#200;
winc = 1;
#4000;
winc = 0;
#2000;
winc = 1;
#4000;
winc = 0;
#4000;
$finish;
end
always @(posedge clk or negedge rst_n)
if(!rst_n)
wdata <= 16'd0;
else if(winc && ~wfull)
wdata <= $random%256;
always @(posedge clk or negedge rst_n)
if(!rst_n)
rinc <= 1'b0;
else if(~rempty)
rinc <= 1'b1;
else
rinc <= 1'b0;
sync_fifo#(
.DEPTH(DEPTH),
.WIDTH(WIDTH)
)u_sync_fifo(
.clk (clk ),
.rst_n (rst_n ),
.winc (winc ),
.wdata (wdata ),
.rinc (rinc ),
.rdata (rdata ),
.wfull (wfull ),
.rempty (rempty )
);
initial begin
$fsdbDumpfile("tb_sync_fifo.fsdb");
$fsdbDumpvars(0);
$fsdbDumpMDA(0,tb_sync_fifo);
#100000;
$finish;
end
endmodule
参考博客:
同步fifo设计_IamSarah的博客-优快云博客_同步fifo设计
两种同步FIFO的设计方法(计数器、扩展位)_SD.ZHAI的博客-优快云博客_同步fifo设计
GitHub - DeamonYang/FPGA_SYNC_ASYNC_FIFO: FPGA 同步FIFO与异步FIFO