123 lines
4.7 KiB
Systemverilog
123 lines
4.7 KiB
Systemverilog
`default_nettype none
|
|
|
|
// Handwritten dual-clock FIFO. Only Gray pointers cross clock domains. Memory
|
|
// writes occur in w_clk and the read data is registered in r_clk, allowing BRAM
|
|
// inference without a combinational CDC path.
|
|
module async_fifo_gray #(
|
|
parameter int unsigned DATA_WIDTH = 69,
|
|
parameter int unsigned DEPTH = 16384
|
|
) (
|
|
input wire logic w_clk,
|
|
input wire logic w_rst,
|
|
input wire logic w_en,
|
|
input wire logic [DATA_WIDTH-1:0] w_data,
|
|
output logic w_full,
|
|
output logic [$clog2(DEPTH):0] w_level,
|
|
output logic [63:0] overflow_attempt_count,
|
|
|
|
input wire logic r_clk,
|
|
input wire logic r_rst,
|
|
input wire logic r_en,
|
|
output logic [DATA_WIDTH-1:0] r_data,
|
|
output logic r_valid,
|
|
output logic r_empty,
|
|
output logic [$clog2(DEPTH):0] r_level,
|
|
output logic [63:0] underflow_attempt_count
|
|
);
|
|
localparam int unsigned ADDR_WIDTH = $clog2(DEPTH);
|
|
localparam int unsigned PTR_WIDTH = ADDR_WIDTH + 1;
|
|
|
|
(* ram_style = "block" *) logic [DATA_WIDTH-1:0] mem [0:DEPTH-1];
|
|
logic [PTR_WIDTH-1:0] wbin, wgray, rbin, rgray;
|
|
logic [PTR_WIDTH-1:0] wbin_next, wgray_next;
|
|
logic [PTR_WIDTH-1:0] rbin_next, rgray_next;
|
|
(* ASYNC_REG = "TRUE" *) logic [PTR_WIDTH-1:0] rgray_wsync1, rgray_wsync2;
|
|
(* ASYNC_REG = "TRUE" *) logic [PTR_WIDTH-1:0] wgray_rsync1, wgray_rsync2;
|
|
logic [PTR_WIDTH-1:0] rbin_wsync, wbin_rsync;
|
|
logic [DATA_WIDTH-1:0] r_mem_data;
|
|
logic w_push, r_pop;
|
|
integer i;
|
|
|
|
initial begin
|
|
if (DEPTH < 4 || (DEPTH & (DEPTH-1)) != 0)
|
|
$error("async_fifo_gray DEPTH must be a power of two >= 4");
|
|
if (DATA_WIDTH == 0) $error("async_fifo_gray DATA_WIDTH must be nonzero");
|
|
end
|
|
|
|
assign w_push = w_en && !w_full;
|
|
assign r_pop = r_en && !r_empty;
|
|
assign r_data = r_valid ? r_mem_data : {DATA_WIDTH{1'b0}};
|
|
assign wbin_next = wbin + {{(PTR_WIDTH-1){1'b0}}, w_push};
|
|
assign rbin_next = rbin + {{(PTR_WIDTH-1){1'b0}}, r_pop};
|
|
assign wgray_next = (wbin_next >> 1) ^ wbin_next;
|
|
assign rgray_next = (rbin_next >> 1) ^ rbin_next;
|
|
|
|
always_comb begin
|
|
rbin_wsync[PTR_WIDTH-1] = rgray_wsync2[PTR_WIDTH-1];
|
|
wbin_rsync[PTR_WIDTH-1] = wgray_rsync2[PTR_WIDTH-1];
|
|
for (i = PTR_WIDTH-2; i >= 0; i = i - 1) begin
|
|
rbin_wsync[i] = rbin_wsync[i+1] ^ rgray_wsync2[i];
|
|
wbin_rsync[i] = wbin_rsync[i+1] ^ wgray_rsync2[i];
|
|
end
|
|
end
|
|
|
|
always_comb begin
|
|
w_level = wbin - rbin_wsync;
|
|
r_level = wbin_rsync - rbin;
|
|
end
|
|
|
|
always_ff @(posedge w_clk or posedge w_rst) begin
|
|
if (w_rst) begin
|
|
wbin <= '0;
|
|
wgray <= '0;
|
|
w_full <= 1'b0;
|
|
rgray_wsync1 <= '0;
|
|
rgray_wsync2 <= '0;
|
|
overflow_attempt_count <= 64'd0;
|
|
end else begin
|
|
rgray_wsync1 <= rgray;
|
|
rgray_wsync2 <= rgray_wsync1;
|
|
if (w_en && w_full)
|
|
overflow_attempt_count <= overflow_attempt_count + 64'd1;
|
|
wbin <= wbin_next;
|
|
wgray <= wgray_next;
|
|
// Invert the two most-significant synchronized read-pointer bits.
|
|
w_full <= (wgray_next == {~rgray_wsync2[PTR_WIDTH-1:PTR_WIDTH-2],
|
|
rgray_wsync2[PTR_WIDTH-3:0]});
|
|
end
|
|
end
|
|
|
|
always_ff @(posedge r_clk or posedge r_rst) begin
|
|
if (r_rst) begin
|
|
rbin <= '0;
|
|
rgray <= '0;
|
|
r_valid <= 1'b0;
|
|
r_empty <= 1'b1;
|
|
wgray_rsync1 <= '0;
|
|
wgray_rsync2 <= '0;
|
|
underflow_attempt_count <= 64'd0;
|
|
end else begin
|
|
wgray_rsync1 <= wgray;
|
|
wgray_rsync2 <= wgray_rsync1;
|
|
r_valid <= r_pop;
|
|
if (r_en && r_empty)
|
|
underflow_attempt_count <= underflow_attempt_count + 64'd1;
|
|
rbin <= rbin_next;
|
|
rgray <= rgray_next;
|
|
r_empty <= (rgray_next == wgray_rsync2);
|
|
end
|
|
end
|
|
|
|
// Keep memory access out of the asynchronously-reset pointer processes.
|
|
// This is the supported true-dual-clock block-RAM inference template.
|
|
always_ff @(posedge w_clk) begin
|
|
if (w_push) mem[wbin[ADDR_WIDTH-1:0]] <= w_data;
|
|
end
|
|
|
|
always_ff @(posedge r_clk) begin
|
|
if (r_pop) r_mem_data <= mem[rbin[ADDR_WIDTH-1:0]];
|
|
end
|
|
endmodule
|
|
|
|
`default_nettype wire
|