diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/an.v | 122 | ||||
| -rw-r--r-- | src/bin_to_ascii.v | 128 | ||||
| -rw-r--r-- | src/cam.v | 85 | ||||
| -rw-r--r-- | src/clk_gen.v | 67 | ||||
| -rw-r--r-- | src/controller.v | 779 | ||||
| -rw-r--r-- | src/directives.v | 34 | ||||
| -rw-r--r-- | src/dpram.v | 102 | ||||
| -rw-r--r-- | src/drop_fifo.v | 176 | ||||
| -rw-r--r-- | src/ethernet_params.v | 39 | ||||
| -rw-r--r-- | src/fcs.v | 142 | ||||
| -rw-r--r-- | src/half_fifo.v | 219 | ||||
| -rw-r--r-- | src/i2c.v | 391 | ||||
| -rw-r--r-- | src/interrupts.v | 77 | ||||
| -rw-r--r-- | src/ipv4.v | 152 | ||||
| -rw-r--r-- | src/link_timer.v | 67 | ||||
| -rw-r--r-- | src/mac.v | 973 | ||||
| -rw-r--r-- | src/mdio.v | 146 | ||||
| -rw-r--r-- | src/mdio_cont.v | 159 | ||||
| -rw-r--r-- | src/mdio_data_ti.v | 128 | ||||
| -rw-r--r-- | src/metrics.v | 102 | ||||
| -rw-r--r-- | src/pkt_filter.v | 95 | ||||
| -rw-r--r-- | src/sgmii_params.v | 55 | ||||
| -rw-r--r-- | src/spi.v | 315 | ||||
| -rw-r--r-- | src/switch.v | 441 | ||||
| -rw-r--r-- | src/sync_fifo.v | 110 |
25 files changed, 5104 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..54d6976 --- /dev/null +++ b/src/controller.v @@ -0,0 +1,779 @@ +/* + * controller.v + * + * Copyright (C) 2023-2025 Private Island Networks Inc. + * Copyright (C) 2018-2022 Mind Chasers Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 + * + * see https://privateisland.tech/dev/pi-doc for further details + * + * + */ + +module controller #(parameter MDIO_ADDR_SZ = 7, parameter NUM_PHYS=3) +( + input rstn, + input clk, + + // status & errors from ports + input [NUM_PHYS-1:0] phy_int, + input [NUM_PHYS-1:0] mac_int, + + // link status + input [NUM_PHYS-1:0] phy_up, + + // Memory Controller bus + output reg mem_we, + output mem_oe, + output reg [15:0] mem_addr, + output reg[31:0] mem_d_o, + input [31:0] mem_d_i, + + // Device selects for Controller peripherals + output reg [1:0] mac_addr, + output reg [15:0] pkt_filter_addr, + output reg pkt_filter_sel, + output reg mac_sel, + output reg hf_ptrs_sel, + output reg hf_tx_sel, + output reg hf_rx_sel, + + // half FIFO interface + input rx_fifo_int, + output reg rx_fifo_int_acked, + input tx_fifo_empty, + + // mdio_controller interface + output reg mdio_cont_start, + input mdio_cont_done, + output reg [MDIO_ADDR_SZ-1:0] mdio_routine_addr, + input mdio_run, // the mdio controller is active + input mdio_we, // + input [15:0] mdio_d_i, // data read + + // mdio_data params + output [4:0] mdio_phy_addr, + output reg [4:0] mdio_reg_addr, + output reg [7:0] mdio_w_data_h, + output reg [7:0] mdio_w_data_l, + output reg [1:0] mdio_mux_sel, + + // Device Resets + output reg [NUM_PHYS-1:0] phy_resetn, + output reg [NUM_PHYS-1:0] mac_reset, + + // Debug + output [7:0] gpio +); + +`define INCLUDED +`include "cont_params.v" +`include "ethernet_params.v" +`undef INCLUDED + + /* Define Parameters Below */ + // Version: upper byte is major, lower byte is minor + localparam FW_VERSION_VALUE = 16'h0001; + // Version Increment: Set to 0 when releasing version; otherwise, increment value for each preliminary / trial release + localparam FW_INCREMENT_VALUE = 16'h0002; + + // Main Controller states + localparam CONT_ST_INIT= 3'h0, CONT_ST_IDLE=3'h1, CONT_ST_START=3'h2, CONT_ST_BUSY=3'h3, + CONT_ST_DONE= 3'h4, CONT_ST_5=3'h5, CONT_ST_6=3'h6, CONT_ST_7=3'h7; + + // Memory states + localparam MEM_ST_START=4'h0, MEM_ST_1=4'h1, MEM_ST_2=4'h2, MEM_ST_3=4'h3, + MEM_ST_IDLE=4'h4, MEM_ST_RX_START=4'h5, MEM_ST_RX_GET_WR_PTR=4'h6, + MEM_ST_RD_FIFO_START=4'h7, MEM_ST_RD_FIFO=4'h8, MEM_ST_RD_FIFO_DONE=4'h9, + MEM_ST_A=4'ha, MEM_ST_B=4'hb, MEM_ST_VALIDATE=4'hc, MEM_ST_REPLY_START=4'hd, + MEM_ST_REPLY=4'he, MEM_ST_DONE=4'hf; + + // MDIO Init + localparam MI_ST_INIT=4'h0, MI_ST_1=4'h1, MI_ST_2=4'h2, MI_ST_3=4'h3, MI_ST_4=4'h4, + MI_ST_5=4'h5, MI_ST_6=4'h6, MI_ST_7=4'h7, MI_ST_8=4'h8, MI_ST_IDLE=4'h9; + + localparam MEM_EVENT_NONE=3'h0, MEM_EVENT_FIFO=3'h1; + + // Controller Address Space + localparam FW_VERSION_ADDR = 16'h0000; + localparam FW_INCREMENT_ADDR = 16'h0004; + localparam MAC_ADDR = 16'h0010; + localparam PHY_ADDR = 16'h0014; // Controls mdio_mux_sel, not the MIDO PHY Address + localparam PKT_FILT_ADDR = 16'h0018; + localparam RX_MSG_CNT_ADDR = 16'h0020; + localparam TX_PKT_CNT_ADDR = 16'h0024; + + /* Define Variables and Nets Below */ + reg [2:0] cont_state; + reg [3:0] mem_state; + + reg [3:0] mi_state; // mdio init state + reg [7:0] mdio_init_cnt; + reg [1:0] mdio_init_mux_sel; + + // metastability synchronizer regs + reg rx_fifo_int_m1, rx_fifo_int_m2; + + // Vars to capture Message fields + reg [7:0] msg_type; // 1st byte in message + reg [7:0] msg_token; // 2nd byte + reg [15:0] msg_addr; // 3rd and 4th bytes + reg [31:0] msg_data; // 5th through 8th bytes + reg msg_addr_valid, msg_addr_ro, msg_error; // flags + reg [31:0] msg_response; // response to a valid message + + // Command Processing + reg rx_msg_captured; + reg mdio_cmd, cont_msg; + reg [15:0] mdio_d; + + // MDIO Init + reg [7:0] mdio_init_w_data_h, mdio_init_w_data_l; + reg [3:0] mdio_init_reg_addr; + reg [MDIO_ADDR_SZ-1:0] mdio_init_routine_addr; + + // memory controller, HFIFO vars + reg [10:0] rx_wr_ptr, rx_rd_ptr; // access the HFIFO RX data + reg [10:0] tx_wr_ptr; // write into the HFIFO TX data + reg rx_rd_active, tx_wr_active; + reg [4:0] rx_cnt, tx_cnt; + + // metrics + reg [15:0] rx_msg_cnt; + reg [15:0] tx_pkt_cnt; + + + // Synchronize rx_fifo_int + always @(posedge clk, negedge rstn) + if (!rstn) begin + rx_fifo_int_m1 <= 1'b0; + rx_fifo_int_m2 <= 1'b0; + end + else begin + rx_fifo_int_m1 <= rx_fifo_int; + rx_fifo_int_m2 <= rx_fifo_int_m1; + end + + // rx_msg_captured signals a message has been received + always @(posedge clk or negedge rstn) + if (!rstn) + rx_msg_captured <= 1'b0; + else if (mem_state == MEM_ST_RD_FIFO && !rx_rd_active) + rx_msg_captured <= 1'b1; + else + rx_msg_captured <= 1'b0; + + + // rx_msg_cnt + // TODO: reset counter by writing the register + always @(posedge clk or negedge rstn) + if (!rstn) + rx_msg_cnt <= 16'd0; + else if (rx_msg_captured) + rx_msg_cnt <= rx_msg_cnt + 1'b1; + + /* + * state machine for internal controller + * internal commands are handled in this FSM + * MDIO and other external commands need to finish before reaching the end state + */ + always @(posedge clk or negedge rstn) + begin + if (!rstn) + cont_state <= CONT_ST_INIT; + else + case (cont_state) + CONT_ST_INIT: cont_state <= CONT_ST_IDLE; + CONT_ST_IDLE: if (rx_msg_captured) + cont_state <= CONT_ST_START; + CONT_ST_START: cont_state <= CONT_ST_BUSY; + CONT_ST_BUSY: + if (cont_msg || msg_error) + cont_state <= CONT_ST_DONE; + else if (mdio_cmd && mdio_cont_done) + cont_state <= CONT_ST_DONE; + CONT_ST_DONE: + cont_state <= CONT_ST_IDLE; + default: cont_state <= cont_state; + endcase + end + + // cont_msg address decode + always @(posedge clk or negedge rstn) + if (!rstn) + cont_msg <= 1'b0; + else if (cont_state == CONT_ST_IDLE && !rx_msg_captured) + cont_msg <= 1'b0; + else if (rx_msg_captured && (msg_addr[15:8] < MSG_MDIO_ADDR[15:8])) + cont_msg <= 1'b1; + +/*********************************************************************** + * + * Parse messages received from the LAN + * + **********************************************************************/ + + // msg_type + always @(posedge clk or negedge rstn) + if (!rstn) + msg_type <= 7'h0; + else if (mem_state == MEM_ST_IDLE) + msg_type <= 7'h0; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h4) + msg_type <= mem_d_i[7:0]; + + // msg_token + always @(posedge clk or negedge rstn) + if (!rstn) + msg_token <= 7'h0; + else if (mem_state == MEM_ST_IDLE) + msg_token <= 7'h0; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h5) + msg_token <= mem_d_i[7:0]; + + // message address + always @(posedge clk or negedge rstn) + if (!rstn) + msg_addr <= 16'h0000; + else if (mem_state == MEM_ST_IDLE) + msg_addr <= 16'h0000; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h6) + msg_addr[15:8] <= mem_d_i[7:0]; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h7) + msg_addr[7:0] <= mem_d_i[7:0]; + + // message address valid testing + always @(posedge clk or negedge rstn) + if (!rstn) + msg_addr_valid <= 1'b0; + else if (mem_state == MEM_ST_IDLE) + msg_addr_valid <= 1'b0; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h8) + if (msg_addr == FW_VERSION_ADDR || msg_addr == FW_INCREMENT_ADDR || + (msg_addr == MAC_ADDR || msg_addr == PHY_ADDR || msg_addr == PKT_FILT_ADDR) || + (msg_addr == RX_MSG_CNT_ADDR || msg_addr == TX_PKT_CNT_ADDR) || + (msg_addr >= MSG_MAC_ADDR && msg_addr < MSG_INVALID_ADDR)) + msg_addr_valid <= 1'b1; + else + msg_addr_valid <= 1'b0; + + // message read only logic + always @(posedge clk or negedge rstn) + if (!rstn) + msg_addr_ro <= 1'b0; + else if (mem_state == MEM_ST_IDLE) + msg_addr_ro <= 1'b0; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h9) + if (msg_addr == FW_VERSION_ADDR || msg_addr == FW_INCREMENT_ADDR) + msg_addr_ro <= 1'b1; + else + msg_addr_ro <= 1'b0; + + // msg_error + always @(posedge clk or negedge rstn) + if (!rstn) + msg_error <= 1'b0; + else if (mem_state == MEM_ST_IDLE) + msg_error <= 1'b0; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h5 && msg_type > MSG_TYPE_REPLY_ERROR) + msg_error <= 1'b1; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'h9 && !msg_addr_valid) + msg_error <= 1'b1; + + // msg data on writes + always @(posedge clk or negedge rstn) + if (!rstn) + msg_data <= 32'd0; + else if (mem_state == MEM_ST_IDLE) + msg_data <= 32'd0; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'd8) + msg_data[31:24] <= mem_d_i[7:0]; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'd9) + msg_data[23:16] <= mem_d_i[7:0]; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'd10) + msg_data[15:8] <= mem_d_i[7:0]; + else if (mem_state == MEM_ST_RD_FIFO && rx_cnt == 5'd11) + msg_data[7:0] <= mem_d_i[7:0]; + + // msg_response + // TODO: add other peripherals + always @(posedge clk, negedge rstn) + if (!rstn) + msg_response <= 16'heeee; + else if (mem_state == MEM_ST_IDLE) + msg_response <= 16'heeee; + else if (mem_state == MEM_ST_RD_FIFO_DONE && cont_state == CONT_ST_DONE) + casez (msg_addr) + FW_VERSION_ADDR : msg_response <= FW_VERSION_VALUE; + FW_INCREMENT_ADDR : msg_response <= FW_INCREMENT_VALUE; + MAC_ADDR: msg_response <= mac_addr; + PHY_ADDR: msg_response <= mdio_mux_sel; + PKT_FILT_ADDR: msg_response <= pkt_filter_addr; + RX_MSG_CNT_ADDR : msg_response <= rx_msg_cnt; + TX_PKT_CNT_ADDR : msg_response <= tx_pkt_cnt; + {MSG_MDIO_ADDR[15:8],8'h??}: msg_response <= mdio_d; + default: msg_response <= mem_d_i[15:0]; + endcase + + /* + * MDIO controller related + */ + + // mdio_cmd address decode + always @(posedge clk or negedge rstn) + if (!rstn) + mdio_cmd <= 1'b0; + else if (cont_state == CONT_ST_IDLE && !rx_msg_captured) + mdio_cmd <= 1'b0; + else if (rx_msg_captured && msg_addr[15:8] == MSG_MDIO_ADDR[15:8]) + mdio_cmd <= 1'b1; + + // mdio_cont_start indicates to MDIO controller to begin + always @(posedge clk or negedge rstn) + begin + if (!rstn) + mdio_cont_start <= 1'b0; + else if (mi_state == MI_ST_1 || mi_state == MI_ST_3 || mi_state == MI_ST_5) + mdio_cont_start <= 1'b0; + else if (mdio_cmd && cont_state == CONT_ST_START) + mdio_cont_start <= 1'b1; + else + mdio_cont_start <= 1'b0; + end + + // mdio_routine_addr depends on msg_type + always @(posedge clk or negedge rstn) + begin + if (!rstn) + mdio_routine_addr <= 'd0; + else if (mi_state > MI_ST_INIT && mi_state < MI_ST_IDLE) + mdio_routine_addr <= mdio_init_routine_addr; + else if (mdio_cmd && cont_state == CONT_ST_START && msg_type == MSG_TYPE_READ) + mdio_routine_addr <= 'd48; + else if (mdio_cmd && cont_state == CONT_ST_START && msg_type == MSG_TYPE_WRITE) + mdio_routine_addr <= 'd50; + end + + // set mdio_phy_addr + assign mdio_phy_addr = 5'h0c; // Set phy_addr[3:2] + + // set mdio_reg_addr + always @(posedge clk or negedge rstn) + begin + if (!rstn) + mdio_reg_addr <= 'd0; + else if (mi_state > MI_ST_INIT && mi_state < MI_ST_IDLE) + mdio_reg_addr <= mdio_init_reg_addr; + else if (mdio_cmd && cont_state == CONT_ST_START) + mdio_reg_addr <= msg_addr[4: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 (mi_state > MI_ST_INIT && mi_state < MI_ST_IDLE) begin + mdio_w_data_h <= mdio_init_w_data_h; + mdio_w_data_l <= mdio_init_w_data_l; + end + else if (mdio_cmd && msg_type == MSG_TYPE_WRITE) begin + mdio_w_data_h <= msg_data[15:8]; + mdio_w_data_l <= msg_data[7:0]; + end + end + + // mdio_d, data from MDIO read cycle + always @(posedge clk or negedge rstn) + begin + if (!rstn) + mdio_d <= 16'h0000; + else if (mdio_we) + mdio_d <= mdio_d_i; + end + + // PHY Reset + always @(posedge clk, negedge rstn) + if (!rstn) + phy_resetn <= 2'b00; + else if (1'b0) + phy_resetn <= 2'b00; // assert reset + else + phy_resetn <= 2'b11; + + // MAC Reset + always @(posedge clk, negedge rstn) + if (!rstn) + mac_reset <= 2'b11; + else if (1'b0) + mac_reset <= 2'b11; // assert reset + else + mac_reset <= 2'b00; + +/*********************************************************************** + * + * Memory Controller and HFIFO Access LAN Controller + * + * TODO: Review and implement better LAN flow control + * + **********************************************************************/ + + // Memory Controller Tie Offs + assign mem_oe = 1'b1; + + // Memory Controller State Machine + always @(posedge clk, negedge rstn) + if (!rstn) + mem_state <= MEM_ST_START; + else + case (mem_state) + MEM_ST_START: mem_state <= MEM_ST_IDLE; // states before idle are initialization states + MEM_ST_IDLE: + if (rx_fifo_int_m2 && tx_fifo_empty) + mem_state <= MEM_ST_RX_START; + MEM_ST_RX_START: mem_state <= MEM_ST_RX_GET_WR_PTR; // get rx_wr_ptr + MEM_ST_RX_GET_WR_PTR: mem_state <= MEM_ST_RD_FIFO_START; // read first word into cmd buffer + MEM_ST_RD_FIFO_START: mem_state <= MEM_ST_RD_FIFO; + MEM_ST_RD_FIFO: + if(!rx_rd_active) + mem_state <= MEM_ST_RD_FIFO_DONE; + MEM_ST_RD_FIFO_DONE: + if (cont_state == CONT_ST_DONE) + mem_state <= MEM_ST_VALIDATE; + MEM_ST_VALIDATE: + if (msg_type == MSG_TYPE_READ || msg_type == MSG_TYPE_WRITE) + mem_state <= MEM_ST_REPLY_START; + else + mem_state <= MEM_ST_IDLE; + MEM_ST_REPLY_START: if (tx_fifo_empty) // wait for TX FIFO to empty + mem_state <= MEM_ST_REPLY; + MEM_ST_REPLY: // write the response + if(!tx_wr_active) + mem_state <= MEM_ST_DONE; + MEM_ST_DONE: mem_state <= MEM_ST_IDLE; // update tx_wr_ptr + default: mem_state <= MEM_ST_IDLE; + endcase + + + // Primary Memory Controller Actions + always @(posedge clk, negedge rstn) + if (!rstn) begin + mem_we <= 1'b0; + mem_d_o <= 32'd0; + mem_addr <= HF_NULL; + mac_sel <= 1'b0; + pkt_filter_sel <= 1'b0; + hf_ptrs_sel <= 1'b0; + hf_tx_sel <= 1'b0; + hf_rx_sel <= 1'b0; + end + else if (mem_state == MEM_ST_START || mem_state == MEM_ST_IDLE) begin + mem_we <= 1'b0; + mem_d_o <= 32'd0; + mem_addr <= HF_NULL; + hf_ptrs_sel <= 1'b0; + hf_tx_sel <= 1'b0; + hf_rx_sel <= 1'b0; + end + else if (mem_state == MEM_ST_RX_START) begin // start processing interrupt from LAN + hf_ptrs_sel <= 1'b1; + mem_addr <= HF_RX_WR_PTR_LTCH_ADDR; + end + else if (mem_state >= MEM_ST_RX_GET_WR_PTR && mem_state <= MEM_ST_RD_FIFO) begin + hf_ptrs_sel <= 1'b0; + hf_rx_sel <= 1'b1; + mem_addr <= rx_rd_ptr; + end + // perform message related memory functions as needed + else if (mem_state == MEM_ST_RD_FIFO_DONE && cont_state == CONT_ST_START) begin + hf_ptrs_sel <= 1'b0; + hf_tx_sel <= 1'b0; + hf_rx_sel <= 1'b0; + if ((msg_type == MSG_TYPE_WRITE || msg_type == MSG_TYPE_READ) && !msg_error) begin + mem_d_o <= msg_data; + mem_addr <= msg_addr[7:0]; + if (msg_type == MSG_TYPE_WRITE) + mem_we <= 1'b1; + // address decode + case (msg_addr[15:8]) + MSG_MAC_ADDR[15:8]: mac_sel <= 1'b1; + MSG_PKT_FILTER_ADDR[15:8]: pkt_filter_sel <= 1'b1; + endcase + end + end + else if (mem_state == MEM_ST_RD_FIFO_DONE && cont_state == CONT_ST_DONE) begin + hf_tx_sel <= 1'b0; + hf_rx_sel <= 1'b0; + mem_we <= 1'b0; + end + else if (mem_state == MEM_ST_REPLY_START && tx_fifo_empty) begin // write byte cnt + hf_ptrs_sel <= 1'b1; + hf_tx_sel <= 1'b0; + hf_rx_sel <= 1'b0; + mem_we <= 1'b1; + mem_addr <= HF_TX_BYTE_CNT_ADDR; + mem_d_o <= MSG_SZ; + end + else if (mem_state == MEM_ST_REPLY && tx_cnt == 5'd0) begin // write Response to hfifo + hf_ptrs_sel <= 1'b0; + hf_tx_sel <= 1'b1; + hf_rx_sel <= 1'b0; + mem_we <= 1'b1; + mem_addr <= tx_wr_ptr; + if (msg_error || (msg_type == MSG_TYPE_WRITE && msg_addr_ro)) + mem_d_o <= MSG_TYPE_REPLY_ERROR; + else + mem_d_o <= MSG_TYPE_REPLY_SUCCESS; + end + else if (mem_state == MEM_ST_REPLY && tx_cnt == 5'd1) begin + mem_addr <= tx_wr_ptr; + mem_d_o <= msg_token; + end + else if (mem_state == MEM_ST_REPLY && tx_cnt == 5'd2) begin // write addr to hfifo + mem_addr <= tx_wr_ptr; + mem_d_o <= msg_addr[15:8]; + end + else if (mem_state == MEM_ST_REPLY && tx_cnt == 5'd3) begin + mem_addr <= tx_wr_ptr; + mem_d_o <= msg_addr[7:0]; + end + else if (mem_state == MEM_ST_REPLY && tx_cnt == 5'd4) begin // write address to hfifo + mem_addr <= tx_wr_ptr; + mem_d_o <= msg_response[31:24]; + end + else if (mem_state == MEM_ST_REPLY && tx_cnt == 5'd5) begin // write data msb to hfifo + mem_addr <= tx_wr_ptr; + mem_d_o <= {24'd0, msg_response[23:16]}; + end + else if (mem_state == MEM_ST_REPLY && tx_cnt == 5'd6) begin // write data msb to hfifo + mem_addr <= tx_wr_ptr; + mem_d_o <= msg_response[15:8]; + end + else if (mem_state == MEM_ST_REPLY && tx_cnt == 5'd7) begin // write data lsb to hfifo + mem_addr <= tx_wr_ptr; + mem_d_o <= {1'b1, msg_response[7:0]}; // bit 8: set end of frame bit + end + else if (mem_state == MEM_ST_DONE) begin // update tx_wr_ptr in hfifo + hf_tx_sel <= 1'b0; + hf_ptrs_sel <= 1'b1; + mem_addr <= HF_TX_WR_PTR_ADDR; + mem_we <= 1'b1; + mem_d_o <= {21'd0, tx_wr_ptr}; + end + else begin + mem_we <= 1'b0; + mem_d_o <= 32'd0; + mem_addr <= HF_NULL; + mac_sel <= 1'b0; + pkt_filter_sel <= 1'b0; + hf_ptrs_sel <= 1'b0; + hf_tx_sel <= 1'b0; + hf_rx_sel <= 1'b0; + end + + // rx_cnt + always @(posedge clk, negedge rstn) + if (!rstn) + rx_cnt <= 5'd2; + else if (mem_state == MEM_ST_IDLE) + rx_cnt <= 5'd2; + else if (rx_rd_active) + rx_cnt <= rx_cnt + 1'b1; + + // tx_cnt + always @(posedge clk, negedge rstn) + if (!rstn) + tx_cnt <= 5'd0; + else if (mem_state == MEM_ST_IDLE || mem_state == MEM_ST_REPLY_START) + tx_cnt <= 5'd0; + else if (tx_wr_active) + tx_cnt <= tx_cnt + 1'b1; + + always @(posedge clk, negedge rstn) + if (!rstn) + tx_pkt_cnt <= 16'd0; + else if (mem_state == MEM_ST_DONE) // MSG + tx_pkt_cnt <= tx_pkt_cnt + 1'b1; + + + +/*********************************************************************** + * + * HFIFO Control Logic for accessing the LAN + * + * General Flow: + * interrupt + * receive message (data path: Controller from LAN->Switch->Controller) + * transmit response(s) (data path: Controller->Switch->LAN) + * + **********************************************************************/ + + // ACK HFIFO interrupt due to LAN activity + always @(posedge clk, negedge rstn) + if (!rstn) + rx_fifo_int_acked <= 1'b0; + else if (mem_state == MEM_ST_RX_START && tx_fifo_empty) + rx_fifo_int_acked <= 1'b1; + else + rx_fifo_int_acked <= 1'b0; + + // rx_rd_ptr for reading from HFIFO + always @(posedge clk, negedge rstn) + if (!rstn) + rx_rd_ptr <= 11'd0; + else if (mem_state == MEM_ST_RX_START) + rx_rd_ptr <= rx_rd_ptr + SZ_ETH_HEADER + SZ_IPV4_HEADER + SZ_UDP_HEADER; + else if (rx_rd_active && rx_rd_ptr != rx_wr_ptr) + rx_rd_ptr <= rx_rd_ptr + 11'd1; + + // capture rx_wr_ptr to determine when FIFO is empty + always @(posedge clk, negedge rstn) + if (!rstn) + rx_wr_ptr <= 11'h0; + else if (mem_state == MEM_ST_RX_GET_WR_PTR) + rx_wr_ptr <= mem_d_i[10:0]; + + // rx_rd_active + always @(posedge clk, negedge rstn) + if (!rstn) + rx_rd_active <= 1'b0; + else if (rx_rd_ptr != rx_wr_ptr && (mem_state == MEM_ST_RD_FIFO_START || mem_state == MEM_ST_RD_FIFO)) + rx_rd_active <= 1'b1; + else + rx_rd_active <= 1'b0; + + // tx_wr_active into TX DPRAM + always @(posedge clk, negedge rstn) + if (!rstn) + tx_wr_active <= 1'b0; + else if ((mem_state == MEM_ST_REPLY_START && tx_fifo_empty) || (mem_state == MEM_ST_REPLY && tx_cnt <= MSG_SZ[4:0])) // Response + tx_wr_active <= 1'b1; + else + tx_wr_active <= 1'b0; + + always @(posedge clk, negedge rstn) + if (!rstn) + tx_wr_ptr <= 11'd0; + else if (tx_wr_active) + tx_wr_ptr <= tx_wr_ptr + 11'd1; + + + /*********************************************************************** + * + * Write Logic for Controller Internal Registers + * + **********************************************************************/ + + always @(posedge clk, negedge rstn) + if (!rstn) begin + mac_addr <= 2'd0; + mdio_mux_sel <= 2'b00; + pkt_filter_addr <= 16'd0; + end + else if (rx_msg_captured && !msg_error && msg_type == MSG_TYPE_WRITE) begin + if (msg_addr==MAC_ADDR) + mac_addr <= msg_data[1:0]; + else if (msg_addr==PHY_ADDR) + mdio_mux_sel <= msg_data[1:0]; + else if (msg_addr==PKT_FILT_ADDR) + pkt_filter_addr <= msg_data[15:0]; + end + + /*********************************************************************** + * + * MDIO Initialization + * + * TODO: Specific for Betsy, such as PHY LED assignment + * + **********************************************************************/ + + // Give the board and circuits time to stabilize (being overly cautious) + always @(posedge clk or negedge rstn) + if (!rstn) + mdio_init_cnt <= 8'd0; + else if (mi_state == MI_ST_2 && mdio_cont_done) + mdio_init_cnt <= 8'd0; + else if (mdio_init_cnt < 8'd100) + mdio_init_cnt <= mdio_init_cnt + 8'd1; + + + always @(posedge clk or negedge rstn) + if (!rstn) + mi_state <= MI_ST_INIT; + else if (1'b1) + mi_state <= MI_ST_INIT; + else + case (mi_state) + MI_ST_INIT: if (mdio_init_cnt == 8'd100) + mi_state <= MI_ST_1; + MI_ST_1: mi_state <= MI_ST_2; + MI_ST_2: if (mdio_cont_done) + mi_state <= MI_ST_INIT; + MI_ST_3: mi_state <= MI_ST_4; + MI_ST_4: if (mdio_cont_done) + mi_state <= MI_ST_5; + MI_ST_5: mi_state <= MI_ST_6; + MI_ST_6: if (mdio_cont_done) + mi_state <= MI_ST_IDLE; + MI_ST_7: mi_state <= MI_ST_8; + MI_ST_8: if (mdio_cont_done) + mi_state <= MI_ST_IDLE; + default: mi_state <= MI_ST_IDLE; + endcase + + + // MDIO INIT + // TODO: Configure PHY LEDs + always @(posedge clk or negedge rstn) + if (!rstn) begin + mdio_init_mux_sel <= 2'b00; + mdio_init_reg_addr <= 'd3; // PHYIDR2 + mdio_init_routine_addr <= 'd20; + mdio_init_w_data_h <= 8'h00; + mdio_init_w_data_l <= 8'h00; + end + else if (mi_state == MI_ST_1) begin // Extended Read of PHY0 PROG_GAIN + mdio_init_mux_sel <= 2'b00; + mdio_init_routine_addr <= 'd20; // 'd60; + mdio_init_w_data_h <= 8'h01; + mdio_init_w_data_l <= 8'hD5; + end + else if (mi_state == MI_ST_3) begin // Extended Write of PHY0 PROG_GAIN + mdio_init_routine_addr <= 'd80; + mdio_init_w_data_h <= 8'hF5; + mdio_init_w_data_l <= 8'h02; + end + else if (mi_state == MI_ST_5) begin // Extended Read of PHY1 PROG_GAIN + mdio_init_mux_sel <= 2'b01; + mdio_init_routine_addr <= 'd60; + mdio_init_w_data_h <= 8'h01; + mdio_init_w_data_l <= 8'hD5; + end + else if (mi_state == MI_ST_7) begin // Extended Write of PHY1 PROG_GAIN + mdio_init_routine_addr <= 'd80; + mdio_init_w_data_h <= 8'hF5; + mdio_init_w_data_l <= 8'h03; + end + + /* + * Debug + */ + assign gpio = 8'h00; + + +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..c85f6ac --- /dev/null +++ b/src/half_fifo.v @@ -0,0 +1,219 @@ +/* + * half_fifo.v + * + * Copyright (C) 2024-2025 Private Island Networks Inc. + * Copyright (C) 2018-2022 Mind Chasers Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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. + * + * Internal Controller / FIFO interface for accessing Ethernet Switch + * + * + */ + +module half_fifo #(parameter DATA_WIDTH = 9, DPRAM_DEPTH=11) +( + input rstn, + input fifo_clk, + input uc_clk, + + // controller interrupt support + output reg rx_fifo_int, // signals that a complete packet has arrived + input rx_fifo_int_acked, + + // controller interface + input [DPRAM_DEPTH-1:0] dpram_addr, + input [DPRAM_DEPTH-1:0] dpram_din, // size it to include 11-bit pointers + output reg [DPRAM_DEPTH-1:0] dpram_dout, // size it to include 11-bit pointers + input dpram_we, + input dpram_oe, + input dpram_ptrs_sel, + input dpram_tx_sel, + input dpram_rx_sel, + + // FIFO RX (switch -> hfifo -> controller) + input fifo_we, + input [DATA_WIDTH-1:0] fifo_d_in, + + // FIFO TX (controller -> hfifo -> switch) + input fifo_re, + output reg [10:0] tx_byte_cnt, + output [DATA_WIDTH-1:0] fifo_d_out, + output tx_empty + +); + +`define INCLUDED +`include "cont_params.v" +`undef INCLUDED + +/* + * Pointers for accessing memory. + * UC writes TX and reads RX + * Switch writes (via FIFO I/F) RX and reads TX + * This is the opposite of the system view + */ +reg [DPRAM_DEPTH-1:0] rx_wr_ptr, rx_wr_ptr_latched; + +reg [DPRAM_DEPTH-1:0] tx_wr_ptr, tx_wr_ptr_latched; +reg [DPRAM_DEPTH-1:0] tx_rd_ptr; + +reg tx_wr_ptr_written, tx_wr_ptr_written_m1, tx_wr_ptr_written_m2; + +reg fifo_we_m1; + +reg reset_ptrs; +wire [DATA_WIDTH-1:0] dpram_tx_dout, dpram_rx_dout; + +/* read data mux, refer to addr params above */ +always @(*) + casez({ dpram_tx_sel, dpram_rx_sel, dpram_ptrs_sel, dpram_addr[2:0] }) + 6'b001000: dpram_dout = tx_wr_ptr; + 6'b001001: dpram_dout = tx_rd_ptr; // meta stable + 6'b001010: dpram_dout = rx_wr_ptr_latched; + 6'b001011: dpram_dout = 11'd0; + 6'b001100: dpram_dout = {10'd0, reset_ptrs}; + 6'b010???: dpram_dout = {2'b00, dpram_rx_dout}; + 6'b100???: dpram_dout = {2'b00, dpram_tx_dout}; + default: dpram_dout = {2'b00, dpram_rx_dout}; + endcase + +always @(posedge uc_clk, negedge rstn) + if (!rstn) + reset_ptrs <= 1'b0; + else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_RESET_PTRS) + reset_ptrs <= dpram_din[0]; + +always @(posedge uc_clk, negedge rstn) + if (!rstn) + tx_byte_cnt <= 11'd0; + else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_TX_BYTE_CNT_ADDR) + tx_byte_cnt <= dpram_din; + +/* TX wr ptr (UC writes) */ +always @(posedge uc_clk, negedge rstn) + if (!rstn) begin + tx_wr_ptr <= 'h0; + tx_wr_ptr_written <= 1'b0; + end + else if (reset_ptrs) begin + tx_wr_ptr <= 'h0; + tx_wr_ptr_written <= 1'b1; + end + else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_TX_WR_PTR_ADDR) begin + tx_wr_ptr <= dpram_din; + tx_wr_ptr_written <= 1'b1; + end + else + tx_wr_ptr_written <= 1'b0; + +always @(posedge fifo_clk, negedge rstn) + if (!rstn) begin + tx_wr_ptr_written_m1 <= 1'b0; + tx_wr_ptr_written_m2 <= 1'b0; + end + else begin + tx_wr_ptr_written_m1 <= tx_wr_ptr_written; + tx_wr_ptr_written_m2 <= tx_wr_ptr_written_m1; + end + + +always @(posedge fifo_clk, negedge rstn) + if(!rstn) + tx_wr_ptr_latched <= 8'h00; + else if (tx_wr_ptr_written_m2) + tx_wr_ptr_latched <= tx_wr_ptr; + +/* TX rd ptr (switch reads via FIFO), auto increment */ +always @(posedge fifo_clk, negedge rstn) + if(!rstn) + tx_rd_ptr <= 11'd0; + else if (reset_ptrs) + tx_rd_ptr <= 11'd0; + else if (fifo_re && !tx_empty) // advance the pointer if FIFO isn't empty + tx_rd_ptr <= tx_rd_ptr + 1'b1; + +/* RX wr ptr (switch writes via FIFO) */ +always @(posedge fifo_clk, negedge rstn) + if(!rstn) + rx_wr_ptr <= 11'd0; + else if (reset_ptrs) + rx_wr_ptr <= 11'd0; + else if (fifo_we) + rx_wr_ptr <= rx_wr_ptr + 1'b1; + +assign tx_empty = (tx_rd_ptr == tx_wr_ptr_latched); + +/* Assert interrupt whenever fifo_we is negated (writing is done) */ +always @(posedge fifo_clk, negedge rstn) + if (!rstn) + fifo_we_m1 <= 1'b0; + else + fifo_we_m1 <= fifo_we; + +// Metastability note: rx_wr_ptr_latched should be read one or more clock cycles after rx_fifo_int asserts +always @(posedge fifo_clk, negedge rstn) + if (!rstn) begin + rx_fifo_int <= 1'b0; + rx_wr_ptr_latched <= 11'd0; + end + else if (!fifo_we & fifo_we_m1) begin // end of write + rx_fifo_int <= 1'b1; + rx_wr_ptr_latched <= rx_wr_ptr; + end + else if (rx_fifo_int_acked) // clear interrupt + rx_fifo_int <= 1'b0; + +/* controller uses A side, FIFO uses B side + * controller writes TX path and reads RX path */ +// 2K DPRAM +dpram_inf dpram_tx( + .rstn(rstn), + .a_clk(uc_clk), + .a_clk_e(dpram_tx_sel), + .a_we(dpram_we), + .a_oe(1'b1), + .a_addr(dpram_addr), + .a_din(dpram_din[8:0]), + .a_dout(dpram_tx_dout), + // port B + .b_clk(fifo_clk), + .b_clk_e(1'b1), + .b_we(1'b0), + .b_oe(fifo_re), + .b_addr(tx_rd_ptr), + .b_din(9'h0), + .b_dout(fifo_d_out) +); + +// 2K DPRAM +dpram_inf dpram_rx( + .rstn(rstn), + .a_clk(uc_clk), + .a_clk_e(dpram_rx_sel), + .a_we(1'b0), + .a_oe(1'b1), + .a_addr(dpram_addr), + .a_din(9'h000), + .a_dout(dpram_rx_dout), + // port B + .b_clk(fifo_clk), + .b_clk_e(1'b1), + .b_we(fifo_we), + .b_oe(1'b0), + .b_addr(rx_wr_ptr), + .b_din(fifo_d_in), + .b_dout() +); + +endmodule diff --git a/src/i2c.v b/src/i2c.v 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..fa6d75e --- /dev/null +++ b/src/switch.v @@ -0,0 +1,441 @@ +/* + * switch.v + * + * Copyright 2024, 2025 Private Island Networks Inc. + * Copyright 2018 Mind Chasers Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * function: switch packet paths from RX to TX for LF_A_V01 + * + */ + +module switch #(parameter NUM_PHYS=3) +( + input rstn, + input clk, + + // PHY status + input [NUM_PHYS-1:0] phy_up, + input [NUM_PHYS-1:0] mode_100Mbit, + + // FIFO input data from RX FIFOs + input [8:0] rx_d_01, + input [8:0] rx_d_02, + input [8:0] rx_d_0u, + input [8:0] rx_d_10, + input [8:0] rx_d_12, + input [8:0] rx_d_20, + input [8:0] rx_d_21, + input [8:0] rx_d_u0, + + // RX FIFO read enables + output reg rx_fifo_re_01, rx_fifo_re_02, rx_fifo_re_0u, + output reg rx_fifo_re_10, rx_fifo_re_12, + output reg rx_fifo_re_20, rx_fifo_re_21, + output reg rx_fifo_re_u0, + + // RX FIFO Empty flags + input rx_fifo_empty_01, rx_fifo_empty_02, rx_fifo_empty_0u, + input rx_fifo_empty_10, rx_fifo_empty_12, + input rx_fifo_empty_20, rx_fifo_empty_21, + input rx_fifo_empty_u0, + + input [NUM_PHYS-1:0] rx_wr_done, + + // RX Byte Count + input [10:0] rx0_byte_cnt, + input [10:0] rx1_byte_cnt, + input [10:0] rx2_byte_cnt, + input [10:0] rxu_byte_cnt, + + // TX FIFO output from internal muxes + output reg [8:0] tx_d0, + output reg [8:0] tx_d1, + output reg [8:0] tx_d2, + output [8:0] tx_du, + + // TX FIFO read enable inputs (need to route to RX output FIFOs) + input [NUM_PHYS-1:0] tx_fifo_re, + output reg tx_fifo_we_u, + + // TX FIFO Empty Flags (need to route to RX output FIFOs) + output reg [NUM_PHYS-1:0] tx_fifo_empty, + + // TX modes for the PHYs and uc + output reg [2:0] tx_mode0, + output reg [2:0] tx_mode1, + output reg [2:0] tx_mode2, + output reg [2:0] tx_modeu, + + // TX byte cnt + output reg [10:0] tx0_byte_cnt, + output reg [10:0] tx1_byte_cnt, + output reg [10:0] tx2_byte_cnt, + + output reg [2:0] tx0_src_sel, + output reg [2:0] tx1_src_sel, + output reg [2:0] tx2_src_sel, + + // TX state machine done flag + input [NUM_PHYS-1:0] tx_f, + + // TX custom packet + input tx_custom +); + + + +// IPG for Port 0 +wire ipg_met; +reg [6:0] ipg_cnt; + +reg rx_fifo_empty_u0_m1, rx_fifo_empty_u0_m2; + + +reg [3:0] fr_100mbit_cnt; +reg i_tx_fifo_we_u; +reg [10:0] i_rx0_byte_cnt, i_rx1_byte_cnt, i_rx2_byte_cnt; + +`define INCLUDED +`include "ethernet_params.v" +`undef INCLUDED + +localparam SEL_PHY0 = 3'b000, + SEL_PHY1 = 3'b001, + SEL_PHY2 = 3'b010, + SEL_UC = 3'b111; + + +// capture the rx_byte_cnt values when write is done. TODO: needs to be a shallow Q to match FIFO +always @(posedge clk, negedge rstn) + if ( !rstn ) + i_rx0_byte_cnt <= 'h0; + else if ( rx_wr_done[0]) + i_rx0_byte_cnt <= rx0_byte_cnt; + +always @(posedge clk, negedge rstn) + if ( !rstn ) + i_rx1_byte_cnt <= 'h0; + else if ( rx_wr_done[1]) + i_rx1_byte_cnt <= rx1_byte_cnt; + +always @(posedge clk, negedge rstn) + if ( !rstn ) + i_rx2_byte_cnt <= 'h0; + else if ( rx_wr_done[2]) + i_rx2_byte_cnt <= rx2_byte_cnt; + +assign ipg_met = ipg_cnt >= IPG ? 1'b1 : 1'b0; + +/* free running 100Mbit counter */ +always @(posedge clk, negedge rstn) + if ( !rstn ) + fr_100mbit_cnt <= 4'd0; + else if ( fr_100mbit_cnt == 4'd9 ) + fr_100mbit_cnt <= 4'd0; + else + fr_100mbit_cnt <= fr_100mbit_cnt + 1'b1; + +/* IPG counter */ +always @(posedge clk, negedge rstn) + if ( !rstn ) + ipg_cnt <= 7'd0; + else if ( tx_f[0] && tx_mode0 >= TX_MODE_XMT_PKT ) + ipg_cnt <= 7'd0; + else if ( mode_100Mbit[0] && fr_100mbit_cnt == 4'd9 && !ipg_met ) + ipg_cnt <= ipg_cnt + 1; + else if ( !mode_100Mbit[0] && !ipg_met ) + ipg_cnt <= ipg_cnt + 1'b1; + + +// Transfer to the pclk domain +always @(posedge clk, negedge rstn) + if ( !rstn ) begin + rx_fifo_empty_u0_m1 <= 1'b1; + rx_fifo_empty_u0_m2 <= 1'b1; + end + else begin + rx_fifo_empty_u0_m1 <= rx_fifo_empty_u0; + rx_fifo_empty_u0_m2 <= rx_fifo_empty_u0_m1; + end + + +// TX0 Switch Logic +// Possible sources: u, 1, 2 +always @(posedge clk, negedge rstn) + if ( !rstn ) + begin + tx_mode0 <= TX_MODE_AN; + tx0_src_sel <= SEL_PHY1; + tx0_byte_cnt <= 'h0; + end + else if ( tx_f[0] ) + case( tx_mode0 ) + TX_MODE_AN: + if ( phy_up[0] ) + tx_mode0 <= TX_MODE_IDLE; + TX_MODE_IDLE: + if ( !phy_up[0] ) + tx_mode0 <= TX_MODE_AN; + else if ( !ipg_met ) + tx_mode0 <= TX_MODE_IDLE; + else if (!rx_fifo_empty_u0_m2 ) // controller has data + begin + tx_mode0 <= TX_MODE_XMT_CUSTOM; + tx0_src_sel <= SEL_UC; + tx0_byte_cnt <= rxu_byte_cnt; + end +`ifdef PHY2_PRESENT + else if (tx0_src_sel==SEL_PHY1 && !rx_fifo_empty_20 ) + begin + tx_mode0 <= TX_MODE_XMT_PKT; + tx0_src_sel <= SEL_PHY2; + tx0_byte_cnt <= i_rx2_byte_cnt; + end +`endif + else if (!rx_fifo_empty_10 ) + begin + tx_mode0 <= TX_MODE_XMT_PKT; + tx0_src_sel <= SEL_PHY1; + tx0_byte_cnt <= i_rx1_byte_cnt; + end +`ifdef PHY2_PRESENT + else if (!rx_fifo_empty_20 ) + begin + tx_mode0 <= TX_MODE_XMT_PKT; + tx0_src_sel <= SEL_PHY2; + tx0_byte_cnt <= i_rx2_byte_cnt; + end +`endif + TX_MODE_XMT_PKT: + if ( !phy_up[0] ) + tx_mode0 <= TX_MODE_AN; + else + tx_mode0 <= TX_MODE_IDLE; + default: tx_mode0 <= TX_MODE_IDLE; + endcase + + +// TX0 data mux +always @(*) begin + case(tx0_src_sel) + SEL_PHY0: tx_d0 = 9'h000; + SEL_PHY1: tx_d0 = rx_d_10; + SEL_PHY2: tx_d0 = rx_d_20; + SEL_UC: tx_d0 = rx_d_u0; + default: tx_d0 = 9'h000; + endcase +end + +// TX0 FIFO read enable +always @(*) begin + rx_fifo_re_10 = 1'b0; + rx_fifo_re_20 = 1'b0; + rx_fifo_re_u0 = 1'b0; + case(tx0_src_sel) + SEL_PHY1: rx_fifo_re_10 = tx_fifo_re[0]; + SEL_PHY2: rx_fifo_re_20 = tx_fifo_re[0]; + SEL_UC: rx_fifo_re_u0 = tx_fifo_re[0]; + endcase +end + +// TX0 FIFO Empty Routing +always @(*) begin + case(tx0_src_sel) + SEL_PHY1: tx_fifo_empty[0] = rx_fifo_empty_10; + SEL_PHY2: tx_fifo_empty[0] = rx_fifo_empty_20; + SEL_UC: tx_fifo_empty[0] = rx_fifo_empty_u0_m2; + default: tx_fifo_empty[0] = 1'b1; + endcase +end + +// TX1 Switch Logic +// Possible sources: 0, 2 in priority +always @(posedge clk, negedge rstn) + if ( !rstn ) + begin + tx_mode1 <= TX_MODE_AN; + tx1_src_sel <= SEL_PHY0; + end + else if ( tx_f[1] ) + case( tx_mode1 ) + TX_MODE_AN: + if ( phy_up[1] ) + tx_mode1 <= TX_MODE_IDLE; + TX_MODE_IDLE: + if ( !phy_up[1] ) + tx_mode1 <= TX_MODE_AN; +`ifdef PHY2_PRESENT + else if (tx1_src_sel==SEL_PHY0 && !rx_fifo_empty_21 ) + begin + tx_mode1 <= TX_MODE_XMT_PKT; + tx1_src_sel <= SEL_PHY2; + end +`endif + else if (!rx_fifo_empty_01 ) + begin + tx_mode1 <= TX_MODE_XMT_PKT; + tx1_src_sel <= SEL_PHY0; + end +`ifdef PHY2_PRESENT + else if (!rx_fifo_empty_21 ) + begin + tx_mode1 <= TX_MODE_XMT_PKT; + tx1_src_sel <= SEL_PHY2; + end +`endif + TX_MODE_XMT_PKT: + if ( !phy_up[1] ) + tx_mode1 <= TX_MODE_AN; + else + tx_mode1 <= TX_MODE_IDLE; + default: tx_mode1 <= TX_MODE_IDLE; + endcase + +// TX1 data mux +always @(*) begin + case(tx1_src_sel) + SEL_PHY0: tx_d1 = rx_d_01; + SEL_PHY1: tx_d1 = 9'h000; + SEL_PHY2: tx_d1 = rx_d_21; + SEL_UC: tx_d1 = 9'h000; + default: tx_d1 = 9'h000; + endcase +end + +// TX1 FIFO read enable +always @(*) begin + rx_fifo_re_01 = 1'b0; + rx_fifo_re_21 = 1'b0; + case(tx1_src_sel) + SEL_PHY0: rx_fifo_re_01 = tx_fifo_re[1]; + SEL_PHY2: rx_fifo_re_21 = tx_fifo_re[1]; + endcase +end + +// TX1 FIFO Empty Routing +always @(*) begin + case(tx1_src_sel) + SEL_PHY0: tx_fifo_empty[1] = rx_fifo_empty_01; + SEL_PHY1: tx_fifo_empty[1] = 1'b1; + SEL_PHY2: tx_fifo_empty[1] = rx_fifo_empty_21; + SEL_UC: tx_fifo_empty[1] = 1'b1; + default: tx_fifo_empty[1] = 1'b1; + endcase +end + + +/* + * TX2 Switch Logic + * Possible Sources: 0, 1 + */ +always @(posedge clk, negedge rstn) + if ( !rstn ) + begin + tx_mode2 <= TX_MODE_AN; + tx2_src_sel <= SEL_PHY0; + end + else if ( tx_f[2] ) + case( tx_mode2 ) + TX_MODE_AN: + if ( phy_up[2] ) + tx_mode2 <= TX_MODE_IDLE; + TX_MODE_IDLE: + if ( !phy_up[2] ) + tx_mode2 <= TX_MODE_AN; + else if (tx2_src_sel==SEL_PHY0 && !rx_fifo_empty_12 ) + begin + tx_mode2 <= TX_MODE_XMT_PKT; + tx2_src_sel <= SEL_PHY1; + end + else if (!rx_fifo_empty_02 ) + begin + tx_mode2 <= TX_MODE_XMT_PKT; + tx2_src_sel <= SEL_PHY0; + end + else if (!rx_fifo_empty_12 ) + begin + tx_mode2 <= TX_MODE_XMT_PKT; + tx2_src_sel <= SEL_PHY1; + end + TX_MODE_XMT_PKT: + if ( !phy_up[2] ) + tx_mode2 <= TX_MODE_AN; + else + tx_mode2 <= TX_MODE_IDLE; + default: tx_mode2 <= TX_MODE_IDLE; + endcase + +// TX2 data mux +always @(*) begin + case(tx2_src_sel) + SEL_PHY0: tx_d2 = rx_d_02; + SEL_PHY1: tx_d2 = rx_d_12; + SEL_PHY2: tx_d2 = 9'h000; + default: tx_d2 = 9'h000; + endcase +end + +// TX2 FIFO read enable +always @(*) begin + rx_fifo_re_02 = 1'b0; + rx_fifo_re_12 = 1'b0; + case(tx2_src_sel) + SEL_PHY0: rx_fifo_re_02 = tx_fifo_re[2]; + SEL_PHY1: rx_fifo_re_12 = tx_fifo_re[2]; + endcase +end + +// TX2 FIFO Empty Routing +always @(*) begin + case(tx2_src_sel) + SEL_PHY0: tx_fifo_empty[2] = rx_fifo_empty_02; + SEL_PHY1: tx_fifo_empty[2] = rx_fifo_empty_12; + SEL_PHY2: tx_fifo_empty[2] = 1'b0; // + default: tx_fifo_empty[2] = 1'b1; + endcase +end + + + +/* + * Transmit Logic for UC + * + * The only possible driver is PHY0 + * + * We need to delay the fifo_we one clock since the DPRAM read data comes out one clock delayed + */ + +assign tx_du = rx_d_0u; + +always @(*) + if ( !rx_fifo_empty_0u ) + begin + i_tx_fifo_we_u = 1'b1; + rx_fifo_re_0u = 1'b1; + end + else + begin + i_tx_fifo_we_u = 1'b0; + rx_fifo_re_0u = 1'b0; + end + +always @(posedge clk, negedge rstn) + if ( !rstn ) + tx_fifo_we_u <= 1'b0; + else + tx_fifo_we_u <= i_tx_fifo_we_u; + + +endmodule
\ No newline at end of file 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 |



