题解 | #同步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)-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