题解 | #同步FIFO#

同步FIFO

http://www.nowcoder.com/practice/3ece2bed6f044ceebd172a7bf5cfb416

正确代码3:使用套圈判断,但是需要考虑的更全面

参考:https://blog.nowcoder.net/n/d5411484475d4c5c84ce711a6d668f7d?f=comment

待完成。

正确代码1:使用套圈判断是否空满;waddr和raddr为$clog2(DEPTH), 地址指针waddr和raddr比实际地址多一位,最高位用来指示套圈情况。

相等时为空,waddr比raddr大DEPTH时为满。cnt模块没用到,也可以通过cnt计数判断空满啊

参考:https://blog.nowcoder.net/n/1e420b4bc0f8426eb4ee0b844f0aca35?f=comment

`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
);
    //计数器,判断空满
    reg [$clog2(DEPTH)-1:0] cnt;
    
    //定义fifo
    //reg [WIDTH -1 : 0] s_fifo [0 : DEPTH-1];
    
    //定义读写指针
    reg [$clog2(DEPTH):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 <= 0;
        end
        else if(winc && (!wfull)) begin
            //dual_port_RAM ram1(.wclk(clk), .wenc(winc), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(1'b0), .raddr(raddr), .rdata(rdata));
            waddr <= waddr + 1;
        end
        else
            waddr <= waddr;
    end

    //读地址模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            raddr <= 0;
           // rdata <= 0;
        end
        else if(rinc && (!rempty)) begin
            //dual_port_RAM ram2(.wclk(clk), .wenc(1'b0), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(rinc), .raddr(raddr), .rdata(rdata));
            raddr <= raddr + 1;
        end
        else
            raddr <= raddr;
    end
    
    //cnt计数模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            cnt <= 0;
        end
        else if(winc && (!wfull) && !rinc) begin
            cnt <= cnt + 1;
        end
        else if(rinc && (!rempty) && !winc)
            cnt <= cnt - 1;
        else
            cnt <= cnt;
    end
    
    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
    
    
endmodule

正确代码2:使用cnt计数判断空满

参考:https://blog.nowcoder.net/n/58228b413e6d43689a50050d94886684?f=comment

地址指针waddr和raddr比实际地址多一位,最高位用来指示套圈情况。当waddr和raddr的最高位相同时,fifo_cnt = waddr-raddr;当waddr和raddr的最高位相反时,fifo_cnt = DEPTH - raddr[ADDR_WIDTH-1:0] + waddr[ADDR_WIDTH-1:0] 。

如何根据fifo_cnt 的值来判断空满呢?对于空,只要fifo_cnt == 0,即为空,对于满,只要fifo_cnt == DEPTH,即为满。注意,为什么不是fifo_cnt == DEPTH-1呢?假设FIFO深度设计为16,DEPTH=16,那么第16个写数据是写在了waddr=15的地址中,但是当写完第16个数据后,即使winc拉低,waddr也会自动加1,停在waddr = 10000,所以相当于写完数据后的写地址比最后一位数据的存储地址,多加了1,所以就不需要再DEPTH-1了。fifo_cnt = 10000 - 0000 + 0000 = DEPTH。

alt

`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 if(winc && (!wfull)) begin
            //dual_port_RAM ram1(.wclk(clk), .wenc(winc), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(1'b0), .raddr(raddr), .rdata(rdata));
            waddr <= waddr + 1'd1;
            //cnt <= cnt + 1;
        end
        else
            waddr <= waddr;
    end

    //读地址模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            raddr <= 'd0;
           // rdata <= 0;
        end
        else if(rinc && (!rempty)) begin
            //dual_port_RAM ram2(.wclk(clk), .wenc(1'b0), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(rinc), .raddr(raddr), .rdata(rdata));
            raddr <= raddr + 1'd1;
            //cnt <= cnt - 1;
        end
        else
            raddr <= 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 if(cnt == DEPTH) begin
            wfull <= 1'b1;
        end
        else if(cnt == 'd0) begin
            rempty <= 1'b1;
        end
        else begin
            wfull <= 1'b0;
            rempty <= 1'b0;
        end
    end
    
endmodule

错误代码1:使用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
);
    //计数器,判断空满
    reg [$clog2(DEPTH):0] cnt;
    
    //定义fifo
    //reg [WIDTH -1 : 0] s_fifo [0 : DEPTH-1];
    
    //定义读写指针
    reg [$clog2(DEPTH):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 <= 0;
        end
        else if(winc && (!wfull)) begin
            //dual_port_RAM ram1(.wclk(clk), .wenc(winc), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(1'b0), .raddr(raddr), .rdata(rdata));
            waddr <= waddr + 1;
            cnt <= cnt + 1;
        end
        else
            waddr <= waddr;
    end

    //读地址模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            raddr <= 0;
           // rdata <= 0;
        end
        else if(rinc && (!rempty)) begin
            //dual_port_RAM ram2(.wclk(clk), .wenc(1'b0), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(rinc), .raddr(raddr), .rdata(rdata));
            raddr <= raddr + 1;
            cnt <= cnt - 1;
        end
        else
            raddr <= raddr;
    end
    
//     //cnt计数模块
//     always @ (posedge clk, negedge rst_n) begin
//         if(!rst_n) begin
//             cnt <= 0;
//         end
//         else if(winc && (!wfull) && !rinc) begin
//             cnt <= cnt + 1;
//         end
//         else if(rinc && (!rempty) && !winc)
//             cnt <= cnt - 1;
//         else
//             cnt <= cnt;
//     end
    
//     //通过地址判断空满。
//     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 <= 0;
            rempty <= 0;
        end
        else begin
            wfull <= cnt == DEPTH-1;
            rempty <= cnt == 0;
        end
        
    end
    
endmodule

1 很明显的错误,在always中例化模块,属于基础不牢固了。

`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
);
    //计数器,判断空满
    reg [$clog2(DEPTH)-1:0] cnt;
    
    //定义fifo
    reg [WIDTH -1 : 0] s_fifo [0 : DEPTH-1];
    
    //定义读写指针
    reg [$clog2(DEPTH)-1:0] waddr, raddr;
    
    //写数据模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            waddr <= 0;
        end
        else if(winc && (!wfull)) begin
            dual_port_RAM ram1(.wclk(clk), .wenc(winc), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(1'b0), .raddr(raddr), .rdata(rdata));
            waddr <= waddr + 1;
        end
        else
            waddr <= waddr;
    end

    //读数据模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            raddr <= 0;
            rdata <= 0;
        end
        else if(rinc && (!rempty)) begin
            dual_port_RAM ram2(.wclk(clk), .wenc(1'b0), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(rinc), .raddr(raddr), .rdata(rdata));
            raddr <= raddr + 1;
        end
        else
            raddr <= raddr;
    end
    
    //cnt计数模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            cnt <= 0;
        end
        else if(winc && (!wfull)) begin
            cnt <= cnt + 1;
        end
        else if((winc && (!rempty))
            cnt <= cnt - 1;
        else
            cnt <= cnt;
    end
    always (*) begin
        wfull = cnt == (DEPTH - 1);
        rempty = cnt == 0;
    end
    
    
endmodule

2 直接例化一个ram就可以了,时序不对,没通过,地址递增模块没有啊

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

//     //读数据模块
//     always @ (posedge clk, negedge rst_n) begin
//         if(!rst_n) begin
//             raddr <= 0;
//             rdata <= 0;
//         end
//         else if(rinc && (!rempty)) begin
//             dual_port_RAM ram2(.wclk(clk), .wenc(1'b0), .waddr(waddr), .wdata(wdata), .rclk(clk), .renc(rinc), .raddr(raddr), .rdata(rdata));
//             raddr <= raddr + 1;
//         end
//         else
//             raddr <= raddr;
//     end
    
    //cnt计数模块
    always @ (posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            cnt <= 0;
        end
        else if(winc && (!wfull)) begin
            cnt <= cnt + 1;
        end
        else if(winc && (!rempty))
            cnt <= cnt - 1;
        else
            cnt <= cnt;
    end
    
    always @ (*) begin
        wfull <= (cnt == (DEPTH - 1));
        rempty <= (cnt == 0);
    end
    
    
endmodule

alt

错误3:还是不对

全部评论
正确代码1 只是牛客的正确答案。其实是有问题的,主要是empty, full 都晚了一拍,会导致数据被覆盖。(具体可看你的正确代码3)
点赞 回复 分享
发布于 2022-08-20 12:09 广东
写的几种方法太乱了,整理一下。
点赞 回复 分享
发布于 2022-07-28 17:49

相关推荐

GoldenPota...:能做这个方向的人200一天是吧🤗
找AI工作可以去哪些公司...
点赞 评论 收藏
分享
03-26 13:04
已编辑
电子科技大学 算法工程师
xiaowl:你这个简历“条目上”都比较有深度性,但是实际上面试官又没法很好的评估你是怎么达到很多看上去很厉害的结果的。要避免一些看上去很厉害的包装,比如高效的内存复用策略的表达,如果仅是简单的一些内存共享机制,而且面试上也没有深挖的空间,就不要这样表达。比如,工程化模式本质上可能就是定义了一些abstract class,那也就没特别多值得讲的内容。建议简历上应该侧重那些你花了大量时间和精力解决、研究的问题,不要过分追求“丰富”,而是关注在技术深入度、问题解决能力的表现上。
没有实习经历,还有机会进...
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

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