/* * 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