Files

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