summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorPrivate Island Networks <opensource@privateisland.tech>2025-09-01 12:34:17 -0400
committerPrivate Island Networks <opensource@privateisland.tech>2025-09-01 12:34:17 -0400
commitc8b273c80abe98e53828f46079a187975938a56a (patch)
tree410577af8067ee001ba473dcf0c7bcae46d83de1 /src
parentac2bbbd2f816c223ef4dcfa2f8440d9c0c73bffe (diff)
rename source to src
Diffstat (limited to 'src')
-rw-r--r--src/an.v122
-rw-r--r--src/bin_to_ascii.v128
-rw-r--r--src/cam.v85
-rw-r--r--src/clk_gen.v67
-rw-r--r--src/controller.v472
-rw-r--r--src/directives.v34
-rw-r--r--src/dpram.v102
-rw-r--r--src/drop_fifo.v176
-rw-r--r--src/ethernet_params.v39
-rw-r--r--src/fcs.v142
-rw-r--r--src/half_fifo.v194
-rw-r--r--src/i2c.v391
-rw-r--r--src/interrupts.v77
-rw-r--r--src/ipv4.v152
-rw-r--r--src/link_timer.v67
-rw-r--r--src/mac.v973
-rw-r--r--src/mdio.v146
-rw-r--r--src/mdio_cont.v159
-rw-r--r--src/mdio_data_ti.v128
-rw-r--r--src/metrics.v102
-rw-r--r--src/pkt_filter.v95
-rw-r--r--src/sgmii_params.v55
-rw-r--r--src/spi.v315
-rw-r--r--src/switch.v531
-rw-r--r--src/sync_fifo.v110
25 files changed, 4862 insertions, 0 deletions
diff --git a/src/an.v b/src/an.v
new file mode 100644
index 0000000..18b5d95
--- /dev/null
+++ b/src/an.v
@@ -0,0 +1,122 @@
+/*
+ * 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
new file mode 100644
index 0000000..12464dc
--- /dev/null
+++ b/src/bin_to_ascii.v
@@ -0,0 +1,128 @@
+/*
+ * 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
+
diff --git a/src/cam.v b/src/cam.v
new file mode 100644
index 0000000..082194a
--- /dev/null
+++ b/src/cam.v
@@ -0,0 +1,85 @@
+/*
+ * cam.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: single cycle, parameterized CAM
+ *
+ */
+
+`timescale 1ns /10ps
+
+module cam #(parameter DEPTH = 4,
+ parameter DEPTHW = 2,
+ parameter WIDTH = 32)
+(
+ input rstn,
+ input clk,
+
+ // input for programming
+ input sel,
+ input we,
+ input [DEPTHW+1:0] addr, // add two bits for the byte selects
+ input [7:0] d_in,
+
+ input search,
+ input [(WIDTH-1):0]search_address,
+
+ // output
+ output reg match
+);
+
+ reg [(WIDTH-1):0] content[0:DEPTH-1];
+ reg [(DEPTH-1):0] valid;
+ integer i,j;
+
+ // Program the CAM
+ always @(posedge clk, negedge rstn)
+ if( !rstn ) begin
+ for (i=0; i < DEPTH; i=i+1) begin
+ content[i] <= 32'h0;
+ 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;
+ end
+
+ // 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
+
+
+endmodule
diff --git a/src/clk_gen.v b/src/clk_gen.v
new file mode 100644
index 0000000..9d3e054
--- /dev/null
+++ b/src/clk_gen.v
@@ -0,0 +1,67 @@
+/*
+ * 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/controller.v b/src/controller.v
new file mode 100644
index 0000000..2b95d1e
--- /dev/null
+++ b/src/controller.v
@@ -0,0 +1,472 @@
+/*
+ * controller.v
+ *
+ * Copyright (C) 2018, 2109, 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: FPGA internal state machine controller
+ *
+ */
+
+`timescale 1ns /10ps
+
+module controller #(parameter ADDR_SZ = 7)
+(
+ input rstn,
+ input clk,
+ input init,
+ input pulse_100ms,
+
+ // PCS status lines
+ input [3:0] pcs_rx_error,
+ input [1:0] pll_lol,
+
+ // link status
+ input [3:0] port_up,
+
+ // mdio_controller interface
+ output reg mdio_cont_start,
+ input mdio_cont_done,
+ output reg [ADDR_SZ-1:0] mdio_routine_addr,
+ input mdio_run, // the mdio controller is active
+
+ // mdio_data params
+ output reg [4:0] mdio_page,
+ output reg [4:0] mdio_reg_addr,
+ output reg [7:0] mdio_w_data_h,
+ output reg [7:0] mdio_w_data_l,
+
+ // bin_to_ascii interface for MDIO or CONT -> bin_to_ascii -> FIFO -> I2C
+ input bin_to_ascii_run, // asserted if block is active and writing FIFO
+
+ // sync_fifo interface: controller to fifo
+ output fifo_mux_sel, // 0 is self, 1 is mdio_controller
+ output reg fifo_we,
+ output reg [6:0] read_fifo_d_o,
+
+ // i2c interface: i2c to controller
+ input i2c_rx_we,
+ input i2c_rx_done,
+ input [7:0] i2c_d_in,
+
+ // DCU Resets
+ output [1:0] pcs_rst_dual,
+ output [1:0] serdes_rst_dual,
+ output [1:0] tx_serdes_rst,
+
+ // Channel
+ output [3:0] phy_resetn,
+ output [3:0] mac_reset,
+
+ output [3:0] tx_pcs_rst,
+ output [3:0] rx_serdes_rst,
+ output [3:0] rx_pcs_rst,
+
+ output reg [1:0] mdio_mux_sel,
+
+ // TX custom packet
+ output reg tx_custom
+
+);
+
+ // state encoding
+ localparam BUFFER_SZ = 8; // index starts at 1, 0 is cmd
+
+ localparam S0= 4'h0, S1=4'h1, S2=4'h2, S3=4'h3,
+ S4= 4'h4, S5=4'h5, S6=4'h6, S7=4'h7,
+ S8= 4'h8, S9=4'h9, S10=4'ha, S11=4'hb,
+ S12= 4'hc, S13=4'hd, S14=4'he, S15=4'hf;
+
+ // ascii codes we use
+ localparam ASCIIc = 7'h63, // channel resets
+ ASCIId = 7'h64, // set and dump page
+ ASCIIf = 7'h66, // FIFO control
+ ASCIIi = 7'h69, // init ( sgmii mode )
+ ASCIIl = 7'h6c, // link status
+ ASCIIm = 7'h6d, // mdio mux
+ ASCIIp = 7'h70, // PCS /SERDES block status
+ ASCIIq = 7'h71,
+ ASCIIr = 7'h72, // set address and read reg
+ ASCIIs = 7'h73, // show mdio line status
+ ASCIIt = 7'h74, // transmit test packet
+ ASCIIu = 7'h75,
+ ASCIIw = 7'h77, // write reg at preset page and address
+ ASCIIx = 7'h78, // control word
+ ASCIIy = 7'h79, // extended read
+ ASCIIz = 7'h7a, // extended write
+ ASCII_ = 7'h5f,
+ LF = 7'h0a,
+ CR = 7'h0d;
+
+ reg [1:0] x_reg[0:3]; // ASCIIx: phy_reset and mac_reset
+ reg [2:0] c_reg[0:3]; // ASCIIc one reg per channel
+ reg [2:0] u_reg[0:1]; // 2 DCU's: pcs_rst_dual, serdes_rst_dual, tx_serdes_rst
+
+ reg [3:0] cont_state;
+ reg [3:0] cnt; // input cnt, 0 is cmd, 1-8 are buffer, beyond are thrown out & flag is set
+ reg [6:0] cmd;
+ integer i;
+
+ reg [6:0] buffer [1:BUFFER_SZ]; // data buffer
+
+ reg mdio_cmd, cont_cmd;
+ wire rx_cmd;
+ reg mdio_cont_busy;
+
+ wire[5:0] pcs_s;
+ wire[3:0] link_s;
+
+ reg cont_start, cont_busy, cont_done;
+
+ assign pcs_s = { pll_lol, pcs_rx_error };
+ assign link_s = port_up;
+
+ /*
+ * main state machine for controller
+ */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ cont_state <= S0;
+ else
+ case (cont_state)
+ S0: cont_state <= S1; // S0 is default on reset
+ S1: if ( rx_cmd && cont_cmd )
+ cont_state <= S2;
+ else if ( rx_cmd )
+ cont_state <= S3;
+ S2: cont_state <= S3; // respond to cont cmd
+ S3: if ( !cont_busy )
+ cont_state <= S4;
+ S4: if ( !mdio_cont_busy )
+ cont_state <= S1;
+ default: cont_state <= cont_state;
+ endcase
+ end
+
+/*
+* Controller Tasks, controller always runs upon rx_cmd.
+* Other tasks will hold off until cont_done asserts
+*/
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ cont_start <= 1'b0;
+ else if ( rx_cmd )
+ cont_start <= 1'b1;
+ else
+ cont_start <= 1'b0;
+ end
+
+ // cont_busy
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ cont_busy <= 1'b0;
+ else if ( cont_start )
+ cont_busy <= 1'b1;
+ else if ( cont_done )
+ cont_busy <= 1'b0;
+ end
+
+ // cont_done
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ cont_done <= 1'b0;
+ else if ( cont_busy )
+ cont_done <= 1'b1;
+ else
+ cont_done <= 1'b0;
+ end
+
+ always @(cmd)
+ begin
+ if ( cmd == ASCIIp || cmd == ASCIIl )
+ cont_cmd <= 1'b1;
+ else
+ cont_cmd <= 1'b0;
+ end
+
+ /*
+ * MDIO controller related
+ */
+ always @(cmd)
+ begin
+ if ( cmd == ASCIIi || cmd == ASCIIs || cmd == ASCIId || cmd == ASCIIr || cmd == ASCIIw || cmd == ASCIIy || cmd == ASCIIz )
+ mdio_cmd <= 1'b1;
+ else
+ mdio_cmd <= 1'b0;
+ end
+
+
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ mdio_cont_start <= 1'b0;
+ else if ( cont_done && mdio_cmd )
+ mdio_cont_start <= 1'b1;
+ else
+ mdio_cont_start <= 1'b0;
+ end
+
+ /*
+ * driver: mdio_route_addr
+ */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ mdio_routine_addr <= 'd0;
+ else if ( cont_done && mdio_cmd )
+ if ( cmd == ASCIIi )
+ mdio_routine_addr <= 'd0;
+ else if ( cmd == ASCIIs )
+ mdio_routine_addr <= 'd20;
+ else if ( cmd == ASCIId )
+ mdio_routine_addr <= 'd25;
+ else if ( cmd == ASCIIr )
+ mdio_routine_addr <= 'd48;
+ else if ( cmd == ASCIIw )
+ mdio_routine_addr <= 'd50;
+ else if ( cmd == ASCIIy )
+ mdio_routine_addr <= 'd60;
+ else if ( cmd == ASCIIz )
+ mdio_routine_addr <= 'd80;
+ end
+
+ // mdio_cont_busy
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ mdio_cont_busy <= 1'b0;
+ else if ( mdio_cont_start )
+ mdio_cont_busy <= 1'b1;
+ else if ( mdio_cont_done )
+ mdio_cont_busy <= 1'b0;
+ end
+
+ /* fifo_mux_sel = 1 when controller does NOT have FIFO bus */
+ assign fifo_mux_sel = mdio_cont_busy | mdio_run;
+
+ /* set mdio page */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ mdio_page <= 'd0;
+ else if ( rx_cmd && cmd == ASCIId )
+ mdio_page <= { buffer[1][0], buffer[2][3:0] };
+ end
+
+ /* set mdio_reg_addr */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ mdio_reg_addr <= 'd0;
+ else if ( rx_cmd && cmd == ASCIIr )
+ mdio_reg_addr <= { buffer[1][0], buffer[2][3:0] };
+ end
+
+ /* 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 ( rx_cmd && (cmd == ASCIIw || cmd == ASCIIy || cmd == ASCIIz ) )
+ begin
+ mdio_w_data_h <= { buffer[1][3:0], buffer[2][3:0] };
+ mdio_w_data_l <= { buffer[3][3:0], buffer[4][3:0] };
+ end
+ end
+
+ // Channel Resets
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ begin
+ c_reg[0] <= 'hff;
+ c_reg[1] <= 'hff;
+ c_reg[2] <= 'hff;
+ c_reg[3] <= 'hff;
+ end
+ else if ( rx_cmd && cmd == ASCIIc )
+ begin
+ c_reg[0] <= buffer[4][2:0];
+ c_reg[1] <= buffer[3][2:0];
+ c_reg[2] <= buffer[2][2:0];
+ c_reg[3] <= buffer[1][2:0];
+ end
+ end
+
+ assign rx_pcs_rst[0] = c_reg[0][0];
+ assign rx_serdes_rst[0] = c_reg[0][1];
+ assign tx_pcs_rst[0]= c_reg[0][2];
+
+ assign rx_pcs_rst[1] = c_reg[1][0];
+ assign rx_serdes_rst[1] = c_reg[1][1];
+ assign tx_pcs_rst[1]= c_reg[1][2];
+
+ assign rx_pcs_rst[2] = c_reg[2][0];
+ assign rx_serdes_rst[2] = c_reg[2][1];
+ assign tx_pcs_rst[2]= c_reg[2][2];
+
+ assign rx_pcs_rst[3] = c_reg[3][0];
+ assign rx_serdes_rst[3] = c_reg[3][1];
+ assign tx_pcs_rst[3]= c_reg[3][2];
+
+ /* DCU resets */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ begin
+ u_reg[0] <= 'hff;
+ u_reg[1] <= 'hff;
+ end
+ else if ( rx_cmd && cmd == ASCIIu )
+ begin
+ u_reg[0] <= buffer[2][2:0];
+ u_reg[1] <= buffer[1][2:0];
+ end
+ end
+
+ // DCU0 Reset assignments
+ assign pcs_rst_dual[0] = u_reg[0][2];
+ assign serdes_rst_dual[0] = u_reg[0][1];
+ assign tx_serdes_rst[0] = u_reg[0][0];
+
+ // DCU1 Reset assignments
+ assign pcs_rst_dual[1] = u_reg[1][2];
+ assign serdes_rst_dual[1] = u_reg[1][1];
+ assign tx_serdes_rst[1] = u_reg[1][0];
+
+ /* X control */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ begin
+ x_reg[0] <= 'hff;
+ x_reg[1] <= 'hff;
+ x_reg[2] <= 'hff;
+ x_reg[3] <= 'hff;
+
+ end
+ else if ( rx_cmd && cmd == ASCIIx )
+ begin
+ x_reg[0] <= buffer[4][1:0];
+ x_reg[1] <= buffer[3][1:0];
+ x_reg[2] <= buffer[2][1:0];
+ x_reg[3] <= buffer[1][1:0];
+
+ end
+ end
+
+ assign mac_reset[0] = x_reg[0][0];
+ assign phy_resetn[0] = ~x_reg[0][1];
+
+ assign mac_reset[1] = x_reg[1][0];
+ assign phy_resetn[1] = ~x_reg[1][1];
+
+ assign mac_reset[2] = x_reg[2][0];
+ assign phy_resetn[2] = ~x_reg[2][1];
+
+ assign mac_reset[3] = x_reg[3][0];
+ assign phy_resetn[3] = ~x_reg[3][1];
+
+
+ /* mdio_mux_sel */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ mdio_mux_sel <= 'h0;
+ else if ( rx_cmd && cmd == ASCIIm )
+ mdio_mux_sel <= buffer[1][1:0];
+ end
+
+ /* transmit custom packet */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ tx_custom <= 1'b0;
+ else if ( rx_cmd && cmd == ASCIIt )
+ tx_custom <= 1'b1;
+ else
+ tx_custom <= 1'b0;
+ end
+
+ /* FIFO logic */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ fifo_we <= 1'b0;
+ else if ( cont_state == S2 )
+ fifo_we <= 1'b1;
+ else
+ fifo_we <= 1'b0;
+ end
+
+
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ read_fifo_d_o <= 0;
+ else if ( cont_state == S2 && cmd == ASCIIp ) // pcs status
+ read_fifo_d_o <= { 1'b0, pcs_s };
+ else if ( cont_state == S2 && cmd == ASCIIl ) // link status
+ read_fifo_d_o <= { 3'b000, link_s };
+ end
+
+ /*
+ * capture the cmd and buffer
+ * rx_cmd is a one shot at the end that triggers the state machine
+ */
+ assign rx_cmd = i2c_rx_done;
+
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ cmd <= 7'h0;
+ else if ( i2c_rx_we && cnt == 4'h0 )
+ cmd <= i2c_d_in;
+ end
+
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ begin
+ for (i=1; i<=BUFFER_SZ; i=i+1)
+ begin
+ buffer[i] <= 'h0;
+ end
+ end
+ else if ( i2c_rx_we && cnt > 0 && cnt <= BUFFER_SZ )
+ buffer[cnt] <= i2c_d_in;
+ end
+
+ /*
+ * counter for I2c rx buffer.
+ */
+ always @(posedge clk or negedge rstn)
+ begin
+ if ( !rstn )
+ cnt <= 'h0;
+ else if (i2c_rx_done)
+ cnt <= 'h0;
+ else if (i2c_rx_we )
+ cnt <= cnt + 1;
+ end
+
+
+endmodule
diff --git a/src/directives.v b/src/directives.v
new file mode 100644
index 0000000..5760c23
--- /dev/null
+++ b/src/directives.v
@@ -0,0 +1,34 @@
+/*
+ * 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
new file mode 100644
index 0000000..53c1efc
--- /dev/null
+++ b/src/dpram.v
@@ -0,0 +1,102 @@
+/*
+ * 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/drop_fifo.v b/src/drop_fifo.v
new file mode 100644
index 0000000..736947c
--- /dev/null
+++ b/src/drop_fifo.v
@@ -0,0 +1,176 @@
+/*
+ * drop_fifo.v
+ *
+ * Copyright 2018, 2019, 2020, 2021 Mind Chasers Inc.
+ * Copyright 2023 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: FIFO that supports dropping data if not kept
+ *
+ * For more information, see https://privateisland.tech/dev/pi-doc
+ *
+ */
+
+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
+);
+
+// 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;
+
+/*
+ * 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;
+
+
+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;
+
+/*
+ * 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);
+
+/*
+ * d_out register
+ *
+ */
+always @(posedge clk, negedge rstn)
+ if( !rstn )
+ d_out <= 'd0;
+ else
+ d_out <= d_out_internal;
+
+
+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 )
+);
+
+
+endmodule
diff --git a/src/ethernet_params.v b/src/ethernet_params.v
new file mode 100644
index 0000000..dcb7dba
--- /dev/null
+++ b/src/ethernet_params.v
@@ -0,0 +1,39 @@
+/*
+ * ethernet_params.v
+ *
+ * Copyright (C) 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 related parameters
+ *
+ */
+
+
+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_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;
+
+// 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;
diff --git a/src/fcs.v b/src/fcs.v
new file mode 100644
index 0000000..6abf536
--- /dev/null
+++ b/src/fcs.v
@@ -0,0 +1,142 @@
+/*
+ * fcs.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: FCS checksum
+ *
+ *
+ */
+`timescale 1ns /10ps
+
+////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 1999-2008 Easics NV.
+// This source file may be used and distributed without restriction
+// provided that this copyright statement is not removed from the file
+// and that any derivative work contains the original copyright notice
+// and the associated disclaimer.
+//
+// THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS
+// OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+// WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Purpose : synthesizable CRC function
+// * 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
+//
+// Info : tools@easics.be
+// http://www.easics.com
+////////////////////////////////////////////////////////////////////////////////
+
+/*
+* a) The first 32 bits of the frame are complemented.
+* b) The n bits of the MAC frame considered to be the coefficients of a polynomial M(x) of degree n � 1.
+* The first bit of the Destination Address field corresponds to the x(n�1) term
+* and the last bit of the MAC Client Data field (or Pad field if present) corresponds to the x0 term.
+* c) M(x) is multiplied by x**32 and divided by G(x), producing a remainder R(x) of degree = 31.
+* d) The coefficients of R(x) are considered to be a 32-bit sequence.
+* e) The bit sequence is complemented and the result is the CRC.
+* f) FCS should be trnansmitted msbit first ( opposite of the rest of MAC frame )
+*/
+module fcs(
+ input rstn,
+ input clk,
+
+ // control interface
+ input init,
+ input enable,
+
+ // addr & data
+ input [1:0] addr,
+ input [0:7] din,
+ output [7:0] dout
+);
+
+
+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
new file mode 100644
index 0000000..e18f543
--- /dev/null
+++ b/src/half_fifo.v
@@ -0,0 +1,194 @@
+/*
+ * half_fifo.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: micro / FIFO interface
+ *
+ */
+
+`timescale 1ns /10ps
+
+module half_fifo #(parameter DATA_WIDTH = 9, DPRAM_DEPTH=11 )
+(
+ input rstn,
+ input uc_clk,
+ input fifo_clk,
+
+ // UC interrupt support
+ output reg fifo_we_int,
+
+ // tx_mode
+ input tx_mode,
+
+ // UC side: common DPRAM signals for RX and TX
+ input [DPRAM_DEPTH-1:0] dpram_addr,
+ input [DATA_WIDTH-1:0] dpram_din,
+ output reg [DATA_WIDTH-1:0] dpram_dout,
+ input dpram_we,
+ input dpram_oe,
+ // UC select signals
+ input dpram_ptrs_sel,
+ input dpram_rx_sel,
+ input dpram_tx_sel,
+
+ // FIFO TX (input)
+ input fifo_we,
+ input [DATA_WIDTH-1:0] fifo_d_in,
+
+ // FIFO RX (output)
+ input fifo_re,
+ output [DATA_WIDTH-1:0] fifo_d_out,
+
+ // FIFO flags
+ output rx_empty, // dpram 0 (reader)
+ output tx_full
+
+);
+
+/*
+ * Pointers for accessing memory.
+ * UC writes RX and reads TX
+ * Switch writes (via FIFO I/F) TX and reads RX
+ */
+reg [DPRAM_DEPTH-1:0] rx_wr_ptr;
+reg [DPRAM_DEPTH-1:0] rx_rd_ptr;
+
+reg [DPRAM_DEPTH-1:0] tx_wr_ptr;
+reg [DPRAM_DEPTH-1:0] tx_rd_ptr;
+
+reg fifo_we_m1;
+
+reg reset_ptrs;
+wire [DATA_WIDTH-1:0] dpram_rx_dout, dpram_tx_dout;
+
+/* read data mux */
+always @(*)
+ casez({ dpram_rx_sel, dpram_tx_sel, dpram_ptrs_sel, dpram_addr[2:0] } )
+ 6'b001000: dpram_dout = rx_wr_ptr;
+ 6'b001001: dpram_dout = rx_rd_ptr;
+ 6'b001010: dpram_dout = tx_wr_ptr;
+ 6'b001011: dpram_dout = tx_rd_ptr;
+ 6'b001100: dpram_dout = reset_ptrs;
+ 6'b010???: dpram_dout = dpram_tx_dout;
+ 6'b100???: dpram_dout = dpram_rx_dout;
+ default: dpram_dout = dpram_tx_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] == 3'h4 )
+ reset_ptrs <= dpram_din[0];
+
+/* RX wr ptr (UC writes) */
+always @(posedge uc_clk, negedge rstn)
+ if ( !rstn )
+ rx_wr_ptr <= 'h0;
+ else if ( reset_ptrs )
+ rx_wr_ptr <= 'h0;
+ else if ( dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == 3'h0 )
+ rx_wr_ptr <= dpram_din;
+
+/* TX rd ptr (uP reads) */
+always @(posedge uc_clk, negedge rstn)
+ if ( !rstn )
+ tx_rd_ptr <= 'h0;
+ else if ( reset_ptrs )
+ tx_rd_ptr <= 'h0;
+ else if ( dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == 3'h3 )
+ tx_rd_ptr <= dpram_din;
+
+/* RX rd ptr (switch reads via FIFO), auto increment */
+always @(posedge fifo_clk, negedge rstn)
+ if( !rstn )
+ rx_rd_ptr <= 'd0;
+ else if ( reset_ptrs )
+ rx_rd_ptr <= 'd0;
+ else if ( fifo_re && !rx_empty ) // advance the pointer if FIFO isn't empty
+ rx_rd_ptr <= rx_rd_ptr + 1;
+
+ /* TX wr ptr (switch writes via FIFO) */
+always @(posedge fifo_clk, negedge rstn)
+ if( !rstn )
+ tx_wr_ptr <= 'd0;
+ else if ( reset_ptrs )
+ tx_wr_ptr <= 'd0;
+ else if ( fifo_we )
+ tx_wr_ptr <= tx_wr_ptr + 1;
+
+
+assign rx_empty = (rx_rd_ptr == rx_wr_ptr) ? 1'b1 : 1'b0;
+assign tx_full = (tx_rd_ptr != tx_wr_ptr) ? 1'b1 : 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;
+
+
+always @(posedge fifo_clk, negedge rstn)
+ if ( !rstn )
+ fifo_we_int <= 1'b0;
+ else if ( !fifo_we & fifo_we_m1 )
+ fifo_we_int <= 1'b1;
+ else
+ fifo_we_int <= 1'b0;
+
+
+/* micro uses A side, FIFO uses B side */
+dpram dpram_rx(
+ .rstn( rstn ),
+ .a_clk( uc_clk ),
+ .a_clk_e( dpram_rx_sel ),
+ .a_we( dpram_we ),
+ .a_oe( 1'b1 ),
+ .a_addr( { 2'b00, dpram_addr } ),
+ .a_din( dpram_din ),
+ .a_dout( dpram_rx_dout ),
+ // port B
+ .b_clk( fifo_clk ),
+ .b_clk_e( 1'b1 ),
+ .b_we( 1'b0 ),
+ .b_oe( fifo_re ),
+ .b_addr( { 2'b00, rx_rd_ptr } ),
+ .b_din( 9'h0 ),
+ .b_dout( fifo_d_out )
+);
+
+dpram dpram_tx(
+ .rstn( rstn ),
+ .a_clk( uc_clk ),
+ .a_clk_e( dpram_tx_sel ),
+ .a_we( dpram_we ),
+ .a_oe( 1'b1 ),
+ .a_addr( { 2'b00, dpram_addr } ),
+ .a_din( dpram_din ),
+ .a_dout( dpram_tx_dout ),
+ // port B
+ .b_clk( fifo_clk ),
+ .b_clk_e( 1'b1 ),
+ .b_we( fifo_we ),
+ .b_oe( 1'b0 ),
+ .b_addr( { 2'b00, tx_wr_ptr } ),
+ .b_din( fifo_d_in ),
+ .b_dout( )
+);
+
+endmodule
diff --git a/src/i2c.v b/src/i2c.v
new file mode 100644
index 0000000..aca6452
--- /dev/null
+++ b/src/i2c.v
@@ -0,0 +1,391 @@
+/*
+ * 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
new file mode 100644
index 0000000..c77dfb7
--- /dev/null
+++ b/src/interrupts.v
@@ -0,0 +1,77 @@
+/*
+ * 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.v
new file mode 100644
index 0000000..55ab7a7
--- /dev/null
+++ b/src/ipv4.v
@@ -0,0 +1,152 @@
+/*
+ * ipv4.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: Receive State Machine and Logic for IPv4
+ *
+ *
+ * 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....................................
+ *
+ *
+ * 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,
+
+ // 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
+ );
+
+ /* 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;
+ wire rx_error;
+
+ /*
+ * rx_state machine
+ * capture an IPv4 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_state == RX_ST_IDLE && !pkt_start ) // synch reset
+ rx_byte_cnt <= 1;
+ else if ( rx_state != RX_ST_IDLE || 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 };
+
+ /*
+ * Packet Filter Trigger(s), assert the sample before the data appears
+ *
+ */
+ always @(posedge clk, negedge rstn)
+ if (!rstn)
+ trigger_src_addr <= 1'b0;
+ else if ( rx_byte_cnt == IPV4_SRC_ADDR-1 )
+ trigger_src_addr <= 1'b1;
+ else
+ trigger_src_addr <= 1'b0;
+
+ always @(posedge clk, negedge 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 rx_error = 0;
+
+
+endmodule
diff --git a/src/link_timer.v b/src/link_timer.v
new file mode 100644
index 0000000..7b501ff
--- /dev/null
+++ b/src/link_timer.v
@@ -0,0 +1,67 @@
+/*
+ * 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
new file mode 100644
index 0000000..b5b980a
--- /dev/null
+++ b/src/mac.v
@@ -0,0 +1,973 @@
+/*
+ * 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/mdio.v b/src/mdio.v
new file mode 100644
index 0000000..3339dcc
--- /dev/null
+++ b/src/mdio.v
@@ -0,0 +1,146 @@
+/*
+ * mdio.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: MDIO HW driver / bit banger
+ *
+ */
+
+
+`timescale 1ns /10ps
+
+module mdio(
+ input rstn,
+ input mdc, // clock
+
+ // mdio
+ input mdi,
+ output reg mdo,
+ output reg mdo_oe,
+
+ // mdio_controller interface
+ input rwn, // read / write not
+ input [4:0] phy_addr,
+ input [4:0] reg_addr,
+ input [15:0] di,
+ input ld, // load, start
+ output reg run,
+ output done, // mdio xfer is done
+
+ // output port to converter
+ output reg [15:0] dout,
+ output we
+);
+
+ reg [5:0] state;
+ reg [15:0] d;
+
+ // run state machine
+ // run starts when ld is asserted and finishes when done is asserted
+ always @(negedge mdc or negedge rstn)
+ begin
+ if ( !rstn)
+ run <= 1'b0;
+ else if ( ld )
+ run <= 1'b1;
+ else if ( done )
+ run <= 1'b0;
+ end
+
+
+ // increment state during run
+ always @(negedge mdc or negedge rstn)
+ begin
+ if ( !rstn )
+ state <= 0;
+ else if ( ld )
+ state <= 0;
+ else if ( run )
+ state <= state + 1;
+ end
+
+ // register data for MDIO TX
+ always @(negedge mdc or negedge rstn)
+ begin
+ if ( !rstn )
+ d <= 0;
+ else if ( ld )
+ d <= di;
+ end
+
+ // done combo logic
+ assign done = ( state == 6'd36 ) ? 1'b1 : 1'b0;
+
+ // only assert we on mdio reads
+ assign we = done && rwn;
+
+ // mdo_oe logic
+ always @(*)
+ begin
+ mdo_oe = 1'b0;
+ if ( run && !rwn && state < 6'd36 )
+ mdo_oe = 1'b1;
+ else if ( run && rwn && state < 6'd14 )
+ mdo_oe = 1'b1;
+ end
+
+ // mdo mux
+ always @(*)
+ begin
+ mdo = 1'b0;
+ casez(state)
+ 6'h0: mdo = 1'b0;
+ 6'h1: mdo = 1'b1;
+ 6'h2: if (rwn)
+ mdo = 1'b1;
+ else
+ mdo = 1'b0;
+ 6'h3: if (rwn)
+ mdo = 1'b0;
+ else
+ mdo = 1'b1;
+ 6'h4: mdo = phy_addr[4];
+ 6'h5: mdo = phy_addr[3];
+ 6'h6: mdo = phy_addr[2];
+ 6'h7: mdo = phy_addr[1];
+ 6'h8: mdo = phy_addr[0];
+ 6'h9: mdo = reg_addr[4];
+ 6'ha: mdo = reg_addr[3];
+ 6'hb: mdo = reg_addr[2];
+ 6'hc: mdo = reg_addr[1];
+ 6'hd: mdo = reg_addr[0];
+ 6'he: mdo = 1'b1; // it's a don't care if we're doing a read
+ 6'hf: mdo = 1'b0;
+ 6'h1?: mdo = rwn ? 1'b1 : d[31-state]; // msbit
+ default: mdo = 1'b1;
+ endcase
+ end
+
+
+ /*
+ * capture mdi on rising edge
+ * data out shift register
+ * ( shift into it from mdio to parallel reg )
+ */
+ always @(posedge mdc or negedge rstn)
+ begin
+ if ( !rstn )
+ dout <= 16'h0;
+ else if ( rwn && run && ( state >= 16 && state <= 31 ))
+ dout <= { dout[14:0], mdi };
+ end
+
+endmodule
diff --git a/src/mdio_cont.v b/src/mdio_cont.v
new file mode 100644
index 0000000..ad595fd
--- /dev/null
+++ b/src/mdio_cont.v
@@ -0,0 +1,159 @@
+/*
+ * mdio_cont.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: MDIO controller / state machine
+ *
+ */
+
+`timescale 1ns /10ps
+
+module mdio_controller #(parameter ADDR_SZ = 6)
+(
+
+ // system interface
+ input rstn,
+ input clk, // mdio mdc clock
+
+ // worker interface
+ input work_start,
+ output reg work_run,
+ output reg work_done,
+ input [ADDR_SZ-1:0] routine_addr,
+
+ // arbitration
+ input buffer_full,
+
+ // memory interface
+ output reg [ADDR_SZ-1:0] addr, // controller address
+ input [7:0] di, // controller data bus
+
+ // mdio interface
+ output reg [4:0] reg_addr,
+ output reg [15:0] dout, // data output ( for writes )
+ output ld, // load control / address,
+ output reg rwn, // read / write not
+ input done
+);
+
+ localparam ST_SZ = 4;
+ reg [ST_SZ-1:0] cont_state;
+ wire ld_dl, ld_dh;
+
+
+ // state encoding
+ localparam S0= 4'h0, S1=4'h1, S2=4'h2, S3=4'h3,
+ S4= 4'h4, S5=4'h5, S6=4'h6, S7=4'h7,
+ S8 = 4'h8;
+
+
+ /*
+ * keep running until an eop is found
+ */
+ always @(posedge clk or negedge rstn)
+ begin
+ if (!rstn)
+ cont_state <= S0;
+ else
+ case (cont_state)
+ S0: if (work_start == 1'b1) cont_state <= S1;
+ S1: cont_state <= S2;
+ S2: if ( work_done == 1'b1 ) cont_state <= S0;
+ else if ( buffer_full ) cont_state <= S2; // wait for the fifo to empty out
+ else if ( ~rwn ) cont_state <= S3;
+ else cont_state <= S6;
+ S3: cont_state <= S4;
+ S4: cont_state <= S5;
+ S5: cont_state <= S6;
+ S6: cont_state <= S7;
+ S7: if ( done == 1'b1 ) cont_state <= S8;
+ S8: cont_state <= S1;
+ endcase
+ end
+
+ /* 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
+
+ /* 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
+
+ /* 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
+
+ /* 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
+
+ /* 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
+
+ // 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
+
+ // combinatorial logic here
+ assign ld_dl = ( cont_state == S4 ) ? 1'b1 : 1'b0;
+ assign ld_dh = ( cont_state == S5 ) ? 1'b1 : 1'b0;
+ assign ld = ( cont_state == S6 ) ? 1'b1 : 1'b0;
+
+
+endmodule
diff --git a/src/mdio_data_ti.v b/src/mdio_data_ti.v
new file mode 100644
index 0000000..aef537d
--- /dev/null
+++ b/src/mdio_data_ti.v
@@ -0,0 +1,128 @@
+/*
+ * mdio_data_ti.v ( TI DP83867 PHY )
+ *
+ * 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: MDIO ROM for TI DP83867 PHY
+ *
+ */
+
+
+
+`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,
+
+ // ROM interface
+ input oe,
+ 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
+
+endmodule
+
diff --git a/src/metrics.v b/src/metrics.v
new file mode 100644
index 0000000..bce3389
--- /dev/null
+++ b/src/metrics.v
@@ -0,0 +1,102 @@
+/*
+ * 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/pkt_filter.v b/src/pkt_filter.v
new file mode 100644
index 0000000..9140c74
--- /dev/null
+++ b/src/pkt_filter.v
@@ -0,0 +1,95 @@
+/*
+ * pkt_filter.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: packet filter / CAM wrapper with keep / drop logic
+ *
+ */
+
+`timescale 1ns /10ps
+
+module pkt_filter #(parameter DEPTH = 4,
+ parameter DEPTHW = 2,
+ parameter WIDTH = 32)
+(
+ input rstn,
+ input clk,
+
+ // input for programming
+ input sel,
+ input we,
+ input [DEPTHW+1:0] addr,
+ input [7:0] d_in,
+
+ // 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,
+
+ // filter
+ input new_frame, // assert for each new frame to reset state machines
+ input block,
+ input invert,
+ input trigger,
+ 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 )
+);
+
+
+
+endmodule
+
diff --git a/src/sgmii_params.v b/src/sgmii_params.v
new file mode 100644
index 0000000..a36160e
--- /dev/null
+++ b/src/sgmii_params.v
@@ -0,0 +1,55 @@
+/*
+ * 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
new file mode 100644
index 0000000..d7c054b
--- /dev/null
+++ b/src/spi.v
@@ -0,0 +1,315 @@
+/*
+ * 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
new file mode 100644
index 0000000..98aec4a
--- /dev/null
+++ b/src/switch.v
@@ -0,0 +1,531 @@
+/*
+ * switch.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: Soft Ethernet Switch
+ *
+ */
+
+`timescale 1ns /10ps
+
+module switch(
+ input rstn,
+ input clk,
+
+ // PHY status
+ input [3:0] phy_up,
+ input [3:0] mode_100Mbit,
+
+ // FIFO input data from RX FIFOs
+ input [8:0] rx_d_01,
+ input [8:0] rx_d_02,
+ input [8:0] rx_d_03,
+ input [8:0] rx_d_10,
+ input [8:0] rx_d_12,
+ input [8:0] rx_d_13,
+ input [8:0] rx_d_20,
+ input [8:0] rx_d_21,
+ input [8:0] rx_d_23,
+ input [8:0] rx_d_2u,
+ input [8:0] rx_d_30,
+ input [8:0] rx_d_31,
+ input [8:0] rx_d_32,
+ input [8:0] rx_d_u2,
+
+ // RX FIFO read enables
+ output reg rx_fifo_re_01, rx_fifo_re_02, rx_fifo_re_03,
+ output reg rx_fifo_re_10, rx_fifo_re_12, rx_fifo_re_13,
+ output reg rx_fifo_re_20, rx_fifo_re_21, rx_fifo_re_23, rx_fifo_re_2u,
+ output reg rx_fifo_re_30, rx_fifo_re_31, rx_fifo_re_32,
+ output reg rx_fifo_re_u2,
+
+ // RX FIFO Empty flags
+ input rx_fifo_empty_01, rx_fifo_empty_02, rx_fifo_empty_03,
+ input rx_fifo_empty_10, rx_fifo_empty_12, rx_fifo_empty_13,
+ input rx_fifo_empty_20, rx_fifo_empty_21, rx_fifo_empty_23, rx_fifo_empty_2u,
+ input rx_fifo_empty_30, rx_fifo_empty_31, rx_fifo_empty_32,
+ input rx_fifo_empty_u2,
+
+ // RX Byte Count
+ input [3:0] rx_wr_done,
+ input [10:0] rx0_byte_cnt,
+ input [10:0] rx1_byte_cnt,
+ input [10:0] rx2_byte_cnt,
+ input [10:0] rx3_byte_cnt,
+
+ // TX FIFO output from internal muxes
+ output reg [8:0] tx_d0,
+ output reg [8:0] tx_d1,
+ output reg [8:0] tx_d2,
+ output reg [8:0] tx_d3,
+ output [8:0] tx_du,
+
+ // TX FIFO read enable inputs (need to route to RX output FIFOs)
+ input [3:0] tx_fifo_re,
+ output reg tx_fifo_we_u,
+
+ // TX FIFO Empty Flags (need to route to RX output FIFOs)
+ output reg [3:0] tx_fifo_empty,
+
+ // TX modes for the PHYs and uc
+ output reg [2:0] tx_mode0,
+ output reg [2:0] tx_mode1,
+ output reg [2:0] tx_mode2,
+ output reg [2:0] tx_mode3,
+ output reg tx_modeu,
+
+ // TX byte cnt
+ output reg [10:0] tx0_byte_cnt,
+ output reg [10:0] tx1_byte_cnt,
+ output reg [10:0] tx2_byte_cnt,
+ output reg [10:0] tx3_byte_cnt,
+
+ // TX Source Select
+ output reg [2:0] tx_src_sel0,
+ output reg [2:0] tx_src_sel1,
+ output reg [2:0] tx_src_sel2,
+ output reg [2:0] tx_src_sel3,
+
+ // TX state machine done flag
+ input [3:0] tx_f,
+
+ // TX custom packet
+ input tx_custom
+);
+
+// IPG for Port 0
+wire ipg_met;
+reg [6:0] ipg_cnt;
+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, i_rx3_byte_cnt;
+
+`include "ethernet_params.v"
+`include "sgmii_params.v"
+
+localparam SEL_PHY0 = 3'b000,
+ SEL_PHY1 = 3'b001,
+ SEL_PHY2 = 3'b010,
+ SEL_PHY3 = 3'b011,
+ 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;
+
+always @(posedge clk, negedge rstn)
+ if ( !rstn )
+ i_rx3_byte_cnt <= 'h0;
+ else if ( rx_wr_done[3])
+ i_rx3_byte_cnt <= rx3_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;
+
+/* 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;
+
+
+// TX0 Switch Logic
+// Possible sources: 1, 2
+always @(posedge clk, negedge rstn)
+ if ( !rstn )
+ begin
+ tx_mode0 <= TX_MODE_AN;
+ tx_src_sel0 <= 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 (tx_src_sel0==SEL_PHY1 && !rx_fifo_empty_20 )
+ begin
+ tx_mode0 <= TX_MODE_XMT_CUSTOM;
+ tx_src_sel0 <= SEL_PHY2;
+ tx0_byte_cnt <= i_rx2_byte_cnt;
+ end
+ else if (!rx_fifo_empty_10 )
+ begin
+ tx_mode0 <= TX_MODE_XMT_PKT;
+ tx_src_sel0 <= SEL_PHY1;
+ tx0_byte_cnt <= i_rx1_byte_cnt;
+ end
+ else if (!rx_fifo_empty_20 )
+ begin
+ tx_mode0 <= TX_MODE_XMT_CUSTOM;
+ tx_src_sel0 <= SEL_PHY2;
+ tx0_byte_cnt <= i_rx2_byte_cnt;
+ end
+ 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(tx_src_sel0)
+ SEL_PHY0: tx_d0 = 9'h000;
+ SEL_PHY1: tx_d0 = rx_d_10;
+ SEL_PHY2: tx_d0 = rx_d_20;
+ SEL_UC: tx_d0 = 9'h000;
+ 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_30 = 1'b0;
+ case(tx_src_sel0)
+ SEL_PHY1: rx_fifo_re_10 = tx_fifo_re[0];
+ SEL_PHY2: rx_fifo_re_20 = tx_fifo_re[0];
+ SEL_PHY3: rx_fifo_re_30 = tx_fifo_re[0];
+ endcase
+end
+
+// TX0 FIFO Empty Routing
+always @(*) begin
+ case(tx_src_sel0)
+ SEL_PHY1: tx_fifo_empty[0] = rx_fifo_empty_10;
+ SEL_PHY2: tx_fifo_empty[0] = rx_fifo_empty_20;
+ SEL_PHY3: tx_fifo_empty[0] = rx_fifo_empty_30;
+ default: tx_fifo_empty[0] = 1'b1;
+ endcase
+end
+
+// TX1 Switch Logic
+// Possible sources: 0, 2, 3 in priority
+always @(posedge clk, negedge rstn)
+ if ( !rstn )
+ begin
+ tx_mode1 <= TX_MODE_AN;
+ tx_src_sel1 <= 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;
+ else if (tx_src_sel1==SEL_PHY0 && !rx_fifo_empty_21 )
+ begin
+ tx_mode1 <= TX_MODE_XMT_PKT;
+ tx_src_sel1 <= SEL_PHY2;
+ end
+ else if (tx_src_sel1==SEL_PHY0 && !rx_fifo_empty_31 )
+ begin
+ tx_mode1 <= TX_MODE_XMT_PKT;
+ tx_src_sel1 <= SEL_PHY3;
+ end
+ else if (!rx_fifo_empty_01 )
+ begin
+ tx_mode1 <= TX_MODE_XMT_PKT;
+ tx_src_sel1 <= SEL_PHY0;
+ end
+ else if (!rx_fifo_empty_21 )
+ begin
+ tx_mode1 <= TX_MODE_XMT_PKT;
+ tx_src_sel1 <= SEL_PHY2;
+ end
+ else if (!rx_fifo_empty_31 )
+ begin
+ tx_mode1 <= TX_MODE_XMT_PKT;
+ tx_src_sel1 <= SEL_PHY3;
+ end
+ 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(tx_src_sel1)
+ SEL_PHY0: tx_d1 = rx_d_01;
+ SEL_PHY1: tx_d1 = 9'h000;
+ SEL_PHY2: tx_d1 = rx_d_21;
+ SEL_PHY3: tx_d1 = rx_d_31;
+ 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;
+ rx_fifo_re_31 = 1'b0;
+ case(tx_src_sel1)
+ SEL_PHY0: rx_fifo_re_01 = tx_fifo_re[1];
+ SEL_PHY2: rx_fifo_re_21 = tx_fifo_re[1];
+ SEL_PHY3: rx_fifo_re_31 = tx_fifo_re[1];
+ endcase
+end
+
+// TX1 FIFO Empty Routing
+always @(*) begin
+ case(tx_src_sel1)
+ 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_PHY3: tx_fifo_empty[1] = rx_fifo_empty_31;
+ SEL_UC: tx_fifo_empty[1] = 1'b1;
+ default: tx_fifo_empty[1] = 1'b1;
+ endcase
+end
+
+
+/*
+ * TX2 Switch Logic
+ * Possible Sources: 0, 1, UC
+ * Note that for TX_MODE_XMT_METRICS, we set the source to be loopback, but it is expected that the
+ * phy controller will use a combination of local memory and the metrics block for its data. These decisions
+ * should be revisited when implementing loopback within this switch module.
+ */
+always @(posedge clk, negedge rstn)
+ if ( !rstn )
+ begin
+ tx_mode2 <= TX_MODE_AN;
+ tx_src_sel2 <= SEL_PHY0;
+ end
+ else if ( tx_f[2] )
+ case( tx_mode2 )
+ TX_MODE_AN:
+ if ( phy_up[2] )
+ tx_mode2 <= TX_MODE_IDLE;
+ TX_MODE_IDLE:
+ if ( !phy_up[2] )
+ tx_mode2 <= TX_MODE_AN;
+ else if (tx_src_sel2==SEL_PHY0 && !rx_fifo_empty_12 )
+ begin
+ tx_mode2 <= TX_MODE_XMT_PKT;
+ tx_src_sel2 <= SEL_PHY1;
+ end
+ else if (!rx_fifo_empty_02 )
+ begin
+ tx_mode2 <= TX_MODE_XMT_PKT;
+ tx_src_sel2 <= SEL_PHY0;
+ end
+ else if (!rx_fifo_empty_12 )
+ begin
+ tx_mode2 <= TX_MODE_XMT_PKT;
+ tx_src_sel2 <= SEL_PHY1;
+ end
+ else if (!rx_fifo_empty_u2)
+ begin
+ tx_mode2 <= TX_MODE_XMT_PKT;
+ tx_src_sel2 <= SEL_UC;
+ end
+ else if (tx_custom)
+ begin
+ tx_mode2 <= TX_MODE_XMT_CUSTOM;
+ tx_src_sel2 <= SEL_PHY2;
+ 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(tx_src_sel2)
+ SEL_PHY0: tx_d2 = rx_d_02;
+ SEL_PHY1: tx_d2 = rx_d_12;
+ SEL_PHY2: tx_d2 = 9'h000;
+ SEL_UC: tx_d2 = rx_d_u2;
+ 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;
+ rx_fifo_re_u2 = 1'b0;
+ case(tx_src_sel2)
+ SEL_PHY0: rx_fifo_re_02 = tx_fifo_re[2];
+ SEL_PHY1: rx_fifo_re_12 = tx_fifo_re[2];
+ SEL_UC: rx_fifo_re_u2 = tx_fifo_re[2];
+ endcase
+end
+
+// TX2 FIFO Empty Routing
+always @(*) begin
+ case(tx_src_sel2)
+ 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; //
+ SEL_UC: tx_fifo_empty[2] = rx_fifo_empty_u2;
+ default: tx_fifo_empty[2] = 1'b1;
+ endcase
+end
+
+
+// TX3 Switch Logic
+// Possible sources: 0, 1
+always @(posedge clk, negedge rstn)
+ if ( !rstn )
+ begin
+ tx_mode3 <= TX_MODE_AN;
+ tx_src_sel3 <= SEL_PHY1;
+ end
+ else if ( tx_f[3] )
+ case( tx_mode3 )
+ TX_MODE_AN:
+ if ( phy_up[3] )
+ tx_mode3 <= TX_MODE_IDLE;
+ TX_MODE_IDLE:
+ if ( !phy_up[3] )
+ tx_mode3 <= TX_MODE_AN;
+ else if (tx_src_sel3==SEL_PHY1 && !rx_fifo_empty_03 )
+ begin
+ tx_mode3 <= TX_MODE_XMT_PKT;
+ tx_src_sel3 <= SEL_PHY0;
+ end
+ else if (!rx_fifo_empty_13 )
+ begin
+ tx_mode3 <= TX_MODE_XMT_PKT;
+ tx_src_sel3 <= SEL_PHY1;
+ end
+ else if (!rx_fifo_empty_03 )
+ begin
+ tx_mode3 <= TX_MODE_XMT_PKT;
+ tx_src_sel3 <= SEL_PHY0;
+ end
+ TX_MODE_XMT_PKT:
+ if ( !phy_up[3] )
+ tx_mode3 <= TX_MODE_AN;
+ else
+ tx_mode3 <= TX_MODE_IDLE;
+ default: tx_mode3 <= TX_MODE_IDLE;
+ endcase
+
+ // TX3 data mux
+ always @(*) begin
+ case(tx_src_sel3)
+ SEL_PHY0: tx_d3 = rx_d_03;
+ SEL_PHY1: tx_d3 = rx_d_13;
+ SEL_PHY2: tx_d3 = rx_d_23;
+ SEL_PHY3: tx_d3 = 9'h000;
+ SEL_UC: tx_d3 = 9'h000;
+ default: tx_d3 = 9'h000;
+ endcase
+ end
+
+ // TX3 FIFO read enable
+ always @(*) begin
+ rx_fifo_re_03 = 1'b0;
+ rx_fifo_re_13 = 1'b0;
+ rx_fifo_re_23 = 1'b0;
+ case(tx_src_sel3)
+ SEL_PHY0: rx_fifo_re_03 = tx_fifo_re[3];
+ SEL_PHY1: rx_fifo_re_13 = tx_fifo_re[3];
+ SEL_PHY2: rx_fifo_re_23 = tx_fifo_re[3];
+ endcase
+ end
+
+ // TX3 FIFO Empty Routing
+ always @(*) begin
+ case(tx_src_sel3)
+ SEL_PHY0: tx_fifo_empty[3] = rx_fifo_empty_03;
+ SEL_PHY1: tx_fifo_empty[3] = rx_fifo_empty_13;
+ SEL_PHY2: tx_fifo_empty[3] = rx_fifo_empty_23;
+ default: tx_fifo_empty[3] = 1'b1;
+ endcase
+ end
+
+/*
+ * Transmit Logic for UC
+ *
+ * The only possible driver is PHY2
+ *
+ * We need to delay the fifo_we one clock since the DPRAM read data comes out one clock delayed
+ */
+
+assign tx_du = rx_d_2u;
+
+always @(*)
+ if ( !rx_fifo_empty_2u )
+ begin
+ i_tx_fifo_we_u = 1'b1;
+ rx_fifo_re_2u = 1'b1;
+ end
+ else
+ begin
+ i_tx_fifo_we_u = 1'b0;
+ rx_fifo_re_2u = 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
diff --git a/src/sync_fifo.v b/src/sync_fifo.v
new file mode 100644
index 0000000..56c83d4
--- /dev/null
+++ b/src/sync_fifo.v
@@ -0,0 +1,110 @@
+/*
+ * sync_fifo.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: FIFO / data store between RX and TX for each path
+ *
+ */
+
+`timescale 1ns /10ps
+
+module sync_fifo #(parameter FIFO_PTR = 11,
+ FIFO_WIDTH = 9,
+ FIFO_DEPTH = 2048 )
+(
+ input rstn,
+ input clk,
+
+
+ // input
+ input we,
+ input [FIFO_WIDTH-1:0] d_in,
+
+ // output
+ input re,
+ output [FIFO_WIDTH-1:0] d_out,
+ output empty,
+ output almost_full,
+
+ // fifo_control
+ input reset_ptrs,
+
+ // debug
+ output active
+
+);
+
+`include "ethernet_params.v"
+
+reg [FIFO_PTR-1:0] wr_ptr;
+reg [FIFO_PTR-1:0] rd_ptr;
+reg [FIFO_PTR-1:0] wr_bytes_available; // use for size calculation below
+
+
+always @(posedge clk, negedge rstn)
+ if( !rstn )
+ wr_ptr <= 'd0;
+ else if ( reset_ptrs )
+ wr_ptr <= 'd0;
+ else if ( we )
+ wr_ptr <= wr_ptr + 1;
+
+/*
+ * rd_ptr
+ * use empty flat to make sure rd_ptr doesn't advance when empty ( error condition )
+ */
+always @(posedge clk, negedge rstn)
+ if( !rstn )
+ rd_ptr <= 'd0;
+ else if ( reset_ptrs )
+ rd_ptr <= 'd0;
+ else if ( re && !empty )
+ rd_ptr <= rd_ptr + 1;
+
+assign empty = ( rd_ptr == wr_ptr ) ? 1'b1 : 1'b0;
+assign almost_full = wr_bytes_available < MTU ? 1'b1 : 1'b0;
+
+always @(posedge clk, negedge rstn)
+ if( !rstn )
+ wr_bytes_available <= FIFO_DEPTH-1;
+ else if ( wr_ptr >= rd_ptr )
+ wr_bytes_available <= FIFO_DEPTH-1 - (wr_ptr - rd_ptr);
+ else
+ wr_bytes_available <= rd_ptr - wr_ptr;
+
+assign active = ~empty;
+
+
+dpram dpram_fifo(
+ .rstn( rstn ),
+ .a_clk( clk ),
+ .a_clk_e( 1'b1 ),
+ .a_we( we ),
+ .a_oe( 1'b0 ),
+ .a_addr( wr_ptr ),
+ .a_din( d_in ),
+ .a_dout( ),
+ // port B
+ .b_clk( clk ),
+ .b_clk_e( re ),
+ .b_we( 1'b0 ),
+ .b_oe( re ),
+ .b_addr( rd_ptr ),
+ .b_din( 9'h0 ),
+ .b_dout( d_out )
+);
+
+endmodule

Highly Recommended Verilog Books