diff options
Diffstat (limited to 'src/mac_rgmii.v')
| -rw-r--r-- | src/mac_rgmii.v | 998 |
1 files changed, 998 insertions, 0 deletions
diff --git a/src/mac_rgmii.v b/src/mac_rgmii.v new file mode 100644 index 0000000..aa96705 --- /dev/null +++ b/src/mac_rgmii.v @@ -0,0 +1,998 @@ +/* + * 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 |



