请根据题目中给出的双口RAM代码和接口描述,实现异步FIFO,要求FIFO位宽和深度参数化可配置。
电路的接口如下图所示。
双口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
题解:
`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
/***************************************AFIFO*****************************************/
module asyn_fifo#(
parameter WIDTH = 8, //数据位宽8
parameter DEPTH = 16 //地址位宽为4,即[3:0],深度为8
)(
input wclk ,
input rclk ,
input wrstn ,
input rrstn ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output wire wfull ,
output wire rempty ,
output wire [WIDTH-1:0] rdata
);
//格雷码计数 0000 0001 0011 0010 0110 0111 0101 0100 // 1100 1101 1111 1110 1010 1011 1001 1000
//当读写指针相等时,fifo空;当写指针比读指针多循环一周时(读写指针最高位和次高位都相反,其余位同),fifo满
/* 读写地址 */
reg [$clog2(DEPTH):0] waddr_fifo;//注意位宽比地址位宽多一位(以便进行空满判断)
reg [$clog2(DEPTH):0] raddr_fifo;
always@(posedge wclk or negedge wrstn)begin
if(!wrstn)
waddr_fifo <= 0 ;
else if(!wfull & winc)
waddr_fifo<=waddr_fifo+1;
end
always@(posedge rclk or negedge rrstn)begin
if(!rrstn)
raddr_fifo <= 0 ;
else if(!rempty & rinc)
raddr_fifo<=raddr_fifo+1;
end
/* 地址转换为指针后 转换为格雷码指针 */
wire [$clog2(DEPTH) : 0] waddr_gray;
wire [$clog2(DEPTH) : 0] raddr_gray;
reg [$clog2(DEPTH) : 0] wptr;
reg [$clog2(DEPTH) : 0] rptr;
assign waddr_gray = waddr_fifo ^ (waddr_fifo>>1);
assign raddr_gray = raddr_fifo ^ (raddr_fifo>>1);
always @(posedge wclk or negedge wrstn) begin
if(~wrstn)
wptr <= 0;
else
wptr <= waddr_gray;
end
always @(posedge rclk or negedge rrstn) begin
if(~rrstn)
rptr <= 0;
else
rptr <= raddr_gray;
end
/* 指针打两拍同步,注意是跨时钟 */
reg [$clog2(DEPTH):0] wptr_buff;
reg [$clog2(DEPTH):0] wptr_syn;
reg [$clog2(DEPTH):0] rptr_buff;
reg [$clog2(DEPTH):0] rptr_syn;
always @(posedge wclk or negedge wrstn) begin
if(~wrstn) begin
rptr_buff <= 0 ;
rptr_syn <= 0 ;
end
else begin
rptr_buff <= rptr ;
rptr_syn <= rptr_buff ;
end
end
always @(posedge rclk or negedge rrstn) begin
if(~rrstn) begin
wptr_buff <= 0 ;
wptr_syn <= 0 ;
end
else begin
wptr_buff <= wptr ;
wptr_syn <= wptr_buff ;
end
end
/* 产生空满信号(相同时钟下信号的比较) */
assign wfull = (wptr == {~rptr_syn[$clog2(DEPTH):$clog2(DEPTH)-1],rptr_syn[$clog2(DEPTH)-2:0]});//最高位和次高位都相反
assign rempty = (rptr == wptr_syn);
/* 例化RAM */
//wire [$clog2(DEPTH)-1:0] waddr;
//wire [$clog2(DEPTH)-1:0] raddr;
//wire wenc;
//wire renc;
//assign waddr = waddr_fifo[$clog2(DEPTH)-1:0] ;
//assign raddr = raddr_fifo[$clog2(DEPTH)-1:0] ;
//assign wenc = !wfull & winc ;
//assign renc = !rempty & rinc ;
dual_port_RAM #(.DEPTH(DEPTH),
.WIDTH(WIDTH)
)dual_port_RAM_inst(
.wclk (wclk),
.wenc (!wfull & winc),
.waddr (waddr_fifo[$clog2(DEPTH)-1:0]),
.wdata (wdata),
.rclk (rclk),
.renc (!rempty & rinc),
.raddr (raddr_fifo[$clog2(DEPTH)-1:0]),
.rdata (rdata)
);
endmodule