234 lines
10 KiB
Systemverilog
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
|