Implement generic MAX2771 raw Ethernet recorder
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
`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
|
||||
Reference in New Issue
Block a user