一水寒

从同步FIFO看模块化设计风格

0
阅读(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