从同步FIFO看模块化设计风格
0赞
发表于 12/15/2012 10:24:45 PM
阅读(3663)
这两天抽时间把FIFO好好看了下,异步FIFO空满标志的算法值得深究,同步FIFO虽然用的不是很多,但是对于理解fifo的原理还是非常有益的,写异步FIFO也是先从写好同步fifo开始,下面贴出同步fifo代码,备忘...层次化设计是把更成细分为很多的小功能模块,设计思路非常清晰,代码简洁易懂,好的设计风格是每个模块代码都不要超过50-80行,,
层次化设计,原理图如下:一个顶层模块,四个功能模块分别是写地址发生器,读地址发生器,一个fifo标志位发生器,一个双口ram,首先划分功能模块,然后详细规划每个模块的接口设计,接口设计好了以后,用原理图把框架搭好,如下图所示,然后再往每个模块里面填代码。
第一个代码module是顶层模块,对应下面顶层原理图,第二个module是下面四个小模块的例化,最后四个模块是功能模块代码。


顶层代码,用四个例化功能模块组成我们的同步FIFO,对应上面第一张图。其实这个模块是可以要也可以不要的,可以就用下面的my_fifo做顶层也是可以的,只是这样更清晰一些,等于把my_fifo再打一次包,我习惯这样。
module syn_fifo(//顶层 clk, rst_n, rd_en, wr_en, data_in, empty, full, data_out ); input wire clk; input wire rst_n; input wire rd_en; input wire wr_en; input wire [7:0] data_in; output wire empty; output wire full; output wire [7:0] data_out; my_fifo b2v_inst( .clk(clk), .rst_n(rst_n), .rd_en(rd_en), .wr_en(wr_en), .data_in(data_in), .empty(empty), .full(full), .data_out(data_out)); endmodule 下面是四个功能模块的例化,对应上面第二张图。
module my_fifo( clk, rst_n, rd_en, wr_en, data_in, empty, full, data_out); input wire clk; input wire rst_n; input wire rd_en; input wire wr_en; input wire [7:0] data_in; output wire empty; output wire full; output wire [7:0] data_out; wire empty_wire; wire full_wire; wire [3:0] rd_addr; wire [3:0] wr_addr; wr_addr_gen b2v_inst( .clk(clk), .rst_n(rst_n), .wr_en(wr_en), .full(full_wire), .wr_addr(wr_addr)); rd_addr_gen b2v_inst1( .clk(clk), .rst_n(rst_n), .rd_en(rd_en), .empty(empty_wire), .rd_addr(rd_addr)); flag_gen b2v_inst2( .clk(clk), .rst_n(rst_n), .rd_en(rd_en), .wr_en(wr_en), .full(full_wire), .empty(empty_wire)); ram b2v_inst3( .clk(clk), .rst_n(rst_n), .wr_en(wr_en), .rd_en(rd_en), .data_in(data_in), .rd_addr(rd_addr), .wr_addr(wr_addr), .data_out(data_out)); defparam b2v_inst3.depth = 16; assign empty = empty_wire; assign full = full_wire; endmodule
写地址发生器,当写使能信号为1而且fifo非满的时候写地址加1; module wr_addr_gen(clk, rst_n,wr_en, full, wr_addr); input clk; input rst_n; input full; input wr_en; output reg[3:0] wr_addr; always@(posedge clk or negedge rst_n) if(~rst_n) wr_addr<=4'b0000; else if(~full&wr_en) wr_addr<=wr_addr+1; else wr_addr<=4'b0; endmodule
module rd_addr_gen(clk, rst_n,rd_en, empty, rd_addr);
input clk;
input rst_n;
input empty;
input rd_en;
output reg[3:0] rd_addr;
always@(posedge clk or negedge rst_n)
if(~rst_n) rd_addr<=4'b0000;
else if(~empty&rd_en) rd_addr<=rd_addr+1;
else rd_addr<=4'b0000;
endmodule
module flag_gen(clk, rst_n, rd_en, wr_en, full, empty);
input clk;
input rst_n;
input rd_en;
input wr_en;
output reg full;
output reg empty;
reg [4:0]fifo_cnt;
always@(posedge clk or negedge rst_n)
if(~rst_n)fifo_cnt<=0;
else
if(wr_en&rd_en)
fifo_cnt<=fifo_cnt;
else if(wr_en&~rd_en&~(fifo_cnt==16))//only write and fifo_cnt must <16;
fifo_cnt<=fifo_cnt+1; //the data number in fifo will add 1;
else if(~wr_en&rd_en&(fifo_cnt>0)) //only read and fifo_cnt must >0;
fifo_cnt<=fifo_cnt-1; //the data number in fifo will subtract 1;
always@(posedge clk)
if(~rst_n)
begin
full<=0;
empty<=0;
end
else if(fifo_cnt==0)
begin
empty<=1;
full<=0;
end
else if(fifo_cnt==16)
begin
empty<=0;
full<=1;
end
else
begin
empty<=0;
full<=0;
end
endmodule
module ram(clk, rst_n, wr_en, rd_en, wr_addr, rd_addr, data_in, data_out); input clk; input rst_n; input wr_en; input rd_en; input [3:0] wr_addr; input [3:0] rd_addr; input [7:0] data_in; output reg[7:0] data_out; parameter depth=16; reg[7:0]fifo_data[depth-1:0]; always@(posedge clk or negedge rst_n) if(rst_n==0)data_out<=8'hzz; else begin if(wr_en) fifo_data[wr_addr]<=data_in; if(rd_en) data_out<=fifo_data[rd_addr]; //before this "if" can not add a "else" else //you can also write two block of 'always' data_out<=8'hzz; end endmodule
下面是我用的tsetbench,测试还算是全面的,,
`timescale 1 ns/ 1 ps
module syn_fifo_tb();
reg clk;
reg [7:0] data_in;
reg rd_en;
reg rst_n;
reg wr_en;
wire [7:0] data_out;
wire empty;
wire full;
my_fifo i1 (
.clk(clk),
.data_in(data_in),
.data_out(data_out),
.empty(empty),
.full(full),
.rd_en(rd_en),
.rst_n(rst_n),
.wr_en(wr_en)
);
initial
begin
clk=0;rst_n=0;wr_en=0;rd_en=0;data_in=0;
#20 rst_n=1;
#20 wr_en=1;data_in=10;
#20 wr_en=1;data_in=11;
#20 wr_en=1;data_in=12;
#20 wr_en=1;data_in=13;
#20 wr_en=1;data_in=14;
#20 wr_en=1;data_in=15;
#20 wr_en=1;data_in=16;
#20 wr_en=1;data_in=17;
#20 wr_en=1;data_in=18;
#20 wr_en=1;data_in=19;
#20 wr_en=1;data_in=1;
#20 wr_en=1;data_in=2;
#20 wr_en=1;data_in=3;
#20 wr_en=1;data_in=4;
#20 wr_en=1;data_in=5;
#20 wr_en=1;data_in=6;
#20 wr_en=1;data_in=7;
#20 rd_en=1;wr_en=0;
#20 rd_en=1;
#100 rd_en=1;
#20 rd_en=0;
#20 wr_en=1;data_in=30;
#20 wr_en=1;data_in=29;
#20 wr_en=1;data_in=28;
#20 wr_en=1;data_in=27;
#140 data_in=24;
#100 rd_en=1;
#20 wr_en=1;data_in=40;
#20 wr_en=1;data_in=39;
#20 wr_en=1;data_in=38;
#20 wr_en=1;data_in=37;
#20 wr_en=1;data_in=40;
#20 wr_en=1;data_in=39;
#20 wr_en=1;data_in=38;
#20 wr_en=1;data_in=37;
#40 wr_en=0;
end
always #10 clk=~clk;
endmodule
