数字IC手撕代码(八)

这次打算写异步FIFO和同步FIFO吧(未经仿真)
异步FIFO设计中四个部分:
(1)Memory,存储数据
(2)写逻辑,主要产生写信号和写地址
(3)读逻辑,主要产生读信号和读地址
(4)跨时钟域地址比较,产生FIFO的空、满标志

技术关键点:
(1)读写地址信号跨时钟域同步
(2)读写地址的比较
(3)将空和将满的产生

数字IC手撕代码(八)_第1张图片
数字IC手撕代码(八)_第2张图片
数字IC手撕代码(八)_第3张图片

module afifo #(
	parameter DATASIZE = 8,
	parameter ADDRSIZE = 4,
	parameter ALMOST_GAP = 3
)(
	input [DATASIZE-1:0] wdata,
	input winc,
	input wclk,
	input wrst_n,
	
	input rclk,
	input rinc,
	input rrst_n,
	
	output reg wfull,
	output reg rempty,
	output wire [DATASIZE-1:0] rdata,
	output reg almost_full,
	output reg almost_empty,
);

localparam DEPTH = 1 << ADDRSIZE;
reg [DATASIZE-1:0] mem [0:DEPTH-1];

wire [ADDRSIZE-1:0] waddr;
wire [ADDRSIZE-1:0] raddr;
reg [ADDRSIZE:0] wptr;
reg [ADDRSIZE:0] rptr;
wire rempty_val;
wire remoty_val;



assign rdata = mem[raddr];
always @ (posedge wclk) begin
	if(winc && !wfull) begin
		mem[waddr] <= wdata;
	end
end
//----------------------------------
//将读指针(格雷码)同步到写时钟域
reg [ADDRSIZE:0] w1_rptr;
reg [ADDRSIZE:0] w2_rptr;

always @ (posedge wclk or wrst_n) begin
	if(!wrst_n) begin
		{w2_rptr,w1_rptr} <= 0;
	end
	else begin
		{w2_rptr,w1_rptr} <= {w1_rptr,rptr};
	end
end

//将写指针(格雷码)同步到读时钟域
reg [ADDRSIZE:0] r1_wptr;
reg [ADDRSIZE:0] r2_wptr;

always @ (posedge rclk or negedge rrst_n) begin
	if(!rrst_n) begin
		{r2_wptr,r1_wptr} <= 0;
	end
	else begin
		{r2_wptr,r1_wptr} <= {r1_wptr,wptr};
	end
end

//------------------------------
reg [ADDRSIZE:0] rbin;
wire [ADDRSIZE:0] rgraynext;
wire [ADDRSIZE:0] rbinnext;
//相当于两个always块写一块,分别是格雷码和指针
//读指针自加
always @ (posedge rclk or negedge rrst_n) begin
	if(!rrst_n) begin
		rbin <= 0;
	end
	else begin
		rbin <= rbinnext;
	end
end

assign rbinnext = rbin + (rinc & (~empty));
//读地址
assign raddr = rbin[ADDRSIZE-1:0];


//二进制同步成格雷码
always @ (posedge rclk or negedge rrst_n) begin
	if(!rrst_n) begin
		rptr <= 0;
	end
	else begin
		rptr <= rgraynext;
	end
end

assign rgraynext = (rbinnext>>1) ^ rbinnext;

-------------------------------
//产生空信号
assign rempty_val = (rgraynext==r2_wptr);
always @ (posedge rclk or negedge rrst_n) begin
	if(!rrst_n) begin
		rempty <= 1'b1;
	end
	else begin
		rempty <= rempty_val;
	end
end
//-------------------------------
//写时钟域写求写满
reg [ADDRSIZE:0] wbin;
wire [ADDRSIZE:0] wgraynext;
wire [ADDRSIZE:0] wbinnext;
//写指针
always @ (posedge wclk or negedge wrst_n) begin
	if(!wrst_n) begin
		wbin <= 0;
	end
	else begin
		wbin <= wbinnext;
	end
end
assign wbinnext = wbin + (winc & (~wfull));
//产生写地址
assign waddr = wbin[ADDRSIZE-1:0];
//产生写指针格雷码
always @ (posedge wclk or negedge wrst_n) begin
	if(!wrst_n) begin
		wptr <= 0;
	end
	else begin
		wptr <= wgraynext;
	end
end

assign wgraynext = (wbinnext>>1) ^ wbinnext;

//产生满信号,判断满信号产生的条件,最高读指针两位和写指针最高两位相反,其余相同
wire [ADDRSIZE:0] full_flag;
assign full_flag = (~w2_rptr[ADDRSIZE:ADDRSIZE-1],w2_rptr[ADDRSIZE-2:0]);
assign wfull_val = (wgraynext==full_flag);
always @ (posedge wclk or negedge wrst_n) begin
	if(!wrst_n) begin
		wfull <= 1'b0;
	end
	else begin
		wfull <= wfull_val;
	end
end

//-----------------------------
//almost full 和 almost empty信号的产生
//得把格雷码再转换成二进制进行比较产生将满将空信号
wire [ADDRSIZE:0] r2_wptr_bin;
wire [ADDRSIZE:0] w2_rptr_bin;
wire almost_empty_val;
wire almost_full_val;
 
assign r2_wptr_bin[ADDRSIZE] = r2_wptr[ADDRSIZE];
assign w2_rptr_bin[ADDRSIZE] = w2_rptr[ADDRSIZE];

genvar i;
generate 
	for(i=ADDRSIZE-1;i>=0;i=i-1) begin:wrgary2bin
		assign r2_wptr_bin[i] = r2_wptr_bin[i+1]^r2_wptr[i];
		assign w2_rptr_bin[i] = w2_rptr_bin[i+1]^w2_rptr[i];
	end
endgenerate
//读快空
wire [ADDRSIZE:0] rgap_reg;
assign rgap_req = rw_wptr_bin - rbin;
assign almost_empty_val = (rgap_reg <= ALMOST_GAP);

always @ (posedge rclk or negedge rrst_n) begin
	if(!rrst_n) begin
		almost_empty <= 1'b1;
	end
	else begin
		almost_empty <= almost_empty_val;
	end
end

//写快满
wire [ADDRSIZE:0] wgap_reg;
assign wgap_reg = (wbin[ADDRSIZE]^w2_rptr_bin[ADDRSIZE]) ? w2_rptr_bin[ADDRSIZE-1:0] - wbin[ADDRSIZE-1:0]:DEPTH+wq2_rptr_bin - wbin;
assign almost_full_val = (wgap_reg <= ALMOST_GAP);

always @(posedge wclk or negedge wrst_n) begin
	if (!wrst_n) begin
		almost_full <= 1'b0;
	end
	else begin
		almost_full <= almost_full_val;
	end
end
endmodule
	

同步FIFO

module afifo(
	input [7:0] din,
	input clk,
	input rst_n,
	input rden,
	input wren,
	
	output [7:0] dout,
	output wfull,
	output rempty
);

reg full_in;
reg empty_in;

reg [7:0] mem [15:0];
reg [3:0] rptr;
reg [3:0] wptr;

assign wfull = full_in;
assign rempty = empty_in;
//读数据
assign dout = mem[rptr];
//写数据
always @ (posedge clk) begin
	if(wren && ~full_in) begin
		mem[wptr] <= din;
	end
end

//产生写指针
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		wptr <= 4'd0;
	end
	else if(wren && ~full_in) begin
		wptr <= wptr + 1'b1;
	end
end
//产生读指针
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		rptr <= 4'd0;
	end
	else if(rden && ~empty_in) begin
		rptr <= rptr + 1'b1;
	end
end

//产生满信号
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		full_in <= 1'b0;
	end
	else if((~rden&&wren)&&((wptr==rptr-1)|| (rptr==4'd0&&4'd15))) begin
		full_in <= 1'b1;
	end
	else if(full_in && rden) begin
		full_in <= 1'b0;
	end
end


//产生空信号
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) begin
		epmty_in <= 1'b1;
	end
	else if((rden&&(~wren)) && (rptr==wptr-1 || (rptr==4'd15 && wptr==4'd0))) begin
		empty_in <= 1'b1;
	end
	else if(empty_in && wr) begin
		empty_in <= 1'b0;
	end
end

endmodule


你可能感兴趣的:(数字IC手撕代码(八))