/* * switch.v * * Copyright 2024, 2025 Private Island Networks Inc. * Copyright 2018 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: switch packet paths from RX to TX for LF_A_V01 * */ module switch #(parameter NUM_PHYS=3) ( input rstn, input clk, // PHY status input [NUM_PHYS-1:0] phy_up, input [NUM_PHYS-1:0] mode_100Mbit, // FIFO input data from RX FIFOs input [8:0] rx_d_01, input [8:0] rx_d_02, input [8:0] rx_d_0u, input [8:0] rx_d_10, input [8:0] rx_d_12, input [8:0] rx_d_20, input [8:0] rx_d_21, input [8:0] rx_d_u0, // RX FIFO read enables output reg rx_fifo_re_01, rx_fifo_re_02, rx_fifo_re_0u, output reg rx_fifo_re_10, rx_fifo_re_12, output reg rx_fifo_re_20, rx_fifo_re_21, output reg rx_fifo_re_u0, // RX FIFO Empty flags input rx_fifo_empty_01, rx_fifo_empty_02, rx_fifo_empty_0u, input rx_fifo_empty_10, rx_fifo_empty_12, input rx_fifo_empty_20, rx_fifo_empty_21, input rx_fifo_empty_u0, input [NUM_PHYS-1:0] rx_wr_done, // RX Byte Count input [10:0] rx0_byte_cnt, input [10:0] rx1_byte_cnt, input [10:0] rx2_byte_cnt, input [10:0] rxu_byte_cnt, // TX FIFO output from internal muxes output reg [8:0] tx_d0, output reg [8:0] tx_d1, output reg [8:0] tx_d2, output [8:0] tx_du, // TX FIFO read enable inputs (need to route to RX output FIFOs) input [NUM_PHYS-1:0] tx_fifo_re, output reg tx_fifo_we_u, // TX FIFO Empty Flags (need to route to RX output FIFOs) output reg [NUM_PHYS-1:0] tx_fifo_empty, // TX modes for the PHYs and uc output reg [2:0] tx_mode0, output reg [2:0] tx_mode1, output reg [2:0] tx_mode2, output reg [2:0] tx_modeu, // TX byte cnt output reg [10:0] tx0_byte_cnt, output reg [10:0] tx1_byte_cnt, output reg [10:0] tx2_byte_cnt, output reg [2:0] tx0_src_sel, output reg [2:0] tx1_src_sel, output reg [2:0] tx2_src_sel, // TX state machine done flag input [NUM_PHYS-1:0] tx_f, // TX custom packet input tx_custom ); // IPG for Port 0 wire ipg_met; reg [6:0] ipg_cnt; reg rx_fifo_empty_u0_m1, rx_fifo_empty_u0_m2; reg [3:0] fr_100mbit_cnt; reg i_tx_fifo_we_u; reg [10:0] i_rx0_byte_cnt, i_rx1_byte_cnt, i_rx2_byte_cnt; `define INCLUDED `include "ethernet_params.v" `undef INCLUDED localparam SEL_PHY0 = 3'b000, SEL_PHY1 = 3'b001, SEL_PHY2 = 3'b010, SEL_UC = 3'b111; // capture the rx_byte_cnt values when write is done. TODO: needs to be a shallow Q to match FIFO always @(posedge clk, negedge rstn) if ( !rstn ) i_rx0_byte_cnt <= 'h0; else if ( rx_wr_done[0]) i_rx0_byte_cnt <= rx0_byte_cnt; always @(posedge clk, negedge rstn) if ( !rstn ) i_rx1_byte_cnt <= 'h0; else if ( rx_wr_done[1]) i_rx1_byte_cnt <= rx1_byte_cnt; always @(posedge clk, negedge rstn) if ( !rstn ) i_rx2_byte_cnt <= 'h0; else if ( rx_wr_done[2]) i_rx2_byte_cnt <= rx2_byte_cnt; assign ipg_met = ipg_cnt >= IPG ? 1'b1 : 1'b0; /* free running 100Mbit counter */ always @(posedge clk, negedge rstn) if ( !rstn ) fr_100mbit_cnt <= 4'd0; else if ( fr_100mbit_cnt == 4'd9 ) fr_100mbit_cnt <= 4'd0; else fr_100mbit_cnt <= fr_100mbit_cnt + 1'b1; /* IPG counter */ always @(posedge clk, negedge rstn) if ( !rstn ) ipg_cnt <= 7'd0; else if ( tx_f[0] && tx_mode0 >= TX_MODE_XMT_PKT ) ipg_cnt <= 7'd0; else if ( mode_100Mbit[0] && fr_100mbit_cnt == 4'd9 && !ipg_met ) ipg_cnt <= ipg_cnt + 1; else if ( !mode_100Mbit[0] && !ipg_met ) ipg_cnt <= ipg_cnt + 1'b1; // Transfer to the pclk domain always @(posedge clk, negedge rstn) if ( !rstn ) begin rx_fifo_empty_u0_m1 <= 1'b1; rx_fifo_empty_u0_m2 <= 1'b1; end else begin rx_fifo_empty_u0_m1 <= rx_fifo_empty_u0; rx_fifo_empty_u0_m2 <= rx_fifo_empty_u0_m1; end // TX0 Switch Logic // Possible sources: u, 1, 2 always @(posedge clk, negedge rstn) if ( !rstn ) begin tx_mode0 <= TX_MODE_AN; tx0_src_sel <= SEL_PHY1; tx0_byte_cnt <= 'h0; end else if ( tx_f[0] ) case( tx_mode0 ) TX_MODE_AN: if ( phy_up[0] ) tx_mode0 <= TX_MODE_IDLE; TX_MODE_IDLE: if ( !phy_up[0] ) tx_mode0 <= TX_MODE_AN; else if ( !ipg_met ) tx_mode0 <= TX_MODE_IDLE; else if (!rx_fifo_empty_u0_m2 ) // controller has data begin tx_mode0 <= TX_MODE_XMT_CUSTOM; tx0_src_sel <= SEL_UC; tx0_byte_cnt <= rxu_byte_cnt; end `ifdef PHY2_PRESENT else if (tx0_src_sel==SEL_PHY1 && !rx_fifo_empty_20 ) begin tx_mode0 <= TX_MODE_XMT_PKT; tx0_src_sel <= SEL_PHY2; tx0_byte_cnt <= i_rx2_byte_cnt; end `endif else if (!rx_fifo_empty_10 ) begin tx_mode0 <= TX_MODE_XMT_PKT; tx0_src_sel <= SEL_PHY1; tx0_byte_cnt <= i_rx1_byte_cnt; end `ifdef PHY2_PRESENT else if (!rx_fifo_empty_20 ) begin tx_mode0 <= TX_MODE_XMT_PKT; tx0_src_sel <= SEL_PHY2; tx0_byte_cnt <= i_rx2_byte_cnt; end `endif TX_MODE_XMT_PKT: if ( !phy_up[0] ) tx_mode0 <= TX_MODE_AN; else tx_mode0 <= TX_MODE_IDLE; default: tx_mode0 <= TX_MODE_IDLE; endcase // TX0 data mux always @(*) begin case(tx0_src_sel) SEL_PHY0: tx_d0 = 9'h000; SEL_PHY1: tx_d0 = rx_d_10; SEL_PHY2: tx_d0 = rx_d_20; SEL_UC: tx_d0 = rx_d_u0; default: tx_d0 = 9'h000; endcase end // TX0 FIFO read enable always @(*) begin rx_fifo_re_10 = 1'b0; rx_fifo_re_20 = 1'b0; rx_fifo_re_u0 = 1'b0; case(tx0_src_sel) SEL_PHY1: rx_fifo_re_10 = tx_fifo_re[0]; SEL_PHY2: rx_fifo_re_20 = tx_fifo_re[0]; SEL_UC: rx_fifo_re_u0 = tx_fifo_re[0]; endcase end // TX0 FIFO Empty Routing always @(*) begin case(tx0_src_sel) SEL_PHY1: tx_fifo_empty[0] = rx_fifo_empty_10; SEL_PHY2: tx_fifo_empty[0] = rx_fifo_empty_20; SEL_UC: tx_fifo_empty[0] = rx_fifo_empty_u0_m2; default: tx_fifo_empty[0] = 1'b1; endcase end // TX1 Switch Logic // Possible sources: 0, 2 in priority always @(posedge clk, negedge rstn) if ( !rstn ) begin tx_mode1 <= TX_MODE_AN; tx1_src_sel <= SEL_PHY0; end else if ( tx_f[1] ) case( tx_mode1 ) TX_MODE_AN: if ( phy_up[1] ) tx_mode1 <= TX_MODE_IDLE; TX_MODE_IDLE: if ( !phy_up[1] ) tx_mode1 <= TX_MODE_AN; `ifdef PHY2_PRESENT else if (tx1_src_sel==SEL_PHY0 && !rx_fifo_empty_21 ) begin tx_mode1 <= TX_MODE_XMT_PKT; tx1_src_sel <= SEL_PHY2; end `endif else if (!rx_fifo_empty_01 ) begin tx_mode1 <= TX_MODE_XMT_PKT; tx1_src_sel <= SEL_PHY0; end `ifdef PHY2_PRESENT else if (!rx_fifo_empty_21 ) begin tx_mode1 <= TX_MODE_XMT_PKT; tx1_src_sel <= SEL_PHY2; end `endif TX_MODE_XMT_PKT: if ( !phy_up[1] ) tx_mode1 <= TX_MODE_AN; else tx_mode1 <= TX_MODE_IDLE; default: tx_mode1 <= TX_MODE_IDLE; endcase // TX1 data mux always @(*) begin case(tx1_src_sel) SEL_PHY0: tx_d1 = rx_d_01; SEL_PHY1: tx_d1 = 9'h000; SEL_PHY2: tx_d1 = rx_d_21; SEL_UC: tx_d1 = 9'h000; default: tx_d1 = 9'h000; endcase end // TX1 FIFO read enable always @(*) begin rx_fifo_re_01 = 1'b0; rx_fifo_re_21 = 1'b0; case(tx1_src_sel) SEL_PHY0: rx_fifo_re_01 = tx_fifo_re[1]; SEL_PHY2: rx_fifo_re_21 = tx_fifo_re[1]; endcase end // TX1 FIFO Empty Routing always @(*) begin case(tx1_src_sel) SEL_PHY0: tx_fifo_empty[1] = rx_fifo_empty_01; SEL_PHY1: tx_fifo_empty[1] = 1'b1; SEL_PHY2: tx_fifo_empty[1] = rx_fifo_empty_21; SEL_UC: tx_fifo_empty[1] = 1'b1; default: tx_fifo_empty[1] = 1'b1; endcase end /* * TX2 Switch Logic * Possible Sources: 0, 1 */ always @(posedge clk, negedge rstn) if ( !rstn ) begin tx_mode2 <= TX_MODE_AN; tx2_src_sel <= SEL_PHY0; end else if ( tx_f[2] ) case( tx_mode2 ) TX_MODE_AN: if ( phy_up[2] ) tx_mode2 <= TX_MODE_IDLE; TX_MODE_IDLE: if ( !phy_up[2] ) tx_mode2 <= TX_MODE_AN; else if (tx2_src_sel==SEL_PHY0 && !rx_fifo_empty_12 ) begin tx_mode2 <= TX_MODE_XMT_PKT; tx2_src_sel <= SEL_PHY1; end else if (!rx_fifo_empty_02 ) begin tx_mode2 <= TX_MODE_XMT_PKT; tx2_src_sel <= SEL_PHY0; end else if (!rx_fifo_empty_12 ) begin tx_mode2 <= TX_MODE_XMT_PKT; tx2_src_sel <= SEL_PHY1; end TX_MODE_XMT_PKT: if ( !phy_up[2] ) tx_mode2 <= TX_MODE_AN; else tx_mode2 <= TX_MODE_IDLE; default: tx_mode2 <= TX_MODE_IDLE; endcase // TX2 data mux always @(*) begin case(tx2_src_sel) SEL_PHY0: tx_d2 = rx_d_02; SEL_PHY1: tx_d2 = rx_d_12; SEL_PHY2: tx_d2 = 9'h000; default: tx_d2 = 9'h000; endcase end // TX2 FIFO read enable always @(*) begin rx_fifo_re_02 = 1'b0; rx_fifo_re_12 = 1'b0; case(tx2_src_sel) SEL_PHY0: rx_fifo_re_02 = tx_fifo_re[2]; SEL_PHY1: rx_fifo_re_12 = tx_fifo_re[2]; endcase end // TX2 FIFO Empty Routing always @(*) begin case(tx2_src_sel) SEL_PHY0: tx_fifo_empty[2] = rx_fifo_empty_02; SEL_PHY1: tx_fifo_empty[2] = rx_fifo_empty_12; SEL_PHY2: tx_fifo_empty[2] = 1'b0; // default: tx_fifo_empty[2] = 1'b1; endcase end /* * Transmit Logic for UC * * The only possible driver is PHY0 * * We need to delay the fifo_we one clock since the DPRAM read data comes out one clock delayed */ assign tx_du = rx_d_0u; always @(*) if ( !rx_fifo_empty_0u ) begin i_tx_fifo_we_u = 1'b1; rx_fifo_re_0u = 1'b1; end else begin i_tx_fifo_we_u = 1'b0; rx_fifo_re_0u = 1'b0; end always @(posedge clk, negedge rstn) if ( !rstn ) tx_fifo_we_u <= 1'b0; else tx_fifo_we_u <= i_tx_fifo_we_u; endmodule