diff options
| author | Private Island Networks Inc <opensource@privateisland.tech> | 2025-12-21 20:51:04 -0500 |
|---|---|---|
| committer | Private Island Networks Inc <opensource@privateisland.tech> | 2025-12-21 20:51:04 -0500 |
| commit | 7b1b5e7eb712d41888398934834cae730e0aa5a0 (patch) | |
| tree | 8b8aba85e19a079fbbd4962c57ff89ca701c6e4d /src | |
| parent | f4bdc9f4365d3a3ce3f906e68cd018cb57561e56 (diff) | |
betsy: preliminary beta snapshot
Diffstat (limited to 'src')
| -rw-r--r-- | src/an.v | 122 | ||||
| -rw-r--r-- | src/bin_to_ascii.v | 128 | ||||
| -rw-r--r-- | src/cam.v | 48 | ||||
| -rw-r--r-- | src/clk_gen.v | 67 | ||||
| -rw-r--r-- | src/cont_params.v | 61 | ||||
| -rw-r--r-- | src/controller.v | 136 | ||||
| -rw-r--r-- | src/directives.v | 34 | ||||
| -rw-r--r-- | src/dpram.v | 102 | ||||
| -rw-r--r-- | src/dpram_inf.v | 84 | ||||
| -rw-r--r-- | src/drop_fifo.v | 328 | ||||
| -rw-r--r-- | src/ethernet_params.v | 36 | ||||
| -rw-r--r-- | src/fcs.v | 155 | ||||
| -rw-r--r-- | src/half_fifo.v | 299 | ||||
| -rw-r--r-- | src/i2c.v | 391 | ||||
| -rw-r--r-- | src/interrupts.v | 77 | ||||
| -rw-r--r-- | src/ipv4_rx.v (renamed from src/ipv4.v) | 75 | ||||
| -rw-r--r-- | src/ipv4_rx_c.v | 134 | ||||
| -rw-r--r-- | src/ipv4_tx_c.v | 179 | ||||
| -rw-r--r-- | src/ipv4_tx_mle.v | 175 | ||||
| -rw-r--r-- | src/link_timer.v | 67 | ||||
| -rw-r--r-- | src/mac.v | 973 | ||||
| -rw-r--r-- | src/mac_rgmii.v | 998 | ||||
| -rw-r--r-- | src/mdio.v | 5 | ||||
| -rw-r--r-- | src/mdio_cont.v | 93 | ||||
| -rw-r--r-- | src/mdio_data_ti.v | 181 | ||||
| -rw-r--r-- | src/metrics.v | 102 | ||||
| -rw-r--r-- | src/ml_engine.v | 563 | ||||
| -rw-r--r-- | src/pkt_filter.v | 106 | ||||
| -rw-r--r-- | src/rgmii_params.v | 46 | ||||
| -rw-r--r-- | src/sgmii_params.v | 55 | ||||
| -rw-r--r-- | src/spi.v | 315 | ||||
| -rw-r--r-- | src/switch.v | 656 | ||||
| -rw-r--r-- | src/sync_fifo.v | 13 | ||||
| -rw-r--r-- | src/udp_rx.v | 117 | ||||
| -rw-r--r-- | src/udp_rx_c.v | 89 |
35 files changed, 3552 insertions, 3458 deletions
diff --git a/src/an.v b/src/an.v deleted file mode 100644 index 18b5d95..0000000 --- a/src/an.v +++ /dev/null @@ -1,122 +0,0 @@ -/* - * an.v - * - * Copyright 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: Auto Negotiation - * - * Notes: a simplified version of AN suitable for SGMII only (not 1000BASE-SX) - */ - -`timescale 1ns /10ps - -module an( - input rstn, - input phy_resetn, // The external PHY has its reset signal asserted - input clk, - - // 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 reg an_duplex, - output reg [1:0] an_speed, - output reg an_link_up, - output reg [15:0] tx_config_reg, - output reg phy_up, - - input rx_k_m1, - input rx_k_m2, - input rx_k_m3, - input rx_k_m4, - - input [7:0] rx_data_m1, - input [7:0] rx_data_m2, - input [7:0] rx_data_m3, - input [7:0] rx_data_m4 - - // Debug -); - -`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_TYPE_SX = 1'b0, - AN_TYPE_SGMII = 1'b1; - -localparam AN_ST_DISABLE=4'h0, AN_ST_ENABLE=4'h1, AN_ST_RESTART=4'h2, - AN_ST_ABILITY_DETECT=4'h3, AN_ST_ACK_DETECT=4'h4, - AN_ST_COMPLETE_ACK=4'h5, AN_ST_IDLE_DETECT=4'h6, AN_ST_LINK_OK=4'h7; - -// AN -reg [2:0] an_state; -wire an_ability_detect; -wire an_link_timer_pulse; -reg rx_config_detect; -reg [15:0] rx_config_reg, tx_config_reg; -reg [1:0] rx_config_cnt; // use to count consecutive rx config_regs - -/* - * SGMII Auto Negotiation State Machine - * Look for configuration /C/ ordered set - * /C/ is Alternating /C1/ and /C2/ - * /C1/: /K28.5/D21.5(0xb5)/Config_Reg - * /C2/: /K28.5/D2.2(0x42)/Config_Reg - * Config Reg: Low High - */ -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; - - -assign an_ability_detect = (rx_config_cnt == 2'd3 && rx_config_reg != 16'h0000 ); -assign link_timer_pulse = (phy_type == PHY_TYPE_SGMII && pulse_1_6ms) || (phy_type == PHY_TYPE_SX && pulse_10ms); - -endmodule diff --git a/src/bin_to_ascii.v b/src/bin_to_ascii.v deleted file mode 100644 index 12464dc..0000000 --- a/src/bin_to_ascii.v +++ /dev/null @@ -1,128 +0,0 @@ -/* - * bin_to_asc.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: Convert binary to ASCII ( 16-bit or 8-bit ) - * - */ - -`timescale 1ns /10ps - -module bin_to_ascii( - input rstn, - input clk, - input width, // 0 for 8-bit, 1 for 16-bit - - // mdio interface - input we_i, // latch the data and start - input [15:0] d_in, // binary in - - // mdio controller interface - output reg run, // block has the output bus - output reg done, // block is done it's processing, use as an internal reset - input busy, // instruct the block to hold off on writing - - // write to output port - output we_o, // asserts we for each ascii word to write out - output reg [6:0] d_out // ascii out -); - - // ascii codes we use - localparam ASCII_ = 7'h5f, - ASCIIq = 7'h3f, - LF = 7'h0a, - CR = 7'h0d; - - - reg [2:0] cnt; // count the nibbles, last two are for CR and LF - reg [3:0] d_bin_in_4; - reg [15:0] d; // internally latched data - - /* start running on we */ - always @(posedge clk or negedge rstn) - begin - if ( !rstn | done ) - run <= 1'b0; - else if ( we_i ) - run <= 1'b1; - end - - /* register the incoming data */ - always @(posedge clk, negedge rstn) - begin - if ( !rstn ) - d <= 16'h0; - else if ( we_i ) - d <= d_in; - end - - /* increment the counter when running, start at 0 or 2 depending on width */ - always @(posedge clk or negedge rstn) - begin - if ( !rstn | done ) - cnt <= 3'd0; - else if ( we_i & ~width ) - cnt <= 3'd2; - else if ( run ) - cnt <= cnt + 1; - end - - always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - done <= 1'b0; - else if ( cnt == 3'd5 ) - done <= 1'b1; - else - done <= 1'b0; - end - - assign we_o = run && ~done; - - /* d_bin_in_4 mux */ - always@(*) - begin - case ( cnt ) - 3'b000: d_bin_in_4 = d[15:12]; - 3'b001: d_bin_in_4 = d[11:8]; - 3'b010: d_bin_in_4 = d[7:4]; - 3'b011: d_bin_in_4 = d[3:0]; - default: d_bin_in_4 = d[3:0]; - endcase - end - - /* perform the conversion */ - always @(*) - begin - d_out = { 3'b011, d_bin_in_4 }; - if ( cnt < 3'd4 && d_bin_in_4 >= 4'd10 ) - case( d_bin_in_4 ) - 4'ha : d_out = 7'h61; - 4'hb : d_out = 7'h62; - 4'hc : d_out = 7'h63; - 4'hd : d_out = 7'h64; - 4'he : d_out = 7'h65; - 4'hf : d_out = 7'h66; - default : d_out = 7'h3f; // question mark - endcase - else if ( cnt == 3'd4 ) - d_out = CR; - else if ( cnt == 3'd5 ) - d_out = LF; - end - -endmodule - @@ -1,7 +1,8 @@ /* * cam.v * - * Copyright (C) 2018, 2019 Mind Chasers Inc. + * Copyright (C) 2025 Private Island Networks Inc. + * Copyright (C) 2018-2022 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. @@ -18,66 +19,55 @@ * function: single cycle, parameterized CAM * */ - -`timescale 1ns /10ps -module cam #(parameter DEPTH = 4, - parameter DEPTHW = 2, - parameter WIDTH = 32) +module cam #(parameter DEPTH = 8, DATAW = 32) ( input rstn, input clk, // input for programming + input prgclk, input sel, input we, - input [DEPTHW+1:0] addr, // add two bits for the byte selects - input [7:0] d_in, + input [$clog2(DEPTH)-1:0] addr, // include two lsbits for the byte selects + input [DATAW-1:0] d_i, + output [DATAW-1:0] d_o, input search, - input [(WIDTH-1):0]search_address, + input [DATAW-1:0] search_address, // output output reg match ); + - reg [(WIDTH-1):0] content[0:DEPTH-1]; - reg [(DEPTH-1):0] valid; + reg [DATAW-1:0] content[0:DEPTH-1]; + reg valid[0:DEPTH-1]; integer i,j; // Program the CAM - always @(posedge clk, negedge rstn) + always @(posedge prgclk, negedge rstn) if( !rstn ) begin for (i=0; i < DEPTH; i=i+1) begin - content[i] <= 32'h0; + content[i] <= 'd0; valid[i] <= 1'b0; end end else if ( we && sel ) - if (addr[1:0] == 2'b00) begin - content[addr[DEPTHW+1:2]][7:0] <= d_in; - valid[addr[DEPTHW+1:2]] <= 1'b1; - end - else if (addr[1:0] == 2'b01) begin - content[addr[DEPTHW+1:2]][15:8] <= d_in; - valid[addr[DEPTHW+1:2]] <= 1'b1; - end - else if (addr[1:0] == 2'b10) begin - content[addr[DEPTHW+1:2]][23:16] <= d_in; - valid[addr[DEPTHW+1:2]] <= 1'b1; - end - else if (addr[1:0] == 2'b11) begin - content[addr[DEPTHW+1:2]][31:24] <= d_in; - valid[addr[DEPTHW+1:2]] <= 1'b1; + begin + content[addr] <= d_i; + valid[addr] <= 1'b1; end + assign d_o = valid[addr] ? content[addr] : 'd0; + + // search the CAM always @(posedge clk) begin match <= 1'b0; for (j=0; j < DEPTH; j=j+1) begin if (search && valid[j] && search_address == content[j]) match <= 1'b1; - end end diff --git a/src/clk_gen.v b/src/clk_gen.v deleted file mode 100644 index 9d3e054..0000000 --- a/src/clk_gen.v +++ /dev/null @@ -1,67 +0,0 @@ -/* - * clk_gen.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: Generate system / controller clocks from internal oscillator - * - */ - -`timescale 1ns /10ps - -module clk_gen( - input rstn, - output clk_10, - output clk_5, - output clk_2_5, - output clk_1_25, - output clk_slow -); - - - wire clk; - reg [8:0] clk_cnt; - wire clk_0_625, clk_0_3125, clk_75K, clk_37_5K, clk_150K; - - /* - +/- 15% variation in output frequency - 128 is the default for a 2.5 MHz clock, +/- 15% - 32 is 9.7 MHz - 16 for 19.4 MHz. - */ - OSCG oscg_inst(.OSC(clk)); - defparam oscg_inst.DIV = 32; - - always @ (posedge clk or negedge rstn) - begin - if ( !rstn ) - clk_cnt <= 0; - else - clk_cnt <= clk_cnt + 1; - end - - assign clk_10 = clk; // 10 MHz system clock - assign clk_5 = clk_cnt[0]; - assign clk_2_5 = clk_cnt[1]; - assign clk_1_25 = clk_cnt[2]; // 1.22MHz - assign clk_0_625 = clk_cnt[3]; - assign clk_0_3125 = clk_cnt[4]; - assign clk_150K = clk_cnt[5]; - assign clk_75K = clk_cnt[6]; - assign clk_37_5K = clk_cnt[7]; - - assign clk_slow = clk_37_5K; - -endmodule diff --git a/src/cont_params.v b/src/cont_params.v new file mode 100644 index 0000000..cbdddcc --- /dev/null +++ b/src/cont_params.v @@ -0,0 +1,61 @@ +/* + * cont_params.v + * + * Copyright (C) 2024-2025 Private Island Networks Inc. + * Copyright (C) 2018-2023 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: Controller related parameters + * + */ + + +`ifdef INCLUDED + +/* Message Protocol: + * Type: 8-bit (rx_cnt==4) + * Token: 8-bit (rx_cnt==5) + * Address: 16-bit (rx_cnt==6,7) + * Data: 32-bit (rx_cnt==8,9,10,11) + */ + +localparam MSG_SZ = 8'h8; + +// Message Types (16-bit address) +localparam MSG_TYPE_NULL = 8'h0; +localparam MSG_TYPE_WRITE = 8'h1; +localparam MSG_TYPE_READ = 8'h2; +localparam MSG_TYPE_REPLY_SUCCESS = 8'h3; +localparam MSG_TYPE_REPLY_ERROR = 8'h4; +localparam MSG_TYPE_NOTIY = 8'h5; + + +localparam MSG_CONTROLLER_ADDR = 16'h0000; +localparam MSG_MAC_ADDR = 16'h0100; +localparam MSG_PKT_FILTER_ADDR = 16'h0200; +localparam MSG_SWITCH_ADDR = 16'h0500; +localparam MSG_MDIO_ADDR = 16'h0600; +localparam MSG_MLE_ADDR = 16'h0700; +localparam MSG_INVALID_ADDR = 16'h0800; + + +localparam HF_TX_WR_PTR_ADDR = 16'd0; +localparam HF_TX_RD_PTR_ADDR = 16'd1; // read only +localparam HF_TX_BYTE_CNT_ADDR = 16'd1; // write only +localparam HF_RX_WR_PTR_LTCH_ADDR = 16'd2; +localparam HF_NULL = 16'd3; +localparam HF_RESET_PTRS = 16'd4; + + +`endif diff --git a/src/controller.v b/src/controller.v index 54d6976..e30d461 100644 --- a/src/controller.v +++ b/src/controller.v @@ -1,5 +1,5 @@ /* - * controller.v + * controller.v * * Copyright (C) 2023-2025 Private Island Networks Inc. * Copyright (C) 2018-2022 Mind Chasers Inc. @@ -18,8 +18,7 @@ * * function: FPGA internal state machine controller * - * see https://privateisland.tech/dev/pi-doc for further details - * + * see https://privateisland.tech/dev/pi-controller for further details * */ @@ -35,18 +34,20 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) // link status input [NUM_PHYS-1:0] phy_up, - // Memory Controller bus + // Memory Controller I/F output reg mem_we, output mem_oe, output reg [15:0] mem_addr, output reg[31:0] mem_d_o, input [31:0] mem_d_i, + input mem_tgt_ready, // Device selects for Controller peripherals output reg [1:0] mac_addr, output reg [15:0] pkt_filter_addr, output reg pkt_filter_sel, output reg mac_sel, + output reg mle_sel, output reg hf_ptrs_sel, output reg hf_tx_sel, output reg hf_rx_sel, @@ -88,7 +89,7 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) // Version: upper byte is major, lower byte is minor localparam FW_VERSION_VALUE = 16'h0001; // Version Increment: Set to 0 when releasing version; otherwise, increment value for each preliminary / trial release - localparam FW_INCREMENT_VALUE = 16'h0002; + localparam FW_INCREMENT_VALUE = 16'h0007; // Main Controller states localparam CONT_ST_INIT= 3'h0, CONT_ST_IDLE=3'h1, CONT_ST_START=3'h2, CONT_ST_BUSY=3'h3, @@ -108,13 +109,14 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) localparam MEM_EVENT_NONE=3'h0, MEM_EVENT_FIFO=3'h1; // Controller Address Space - localparam FW_VERSION_ADDR = 16'h0000; - localparam FW_INCREMENT_ADDR = 16'h0004; - localparam MAC_ADDR = 16'h0010; - localparam PHY_ADDR = 16'h0014; // Controls mdio_mux_sel, not the MIDO PHY Address - localparam PKT_FILT_ADDR = 16'h0018; - localparam RX_MSG_CNT_ADDR = 16'h0020; - localparam TX_PKT_CNT_ADDR = 16'h0024; + localparam + FW_VERSION_ADDR = 16'h0000, + FW_INCREMENT_ADDR = 16'h0004, + MAC_ADDR = 16'h0010, + PHY_ADDR = 16'h0014, // Controls mdio_mux_sel, not the MIDO PHY Address + PKT_FILT_ADDR = 16'h0018, + RX_MSG_CNT_ADDR = 16'h0020, + TX_PKT_CNT_ADDR = 16'h0024; /* Define Variables and Nets Below */ reg [2:0] cont_state; @@ -137,7 +139,7 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) // Command Processing reg rx_msg_captured; - reg mdio_cmd, cont_msg; + reg mdio_cmd, mem_cmd, cont_msg; reg [15:0] mdio_d; // MDIO Init @@ -150,10 +152,11 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) reg [10:0] tx_wr_ptr; // write into the HFIFO TX data reg rx_rd_active, tx_wr_active; reg [4:0] rx_cnt, tx_cnt; + reg mem_tgt_ready_m1, mem_tgt_ready_m2; // metrics reg [15:0] rx_msg_cnt; - reg [15:0] tx_pkt_cnt; + reg [15:0] tx_msg_cnt; // Synchronize rx_fifo_int @@ -166,7 +169,18 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) rx_fifo_int_m1 <= rx_fifo_int; rx_fifo_int_m2 <= rx_fifo_int_m1; end - + + // Synchronize mem_tgt_ready + always @(posedge clk, negedge rstn) + if (!rstn) begin + mem_tgt_ready_m1 <= 1'b0; + mem_tgt_ready_m2 <= 1'b0; + end + else begin + mem_tgt_ready_m1 <= mem_tgt_ready; + mem_tgt_ready_m2 <= mem_tgt_ready_m1; + end + // rx_msg_captured signals a message has been received always @(posedge clk or negedge rstn) if (!rstn) @@ -178,7 +192,6 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) // rx_msg_cnt - // TODO: reset counter by writing the register always @(posedge clk or negedge rstn) if (!rstn) rx_msg_cnt <= 16'd0; @@ -203,6 +216,8 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) CONT_ST_BUSY: if (cont_msg || msg_error) cont_state <= CONT_ST_DONE; + else if (mem_cmd && mem_tgt_ready_m2) + cont_state <= CONT_ST_DONE; else if (mdio_cmd && mdio_cont_done) cont_state <= CONT_ST_DONE; CONT_ST_DONE: @@ -217,7 +232,7 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) cont_msg <= 1'b0; else if (cont_state == CONT_ST_IDLE && !rx_msg_captured) cont_msg <= 1'b0; - else if (rx_msg_captured && (msg_addr[15:8] < MSG_MDIO_ADDR[15:8])) + else if (rx_msg_captured && (msg_addr[15:8] == MSG_CONTROLLER_ADDR[15:8])) cont_msg <= 1'b1; /*********************************************************************** @@ -293,7 +308,7 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h9 && !msg_addr_valid) msg_error <= 1'b1; - // msg data on writes + // msg data always @(posedge clk or negedge rstn) if (!rstn) msg_data <= 32'd0; @@ -309,7 +324,6 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) msg_data[7:0] <= mem_d_i[7:0]; // msg_response - // TODO: add other peripherals always @(posedge clk, negedge rstn) if (!rstn) msg_response <= 16'heeee; @@ -317,20 +331,25 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) msg_response <= 16'heeee; else if (mem_state == MEM_ST_RD_FIFO_DONE && cont_state == CONT_ST_DONE) casez (msg_addr) - FW_VERSION_ADDR : msg_response <= FW_VERSION_VALUE; + FW_VERSION_ADDR : msg_response <= FW_VERSION_VALUE; FW_INCREMENT_ADDR : msg_response <= FW_INCREMENT_VALUE; MAC_ADDR: msg_response <= mac_addr; PHY_ADDR: msg_response <= mdio_mux_sel; PKT_FILT_ADDR: msg_response <= pkt_filter_addr; - RX_MSG_CNT_ADDR : msg_response <= rx_msg_cnt; - TX_PKT_CNT_ADDR : msg_response <= tx_pkt_cnt; - {MSG_MDIO_ADDR[15:8],8'h??}: msg_response <= mdio_d; + RX_MSG_CNT_ADDR : msg_response <= rx_msg_cnt; + TX_PKT_CNT_ADDR : msg_response <= tx_msg_cnt; + {MSG_MAC_ADDR[15:8],8'h??}: msg_response <= mem_d_i; + {MSG_MDIO_ADDR[15:8],8'h??}: msg_response <= mdio_d; + {MSG_MLE_ADDR[15:8],8'h??}: msg_response <= mem_d_i; default: msg_response <= mem_d_i[15:0]; endcase - /* - * MDIO controller related - */ + +/*********************************************************************** + * + * MDIO controller related + * + **********************************************************************/ // mdio_cmd address decode always @(posedge clk or negedge rstn) @@ -368,7 +387,7 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) end // set mdio_phy_addr - assign mdio_phy_addr = 5'h0c; // Set phy_addr[3:2] + assign mdio_phy_addr = 5'h00; // Set phy_addr[3:2] // set mdio_reg_addr always @(posedge clk or negedge rstn) @@ -383,20 +402,18 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) /* set mdio_w_data_l and mdio_w_data_h */ always @(posedge clk or negedge rstn) - begin - if (!rstn) begin - mdio_w_data_l <= 'd0; - mdio_w_data_h <= 'd0; - end - else if (mi_state > MI_ST_INIT && mi_state < MI_ST_IDLE) begin - mdio_w_data_h <= mdio_init_w_data_h; - mdio_w_data_l <= mdio_init_w_data_l; - end - else if (mdio_cmd && msg_type == MSG_TYPE_WRITE) begin - mdio_w_data_h <= msg_data[15:8]; - mdio_w_data_l <= msg_data[7:0]; - end - end + if (!rstn) begin + mdio_w_data_l <= 'd0; + mdio_w_data_h <= 'd0; + end + else if (mi_state > MI_ST_INIT && mi_state < MI_ST_IDLE) begin + mdio_w_data_h <= mdio_init_w_data_h; + mdio_w_data_l <= mdio_init_w_data_l; + end + else if (mdio_cmd && msg_type == MSG_TYPE_WRITE) begin + mdio_w_data_h <= msg_data[15:8]; + mdio_w_data_l <= msg_data[7:0]; + end // mdio_d, data from MDIO read cycle always @(posedge clk or negedge rstn) @@ -410,27 +427,21 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) // PHY Reset always @(posedge clk, negedge rstn) if (!rstn) - phy_resetn <= 2'b00; - else if (1'b0) - phy_resetn <= 2'b00; // assert reset + phy_resetn <= 3'b000; else - phy_resetn <= 2'b11; + phy_resetn = 3'b111; // MAC Reset always @(posedge clk, negedge rstn) if (!rstn) - mac_reset <= 2'b11; - else if (1'b0) - mac_reset <= 2'b11; // assert reset + mac_reset <= 3'b111; else - mac_reset <= 2'b00; + mac_reset = 3'b000; /*********************************************************************** * * Memory Controller and HFIFO Access LAN Controller * - * TODO: Review and implement better LAN flow control - * **********************************************************************/ // Memory Controller Tie Offs @@ -468,7 +479,15 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) MEM_ST_DONE: mem_state <= MEM_ST_IDLE; // update tx_wr_ptr default: mem_state <= MEM_ST_IDLE; endcase - + + // mem_cmd address decode + always @(posedge clk or negedge rstn) + if (!rstn) + mem_cmd <= 1'b0; + else if (cont_state == CONT_ST_IDLE && !rx_msg_captured) + mem_cmd <= 1'b0; + else if (rx_msg_captured && msg_addr[15:8] >= MSG_MAC_ADDR[15:8] && msg_addr[15:8] < MSG_MLE_ADDR[15:8]) + mem_cmd <= 1'b1; // Primary Memory Controller Actions always @(posedge clk, negedge rstn) @@ -477,6 +496,7 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) mem_d_o <= 32'd0; mem_addr <= HF_NULL; mac_sel <= 1'b0; + mle_sel <= 1'b0; pkt_filter_sel <= 1'b0; hf_ptrs_sel <= 1'b0; hf_tx_sel <= 1'b0; @@ -513,6 +533,7 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) case (msg_addr[15:8]) MSG_MAC_ADDR[15:8]: mac_sel <= 1'b1; MSG_PKT_FILTER_ADDR[15:8]: pkt_filter_sel <= 1'b1; + MSG_MLE_ADDR[15:8]: mle_sel <= 1'b1; endcase end end @@ -520,6 +541,9 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) hf_tx_sel <= 1'b0; hf_rx_sel <= 1'b0; mem_we <= 1'b0; + mac_sel <= 1'b0; + mle_sel <= 1'b0; + pkt_filter_sel <= 1'b0; end else if (mem_state == MEM_ST_REPLY_START && tx_fifo_empty) begin // write byte cnt hf_ptrs_sel <= 1'b1; @@ -579,8 +603,6 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) mem_we <= 1'b0; mem_d_o <= 32'd0; mem_addr <= HF_NULL; - mac_sel <= 1'b0; - pkt_filter_sel <= 1'b0; hf_ptrs_sel <= 1'b0; hf_tx_sel <= 1'b0; hf_rx_sel <= 1'b0; @@ -606,11 +628,9 @@ module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) always @(posedge clk, negedge rstn) if (!rstn) - tx_pkt_cnt <= 16'd0; + tx_msg_cnt <= 16'd0; else if (mem_state == MEM_ST_DONE) // MSG - tx_pkt_cnt <= tx_pkt_cnt + 1'b1; - - + tx_msg_cnt <= tx_msg_cnt + 1'b1; /*********************************************************************** * diff --git a/src/directives.v b/src/directives.v deleted file mode 100644 index 5760c23..0000000 --- a/src/directives.v +++ /dev/null @@ -1,34 +0,0 @@ -/* - * directives.v - * - * Copyright (C) 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. - * - * Description: misc. directives for board options - * - */ - -// Debug Options for Darsena -//`define DEBUG_I2C -//`define DEBUG_MDIO -//`define DEBUG_SPI -//`define ARD_EXP_UART - -`define DARSENA_V02 -//`define DARSENA_V03 - -// Shield Definition (PORTS 3 & 4) -//`define SHIELD_SMA_SMA -`define SHIELD_GIGE_SMA -//`define SHIELD_SFP_SMA diff --git a/src/dpram.v b/src/dpram.v deleted file mode 100644 index 53c1efc..0000000 --- a/src/dpram.v +++ /dev/null @@ -1,102 +0,0 @@ -/* - * dpram.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: DPRAM wrapper - * - */ - -`timescale 1ns /10ps - - -module dpram( - input rstn, - - // port A - input a_clk, - input a_clk_e, - input a_we, - input a_oe, - input [10:0] a_addr, - input [8:0] a_din, - output [8:0] a_dout, - - // port B - input b_clk, - input b_clk_e, - input b_we, - input b_oe, - input [10:0] b_addr, - input [8:0] b_din, - output [8:0] b_dout -); - - -DP16KD dp16kd_inst( - // input data port A - .DIA17( 1'b0 ), .DIA16( 1'b0 ), .DIA15( 1'b0 ), .DIA14( 1'b0 ), .DIA13( 1'b0 ), - .DIA12( 1'b0 ), .DIA11( 1'b0 ), .DIA10( 1'b0 ), .DIA9( 1'b0 ), .DIA8( a_din[8] ), - .DIA7( a_din[7] ), .DIA6( a_din[6] ), .DIA5( a_din[5] ), .DIA4( a_din[4] ), - .DIA3( a_din[3] ), .DIA2( a_din[2] ), .DIA1( a_din[1] ), .DIA0( a_din[0] ), - - // input address bus port A - .ADA13( a_addr[10] ), .ADA12( a_addr[9] ), - .ADA11( a_addr[8] ), .ADA10( a_addr[7]), .ADA9( a_addr[6] ), .ADA8( a_addr[5] ), - .ADA7( a_addr[4] ), .ADA6( a_addr[3] ), .ADA5( a_addr[2] ), .ADA4( a_addr[1] ), .ADA3( a_addr[0] ), - .ADA2( 1'b0 ), .ADA1( 1'b0 ), .ADA0( 1'b0 ), - - .CEA( a_clk_e ), // clock enable - .OCEA( a_oe ), // output clock enable - .CLKA( a_clk ), // clock for port A - .WEA( a_we ), // write enable - .CSA2( 1'b0 ), .CSA1( 1'b0 ), .CSA0( 1'b0 ), // chip selects - .RSTA( ~rstn ), // reset for port A - - // outputs - .DOA17(), .DOA16(), .DOA15(), .DOA14(), .DOA13(), .DOA12(), .DOA11(), .DOA10(), .DOA9( ), - .DOA8( a_dout[8] ), .DOA7( a_dout[7] ), .DOA6( a_dout[6] ), .DOA5( a_dout[5] ), .DOA4( a_dout[4] ), - .DOA3( a_dout[3] ), .DOA2( a_dout[2] ), .DOA1( a_dout[1] ), .DOA0( a_dout[0] ), - - // input data port B - .DIB17( 1'b0 ), .DIB16( 1'b0 ), .DIB15( 1'b0 ), .DIB14( 1'b0 ), .DIB13( 1'b0 ), - .DIB12( 1'b0 ), .DIB11( 1'b0 ), .DIB10( 1'b0 ), .DIB9( 1'b0 ), - .DIB8( b_din[8] ), .DIB7( b_din[7] ), .DIB6( b_din[6] ), .DIB5( b_din[5] ), - .DIB4( b_din[4] ), .DIB3( b_din[3] ), .DIB2( b_din[2] ), .DIB1( b_din[1] ), .DIB0( b_din[0] ), - - // input address bus port B - .ADB13( b_addr[10] ), .ADB12( b_addr[9] ), .ADB11( b_addr[8] ), .ADB10( b_addr[7] ), .ADB9( b_addr[6] ), - .ADB8( b_addr[5] ), .ADB7( b_addr[4] ), .ADB6( b_addr[3] ), .ADB5( b_addr[2] ), - .ADB4( b_addr[1] ), .ADB3( b_addr[0] ), .ADB2( 1'b0 ), .ADB1( 1'b0 ), .ADB0( 1'b0 ), - - .CEB( b_clk_e ), // clock enable - .OCEB( b_oe ), // output clock enable - .CLKB( b_clk ), // clock for port B - .WEB( b_we ), // write enable - .CSB2( 1'b0 ), .CSB1( 1'b0 ), .CSB0( 1'b0 ), // chip selects - .RSTB( ~rstn ), // reset for port B - - // outputs - - .DOB17(), .DOB16(), .DOB15(), .DOB14(), .DOB13(), .DOB12(), .DOB11(), .DOB10(), .DOB9( ), - .DOB8( b_dout[8] ), .DOB7( b_dout[7] ), .DOB6( b_dout[6] ), .DOB5( b_dout[5] ), - .DOB4( b_dout[4] ), .DOB3( b_dout[3] ), .DOB2( b_dout[2] ), .DOB1( b_dout[1] ), .DOB0( b_dout[0] ) - -); -// defparam dp16kd_inst.INITVAL_00 = "0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcaa9980123456789abcde"; -defparam dp16kd_inst.DATA_WIDTH_A = 9; -defparam dp16kd_inst.DATA_WIDTH_B = 9; - -endmodule diff --git a/src/dpram_inf.v b/src/dpram_inf.v new file mode 100644 index 0000000..5112dfb --- /dev/null +++ b/src/dpram_inf.v @@ -0,0 +1,84 @@ +/* + * dpram_inf.v + * + * Copyright (C) 2024-2025 Private Island Networks 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. + * + * Inferred DPRAM + * +*/ + +module dpram_inf +#(parameter DATA_WIDTH=9, parameter ADDR_WIDTH=11, parameter DPRAM_INIT="zero.txt") +( + input rstn, + // PORT A + input a_clk, + input a_clk_e, + input a_we, + input a_oe, + input [(ADDR_WIDTH-1):0] a_addr, + input [(DATA_WIDTH-1):0] a_din, + output reg [(DATA_WIDTH-1):0] a_dout, + // PORT A + input b_clk, + input b_clk_e, + input b_we, + input b_oe, + input [(ADDR_WIDTH-1):0] b_addr, + input [(DATA_WIDTH-1):0] b_din, + output reg [(DATA_WIDTH-1):0] b_dout +); + + `ifdef SIMULATION + localparam DPRAM_FILE = {"../../",DPRAM_INIT}; + `else + localparam DPRAM_FILE = DPRAM_INIT; + `endif + + // Declare the RAM variable + reg [11:0] ram[2**ADDR_WIDTH-1:0]; + initial begin + $readmemh(DPRAM_FILE, ram); + end + + always @ (posedge a_clk) + begin + // Port A + if (a_clk_e && a_we) + begin + ram[a_addr] <= a_din; + a_dout <= a_din; + end + else + begin + a_dout <= ram[a_addr][(DATA_WIDTH-1):0]; + end + end + + always @ (posedge b_clk) + begin + // Port B + if (b_clk_e && b_we) + begin + ram[b_addr] <= b_din; + b_dout <= b_din; + end + else + begin + b_dout <= ram[b_addr][(DATA_WIDTH-1):0]; + end + end + +endmodule diff --git a/src/drop_fifo.v b/src/drop_fifo.v index 736947c..faefac0 100644 --- a/src/drop_fifo.v +++ b/src/drop_fifo.v @@ -1,8 +1,8 @@ /* * drop_fifo.v * - * Copyright 2018, 2019, 2020, 2021 Mind Chasers Inc. - * Copyright 2023 Private Island Networks Inc. + * Copyright (C) 2023-2025 Private Island Networks Inc. + * Copyright (C) 2018-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. @@ -23,154 +23,200 @@ */ module drop_fifo( - input rstn, - input clk, - input passthrough, // don't store / delay data, write through instead - input enable, // enable the FIFO - input keep, // this packet won't be dropped, start transferring it (writer must be sure FIFO won't run dry during writes) - - // input data - input we_in, - input [8:0] d_in, - input wr_done, // keep should occur before wr_done is asserted. - input rx_eop, - - // output data - output reg we_out, - output reg [8:0] d_out, - - // debug - output reg active + input rstn, + input phy_up, // Use the associated PHY state to gate write activity into the DF + input rx_clk, + input tx_clk, + input passthrough, // don't store / delay data, write through instead + input enable, // enable the FIFO + input keep, // this packet won't be dropped, start transferring it (writer must be sure FIFO won't run dry during writes) + + // input data + input we_in, + input [8:0] d_in, + input wr_done, // keep should occur before wr_done is asserted. + input rx_idle, // Use to clean up states and pointers + input rx_error, + + // output data + output reg we_out, // assert when data is available for writing to the next stage + output reg [8:0] d_out, + + // debug + output reg active ); - -// local parameters and includes - - -// nets and registers -reg [10:0] wr_ptr0, wr_ptr1; // wr_ptr0 is updated on each write; wr_ptr1 is updated at end of packet -reg [10:0] rd_ptr; -wire [8:0] d_out_internal; -reg read_run, read_run_m1, read_run_m2; // read continues while read_run is set -reg kept; -wire fifo_empty; - -/* - * kept: assert for length of write duration after keep asserts (when enable is active) - */ -always @(posedge clk, negedge rstn) - if( !rstn ) - kept <= 1'b0; - else if ( wr_done ) - kept <= 1'b0; - else if ( keep && enable ) - kept <= 1'b1; -/* - * wr_ptr0 logic - */ -always @(posedge clk, negedge rstn) - if( !rstn ) - wr_ptr0 <= 'd0; - else if ( we_in ) - wr_ptr0 <= wr_ptr0 + 1'b1; + // local parameters and includes + localparam SZ_DPRAM = 16'd2048; -/* - * wr_ptr1 logic - * sync pointers at end of rx packet for next packet -*/ -always @(posedge clk, negedge rstn) - if( !rstn ) - wr_ptr1 <= 'd0; - else if ( wr_done ) - wr_ptr1 <= wr_ptr0 +1; // a write takes place with wr_done -/* - * read_run logic - * continues until the FIFO is empty - */ -always @(posedge clk, negedge rstn) - if( !rstn ) - read_run <= 1'b0; - else if (kept && wr_done) - read_run <= 1'b1; - else if ( fifo_empty ) - read_run <= 1'b0; - - -always @(posedge clk, negedge rstn) - if( !rstn ) - read_run_m1 <= 1'b0; - else - read_run_m1 <= read_run; + // nets and registers + reg [10:0] wr_ptr0, wr_ptr1; // wr_ptr0 is updated on each write; wr_ptr1 is updated at end of packet + reg [10:0] rd_ptr; + wire [8:0] d_out_internal; + reg read_run, read_run_m1, read_run_m2; // read continues while read_run is set + reg wr_done_m1, wr_done_m2, wr_done_m3; // CDC flag + reg rx_idle_m1, rx_idle_m2; + reg kept; + wire fifo_empty; + reg [15:0] df_bytes; + + /* + * kept: assert for length of write duration after keep asserts (when enable is active) + */ + always @(posedge rx_clk, negedge rstn) + if(!rstn) + kept <= 1'b0; + else if (read_run_m2) // CDC flag that is asserted for multiple clocks + kept <= 1'b0; + else if (phy_up && keep && enable) + kept <= 1'b1; + + /* + * wr_ptr0 logic + */ + always @(posedge rx_clk, negedge rstn) + if(!rstn) + wr_ptr0 <= 'd0; + else if (phy_up && we_in) + wr_ptr0 <= wr_ptr0 + 1'b1; + + + // convert clock domain for rx_idle + always @(posedge tx_clk, negedge rstn) + if(!rstn) begin + rx_idle_m1 <= 1'b0; + rx_idle_m2 <= 1'b0; + end + else begin + rx_idle_m1 <= rx_idle; + rx_idle_m2 <= rx_idle_m1; + end + + // convert clock domain for wr_done + always @(posedge tx_clk, negedge rstn) + if(!rstn) begin + wr_done_m1 <= 1'b0; + wr_done_m2 <= 1'b0; + wr_done_m3 <= 1'b0; + end + else begin + wr_done_m1 <= wr_done; + wr_done_m2 <= wr_done_m1; + wr_done_m3 <= wr_done_m2; + end + + /* + * wr_ptr1 logic + * sync pointers at end of RX packet + */ + always @(posedge tx_clk, negedge rstn) + if(!rstn) + wr_ptr1 <= 'd0; + else if (phy_up && wr_done_m3) + wr_ptr1 <= wr_ptr0; // + else if (rx_idle_m2 && !read_run) + wr_ptr1 <= wr_ptr0; // final clean up + + /* + * read_run logic + * continues until the FIFO is empty + */ + always @(posedge tx_clk, negedge rstn) + if(!rstn) + read_run <= 1'b0; + else if (phy_up && kept && wr_done_m3) + read_run <= 1'b1; + else if (fifo_empty) + read_run <= 1'b0; -always @(posedge clk, negedge rstn) - if( !rstn ) - read_run_m2 <= 1'b0; - else - read_run_m2 <= read_run_m1; - -/* -* rd_ptr logic -*/ -always @(posedge clk, negedge rstn) - if( !rstn ) - rd_ptr <= 'd0; - else if (kept && wr_done) - rd_ptr <= wr_ptr1; // set rd_ptr to the beginning of where the write started - else if (read_run && !fifo_empty) - rd_ptr <= rd_ptr+ 1'b1; + always @(posedge tx_clk, negedge rstn) + if(!rstn) begin + read_run_m1 <= 1'b0; + read_run_m2 <= 1'b0; + end + else begin + read_run_m1 <= read_run; + read_run_m2 <= read_run_m1; + end -/* - * we_out logic - */ -always @(posedge clk, negedge rstn) - if( !rstn ) - we_out <= 1'b0; - else if ( read_run_m1 && read_run_m2 ) - we_out <= 1'b1; - else - we_out <= 1'b0; - -assign fifo_empty = (wr_ptr1 == rd_ptr); + /* + * rd_ptr logic + */ + always @(posedge tx_clk, negedge rstn) + if(!rstn) + rd_ptr <= 'd0; + else if (phy_up && kept && wr_done_m2) + rd_ptr <= wr_ptr1; // set rd_ptr to the beginning of where the write started + else if (read_run && !fifo_empty) + rd_ptr <= rd_ptr+ 1'b1; -/* - * d_out register - * - */ -always @(posedge clk, negedge rstn) - if( !rstn ) - d_out <= 'd0; - else - d_out <= d_out_internal; + /* + * we_out logic + */ + always @(posedge tx_clk, negedge rstn) + if(!rstn) + we_out <= 1'b0; + else if (read_run && read_run_m1) + we_out <= 1'b1; + else + we_out <= 1'b0; -always @(posedge clk, negedge rstn) - if( !rstn ) - active <= 1'b0; - else if ( we_in || we_out ) - active <= 1'b1; - else - active <= 1'b0; - -dpram dpram_fifo( -.rstn( rstn ), -.a_clk( clk ), -.a_clk_e( 1'b1 ), -.a_we( we_in ), -.a_oe( 1'b0 ), -.a_addr( wr_ptr0 ), -.a_din( d_in ), -.a_dout( ), -// port B -.b_clk( clk ), -.b_clk_e( read_run ), -.b_we( 1'b0 ), -.b_oe( 1'b1 ), -.b_addr( rd_ptr ), -.b_din( 9'h0 ), -.b_dout( d_out_internal ) -); - + assign fifo_empty = (wr_ptr1 == rd_ptr); + + /* + * d_out register + * + */ + always @(posedge tx_clk, negedge rstn) + if(!rstn) + d_out <= 'd0; + else + d_out <= d_out_internal; + + // debug flag + always @(posedge tx_clk, negedge rstn) + if(!rstn) + active <= 1'b0; + else if (we_in || we_out) + active <= 1'b1; + else + active <= 1'b0; + + /* + * debug: bytes in DF + */ + always @(posedge rx_clk, negedge rstn) + if(!rstn) + df_bytes <= 'd0; + else if (! phy_up) + df_bytes <= 'd0; + else + if (wr_ptr1 <= wr_ptr0) + df_bytes <= (wr_ptr0 + 1'b1) - wr_ptr1; + else + df_bytes <= SZ_DPRAM + (wr_ptr0+1'b1) - wr_ptr1; + + + dpram_inf dpram_fifo( + .rstn(rstn), + .a_clk(rx_clk), + .a_clk_e(1'b1), + .a_we(we_in), + .a_oe(1'b0), + .a_addr(wr_ptr0), + .a_din(d_in), + .a_dout(), + // port B + .b_clk(tx_clk), + .b_clk_e(read_run), + .b_we(1'b0), + .b_oe(1'b1), + .b_addr(rd_ptr), + .b_din(9'h0), + .b_dout(d_out_internal) + ); endmodule diff --git a/src/ethernet_params.v b/src/ethernet_params.v index dcb7dba..406f46f 100644 --- a/src/ethernet_params.v +++ b/src/ethernet_params.v @@ -1,7 +1,8 @@ /* - * ethernet_params.v + * ethernet_params.v * - * Copyright (C) 2018, 2019, 2020 Mind Chasers Inc. + * Copyright (C) 2024-2025 Private Island Networks Inc. + * Copyright (C) 2018-2023 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. @@ -18,22 +19,37 @@ * function: Ethernet related parameters * */ + + +`ifdef INCLUDED +localparam TX_SRC_SEL_PHY0 = 3'b000, + TX_SRC_SEL_PHY1 = 3'b001, + TX_SRC_SEL_PHY2 = 3'b010, + TX_SRC_SEL_MLE = 3'b110, + TX_SRC_SEL_UC = 3'b111; + +localparam PHY_TYPE_SGMII = 2'b00, + PHY_TYPE_RGMII = 2'b01, + PHY_TYPE_1000BASE_X = 2'b10, + PHY_TYPE_OTHER = 2'b11; localparam MTU = 1520; // Ethernet is actually 1500+framing (max 18) localparam IPG = 96; // Inter-packet Gap Bits -localparam SZ_ETH_HEADER = 14; // w/o VLAN -localparam SZ_IPV4_HEADER = 20; // w/o Options +localparam SZ_ETH_HEADER = 14; // MAC SRC, DEST, ETHER TYPE, w/o VLAN +localparam SZ_IPV4_HEADER = 20; // w/o Options localparam SZ_UDP_HEADER = 8; localparam TX_MODE_AN = 3'b000, -TX_MODE_IDLE = 3'b001, -TX_MODE_XMT_PKT = 3'b010, // anything >= to this is a mode where a packet is transmitted -TX_MODE_XMT_METRICS = 3'b011, -TX_MODE_XMT_CUSTOM = 3'b100; + TX_MODE_IDLE = 3'b001, + TX_MODE_XMT_PKT = 3'b010, // anything >= to this is a mode where a packet is transmitted + TX_MODE_XMT_METRICS = 3'b011, + TX_MODE_XMT_CUSTOM = 3'b100; // Note: The Length/Type field is transmitted and received with the high order octet first. localparam ETHER_TYPE_IPV4 = 16'h0800, -ETHER_TYPE_IPV6 = 16'h86DD, -ETHER_TYPE_ARP = 16'h0806; + ETHER_TYPE_IPV6 = 16'h86DD, + ETHER_TYPE_ARP = 16'h0806; + +`endif @@ -1,7 +1,7 @@ /* - * fcs.v - * - * Copyright (C) 2018, 2019 Mind Chasers Inc. + * fcs.v + * Copyright (C) 2025 Private Island Networks Inc. + * 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. @@ -19,7 +19,6 @@ * * */ -`timescale 1ns /10ps //////////////////////////////////////////////////////////////////////////////// // Copyright (C) 1999-2008 Easics NV. @@ -65,78 +64,78 @@ module fcs( ); -reg [31:0] crc; -reg [7:0] d; - -assign dout = ~{ d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7] }; - -always @(*) - begin - case(addr) - 2'b00: d <= crc[31:24]; - 2'b01: d <= crc[23:16]; - 2'b10: d <= crc[15:8]; - 2'b11: d <= crc[7:0]; - endcase - end - -always @(posedge clk or negedge rstn) - if(!rstn) - crc <= 32'hffffffff; - else if (init) - crc <= 32'hffffffff; - else if (enable) - crc <= nextCRC32_D8(din,crc); - -// {d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7]} - - // polynomial: x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 - // data width: 8 - // convention: the first serial bit is D[7] - function [31:0] nextCRC32_D8; - - input [0:7] Data; - input [31:0] crc; - reg [0:7] d; - reg [31:0] c; - reg [31:0] newcrc; - begin - d = Data; - c = crc; - - newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30]; - newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31]; - newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31]; - newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31]; - newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30]; - newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31]; - newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31]; - newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31]; - newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28]; - newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29]; - newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29]; - newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28]; - newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30]; - newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31]; - newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31]; - newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31]; - newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29]; - newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30]; - newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31]; - newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31]; - newcrc[20] = d[4] ^ c[12] ^ c[28]; - newcrc[21] = d[5] ^ c[13] ^ c[29]; - newcrc[22] = d[0] ^ c[14] ^ c[24]; - newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30]; - newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31]; - newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27]; - newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30]; - newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31]; - newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30]; - newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31]; - newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31]; - newcrc[31] = d[5] ^ c[23] ^ c[29]; - nextCRC32_D8 = newcrc; - end - endfunction + reg [31:0] crc; + reg [7:0] d; + + assign dout = ~{ d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7] }; + + always @(*) + begin + case(addr) + 2'b00: d <= crc[31:24]; + 2'b01: d <= crc[23:16]; + 2'b10: d <= crc[15:8]; + 2'b11: d <= crc[7:0]; + endcase + end + + always @(posedge clk or negedge rstn) + if(!rstn) + crc <= 32'hffffffff; + else if (init) + crc <= 32'hffffffff; + else if (enable) + crc <= nextCRC32_D8(din,crc); + + // {d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7]} + + // polynomial: x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + // data width: 8 + // convention: the first serial bit is D[7] + function [31:0] nextCRC32_D8; + + input [0:7] Data; + input [31:0] crc; + reg [0:7] d; + reg [31:0] c; + reg [31:0] newcrc; + begin + d = Data; + c = crc; + + newcrc[0] = d[6] ^ d[0] ^ c[24] ^ c[30]; + newcrc[1] = d[7] ^ d[6] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[30] ^ c[31]; + newcrc[2] = d[7] ^ d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[26] ^ c[30] ^ c[31]; + newcrc[3] = d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[27] ^ c[31]; + newcrc[4] = d[6] ^ d[4] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[28] ^ c[30]; + newcrc[5] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28] ^ c[29] ^ c[30] ^ c[31]; + newcrc[6] = d[7] ^ d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30] ^ c[31]; + newcrc[7] = d[7] ^ d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[24] ^ c[26] ^ c[27] ^ c[29] ^ c[31]; + newcrc[8] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[0] ^ c[24] ^ c[25] ^ c[27] ^ c[28]; + newcrc[9] = d[5] ^ d[4] ^ d[2] ^ d[1] ^ c[1] ^ c[25] ^ c[26] ^ c[28] ^ c[29]; + newcrc[10] = d[5] ^ d[3] ^ d[2] ^ d[0] ^ c[2] ^ c[24] ^ c[26] ^ c[27] ^ c[29]; + newcrc[11] = d[4] ^ d[3] ^ d[1] ^ d[0] ^ c[3] ^ c[24] ^ c[25] ^ c[27] ^ c[28]; + newcrc[12] = d[6] ^ d[5] ^ d[4] ^ d[2] ^ d[1] ^ d[0] ^ c[4] ^ c[24] ^ c[25] ^ c[26] ^ c[28] ^ c[29] ^ c[30]; + newcrc[13] = d[7] ^ d[6] ^ d[5] ^ d[3] ^ d[2] ^ d[1] ^ c[5] ^ c[25] ^ c[26] ^ c[27] ^ c[29] ^ c[30] ^ c[31]; + newcrc[14] = d[7] ^ d[6] ^ d[4] ^ d[3] ^ d[2] ^ c[6] ^ c[26] ^ c[27] ^ c[28] ^ c[30] ^ c[31]; + newcrc[15] = d[7] ^ d[5] ^ d[4] ^ d[3] ^ c[7] ^ c[27] ^ c[28] ^ c[29] ^ c[31]; + newcrc[16] = d[5] ^ d[4] ^ d[0] ^ c[8] ^ c[24] ^ c[28] ^ c[29]; + newcrc[17] = d[6] ^ d[5] ^ d[1] ^ c[9] ^ c[25] ^ c[29] ^ c[30]; + newcrc[18] = d[7] ^ d[6] ^ d[2] ^ c[10] ^ c[26] ^ c[30] ^ c[31]; + newcrc[19] = d[7] ^ d[3] ^ c[11] ^ c[27] ^ c[31]; + newcrc[20] = d[4] ^ c[12] ^ c[28]; + newcrc[21] = d[5] ^ c[13] ^ c[29]; + newcrc[22] = d[0] ^ c[14] ^ c[24]; + newcrc[23] = d[6] ^ d[1] ^ d[0] ^ c[15] ^ c[24] ^ c[25] ^ c[30]; + newcrc[24] = d[7] ^ d[2] ^ d[1] ^ c[16] ^ c[25] ^ c[26] ^ c[31]; + newcrc[25] = d[3] ^ d[2] ^ c[17] ^ c[26] ^ c[27]; + newcrc[26] = d[6] ^ d[4] ^ d[3] ^ d[0] ^ c[18] ^ c[24] ^ c[27] ^ c[28] ^ c[30]; + newcrc[27] = d[7] ^ d[5] ^ d[4] ^ d[1] ^ c[19] ^ c[25] ^ c[28] ^ c[29] ^ c[31]; + newcrc[28] = d[6] ^ d[5] ^ d[2] ^ c[20] ^ c[26] ^ c[29] ^ c[30]; + newcrc[29] = d[7] ^ d[6] ^ d[3] ^ c[21] ^ c[27] ^ c[30] ^ c[31]; + newcrc[30] = d[7] ^ d[4] ^ c[22] ^ c[28] ^ c[31]; + newcrc[31] = d[5] ^ c[23] ^ c[29]; + nextCRC32_D8 = newcrc; + end + endfunction endmodule diff --git a/src/half_fifo.v b/src/half_fifo.v index c85f6ac..fb02176 100644 --- a/src/half_fifo.v +++ b/src/half_fifo.v @@ -50,170 +50,169 @@ module half_fifo #(parameter DATA_WIDTH = 9, DPRAM_DEPTH=11) output reg [10:0] tx_byte_cnt, output [DATA_WIDTH-1:0] fifo_d_out, output tx_empty - ); `define INCLUDED `include "cont_params.v" `undef INCLUDED -/* - * Pointers for accessing memory. - * UC writes TX and reads RX - * Switch writes (via FIFO I/F) RX and reads TX - * This is the opposite of the system view - */ -reg [DPRAM_DEPTH-1:0] rx_wr_ptr, rx_wr_ptr_latched; - -reg [DPRAM_DEPTH-1:0] tx_wr_ptr, tx_wr_ptr_latched; -reg [DPRAM_DEPTH-1:0] tx_rd_ptr; - -reg tx_wr_ptr_written, tx_wr_ptr_written_m1, tx_wr_ptr_written_m2; - -reg fifo_we_m1; - -reg reset_ptrs; -wire [DATA_WIDTH-1:0] dpram_tx_dout, dpram_rx_dout; - -/* read data mux, refer to addr params above */ -always @(*) - casez({ dpram_tx_sel, dpram_rx_sel, dpram_ptrs_sel, dpram_addr[2:0] }) - 6'b001000: dpram_dout = tx_wr_ptr; - 6'b001001: dpram_dout = tx_rd_ptr; // meta stable - 6'b001010: dpram_dout = rx_wr_ptr_latched; - 6'b001011: dpram_dout = 11'd0; - 6'b001100: dpram_dout = {10'd0, reset_ptrs}; - 6'b010???: dpram_dout = {2'b00, dpram_rx_dout}; - 6'b100???: dpram_dout = {2'b00, dpram_tx_dout}; - default: dpram_dout = {2'b00, dpram_rx_dout}; - endcase + /* + * Pointers for accessing memory. + * UC writes TX and reads RX + * Switch writes (via FIFO I/F) RX and reads TX + * This is the opposite of the system view + */ + reg [DPRAM_DEPTH-1:0] rx_wr_ptr, rx_wr_ptr_latched; -always @(posedge uc_clk, negedge rstn) - if (!rstn) - reset_ptrs <= 1'b0; - else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_RESET_PTRS) - reset_ptrs <= dpram_din[0]; + reg [DPRAM_DEPTH-1:0] tx_wr_ptr, tx_wr_ptr_latched; + reg [DPRAM_DEPTH-1:0] tx_rd_ptr; -always @(posedge uc_clk, negedge rstn) - if (!rstn) - tx_byte_cnt <= 11'd0; - else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_TX_BYTE_CNT_ADDR) - tx_byte_cnt <= dpram_din; - -/* TX wr ptr (UC writes) */ -always @(posedge uc_clk, negedge rstn) - if (!rstn) begin - tx_wr_ptr <= 'h0; - tx_wr_ptr_written <= 1'b0; - end - else if (reset_ptrs) begin - tx_wr_ptr <= 'h0; - tx_wr_ptr_written <= 1'b1; - end - else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_TX_WR_PTR_ADDR) begin - tx_wr_ptr <= dpram_din; - tx_wr_ptr_written <= 1'b1; - end - else - tx_wr_ptr_written <= 1'b0; + reg tx_wr_ptr_written, tx_wr_ptr_written_m1, tx_wr_ptr_written_m2; -always @(posedge fifo_clk, negedge rstn) - if (!rstn) begin - tx_wr_ptr_written_m1 <= 1'b0; - tx_wr_ptr_written_m2 <= 1'b0; - end - else begin - tx_wr_ptr_written_m1 <= tx_wr_ptr_written; - tx_wr_ptr_written_m2 <= tx_wr_ptr_written_m1; - end + reg fifo_we_m1; + reg reset_ptrs; + wire [DATA_WIDTH-1:0] dpram_tx_dout, dpram_rx_dout; -always @(posedge fifo_clk, negedge rstn) - if(!rstn) - tx_wr_ptr_latched <= 8'h00; - else if (tx_wr_ptr_written_m2) - tx_wr_ptr_latched <= tx_wr_ptr; + /* read data mux, refer to addr params above */ + always @(*) + casez({ dpram_tx_sel, dpram_rx_sel, dpram_ptrs_sel, dpram_addr[2:0] }) + 6'b001000: dpram_dout = tx_wr_ptr; + 6'b001001: dpram_dout = tx_rd_ptr; // meta stable + 6'b001010: dpram_dout = rx_wr_ptr_latched; + 6'b001011: dpram_dout = 11'd0; + 6'b001100: dpram_dout = {10'd0, reset_ptrs}; + 6'b010???: dpram_dout = {2'b00, dpram_rx_dout}; + 6'b100???: dpram_dout = {2'b00, dpram_tx_dout}; + default: dpram_dout = {2'b00, dpram_rx_dout}; + endcase + + always @(posedge uc_clk, negedge rstn) + if (!rstn) + reset_ptrs <= 1'b0; + else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_RESET_PTRS) + reset_ptrs <= dpram_din[0]; + + always @(posedge uc_clk, negedge rstn) + if (!rstn) + tx_byte_cnt <= 11'd0; + else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_TX_BYTE_CNT_ADDR) + tx_byte_cnt <= dpram_din; -/* TX rd ptr (switch reads via FIFO), auto increment */ -always @(posedge fifo_clk, negedge rstn) - if(!rstn) - tx_rd_ptr <= 11'd0; - else if (reset_ptrs) - tx_rd_ptr <= 11'd0; - else if (fifo_re && !tx_empty) // advance the pointer if FIFO isn't empty - tx_rd_ptr <= tx_rd_ptr + 1'b1; - -/* RX wr ptr (switch writes via FIFO) */ -always @(posedge fifo_clk, negedge rstn) - if(!rstn) - rx_wr_ptr <= 11'd0; - else if (reset_ptrs) - rx_wr_ptr <= 11'd0; - else if (fifo_we) - rx_wr_ptr <= rx_wr_ptr + 1'b1; + /* TX wr ptr (UC writes) */ + always @(posedge uc_clk, negedge rstn) + if (!rstn) begin + tx_wr_ptr <= 'h0; + tx_wr_ptr_written <= 1'b0; + end + else if (reset_ptrs) begin + tx_wr_ptr <= 'h0; + tx_wr_ptr_written <= 1'b1; + end + else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_TX_WR_PTR_ADDR) begin + tx_wr_ptr <= dpram_din; + tx_wr_ptr_written <= 1'b1; + end + else + tx_wr_ptr_written <= 1'b0; + + always @(posedge fifo_clk, negedge rstn) + if (!rstn) begin + tx_wr_ptr_written_m1 <= 1'b0; + tx_wr_ptr_written_m2 <= 1'b0; + end + else begin + tx_wr_ptr_written_m1 <= tx_wr_ptr_written; + tx_wr_ptr_written_m2 <= tx_wr_ptr_written_m1; + end + + + always @(posedge fifo_clk, negedge rstn) + if(!rstn) + tx_wr_ptr_latched <= 8'h00; + else if (tx_wr_ptr_written_m2) + tx_wr_ptr_latched <= tx_wr_ptr; + + /* TX rd ptr (switch reads via FIFO), auto increment */ + always @(posedge fifo_clk, negedge rstn) + if(!rstn) + tx_rd_ptr <= 11'd0; + else if (reset_ptrs) + tx_rd_ptr <= 11'd0; + else if (fifo_re && !tx_empty) // advance the pointer if FIFO isn't empty + tx_rd_ptr <= tx_rd_ptr + 1'b1; -assign tx_empty = (tx_rd_ptr == tx_wr_ptr_latched); - -/* Assert interrupt whenever fifo_we is negated (writing is done) */ -always @(posedge fifo_clk, negedge rstn) - if (!rstn) - fifo_we_m1 <= 1'b0; - else - fifo_we_m1 <= fifo_we; + /* RX wr ptr (switch writes via FIFO) */ + always @(posedge fifo_clk, negedge rstn) + if(!rstn) + rx_wr_ptr <= 11'd0; + else if (reset_ptrs) + rx_wr_ptr <= 11'd0; + else if (fifo_we) + rx_wr_ptr <= rx_wr_ptr + 1'b1; + + assign tx_empty = (tx_rd_ptr == tx_wr_ptr_latched); -// Metastability note: rx_wr_ptr_latched should be read one or more clock cycles after rx_fifo_int asserts -always @(posedge fifo_clk, negedge rstn) - if (!rstn) begin - rx_fifo_int <= 1'b0; - rx_wr_ptr_latched <= 11'd0; - end - else if (!fifo_we & fifo_we_m1) begin // end of write - rx_fifo_int <= 1'b1; - rx_wr_ptr_latched <= rx_wr_ptr; - end - else if (rx_fifo_int_acked) // clear interrupt - rx_fifo_int <= 1'b0; + /* Assert interrupt whenever fifo_we is negated (writing is done) */ + always @(posedge fifo_clk, negedge rstn) + if (!rstn) + fifo_we_m1 <= 1'b0; + else + fifo_we_m1 <= fifo_we; + + // Metastability note: rx_wr_ptr_latched should be read one or more clock cycles after rx_fifo_int asserts + always @(posedge fifo_clk, negedge rstn) + if (!rstn) begin + rx_fifo_int <= 1'b0; + rx_wr_ptr_latched <= 11'd0; + end + else if (!fifo_we & fifo_we_m1) begin // end of write + rx_fifo_int <= 1'b1; + rx_wr_ptr_latched <= rx_wr_ptr; + end + else if (rx_fifo_int_acked) // clear interrupt + rx_fifo_int <= 1'b0; + + /* controller uses A side, FIFO uses B side + * controller writes TX path and reads RX path */ + // 2K DPRAM + dpram_inf dpram_tx( + .rstn(rstn), + .a_clk(uc_clk), + .a_clk_e(dpram_tx_sel), + .a_we(dpram_we), + .a_oe(1'b1), + .a_addr(dpram_addr), + .a_din(dpram_din[8:0]), + .a_dout(dpram_tx_dout), + // port B + .b_clk(fifo_clk), + .b_clk_e(1'b1), + .b_we(1'b0), + .b_oe(fifo_re), + .b_addr(tx_rd_ptr), + .b_din(9'h0), + .b_dout(fifo_d_out) + ); -/* controller uses A side, FIFO uses B side - * controller writes TX path and reads RX path */ -// 2K DPRAM -dpram_inf dpram_tx( - .rstn(rstn), - .a_clk(uc_clk), - .a_clk_e(dpram_tx_sel), - .a_we(dpram_we), - .a_oe(1'b1), - .a_addr(dpram_addr), - .a_din(dpram_din[8:0]), - .a_dout(dpram_tx_dout), - // port B - .b_clk(fifo_clk), - .b_clk_e(1'b1), - .b_we(1'b0), - .b_oe(fifo_re), - .b_addr(tx_rd_ptr), - .b_din(9'h0), - .b_dout(fifo_d_out) -); - -// 2K DPRAM -dpram_inf dpram_rx( - .rstn(rstn), - .a_clk(uc_clk), - .a_clk_e(dpram_rx_sel), - .a_we(1'b0), - .a_oe(1'b1), - .a_addr(dpram_addr), - .a_din(9'h000), - .a_dout(dpram_rx_dout), - // port B - .b_clk(fifo_clk), - .b_clk_e(1'b1), - .b_we(fifo_we), - .b_oe(1'b0), - .b_addr(rx_wr_ptr), - .b_din(fifo_d_in), - .b_dout() -); + // 2K DPRAM + dpram_inf dpram_rx( + .rstn(rstn), + .a_clk(uc_clk), + .a_clk_e(dpram_rx_sel), + .a_we(1'b0), + .a_oe(1'b1), + .a_addr(dpram_addr), + .a_din(9'h000), + .a_dout(dpram_rx_dout), + // port B + .b_clk(fifo_clk), + .b_clk_e(1'b1), + .b_we(fifo_we), + .b_oe(1'b0), + .b_addr(rx_wr_ptr), + .b_din(fifo_d_in), + .b_dout() + ); endmodule diff --git a/src/i2c.v b/src/i2c.v deleted file mode 100644 index aca6452..0000000 --- a/src/i2c.v +++ /dev/null @@ -1,391 +0,0 @@ -/* - * i2c.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: I2C slave controller for communicating with internal controller - * - */ - - -`timescale 1ns /10ps - - -module i2c( - input rstn, - input clk, - - // external I2C I/O - input scl_i, // serial clock - output scl_o, // serial clock out - output scl_oe, // serial clock output enable - - input sda_i, // serial data in - output reg sda_o, // serial data out - output reg sda_oe, // serial data output enable - - // shared memory and controller data output - output [7:0] mem_do, - - // dedicated memory interface - output [10:0] mem_ad, - output mem_ce, - output reg mem_we, - input [7:0] mem_di, - - // dedicated controller write interface - output reg cont_we, - output cont_done, - - // Controller->FIFO input interface - input tx_fifo_empty, // use for control and pass as msbit of data - input [6:0] fifo_di, // data from FIFO to transmit on I2C - output reg fifo_re - -); - -localparam CONT_SEL = 7'h10, - DPRAM_SEL = 7'h20, - SCI_SEL = 7'h30; - -// slave-related signals - -reg scl_high, scl_low; -reg [4:0] bit_cnt; // cnt the bits received -reg start; // falling SDA while SCL is high -reg stop; // rising SDA while SCL is high -reg run; // assert while running and counting bits -reg rwn; // follows address - -reg [6:0] dev_ad; // 7-bit device address -reg [7:0] addr; // address -reg [7:0] d; // data received from external i2c controller during an I2C write -wire target_sel, dpram_sel, cont_sel; -reg scl_i_m1, scl_i_m2, sda_i_m1; // delayed versions of the inputs -reg ack; - -wire [7:0] i_di; // internal data in muxed from external sources (e.g., DPRAM and controller) - -assign i_di = dpram_sel ? mem_di : { tx_fifo_empty, fifo_di }; - -// master-related signals -//reg halt; // assert if we lose arbitration, detect a zero in place of a one - -assign scl_oe = 1'b0; - -// since it's open drain, never drive the outputs high -assign scl_o = 1'b0; - -// slave interface - -/* - debounce and capture the asynch inputs - delayed signals, by default, they're 1 since they're open drain -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - begin - scl_i_m1 <= 1'b1; - scl_i_m2 <= 1'b1; - sda_i_m1 <= 1'b1; - end - else - begin - scl_i_m1 <= scl_i; - scl_i_m2 <= scl_i_m1; - sda_i_m1 <= sda_i; - end - end - -/* - create a one shot when scl is first stable high - use this to register inputs -*/ -always@(posedge clk or negedge rstn) - begin - if (!rstn) - scl_high <= 1'b0; - else if (scl_i && scl_i_m1 && ~scl_i_m2) - scl_high <= 1'b1; - else - scl_high <= 1'b0; - end - -/* - create a one shot when scl is first stable low - use this to register outputs -*/ -always@(posedge clk or negedge rstn) - begin - if (!rstn) - scl_low <= 1'b0; - else if ( !scl_i && !scl_i_m1 && scl_i_m2) - scl_low <= 1'b1; - else - scl_low <= 1'b0; - end - -/* - one shot start/restart bit, sda 1 to 0 anytime while scl is high -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - start <= 1'b0; - else if ( scl_i == 1'b1 && scl_i_m1 == 1'b1 && sda_i == 1'b0 && sda_i_m1 == 1'b1 ) - start <= 1'b1; - else - start <= 1'b0; - end - -/* - one shot stop bit, sda 0 to 1 anytime while scl is high -*/ -assign cont_done = stop & ~rwn; - -always @(posedge clk or negedge rstn) - begin - if (!rstn ) - stop <= 1'b0; - else if ( scl_i == 1'b1 && scl_i_m1 == 1'b1 && sda_i == 1'b1 && sda_i_m1 == 1'b0 ) - stop <= 1'b1; - else - stop <= 1'b0; - end - -/* - This I2C block runs between start and stop while run == 1 -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - run <= 1'b0; - else if ( start ) - run <= 1'b1; - else if ( stop ) - run <= 1'b0; - end - - -/* - bit_cnt indicates the state of the i2c transfer: - start/stop act as synchronous resets - otherwise, bit_cnt can change when scl is low - - 31 (1F): reserved for first cycle after start / idle state - 0:6: dev_ad - 7: rwn, ACK is clocked out - 8: - 9:16: if rwn == 0 addr, else d, ACK is clocked out on 16 - 17: ( restart to 9 if read ) - 18:25: write data, ack write data is clocked out on 25 - 26: ( restart to 18 for write data ) -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - bit_cnt <= 5'h1f; - else if ( start || stop ) - bit_cnt <= 5'h1f; - else if ( run && scl_low ) - begin - if ( rwn && bit_cnt == 5'd17 ) - bit_cnt <= 5'd9; - else if ( bit_cnt == 5'd26 ) - bit_cnt <= 5'd18; - else - bit_cnt <= bit_cnt + 1; - end - end - -/* - shift device address (dev_ad) into the module from the i2c bus -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - dev_ad <= 7'h0; - else if ( stop ) - dev_ad <= 7'h0; - else if ( run && scl_high && bit_cnt <= 5'h6 ) - dev_ad <= { dev_ad[5:0], sda_i }; - end - - -/* - shift I2C memory addr into the module from the i2c bus during first cycle - auto increment on subsequent byte reads during same I2C cycle -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - addr <= 8'h00; - else if ( run && scl_high) - begin - if ( !rwn && bit_cnt >= 5'd9 && bit_cnt <= 5'd16 ) - addr <= { addr[6:0], sda_i }; - else if ( (rwn && bit_cnt == 5'd17) || (!rwn && bit_cnt == 5'd26) ) - addr <= addr + 1; - end - end - - -/* -* shift write data (d) into the module from the i2c bus. -*/ -always @(posedge clk or negedge rstn) - begin - if (!rstn) - d <= 8'ha5; - else if ( run && scl_high && !rwn && !start && bit_cnt >= 5'd18 && bit_cnt < 5'd26 ) - d <= { d[6:0], sda_i }; - end - - - -assign dpram_sel = ( dev_ad == DPRAM_SEL ) ? 1 : 0; -assign cont_sel = ( dev_ad == CONT_SEL ) ? 1 : 0; -assign target_sel = cont_sel | dpram_sel; - -/* - register ack during I2C reads when bit_cnt == 17 -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - ack <= 1'b0; - else if ( run && scl_high && rwn && bit_cnt == 5'd17 ) - ack <= ~sda_i; - end - -/* - register rwn bit -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - rwn <= 1'b1; - else if ( stop ) - rwn <= 1'b1; - else if ( run && scl_high && bit_cnt == 5'd7 ) - rwn <= sda_i; - end - -/* - sda_oe logic, note that the bit_cnt will be changing simultaneously, so it's one behind -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - begin - sda_oe <= 1'b0; - sda_o <= 1'b1; - end - else if ( stop ) - begin - sda_oe <= 1'b0; - sda_o <= 1'b1; - end - else if ( scl_low ) - begin - // ack the first byte written by master if it's addressed to our I2C slave - if ( target_sel && bit_cnt == 5'd7 ) - begin - sda_oe <= 1'b1; - sda_o <= 1'b0; - end - // ack write address - else if ( target_sel && rwn == 1'b0 && bit_cnt == 5'd16 ) - begin - sda_oe <= 1'b1; - sda_o <= 1'b0; - end - // ack write data - else if ( target_sel && rwn == 1'b0 && bit_cnt == 5'd25 ) - begin - sda_oe <= 1'b1; - sda_o <= 1'b0; - end - // drive read data - else if ( target_sel && rwn == 1'b1 && bit_cnt >= 5'd8 && bit_cnt <=5'd15 ) // xmt data for read cycle - begin - sda_oe <= 1'b1; - sda_o <= i_di[7-bit_cnt[2:0]]; - end - // drive data for first bit of burst read cycle, multi-byte if we get an ack - // if no ack, then the burst read or single read is done - else if ( target_sel && rwn == 1'b1 && ack && bit_cnt == 5'd17 ) - begin - sda_oe <= 1'b1; - sda_o <= i_di[7]; - end - else - begin - sda_oe <= 1'b0; - sda_o <= 1'b1; // don't care - end - end - end - -/* DPRAM Control */ -assign mem_ad = {3'b0, addr}; -assign mem_ce = 1'b1; - -/* - driver: mem_do - shared between both cont and dpram interfaces - drive addr for first byte since this is the controller cmd -*/ -assign mem_do = ( bit_cnt <= 5'd17 ) ? addr : d; - -// mem_we bit -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - mem_we <= 1'b0; - else if ( run && scl_high && dpram_sel && !rwn && bit_cnt == 5'd26 ) - mem_we <= 1'b1; - else - mem_we <= 1'b0; - end - -// cont_we bit is asserted at the end of both command and data bytes -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - cont_we <= 1'b0; - else if ( run && scl_high && cont_sel && !rwn && (bit_cnt == 5'd26 || bit_cnt == 5'd17) ) - cont_we <= 1'b1; - else - cont_we <= 1'b0; - end - -/* -* drive: fifo_re -* Stop asserting during a burst if we don't get an ACK (use sda_i for real-time ACK) -*/ -always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - fifo_re <= 1'b0; - else if ( run && scl_high && cont_sel && rwn && !tx_fifo_empty && ( bit_cnt == 5'd8 || ( bit_cnt == 5'd17 && !sda_i ) ) ) - fifo_re <= 1'b1; - else - fifo_re <= 1'b0; - end - - -endmodule diff --git a/src/interrupts.v b/src/interrupts.v deleted file mode 100644 index c77dfb7..0000000 --- a/src/interrupts.v +++ /dev/null @@ -1,77 +0,0 @@ -/* - * interrupts.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: uC Interrupt Controller - * - */ - -`timescale 1ns /10ps - - -module interrupts ( - input rstn, - input clk, - input uc_clk, - - -// uC interface - input sel, - input we, - input addr, - input [6:0] d_in, - output [6:0] d_out, - - // interrupt sources - input cont_int, - input [3:0] phy_int, - input [3:0] mac_int, - - // int out - output int_o -); - -localparam INT_CONTROLLER = 'h01, - INT_PHY0 = 'h02, - INT_PHY1 = 'h04, - INT_MAC0 = 'h08, - INT_MAC1 = 'h10, - INT_MAC2 = 'h20, - INT_MAC3 = 'h40; - -reg [6:0] int_enable; -reg [6:0] int_src; - -assign d_out = addr ? int_src : int_enable; -assign int_o = int_src[6] & int_enable[6] | int_src[5] & int_enable[5] | int_src[4] & int_enable[4] | int_src[3] & int_enable[3] | - int_src[2] & int_enable[2] | int_src[1] & int_enable[1] | int_src[0] & int_enable[0]; - -always @(posedge uc_clk or negedge rstn) - if (!rstn) - int_enable <= INT_MAC2 | INT_MAC3; - else if (sel && we && !addr) - int_enable <= d_in; - -always @(posedge uc_clk or negedge rstn) - if (!rstn) - int_src <= 7'h0; - else if (sel && we && addr) - int_src <= d_in; - else - int_src <= int_src | { mac_int, phy_int[1:0], cont_int }; - - -endmodule diff --git a/src/ipv4.v b/src/ipv4_rx.v index 55ab7a7..f948729 100644 --- a/src/ipv4.v +++ b/src/ipv4_rx.v @@ -1,7 +1,8 @@ /* - * ipv4.v + * ipv4_rx.v * - * Copyright (C) 2018, 2019 Mind Chasers Inc. + * Copyright (C) 2024-2025 Private Island Networks Inc. + * 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. @@ -30,30 +31,30 @@ * When pkt_start is detected, rx_data_m2 has the first byte */ -`timescale 1ns /10ps +module ipv4_rx +( + input rstn, + input clk, + + // control + input phy_resetn, + input phy_up, -module ipv4_rx ( - input rstn, - input clk, - - // control - input phy_resetn, - input phy_up, + // packet data + input pkt_start, // assert for each new frame to start state machines + input rx_sample, // enable in case we have connected at 100 Mbit + input rx_eop, // use as a synch reset + input[7:0] rx_data_m1, + input[7:0] rx_data_m2, + input[7:0] rx_data_m3, // first byte of packet appears here simultaneous with pkt_start + input[7:0] rx_data_m4, - // packet data - input pkt_start, // assert for each new frame to start state machines - input rx_eop, // use as a synch reset - input[7:0] rx_data_m1, - input[7:0] rx_data_m2, - input[7:0] rx_data_m3, // first byte of packet appears here simultaneous with pkt_start - input[7:0] rx_data_m4, - - // flags - output pkt_complete, - output reg trigger_src_addr, - output reg trigger_dst_addr, - output reg keep - ); + // flags + output pkt_complete, + output reg trigger_src_addr, + output reg trigger_dst_addr, + output reg keep +); /* Byte Address (when last byte of field is present on rx_data_m1 ) */ localparam IPV4_TOS=1, IPV4_LENGTH=3, IPV4_IP_ID=5, IPV4_PROTOCOL=9, @@ -67,6 +68,9 @@ module ipv4_rx ( localparam IPV4_PROTO_ICMP = 1, IPV4_PROTO_IGMP = 2, IPV4_PROTO_TCP = 6, IPV4_PROTO_UDP = 17, IPV4_PROTO_ENCAP = 41, IPV4_PROTO_OSPF = 89, IPV4_PROTO_SCTP = 132; + localparam IP_DST_MATCH_2 = 3, + IP_DST_MATCH_3 = 168, + IP_DST_MATCH_4 = 192; reg [3:0] rx_state; reg [10:0] rx_byte_cnt; @@ -102,12 +106,12 @@ module ipv4_rx ( rx_byte_cnt <= 0; else if ( rx_state == RX_ST_IDLE && !pkt_start ) // synch reset rx_byte_cnt <= 1; - else if ( rx_state != RX_ST_IDLE || pkt_start ) + else if ( (rx_sample && rx_state != RX_ST_IDLE && rx_sample) || pkt_start ) rx_byte_cnt <= rx_byte_cnt + 1; /* protocol */ - always @(posedge clk, negedge rstn) + always @(posedge clk, negedge rstn) if ( !rstn ) protocol <= 0; else if ( rx_eop ) @@ -126,9 +130,9 @@ module ipv4_rx ( rx_pkt_length <= { rx_data_m2[2:0], rx_data_m1 }; /* - * Packet Filter Trigger(s), assert the sample before the data appears - * - */ + * Packet Filter Trigger(s), assert the sample before the data appears + * + */ always @(posedge clk, negedge rstn) if (!rstn) trigger_src_addr <= 1'b0; @@ -138,15 +142,24 @@ module ipv4_rx ( trigger_src_addr <= 1'b0; always @(posedge clk, negedge rstn) - if (!rstn) + if (!rstn) trigger_dst_addr <= 1'b0; else if ( rx_byte_cnt == IPV4_DST_ADDR-1 ) trigger_dst_addr <= 1'b1; else trigger_dst_addr <= 1'b0; - assign pkt_complete = ( rx_state >= RX_ST_DATA && rx_pkt_length == rx_byte_cnt+1 ) ? 1 : 0; + assign pkt_complete = ( rx_state >= RX_ST_DATA && rx_pkt_length == rx_byte_cnt+1 ) ? 1'b1 : 1'b0; assign rx_error = 0; + // ipv4 keep logic: add your criteria below + always @(posedge clk, negedge rstn) + if (!rstn) + keep <= 1'b0; + else if (1'b0) + keep <= 1'b1; + else + keep <= 1'b0; + endmodule diff --git a/src/ipv4_rx_c.v b/src/ipv4_rx_c.v new file mode 100644 index 0000000..262d328 --- /dev/null +++ b/src/ipv4_rx_c.v @@ -0,0 +1,134 @@ +/* + * ipv4_rx_c.v + * + * Copyright (C) 2024-2025 Private Island Networks Inc. + * 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: Receive State Machine and Logic for IPv4 for the internal controller + * + * + * 0-7 8-15 16-23 24-31 + * 0 Version/IHL TOS Total Length............. + * 4 Identification...... Flags/Frag Off........... + * 8 TTL Protocol Header Checksum.......... + * 12 Source IP Address.................................... + * 16 Dest IP Address...................................... + * 20 Options / Padding.................................... + * + * + */ + +module ipv4_rx_c #(parameter IP_DST_MATCH_1=192, IP_DST_MATCH_2=168, + IP_DST_MATCH_3=5, IP_DST_MATCH_4=200) +( + input rstn, + input clk, + + // control + input phy_up, + + // packet data + input pkt_start, // assert for each new frame to start state machines + input rx_sample, // enable in case we have connected at 100 Mbit + input rx_eop, // use as a synch reset + input[7:0] rx_data_m1, + input[7:0] rx_data_m2, + input[7:0] rx_data_m3, // first byte of packet appears here simultaneous with pkt_start + input[7:0] rx_data_m4, + + // flags + output pkt_complete, + output reg ip_addr_match +); + + /* Byte Address (when last byte of field is present on rx_data_m1 ) */ + localparam IPV4_TOS=1, IPV4_LENGTH=3, IPV4_IP_ID=5, IPV4_PROTOCOL=9, + IPV4_HDR_CKSUM=11, IPV4_SRC_ADDR=15, IPV4_DST_ADDR=19; + + localparam RX_ST_IDLE=4'h0, RX_ST_DATA=4'h1, RX_ST_DONE=4'h2, RX_ST_3=4'h3, + RX_ST_4=4'h4, RX_ST_5=4'h5, RX_ST_6=4'h6, RX_ST_7=4'h7, + RX_ST_8=4'h8, RX_ST_9=4'h9, RX_ST_A=4'ha, RX_ST_B=4'hb, + RX_ST_C=4'hc, RX_ST_D=4'hd, RX_ST_E=4'he, RX_ST_F=4'hf; + + localparam IPV4_PROTO_ICMP = 1, IPV4_PROTO_IGMP = 2, IPV4_PROTO_TCP = 6, IPV4_PROTO_UDP = 17, + IPV4_PROTO_ENCAP = 41, IPV4_PROTO_OSPF = 89, IPV4_PROTO_SCTP = 132; + + reg [3:0] rx_state; + reg [10:0] rx_byte_cnt; + reg [10:0] rx_pkt_length; + reg [7:0] protocol; + + /* + * rx_state machine + * capture an IPv4 Packet + * + */ + always @(posedge clk, negedge rstn) + if (!rstn) + rx_state <= RX_ST_IDLE; + else if ( rx_eop || !phy_up ) // EOP will reset state machine + rx_state <= RX_ST_IDLE; + else + case ( rx_state ) + RX_ST_IDLE: if ( pkt_start ) // Found /S/ + rx_state <= RX_ST_DATA; + RX_ST_DATA: if (pkt_complete) + rx_state <= RX_ST_DONE; + RX_ST_DONE: rx_state <= RX_ST_IDLE; + default: rx_state <= RX_ST_IDLE; + endcase + + /* rx_byte_cnt */ + always @(posedge clk, negedge rstn) + if (!rstn) + rx_byte_cnt <= 0; + else if ( rx_state == RX_ST_IDLE && !pkt_start ) // synch reset + rx_byte_cnt <= 1; + else if ( (rx_sample && rx_state != RX_ST_IDLE && rx_sample) || pkt_start ) + rx_byte_cnt <= rx_byte_cnt + 1; + + + /* protocol */ + always @(posedge clk, negedge rstn) + if ( !rstn ) + protocol <= 0; + else if ( rx_eop ) + protocol <= 0; + else if ( rx_byte_cnt == IPV4_PROTOCOL ) + protocol <= rx_data_m1; + + /* rx_pkt_length */ + always @(posedge clk, negedge rstn) + if ( !rstn ) + rx_pkt_length <= 11'h7ff; + else if ( rx_eop ) + rx_pkt_length <= 11'h7ff; + else if ( rx_state == RX_ST_DATA && rx_byte_cnt == IPV4_LENGTH ) + rx_pkt_length <= { rx_data_m2[2:0], rx_data_m1 }; + + // IPv4 Dest Address match logic + always @(posedge clk, negedge rstn) + if (!rstn) + ip_addr_match <= 1'b0; + else if (rx_eop) + ip_addr_match <= 1'b0; + else if ( rx_sample && rx_data_m1 == (IP_DST_MATCH_4) && rx_data_m2 == IP_DST_MATCH_3 && rx_data_m3 == IP_DST_MATCH_2 && rx_data_m4 == IP_DST_MATCH_1 ) + ip_addr_match <= 1'b1; + + assign pkt_complete = ( rx_state >= RX_ST_DATA && rx_pkt_length == rx_byte_cnt+1 ) ? 1'b1 : 1'b0; + + +endmodule diff --git a/src/ipv4_tx_c.v b/src/ipv4_tx_c.v new file mode 100644 index 0000000..3c33548 --- /dev/null +++ b/src/ipv4_tx_c.v @@ -0,0 +1,179 @@ +/* + * ipv4_tx.v + * + * Copyright 2025 Private Island Networks Inc. + * Copyright 2018, 2019, 2020 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: Layer 3 (and 4) Support for Controller Transmit + * + * + */ + +module ipv4_tx_c( + input rstn, + input phy_resetn, + input clk, + + // Control Interface + input cont_clk, // controller clock + input cont_sel, + input cont_we, + input [7:0] cont_addr, + input [15:0] cont_d_i, + output reg [15:0] cont_d_o, + + // Line State + input mode_100Mbit, + input phy_up, + + // Switch I/F + input [2:0] tx_mode, + input [2:0] tx_src_sel, + input [10:0] byte_cnt_i, + + // MAC Interface + input fifo_re_i, + output reg fifo_empty_o, + output reg [8:0] fifo_d_o, + + // Debug + output [7:0] gpio +); + +`define INCLUDED +`include "ethernet_params.v" +`include "rgmii_params.v" +`undef INCLUDED + + localparam TX_CNT_OFFSET = 11'h15; // offset for start of IP Header in Ethernet frame + localparam TX_CNT_CKSUM_START = TX_CNT_OFFSET + 10; + + wire ipv4_hdr_active; + reg [10:0] tx_cnt; + reg [17:0] ipv4_cksum; + reg [15:0] ipv4_pkt_length; + reg [15:0] udp_pkt_length; + integer i; + + assign ipv4_hdr_active = (tx_mode == TX_MODE_XMT_CUSTOM && tx_src_sel == TX_SRC_SEL_UC); + + always @(posedge clk, negedge rstn) + if ( !rstn ) + tx_cnt <= 11'd0; + else if (ipv4_hdr_active) + tx_cnt <= tx_cnt + 1'b1; + else + tx_cnt <= 11'd0; + + + always @(posedge clk, negedge rstn) + if ( !rstn ) begin + ipv4_pkt_length <= 16'd0; + udp_pkt_length <= 16'd0; + end + else if (ipv4_hdr_active && tx_cnt == 11'd0) begin + ipv4_pkt_length <= byte_cnt_i + SZ_IPV4_HEADER + SZ_UDP_HEADER; + udp_pkt_length <= byte_cnt_i + SZ_UDP_HEADER; + end + + always @(posedge clk, negedge rstn) + if ( !rstn ) + ipv4_cksum <= 18'd0; + else if (!ipv4_hdr_active) + ipv4_cksum <= 18'd0; + else if (tx_cnt < 11'd20 && tx_cnt[0]) + ipv4_cksum <= ipv4_cksum + (ipv4_hdr(tx_cnt-1)<<8) + ipv4_hdr(tx_cnt); + // else if (tx_cnt == 11'd20) + // ipv4_cksum <= ipv4_cksum + ipv4_pkt_length; + else if (tx_cnt == 11'd21) + ipv4_cksum <= {2'b00,ipv4_cksum[15:0]} + {16'h0000,ipv4_cksum[17:16]}; + else if (tx_cnt == 11'd22) + ipv4_cksum <= {2'b00, ~ipv4_cksum[15:0]}; + + // fifo_empty_o + always @(posedge clk, negedge rstn) + if ( !rstn ) + fifo_empty_o <= 1'b1; + else if (tx_cnt >= TX_CNT_OFFSET && tx_cnt < (TX_CNT_OFFSET + SZ_IPV4_HEADER + SZ_UDP_HEADER)) + fifo_empty_o <= 1'b0; + else + fifo_empty_o <= 1'b1; + + // fifo_d_o + always @(posedge clk, negedge rstn) + if ( !rstn ) + fifo_d_o <= 9'h000; + else if (tx_cnt == TX_CNT_CKSUM_START) + fifo_d_o <= ipv4_cksum[15:8]; + else if (tx_cnt == TX_CNT_CKSUM_START+1) + fifo_d_o <= ipv4_cksum[7:0]; + else if (tx_cnt >= TX_CNT_OFFSET && tx_cnt < TX_CNT_OFFSET + SZ_IPV4_HEADER) + fifo_d_o <= ipv4_hdr(tx_cnt-TX_CNT_OFFSET); + else if (tx_cnt >= TX_CNT_OFFSET && tx_cnt < TX_CNT_OFFSET + SZ_IPV4_HEADER + SZ_UDP_HEADER) + fifo_d_o <= udp_hdr(tx_cnt-(TX_CNT_OFFSET + SZ_IPV4_HEADER)); + else + fifo_d_o <= 9'h000; + + + // generate the IPv4 header + // Assuming the header is only 20 bytes, this is both efficient and flexible + function automatic [7:0] ipv4_hdr; + input [10:0] ad; + case(ad) + 0: ipv4_hdr = 8'h45; // Version + 1: ipv4_hdr = 8'h00; // IHL + 2: ipv4_hdr = ipv4_pkt_length[15:8]; // Total Length + 3: ipv4_hdr = ipv4_pkt_length[7:0]; + 4: ipv4_hdr = 8'h54; + 5: ipv4_hdr = 8'hDB; + 6: ipv4_hdr = 8'h40; + 7: ipv4_hdr = 8'h00; + 8: ipv4_hdr = 8'h40; // TTL + 9: ipv4_hdr = 8'h11; // L4 Protocol (UDP=0x11) + 10: ipv4_hdr = 8'h00; // Checksum + 11: ipv4_hdr = 8'h00; + 12: ipv4_hdr = 8'd192; // IP Source Address + 13: ipv4_hdr = 8'd168; + 14: ipv4_hdr = 8'd5; + 15: ipv4_hdr = 8'd100; + 16: ipv4_hdr = 8'd192; // IP Destination Address + 17: ipv4_hdr = 8'd168; + 18: ipv4_hdr = 8'd5; + 19: ipv4_hdr = 8'd40; + default: ipv4_hdr = 8'h00; + endcase + endfunction + + + // generate the UDP header + function automatic [7:0] udp_hdr; + input [10:0] ad; + case(ad) + 0: udp_hdr = 8'h80; // SRC Port + 1: udp_hdr = 8'h00; // + 2: udp_hdr = 8'h80; // DST Port + 3: udp_hdr = 8'h00; + 4: udp_hdr = udp_pkt_length[15:8]; // Length + 5: udp_hdr = udp_pkt_length[7:0]; + 6: udp_hdr = 8'h00; //Checksum + 7: udp_hdr = 8'h00; + default: udp_hdr = 8'h00; + endcase + endfunction + + assign gpio[0] = |ipv4_cksum[7:0]; + assign gpio[1] = |ipv4_cksum[15:8]; + +endmodule diff --git a/src/ipv4_tx_mle.v b/src/ipv4_tx_mle.v new file mode 100644 index 0000000..28d6d85 --- /dev/null +++ b/src/ipv4_tx_mle.v @@ -0,0 +1,175 @@ +/* + * ipv4_tx.v + * + * Copyright 2025 Private Island Networks Inc. + * Copyright 2018, 2019, 2020 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: Layer 3 (and 4) Support for Controller Transmit + * + */ + +module ipv4_tx_mle( + input rstn, + input phy_resetn, + input clk, + + // Control Interface + input cont_clk, // controller clock + input cont_sel, + input cont_we, + input [7:0] cont_addr, + input [15:0] cont_d_i, + output reg [15:0] cont_d_o, + + // Line State + input mode_100Mbit, + input phy_up, + + // Switch I/F + input [2:0] tx_mode, + input [2:0] tx_src_sel, + input [10:0] byte_cnt_i, // # of payload bytes + + // MAC Interface + input fifo_re_i, + output reg fifo_empty_o, + output reg [8:0] fifo_d_o, + + // Debug + output [7:0] gpio +); + + `define INCLUDED + `include "ethernet_params.v" + `include "rgmii_params.v" + `undef INCLUDED + + localparam TX_CNT_OFFSET = 11'h15; // offset for start of IP Header in Ethernet frame + localparam TX_CNT_CKSUM_START = TX_CNT_OFFSET + 10; + + wire ipv4_hdr_active; + reg [10:0] tx_cnt; + reg [17:0] ipv4_cksum; + reg [15:0] ipv4_pkt_length; + reg [15:0] udp_pkt_length; + integer i; + + assign ipv4_hdr_active = (tx_mode == TX_MODE_XMT_CUSTOM && tx_src_sel == TX_SRC_SEL_MLE); + + always @(posedge clk, negedge rstn) + if ( !rstn ) + tx_cnt <= 11'd0; + else if (ipv4_hdr_active) + tx_cnt <= tx_cnt + 1'b1; + else + tx_cnt <= 11'd0; + + always @(posedge clk, negedge rstn) + if ( !rstn ) begin + ipv4_pkt_length <= 16'd0; + udp_pkt_length <= 16'd0; + end + else if (ipv4_hdr_active && tx_cnt == 11'd0) begin + ipv4_pkt_length <= byte_cnt_i + SZ_IPV4_HEADER + SZ_UDP_HEADER; + udp_pkt_length <= byte_cnt_i + SZ_UDP_HEADER; + end + + always @(posedge clk, negedge rstn) + if ( !rstn ) + ipv4_cksum <= 18'd0; + else if (!ipv4_hdr_active) + ipv4_cksum <= 18'd0; + else if (tx_cnt < 11'd20 && tx_cnt[0]) + ipv4_cksum <= ipv4_cksum + (ipv4_hdr(tx_cnt-1)<<8) + ipv4_hdr(tx_cnt); + else if (tx_cnt == 11'd21) + ipv4_cksum <= {2'b00,ipv4_cksum[15:0]} + {16'h0000,ipv4_cksum[17:16]}; + else if (tx_cnt == 11'd22) + ipv4_cksum <= {2'b00, ~ipv4_cksum[15:0]}; + + // fifo_empty_o + always @(posedge clk, negedge rstn) + if ( !rstn ) + fifo_empty_o <= 1'b1; + else if (tx_cnt >= TX_CNT_OFFSET && tx_cnt < (TX_CNT_OFFSET + SZ_IPV4_HEADER + SZ_UDP_HEADER)) + fifo_empty_o <= 1'b0; + else + fifo_empty_o <= 1'b1; + + // fifo_d_o + always @(posedge clk, negedge rstn) + if ( !rstn ) + fifo_d_o <= 9'h000; + else if (tx_cnt == TX_CNT_CKSUM_START) + fifo_d_o <= ipv4_cksum[15:8]; + else if (tx_cnt == TX_CNT_CKSUM_START+1) + fifo_d_o <= ipv4_cksum[7:0]; + else if (tx_cnt >= TX_CNT_OFFSET && tx_cnt < TX_CNT_OFFSET + SZ_IPV4_HEADER) + fifo_d_o <= ipv4_hdr(tx_cnt-TX_CNT_OFFSET); + else if (tx_cnt >= TX_CNT_OFFSET && tx_cnt < TX_CNT_OFFSET + SZ_IPV4_HEADER + SZ_UDP_HEADER) + fifo_d_o <= udp_hdr(tx_cnt-(TX_CNT_OFFSET + SZ_IPV4_HEADER)); + else + fifo_d_o <= 9'h000; + + + // generate the IPv4 header + // Assuming the header is only 20 bytes, this is both efficient and flexible + function automatic [7:0] ipv4_hdr; + input [10:0] ad; + case(ad) + 0: ipv4_hdr = 8'h45; // Version + 1: ipv4_hdr = 8'h00; // IHL + 2: ipv4_hdr = ipv4_pkt_length[15:8]; // Total Length + 3: ipv4_hdr = ipv4_pkt_length[7:0]; + 4: ipv4_hdr = 8'h54; + 5: ipv4_hdr = 8'hDB; + 6: ipv4_hdr = 8'h40; + 7: ipv4_hdr = 8'h00; + 8: ipv4_hdr = 8'h40; // TTL + 9: ipv4_hdr = 8'h11; // L4 Protocol (UDP=0x11) + 10: ipv4_hdr = 8'h00; // Checksum + 11: ipv4_hdr = 8'h00; + 12: ipv4_hdr = 8'd192; // IP Source Address + 13: ipv4_hdr = 8'd168; + 14: ipv4_hdr = 8'd5; + 15: ipv4_hdr = 8'd100; + 16: ipv4_hdr = 8'd192; // IP Destination Address + 17: ipv4_hdr = 8'd168; + 18: ipv4_hdr = 8'd5; + 19: ipv4_hdr = 8'd40; + default: ipv4_hdr = 8'h00; + endcase + endfunction + + + // generate the UDP header + function automatic [7:0] udp_hdr; + input [10:0] ad; + case(ad) + 0: udp_hdr = 8'h80; // SRC Port + 1: udp_hdr = 8'h10; // + 2: udp_hdr = 8'h80; // DST Port + 3: udp_hdr = 8'h10; + 4: udp_hdr = udp_pkt_length[15:8]; // Length + 5: udp_hdr = udp_pkt_length[7:0]; + 6: udp_hdr = 8'h00; //Checksum + 7: udp_hdr = 8'h00; + default: udp_hdr = 8'h00; + endcase + endfunction + + assign gpio[0] = |ipv4_cksum[7:0]; + assign gpio[1] = |ipv4_cksum[15:8]; + +endmodule diff --git a/src/link_timer.v b/src/link_timer.v deleted file mode 100644 index 7b501ff..0000000 --- a/src/link_timer.v +++ /dev/null @@ -1,67 +0,0 @@ -/* - * link_timer.v - * - * Copyright 2020 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: Generate link timer pulse (1.6 and 10 ms) per SGMII and IEEE 802.3 - * - */ - -module link_timer( - input rstn, - input clk, // 125 MHz data clock - output pulse_1_6ms, - output pulse_10ms -); - - reg [13:0] cnt; // 0.1 ms counter - reg [3:0] cnt_1_6ms; - reg [6:0] cnt_10ms; - - // 0.1 ms counter - always @ (posedge clk or negedge rstn) - if ( !rstn ) - cnt <= 'd0; - else if (cnt == 'd12500) - cnt <= 'd0; - else - cnt <= cnt + 1; - - // 1.6 ms counter - always @ (posedge clk or negedge rstn) - if ( !rstn ) - cnt_1_6ms <= 'd0; - else if (cnt == 'd12500) - if (cnt_1_6ms == 'd16) - cnt_1_6ms <= 'd0; - else - cnt_1_6ms <= cnt_1_6ms + 1; - - assign pulse_1_6ms = (cnt == 'd12500 && cnt_1_6ms == 'd15); - - // 10 ms counter - always @ (posedge clk or negedge rstn) - if ( !rstn ) - cnt_10ms <= 'd0; - else if (cnt == 'd12500) - if (cnt_10ms == 'd99) - cnt_10ms <= 'd0; - else - cnt_10ms <= cnt_10ms + 1; - - assign pulse_10ms = (cnt == 'd12500 && cnt_10ms == 'd99); - - -endmodule
\ No newline at end of file diff --git a/src/mac.v b/src/mac.v deleted file mode 100644 index b5b980a..0000000 --- a/src/mac.v +++ /dev/null @@ -1,973 +0,0 @@ -/* - * 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 diff --git a/src/mac_rgmii.v b/src/mac_rgmii.v new file mode 100644 index 0000000..aa96705 --- /dev/null +++ b/src/mac_rgmii.v @@ -0,0 +1,998 @@ +/* + * mac_rgmii.v + * + * Copyright 2025 Private Island Networks Inc. + * Copyright 2018, 2019, 2020 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 for RGMII Interface + * + */ + +module mac_rgmii( + input rstn, + input phy_resetn, // The external PHY has its reset signal asserted + input rx_clk, // rx_clk + input tx_clk, + input tap_port, + + // Control Interface + input cont_clk, // controller clock + input cont_sel, + input cont_we, + input [7:0] cont_addr, + input [15:0] cont_d_i, + output reg [15:0] cont_d_o, + output cont_tgt_ready, + + // ML Engine Interface + input mle_active, + input mle_if_enable, + input mle_if_oe, + output reg mle_if_we, + output reg mle_if_empty, + output reg [8:0] mle_if_d_o, + + // Line State + input fixed_speed, // 0 = 100 MBit, 1 = GigE + output mode_100Mbit, + output reg phy_up, + + // Switch I/F + input [2:0] tx_mode, + output reg tx_f, + + // RGMII data I/F + input [1:0] rx_ctl, + input [7:0] rx_d, + output reg [1:0] tx_ctl, + output reg [7:0] tx_d, + + // RX FCS + output reg fcs_rx_init, + output reg fcs_rx_enable, + output reg [1:0] fcs_rx_addr, + output reg [7:0] fcs_rx_dout, + input [7:0] fcs_rx_din, + + // TX FCS + output reg fcs_tx_init, + output reg fcs_tx_enable, + output reg [1:0] fcs_tx_addr, + output reg [7:0] fcs_tx_dout, + input [7:0] fcs_tx_din, + + // MAC RX / FIFO Write + output rx_fifo_we, + output [8:0] rx_fifo_d, + output rx_keep, + output reg rx_wr_done, + output reg [10:0] rx_byte_cnt, + output reg rx_idle, + output reg rx_error, + + // 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, + + // Alternate TX Port utilized by Controller + input tx_uc_fifo_empty, + input [8:0] tx_uc_fifo_d, + + // Alternate TX Port utilized by MLE + input tx_mle_fifo_empty, + input [8:0] tx_mle_fifo_d, + + // Packet Filter + output rx_sample, + output reg ipv4_pkt_start, + output trigger, + + output reg[1:0] rx_ctl_m1, + output reg[1:0] rx_ctl_m2, + output reg[1:0] rx_ctl_m3, + output reg[1:0] rx_ctl_m4, + + output reg[7:0] rx_d_m1, + output reg[7:0] rx_d_m2, + output reg[7:0] rx_d_m3, + output reg[7:0] rx_d_m4, + + // Param RAM + output reg [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 [15:0] rx_l3_proto, + output reg [11:0] rx_pkt_length, + output reg mac_int, + output reg rx_sop, // start of packet + output reg rx_eop, + output reg tx_sop, + output reg tx_eop, + + // Debug + output reg rx_active, + output reg tx_active +); + +`define INCLUDED +`include "ethernet_params.v" +`include "rgmii_params.v" +`undef INCLUDED + + + 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, RX_ST_DATA_DONE3=4'hb, RX_ST_DATA_DONE4=4'hc; + + 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_EOP=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; + + // Controller Address Space + localparam + RX_PKT_CNT_ADDR = 16'h0000, + TX_PKT_CNT_ADDR = 16'h0004; + + reg [3:0] rx_cnt_100mbit, tx_cnt_100mbit; + wire mode_1Gbit; + + wire tx_sample, tx_sample_re; + reg rx_packet_complete; + + reg [9:0] rx_line_up_cnt; + reg link_up; + reg [1:0] link_speed; // TODO: what's the right IEEE name? + reg link_duplex; + reg phy_up_m1; // previous state + + reg [3:0] rx_state; + + // TODO: consider reorganizing state machines to reuse registers. + reg [7:0] tx_d_an, tx_d_idle, tx_data_pkt; + reg [1:0] tx_ctl_an, tx_ctl_idle, tx_ctl_pkt; + reg tx_f_an, tx_f_idle, tx_f_pkt; + + // Transmit Registers and Wires + reg [3:0] tx_state; // transmit state machine + reg [10:0] tx_cnt; + reg tx_last_byte; + wire tx_finished; + + // FIFOs: + reg [8:0] tx_fifo_d_m1, tx_fifo_d_m2; + + // FCS + reg fcs_tx_addr_e, fcs_rx_addr_e; + reg fcs_rx_error; + + // pipeline the param RAM for timing + reg [8:0] dpr_di_reg, dpr_di_reg_m1; + + // counter for detecting Ethernet broadcast, only needs to count to 6 + reg [2:0] rx_enet_bcast_cnt; + + // ML Engine + reg [3:0] mle_if_cnt; + + // Metrics + reg [15:0] rx_pkt_cnt; + reg [15:0] tx_pkt_cnt; + + + /* + * Controller Interface + * + * System's internal controller can write and read important parameters + * + */ + + // Controller Read Data Mux + always @(posedge cont_clk, negedge rstn) + if (!rstn) + cont_d_o <= 16'hcccc; + else + case (cont_addr) + RX_PKT_CNT_ADDR: cont_d_o <= rx_pkt_cnt; + TX_PKT_CNT_ADDR: cont_d_o <= tx_pkt_cnt; + default: cont_d_o <= cont_d_o; + endcase + + // TODO: add logic to prevent controller reading metastable data + assign cont_tgt_ready = 1'b1; + + + /* + * ML Engine Interface + * + */ + + // mle_if_cnt: + // TODO: write cnt at end, rework this logic and make it more meaningful + always @(posedge tx_clk, negedge rstn) + if(!rstn) begin + mle_if_cnt <= 4'd0; + mle_if_empty <= 1'b1; + mle_if_we <= 1'b0; + end + else if (rx_eop) begin + mle_if_cnt <= 4'd4; + mle_if_empty <= 1'b0; + mle_if_we <= 1'b0; + end + else if (mle_if_cnt > 4'd0 && mle_if_enable) begin + mle_if_cnt <= mle_if_cnt - 1'b1; + mle_if_empty <= 1'b0; + mle_if_we <= 1'b1; + end + else if (mle_if_cnt == 'd0 && mle_if_enable) begin + mle_if_empty <= 1'b1; + mle_if_we <= 1'b0; + end + + + // mle_if_d_o: + always @(posedge tx_clk, negedge rstn) + if(!rstn) + mle_if_d_o <= 'd0; + else if (mle_if_enable) begin + if (mle_if_cnt > 4'd1) + mle_if_d_o <= mle_if_d_o + 1'b1; + else if (mle_if_cnt == 4'd1) + mle_if_d_o <= (mle_if_d_o + 1'b1) | 9'h100; + else + mle_if_d_o <= 'd0; + end + + + + /* + * RX DIRECTION + * + */ + + /* + * A shallow pool of RX registers for analysis + */ + always @(posedge rx_clk, negedge rstn) + begin + if (!rstn) begin + rx_ctl_m1 <= NORMAL_INTERFRAME; + rx_ctl_m2 <= NORMAL_INTERFRAME; + rx_ctl_m3 <= NORMAL_INTERFRAME; + rx_ctl_m4 <= NORMAL_INTERFRAME; + rx_d_m1 <= 8'h0; + rx_d_m2 <= 8'h0; + rx_d_m3 <= 8'h0; + rx_d_m4 <= 8'h0; + end + else begin + rx_ctl_m1 <= rx_ctl; + rx_ctl_m2 <= rx_ctl_m1; + rx_ctl_m3 <= rx_ctl_m2; + rx_ctl_m4 <= rx_ctl_m3; + rx_d_m1 <= rx_d; + rx_d_m2 <= rx_d_m1; + rx_d_m3 <= rx_d_m2; + rx_d_m4 <= rx_d_m3; + end + end + + + // Link State + always @(posedge rx_clk, negedge rstn) + if (!rstn) + rx_line_up_cnt <= 10'd0; + else if ((rx_ctl_m1 == NORMAL_INTERFRAME && rx_d_m1 == {D_IDLE, D_IDLE}) || rx_ctl_m1 == NORMAL_DATA_RX) begin + if (rx_line_up_cnt < 10'd1023) + rx_line_up_cnt <= rx_line_up_cnt + 1'b1; + end + else + rx_line_up_cnt <= 10'd0; + + // + always @(posedge rx_clk, negedge rstn) + if (!rstn) + phy_up <= 1'b0; + else if (rx_line_up_cnt >= 10'd5) + phy_up <= 1'b1; + else + phy_up <= 1'b0; + + always @(posedge rx_clk, negedge rstn) + if (!rstn) + phy_up_m1 <= 1'b0; + else + phy_up_m1 <= phy_up; + + // capture link metrics during normal inter-frame + always @(posedge rx_clk, negedge rstn) + if (!rstn) begin + link_up <= 1'b0; + link_speed <= 2'b11; // reserved + link_duplex <= 1'b0; + end + else if (rx_ctl == NORMAL_INTERFRAME) begin + link_up <= rx_d[0]; + link_speed <= rx_d[2:1]; + link_duplex <= rx_d[3]; + end + + + assign mode_1Gbit = 1'b1; + assign mode_100Mbit = !mode_1Gbit; + + // RX 100 Mbit support + assign rx_sample = (rx_cnt_100mbit == 4'd9 && mode_100Mbit) || !mode_100Mbit ? 1'b1 : 1'b0; + always @(posedge rx_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 rx_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 && phy_up_m1 ) + case ( rx_state ) + RX_ST_IDLE: if (rx_ctl_m1 == NORMAL_DATA_RX) // + rx_state <= RX_ST_SOP; + RX_ST_SOP: if ( rx_sample ) // + rx_state <= RX_ST_PREAMBLE; + RX_ST_PREAMBLE: if ( rx_sample && rx_d_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; + RX_ST_DATA_DONE1: if ( rx_sample ) + rx_state <= RX_ST_DATA_DONE2; + RX_ST_DATA_DONE2: if ( rx_sample ) + rx_state <= RX_ST_DATA_DONE3; + RX_ST_DATA_DONE3: if ( rx_sample ) + rx_state <= RX_ST_DATA_DONE4; + RX_ST_DATA_DONE4: if ( rx_sample ) + rx_state <= rx_state; + 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) + * TODO: Add state information to only trigger on DEST ADDRESS + * + */ + always @(posedge rx_clk, negedge rstn) + if (!rstn) + rx_enet_bcast_cnt <= 3'h0; + else if ( rx_sample ) + if (rx_d_m1 == 9'hff) + rx_enet_bcast_cnt <= rx_enet_bcast_cnt + 1'b1; + else + rx_enet_bcast_cnt <= 3'h0; + + /* Ethernet Broadcast Dest Address, must be a one shot */ + always @(posedge rx_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 rx_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 rx_clk, negedge rstn) + if ( !rstn ) + rx_l3_proto <= 16'd0; + else if ( rx_sop ) + rx_l3_proto <= 16'd0; + else if ( rx_sample && rx_state == RX_ST_MAC_TYPE0 ) + rx_l3_proto <= { rx_d_m2, rx_d_m1 }; + + // assert ipv4 ARP flag for filtering operations + always @(posedge rx_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; + assign rx_keep = rx_state == RX_ST_MAC_TYPE1; + + // 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'b1; + else if (fcs_rx_error) + rx_error = 1'b1; + else + rx_error = 1'b0; + + /* rx_byte_cnt */ + always @(posedge rx_clk, negedge rstn) + if (!rstn) + rx_byte_cnt <= 11'd0; + else if (rx_sample) + if ( rx_state == RX_ST_IDLE || rx_state == RX_ST_PREAMBLE ) + rx_byte_cnt <= 11'd0; + else if ( rx_state == RX_ST_MAC_TYPE0 ) + rx_byte_cnt <= 11'd1; + else + rx_byte_cnt <= rx_byte_cnt + 1'b1; + + /* rx_pkt_length */ + always @(posedge rx_clk, negedge rstn) + if ( !rstn ) + rx_pkt_length <= 12'd0; + else if ( rx_sop ) + rx_pkt_length <= 12'd0; + else if (rx_sample) + if ( rx_l3_proto == ETHER_TYPE_IPV4 && rx_state == RX_ST_DATA && rx_byte_cnt == 'h4 ) + rx_pkt_length <= { rx_d_m2[2:0], rx_d_m1 }; + else if ( rx_l3_proto == ETHER_TYPE_IPV6 && rx_state == RX_ST_DATA && rx_byte_cnt == 'h6 ) + rx_pkt_length <= { rx_d_m2[2:0], rx_d_m1 } + 'd40; + else if ( rx_l3_proto == ETHER_TYPE_ARP && rx_state == RX_ST_DATA ) + rx_pkt_length <= 'd46; + + /* ipv4 flag */ + always @(posedge rx_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; + + // implement minimum Ethernet frame of 64 bytes + always @(*) + if (rx_pkt_length >= 12'd46) + rx_packet_complete = (rx_sample && rx_state >= RX_ST_DATA && rx_pkt_length == rx_byte_cnt); + else + rx_packet_complete = (rx_sample && rx_state >= RX_ST_DATA && rx_byte_cnt == 12'd46); + + // FIFO data interface + assign rx_fifo_d[7:0] = rx_d_m1; + assign rx_fifo_d[8] = rx_packet_complete; + + + // TODO: take into account errors RXERR + always @(posedge rx_clk, negedge rstn) + if (!rstn) + rx_sop <= 1'b0; + else if (rx_ctl_m1 ==NORMAL_DATA_RX && rx_ctl_m2 == NORMAL_INTERFRAME) + rx_sop <= 1'b1; + else + rx_sop <= 1'b0; + + /* + * rx_idle + */ + always @(posedge rx_clk, negedge rstn) + if (!rstn) + rx_idle <= 1'b0; + else if (rx_state == RX_ST_IDLE) + rx_idle <= 1'b1; + else + rx_idle <= 1'b0; + + /* + * rx_eop + */ + always @(posedge rx_clk, negedge rstn) + if (!rstn) + rx_eop <= 1'b0; + else if (rx_ctl_m2 ==NORMAL_DATA_RX && rx_ctl_m1 == NORMAL_INTERFRAME) + 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 rx_clk or negedge rstn) + if (!rstn) + mac_int <=1'b0; + else if ( rx_error ) + mac_int <= 1'b1; + else + mac_int <= 1'b0; + + + /* RX Metrics and Debug */ + always @(posedge rx_clk or negedge rstn) + if (!rstn) + rx_active <=1'b0; + else if ( rx_state != 0 ) + rx_active <= 1'b1; + else + rx_active <= 1'b0; + + // rx_pkt_cnt + always @(posedge rx_clk or negedge rstn) + if (!rstn) + rx_pkt_cnt <= 16'd0; + else if (rx_eop) + rx_pkt_cnt <= rx_pkt_cnt + 1'b1; + + always @(*) + if (mode_1Gbit && (rx_state > RX_ST_SFD && rx_state <= RX_ST_DATA_DONE0) ) + fcs_rx_enable = 1'b1; + else if ( mode_100Mbit && rx_sample && (rx_state > RX_ST_PREAMBLE && rx_state <= RX_ST_DATA_DONE0)) + fcs_rx_enable = 1'b1; + else + fcs_rx_enable = 1'b0; + + /* FCS RX State Machine */ + always @(posedge rx_clk or negedge rstn) + if (!rstn) begin + fcs_rx_init <= 1'b0; + fcs_rx_addr_e <= 1'b0; + fcs_rx_dout <= 8'h00; + end + else if(rx_state == RX_ST_SOP) + fcs_rx_init = 1'b1; + else if (rx_state > RX_ST_DATA && rx_state <= RX_ST_DATA_DONE3) begin + fcs_rx_init <= 1'b0; + fcs_rx_addr_e <= 1'b1; + fcs_rx_dout <= 8'h00; + end + else begin + fcs_rx_init <= 1'b0; + fcs_rx_addr_e <= 1'b0; + fcs_rx_dout <= rx_d_m1; + end + + always @(posedge rx_clk or negedge rstn) + begin + if ( !rstn ) + fcs_rx_addr <= 2'b00; + else if (rx_sample) + if ( !fcs_rx_addr_e ) + fcs_rx_addr <= 2'b00; + else + fcs_rx_addr <= fcs_rx_addr + 1'b1; + end + + // Test FCS RX result + always @(posedge rx_clk or negedge rstn) + if ( !rstn ) + fcs_rx_error <= 1'b0; + else if (fcs_rx_addr_e && rx_d_m2 != fcs_rx_din) + fcs_rx_error <= 1'b1; + else + fcs_rx_error <= 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 tx_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'b1; + + /* + * + * Transmit Mux + */ + always @(posedge tx_clk or negedge rstn) + if (!rstn) begin + tx_d <= 8'hDD; + tx_ctl <= 2'b00; + end + else begin + case(tx_mode) + TX_MODE_AN: // before phy_up == 1 + begin + tx_d <= tx_d_an; + tx_ctl <= tx_ctl_an; + end + TX_MODE_IDLE: + begin + tx_d <= tx_d_idle; + tx_ctl <= tx_ctl_idle; + end + default: + begin + tx_d <= tx_data_pkt; + tx_ctl <= tx_ctl_pkt; + 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 + + /* AN SM */ + always @(*) + begin + tx_f_an = 1'b1; + case(tx_cnt[0]) + 3'd0: + begin + tx_d_an = 8'hDD; + tx_ctl_an = 2'b00; + end + default: begin + tx_d_an = 8'hDD; + tx_ctl_an = 2'b00; + end + endcase + end + + /* IDLE SM */ + always @(*) + begin + tx_f_idle = 1'b1; + case(tx_cnt[0]) + 3'd0: + begin + tx_d_idle = 8'hDD; + tx_ctl_idle = 2'b00; + end + default: begin + tx_d_idle = 8'hDD; + tx_ctl_idle = 2'b00; + end + endcase + end + + /* + * TX Finished Logic + */ + assign tx_finished = tx_last_byte || tx_cnt == 11'd2000; + + /* + * Transmit Packet State Machine + * + */ + always @(posedge tx_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 ) // + tx_state <= TX_ST_1; + TX_ST_1: if ( tx_sample && tx_cnt == 11'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_cnt + TX_ST_3: if ( tx_sample ) + tx_state <= TX_ST_4; // preamble 0xD5 + TX_ST_4: if ( tx_sample && tx_finished && tx_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_cnt >= 60 ) // pad state, test for sufficient frame size + tx_state <= TX_ST_6; + TX_ST_6: if ( tx_sample && fcs_tx_addr == 2'b10 ) // Start FCS + tx_state <= TX_ST_7; + TX_ST_7: if (tx_sample && fcs_tx_addr == 2'b11 ) // Finish FCS + tx_state <= TX_ST_EOP; + TX_ST_EOP: tx_state <= TX_ST_0; // EOP + 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_ctl_pkt = 2'b11; + tx_last_byte = 1'b0; + fcs_tx_init = 1'b0; + fcs_tx_addr_e = 1'b0; + fcs_tx_dout = tx_fifo_d_m1[7:0]; + case(tx_state) + TX_ST_0: + begin + tx_data_pkt = 8'hDD; // start of packet + tx_ctl_pkt = 2'b00; + fcs_tx_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_cnt <= SZ_ETH_HEADER) begin + tx_data_pkt = dpr_di_reg_m1[7:0]; // packet headers + fcs_tx_dout = dpr_di_reg_m1[7:0]; + end + else if (tx_mode == TX_MODE_XMT_CUSTOM && tx_cnt <= SZ_ETH_HEADER + SZ_IPV4_HEADER + SZ_UDP_HEADER ) + if(tx_src_sel == TX_SRC_SEL_UC) begin + tx_data_pkt = tx_uc_fifo_d[7:0]; // packet headers + fcs_tx_dout = tx_uc_fifo_d[7:0]; + end + else begin // mle + tx_data_pkt = tx_mle_fifo_d[7:0]; + fcs_tx_dout = tx_mle_fifo_d[7:0]; + end + else if (tx_mode == TX_MODE_XMT_CUSTOM) + begin + tx_data_pkt = tx_fifo_d_m2[7:0]; // read data from FIFO with extra delay + fcs_tx_dout = tx_fifo_d_m2[7:0]; + tx_last_byte = tx_fifo_d_m2[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_tx_dout = 8'h00; + end + TX_ST_6: begin + tx_data_pkt = fcs_tx_din; // read from fcs + fcs_tx_addr_e = 1'b1; + end + TX_ST_7: begin + tx_data_pkt = fcs_tx_din; // read from fcs + fcs_tx_addr_e = 1'b1; + end + TX_ST_EOP: + begin + tx_data_pkt = 8'hDD; // end of packet + tx_ctl_pkt = 2'b00; + tx_f_pkt = 1'b1; + end + default: + begin + tx_data_pkt = 8'hDD; + tx_ctl_pkt = 2'b00; + 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_EOP ) // 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_cnt > SZ_ETH_HEADER + SZ_IPV4_HEADER + SZ_UDP_HEADER - 3) + tx_fifo_re = 1'b1; + else + tx_fifo_re = 1'b0; + + + + + // tx_cnt: count number of transmit bytes / octets + always @(posedge tx_clk, negedge rstn) + if (!rstn) + tx_cnt <= 11'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_cnt <= 11'h0; + else if ( tx_state == TX_ST_2 ) + tx_cnt <= 11'h0; // start counting the Ethernet Frame after preamble + else if (tx_cnt < 11'd2000) + tx_cnt <= tx_cnt + 1'b1; + + /* + * pipeline data from FIFO + */ + always @(posedge tx_clk or negedge rstn) + begin + if ( !rstn ) begin + tx_fifo_d_m1 <= 9'h0; + tx_fifo_d_m2 <= 9'h0; + end + else begin + tx_fifo_d_m1 <= tx_fifo_d; + tx_fifo_d_m2 <= tx_fifo_d_m1; + end + end + + /* + * FCS + */ + always @(posedge tx_clk or negedge rstn) + begin + if ( !rstn ) + fcs_tx_addr <= 2'b00; + else if (tx_sample) + if ( !fcs_tx_addr_e ) + fcs_tx_addr <= 2'b00; + else + fcs_tx_addr <= fcs_tx_addr + 1'b1; + end + + always @(*) + if (mode_1Gbit && (tx_state == TX_ST_4 || tx_state == TX_ST_5) ) + fcs_tx_enable = 1'b1; + else if ( mode_100Mbit && tx_sample && (tx_state == TX_ST_4 || tx_state == TX_ST_5) ) + fcs_tx_enable = 1'b1; + else + fcs_tx_enable = 1'b0; + + /* + * DPRAM, param ram Control for TAP port + */ + + + always @(posedge tx_clk, negedge rstn) + if (!rstn) + dpr_ad <= 11'h0; + else if (tx_sample) + if ( mode_100Mbit && tx_state == TX_ST_1 && tx_cnt == 11'h5 ) + dpr_ad <= 'h0; + else if ( mode_1Gbit && tx_state == TX_ST_1 && tx_cnt == 11'h1 ) + dpr_ad <= 'h0; + else + dpr_ad <= dpr_ad + 1'b1; + + + always @(posedge tx_clk or negedge rstn) + if ( !rstn ) begin + dpr_di_reg <= 9'h0; + dpr_di_reg_m1 <= 9'h0; + end + else if (tx_sample) begin + dpr_di_reg <= dpr_di; + dpr_di_reg_m1 <= dpr_di_reg; + end + + + assign dpr_we = 1'b0; + assign dpr_ce = 1'b1; + 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 tx_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 tx_clk or negedge rstn) + if (!rstn) + tx_eop <=1'b0; + else if ( tx_state == TX_ST_EOP ) + tx_eop <= 1'b1; + else + tx_eop <= 1'b0; + + /* TX Debug and Metrics */ + always @(posedge tx_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; + + always @(posedge tx_clk, negedge rstn) + if (!rstn) + tx_pkt_cnt <= 16'd0; + else if (tx_eop) + tx_pkt_cnt <= tx_pkt_cnt + 1'b1; + + + +endmodule @@ -1,6 +1,7 @@ /* * mdio.v * + * Copyright (C) 2025 Private Island Networks Inc. * Copyright (C) 2018, 2019 Mind Chasers Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,8 +21,6 @@ */ -`timescale 1ns /10ps - module mdio( input rstn, input mdc, // clock @@ -69,7 +68,7 @@ module mdio( else if ( ld ) state <= 0; else if ( run ) - state <= state + 1; + state <= state + 1'b1; end // register data for MDIO TX diff --git a/src/mdio_cont.v b/src/mdio_cont.v index ad595fd..a31a30d 100644 --- a/src/mdio_cont.v +++ b/src/mdio_cont.v @@ -1,6 +1,7 @@ /* - * mdio_cont.v + * mdio_cont.v * + * Copyright (C) 2025 Private Island Networks Inc. * Copyright (C) 2018, 2019 Mind Chasers Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,9 +20,7 @@ * */ -`timescale 1ns /10ps - -module mdio_controller #(parameter ADDR_SZ = 6) +module mdio_cont #(parameter ADDR_SZ = 6) ( // system interface @@ -86,69 +85,59 @@ module mdio_controller #(parameter ADDR_SZ = 6) /* read the eop bit during S1 */ always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - work_done <= 1'b0; - else if ( cont_state == S1 && di[7] == 1'b1 ) - work_done <= 1'b1; - else - work_done <= 1'b0; - end + if ( !rstn ) + work_done <= 1'b0; + else if ( cont_state == S1 && di[7] == 1'b1 ) + work_done <= 1'b1; + else + work_done <= 1'b0; /* work_run */ always @(posedge clk or negedge rstn) - begin - if ( !rstn ) - work_run <= 1'b0; - else if ( work_start == 1'b1 ) - work_run <= 1'b1; - else if ( work_done == 1'b1 ) - work_run <= 1'b0; - end + if ( !rstn ) + work_run <= 1'b0; + else if ( work_start == 1'b1 ) + work_run <= 1'b1; + else if ( work_done == 1'b1 ) + work_run <= 1'b0; /* set RWN for duration of cyle */ always @(posedge clk or negedge rstn) - begin - if ( rstn == 1'b0 || work_done ==1'b1 ) - begin - rwn <= 1'b1; - end - else if ( cont_state == S1 ) - begin - rwn <= di[5]; - end - end + if (!rstn) + rwn <= 1'b1; + else if (work_done) + rwn <= 1'b1; + else if ( cont_state == S1 ) + rwn <= di[5]; /* reg_addr is the mdio register address */ always @(posedge clk or negedge rstn) - begin - if ( rstn == 1'b0 || work_done ==1'b1 ) - reg_addr <= 5'h0; - else if ( cont_state == S1 ) - reg_addr <= di[4:0]; - end + if (!rstn) + reg_addr <= 5'h0; + else if (work_done) + reg_addr <= 5'h0; + else if ( cont_state == S1 ) + reg_addr <= di[4:0]; /* addr is the program address */ always @(posedge clk or negedge rstn) - begin - if (rstn == 1'b0 || work_done == 1'b1 ) - addr <= 0; - else if ( work_start == 1'b1 ) - addr <= routine_addr[ADDR_SZ-1:0]; - else if ( cont_state == S3 || cont_state == S4 || cont_state == S8 ) - addr <= addr + 1; - end + if (!rstn) + addr <= 0; + else if (work_done) + addr <= 0; + else if (work_start) + addr <= routine_addr[ADDR_SZ-1:0]; + else if ( cont_state == S3 || cont_state == S4 || cont_state == S8 ) + addr <= addr + 1'b1; // latch the write data to mdio always @(posedge clk or negedge rstn) - begin - if (rstn == 0) - dout <= 16'h0000; - else if ( ld_dl == 1'b1 ) - dout[7:0] <= di; - else if ( ld_dh == 1'b1 ) - dout[15:8] <= di; - end + if (rstn == 0) + dout <= 16'h0000; + else if ( ld_dl == 1'b1 ) + dout[7:0] <= di; + else if ( ld_dh == 1'b1 ) + dout[15:8] <= di; // combinatorial logic here assign ld_dl = ( cont_state == S4 ) ? 1'b1 : 1'b0; diff --git a/src/mdio_data_ti.v b/src/mdio_data_ti.v index aef537d..b2e7cf4 100644 --- a/src/mdio_data_ti.v +++ b/src/mdio_data_ti.v @@ -1,6 +1,7 @@ /* * mdio_data_ti.v ( TI DP83867 PHY ) * + * Copyright (C) 2025 Private Island Networks Inc. * Copyright (C) 2018, 2019 Mind Chasers Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,14 +20,9 @@ * */ - - -`timescale 1ns /10ps - module mdio_data_ti #(parameter ADDR_SZ = 7) ( // params that alter the data returned - input [4:0] page, input [4:0] reg_addr, input [7:0] data_in_h, input [7:0] data_in_l, @@ -36,93 +32,94 @@ module mdio_data_ti #(parameter ADDR_SZ = 7) input [ADDR_SZ-1:0]ad, output [7:0]d ); - -localparam R = 8'h20; -localparam W = 8'h00; -localparam EOP = 8'h80; - -localparam REGCR = 5'h0d; -localparam ADDAR = 5'h0e; - -reg [7:0]data; - -assign d = oe ? data : 8'hzz; - -// register 22 is page -always @ (*) -begin - case (ad) - /* - Subroutine: SGMII Init ( i ) Not Needed for TI PHY, but read PHY Control Register (PHYCR), Address 0x0010 - */ - - // read the PHYCR register, SGMII Enable is bill 11 - 0: data = R|8'h10; // pg 18, reg 20 - - /* - Subroutine: Read Live Status ( s ) - */ - // read the live copper status PHYSTS (0x17) - 20 : data = R|8'h11; // read addr[17] - - /* - Subroutine: Dump Registers 0:3 - */ - 25: data = R|8'd0; - 26 : data = R|8'd1; - 27 : data = R|8'd2; - 28 : data = R|8'd3; - - /* - Subroutine: : Loopback ( 0_0.14 ) - */ - 40 : data = W|8'd0; // write addr[0] - 44 : data = 8'b01000000; // collision, speed select, reserved - 45 : data = 8'b01010001; // reset, loopback, speed, AN enable, power down, isolate, restart AN, duplex; - - // read it back - 46: data = R|8'd0; - - /* - Subroutine: : Read a register. - */ - 48: data = { 3'b001, reg_addr }; - - /* - Subroutine: : Write a register. - */ - 50: data = { 3'b000, reg_addr }; - 51: data = data_in_l; - 52: data = data_in_h; - // read it back - // 53: data = { 3'b001, reg_addr }; - - // y: extended read - 60: data = {3'b000 , REGCR }; - 61: data = 8'h1f; - 62: data = 8'h00; - // Write the extended address to 0xe - 63: data = { 3'b000, ADDAR }; - 64: data = data_in_l; - 65: data = data_in_h; - // Write 0x401f to 0xd - 66: data = { 3'b000, REGCR }; - 67: data = 8'h1f; - 68: data = 8'h40; - // Read value in extended register: read 0x0E - 69: data = { 3'b001, ADDAR }; - - // z: extended write - // Write value in extended register: 0x0E - 80: data = { 3'b000, ADDAR }; - 81: data = data_in_l; - 82: data = data_in_h; - // read it back - 83: data = { 3'b001, ADDAR }; - - default: data = R|EOP; - endcase -end + + localparam R = 8'h20; + localparam W = 8'h00; + localparam EOP = 8'h80; + + /* REGCR[15:14] holds the access function: address (00), data with no post increment (01), + data with post increment on read and writes (10) and data with post increment on writes only (11). */ + localparam REGCR = 5'h0d; // MDIO Manageable MMD access control + localparam ADDAR = 5'h0e; + + reg [7:0]data; + + assign d = oe ? data : 8'h00; + + always @ (*) + begin + case (ad) + /* + Subroutine: SGMII Init ( i ): read PHY Control Register (PHYCR), Address 0x0010 + */ + + // read the PHYCR register, SGMII Enable is bit 11 + 0: data = R|8'h10; // + + /* + Subroutine: Read Live Status ( s ) + */ + // read the live copper status PHYSTS (0x0011) + 20 : data = R|8'h11; // read addr[17] + + /* + Subroutine: Dump Registers 0:3 + */ + 25: data = R|8'd0; + 26 : data = R|8'd1; + 27 : data = R|8'd2; + 28 : data = R|8'd3; + + /* + Subroutine: : Loopback + */ + 40 : data = W|8'd0; // write addr[0] + 44 : data = 8'b01000000; // collision, speed select, reserved + 45 : data = 8'b01010001; // reset, loopback, speed, AN enable, power down, isolate, restart AN, duplex; + + // read it back + 46: data = R|8'd0; + + /* + Subroutine: : Read a register from 0 to 31. + */ + 48: data = { 3'b001, reg_addr }; + + /* + Subroutine: : Write a register from 0 to 31. + */ + 50: data = { 3'b000, reg_addr }; + 51: data = data_in_l; + 52: data = data_in_h; + // read it back + // 53: data = { 3'b001, reg_addr }; + + // y: extended read + 60: data = {3'b000 , REGCR }; + 61: data = 8'h1f; + 62: data = 8'h00; + // Write the extended address to 0xe + 63: data = { 3'b000, ADDAR }; + 64: data = data_in_l; + 65: data = data_in_h; + // Write 0x401f to 0xd + 66: data = { 3'b000, REGCR }; + 67: data = 8'h1f; + 68: data = 8'h40; + // Read value in extended register: read 0x0E + 69: data = { 3'b001, ADDAR }; + + // z: extended write + // Write value in extended register: 0x0E + 80: data = { 3'b000, ADDAR }; + 81: data = data_in_l; + 82: data = data_in_h; + // read it back + 83: data = { 3'b001, ADDAR }; + + default: data = R|EOP; + endcase + end endmodule diff --git a/src/metrics.v b/src/metrics.v deleted file mode 100644 index bce3389..0000000 --- a/src/metrics.v +++ /dev/null @@ -1,102 +0,0 @@ -/* - * metrics.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: Collect metrics here and register them for transmit - * - * - */ - -`timescale 1ns /10ps - -module metrics( - input rstn, - input clk, - input mode_100Mbit, - - // input data for gathering metrics - input [3:0] rx_mac_keep, - input rx_pf_keep_01, - input rx_pf_keep_02, - input rx_pf_keep_10, - input rx_pf_keep_12, - input rx_pf_keep_20, - input rx_pf_keep_21, - input rx_pf_keep_23, - - input [3:0] rx_eop, - input [3:0] rx_sop, - input [3:0] tx_eop, - input [3:0] tx_sop, - - // metric outputs - input metrics_start, - output reg [8:0] metrics_d -); - -reg [7:0] rx_pkt_cnt; -reg [7:0] tx_pkt_cnt; - -reg [7:0] rx0_drop_cnt; -reg [7:0] rx1_drop_cnt; - -reg [3:0] bit_cnt; -reg [3:0] addr; - -always @(posedge clk or negedge rstn) - if (!rstn) begin - bit_cnt <= 4'h0; - addr <= 4'h0; - end - else if ( metrics_start ) begin - bit_cnt <= 4'h0; - addr <= 4'h0; - end - else if ( !mode_100Mbit || ( mode_100Mbit && bit_cnt == 4'h9 ) ) begin - bit_cnt <= 4'h0; - addr <= addr + 1; - end - else - bit_cnt <= bit_cnt + 1; - -always @(posedge clk or negedge rstn) - if (!rstn) - rx_pkt_cnt <= 'h0; - else if (rx_eop[2]) - rx_pkt_cnt <= rx_pkt_cnt + 1; - -always @(posedge clk or negedge rstn) - if (!rstn) - tx_pkt_cnt <= 'h0; - else if (tx_eop[2]) - tx_pkt_cnt <= tx_pkt_cnt + 1; - -always @(posedge clk or negedge rstn) - if (!rstn) - metrics_d <= 9'h100; - else begin - case(addr) - 'h0: metrics_d <= { 1'b0, rx_pkt_cnt }; - 'h1: metrics_d <= { 1'b0, tx_pkt_cnt }; - 'h2: metrics_d <= { 1'b0, rx0_drop_cnt }; - 'h3: metrics_d <= { 1'b1, rx1_drop_cnt }; - default: metrics_d <= 9'h100; - endcase - end - - - -endmodule diff --git a/src/ml_engine.v b/src/ml_engine.v new file mode 100644 index 0000000..ec50d87 --- /dev/null +++ b/src/ml_engine.v @@ -0,0 +1,563 @@ +/* + * ml_engine.v + * + * Copyright (C) 2025 Private Island Networks 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: Machine Learning Engine Framework + * + * see https://privateisland.tech/dev/pi-ml-framework + * + */ + +module ml_engine #(parameter NUM_IF=8, DPRAM_DEPTH=1024) +( + input rstn, + input clk, + + // controller interface + input cont_clk, + input cont_sel, + input cont_we, + input [15:0] cont_addr, + input [15:0] cont_d_i, + output reg [15:0] cont_d_o, + output cont_tgt_ready, + + // module interface + output reg evt_start, + output reg evt_active, + output reg [NUM_IF-1:0] enable, + input [NUM_IF-1:0] empty, + input clk_e, + input we, + input [8:0] d_i, + + // switch interface + output reg fifo_empty_o, + input fifo_re, + output [8:0] fifo_d_o, + output [10:0] byte_cnt + + // action interface + + // memory/coefficient interface + +); + +`define DIRECT_OUTPUT // this disables processing and second DPRAM + + localparam BLOCK_OFFSET = 'h80; // 128 bytes + localparam BLOCK_OFFSET_SHIT = 'd7; // Left shift to multiple by 128 + +`ifdef SIMULATION + localparam EVT_CNT_DELAY_1 = 32'h0000_0010, + EVT_CNT_DELAY_2 = 32'h0000_0020, + EVT_CNT_OUT = 32'h0000_0030, + EVT_CNT_STOP = 32'h0000_00C0, + EVT_CNT_MAX = 32'h0000_0100; +`else + localparam EVT_CNT_DELAY_1 = 32'h0000_0010, + EVT_CNT_DELAY_2 = 32'h0000_0020, + EVT_CNT_OUT = 32'h0000_0030, + EVT_CNT_STOP = 32'h0800_0000, + EVT_CNT_MAX = 32'h1000_0000; // Sets max event interval +`endif + + + // Controller I/F Addresses + localparam MLE_ENABLE_ADDR = 'h0; + + // FSM States: two loops: event/block and data unit (DU) + localparam MLE_ST_IDLE=4'h0, MLE_ST_START=4'h1, MLE_ST_EVT_START = 4'h2, + MLE_ST_DU_START = 4'h3, MLE_ST_DU_CONT = 4'h4, MLE_ST_DU_DONE = 4'h5, + MLE_ST_EVT_DONE = 4'h6; + + // variables + reg mle_enable, mle_enable_m1, mle_enable_m2; + reg[31:0] evt_counter; + reg evt_delay_1, evt_delay_2, evt_delay_out; + + reg [NUM_IF-1:0] empty_m1, empty_m2; + + // Set up 1K DPRAM as 8 blocks of 128 words. + wire [$clog2(DPRAM_DEPTH)-1:0] wr_addr0, wr_addr1; + wire [$clog2(DPRAM_DEPTH)-1:0] rd_addr0, rd_addr1; + reg [2:0] wr_block0, wr_block1; + reg [2:0] rd_block0, rd_block1; + reg [6:0] wr_ptr0, wr_ptr1; + reg [6:0] rd_ptr0, rd_ptr1; + wire rd_oe0; + reg [6:0] cnt0; + reg [$clog2(DPRAM_DEPTH)-1:0] pkt_sz; + + wire [8:0] d_s0_o, d_i_internal; + + reg [NUM_IF-1:0] enable_logic_active; // enable logic active + + wire we0, we1; + + reg [3:0] mle_0_state, mle_1_state, mle_2_state; + reg d_out_avail; + wire [8:0] fifo_d; + reg fifo_empty; + reg fifo_d_out_flag; + + // Debug + reg [8*12:1] mle_0_state_str; + + + /****************************************************** + + Controller Interface + + System's internal controller can write and read important parameters + + ******************************************************/ + + // Controller Read Data Mux + always @(posedge cont_clk, negedge rstn) + if (!rstn) + cont_d_o <= 16'hcccc; + else + case (cont_addr) + MLE_ENABLE_ADDR: cont_d_o <= mle_enable; + default: cont_d_o <= cont_d_o; + endcase + + // TODO: add logic to prevent controller reading metastable data + assign cont_tgt_ready = 1'b1; + + + // mle_enable: enable / disable the MLE + always @(posedge cont_clk, negedge rstn) + if (!rstn) + mle_enable <= 1'b0; + else if (cont_we && cont_sel && cont_addr == MLE_ENABLE_ADDR) + mle_enable <= cont_d_i[0]; + + // synchronizer for controller vars + always @(posedge clk, negedge rstn) + if( !rstn ) begin + mle_enable_m1 <= 1'b0; + mle_enable_m2 <= 1'b0; + end + else begin + mle_enable_m1 <= mle_enable; + mle_enable_m2 <= mle_enable_m1; + end + + /****************************************************** + + Event Logic + + ******************************************************/ + + // evt_counter: + always @(posedge clk, negedge rstn) + if( !rstn ) + evt_counter <= EVT_CNT_MAX; + else if (evt_counter == EVT_CNT_MAX) + evt_counter <= 'd0; + else + evt_counter <= evt_counter + 1'b1; + + // evt_start: + always @(posedge clk, negedge rstn) + if( !rstn ) + evt_start <= 'd0; + else if (mle_enable_m2 && evt_counter == 32'd0) + evt_start <= 1'b1; + else + evt_start <= 1'b0; + + // evt_active: + always @(posedge clk, negedge rstn) + if( !rstn ) + evt_active <= 'd0; + else if (evt_counter == EVT_CNT_STOP) + evt_active <= 1'b0; + else if (evt_start) + evt_active <= 'd1; + + /****************************************************** + + FSM_0, Accept data from RX modules into DPRAM Step 0 + + ******************************************************/ + + // FSM_0: + always @(posedge clk, negedge rstn) + if (!rstn) + mle_0_state <= MLE_ST_IDLE; + else + case (mle_0_state) + MLE_ST_IDLE: if (evt_start && !(&empty)) + mle_0_state <= MLE_ST_EVT_START; + MLE_ST_EVT_START: if (!(&empty)) // Is a DU available? + mle_0_state <= MLE_ST_DU_START; + MLE_ST_DU_START: + mle_0_state <= MLE_ST_DU_CONT; + MLE_ST_DU_CONT: if (d_i[8]) // Done flag set? + mle_0_state <= MLE_ST_DU_DONE; + MLE_ST_DU_DONE: if (&empty) // Is another DU available? + mle_0_state <= MLE_ST_EVT_DONE; + else + mle_0_state <= MLE_ST_DU_START; + MLE_ST_EVT_DONE: if (1'b1) + mle_0_state <= MLE_ST_IDLE; + default: mle_0_state <= mle_0_state; + endcase + + + always @(*) + case(mle_0_state) + MLE_ST_IDLE: mle_0_state_str <= "IDLE"; + MLE_ST_START: mle_0_state_str <= "START"; + MLE_ST_EVT_START: mle_0_state_str <= "EVT_START"; + MLE_ST_DU_START: mle_0_state_str <= "DU_START"; + MLE_ST_DU_CONT: mle_0_state_str <= "DU_CONT"; + MLE_ST_DU_DONE: mle_0_state_str <= "DU_DONE"; + MLE_ST_EVT_DONE: mle_0_state_str <= "EVT_DONE"; + default: mle_0_state_str <= "UNDEFINED"; + endcase + + + // wr_block0: + always @(posedge clk, negedge rstn) + if( !rstn ) + wr_block0 <= 'd1; + else if (evt_start) + wr_block0 <= wr_block0 + 1'b1; + + // wr_ptr0: dpram_s0 write address + always @(posedge clk, negedge rstn) + if( !rstn ) + wr_ptr0 <= 'd0; + else if (!(|enable)) + wr_ptr0 <= 'd0; + else if (we0) + wr_ptr0 <= wr_ptr0 + 1'b1; + + assign wr_addr0 = (wr_block0 << BLOCK_OFFSET_SHIT) + wr_ptr0; + assign we0 = we; + + // enable_logic_active: assert a bit to indicate which enable logic block is active + always @(posedge clk, negedge rstn) + if( !rstn ) + enable_logic_active <= 'd0; + else if (mle_0_state == MLE_ST_IDLE) + enable_logic_active <= 'd0; + else if (mle_0_state == MLE_ST_EVT_START) + enable_logic_active <= 1'b1; + else if (mle_0_state == MLE_ST_DU_DONE) + enable_logic_active <= enable_logic_active << 1; + + // enable logic: assert each enable until empty + generate + genvar i; + for (i=0; i< NUM_IF; i=i+1) begin: enable_logic + always @(posedge clk, negedge rstn) + if(!rstn) + enable[i] <= 1'b0; + else if (enable[i] && empty[i]) + enable[i] <= 1'b0; + else if (enable_logic_active[i] && !empty[i]) + enable[i] <= 1'b1; + else + enable[i] <= 1'b0; + + always @(posedge clk, negedge rstn) + if(!rstn) begin + empty_m1[i] <= 1'b1; + empty_m2[i] <= 1'b1; + end + else begin + empty_m1[i] <= empty[i]; + empty_m2[i] <= empty_m1[i]; + end + end + endgenerate + + // cnt0: count number of words written into dpram_s0 per event cycle + always @(posedge clk, negedge rstn) + if( !rstn ) + cnt0 <= 'd0; + else if (!evt_active) + cnt0 <= 'd0; + else if (|enable && !(|empty)) + cnt0 <= cnt0 + 1'b1; + + + // Instantiate x9 DPRAM for Step 0 DPRAM + dpram_inf #(.ADDR_WIDTH(10),.DPRAM_INIT("mle_ram_0.txt")) dpram_s0( + .rstn(rstn), + .a_clk(clk), + .a_clk_e(clk_e), + .a_we(we0), + .a_oe(1'b0), + .a_addr(wr_addr0), + .a_din(d_i), + .a_dout(), + // port B + .b_clk(clk), + .b_clk_e(1'b1), + .b_we(1'b0), + .b_oe(rd_oe0), + .b_addr(rd_addr0), + .b_din(9'h0), + .b_dout(d_s0_o) + ); + +`ifdef DIRECT_OUTPUT + + assign rd_oe0 = fifo_re; + assign fifo_d = d_s0_o; + assign fifo_d_o[7:0] = fifo_empty ? 8'h00 : d_s0_o[7:0]; + assign fifo_d_o[8] = fifo_empty_o ? 1'b1 : 1'b0; + assign rd_addr0 = rd_addr1; + assign wr_addr1 = wr_addr0; + assign we1 = we0; + +`else + + + /****************************************************** + + FSM_1: Read data from DPRAM Step 0. + read and store code. FSM logic/path may depend on code + Do I need a size field? + + ******************************************************/ + + // evt_delay_1: + always @(posedge clk, negedge rstn) + if( !rstn ) + evt_delay_1 <= 'd0; + else if (evt_counter == EVT_CNT_DELAY_1) + evt_delay_1 <= 1'b1; + else + evt_delay_1 <= 1'b0; + + // FSM_1: + always @(posedge clk, negedge rstn) + if (!rstn) + mle_1_state <= MLE_ST_IDLE; + else if (evt_active) + case (mle_1_state) + MLE_ST_IDLE: if (evt_delay_1) + mle_1_state <= MLE_ST_DU_START; + MLE_ST_DU_START: + mle_1_state <= MLE_ST_DU_CONT; + MLE_ST_DU_CONT: if (d_s0_o[8]) + mle_1_state <= MLE_ST_DU_DONE; + MLE_ST_DU_DONE: if (d_s0_o[8]) + mle_1_state <= MLE_ST_IDLE; + else + mle_1_state <= MLE_ST_DU_START; + default: mle_1_state <= mle_1_state; + endcase + + // rd_block0: + always @(posedge clk, negedge rstn) + if( !rstn ) + rd_block0 <= 'd0; + else if (evt_delay_1) + rd_block0 <= wr_block0; + + // rd_ptr0: dpram_s0 read pointer + always @(posedge clk, negedge rstn) + if( !rstn ) + rd_ptr0 <= 'd0; + else + rd_ptr0 <= rd_ptr0 + 1'b1; + + assign rd_addr0 = rd_block0 << BLOCK_OFFSET_SHIT + rd_ptr0; + + // rd_oe0: + assign rd_oe0 = 1'b1; + + // wr_block1: + always @(posedge clk, negedge rstn) + if( !rstn ) + wr_block1 <= 'd1; + else if (evt_delay_2) + wr_block1 <= wr_block0; + + // wr_ptr1: dpram_s1 write address + always @(posedge clk, negedge rstn) + if( !rstn ) + wr_ptr1 <= 'd0; + else if (1'b0) + wr_ptr1 <= wr_ptr1 + 1'b1; + + assign wr_addr1 = wr_block1 << BLOCK_OFFSET_SHIT + wr_ptr1; + + + + // we1: write enable for dpram_s1 + always @(posedge clk, negedge rstn) + if( !rstn ) + we1 <= 1'b0; + else if (1'b0) + we1 <= 1'b1; + else + we1 <= 1'b0; + + + + // Instantiate 1k x 9 DPRAM + // dpram_s1: port B interfaces with Switch + dpram_inf #(.ADDR_WIDTH(10), .DPRAM_INIT("mle_ram_1.txt")) dpram_s1( + .rstn(rstn), + .a_clk(clk), + .a_clk_e(1'b1), + .a_we(we1), + .a_oe(1'b0), + .a_addr(wr_addr1), + .a_din(d_i_internal), + .a_dout(), + // port B + .b_clk(clk), + .b_clk_e(1'b1), + .b_we(1'b0), + .b_oe(fifo_re), + .b_addr(rd_addr1), + .b_din(9'h0), + .b_dout(fifo_d) + ); + + /****************************************************** + + FSM_2, Transfer data from Processing to DPRAM Out + + ******************************************************/ + + // evt_delay_2: + always @(posedge clk, negedge rstn) + if( !rstn ) + evt_delay_2 <= 'd0; + else if (evt_counter == EVT_CNT_DELAY_2) + evt_delay_2 <= 1'b1; + else + evt_delay_2 <= 1'b0; + + + // FSM_2: + always @(posedge clk, negedge rstn) + if (!rstn) + mle_2_state <= MLE_ST_IDLE; + else if (evt_active) + case (mle_2_state) + MLE_ST_EVT_START: if (1'b1) + mle_2_state <= MLE_ST_DU_START; + MLE_ST_DU_START: if (1'b1) + mle_2_state <= MLE_ST_DU_CONT; + MLE_ST_DU_CONT: if (1'b1) + mle_2_state <= MLE_ST_DU_DONE; + MLE_ST_DU_DONE: if (1'b1) + mle_2_state <= MLE_ST_EVT_DONE; + MLE_ST_EVT_DONE: if (1'b1) + mle_2_state <= MLE_ST_IDLE; + default: mle_2_state <= mle_2_state; + endcase + + + /****************************************************** + + Output, Switch reads from dpram_s1 + + ******************************************************/ + + +`endif + + // d_out_avail: data for output is available + always @(posedge clk, negedge rstn) + if( !rstn ) + d_out_avail <= 1'b0; + else if (evt_delay_out) + d_out_avail <= 1'b0; + else if (we1) + d_out_avail <= 1'b1; + + // evt_delay_out: + always @(posedge clk, negedge rstn) + if( !rstn ) + evt_delay_out <= 'd0; + else if (evt_counter == EVT_CNT_OUT && d_out_avail) + evt_delay_out <= 1'b1; + else + evt_delay_out <= 1'b0; + + // fifo_d_out_flag: bit 8 from dpram_s1 + always @(posedge clk, negedge rstn) + if (!rstn) + fifo_d_out_flag <= 1'b0; + else if (fifo_re) + fifo_d_out_flag <= fifo_d[8]; + else + fifo_d_out_flag <= 1'b0; + + assign byte_cnt = BLOCK_OFFSET; + + + // fifo_empty: assert when the last byte from the DPRAM is read + always @(posedge clk, negedge rstn) + if( !rstn ) + fifo_empty <= 1'b1; + else if (fifo_d_out_flag) + fifo_empty <= 1'b1; + else if (evt_delay_out) + fifo_empty <= 1'b0; + + + // fifo_empty_o: module output to indicate to reader to stop + always @(posedge clk, negedge rstn) + if( !rstn ) + fifo_empty_o <= 1'b1; + else if (rd_addr1 == (rd_block1 << BLOCK_OFFSET_SHIT) + (byte_cnt-1)) + fifo_empty_o <= 1'b1; + else if (evt_delay_out) + fifo_empty_o <= 1'b0; + + + // rd_block1: + always @(posedge clk, negedge rstn) + if( !rstn ) + rd_block1 <= 'd0; + else if (evt_delay_out) + rd_block1 <= wr_block0; + + // rd_ptr1: dpram_s1 write address + always @(posedge clk, negedge rstn) + if( !rstn ) + rd_ptr1 <= 'd0; + else if (evt_delay_out) + rd_ptr1 <= 'd0; + else if (fifo_re) + rd_ptr1 <= rd_ptr1 + 1'b1; + + assign rd_addr1 = (rd_block1 << BLOCK_OFFSET_SHIT) + rd_ptr1; + + // pkt_sz: store number of bytes in dpram_s1 to use as packet size + always @(posedge clk, negedge rstn) + if( !rstn ) + pkt_sz <= 'd0; + else + pkt_sz <= pkt_sz; + + + +endmodule diff --git a/src/pkt_filter.v b/src/pkt_filter.v index 9140c74..843315a 100644 --- a/src/pkt_filter.v +++ b/src/pkt_filter.v @@ -1,7 +1,8 @@ /* - * pkt_filter.v + * pkt_filter.v * - * Copyright 2018, 2019, 2020, 2021 Mind Chasers Inc. + * Copyright (C) 2025 Private Island Networks Inc. + * Copyright (C) 2018-2022 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. @@ -19,26 +20,24 @@ * */ -`timescale 1ns /10ps - -module pkt_filter #(parameter DEPTH = 4, - parameter DEPTHW = 2, - parameter WIDTH = 32) +module pkt_filter #(parameter DEPTH = 8, parameter DATAW = 32) ( input rstn, input clk, // input for programming + input prgclk, input sel, input we, - input [DEPTHW+1:0] addr, - input [7:0] d_in, + input [$clog2(DEPTH)-1:0] addr, + input [DATAW-1:0] d_i, + output [DATAW-1:0] d_o, // registered data - input[7:0] rx_data_m1, - input[7:0] rx_data_m2, - input[7:0] rx_data_m3, - input[7:0] rx_data_m4, + input[7:0] rx_d_m1, + input[7:0] rx_d_m2, + input[7:0] rx_d_m3, + input[7:0] rx_d_m4, // filter input new_frame, // assert for each new frame to reset state machines @@ -48,48 +47,47 @@ module pkt_filter #(parameter DEPTH = 4, output reg keep ); -reg trigger_m1; -wire match; - - -/* trigger_m1 is used to sync the CAM search with testing the results below */ -always @(posedge clk or negedge rstn) - if (!rstn) - trigger_m1 <= 1'b0; - else - trigger_m1 <= trigger; - -/* keep is a one shot */ -always @(posedge clk or negedge rstn) - if (!rstn) - keep <= 1'b0; - else if (trigger_m1) - if ( block ) - keep <= 1'b0; - else if ( !invert ) - keep <= match; - else begin - keep <= ~match; - end - else - keep <= 1'b0; - -cam #(.DEPTH(DEPTH), .DEPTHW(DEPTHW), .WIDTH(WIDTH)) cam_0( - .rstn( rstn ), - .clk( clk ), - - // input for programming - .sel( sel ), - .we( we ), - .addr( addr ), - .d_in( d_in ), - // cam action - .search( trigger ), - .search_address( { rx_data_m4, rx_data_m3, rx_data_m2, rx_data_m1 } ), - .match( match ) -); - + reg trigger_m1; + wire match; + /* trigger_m1 is used to sync the CAM search with testing the results below */ + always @(posedge clk or negedge rstn) + if (!rstn) + trigger_m1 <= 1'b0; + else + trigger_m1 <= trigger; + /* keep is a one shot */ + always @(posedge clk or negedge rstn) + if (!rstn) + keep <= 1'b0; + else if (trigger_m1) + if ( block ) + keep <= 1'b0; + else if ( !invert ) + keep <= match; + else begin + keep <= ~match; + end + else + keep <= 1'b0; + + cam #(.DEPTH(DEPTH), .DATAW(DATAW)) cam_0( + .rstn( rstn ), + .clk( clk ), + + // Input for programming + .prgclk(prgclk), + .sel( sel ), + .we( we ), + .addr(addr), + .d_i(d_i), + .d_o(d_o), + // CAM action + .search( trigger ), + .search_address( { rx_d_m4, rx_d_m3, rx_d_m2, rx_d_m1 } ), + .match( match ) + ); + endmodule diff --git a/src/rgmii_params.v b/src/rgmii_params.v new file mode 100644 index 0000000..d0f4390 --- /dev/null +++ b/src/rgmii_params.v @@ -0,0 +1,46 @@ +/* + * rgmii_params.v + * + * Copyright 2025 Private Island Networks 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: RGMII Related Parameters + * + */ + +`ifdef INCLUDED + + // PHY Status Params (Table 4) + localparam LINK_UP = 4'b0001, + LINK_DOWN = 4'b0000; + + localparam CLOCK_SPEED_2_5 = 4'b0000, + CLOCK_SPEED_25 = 4'b0010, + CLOCK_SPEED_125 = 4'b0100, + CLOCK_SPEED_RSVD = 4'b0110; + + localparam FULL_DUPLEX = 4'b1000, + HALF_DUPLEX = 4'b0000; + + localparam D_IDLE = FULL_DUPLEX | CLOCK_SPEED_125 | LINK_UP; + + + // RGMII CTL Bits + localparam NORMAL_INTERFRAME = 2'b00, + CARRIER_STATUS = 2'b01, + ERROR_DATA_RX = 2'b10, + NORMAL_DATA_RX = 2'b11; + + +`endif diff --git a/src/sgmii_params.v b/src/sgmii_params.v deleted file mode 100644 index a36160e..0000000 --- a/src/sgmii_params.v +++ /dev/null @@ -1,55 +0,0 @@ -/* - * sgmii_params.v - * - * Copyright 2018, 2019, 2020 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 Related Parameters - * - */ - - -/* SGMII Speed Bits [11:10] */ - -localparam SGMII_SPEED_10MBIT = 2'b00, - SGMII_SPEED_100MBIT = 2'b01, - SGMII_SPEED_1GBIT = 2'b10, - SGMII_SPEED_RSVD = 2'b11, - SGMII_SPEED_AN = 2'b11; - -/* - * Notes about K Codes: - * Start of Packet: /S/, K27.7, 0xFB - * End of Packet: /T/, K23.7, 0xF7 - * - */ -localparam D2_2 = 8'h42, - D2_5 = 8'ha2, - D5_6 = 8'hC5, - D10_2 = 8'h4a, - D16_2 = 8'h50, - D21_5 = 8'hb5, // used in a Config Code Group - - /* Note that these are only K codes if the k bit is asserted */ - K23_7 = 8'hf7, // /R/ Carrier Extend - K27_7 = 8'hfb, // /S/ Start_of_Packet - K28_0 = 8'h1c, - K28_1 = 8'h3c, - K28_2 = 8'h5c, - K28_3 = 8'h7c, - K28_4 = 8'h9c, - K28_5 = 8'hbc, - K28_6 = 8'hdc, - K29_7 = 8'hfd, // /T/ End_of_Packet - K_ERROR = 8'hee; diff --git a/src/spi.v b/src/spi.v deleted file mode 100644 index d7c054b..0000000 --- a/src/spi.v +++ /dev/null @@ -1,315 +0,0 @@ -/* - * spi.v - * - * Copyright (C) 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: SPI slave controller - * - * refer to SPI protocol figure at https://mindchasers.com/dev/hw-spi - * - */ - -`timescale 1ns /10ps - - module spi( - input rstn, - input clk, - - // SPI signals - input spi_cs, - input spi_clk, - input spi_d_in, - output reg spi_d_o, - output spi_d_oe, - - // internal FPGA memory interface - output [10:0] mem_addr, - output [4:0] mux_sel, - input [8:0] d_i, // data out of FPGA memories - output reg [8:0] d_o, // data into FPGA memories - - // individual memory selects - output reg we, - output oe, - output reg dpram_tx_sel, - output reg dpram_rx_sel, - output reg dpram_ptrs_sel, - output reg [3:0] param_sel, - output reg pkt_filter_sel_01, - output reg pkt_filter_sel_02, - output reg pkt_filter_sel_03, - output reg pkt_filter_sel_10, - output reg pkt_filter_sel_12, - output reg pkt_filter_sel_13, - output reg pkt_filter_sel_20, - output reg pkt_filter_sel_21, - output reg pkt_filter_sel_23, - output reg pkt_filter_sel_2u, - output reg pkt_filter_sel_30, - output reg pkt_filter_sel_31, - output reg pkt_filter_sel_32, - output reg pkt_filter_sel_u2, - output reg interrupts_sel, - output reg[1:0] sci_sel_dual, - output reg[3:0] sci_sel_ch - ); - - localparam start_code = 9'h76; - - wire bit_cnt_rstn; // async reset at start of SPI cycle - - reg spi_clk_m1, spi_clk_m2; // detect / debounce the SPI CLK - reg spi_clk_high, spi_clk_high_m1; // one shot clocks for sequencing events - reg spi_clk_low, spi_clk_low_m1; // one shot clocks for sequencing events - - reg [4:0] bit_cnt; // cnt the bits - reg [6:0] word_cnt; // number of bytes transferred, OK to roll over - - // capture these from SPI bus - reg [7:0] dev_ad; // device address - reg rwn; // follows address - reg [8:0] addr; // address - - wire mem_sel, pkt_filter_sel; // - - // async reset at the start of each SPI transfer - assign bit_cnt_rstn = spi_cs & rstn; - - // debounce and capture the asynch spi_clk - always @(posedge clk or negedge bit_cnt_rstn) - begin - if ( !bit_cnt_rstn ) begin - spi_clk_m1 <= 1'b0; - spi_clk_m2 <= 1'b0; - end - else if ( spi_cs ) begin - spi_clk_m1 <= spi_clk; - spi_clk_m2 <= spi_clk_m1; - end - end - - // create two seq one shots for actions - always @(posedge clk or negedge bit_cnt_rstn) - begin - if ( !bit_cnt_rstn ) - spi_clk_high <= 0; - else if ( spi_cs && spi_clk && spi_clk_m1 && !spi_clk_m2 ) - spi_clk_high <= 1'b1; - else - spi_clk_high <= 1'b0; - end - - always @(posedge clk or negedge bit_cnt_rstn) - begin - if ( !bit_cnt_rstn ) - spi_clk_high_m1 <= 0; - else - spi_clk_high_m1 <= spi_clk_high; - end - - - // create two seq one shots for actions - always @(posedge clk or negedge bit_cnt_rstn) - begin - if ( !bit_cnt_rstn ) - spi_clk_low <= 0; - else if ( spi_cs && spi_clk && !spi_clk_m1 && spi_clk_m2 ) - spi_clk_low <= 1'b1; - else - spi_clk_low <= 1'b0; - end - - always @(posedge clk or negedge bit_cnt_rstn) - begin - if ( !bit_cnt_rstn ) - spi_clk_low_m1 <= 0; - else - spi_clk_low_m1 <= spi_clk_low; - end - - /* - bit_cnt indicates the state of the SPI transfer - - 0:7: dev_ad - 8: rwn - 9:17: addr - 18:26: data (repeats) - */ - always @(posedge clk or negedge bit_cnt_rstn) - begin - if ( !bit_cnt_rstn ) - bit_cnt <= 5'h0; - else if ( spi_cs && spi_clk_high_m1 ) begin - if ( bit_cnt == 5'd26 ) - bit_cnt <= 5'd18; - else - bit_cnt <= bit_cnt + 1; - end - end - - // word_cnt - always @(posedge clk or negedge bit_cnt_rstn) - begin - if (!bit_cnt_rstn) - word_cnt <= 0; - else if ( spi_cs && spi_clk_high && bit_cnt == 5'd26 ) - word_cnt <= word_cnt + 1; - end - - /* Logic to capture common dev_ad, rwn, and address */ - - // capture dev_ad - always @(posedge clk or negedge bit_cnt_rstn) - begin - if ( !bit_cnt_rstn ) - dev_ad <= 'h0; - else if ( spi_cs && spi_clk_high && bit_cnt < 5'd8 ) - dev_ad <= { dev_ad[6:0], spi_d_in }; - end - - // capture rwn bit - always @(posedge clk or negedge bit_cnt_rstn) - begin - if ( !bit_cnt_rstn ) - rwn <= 1'b1; - else if ( spi_cs && spi_clk_high && bit_cnt == 5'd8 ) - rwn <= spi_d_in; - end - - // capture addr - always @(posedge clk or negedge bit_cnt_rstn) - begin - if (!bit_cnt_rstn) - addr <= 'h0; - else if (spi_cs ) begin - if ( spi_clk_high && bit_cnt >= 5'd9 && bit_cnt <= 5'd17 ) - addr <= { addr[7:0], spi_d_in }; - // delay advancing addr until write completes - else if ( spi_clk_high_m1 && bit_cnt == 5'd26 ) - addr <= addr + 1; - end - end - - assign mem_addr = { dev_ad[1:0], addr }; - - /* Logic for write data into FPGA */ - - // capture write data - always @(posedge clk or negedge bit_cnt_rstn) - begin - if (!bit_cnt_rstn) - d_o <= 8'h0; - else if (spi_cs && spi_clk_high && !rwn && bit_cnt >= 5'd18) - d_o <= { d_o[7:0], spi_d_in }; - end - - // we - always @(posedge clk or negedge bit_cnt_rstn) - if (!bit_cnt_rstn) - we <= 1'b0; - else if ( spi_cs ) - if ( spi_clk_high && !rwn && bit_cnt == 5'd26 ) - we <= 1'b1; - else - we <= 1'b0; - - /* SPI data output enable */ - assign spi_d_oe = spi_cs; - - - /* - * clock out msb first. - * - */ - always @(posedge clk or negedge bit_cnt_rstn) - if ( !bit_cnt_rstn ) - spi_d_o <= start_code[8]; - else if ( spi_cs && spi_clk_high_m1 ) begin - if ( bit_cnt < 9 ) - spi_d_o <= start_code['d8 - bit_cnt[3:0]]; - else if ( !rwn && bit_cnt >= 'd18 && bit_cnt < 'd26 ) - spi_d_o <= word_cnt['d25-bit_cnt]; - else if ( rwn && bit_cnt >= 'd18 && bit_cnt < 'd26 ) - spi_d_o <= d_i['d25-bit_cnt]; - else - spi_d_o <= 1'b0; - end - else if (spi_cs && spi_clk_high_m1 && rwn && bit_cnt == 'd26) - spi_d_o <= d_i[8]; - - assign oe = mem_sel && rwn; - - /* Address Decoding */ - assign pkt_filter_sel = pkt_filter_sel_01 | pkt_filter_sel_02 | pkt_filter_sel_10 | pkt_filter_sel_12 | - pkt_filter_sel_20 | pkt_filter_sel_21 | pkt_filter_sel_23; - - assign mem_sel = dpram_rx_sel | dpram_tx_sel | dpram_ptrs_sel | pkt_filter_sel | param_sel[0] | - param_sel[1] | param_sel[2] | param_sel[2] | interrupts_sel; - - - // use to steer data into this module - assign mux_sel = dev_ad[6:2]; - - // address decode - always @(*) - begin - sci_sel_dual = 2'b00; - sci_sel_ch = 4'b000; - dpram_rx_sel = 1'b0; - dpram_tx_sel = 1'b0; - dpram_ptrs_sel = 1'b0; - pkt_filter_sel_01 = 1'b0; - pkt_filter_sel_02 = 1'b0; - pkt_filter_sel_10 = 1'b0; - pkt_filter_sel_12 = 1'b0; - pkt_filter_sel_20 = 1'b0; - pkt_filter_sel_21 = 1'b0; - pkt_filter_sel_23 = 1'b0; - param_sel = 4'b0000; - interrupts_sel = 1'b0; - casez( dev_ad[7:0] ) - 8'b00000000: sci_sel_ch = 4'b0001; // sgmii0 - 8'b00000001: sci_sel_ch = 4'b0010; // sgmii1 - 8'b00000010: sci_sel_dual = 2'b01; // DCU0 Dual - 8'b00000100: sci_sel_ch = 4'b0100; // sgmii2 - 8'b00000101: sci_sel_ch = 4'b1000; // sgmii3 - 8'b00000110: sci_sel_dual = 2'b10; // DCU1 Dual - 8'b000010??: dpram_rx_sel = 1'b1; - 8'b000011??: dpram_tx_sel = 1'b1; - 8'b00010000: dpram_ptrs_sel = 1'b1; - 8'b00010001: interrupts_sel = 1'b1; - 8'b001000??: param_sel[0] = 1'b1; - 8'b001001??: param_sel[1] = 1'b1; - 8'b001010??: param_sel[2] = 1'b1; - 8'b001011??: param_sel[3] = 1'b1; - 8'b01000001: pkt_filter_sel_01 = 1'b1; - 8'b01000010: pkt_filter_sel_02 = 1'b1; - 8'b01000011: pkt_filter_sel_03 = 1'b1; - 8'b01001000: pkt_filter_sel_10 = 1'b1; - 8'b01001010: pkt_filter_sel_12 = 1'b1; - 8'b01001011: pkt_filter_sel_13 = 1'b1; - 8'b01010000: pkt_filter_sel_20 = 1'b1; - 8'b01010001: pkt_filter_sel_21 = 1'b1; - 8'b01010011: pkt_filter_sel_23 = 1'b1; - 8'b01010111: pkt_filter_sel_2u = 1'b1; - 8'b01011000: pkt_filter_sel_30 = 1'b1; - 8'b01011001: pkt_filter_sel_31 = 1'b1; - 8'b01011010: pkt_filter_sel_32 = 1'b1; - 8'b01111010: pkt_filter_sel_u2 = 1'b1; - endcase - end - - endmodule -
\ No newline at end of file diff --git a/src/switch.v b/src/switch.v index fa6d75e..661d848 100644 --- a/src/switch.v +++ b/src/switch.v @@ -38,18 +38,21 @@ module switch #(parameter NUM_PHYS=3) input [8:0] rx_d_20, input [8:0] rx_d_21, input [8:0] rx_d_u0, + input [8:0] rx_mle_fifo_d, // 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, + output reg rx_fifo_re_mle, // 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 rx_fifo_empty_mle, input [NUM_PHYS-1:0] rx_wr_done, @@ -58,6 +61,7 @@ module switch #(parameter NUM_PHYS=3) input [10:0] rx1_byte_cnt, input [10:0] rx2_byte_cnt, input [10:0] rxu_byte_cnt, + input [10:0] rx_mle_byte_cnt, // TX FIFO output from internal muxes output reg [8:0] tx_d0, @@ -94,348 +98,358 @@ module switch #(parameter NUM_PHYS=3) 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 + `define INCLUDED + `include "ethernet_params.v" + `undef INCLUDED + // 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; -// 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 + // 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; -// 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 - + 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 + -/* - * TX2 Switch Logic - * Possible Sources: 0, 1 - */ -always @(posedge clk, negedge rstn) - if ( !rstn ) + // TX0 Switch Logic + // Possible sources: u, mle, 1, 2 + always @(posedge clk, negedge rstn) + if ( !rstn ) begin - tx_mode2 <= TX_MODE_AN; - tx2_src_sel <= SEL_PHY0; + tx_mode0 <= TX_MODE_AN; + tx0_src_sel <= TX_SRC_SEL_PHY1; + tx0_byte_cnt <= 'h0; 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 ) + 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_mle) // mle has data + begin + tx_mode0 <= TX_MODE_XMT_CUSTOM; + tx0_src_sel <= TX_SRC_SEL_MLE; + tx0_byte_cnt <= rx_mle_byte_cnt; + end + else if (!rx_fifo_empty_u0_m2 ) // controller has data begin - tx_mode2 <= TX_MODE_XMT_PKT; - tx2_src_sel <= SEL_PHY1; + tx_mode0 <= TX_MODE_XMT_CUSTOM; + tx0_src_sel <= TX_SRC_SEL_UC; + tx0_byte_cnt <= rxu_byte_cnt; end - else if (!rx_fifo_empty_02 ) + `ifdef PHY2_PRESENT + else if (tx0_src_sel==TX_SRC_SEL_PHY1 && !rx_fifo_empty_20 ) begin - tx_mode2 <= TX_MODE_XMT_PKT; - tx2_src_sel <= SEL_PHY0; + tx_mode0 <= TX_MODE_XMT_PKT; + tx0_src_sel <= TX_SRC_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 <= TX_SRC_SEL_PHY1; + tx0_byte_cnt <= i_rx1_byte_cnt; end - else if (!rx_fifo_empty_12 ) + `ifdef PHY2_PRESENT + else if (!rx_fifo_empty_20 ) begin - tx_mode2 <= TX_MODE_XMT_PKT; - tx2_src_sel <= SEL_PHY1; + tx_mode0 <= TX_MODE_XMT_PKT; + tx0_src_sel <= TX_SRC_SEL_PHY2; + tx0_byte_cnt <= i_rx2_byte_cnt; 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; + `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) + TX_SRC_SEL_PHY0: tx_d0 = 9'h000; + TX_SRC_SEL_PHY1: tx_d0 = rx_d_10; + TX_SRC_SEL_PHY2: tx_d0 = rx_d_20; + TX_SRC_SEL_MLE: tx_d0 = rx_mle_fifo_d; + TX_SRC_SEL_UC: tx_d0 = rx_d_u0; + default: tx_d0 = 9'h000; endcase + end -// 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 + // TX0 FIFO read enable + always @(*) begin + rx_fifo_re_10 = 1'b0; + rx_fifo_re_20 = 1'b0; + rx_fifo_re_mle = 1'b0; + rx_fifo_re_u0 = 1'b0; -// 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 + case(tx0_src_sel) + TX_SRC_SEL_PHY1: rx_fifo_re_10 = tx_fifo_re[0]; + TX_SRC_SEL_PHY2: rx_fifo_re_20 = tx_fifo_re[0]; + TX_SRC_SEL_MLE: rx_fifo_re_mle = tx_fifo_re[0]; + TX_SRC_SEL_UC: rx_fifo_re_u0 = tx_fifo_re[0]; + 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 - */ + // TX0 FIFO Empty Routing + always @(*) begin + case(tx0_src_sel) + TX_SRC_SEL_PHY1: tx_fifo_empty[0] = rx_fifo_empty_10; + TX_SRC_SEL_PHY2: tx_fifo_empty[0] = rx_fifo_empty_20; + TX_SRC_SEL_MLE: tx_fifo_empty[0] = rx_fifo_empty_mle; + TX_SRC_SEL_UC: tx_fifo_empty[0] = rx_fifo_empty_u0_m2; + default: tx_fifo_empty[0] = 1'b1; + endcase + end -assign tx_du = rx_d_0u; - -always @(*) - if ( !rx_fifo_empty_0u ) + // TX1 Switch Logic + // Possible sources: 0, 2 in priority + always @(posedge clk, negedge rstn) + if ( !rstn ) 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; + tx_mode1 <= TX_MODE_AN; + tx1_src_sel <= TX_SRC_SEL_PHY0; + tx1_byte_cnt <= 'h0; 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==TX_SRC_SEL_PHY0 && !rx_fifo_empty_21 ) + begin + tx_mode1 <= TX_MODE_XMT_PKT; + tx1_src_sel <= TX_SRC_SEL_PHY2; + tx1_byte_cnt <= i_rx1_byte_cnt; + end + `endif + else if (!rx_fifo_empty_01 ) + begin + tx_mode1 <= TX_MODE_XMT_PKT; + tx1_src_sel <= TX_SRC_SEL_PHY0; + tx1_byte_cnt <= i_rx0_byte_cnt; + end + `ifdef PHY2_PRESENT + else if (!rx_fifo_empty_21 ) + begin + tx_mode1 <= TX_MODE_XMT_PKT; + tx1_src_sel <= TX_SRC_SEL_PHY2; + tx1_byte_cnt <= i_rx1_byte_cnt; + 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) + TX_SRC_SEL_PHY0: tx_d1 = rx_d_01; + TX_SRC_SEL_PHY1: tx_d1 = 9'h000; + TX_SRC_SEL_PHY2: tx_d1 = rx_d_21; + TX_SRC_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) + TX_SRC_SEL_PHY0: rx_fifo_re_01 = tx_fifo_re[1]; + TX_SRC_SEL_PHY2: rx_fifo_re_21 = tx_fifo_re[1]; + endcase + end + + // TX1 FIFO Empty Routing + always @(*) begin + case(tx1_src_sel) + TX_SRC_SEL_PHY0: tx_fifo_empty[1] = rx_fifo_empty_01; + TX_SRC_SEL_PHY1: tx_fifo_empty[1] = 1'b1; + TX_SRC_SEL_PHY2: tx_fifo_empty[1] = rx_fifo_empty_21; + TX_SRC_SEL_UC: tx_fifo_empty[1] = 1'b1; + default: tx_fifo_empty[1] = 1'b1; + endcase + end + -always @(posedge clk, negedge rstn) - if ( !rstn ) - tx_fifo_we_u <= 1'b0; - else - tx_fifo_we_u <= i_tx_fifo_we_u; + // TX2 Switch Logic + // Possible sources: 0, 1 + always @(posedge clk, negedge rstn) + if ( !rstn ) + begin + tx_mode2 <= TX_MODE_AN; + tx2_src_sel <= TX_SRC_SEL_PHY0; + tx2_byte_cnt <= 'h0; + 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==TX_SRC_SEL_PHY0 && !rx_fifo_empty_12 ) + begin + tx_mode2 <= TX_MODE_XMT_PKT; + tx2_src_sel <= TX_SRC_SEL_PHY1; + tx2_byte_cnt <= i_rx1_byte_cnt; + end + else if (!rx_fifo_empty_02 ) + begin + tx_mode2 <= TX_MODE_XMT_PKT; + tx2_src_sel <= TX_SRC_SEL_PHY0; + tx2_byte_cnt <= i_rx0_byte_cnt; + end + else if (!rx_fifo_empty_12 ) + begin + tx_mode2 <= TX_MODE_XMT_PKT; + tx2_src_sel <= TX_SRC_SEL_PHY1; + tx2_byte_cnt <= i_rx1_byte_cnt; + 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) + TX_SRC_SEL_PHY0: tx_d2 = rx_d_02; + TX_SRC_SEL_PHY1: tx_d2 = rx_d_12; + TX_SRC_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) + TX_SRC_SEL_PHY0: rx_fifo_re_02 = tx_fifo_re[2]; + TX_SRC_SEL_PHY1: rx_fifo_re_12 = tx_fifo_re[2]; + endcase + end + + // TX2 FIFO Empty Routing + always @(*) begin + case(tx2_src_sel) + TX_SRC_SEL_PHY0: tx_fifo_empty[2] = rx_fifo_empty_02; + TX_SRC_SEL_PHY1: tx_fifo_empty[2] = rx_fifo_empty_12; + TX_SRC_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 diff --git a/src/sync_fifo.v b/src/sync_fifo.v index 56c83d4..52c15cd 100644 --- a/src/sync_fifo.v +++ b/src/sync_fifo.v @@ -1,6 +1,7 @@ /* - * sync_fifo.v + * sync_fifo.v * + * Copyright (C) 2025 Private Island Networks Inc. * Copyright (C) 2018, 2019 Mind Chasers Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,8 +20,6 @@ * */ -`timescale 1ns /10ps - module sync_fifo #(parameter FIFO_PTR = 11, FIFO_WIDTH = 9, FIFO_DEPTH = 2048 ) @@ -47,7 +46,9 @@ module sync_fifo #(parameter FIFO_PTR = 11, ); +`define INCLUDED `include "ethernet_params.v" +`undef INCLUDED reg [FIFO_PTR-1:0] wr_ptr; reg [FIFO_PTR-1:0] rd_ptr; @@ -60,7 +61,7 @@ always @(posedge clk, negedge rstn) else if ( reset_ptrs ) wr_ptr <= 'd0; else if ( we ) - wr_ptr <= wr_ptr + 1; + wr_ptr <= wr_ptr + 1'b1; /* * rd_ptr @@ -72,7 +73,7 @@ always @(posedge clk, negedge rstn) else if ( reset_ptrs ) rd_ptr <= 'd0; else if ( re && !empty ) - rd_ptr <= rd_ptr + 1; + rd_ptr <= rd_ptr + 1'b1; assign empty = ( rd_ptr == wr_ptr ) ? 1'b1 : 1'b0; assign almost_full = wr_bytes_available < MTU ? 1'b1 : 1'b0; @@ -88,7 +89,7 @@ always @(posedge clk, negedge rstn) assign active = ~empty; -dpram dpram_fifo( +dpram_inf dpram_fifo( .rstn( rstn ), .a_clk( clk ), .a_clk_e( 1'b1 ), diff --git a/src/udp_rx.v b/src/udp_rx.v new file mode 100644 index 0000000..8b548aa --- /dev/null +++ b/src/udp_rx.v @@ -0,0 +1,117 @@ +/* + * udp_rx.v + * + * Copyright (C) 2023-2025 Private Island Networks Inc. + * Copyright (C) 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: Receive State Machine and Logic for UDP + * + * + * 0-15 16:32 + * 0 Source port Destination port + * 4 Length Checksum + * + */ + +module udp_rx ( + input rstn, + input clk, + + // control + input phy_resetn, + input phy_up, + + // packet data + input pkt_start, // assert for each new frame to start state machines + input rx_sample, // enable in case we have connected at 100 Mbit + input rx_eop, // use as a synch reset + input[7:0] rx_data_m1, + input[7:0] rx_data_m2, + input[7:0] rx_data_m3, // first byte of packet appears here simultaneous with pkt_start + input[7:0] rx_data_m4, + + // flags + output pkt_complete, + output reg trigger_src_port, + output reg trigger_dst_port, + output reg keep +); + + /* Byte Address (when last byte of field is present on rx_data_m1 ) */ + localparam UDP_SRC_PORT=0, UDP_DST_PORT=2, UDP_LEN=4, UDP_CKSUM =6; + + localparam RX_ST_IDLE=4'h0, RX_ST_DATA=4'h1, RX_ST_DONE=4'h2, RX_ST_3=4'h3, + RX_ST_4=4'h4, RX_ST_5=4'h5, RX_ST_6=4'h6, RX_ST_7=4'h7, + RX_ST_8=4'h8, RX_ST_9=4'h9, RX_ST_A=4'ha, RX_ST_B=4'hb, + RX_ST_C=4'hc, RX_ST_D=4'hd, RX_ST_E=4'he, RX_ST_F=4'hf; + + reg [3:0] rx_state; + reg [3:0] rx_byte_cnt; + wire rx_error; + + /* + * rx_state machine + * capture a UDP 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 ( pkt_start ) // Found /S/ + rx_state <= RX_ST_DATA; + RX_ST_DATA: if (pkt_complete) + rx_state <= RX_ST_DONE; + RX_ST_DONE: rx_state <= RX_ST_IDLE; + endcase + else + rx_state <= RX_ST_IDLE; + + /* rx_byte_cnt */ + always @(posedge clk, negedge rstn) + if (!rstn) + rx_byte_cnt <= 0; + else if ( rx_sample && pkt_start ) // synch reset + rx_byte_cnt <= 0; + else if ( rx_sample && rx_byte_cnt <= 4'h7 ) + rx_byte_cnt <= rx_byte_cnt + 1'b1; + + /* + * Packet Filter Trigger(s) + * + */ + always @(posedge clk, negedge rstn) + if (!rstn) + trigger_src_port <= 1'b0; + else if ( rx_sample && rx_byte_cnt == UDP_SRC_PORT ) + trigger_src_port <= 1'b1; + else + trigger_src_port <= 1'b0; + + always @(posedge clk, negedge rstn) + if (!rstn) + trigger_dst_port <= 1'b0; + else if ( rx_sample && rx_byte_cnt == UDP_DST_PORT ) + trigger_dst_port <= 1'b1; + else + trigger_dst_port <= 1'b0; + + assign pkt_complete = ( rx_byte_cnt == 3'h7 ); + assign rx_error = 0; + +endmodule diff --git a/src/udp_rx_c.v b/src/udp_rx_c.v new file mode 100644 index 0000000..546990f --- /dev/null +++ b/src/udp_rx_c.v @@ -0,0 +1,89 @@ +/* + * udp_rx_c.v + * + * Copyright (C) 2023-2025 Private Island Networks Inc. + * Copyright (C) 2021 Mind Chasers Inc. + * + * function: Receive State Machine and Logic for UDP for the internall Controller + * + * + * 0-15 16:32 + * 0 Source port Destination port + * 4 Length Checksum + * + * UDP Parser for Controller Port + */ + +module udp_rx_c #(parameter UDP_PORT_MATCH_H=8'h90, parameter UDP_PORT_MATCH_L=8'h20) ( + input rstn, + input clk, + + // control + input phy_up, + + // packet data + input pkt_start, // assert for each new frame to start state machines + input rx_sample, // enable in case we have connected at 100 Mbit + input rx_eop, // use as a synch reset + input[7:0] rx_data_m1, + input[7:0] rx_data_m2, + input[7:0] rx_data_m3, // first byte of packet appears here simultaneous with pkt_start + input[7:0] rx_data_m4, + + // flags + output pkt_complete, + output reg udp_port_match +); + + /* Byte Address (when last byte of field is present on rx_data_m1 ) */ + localparam UDP_SRC_PORT=0, UDP_DST_PORT=2, UDP_LEN=4, UDP_CKSUM =6; + + localparam RX_ST_IDLE=4'h0, RX_ST_DATA=4'h1, RX_ST_DONE=4'h2, RX_ST_3=4'h3, + RX_ST_4=4'h4, RX_ST_5=4'h5, RX_ST_6=4'h6, RX_ST_7=4'h7, + RX_ST_8=4'h8, RX_ST_9=4'h9, RX_ST_A=4'ha, RX_ST_B=4'hb, + RX_ST_C=4'hc, RX_ST_D=4'hd, RX_ST_E=4'he, RX_ST_F=4'hf; + + reg [3:0] rx_state; + reg [3:0] rx_byte_cnt; + + /* + * rx_state machine + * capture a UDP Packet + * + */ + always @(posedge clk, negedge rstn) + if (!rstn) + rx_state <= RX_ST_IDLE; + else if ( rx_eop || !phy_up ) // EOP will reset state machine + rx_state <= RX_ST_IDLE; + else + case ( rx_state ) + RX_ST_IDLE: if ( pkt_start ) // Found /S/ + rx_state <= RX_ST_DATA; + RX_ST_DATA: if (pkt_complete) + rx_state <= RX_ST_DONE; + RX_ST_DONE: rx_state <= RX_ST_IDLE; + default: rx_state <= RX_ST_IDLE; + endcase + + /* rx_byte_cnt */ + always @(posedge clk, negedge rstn) + if (!rstn) + rx_byte_cnt <= 0; + else if ( rx_sample && pkt_start ) // synch reset + rx_byte_cnt <= 0; + else if ( rx_sample && rx_byte_cnt <= 4'h7 ) + rx_byte_cnt <= rx_byte_cnt + 1'b1; + + // UDP Dest Port match logic + always @(posedge clk, negedge rstn) + if (!rstn) + udp_port_match <= 1'b0; + else if ( rx_byte_cnt == 4'h3 && rx_data_m1 == (UDP_PORT_MATCH_L) && rx_data_m2 == UDP_PORT_MATCH_H) + udp_port_match <= 1'b1; + else + udp_port_match <= 1'b0; + + assign pkt_complete = ( rx_byte_cnt == 3'h7 ); + +endmodule |



