200 lines
7.8 KiB
Systemverilog
200 lines
7.8 KiB
Systemverilog
`default_nettype none
|
|
|
|
module ethernet_tx_mac #(
|
|
parameter logic [47:0] DEST_MAC = 48'hffffffffffff,
|
|
parameter logic [47:0] SOURCE_MAC = 48'h022771000001,
|
|
parameter logic [15:0] ETHERTYPE = 16'h88b5
|
|
) (
|
|
input wire logic clk,
|
|
input wire logic rst,
|
|
input wire logic frame_start,
|
|
input wire logic [10:0] payload_length,
|
|
input wire logic [7:0] payload_data,
|
|
input wire logic payload_valid,
|
|
output logic payload_ready,
|
|
output logic [7:0] gmii_txd,
|
|
output logic gmii_tx_en,
|
|
output logic gmii_tx_er,
|
|
output logic busy,
|
|
output logic frame_done,
|
|
output logic [63:0] frames_transmitted,
|
|
output logic [63:0] payload_bytes_transmitted
|
|
);
|
|
typedef enum logic [3:0] {
|
|
S_IDLE, S_PREAMBLE, S_SFD, S_DEST, S_SOURCE, S_TYPE,
|
|
S_PAYLOAD, S_PAD, S_FCS, S_IFG
|
|
} state_t;
|
|
state_t state;
|
|
logic [3:0] byte_count;
|
|
logic [10:0] payload_len_q;
|
|
logic [10:0] payload_count;
|
|
logic [5:0] pad_remaining;
|
|
logic [3:0] ifg_count;
|
|
logic [31:0] crc_q;
|
|
logic [31:0] crc_next;
|
|
logic [31:0] fcs_q;
|
|
logic [15:0] requested_body_length;
|
|
|
|
ethernet_crc32 u_crc_next (
|
|
.crc_in(crc_q), .data_in(gmii_txd), .crc_out(crc_next)
|
|
);
|
|
|
|
always_comb begin
|
|
gmii_txd = 8'h00;
|
|
gmii_tx_en = 1'b0;
|
|
gmii_tx_er = 1'b0;
|
|
payload_ready = 1'b0;
|
|
busy = (state != S_IDLE);
|
|
|
|
case (state)
|
|
S_PREAMBLE: begin gmii_tx_en = 1'b1; gmii_txd = 8'h55; end
|
|
S_SFD: begin gmii_tx_en = 1'b1; gmii_txd = 8'hd5; end
|
|
S_DEST: begin
|
|
gmii_tx_en = 1'b1;
|
|
case (byte_count)
|
|
4'd0: gmii_txd = DEST_MAC[47:40];
|
|
4'd1: gmii_txd = DEST_MAC[39:32];
|
|
4'd2: gmii_txd = DEST_MAC[31:24];
|
|
4'd3: gmii_txd = DEST_MAC[23:16];
|
|
4'd4: gmii_txd = DEST_MAC[15:8];
|
|
default: gmii_txd = DEST_MAC[7:0];
|
|
endcase
|
|
end
|
|
S_SOURCE: begin
|
|
gmii_tx_en = 1'b1;
|
|
case (byte_count)
|
|
4'd0: gmii_txd = SOURCE_MAC[47:40];
|
|
4'd1: gmii_txd = SOURCE_MAC[39:32];
|
|
4'd2: gmii_txd = SOURCE_MAC[31:24];
|
|
4'd3: gmii_txd = SOURCE_MAC[23:16];
|
|
4'd4: gmii_txd = SOURCE_MAC[15:8];
|
|
default: gmii_txd = SOURCE_MAC[7:0];
|
|
endcase
|
|
end
|
|
S_TYPE: begin
|
|
gmii_tx_en = 1'b1;
|
|
gmii_txd = (byte_count == 4'd0) ? ETHERTYPE[15:8] : ETHERTYPE[7:0];
|
|
end
|
|
S_PAYLOAD: begin
|
|
gmii_tx_en = 1'b1;
|
|
payload_ready = 1'b1;
|
|
gmii_txd = payload_data;
|
|
end
|
|
S_PAD: begin gmii_tx_en = 1'b1; gmii_txd = 8'h00; end
|
|
S_FCS: begin
|
|
gmii_tx_en = 1'b1;
|
|
case (byte_count)
|
|
4'd0: gmii_txd = fcs_q[7:0];
|
|
4'd1: gmii_txd = fcs_q[15:8];
|
|
4'd2: gmii_txd = fcs_q[23:16];
|
|
default: gmii_txd = fcs_q[31:24];
|
|
endcase
|
|
end
|
|
default: begin end
|
|
endcase
|
|
end
|
|
|
|
always_comb requested_body_length = 16'd14 + {5'd0, payload_length};
|
|
|
|
always_ff @(posedge clk) begin
|
|
if (rst) begin
|
|
state <= S_IDLE;
|
|
byte_count <= 4'd0;
|
|
payload_len_q <= 11'd0;
|
|
payload_count <= 11'd0;
|
|
pad_remaining <= 6'd0;
|
|
ifg_count <= 4'd0;
|
|
crc_q <= 32'hffffffff;
|
|
fcs_q <= 32'd0;
|
|
frame_done <= 1'b0;
|
|
frames_transmitted <= 64'd0;
|
|
payload_bytes_transmitted <= 64'd0;
|
|
end else begin
|
|
frame_done <= 1'b0;
|
|
case (state)
|
|
S_IDLE: begin
|
|
if (frame_start && payload_length <= 11'd1500) begin
|
|
payload_len_q <= payload_length;
|
|
payload_count <= 11'd0;
|
|
byte_count <= 4'd0;
|
|
crc_q <= 32'hffffffff;
|
|
if (requested_body_length < 16'd60)
|
|
pad_remaining <= 16'd60 - requested_body_length;
|
|
else
|
|
pad_remaining <= 6'd0;
|
|
state <= S_PREAMBLE;
|
|
end
|
|
end
|
|
S_PREAMBLE: begin
|
|
if (byte_count == 4'd6) begin byte_count <= 4'd0; state <= S_SFD; end
|
|
else byte_count <= byte_count + 4'd1;
|
|
end
|
|
S_SFD: begin byte_count <= 4'd0; state <= S_DEST; end
|
|
S_DEST: begin
|
|
crc_q <= crc_next;
|
|
if (byte_count == 4'd5) begin byte_count <= 4'd0; state <= S_SOURCE; end
|
|
else byte_count <= byte_count + 4'd1;
|
|
end
|
|
S_SOURCE: begin
|
|
crc_q <= crc_next;
|
|
if (byte_count == 4'd5) begin byte_count <= 4'd0; state <= S_TYPE; end
|
|
else byte_count <= byte_count + 4'd1;
|
|
end
|
|
S_TYPE: begin
|
|
crc_q <= crc_next;
|
|
if (byte_count == 4'd1) begin
|
|
byte_count <= 4'd0;
|
|
if (payload_len_q != 11'd0) state <= S_PAYLOAD;
|
|
else if (pad_remaining != 6'd0) state <= S_PAD;
|
|
else begin fcs_q <= ~crc_next; state <= S_FCS; end
|
|
end else byte_count <= 4'd1;
|
|
end
|
|
S_PAYLOAD: begin
|
|
if (payload_valid) begin
|
|
crc_q <= crc_next;
|
|
payload_count <= payload_count + 11'd1;
|
|
if (payload_count + 11'd1 == payload_len_q) begin
|
|
if (pad_remaining != 6'd0) state <= S_PAD;
|
|
else begin fcs_q <= ~crc_next; byte_count <= 4'd0; state <= S_FCS; end
|
|
end
|
|
end
|
|
end
|
|
S_PAD: begin
|
|
crc_q <= crc_next;
|
|
if (pad_remaining == 6'd1) begin
|
|
pad_remaining <= 6'd0;
|
|
fcs_q <= ~crc_next;
|
|
byte_count <= 4'd0;
|
|
state <= S_FCS;
|
|
end else pad_remaining <= pad_remaining - 6'd1;
|
|
end
|
|
S_FCS: begin
|
|
if (byte_count == 4'd3) begin byte_count <= 4'd0; ifg_count <= 4'd0; state <= S_IFG; end
|
|
else byte_count <= byte_count + 4'd1;
|
|
end
|
|
S_IFG: begin
|
|
if (ifg_count == 4'd11) begin
|
|
ifg_count <= 4'd0;
|
|
state <= S_IDLE;
|
|
frame_done <= 1'b1;
|
|
frames_transmitted <= frames_transmitted + 64'd1;
|
|
payload_bytes_transmitted <= payload_bytes_transmitted + {53'd0, payload_len_q};
|
|
end else ifg_count <= ifg_count + 4'd1;
|
|
end
|
|
default: state <= S_IDLE;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
`ifndef SYNTHESIS
|
|
// The packetizer preassembles every frame, so this contract must hold and
|
|
// hardware never encodes TX_ER on RGMII.
|
|
always_ff @(posedge clk) begin
|
|
if (!rst && state == S_PAYLOAD && !payload_valid)
|
|
$error("ethernet_tx_mac payload stream underflow");
|
|
end
|
|
`endif
|
|
endmodule
|
|
|
|
`default_nettype wire
|