Files
max2771-raw-ethernet-recorder/rtl/common/ethernet_packetizer.sv
T

234 lines
10 KiB
Systemverilog

`default_nettype none
module ethernet_packetizer #(
parameter int unsigned FIFO_DEPTH = 16384,
parameter int unsigned FULL_SAMPLES = 2936,
parameter bit EARLIER_SAMPLE_HIGH_NIBBLE = 1'b1
) (
input wire logic clk,
input wire logic rst,
input wire logic link_ready,
input wire logic [31:0] dropped_samples_low,
input wire logic fifo_empty,
input wire logic [$clog2(FIFO_DEPTH):0] fifo_level,
output logic fifo_rd_en,
input wire logic [68:0] fifo_rd_data,
input wire logic fifo_rd_valid,
output logic mac_frame_start,
output logic [10:0] mac_payload_length,
output logic [7:0] mac_payload_data,
output logic mac_payload_valid,
input wire logic mac_payload_ready,
input wire logic mac_busy,
input wire logic mac_frame_done,
output logic [31:0] packet_sequence,
output logic frame_activity_pulse
);
localparam int unsigned MAX_DATA_BYTES = (FULL_SAMPLES + 1) / 2;
localparam int unsigned SAMPLE_W = $clog2(FULL_SAMPLES + 1);
localparam logic [SAMPLE_W-1:0] FULL_SAMPLES_W = FULL_SAMPLES;
typedef enum logic [2:0] {P_IDLE, P_LOAD, P_READY, P_STREAM, P_WAIT_DONE} pstate_t;
pstate_t state;
(* ram_style = "distributed" *) logic [7:0] sample_bytes [0:MAX_DATA_BYTES-1];
logic [SAMPLE_W-1:0] load_sample_count;
logic [15:0] frame_sample_count;
logic [63:0] frame_first_index;
logic [63:0] expected_index;
logic [31:0] frame_drop_snapshot;
logic [31:0] last_reported_drop;
logic [7:0] frame_flags;
logic [10:0] stream_position;
logic waiting_read;
logic hold_valid;
logic [68:0] hold_entry;
logic recovery_in_frame;
logic link_d;
logic link_packet_pending;
wire logic current_valid = hold_valid || fifo_rd_valid;
wire logic [68:0] current_entry = hold_valid ? hold_entry : fifo_rd_data;
wire logic current_discontinuity = current_entry[68];
wire logic [63:0] current_index = current_entry[67:4];
wire logic [3:0] current_sample = current_entry[3:0];
wire logic [SAMPLE_W-1:0] samples_with_current = load_sample_count + {{(SAMPLE_W-1){1'b0}},1'b1};
initial begin
if (FULL_SAMPLES < 2 || FULL_SAMPLES > 2936)
$error("FULL_SAMPLES must be in the range 2..2936");
if ((FULL_SAMPLES & 1) != 0)
$error("Normal FULL_SAMPLES must be even to preserve byte alignment");
end
function automatic logic [7:0] header_byte(input logic [5:0] offset);
begin
case (offset)
6'd0: header_byte = 8'h4d; // M
6'd1: header_byte = 8'h58; // X
6'd2: header_byte = 8'h32; // 2
6'd3: header_byte = 8'h37; // 7
6'd4: header_byte = 8'd1;
6'd5: header_byte = frame_flags;
6'd6: header_byte = 8'h00;
6'd7: header_byte = 8'h20;
6'd8: header_byte = packet_sequence[31:24];
6'd9: header_byte = packet_sequence[23:16];
6'd10: header_byte = packet_sequence[15:8];
6'd11: header_byte = packet_sequence[7:0];
6'd12: header_byte = frame_first_index[63:56];
6'd13: header_byte = frame_first_index[55:48];
6'd14: header_byte = frame_first_index[47:40];
6'd15: header_byte = frame_first_index[39:32];
6'd16: header_byte = frame_first_index[31:24];
6'd17: header_byte = frame_first_index[23:16];
6'd18: header_byte = frame_first_index[15:8];
6'd19: header_byte = frame_first_index[7:0];
6'd20: header_byte = frame_sample_count[15:8];
6'd21: header_byte = frame_sample_count[7:0];
6'd22: header_byte = 8'd4;
6'd23: header_byte = EARLIER_SAMPLE_HIGH_NIBBLE ? 8'd1 : 8'd2;
6'd24: header_byte = frame_drop_snapshot[31:24];
6'd25: header_byte = frame_drop_snapshot[23:16];
6'd26: header_byte = frame_drop_snapshot[15:8];
6'd27: header_byte = frame_drop_snapshot[7:0];
default: header_byte = 8'h00;
endcase
end
endfunction
always_comb begin
mac_payload_valid = (state == P_STREAM);
if (stream_position < 11'd32)
mac_payload_data = header_byte(stream_position[5:0]);
else
mac_payload_data = sample_bytes[stream_position - 11'd32];
end
always_ff @(posedge clk) begin
if (rst) begin
state <= P_IDLE;
fifo_rd_en <= 1'b0;
mac_frame_start <= 1'b0;
mac_payload_length <= 11'd0;
packet_sequence <= 32'd0;
frame_activity_pulse <= 1'b0;
load_sample_count <= '0;
frame_sample_count <= '0;
frame_first_index <= 64'd0;
expected_index <= 64'd0;
frame_drop_snapshot <= 32'd0;
last_reported_drop <= 32'd0;
frame_flags <= 8'd0;
stream_position <= 11'd0;
waiting_read <= 1'b0;
hold_valid <= 1'b0;
hold_entry <= 69'd0;
recovery_in_frame <= 1'b0;
link_d <= 1'b0;
link_packet_pending <= 1'b1;
end else begin
fifo_rd_en <= 1'b0;
mac_frame_start <= 1'b0;
frame_activity_pulse <= 1'b0;
link_d <= link_ready;
if (link_ready && !link_d) link_packet_pending <= 1'b1;
case (state)
P_IDLE: begin
waiting_read <= 1'b0;
load_sample_count <= '0;
recovery_in_frame <= 1'b0;
// A held boundary sample counts toward the next full frame.
if (link_ready && ((hold_valid && fifo_level >= FULL_SAMPLES-1) ||
(!hold_valid && fifo_level >= FULL_SAMPLES))) begin
state <= P_LOAD;
end
end
P_LOAD: begin
if (current_valid) begin
waiting_read <= 1'b0;
if (hold_valid) hold_valid <= 1'b0;
if ((load_sample_count != 0) &&
(current_discontinuity || current_index != expected_index)) begin
// Preserve the first post-gap sample for the next frame.
hold_entry <= {1'b0, current_entry[67:0]};
hold_valid <= 1'b1;
frame_sample_count <= {{(16-SAMPLE_W){1'b0}}, load_sample_count};
mac_payload_length <= 11'd32 +
((load_sample_count + {{(SAMPLE_W-1){1'b0}},1'b1}) >> 1);
recovery_in_frame <= 1'b1;
state <= P_READY;
end else begin
if (load_sample_count == 0) begin
frame_first_index <= current_index;
recovery_in_frame <= current_discontinuity;
end
expected_index <= current_index + 64'd1;
if (!load_sample_count[0]) begin
if (EARLIER_SAMPLE_HIGH_NIBBLE)
sample_bytes[load_sample_count >> 1] <= {current_sample, 4'h0};
else
sample_bytes[load_sample_count >> 1] <= {4'h0, current_sample};
end else begin
if (EARLIER_SAMPLE_HIGH_NIBBLE)
sample_bytes[load_sample_count >> 1][3:0] <= current_sample;
else
sample_bytes[load_sample_count >> 1][7:4] <= current_sample;
end
load_sample_count <= samples_with_current;
if (samples_with_current == FULL_SAMPLES_W) begin
frame_sample_count <= {{(16-SAMPLE_W){1'b0}}, samples_with_current};
mac_payload_length <= 11'd32 + (FULL_SAMPLES / 2);
state <= P_READY;
end
end
end else if (!waiting_read && !fifo_empty) begin
fifo_rd_en <= 1'b1;
waiting_read <= 1'b1;
end
end
P_READY: begin
if (link_ready && !mac_busy) begin
frame_drop_snapshot <= dropped_samples_low;
frame_flags <= {5'd0,
link_packet_pending,
(packet_sequence == 32'd0),
(recovery_in_frame || dropped_samples_low != last_reported_drop)};
last_reported_drop <= dropped_samples_low;
link_packet_pending <= 1'b0;
stream_position <= 11'd0;
mac_frame_start <= 1'b1;
frame_activity_pulse <= 1'b1;
state <= P_STREAM;
end
end
P_STREAM: begin
if (mac_payload_ready && mac_payload_valid) begin
if (stream_position + 11'd1 == mac_payload_length)
state <= P_WAIT_DONE;
else
stream_position <= stream_position + 11'd1;
end
end
P_WAIT_DONE: begin
if (mac_frame_done) begin
packet_sequence <= packet_sequence + 32'd1;
state <= P_IDLE;
end
end
default: state <= P_IDLE;
endcase
end
end
endmodule
`default_nettype wire