/* * mac.v * * Copyright 2018, 2019, 2020, 2021 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 * */ module mac( input rstn, input phy_resetn, // The external PHY has its reset signal asserted input clk, input tap_port, // PCS / SERDES health input rx_lsm, input rx_cv_err, input rx_disp_err, input rx_cdr_lol, input rx_los, // AN input [1:0] phy_type, // SGMII==0, SX=1, SMA=3 input pulse_1_6ms, // SGMII input pulse_10ms, // SX input [1:0] fixed_speed, input an_disable, output an_duplex, output phy_up, output reg mode_100Mbit, // Switch I/F input [2:0] tx_mode, output reg tx_f, // 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, // 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, // MAC RX / FIFO Write output rx_fifo_we, output [8:0] rx_fifo_d, output reg rx_error, output rx_keep, output reg rx_wr_done, output reg [10:0] rx_byte_cnt, output [1:0] rx_mode, // 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, // 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 output [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 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, output reg rx_active, output reg tx_active ); `include "sgmii_params.v" `include "ethernet_params.v" localparam PHY_TYPE_SGMII = 2'b00, PHY_TYPE_SX = 2'b01, PHY_TYPE_RSVD = 2'b10, PHY_TYPE_SMA = 2'b11; localparam AN_TX_CONFIG_HI = 8'h00, AN_TX_CONFIG_HI_ACK = 8'h40, AN_TX_CONFIG_LO = 8'h21; 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; // AN wire [15:0] tx_config_reg; wire [1:0] an_speed; reg [3:0] rx_cnt_100mbit, tx_cnt_100mbit; wire tx_sample, tx_sample_re; wire rx_packet_complete; wire mode_1Gbit; reg [3:0] rx_state; reg [10:0] rx_pkt_length; reg [15:0] rx_l3_proto; // TODO: consider reorganizing state machines to reuse registers. 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 [10:0] param_addr; reg tx_f_an, tx_f_idle, tx_f_pkt; reg i_tx_disp_correct; 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; // layer 3 TX support reg [18:0] tx_ipv4_cksum; reg [15:0] tx_ipv4_length; // layer 4 TX support reg [15:0] tx_udp_length; wire tx_finished; wire tx_temp; /* * 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 // Auto Negotiation an an_inst ( .rstn(rstn), .phy_resetn(phy_resetn), .clk(clk), // AN .phy_type(phy_type), .pulse_1_6ms(pulse_1_6ms), .pulse_10ms(pulse_10ms), .fixed_speed(fixed_speed), .an_disable(an_disable), .an_duplex(an_duplex), .an_speed(an_speed), .an_link_up(an_link_up), .tx_config_reg(tx_config_reg), .phy_up(phy_up), .rx_k_m1(rx_k_m1), .rx_k_m2(rx_k_m2), .rx_k_m3(rx_k_m3), .rx_k_m4(rx_k_m4), .rx_data_m1(rx_data_m1), .rx_data_m2(rx_data_m2), .rx_data_m3(rx_data_m3), .rx_data_m4(rx_data_m4) ); // 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; // rx_mode assign rx_mode = 2'b00; /* * Detect Ethernet Broadcast (destination address = ff:ff:ff:ff:ff:ff) * TODO: Add state information to only trigger on DEST ADDRESS * */ 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; /* * rx_keep flag * signals must be one shot * */ assign rx_keep = rx_enet_bcast | rx_ipv4_arp; /* rx_error * TODO: should be one shot? * */ 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; assign tx_sample = (tx_cnt_100mbit == 4'd9 && mode_100Mbit) || !mode_100Mbit; 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 @(posedge clk or negedge rstn) if (!rstn) begin tx_data <= 8'h00; tx_k <= 1'b0; tx_disp_correct <= 1'b0; end else begin case(tx_mode) TX_MODE_AN: begin tx_data <= tx_data_an; tx_k <= tx_k_an; tx_disp_correct <= i_tx_disp_correct; end TX_MODE_IDLE: begin tx_data <= tx_data_idle; tx_k <= tx_k_idle; tx_disp_correct <= i_tx_disp_correct; end default: begin tx_data <= tx_data_pkt; tx_k <= tx_k_pkt; tx_disp_correct <= i_tx_disp_correct; 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 /* * 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 /* * TX Finished Logic */ assign tx_temp = (tx_mode==TX_MODE_XMT_CUSTOM && tx_byte_cnt==tx_ipv4_length+SZ_ETH_HEADER); assign tx_finished = tx_last_byte || (tx_mode==TX_MODE_XMT_CUSTOM && tx_byte_cnt==(tx_ipv4_length+SZ_ETH_HEADER)); /* * Transmit Packet State Machine * * * 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_finished && tx_byte_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_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 * 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_k_pkt = 1'b0; i_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_CUSTOM && tx_byte_cnt == 'd17) begin tx_data_pkt = tx_ipv4_length[15:8]; fcs_dout = tx_ipv4_length[15:8]; end else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_byte_cnt == 'd18) begin tx_data_pkt = tx_ipv4_length[7:0]; fcs_dout = tx_ipv4_length[7:0]; end else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_byte_cnt == 'd25) begin tx_data_pkt = tx_ipv4_cksum[15:8]; fcs_dout = tx_ipv4_cksum[15:8]; end else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_byte_cnt == 'd26) begin tx_data_pkt = tx_ipv4_cksum[7:0]; fcs_dout = tx_ipv4_cksum[7:0]; end else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_byte_cnt == 'd38) begin tx_data_pkt = 8'h0 + tx_src_sel[2]; // UDP destination port fcs_dout = 8'h0 + tx_src_sel[2]; end else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_byte_cnt == 'd39) begin tx_data_pkt = tx_udp_length[15:8]; fcs_dout = tx_udp_length[15:8]; end else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_byte_cnt == 'd40) begin tx_data_pkt = tx_udp_length[7:0]; fcs_dout = tx_udp_length[7:0]; end else if ( (tx_mode == TX_MODE_XMT_METRICS || tx_mode == TX_MODE_XMT_CUSTOM) && 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 FIFO tx_last_byte = tx_fifo_d_m1[8]; end end TX_ST_5: begin tx_data_pkt = 8'h00; // pad fcs_dout = 8'h00; 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 i_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 if (tx_mode == TX_MODE_XMT_CUSTOM && tx_state == TX_ST_4 && tx_byte_cnt > SZ_ETH_HEADER + SZ_IPV4_HEADER + SZ_UDP_HEADER - 2) tx_fifo_re = 1'b1; 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 == 'h2 ) 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 = param_addr; 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 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; // Layer 3 TX Support always @(posedge clk or negedge rstn) if (!rstn) tx_ipv4_length <= 16'h0000; else if ( tx_state == TX_ST_1 ) tx_ipv4_length <= {5'h00, tx_byte_cnt_i} +SZ_IPV4_HEADER + SZ_UDP_HEADER; // tx_ipv4_cksum always @(posedge clk or negedge rstn) if (!rstn) tx_ipv4_cksum <= 18'h00000; else if ( tx_state == TX_ST_2 ) tx_ipv4_cksum <= {10'h000, dpr_di_reg[7:0]}; else if ( tx_state == TX_ST_3 ) tx_ipv4_cksum <= {2'b00, dpr_di_reg[7:0], tx_ipv4_cksum[7:0]}; else if (tx_state == TX_ST_4 && tx_byte_cnt == 'd1) tx_ipv4_cksum <= tx_ipv4_cksum + tx_ipv4_length; else if (tx_state == TX_ST_4 && tx_byte_cnt == 'd2) tx_ipv4_cksum <= {2'b00,tx_ipv4_cksum[15:0]} + {16'h0000,tx_ipv4_cksum[17:16]}; else if (tx_state == TX_ST_4 && tx_byte_cnt == 'd3) tx_ipv4_cksum <= {2'b00, ~tx_ipv4_cksum[15:0]}; // Layer 4 TX Support always @(posedge clk or negedge rstn) if (!rstn) tx_udp_length <= 16'h0000; else if ( tx_state == TX_ST_1 ) tx_udp_length <= {5'h00, tx_byte_cnt_i} + SZ_UDP_HEADER; /* Debug TX */ always @(posedge 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; endmodule