summaryrefslogtreecommitdiffhomepage
path: root/src/switch.v
diff options
context:
space:
mode:
Diffstat (limited to 'src/switch.v')
-rw-r--r--src/switch.v441
1 files changed, 441 insertions, 0 deletions
diff --git a/src/switch.v b/src/switch.v
new file mode 100644
index 0000000..fa6d75e
--- /dev/null
+++ b/src/switch.v
@@ -0,0 +1,441 @@
+/*
+ * 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 \ No newline at end of file

Highly Recommended Verilog Books