6 跨时钟域传输
VL45 异步FIFO
很经典的手撕题,这道题要求产生的格雷码要在本时钟域中打一拍,其实不打也没关系。
主要要记住
1、bin2gray的方法:右移一位与移位前异或;
2、格雷码比较方法:空:读指针格雷码和写指针同步过来的格雷码相同;满:写指针格雷码高两位与读指针同步过来的格雷码正好相反,低位相同。
`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 ); parameter ADDR_WIDTH = $clog2(DEPTH); reg [ADDR_WIDTH:0]waddr,raddr;//多一位用于比较空满 always@(posedge wclk or negedge wrstn) begin if(~wrstn) waddr <= "d0; else if(winc&&~wfull) waddr <= waddr + 1; end always@(posedge rclk or negedge rrstn) begin if(~rrstn) raddr <= "d0; else if(rinc&&~rempty) raddr <= raddr + 1; end reg [ADDR_WIDTH:0]waddr_gray,raddr_gray;//格雷码 reg [ADDR_WIDTH:0]waddr_gray_sync1,raddr_gray_sync1;//打一拍后 reg [ADDR_WIDTH:0]waddr_gray_sync2,raddr_gray_sync2;//打两拍后 /*bin2gray*/ // always@(*) // begin // waddr_gray = (waddr>>1)^waddr; // raddr_gray = (raddr>>1)^raddr; // end always@(posedge wclk or negedge wrstn) begin if(~wrstn) waddr_gray <= "d0; else waddr_gray <= (waddr>>1)^waddr; end always@(posedge rclk or negedge rrstn) begin if(~rrstn) raddr_gray <= "d0; else raddr_gray <= (raddr>>1)^raddr; end /*把格雷码打两拍传输*/ always@(posedge wclk or negedge wrstn) begin if(~wrstn)begin raddr_gray_sync1 <= "d0; raddr_gray_sync2 <= "d0; end else begin raddr_gray_sync1 <= raddr_gray; raddr_gray_sync2 <= raddr_gray_sync1; end end always@(posedge rclk or negedge rrstn) begin if(~rrstn)begin waddr_gray_sync1 <= "d0; waddr_gray_sync2 <= "d0; end else begin waddr_gray_sync1 <= waddr_gray; waddr_gray_sync2 <= waddr_gray_sync1; end end /*空满判断*/ assign wfull = (waddr_gray[ADDR_WIDTH:ADDR_WIDTH-1]==~raddr_gray_sync2[ADDR_WIDTH:ADDR_WIDTH-1])&&(waddr_gray[ADDR_WIDTH-2:0]==raddr_gray_sync2[ADDR_WIDTH-2:0]); assign rempty = (raddr_gray==waddr_gray_sync2); dual_port_RAM #(.WIDTH(WIDTH),.DEPTH(DEPTH)) U0( .wclk(wclk), .wenc(winc&&~wfull), .waddr(waddr[ADDR_WIDTH-1:0]), .wdata(wdata), .rclk(rclk), .renc(rinc&&~rempty), .raddr(raddr[ADDR_WIDTH-1:0]), .rdata(rdata) ); endmodule