题解 | #同步FIFO#

同步FIFO

http://www.nowcoder.com/practice/e5e86054a0ce4355b9dfc08238f25f5f

2022-05-05:发现同步FIFO判断空满要考虑的情况很多,还是有点复杂的,通过计数器cnt计数最为简单。

`timescale 1ns/1ns
/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    parameter addr_w  = $clog2(DEPTH);
    //计数器,判断空满
    reg [addr_w:0] cnt;
    
    //定义fifo
    //reg [WIDTH -1 : 0] s_fifo [0 : DEPTH-1];
    
    //定义读写指针
    reg [addr_w:0] waddr, raddr;
    wire wenc, renc;
    assign wenc = winc && (!wfull);
    assign renc = rinc && (!rempty);
    
    //读写模块直接例化一个ram就可以了啊。
    dual_port_RAM ram1(.wclk(clk), .wenc(winc && (!wfull)), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(rinc && (!rempty)), .raddr(raddr), .rdata(rdata));
    
    
    //写地址模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            waddr <= 'd0;
        end
        else 
            waddr <= wenc ? waddr + 1 : waddr;
    end
            

    //读地址模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            raddr <= 'd0;
           // rdata <= 0;
        end
        else 
            raddr <= renc ? raddr + 1 : raddr;            
    end
    
    //cnt计数模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            cnt <= 0;
        end
        else if(wenc && !rinc) begin
            cnt <= cnt + 1;
        end
        else if(renc && !winc)
            cnt <= cnt - 1;
        else
            cnt <= cnt;
    end

    //assign cnt = (waddr[addr_w]==raddr[addr_w]) ? (waddr[addr_w-1:0] - raddr[addr_w-1:0]) : ( DEPTH + waddr[addr_w-1:0] - raddr[addr_w-1:0]);
    

    //通过cnt判断空满。
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            wfull <= 1'b0;
            rempty <= 1'b0;
        end
        else begin
            wfull <= cnt == DEPTH ? 1'b1 : 1'b0;
            rempty <= cnt == 'd0 ? 1'b1 : 1'b0;
        end

    end
    
endmodule

2022-05-04 解法1 通过地址套圈完成:

`timescale 1ns/1ns

/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  
	,input [WIDTH-1:0] wdata      	
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  
	,output reg [WIDTH-1:0] rdata 		
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    reg [$clog2(DEPTH):0] waddr, raddr;
    wire wenc, renc;
    assign wenc = winc &&(!wfull);
    assign renc = rinc && (!rempty);
    
    dual_port_RAM ram1(.wclk(clk), .wenc(wenc), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(renc), .raddr(raddr), .rdata(rdata));
    
    //write addr
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n)
            waddr <= 0;
        else
            waddr <= wenc ? waddr + 1 : waddr;

    end

    //read addr
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n)
            raddr <= 0;
        else 
            raddr <= renc ? raddr + 1 : raddr;
    end
    
    //full or empty
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            wfull <= 1'b0;
            rempty <= 1'b0;
        end
        else begin
            wfull <= waddr == raddr + DEPTH;
            rempty <= waddr == raddr;
        end
        
    end
    
endmodule

解法2:通过cnt,最高位不同,则计数增加一个FIFO的深度,全面简化代码。

`timescale 1ns/1ns
/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    parameter addr_w  = $clog2(DEPTH);
    //计数器,判断空满
    wire [addr_w:0] cnt;
    
    //定义fifo
    //reg [WIDTH -1 : 0] s_fifo [0 : DEPTH-1];
    
    //定义读写指针
    reg [addr_w:0] waddr, raddr;
    wire wenc, renc;
    assign wenc = winc && (!wfull);
    assign renc = rinc && (!rempty);
    
    //读写模块直接例化一个ram就可以了啊。
    dual_port_RAM ram1(.wclk(clk), .wenc(winc && (!wfull)), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(rinc && (!rempty)), .raddr(raddr), .rdata(rdata));
    
    
    //写地址模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            waddr <= 'd0;
        end
        else 
            waddr <= wenc ? waddr + 1 : waddr;
    end
            

    //读地址模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            raddr <= 'd0;
           // rdata <= 0;
        end
        else 
            raddr <= renc ? raddr + 1 : raddr;            
    end
    
    //cnt计数模块
//     always @ (posedge clk, negedge rst_n) begin
//         if(!rst_n) begin
//             cnt <= 0;
//         end
//         else if(wenc && !renc) begin
//             cnt <= cnt + 1;
//         end
//         else if(renc && !wenc)
//             cnt <= cnt - 1;
//         else
//             cnt <= cnt;
//     end
    assign cnt = (waddr[addr_w]==raddr[addr_w]) ? (waddr[addr_w-1:0] - raddr[addr_w-1:0]) : ( DEPTH + waddr[addr_w-1:0] - raddr[addr_w-1:0]);
    
    
    
//     //通过地址判断空满。
//     always @ (posedge clk, negedge rst_n) begin
//         if(!rst_n) begin
//             wfull <= 0;
//             rempty <= 0;
//         end
//         else begin
//             wfull <= waddr == raddr + DEPTH;
//             rempty <= waddr == raddr;
//         end
        
//     end

    //通过cnt判断空满。
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            wfull <= 1'b0;
            rempty <= 1'b0;
        end
        else begin
            wfull <= cnt == DEPTH ? 1'b1 : 1'b0;
            rempty <= cnt == 'd0 ? 1'b1 : 1'b0;
        end

    end
    
endmodule


双端口ram已经提供读写数据模块了,只需要设置偶读写地址模块就可以了。

发现个问题,是不是reg [clog2(DEPTH):0]waddr,raddr;ram,input[clog2(DEPTH):0] waddr, raddr;比ram中的 ,input [clog2(DEPTH)-1:0] waddr
大一位啊,怎么说?,应该是截取位宽了。

注意判断空满

`timescale 1ns/1ns

/**********************************RAM************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  
	,input [WIDTH-1:0] wdata      	
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  
	,output reg [WIDTH-1:0] rdata 		
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/**********************************SFIFO************************************/
module sfifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					clk		, 
	input 					rst_n	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output reg				wfull	,
	output reg				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    reg [$clog2(DEPTH):0] waddr, raddr;
    wire wenc, renc;
    assign wenc = winc &&(!wfull);
    assign renc = rinc && (!rempty);
    
    dual_port_RAM ram1(.wclk(clk), .wenc(wenc), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(renc), .raddr(raddr), .rdata(rdata));
    //write addr
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n)
            waddr <= 0;
        else if (wenc)
            waddr <= waddr + 1;
        else
            waddr <= waddr;
    end

    //read addr
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n)
            raddr <= 0;
        else if (renc)
            raddr <= raddr + 1;
        else
            raddr <= raddr;
    end
    
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            wfull <= 1'b0;
            rempty <= 1'b0;
        end
        else begin
            wfull <= waddr == raddr + DEPTH;
            rempty <= waddr == raddr;
        end
        
    end
    
    
endmodule
全部评论

相关推荐

评论
4
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务