summaryrefslogtreecommitdiffhomepage
path: root/source/mac.v
diff options
context:
space:
mode:
Diffstat (limited to 'source/mac.v')
-rw-r--r--source/mac.v895
1 files changed, 895 insertions, 0 deletions
diff --git a/source/mac.v b/source/mac.v
new file mode 100644
index 0000000..8941710
--- /dev/null
+++ b/source/mac.v
@@ -0,0 +1,895 @@
+/*
+ * mac.v
+ *
+ * Copyright (C) 2018, 2019 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: SGMII TX/RX/AN state machines
+ *
+ */
+
+`timescale 1ns /10ps
+
+module mac(
+ input rstn,
+ input phy_resetn, // The external PHY has its reset signal asserted
+ input clk,
+ input tap_port,
+
+ // SGMII AN
+ input link_timer,
+ input [1:0] fixed_speed,
+ input an_disable,
+ output reg an_link_up,
+ output reg an_duplex,
+ output reg phy_up,
+ output reg mode_100Mbit,
+
+ // Switch I/F
+ input [1:0] tx_mode,
+ output reg tx_f,
+
+ // PCS / SERDES health
+ input rx_lsm,
+ input rx_cv_err,
+ input rx_disp_err,
+ input rx_cdr_lol,
+ input rx_los,
+
+ // PCS data I/F
+ input rx_k,
+ input [7:0] rx_data,
+ output reg tx_k,
+ output reg [7:0] tx_data,
+ output reg tx_disp_correct,
+
+ // Flags and Interrupts
+ output reg rx_enet_bcast,
+ output reg rx_ipv4_arp,
+ output keep,
+
+ // TX FCS
+ output reg fcs_init,
+ output reg fcs_enable,
+ output reg [1:0] fcs_addr,
+ output reg [7:0] fcs_dout,
+ input [7:0] fcs_din,
+
+ // SGMII RX / FIFO Write
+ output rx_fifo_we,
+ output [8:0] rx_fifo_d,
+ output reg rx_error,
+ output reg rx_wr_done,
+
+ // SGMII TX / FIFO Read
+ output reg tx_fifo_re,
+ input [8:0] tx_fifo_d,
+ input tx_fifo_empty,
+
+ // Packet Filter
+ output rx_sample,
+ output reg ipv4_pkt_start,
+ output reg trigger,
+
+
+ output reg rx_k_m1,
+ output reg rx_k_m2,
+ output reg rx_k_m3,
+ output reg rx_k_m4,
+
+ output reg[7:0] rx_data_m1,
+ output reg[7:0] rx_data_m2,
+ output reg[7:0] rx_data_m3,
+ output reg[7:0] rx_data_m4,
+
+ // Param RAM for TAP port
+ output [10:0] dpr_ad,
+ output dpr_we,
+ output dpr_ce,
+ input [8:0] dpr_di,
+ output [8:0] dpr_do,
+
+ // Metrics and Interrupts
+ output reg mac_int,
+ output reg rx_sop, // start of packet
+ output reg rx_eop,
+ output reg tx_sop,
+ output reg tx_eop,
+ output reg metrics_start,
+ input [8:0] metrics_d,
+
+ // Debug
+ output reg rx_active,
+ output reg tx_active
+);
+
+`include "sgmii_params.v"
+`include "ethernet_params.v"
+
+localparam AN_TX_CONFIG_HI = 8'h00,
+ AN_TX_CONFIG_HI_ACK = 8'h40,
+ AN_TX_CONFIG_LO = 8'h01;
+
+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;
+
+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_8=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;
+
+reg [3:0] rx_cnt_100mbit, tx_cnt_100mbit;
+
+wire tx_sample, tx_sample_re;
+wire rx_packet_complete;
+wire mode_1Gbit;
+reg [1:0] an_speed;
+
+reg [3:0] rx_state;
+reg [10:0] rx_byte_cnt;
+reg [10:0] rx_pkt_length;
+reg [15:0] rx_l3_proto;
+
+reg [7:0] tx_data_an, tx_data_idle, tx_data_pkt;
+reg tx_k_an, tx_k_idle, tx_k_pkt;
+
+// Transmit Registers and Wires
+reg [3:0] tx_state; // transmit state machine
+reg [10:0] tx_byte_cnt;
+reg [5:0] param_addr;
+
+reg tx_f_an, tx_f_idle, tx_f_pkt;
+reg tx_last_byte;
+
+// FIFOs:
+reg [8:0] tx_fifo_d_m1;
+
+// FCS
+reg fcs_addr_e;
+
+// pipeline the param RAM for timing
+reg [8:0] dpr_di_reg;
+
+// counter for detecting Ethernet broadcast, only needs to count to 6
+reg [2:0] rx_enet_bcast_cnt;
+
+
+/*
+ * RX DIRECTION
+ *
+ */
+
+/*
+ * A shallow pool of RX registers for analysis
+ */
+always @(posedge clk or negedge rstn)
+ begin
+ if (!rstn)
+ begin
+ rx_k_m1 <= 1'b0;
+ rx_k_m2 <= 1'b0;
+ rx_k_m3 <= 1'b0;
+ rx_k_m4 <= 1'b0;
+ rx_data_m1 <= 8'h0;
+ rx_data_m2 <= 8'h0;
+ rx_data_m3 <= 8'h0;
+ rx_data_m4 <= 8'h0;
+ end
+ else if (mode_1Gbit || rx_sample || rx_state == RX_ST_IDLE || rx_state == RX_ST_DATA_DONE2 )
+ begin
+ rx_k_m1 <= rx_k;
+ rx_k_m2 <= rx_k_m1;
+ rx_k_m3 <= rx_k_m2;
+ rx_k_m4 <= rx_k_m3;
+ rx_data_m1 <= rx_data;
+ rx_data_m2 <= rx_data_m1;
+ rx_data_m3 <= rx_data_m2;
+ rx_data_m4 <= rx_data_m3;
+ end
+ end
+
+/*
+ * SGMII Auto Negotiation State Machine
+ * Look for configuration /C/ ordered set
+ * /C/ is Alternating /C1/ and /C2/
+ * /C1/: /K28.5/D21.5/Config_Reg
+ * /C2/: /K28.5/D2.2/Config_Reg
+ * Config Reg: Low High
+ *
+ * Not using a link timer and not counting 3 frames because testing rmshows it's unnecessary
+ *
+ */
+always @(posedge clk or negedge rstn)
+ if (!rstn)
+ begin
+ an_link_up <= 1'b0;
+ an_duplex <= 1'b0;
+ an_speed <= SGMII_SPEED_RSVD;
+ phy_up <= 1'b0;
+ end
+ else if ( !phy_resetn )
+ begin
+ an_link_up <= 1'b0;
+ an_duplex <= 1'b0;
+ an_speed <= SGMII_SPEED_RSVD;
+ phy_up <= 1'b0;
+ end
+ else if ( an_disable )
+ begin
+ phy_up <= 1'b1;
+ end
+ // D21.5 is part of config ( M2 has low, M1 has high )
+ else if (!rx_k_m1 && !rx_k_m2 && !rx_k_m3 && rx_data_m3 == D21_5 && rx_k_m4 && rx_data_m4 == K28_5 )
+ begin
+ an_link_up <= rx_data_m1[7];
+ an_duplex <= rx_data_m1[4];
+ an_speed <= rx_data_m1[3:2];
+ phy_up <= 1'b0;
+ end
+ // IDLE2 1:0xBC, 0:0x50
+ else if ( !rx_k_m1 && rx_data_m1 == D16_2 && rx_k_m2 == 1'b1 && rx_data_m2 == K28_5 )
+ phy_up <= 1'b1;
+
+// 100 MBit Support. There are no plans to support 10 MBit, so 100 MBit inactive is the same as 1GBit active
+// if/else encodes the priority
+always @(*)
+ if (fixed_speed == SGMII_SPEED_100MBIT)
+ mode_100Mbit = 1'b1;
+ else if (fixed_speed == SGMII_SPEED_1GBIT)
+ mode_100Mbit = 1'b0;
+ else if (an_speed == SGMII_SPEED_100MBIT )
+ mode_100Mbit = 1'b1;
+ else
+ mode_100Mbit = 1'b0;
+
+assign mode_1Gbit = ~mode_100Mbit;
+
+// RX 100 Mbit support
+assign rx_sample = (rx_cnt_100mbit == 4'd9 && mode_100Mbit) || !mode_100Mbit ? 1'b1 : 1'b0;
+always @(posedge 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 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 )
+ case ( rx_state )
+ RX_ST_IDLE: if (rx_data_m1 == K27_7 && rx_k_m1 ) // Found /S/
+ rx_state <= RX_ST_SOP;
+ RX_ST_SOP: if ( rx_sample ) // Capture /S/
+ rx_state <= RX_ST_PREAMBLE;
+ RX_ST_PREAMBLE: if ( rx_sample && rx_data_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; // write an extra byte into the FIFO
+ RX_ST_DATA_DONE1: if ( rx_sample )
+ rx_state <= RX_ST_DATA_DONE2; // write an extra byte into the FIFO
+ RX_ST_DATA_DONE2: if ( rx_sample )
+ rx_state <= rx_state; // waiting for /T/
+ 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)
+ *
+ */
+always @(posedge clk, negedge rstn)
+ if (!rstn)
+ rx_enet_bcast_cnt <= 3'h0;
+ else if ( rx_sample )
+ if (rx_data_m1 == 9'hff)
+ rx_enet_bcast_cnt <= rx_enet_bcast_cnt + 1;
+ else
+ rx_enet_bcast_cnt <= 3'h0;
+
+/* Ethernet Broadcast Dest Address, must be a one shot */
+always @(posedge 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 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 clk, negedge rstn)
+ if ( !rstn )
+ rx_l3_proto <= 0;
+ else if ( rx_sop )
+ rx_l3_proto <= 0;
+ else if ( rx_sample && rx_state == RX_ST_MAC_TYPE0 )
+ rx_l3_proto <= { rx_data_m2, rx_data_m1 };
+
+// assert ipv4 ARP flag for filtering operations
+always @(posedge 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;
+
+/*
+ * keep flag
+ * signals must be one shot
+ *
+ */
+ assign keep = rx_enet_bcast | rx_ipv4_arp;
+
+
+/* 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;
+ else
+ rx_error = 0;
+
+/* rx_byte_cnt */
+always @(posedge clk, negedge rstn)
+ if (!rstn)
+ rx_byte_cnt <= 'h0;
+ else if (rx_sample)
+ if ( rx_state == RX_ST_IDLE || rx_state == RX_ST_PREAMBLE )
+ rx_byte_cnt <= 'h0;
+ else if ( rx_state == RX_ST_MAC_TYPE0 )
+ rx_byte_cnt <= 'h1;
+ else
+ rx_byte_cnt <= rx_byte_cnt + 1;
+
+/* rx_pkt_length */
+always @(posedge clk, negedge rstn)
+ if ( !rstn )
+ rx_pkt_length <= 0;
+ else if ( rx_sop )
+ rx_pkt_length <= 0;
+ else if (rx_sample)
+ if ( rx_l3_proto == ETHER_TYPE_IPV4 && rx_state == RX_ST_DATA && rx_byte_cnt == 'h4 )
+ rx_pkt_length <= { rx_data_m2[2:0], rx_data_m1 };
+ else if ( rx_l3_proto == ETHER_TYPE_IPV6 && rx_state == RX_ST_DATA && rx_byte_cnt == 'h6 )
+ rx_pkt_length <= { rx_data_m2[2:0], rx_data_m1 } + 'd40;
+ else if ( rx_l3_proto == ETHER_TYPE_ARP && rx_state == RX_ST_DATA )
+ rx_pkt_length <= 'd46;
+
+/* ipv4 flag */
+always @(posedge 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;
+
+assign rx_packet_complete = ( rx_sample && rx_state >= RX_ST_DATA && rx_pkt_length == rx_byte_cnt ) ? 1 : 0;
+
+// FIFO data interface
+assign rx_fifo_d[7:0] = rx_data_m1;
+assign rx_fifo_d[8] = rx_packet_complete;
+
+
+/*
+ * rx_sop, K27_7, 0xFB /S/ Start_of_Packet
+ */
+always @(posedge clk or negedge rstn)
+ if (!rstn)
+ rx_sop <=1'b0;
+ else if ( rx_data_m1 == K27_7 && rx_k_m1 == 1'b1 )
+ rx_sop <= 1'b1;
+ else
+ rx_sop <= 1'b0;
+
+/*
+ * rx_eop, K29_7, 0xFD, /T/ End_of_Packet
+ */
+always @(posedge clk or negedge rstn)
+ if (!rstn)
+ rx_eop <=1'b0;
+ else if ( rx_data_m1 == K29_7 && rx_k_m1 == 1'b1 )
+ 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 clk or negedge rstn)
+ if (!rstn)
+ mac_int <=1'b0;
+ else if ( !rx_lsm || rx_cv_err || rx_cdr_lol || rx_los )
+ mac_int <= 1'b1;
+ else
+ mac_int <= 1'b0;
+
+
+/* Debug RX */
+always @(posedge clk or negedge rstn)
+ if (!rstn)
+ rx_active <=1'b0;
+ else if ( rx_state != 0 )
+ rx_active <= 1'b1;
+ else
+ rx_active <= 1'b0;
+
+
+
+/*
+ * TX DIRECTION
+ *
+ */
+
+
+ // TX 100 Mbit support
+assign tx_sample_re = (tx_cnt_100mbit == 4'd8 && mode_100Mbit) || !mode_100Mbit ? 1'b1 : 1'b0;
+assign tx_sample = (tx_cnt_100mbit == 4'd9 && mode_100Mbit) || !mode_100Mbit ? 1'b1 : 1'b0;
+always @(posedge 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;
+
+
+/*
+*
+* Transmit Mux
+*/
+always@(*)
+ begin
+ case(tx_mode)
+ TX_MODE_AN :
+ begin
+ tx_data = tx_data_an;
+ tx_k = tx_k_an;
+ tx_f = tx_f_an;
+ end
+ TX_MODE_IDLE :
+ begin
+ tx_data = tx_data_idle;
+ tx_k = tx_k_idle;
+ tx_f = tx_f_idle;
+ end
+ TX_MODE_XMT_PKT :
+ begin
+ tx_data = tx_data_pkt;
+ tx_k = tx_k_pkt;
+ tx_f = tx_f_pkt;
+ end
+ TX_MODE_XMT_METRICS :
+ begin
+ tx_data = tx_data_pkt;
+ tx_k = tx_k_pkt;
+ tx_f = tx_f_pkt;
+ end
+ default :
+ begin
+ tx_data = K_ERROR;
+ tx_k = 1'b1;
+ tx_f = 1'b1;
+ end
+ endcase
+ end
+
+
+/*
+ * CONFIG SM
+* During SGMII auto negotiation, send /C1/ and /C2/ ordered sets
+* C1: /K28.5/D21.5/Config Regs
+* C2: /K28.5/D2.2/Config Regs
+*/
+always @(*)
+ begin
+ tx_f_an = 1'b0;
+ tx_k_an = 1'b0;
+ case(tx_byte_cnt[2:0])
+ 3'd0:
+ begin
+ tx_data_an = K28_5;
+ tx_k_an = 1'b1;
+ end
+ 3'd1:
+ tx_data_an = D21_5;
+ 3'd2:
+ tx_data_an = AN_TX_CONFIG_LO;
+ 3'd3:
+ if (!an_link_up)
+ tx_data_an = AN_TX_CONFIG_HI;
+ else
+ tx_data_an = AN_TX_CONFIG_HI_ACK;
+ 3'd4:
+ begin
+ tx_data_an = K28_5;
+ tx_k_an = 1'b1;
+ end
+ 3'd5:
+ tx_data_an = D2_2;
+ 3'd6:
+ tx_data_an = AN_TX_CONFIG_LO;
+ 3'd7:
+ if (!an_link_up)
+ begin
+ tx_f_an = 1'b1;
+ tx_data_an = AN_TX_CONFIG_HI;
+ end
+ else
+ begin
+ tx_f_an = 1'b1;
+ tx_data_an = AN_TX_CONFIG_HI_ACK;
+ end
+ default:
+ begin
+ tx_data_an = K_ERROR;
+ tx_k_an = 1'b1;
+ tx_f_an = 1'b1;
+ end
+ endcase
+ end
+
+/* IDLE2 SM */
+always @(*)
+ begin
+ tx_f_idle = 1'b0;
+ case(tx_byte_cnt[1:0])
+ 3'd0:
+ begin
+ tx_data_idle = K28_5;
+ tx_k_idle = 1'b1;
+ end
+ 3'd1:
+ begin
+ tx_data_idle = D16_2;
+ tx_k_idle = 1'b0;
+ tx_f_idle = 1'b1;
+ end
+ default:
+ begin
+ tx_data_idle = K_ERROR;
+ tx_k_idle = 1'b1;
+ tx_f_idle = 1'b1;
+ end
+ endcase
+ end
+
+
+
+
+/*
+* Transmit Packet State Machine for TX_MODE_XMT_PKT and TX_MODE_XMT_METRICS
+*
+*
+* Note: the first /I/ following a transmitted frame or Configuration ordered set
+* restores the current positive or negative running disparity to a
+* negative value.
+*
+*/
+always @(posedge 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 ) // /S/
+ tx_state <= TX_ST_1;
+ TX_ST_1: if ( tx_sample && tx_byte_cnt == 8'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_byte_cnt
+ TX_ST_3: if ( tx_sample )
+ tx_state <= TX_ST_4; // preamble 0xD5
+ TX_ST_4: if ( tx_sample && tx_last_byte && tx_byte_cnt < 60 ) // check if we need to pad?
+ tx_state <= TX_ST_5;
+ else if ( tx_sample && (tx_fifo_empty || tx_last_byte) ) // check if we're done
+ tx_state <= TX_ST_6;
+ TX_ST_5: if ( tx_sample && tx_byte_cnt >= 60 ) // pad state, test for sufficient frame size
+ tx_state <= TX_ST_6;
+ TX_ST_6: if ( tx_sample && fcs_addr == 2'b10 ) // Start FCS
+ tx_state <= TX_ST_7;
+ TX_ST_7: if (tx_sample && fcs_addr == 2'b11 ) // Finish FCS
+ tx_state <= TX_ST_8;
+ TX_ST_8: tx_state <= TX_ST_9; // EOP /T/
+ TX_ST_9: if ( tx_byte_cnt[0] && !mode_100Mbit) // test for odd # of code words when in Gig mode for extra /R/ insertion
+ tx_state <= TX_ST_A;
+ else
+ tx_state <= TX_ST_B;
+ TX_ST_A: tx_state <= TX_ST_B; // 2nd /R/ if necessary ( odd position )
+ TX_ST_B: tx_state <= TX_ST_C; // I2, K28.5
+ TX_ST_C: tx_state <= TX_ST_0; // I2, D16.2
+
+ default: tx_state <= tx_state;
+ endcase
+ end
+
+
+
+
+/*
+* tx related data mux and control signals
+*
+*/
+always @(*)
+ begin
+ tx_f_pkt = 1'b0;
+ tx_k_pkt = 1'b0;
+ tx_disp_correct = 1'b0;
+ tx_last_byte = 1'b0;
+ fcs_init = 1'b0;
+ fcs_addr_e = 1'b0;
+ fcs_dout = tx_fifo_d_m1[7:0];
+ metrics_start = 1'b0;
+ case(tx_state)
+ TX_ST_0:
+ begin
+ tx_data_pkt = K27_7; // start of packet
+ tx_k_pkt = 1'b1;
+ fcs_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_METRICS && tx_byte_cnt <= SZ_ETH_HEADER + SZ_IPV4_HEADER + SZ_UDP_HEADER )
+ begin
+ tx_data_pkt = dpr_di_reg[7:0]; // packet headers
+ fcs_dout = dpr_di_reg[7:0];
+ metrics_start = 1'b1; // keeps the metrics counters in reset
+ end
+ else if ( tx_mode == TX_MODE_XMT_METRICS )
+ begin
+ tx_data_pkt = metrics_d; // packet content
+ fcs_dout = metrics_d[7:0];
+ tx_last_byte = metrics_d[8];
+ end
+ else
+ begin
+ tx_data_pkt = tx_fifo_d_m1[7:0]; // read data from memory
+ tx_last_byte = tx_fifo_d_m1[8];
+ end
+ end
+ TX_ST_5:
+ begin
+ tx_data_pkt = 8'h0; // pad
+ end
+ TX_ST_6: begin
+ tx_data_pkt = fcs_din; // read from fcs
+ fcs_addr_e = 1'b1;
+ end
+ TX_ST_7: begin
+ tx_data_pkt = fcs_din; // read from fcs
+ fcs_addr_e = 1'b1;
+ end
+ TX_ST_8:
+ begin
+ tx_data_pkt = K29_7; // end of packet
+ tx_k_pkt = 1'b1;
+ end
+ TX_ST_9:
+ begin
+ tx_data_pkt = K23_7; // carrier extend
+ tx_k_pkt = 1'b1;
+ end
+ TX_ST_A:
+ begin
+ tx_data_pkt = K23_7; // carrier extend
+ tx_k_pkt = 1'b1;
+ end
+ TX_ST_B:
+ begin
+ tx_data_pkt = K28_5; // 1st idle code
+ tx_k_pkt = 1'b1;
+ end
+ TX_ST_C:
+ begin
+ tx_data_pkt = D16_2; // 2nd idle code
+ tx_disp_correct = 1'b1; // PCS may convert D16.2 to a D5.6 for I2 to flip disparity
+ tx_f_pkt = 1'b1;
+ end
+ default:
+ begin
+ tx_data_pkt = K_ERROR;
+ tx_k_pkt = 1'b1;
+ 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_8 ) // 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
+ tx_fifo_re = 1'b0;
+
+
+always @(posedge clk, negedge rstn)
+ if (!rstn)
+ param_addr <= 'h0;
+ else if (tx_sample)
+ if ( mode_100Mbit && tx_state == TX_ST_1 && tx_byte_cnt == 'h5 )
+ param_addr <= 'h0;
+ else if ( mode_1Gbit && tx_state == TX_ST_1 && tx_byte_cnt == 'h4 )
+ param_addr <= 'h0;
+ else
+ param_addr <= param_addr + 1;
+
+/*
+ tx_byte_cnt
+
+ Increment at pcs clock rate for PCS layer data (e.g., /I1/, /C/, /S/, etc.
+
+ Increment at sample rate for Ethernet data
+
+*/
+always @(posedge clk, negedge rstn)
+ if (!rstn)
+ tx_byte_cnt <= '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_byte_cnt <= 'h0;
+ else if ( tx_state == TX_ST_2 )
+ tx_byte_cnt <= 'h0; // start counting the Ethernet Frame after preamble
+ else
+ tx_byte_cnt <= tx_byte_cnt + 1;
+
+/*
+ * pipeline data from FIFO
+ */
+always @(posedge clk or negedge rstn)
+begin
+ if ( !rstn )
+ tx_fifo_d_m1 <= 9'h0;
+ else if ( tx_sample )
+ tx_fifo_d_m1 <= tx_fifo_d;
+end
+
+/*
+* FCS
+*/
+always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ fcs_addr <= 2'b00;
+ else if (tx_sample)
+ if ( !fcs_addr_e )
+ fcs_addr <= 2'b00;
+ else
+ fcs_addr <= fcs_addr + 1;
+ end
+
+always @(*)
+ if (mode_1Gbit && (tx_state == TX_ST_4 || tx_state == TX_ST_5) )
+ fcs_enable = 1'b1;
+ else if ( mode_100Mbit && tx_sample && (tx_state == TX_ST_4 || tx_state == TX_ST_5) )
+ fcs_enable = 1'b1;
+ else
+ fcs_enable = 1'b0;
+
+/*
+* DPRAM, param ram Control for TAP port
+*/
+always @(posedge clk or negedge rstn)
+ if ( !rstn )
+ dpr_di_reg <= 9'h0;
+ else if (tx_sample)
+ dpr_di_reg <= dpr_di;
+
+
+assign dpr_we = 1'b0;
+assign dpr_ce = 1'b1;
+assign dpr_ad = { 4'h0, param_addr };
+
+/*
+ * tx_sop, K27_7, 0xFB /S/ Start_of_Packet
+ * We choose to not include TX_MODE_CUSTOM_PKT for this metric
+ */
+always @(posedge 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 clk or negedge rstn)
+ if (!rstn)
+ tx_eop <=1'b0;
+ else if ( tx_state == TX_ST_7 )
+ tx_eop <= 1'b1;
+ else
+ tx_eop <= 1'b0;
+
+/* Debug TX */
+always @(posedge clk or negedge rstn)
+ if (!rstn)
+ tx_active <=1'b0;
+ else if ( tx_state != 0 )
+ tx_active <= 1'b1;
+ else
+ tx_active <= 1'b0;
+
+endmodule