`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
);
    reg[$clog2(DEPTH):0] waddr, raddr;
    reg[$clog2(DEPTH):0] gray_waddr, gray_raddr;
     
/***************二进制地址自增,格雷码转换*****************************************/
    always@(posedge wclk or negedge wrstn)begin
        if(!wrstn)begin
            waddr<=0;
        end
        else
            waddr<=wenc?waddr+1:waddr;
    end
    always@(posedge wclk or negedge wrstn)begin
        if(!wrstn)begin
            gray_waddr<=0;
        end
        else
            gray_waddr<=(waddr>>1)^waddr;
    end    
    
    always@(posedge rclk or negedge rrstn)begin
        if(!rrstn)begin
            raddr<=0;
        end
        else
            raddr<=renc?raddr+1:raddr;
    end
    always@(posedge rclk or negedge rrstn)begin
        if(!rrstn)begin
            gray_raddr<=0;
        end
        else
            gray_raddr<=(raddr>>1)^raddr;
    end    
     
/******************将格雷码地址打两拍同步时钟域*****************************************/
    wire wenc, renc;
    reg[$clog2(DEPTH):0] gray_waddr1, gray_waddr2;
    reg[$clog2(DEPTH):0] gray_raddr1, gray_raddr2;
    always@(posedge rclk or negedge rrstn)begin
        if(~rrstn)begin
            gray_waddr1<=0;
            gray_waddr2<=0;
        end
        else begin
            gray_waddr1<=gray_waddr;
            gray_waddr2<=gray_waddr1;
        end
    end
    
    always@(posedge wclk or negedge wrstn)begin
        if(~wrstn)begin
            gray_raddr1<=0;
            gray_raddr2<=0;
        end
        else begin
            gray_raddr1<=gray_raddr;
            gray_raddr2<=gray_raddr1;
        end
    end   
    
    assign wfull=((gray_raddr2[$clog2(DEPTH)]!=gray_waddr[$clog2(DEPTH)]&&
                  gray_raddr2[$clog2(DEPTH)-1]!=gray_waddr[$clog2(DEPTH)-1]&&
                         gray_raddr2[$clog2(DEPTH)-2:0]==gray_waddr[$clog2(DEPTH)-2:0]));
    assign rempty=(gray_waddr2==gray_raddr);
        
    assign wenc=winc&&!wfull;
    assign renc=rinc&&!rempty;
       
    dual_port_RAM #(DEPTH, WIDTH) dut(
        .wclk(wclk)
        ,.wenc(wenc)
        ,.waddr(waddr[$clog2(DEPTH)-1 : 0])  //深度对2取对数,得到地址的位宽。
        ,.wdata(wdata)          //数据写入
        ,.rclk(rclk)
        ,.renc(renc)
        ,.raddr(raddr[$clog2(DEPTH)-1 : 0])  //深度对2取对数,得到地址的位宽。
        ,.rdata(rdata));
  
    
endmodule