summaryrefslogtreecommitdiffhomepage
path: root/src/controller.v
diff options
context:
space:
mode:
Diffstat (limited to 'src/controller.v')
-rw-r--r--src/controller.v779
1 files changed, 779 insertions, 0 deletions
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

Highly Recommended Verilog Books