
////////////////////////////////////////////////
//Engineer: Xiaodong Zhong
//Date:     2019.5.21
//Description: ?????ram
////////////////////////////////////////////////
module syn_fwft_fifo #(
parameter width           =16,
parameter depth           =1024,
parameter prog_full_thre  =512,
parameter prog_empty_thre =16
)(
input               clk,
input				rst,
input               clr,

input               wr_en,
input  [width-1:0]  din,
output              full,
output              almost_full,
output              prog_full,


input               rd_en,
output [width-1:0]  dout,
output              empty,
output              almost_empty,
output              prog_empty,

output [clog2(depth-1)-1:0]     cnt
);
////////////////////////////////////////////////
//log2 function
////////////////////////////////////////////////
function integer clog2(input integer depth);
begin
	for(clog2=0;depth>0;clog2=clog2+1)
		depth =depth>>1;
end
endfunction

localparam aw = clog2(depth-1);

////////////////////////////////////////////////
//reg & wire
////////////////////////////////////////////////
reg  [aw-1:0] wr_p;	
reg  [aw-1:0] rd_p;
wire [aw-1:0] rd_p_next;

reg	 [aw:0]   i;	
reg           wr_en_dly;	

wire [width-1:0]  ram_dout;
wire              ram_rden;
////////////////////////////////////////////////
//combinational logic
////////////////////////////////////////////////
//assign cnt          = (wr_p >= rd_p) ? wr_p - rd_p : depth-rd_p+wr_p;


assign full         = (i>=(depth-1)) ? 1'b1 : 1'b0;
assign almost_full  = (i >=(depth-4)) ? 1'b1 : 1'b0;
assign prog_full    = (i >= (prog_full_thre-1)) ? 1'b1 :1'b0;

assign empty        = (i==1'b0) ? 1'b1 : 1'b0;
assign almost_empty = (i<=1'b1) ? 1'b1 : 1'b0;
assign prog_empty   = (i<=prog_empty_thre) ? 1'b1 :1'b0;

assign cnt			= i[clog2(depth-1)-1:0];

assign rd_p_next    = rd_p + {{(aw-2){1'b0}}, (rd_en & !empty)}; 
////////////////////////////////////////////////
//timing
////////////////////////////////////////////////
reg wr_clr_r;
reg wr_clr;
//----------------------------------------------
always @(posedge clk or posedge clr)
  if(clr)     
    wr_clr <= 1'b1;
  else if(!wr_clr_r)  
    wr_clr <= 1'b0;// Release Clear

always @(posedge clk or posedge clr)
  if(clr) 
    wr_clr_r <= 1'b1;
  else        
    wr_clr_r <= 1'b0;
//----------------------------------------------
always@(posedge clk or posedge rst)begin
	if(rst )begin
	   wr_en_dly <=1'b0;
	end
	else begin
	   wr_en_dly <=wr_en;
	end
end
	
always@(posedge clk or posedge rst)begin
	if(rst )begin
		wr_p <=0;
	end
	else if(wr_clr)begin
	   wr_p <=0;
	end
	else if(wr_en)begin
		if(wr_p == depth-1)begin
			wr_p <=0;
		end
		else begin
			wr_p <=wr_p+1;
		end
	end
	else begin
		wr_p <=wr_p;
	end
end

//----------------------------------------------
always@(posedge clk or posedge rst)begin
	if(rst )begin
		rd_p <=0;
	end
	else if(wr_clr)begin
	   rd_p <=0;
	end
	else if(rd_en)begin
		if(rd_p == depth-1)begin
			rd_p <=0;
		end
		else begin
			rd_p <=rd_p+1;
		end
	end
	else begin
		rd_p <=rd_p;
	end
end

//----------------------------------------------
always@(posedge clk or posedge rst)begin
	if(rst)begin
		i <=0;
	end
	else if(wr_clr)begin
	   i <=0;
	end
	else begin
		case({wr_en_dly,rd_en})
		2'b00:begin 
			i <=i;
		end
		2'b01:begin
			if(i!=0)begin
				i <=i-1'b1;
			end
			else begin
				i <=i;
			end
		end
		2'b10:begin
			if(i!=depth)begin
				i <=i+1'b1;
			end
			else begin
				i <=i;
			end
		end
		2'b11:begin
			i <=i;		
		end
		default:begin
			i <=i;
		end
		endcase
	end
end
////////////////////////////////////////////////
//SRAM
////////////////////////////////////////////////
spram #(
	.width (width   ),
	.depth (depth   )
)spram(
	.clka	(clk	     ),
	.ena	(wr_en       ),
	.dina	(din         ),
	.addra	(wr_p        ),

	.clkb	(clk	     ),
	//.enb	(1'b1        ),
	.enb	(ram_rden     ),
	.doutb	(ram_dout    ),
	.addrb	(rd_p_next   )
);

assign ram_rden = wr_p != rd_p_next;
assign dout = empty ? 0 : ram_dout;

endmodule


