`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,
parameter DEPTH = 16
)(
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
);
//格雷码必须现在本地打一拍,然后再经过异域时钟大两拍,一般我们不在本地打拍也是可以的的,本例题时序是这样,另外最后空满判定也需要本地时钟打拍后的格雷码与异域时钟的进行判断!
//=========================================================================\\
//*****************************define sinals*******************************\\
//=========================================================================\\
//读写指针
reg [$clog2(DEPTH):0] wr_ptr ; //写指针
wire [$clog2(DEPTH)-1:0] wr_ptr_true ; //写指针-真实地址
reg [$clog2(DEPTH):0] rd_ptr ; //读指针
wire [$clog2(DEPTH)-1:0] rd_ptr_true ; //读指针-真实地址
//格雷码读写指针
wire [$clog2(DEPTH):0] wr_ptr_g ; //格雷码写指针
wire [$clog2(DEPTH):0] rd_ptr_g ; //格雷码写指针
//本时钟域打拍的格雷码读写时钟
reg [$clog2(DEPTH):0] wr_ptr_g_b; //本地打拍的格雷码写指针
reg [$clog2(DEPTH):0] rd_ptr_g_b; //本地打拍的格雷码读指针
//跨时钟域打拍的格雷码读写时钟
reg [$clog2(DEPTH):0] wr_ptr_g_d1;
reg [$clog2(DEPTH):0] wr_ptr_g_d2;
reg [$clog2(DEPTH):0] rd_ptr_g_d1;
reg [$clog2(DEPTH):0] rd_ptr_g_d2;
//=========================================================================\\
//*****************************main code***********************************\\
//=========================================================================\\
//二进制转格雷码
assign wr_ptr_g =(wr_ptr >> 1)^ wr_ptr ;
assign rd_ptr_g =(rd_ptr >> 1)^ rd_ptr ;
//真实指针
assign wr_ptr_true = wr_ptr[$clog2(DEPTH)-1:0];
assign rd_ptr_true = rd_ptr[$clog2(DEPTH)-1:0];
//写操作
always@(posedge wclk or negedge wrstn)begin
if(!wrstn)
wr_ptr <= 'd0 ;
else if(!wfull && winc)
wr_ptr <= wr_ptr + 1'b1 ;
end
//格雷码写指针本域打拍
always@(posedge wclk or negedge wrstn)begin
if(!wrstn)
wr_ptr_g_b <= 'd0 ;
else
wr_ptr_g_b <= wr_ptr_g ;
end
//格雷码写指针同步到读时钟域
always@(posedge rclk or negedge rrstn)begin
if(!rrstn)begin
wr_ptr_g_d1 <= 'd0 ;
wr_ptr_g_d2 <= 'd0 ;
end
else begin
wr_ptr_g_d1 <= wr_ptr_g_b ;
wr_ptr_g_d2 <= wr_ptr_g_d1 ;
end
end
//读操作
always@(posedge rclk or negedge rrstn)begin
if(!rrstn)
rd_ptr <= 'd0 ;
else if(!rempty && rinc)
rd_ptr <= rd_ptr + 1'b1 ;
end
//格雷码读指针本域打拍
always@(posedge rclk or negedge rrstn)begin
if(!rrstn)
rd_ptr_g_b <= 'd0 ;
else
rd_ptr_g_b <= rd_ptr_g ;
end
//格雷码读指针同步到写时钟域
always@(posedge wclk or negedge wrstn)begin
if(!wrstn)begin
rd_ptr_g_d1 <= 'd0 ;
rd_ptr_g_d2 <= 'd0 ;
end
else begin
rd_ptr_g_d1 <= rd_ptr_g_b ;
rd_ptr_g_d2 <= rd_ptr_g_d1 ;
end
end
//写满判断 本地打拍的格雷码写指针 与异域打拍的格雷码读指针最高位和次高位相反 其余为相同
assign wfull = (wr_ptr_g_b =={~rd_ptr_g_d2[$clog2(DEPTH):$clog2(DEPTH)-1],rd_ptr_g_d2[$clog2(DEPTH)-2:0]})?1'b1:1'b0 ;
//读空判断 本地打拍的格雷码读指针 与异域打拍的格雷码写指针相等
assign rempty = (rd_ptr_g_b == wr_ptr_g_d2)?1'b1:1'b0 ;
dual_port_RAM
#(
. DEPTH (DEPTH ) ,
. WIDTH (WIDTH )
)
dual_port_RAM_inst
(
. wclk (wclk ),
. wenc (!wfull && winc ),
. waddr (wr_ptr_true ), //深度对2取对数,得到地址的位宽。
. wdata (wdata ), //数据写入
. rclk (rclk ),
. renc (!rempty && rinc ),
. raddr (rd_ptr_true ), //深度对2取对数,得到地址的位宽。
. rdata (rdata ) //数据输出
);
endmodule