同步fifo的设计比异步fifo简单,不需要进行跨时钟域处理,主要难点在于空、满信号的判断;
首先,我们需要先了解一下fifo的读写:可以通俗的理解为装水与取水,wr_addr便是向罐子里装水,而rd_addr便是取水,如果wr_addr的速度大于rd_addr速度,则一段时间后罐子便会装满,即fifo写满,反之,fifo读空,一般而言,装水与取水不同时进行,当罐子装满后,full信号拉高,通知rd_addr进行取水,当罐子里没水后,empty信号拉高,在装水;
现在是如何判断空,满呢,我们可以将rd_addr,er_addr位宽增加一位,利用最高位是否相等来判断,如果最高位不相等,是否可以理解为开始之前fifo处于满状态?由此,我们可以得到fifo剩余量为(fifo的深度 - rd_addr + wr_addr) ,如果最高位相等,那fifo剩余量 = (wr_addr - rd_addr)
完整代码如下:
`timescale 1ns/1ns
/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
parameter WIDTH = 8)(
input wclk
,input wenc
,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。
,input [WIDTH-1:0] wdata //数据写入
,input rclk
,input renc
,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。
,output reg [WIDTH-1:0] rdata //数据输出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end
endmodule
/**********************************SFIFO************************************/
module sfifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input clk ,
input rst_n ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output reg wfull ,
output reg rempty ,
output wire [WIDTH-1:0] rdata
);
parameter addr = $clog2(DEPTH);
reg [addr:0] waddr;
reg [addr:0] raddr;
wire [addr:0] cnt;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
waddr <= 'd0;
else if((winc == 1'b1)&&(wfull == 1'b0))
waddr <= waddr + 1'b1;
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
raddr <= 'd0;
else if((rinc == 1'b1)&&(rempty == 1'b0))
raddr <= raddr + 1'b1;
assign cnt = (waddr[addr] == raddr[addr])?
(waddr[addr:0] - raddr[addr:0]):
(DEPTH + waddr[addr-1:0] - raddr[addr-1:0]);
always@(posedge clk or negedge rst_n)
if(rst_n == 1'b0)
begin
wfull <= 1'b0;
rempty <= 1'b0;
end
else if(cnt == 'd0)
rempty <= 1'b1;
else if(cnt == DEPTH)
wfull <= 1'b1;
else
begin
rempty <= 1'b0;
wfull <= 1'b0;
end
wire wen;
wire ren;
assign wen = ((winc == 1'b1)&&(wfull == 1'b0));
assign ren = ((rinc == 1'b1)&&(rempty == 1'b0));
dual_port_RAM
#(.DEPTH (DEPTH),
.WIDTH (WIDTH))
dual_port_RAM_inst
(
.wclk (clk ),
.wenc (wen),
.waddr (waddr[addr-1:0]), //深度对2取对数,得到地址的位宽。
.wdata (wdata), //数据写入
.rclk (clk),
.renc (ren),
.raddr (raddr[addr-1:0]), //深度对2取对数,得到地址的位宽。
.rdata (rdata) //数据输出
);
endmodule