diff options
Diffstat (limited to 'src/switch.v')
| -rw-r--r-- | src/switch.v | 441 |
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 |



