过段时间就要秋招了,打算做一个知识汇总,也算是为秋招做准备吧
首先fifo是first in first out,先进先出。可以想象成一根管子,一端往里边压入(push)小球,另一边往外边弹出(pop)小球。
同步fifo一般有以下几个必备端口
信号名称 | 端口方向 | 位宽 | 说明 |
clk | I | 1 | 时钟,读写端口共用 |
rst_n | I | 1 | 复位 |
winc | I | 1 | push操作 |
wdata | I | —— | 要写进去的数据 |
full | O | 1 | 满标志 |
rinc | I | 1 | pop操作 |
rdata | O | —— | 读出来的数据 |
empty | O | 1 | 空标志 |
同步fifo的核心是一块双口ram和读写控制逻辑组成。双口ram的端口说明比较显然,就不再介绍了。
fifo的关键原则是写满不溢出,读空不多读。因此空满标志位的产生和双口ram的读写使能信号输入是设计的关键。
module sync_fifo #(
parameter DATA_WIDTH = 8 , // fifo位宽
parameter FIFO_DEPTH = 16 // fifo深度
)(
input clk ,
input rst_n ,
input wr_en ,
input [DATA_WIDTH-1:0] wr_data , //input data
output full , //full flag
input rd_en ,
output [DATA_WIDTH-1:0] rd_data , //output data
output empty
);
localparam ADDR_WIDTH = $clog2(FIFO_DEPTH) ; //4
reg [ADDR_WIDTH:0] wr_ptr ;
reg [ADDR_WIDTH:0] rd_ptr ; //读写指针比地址多1位,用于检测空满
reg [DATA_WIDTH-1:0] buffer_mem [0:FIFO_DEPTH-1]; //寄存器堆
wire fifo_wr ;
wire fifo_rd ;
assign fifo_wr = wr_en && ~full ;
assign fifo_rd = rd_en && ~empty ;
//写指针
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
wr_ptr<= {(ADDR_WIDTH+1){1'b0}};
else if(fifo_wr)
wr_ptr <= wr_ptr + 1'b1 ;
end
//读指针
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
rd_ptr <= {(ADDR_WIDTH+1){1'b0}};
else if(fifo_rd)
rd_ptr <= rd_ptr + 1'b1 ;
end
//空满
assign full = ({~wr_ptr[ADDR_WIDTH],wr_ptr[ADDR_WIDTH-1:0]}==rd_ptr) ;
assign empty = (wr_ptr==rd_ptr) ;
dualport_ram #(
.DATA_WIDTH(DATA_WIDTH),
.RAM_DEPTH(FIFO_DEPTH),
.ADDR_WIDTH(ADDR_WIDTH)
) inst_dualport_ram (
.clka (clk),
.ena (fifo_wr),
.addr_a (wr_ptr[ADDR_WIDTH-1:0]),
.data_a (wr_data),
.clkb (clk),
.enb (fifo_rd),
.addr_b (rd_ptr[ADDR_WIDTH-1:0]),
.data_b (rd_data)
);
endmodule
module dualport_ram #(
parameter DATA_WIDTH = 8 ,
parameter RAM_DEPTH = 16 ,
parameter ADDR_WIDTH = $clog2(RAM_DEPTH)
)(
input clka,
input ena,
input [ADDR_WIDTH-1:0] addr_a,
input [DATA_WIDTH-1:0] data_a,
input clkb,
input enb,
input [ADDR_WIDTH-1:0] addr_b,
output reg [DATA_WIDTH-1:0] data_b
);
reg [DATA_WIDTH-1:0] mem [RAM_DEPTH-1:0] ;
//------------write data--------//
always @(posedge clka ) begin
if(ena)
mem[addr_a]<= data_a ;
end
//------------read data---------//
/*
always @(posedge clkb) begin
if(enb)
data_b<=mem[addr_b];
end
*/
always @(*) begin
if(enb)
data_b=mem[addr_b];
else
data_b=data_b;
end
endmodule
代码中重点是fifo_wr和fifo_rd的逻辑以及fifo空满的判断,指针多1位用来判断空满 ;最高位不同,其余位相同时为满,所有位都相同则为空。同步fifo设计看起来比较简单。
下面进行tb仿真验证波形
testbench
`timescale 1ns/100ps
module tb_sync_fifo (); /* this is automatically generated */
// clock
logic clk;
initial begin
clk = 1'b0;
forever #(5) clk = ~clk;
end
// asynchronous reset
logic rst_n;
// (*NOTE*) replace reset, clock, others
parameter DATA_WIDTH = 8;
parameter FIFO_DEPTH = 16;
logic wr_en;
logic [DATA_WIDTH-1:0] wr_data;
logic full;
logic rd_en;
logic [DATA_WIDTH-1:0] rd_data;
logic empty;
integer cnt;
integer rand0=$random() %4 +2;
initial begin
wr_en = '0;
wr_data = '0;
rd_en = '0;
rst_n = 1'b0;
repeat(2) @(posedge clk); #1;
rst_n = 1;
repeat(3) @(posedge clk); #1;
//test1
//写16个数据
for(cnt = 0; cnt<16; cnt=cnt+1) begin
wr_en = '1;
wr_data = cnt+1 ;
@(posedge clk); //#1;
wr_en = '0;
end
repeat(rand0) @(posedge clk);
//读16个数据
for(cnt = 0; cnt<16; cnt=cnt+1) begin
rd_en = '1;
@(posedge clk); //#1;
rd_en = '0;
end
//test2 写使能大于16T,读使能持续16T
for(cnt = 0; cnt<20; cnt=cnt+1) begin
wr_en = '1;
wr_data = 101+cnt ;
@(posedge clk); //#1;
wr_en = '0;
end
repeat(rand0) @(posedge clk);
//读16个数据
for(cnt = 0; cnt<16; cnt=cnt+1) begin
rd_en = '1;
@(posedge clk); //#1;
rd_en = '0;
end
//test3 写使能16T,读使能持续20T
for(cnt = 0; cnt<16; cnt=cnt+1) begin
wr_en = '1;
wr_data = 201+cnt ;
@(posedge clk); //#1;
wr_en = '0;
end
repeat(rand0) @(posedge clk);
//读16个数据
for(cnt = 0; cnt<20; cnt=cnt+1) begin
rd_en = '1;
@(posedge clk); //#1;
rd_en = '0;
end
end
sync_fifo #(
.DATA_WIDTH(DATA_WIDTH),
.FIFO_DEPTH(FIFO_DEPTH)
) inst_sync_fifo (
.clk (clk),
.rst_n (rst_n),
.wr_en (wr_en),
.wr_data (wr_data),
.full (full),
.rd_en (rd_en),
.rd_data (rd_data),
.empty (empty)
);
endmodule
ram使用组合逻辑读的波形。空满标志位正常,ram的读写逻辑控制时序也正确,
ram使用时序逻辑读的波形 ,读出来的数据比组合逻辑延后一拍。
重点是分析清楚里边的时序关系,保证ram产生正确的读写时能,以及生成正确的fifo空满标志位。