/* * mac_rgmii.v * * Copyright 2025 Private Island Networks Inc. * Copyright 2018, 2019, 2020 Mind Chasers Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * function: Ethernet MAC Layer for RGMII Interface * */ module mac_rgmii( input rstn, input phy_resetn, // The external PHY has its reset signal asserted input rx_clk, // rx_clk input tx_clk, input tap_port, // Control Interface input cont_clk, // controller clock input cont_sel, input cont_we, input [7:0] cont_addr, input [15:0] cont_d_i, output reg [15:0] cont_d_o, output cont_tgt_ready, // ML Engine Interface input mle_active, input mle_if_enable, input mle_if_oe, output reg mle_if_we, output reg mle_if_empty, output reg [8:0] mle_if_d_o, // Line State input fixed_speed, // 0 = 100 MBit, 1 = GigE output mode_100Mbit, output reg phy_up, // Switch I/F input [2:0] tx_mode, output reg tx_f, // RGMII data I/F input [1:0] rx_ctl, input [7:0] rx_d, output reg [1:0] tx_ctl, output reg [7:0] tx_d, // RX FCS output reg fcs_rx_init, output reg fcs_rx_enable, output reg [1:0] fcs_rx_addr, output reg [7:0] fcs_rx_dout, input [7:0] fcs_rx_din, // TX FCS output reg fcs_tx_init, output reg fcs_tx_enable, output reg [1:0] fcs_tx_addr, output reg [7:0] fcs_tx_dout, input [7:0] fcs_tx_din, // MAC RX / FIFO Write output rx_fifo_we, output [8:0] rx_fifo_d, output rx_keep, output reg rx_wr_done, output reg [10:0] rx_byte_cnt, output reg rx_idle, output reg rx_error, // MAC TX / FIFO Read input [10:0] tx_byte_cnt_i, input [2:0] tx_src_sel, output reg tx_fifo_re, input [8:0] tx_fifo_d, input tx_fifo_empty, // Alternate TX Port utilized by Controller input tx_uc_fifo_empty, input [8:0] tx_uc_fifo_d, // Alternate TX Port utilized by MLE input tx_mle_fifo_empty, input [8:0] tx_mle_fifo_d, // Packet Filter output rx_sample, output reg ipv4_pkt_start, output trigger, output reg[1:0] rx_ctl_m1, output reg[1:0] rx_ctl_m2, output reg[1:0] rx_ctl_m3, output reg[1:0] rx_ctl_m4, output reg[7:0] rx_d_m1, output reg[7:0] rx_d_m2, output reg[7:0] rx_d_m3, output reg[7:0] rx_d_m4, // Param RAM output reg [10:0] dpr_ad, output dpr_we, output dpr_ce, input [8:0] dpr_di, output [8:0] dpr_do, // Flags, Metrics, Interrupts, and Debug output reg rx_enet_bcast, output reg rx_ipv4_arp, output reg [15:0] rx_l3_proto, output reg [11:0] rx_pkt_length, output reg mac_int, output reg rx_sop, // start of packet output reg rx_eop, output reg tx_sop, output reg tx_eop, // Debug output reg rx_active, output reg tx_active ); `define INCLUDED `include "ethernet_params.v" `include "rgmii_params.v" `undef INCLUDED localparam RX_ST_IDLE=4'h0, RX_ST_SOP=4'h1, RX_ST_PREAMBLE=4'h2, RX_ST_SFD=4'h3, RX_ST_MAC_ADDR=4'h4, RX_ST_MAC_TYPE0=4'h5, RX_ST_MAC_TYPE1=4'h6, RX_ST_DATA=4'h7, RX_ST_DATA_DONE0=4'h8, RX_ST_DATA_DONE1=4'h9, RX_ST_DATA_DONE2=4'ha, RX_ST_DATA_DONE3=4'hb, RX_ST_DATA_DONE4=4'hc; localparam TX_ST_0=4'h0, TX_ST_1=4'h1, TX_ST_2=4'h2, TX_ST_3=4'h3, TX_ST_4=4'h4, TX_ST_5=4'h5, TX_ST_6=4'h6, TX_ST_7=4'h7, TX_ST_EOP=4'h8, TX_ST_9=4'h9, TX_ST_A=4'ha, TX_ST_B=4'hb, TX_ST_C=4'hc, TX_ST_D=4'hd, TX_ST_E=4'he, TX_ST_F=4'hf; // Controller Address Space localparam RX_PKT_CNT_ADDR = 16'h0000, TX_PKT_CNT_ADDR = 16'h0004; reg [3:0] rx_cnt_100mbit, tx_cnt_100mbit; wire mode_1Gbit; wire tx_sample, tx_sample_re; reg rx_packet_complete; reg [9:0] rx_line_up_cnt; reg link_up; reg [1:0] link_speed; // TODO: what's the right IEEE name? reg link_duplex; reg phy_up_m1; // previous state reg [3:0] rx_state; // TODO: consider reorganizing state machines to reuse registers. reg [7:0] tx_d_an, tx_d_idle, tx_data_pkt; reg [1:0] tx_ctl_an, tx_ctl_idle, tx_ctl_pkt; reg tx_f_an, tx_f_idle, tx_f_pkt; // Transmit Registers and Wires reg [3:0] tx_state; // transmit state machine reg [10:0] tx_cnt; reg tx_last_byte; wire tx_finished; // FIFOs: reg [8:0] tx_fifo_d_m1, tx_fifo_d_m2; // FCS reg fcs_tx_addr_e, fcs_rx_addr_e; reg fcs_rx_error; // pipeline the param RAM for timing reg [8:0] dpr_di_reg, dpr_di_reg_m1; // counter for detecting Ethernet broadcast, only needs to count to 6 reg [2:0] rx_enet_bcast_cnt; // ML Engine reg [3:0] mle_if_cnt; // Metrics reg [15:0] rx_pkt_cnt; reg [15:0] tx_pkt_cnt; /* * Controller Interface * * System's internal controller can write and read important parameters * */ // Controller Read Data Mux always @(posedge cont_clk, negedge rstn) if (!rstn) cont_d_o <= 16'hcccc; else case (cont_addr) RX_PKT_CNT_ADDR: cont_d_o <= rx_pkt_cnt; TX_PKT_CNT_ADDR: cont_d_o <= tx_pkt_cnt; default: cont_d_o <= cont_d_o; endcase // TODO: add logic to prevent controller reading metastable data assign cont_tgt_ready = 1'b1; /* * ML Engine Interface * */ // mle_if_cnt: // TODO: write cnt at end, rework this logic and make it more meaningful always @(posedge tx_clk, negedge rstn) if(!rstn) begin mle_if_cnt <= 4'd0; mle_if_empty <= 1'b1; mle_if_we <= 1'b0; end else if (rx_eop) begin mle_if_cnt <= 4'd4; mle_if_empty <= 1'b0; mle_if_we <= 1'b0; end else if (mle_if_cnt > 4'd0 && mle_if_enable) begin mle_if_cnt <= mle_if_cnt - 1'b1; mle_if_empty <= 1'b0; mle_if_we <= 1'b1; end else if (mle_if_cnt == 'd0 && mle_if_enable) begin mle_if_empty <= 1'b1; mle_if_we <= 1'b0; end // mle_if_d_o: always @(posedge tx_clk, negedge rstn) if(!rstn) mle_if_d_o <= 'd0; else if (mle_if_enable) begin if (mle_if_cnt > 4'd1) mle_if_d_o <= mle_if_d_o + 1'b1; else if (mle_if_cnt == 4'd1) mle_if_d_o <= (mle_if_d_o + 1'b1) | 9'h100; else mle_if_d_o <= 'd0; end /* * RX DIRECTION * */ /* * A shallow pool of RX registers for analysis */ always @(posedge rx_clk, negedge rstn) begin if (!rstn) begin rx_ctl_m1 <= NORMAL_INTERFRAME; rx_ctl_m2 <= NORMAL_INTERFRAME; rx_ctl_m3 <= NORMAL_INTERFRAME; rx_ctl_m4 <= NORMAL_INTERFRAME; rx_d_m1 <= 8'h0; rx_d_m2 <= 8'h0; rx_d_m3 <= 8'h0; rx_d_m4 <= 8'h0; end else begin rx_ctl_m1 <= rx_ctl; rx_ctl_m2 <= rx_ctl_m1; rx_ctl_m3 <= rx_ctl_m2; rx_ctl_m4 <= rx_ctl_m3; rx_d_m1 <= rx_d; rx_d_m2 <= rx_d_m1; rx_d_m3 <= rx_d_m2; rx_d_m4 <= rx_d_m3; end end // Link State always @(posedge rx_clk, negedge rstn) if (!rstn) rx_line_up_cnt <= 10'd0; else if ((rx_ctl_m1 == NORMAL_INTERFRAME && rx_d_m1 == {D_IDLE, D_IDLE}) || rx_ctl_m1 == NORMAL_DATA_RX) begin if (rx_line_up_cnt < 10'd1023) rx_line_up_cnt <= rx_line_up_cnt + 1'b1; end else rx_line_up_cnt <= 10'd0; // always @(posedge rx_clk, negedge rstn) if (!rstn) phy_up <= 1'b0; else if (rx_line_up_cnt >= 10'd5) phy_up <= 1'b1; else phy_up <= 1'b0; always @(posedge rx_clk, negedge rstn) if (!rstn) phy_up_m1 <= 1'b0; else phy_up_m1 <= phy_up; // capture link metrics during normal inter-frame always @(posedge rx_clk, negedge rstn) if (!rstn) begin link_up <= 1'b0; link_speed <= 2'b11; // reserved link_duplex <= 1'b0; end else if (rx_ctl == NORMAL_INTERFRAME) begin link_up <= rx_d[0]; link_speed <= rx_d[2:1]; link_duplex <= rx_d[3]; end assign mode_1Gbit = 1'b1; assign mode_100Mbit = !mode_1Gbit; // RX 100 Mbit support assign rx_sample = (rx_cnt_100mbit == 4'd9 && mode_100Mbit) || !mode_100Mbit ? 1'b1 : 1'b0; always @(posedge rx_clk or negedge rstn) if (!rstn) rx_cnt_100mbit <= 4'b0; else if ( rx_cnt_100mbit == 4'd9 || rx_sop ) rx_cnt_100mbit <= 4'b0; else rx_cnt_100mbit <= rx_cnt_100mbit + 4'd1; /* * rx_state machine * capture the Ethernet MAC header + packet. * */ always @(posedge rx_clk, negedge rstn) if (!rstn) rx_state <= RX_ST_IDLE; else if ( rx_eop || !phy_resetn ) // EOP will reset state machine rx_state <= RX_ST_IDLE; else if ( phy_up && phy_up_m1 ) case ( rx_state ) RX_ST_IDLE: if (rx_ctl_m1 == NORMAL_DATA_RX) // rx_state <= RX_ST_SOP; RX_ST_SOP: if ( rx_sample ) // rx_state <= RX_ST_PREAMBLE; RX_ST_PREAMBLE: if ( rx_sample && rx_d_m1 == 8'hd5 ) // 0xd5 preamble rx_state <= RX_ST_SFD; RX_ST_SFD: if ( rx_sample ) rx_state <= RX_ST_MAC_ADDR; RX_ST_MAC_ADDR: if ( rx_sample && rx_byte_cnt == 12 ) // Use this state transition to signal end of ethernet header and start of packet rx_state <= RX_ST_MAC_TYPE0; RX_ST_MAC_TYPE0: if ( rx_sample ) rx_state <= RX_ST_MAC_TYPE1; // Capture ethertype RX_ST_MAC_TYPE1: if ( rx_sample ) rx_state <= RX_ST_DATA; // RX_ST_DATA: if ( rx_sample && rx_packet_complete ) // write into FIFO until pkt length rx_state <= RX_ST_DATA_DONE0; RX_ST_DATA_DONE0: if ( rx_sample ) rx_state <= RX_ST_DATA_DONE1; RX_ST_DATA_DONE1: if ( rx_sample ) rx_state <= RX_ST_DATA_DONE2; RX_ST_DATA_DONE2: if ( rx_sample ) rx_state <= RX_ST_DATA_DONE3; RX_ST_DATA_DONE3: if ( rx_sample ) rx_state <= RX_ST_DATA_DONE4; RX_ST_DATA_DONE4: if ( rx_sample ) rx_state <= rx_state; default: rx_state <= rx_state; endcase else rx_state <= RX_ST_IDLE; /* * rx_fifo_we */ assign rx_fifo_we = ( rx_sample && ( rx_state >= RX_ST_SFD && rx_state <= RX_ST_DATA_DONE1 ) ) ? 1'b1 : 1'b0; /* * Detect Ethernet Broadcast (destination address = ff:ff:ff:ff:ff:ff) * TODO: Add state information to only trigger on DEST ADDRESS * */ always @(posedge rx_clk, negedge rstn) if (!rstn) rx_enet_bcast_cnt <= 3'h0; else if ( rx_sample ) if (rx_d_m1 == 9'hff) rx_enet_bcast_cnt <= rx_enet_bcast_cnt + 1'b1; else rx_enet_bcast_cnt <= 3'h0; /* Ethernet Broadcast Dest Address, must be a one shot */ always @(posedge rx_clk, negedge rstn) if (!rstn) rx_enet_bcast <= 1'b0; else if ( rx_sample ) if ( rx_enet_bcast_cnt == 3'h6 ) rx_enet_bcast <= 1'b1; else rx_enet_bcast <= 1'b0; /* create a one shot that will assert during RX_ST_DATA_DONE1 so external logic can know that the FIFO write has come to an end ( reset pointers, etc. ) For 100Mbit, since the states change 10 clocks apart, set it during RX_ST_DATA_DONE1 */ always @(posedge rx_clk, negedge rstn) if (!rstn) rx_wr_done <= 1'b0; else if ( mode_1Gbit && rx_state == RX_ST_DATA_DONE0 ) rx_wr_done <= 1'b1; else if ( mode_100Mbit && rx_sample && rx_state == RX_ST_DATA_DONE1 ) rx_wr_done <= 1'b1; else rx_wr_done <= 1'b0; /* capture layer 3 protocol (e.g., ipv4 or ipv6) */ always @(posedge rx_clk, negedge rstn) if ( !rstn ) rx_l3_proto <= 16'd0; else if ( rx_sop ) rx_l3_proto <= 16'd0; else if ( rx_sample && rx_state == RX_ST_MAC_TYPE0 ) rx_l3_proto <= { rx_d_m2, rx_d_m1 }; // assert ipv4 ARP flag for filtering operations always @(posedge rx_clk, negedge rstn) if (!rstn) rx_ipv4_arp <= 1'b0; else if ( rx_sample && rx_state == RX_ST_MAC_TYPE1 && rx_l3_proto == ETHER_TYPE_ARP) rx_ipv4_arp <= 1'b1; else rx_ipv4_arp <= 1'b0; /* * rx_keep flag * signals must be one shot */ // assign rx_keep = rx_enet_bcast | rx_ipv4_arp; assign rx_keep = rx_state == RX_ST_MAC_TYPE1; // rx_error always @(*) if ( rx_sample && rx_state >= RX_ST_DATA && ( rx_l3_proto != ETHER_TYPE_IPV4 && rx_l3_proto != ETHER_TYPE_IPV6 && rx_l3_proto != ETHER_TYPE_ARP) ) rx_error = 1'b1; else if (fcs_rx_error) rx_error = 1'b1; else rx_error = 1'b0; /* rx_byte_cnt */ always @(posedge rx_clk, negedge rstn) if (!rstn) rx_byte_cnt <= 11'd0; else if (rx_sample) if ( rx_state == RX_ST_IDLE || rx_state == RX_ST_PREAMBLE ) rx_byte_cnt <= 11'd0; else if ( rx_state == RX_ST_MAC_TYPE0 ) rx_byte_cnt <= 11'd1; else rx_byte_cnt <= rx_byte_cnt + 1'b1; /* rx_pkt_length */ always @(posedge rx_clk, negedge rstn) if ( !rstn ) rx_pkt_length <= 12'd0; else if ( rx_sop ) rx_pkt_length <= 12'd0; else if (rx_sample) if ( rx_l3_proto == ETHER_TYPE_IPV4 && rx_state == RX_ST_DATA && rx_byte_cnt == 'h4 ) rx_pkt_length <= { rx_d_m2[2:0], rx_d_m1 }; else if ( rx_l3_proto == ETHER_TYPE_IPV6 && rx_state == RX_ST_DATA && rx_byte_cnt == 'h6 ) rx_pkt_length <= { rx_d_m2[2:0], rx_d_m1 } + 'd40; else if ( rx_l3_proto == ETHER_TYPE_ARP && rx_state == RX_ST_DATA ) rx_pkt_length <= 'd46; /* ipv4 flag */ always @(posedge rx_clk, negedge rstn) if ( !rstn ) ipv4_pkt_start <= 1'b0; else if ( rx_sample && rx_l3_proto == ETHER_TYPE_IPV4 && rx_state == RX_ST_MAC_TYPE1 ) ipv4_pkt_start <= 1; else ipv4_pkt_start <= 0; // implement minimum Ethernet frame of 64 bytes always @(*) if (rx_pkt_length >= 12'd46) rx_packet_complete = (rx_sample && rx_state >= RX_ST_DATA && rx_pkt_length == rx_byte_cnt); else rx_packet_complete = (rx_sample && rx_state >= RX_ST_DATA && rx_byte_cnt == 12'd46); // FIFO data interface assign rx_fifo_d[7:0] = rx_d_m1; assign rx_fifo_d[8] = rx_packet_complete; // TODO: take into account errors RXERR always @(posedge rx_clk, negedge rstn) if (!rstn) rx_sop <= 1'b0; else if (rx_ctl_m1 ==NORMAL_DATA_RX && rx_ctl_m2 == NORMAL_INTERFRAME) rx_sop <= 1'b1; else rx_sop <= 1'b0; /* * rx_idle */ always @(posedge rx_clk, negedge rstn) if (!rstn) rx_idle <= 1'b0; else if (rx_state == RX_ST_IDLE) rx_idle <= 1'b1; else rx_idle <= 1'b0; /* * rx_eop */ always @(posedge rx_clk, negedge rstn) if (!rstn) rx_eop <= 1'b0; else if (rx_ctl_m2 ==NORMAL_DATA_RX && rx_ctl_m1 == NORMAL_INTERFRAME) rx_eop <= 1'b1; else rx_eop <= 1'b0; /* MAC Interrupt * Create one shot interrupt while interrupt source is active * Rely on interrupt controller to latch & clear */ always @(posedge rx_clk or negedge rstn) if (!rstn) mac_int <=1'b0; else if ( rx_error ) mac_int <= 1'b1; else mac_int <= 1'b0; /* RX Metrics and Debug */ always @(posedge rx_clk or negedge rstn) if (!rstn) rx_active <=1'b0; else if ( rx_state != 0 ) rx_active <= 1'b1; else rx_active <= 1'b0; // rx_pkt_cnt always @(posedge rx_clk or negedge rstn) if (!rstn) rx_pkt_cnt <= 16'd0; else if (rx_eop) rx_pkt_cnt <= rx_pkt_cnt + 1'b1; always @(*) if (mode_1Gbit && (rx_state > RX_ST_SFD && rx_state <= RX_ST_DATA_DONE0) ) fcs_rx_enable = 1'b1; else if ( mode_100Mbit && rx_sample && (rx_state > RX_ST_PREAMBLE && rx_state <= RX_ST_DATA_DONE0)) fcs_rx_enable = 1'b1; else fcs_rx_enable = 1'b0; /* FCS RX State Machine */ always @(posedge rx_clk or negedge rstn) if (!rstn) begin fcs_rx_init <= 1'b0; fcs_rx_addr_e <= 1'b0; fcs_rx_dout <= 8'h00; end else if(rx_state == RX_ST_SOP) fcs_rx_init = 1'b1; else if (rx_state > RX_ST_DATA && rx_state <= RX_ST_DATA_DONE3) begin fcs_rx_init <= 1'b0; fcs_rx_addr_e <= 1'b1; fcs_rx_dout <= 8'h00; end else begin fcs_rx_init <= 1'b0; fcs_rx_addr_e <= 1'b0; fcs_rx_dout <= rx_d_m1; end always @(posedge rx_clk or negedge rstn) begin if ( !rstn ) fcs_rx_addr <= 2'b00; else if (rx_sample) if ( !fcs_rx_addr_e ) fcs_rx_addr <= 2'b00; else fcs_rx_addr <= fcs_rx_addr + 1'b1; end // Test FCS RX result always @(posedge rx_clk or negedge rstn) if ( !rstn ) fcs_rx_error <= 1'b0; else if (fcs_rx_addr_e && rx_d_m2 != fcs_rx_din) fcs_rx_error <= 1'b1; else fcs_rx_error <= 1'b0; /* * TX DIRECTION * */ // TX 100 Mbit support assign tx_sample_re = (tx_cnt_100mbit == 4'd8 && mode_100Mbit) || !mode_100Mbit; assign tx_sample = (tx_cnt_100mbit == 4'd9 && mode_100Mbit) || !mode_100Mbit; always @(posedge tx_clk or negedge rstn) if (!rstn) tx_cnt_100mbit <= 4'b0; else if ( tx_state == TX_ST_0 ) tx_cnt_100mbit <= 4'b1; // steal a bit here during preamble so we keep an even bit count else if ( tx_cnt_100mbit == 4'd9 ) tx_cnt_100mbit <= 4'b0; else tx_cnt_100mbit <= tx_cnt_100mbit + 1'b1; /* * * Transmit Mux */ always @(posedge tx_clk or negedge rstn) if (!rstn) begin tx_d <= 8'hDD; tx_ctl <= 2'b00; end else begin case(tx_mode) TX_MODE_AN: // before phy_up == 1 begin tx_d <= tx_d_an; tx_ctl <= tx_ctl_an; end TX_MODE_IDLE: begin tx_d <= tx_d_idle; tx_ctl <= tx_ctl_idle; end default: begin tx_d <= tx_data_pkt; tx_ctl <= tx_ctl_pkt; end endcase end // tx_f mux always @(*) case(tx_mode) TX_MODE_AN: tx_f <= tx_f_an; TX_MODE_IDLE: tx_f <= tx_f_idle; default : tx_f <= tx_f_pkt; endcase /* AN SM */ always @(*) begin tx_f_an = 1'b1; case(tx_cnt[0]) 3'd0: begin tx_d_an = 8'hDD; tx_ctl_an = 2'b00; end default: begin tx_d_an = 8'hDD; tx_ctl_an = 2'b00; end endcase end /* IDLE SM */ always @(*) begin tx_f_idle = 1'b1; case(tx_cnt[0]) 3'd0: begin tx_d_idle = 8'hDD; tx_ctl_idle = 2'b00; end default: begin tx_d_idle = 8'hDD; tx_ctl_idle = 2'b00; end endcase end /* * TX Finished Logic */ assign tx_finished = tx_last_byte || tx_cnt == 11'd2000; /* * Transmit Packet State Machine * */ always @(posedge tx_clk, negedge rstn) begin if ( !rstn ) tx_state <= TX_ST_0; else if ( !phy_resetn ) tx_state <= TX_ST_0; else case(tx_state) TX_ST_0: if ( tx_mode >= TX_MODE_XMT_PKT && !tx_f_pkt ) // tx_state <= TX_ST_1; TX_ST_1: if ( tx_sample && tx_cnt == 11'h5 ) // preamble 0x55 tx_state <= TX_ST_2; TX_ST_2: if ( tx_sample ) tx_state <= TX_ST_3; // preamble 0x55, assert tx_fifo_re, reset tx_cnt TX_ST_3: if ( tx_sample ) tx_state <= TX_ST_4; // preamble 0xD5 TX_ST_4: if ( tx_sample && tx_finished && tx_cnt < 60 ) // check if we need to pad? tx_state <= TX_ST_5; else if ( tx_sample && tx_finished) // check if we're done tx_state <= TX_ST_6; TX_ST_5: if ( tx_sample && tx_cnt >= 60 ) // pad state, test for sufficient frame size tx_state <= TX_ST_6; TX_ST_6: if ( tx_sample && fcs_tx_addr == 2'b10 ) // Start FCS tx_state <= TX_ST_7; TX_ST_7: if (tx_sample && fcs_tx_addr == 2'b11 ) // Finish FCS tx_state <= TX_ST_EOP; TX_ST_EOP: tx_state <= TX_ST_0; // EOP default: tx_state <= tx_state; endcase end /* * tx related data mux and control signals * TODO: add additional states for stuffing header values * TODO: this will need to be registered at some point * */ always @(*) begin tx_f_pkt = 1'b0; tx_ctl_pkt = 2'b11; tx_last_byte = 1'b0; fcs_tx_init = 1'b0; fcs_tx_addr_e = 1'b0; fcs_tx_dout = tx_fifo_d_m1[7:0]; case(tx_state) TX_ST_0: begin tx_data_pkt = 8'hDD; // start of packet tx_ctl_pkt = 2'b00; fcs_tx_init = 1'b1; end TX_ST_1: begin tx_data_pkt = 8'h55; // preamble, we need 6 bytes total of 0x55 end TX_ST_2: begin tx_data_pkt = 8'h55; // preamble, single byte of 0x55 and assert fifo_re end TX_ST_3: begin tx_data_pkt = 8'hD5; // preamble, single byte of 0xd5 completes the preamble) end TX_ST_4: begin if (tx_mode == TX_MODE_XMT_CUSTOM && tx_cnt <= SZ_ETH_HEADER) begin tx_data_pkt = dpr_di_reg_m1[7:0]; // packet headers fcs_tx_dout = dpr_di_reg_m1[7:0]; end else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_cnt <= SZ_ETH_HEADER + SZ_IPV4_HEADER + SZ_UDP_HEADER ) if(tx_src_sel == TX_SRC_SEL_UC) begin tx_data_pkt = tx_uc_fifo_d[7:0]; // packet headers fcs_tx_dout = tx_uc_fifo_d[7:0]; end else begin // mle tx_data_pkt = tx_mle_fifo_d[7:0]; fcs_tx_dout = tx_mle_fifo_d[7:0]; end else if (tx_mode == TX_MODE_XMT_CUSTOM) begin tx_data_pkt = tx_fifo_d_m2[7:0]; // read data from FIFO with extra delay fcs_tx_dout = tx_fifo_d_m2[7:0]; tx_last_byte = tx_fifo_d_m2[8]; end else begin tx_data_pkt = tx_fifo_d_m1[7:0]; // read data from FIFO tx_last_byte = tx_fifo_d_m1[8]; end end TX_ST_5: begin tx_data_pkt = 8'h00; // pad fcs_tx_dout = 8'h00; end TX_ST_6: begin tx_data_pkt = fcs_tx_din; // read from fcs fcs_tx_addr_e = 1'b1; end TX_ST_7: begin tx_data_pkt = fcs_tx_din; // read from fcs fcs_tx_addr_e = 1'b1; end TX_ST_EOP: begin tx_data_pkt = 8'hDD; // end of packet tx_ctl_pkt = 2'b00; tx_f_pkt = 1'b1; end default: begin tx_data_pkt = 8'hDD; tx_ctl_pkt = 2'b00; tx_f_pkt = 1'b1; end endcase end /* * tx_fifo_re * * The use of the read fifo is different between 1Gbit and 100Mbit. * */ always @(*) if ( tx_mode == TX_MODE_XMT_PKT ) if ( mode_1Gbit && tx_state >= TX_ST_2 && tx_state <= TX_ST_4 ) tx_fifo_re = 1'b1; else if ( mode_100Mbit && tx_sample_re && tx_state > TX_ST_2 && tx_state <= TX_ST_4 ) tx_fifo_re = 1'b1; else if ( mode_100Mbit && tx_state == TX_ST_EOP ) // we need an extra FIFO strobe at 100MBit for a single 1G clk tx_fifo_re = 1'b1; else tx_fifo_re = 1'b0; else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_state == TX_ST_4 && tx_cnt > SZ_ETH_HEADER + SZ_IPV4_HEADER + SZ_UDP_HEADER - 3) tx_fifo_re = 1'b1; else tx_fifo_re = 1'b0; // tx_cnt: count number of transmit bytes / octets always @(posedge tx_clk, negedge rstn) if (!rstn) tx_cnt <= 11'h0; else if (tx_sample || tx_state == TX_ST_0 || tx_state > TX_ST_7 || tx_mode < TX_MODE_XMT_PKT ) if (tx_f) tx_cnt <= 11'h0; else if ( tx_state == TX_ST_2 ) tx_cnt <= 11'h0; // start counting the Ethernet Frame after preamble else if (tx_cnt < 11'd2000) tx_cnt <= tx_cnt + 1'b1; /* * pipeline data from FIFO */ always @(posedge tx_clk or negedge rstn) begin if ( !rstn ) begin tx_fifo_d_m1 <= 9'h0; tx_fifo_d_m2 <= 9'h0; end else begin tx_fifo_d_m1 <= tx_fifo_d; tx_fifo_d_m2 <= tx_fifo_d_m1; end end /* * FCS */ always @(posedge tx_clk or negedge rstn) begin if ( !rstn ) fcs_tx_addr <= 2'b00; else if (tx_sample) if ( !fcs_tx_addr_e ) fcs_tx_addr <= 2'b00; else fcs_tx_addr <= fcs_tx_addr + 1'b1; end always @(*) if (mode_1Gbit && (tx_state == TX_ST_4 || tx_state == TX_ST_5) ) fcs_tx_enable = 1'b1; else if ( mode_100Mbit && tx_sample && (tx_state == TX_ST_4 || tx_state == TX_ST_5) ) fcs_tx_enable = 1'b1; else fcs_tx_enable = 1'b0; /* * DPRAM, param ram Control for TAP port */ always @(posedge tx_clk, negedge rstn) if (!rstn) dpr_ad <= 11'h0; else if (tx_sample) if ( mode_100Mbit && tx_state == TX_ST_1 && tx_cnt == 11'h5 ) dpr_ad <= 'h0; else if ( mode_1Gbit && tx_state == TX_ST_1 && tx_cnt == 11'h1 ) dpr_ad <= 'h0; else dpr_ad <= dpr_ad + 1'b1; always @(posedge tx_clk or negedge rstn) if ( !rstn ) begin dpr_di_reg <= 9'h0; dpr_di_reg_m1 <= 9'h0; end else if (tx_sample) begin dpr_di_reg <= dpr_di; dpr_di_reg_m1 <= dpr_di_reg; end assign dpr_we = 1'b0; assign dpr_ce = 1'b1; assign dpr_do = 9'd0; /* * tx_sop, K27_7, 0xFB /S/ Start_of_Packet * We choose to not include TX_MODE_CUSTOM_PKT for this metric */ always @(posedge tx_clk or negedge rstn) if (!rstn) tx_sop <=1'b0; else if ( tx_state == TX_ST_0 && tx_mode >= TX_MODE_XMT_PKT ) tx_sop <= 1'b1; else tx_sop <= 1'b0; /* * tx_eop, K29_7, 0xFD, /T/ End_of_Packet */ always @(posedge tx_clk or negedge rstn) if (!rstn) tx_eop <=1'b0; else if ( tx_state == TX_ST_EOP ) tx_eop <= 1'b1; else tx_eop <= 1'b0; /* TX Debug and Metrics */ always @(posedge tx_clk or negedge rstn) if (!rstn) tx_active <=1'b0; else if ( tx_state == TX_ST_1 ) tx_active <= 1'b1; else tx_active <= 1'b0; always @(posedge tx_clk, negedge rstn) if (!rstn) tx_pkt_cnt <= 16'd0; else if (tx_eop) tx_pkt_cnt <= tx_pkt_cnt + 1'b1; endmodule