summaryrefslogtreecommitdiffhomepage
path: root/src/mac_rgmii.v
diff options
context:
space:
mode:
Diffstat (limited to 'src/mac_rgmii.v')
-rw-r--r--src/mac_rgmii.v998
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

Highly Recommended Verilog Books