题解 | #同步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。
`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
错误3:还是不对
查看11道真题和解析