/* * controller.v * * Copyright (C) 2018, 2109, 2020, 2021 Mind Chasers Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * function: FPGA internal state machine controller * */ `timescale 1ns /10ps module controller #(parameter ADDR_SZ = 7) ( input rstn, input clk, input init, input pulse_100ms, // PCS status lines input [3:0] pcs_rx_error, input [1:0] pll_lol, // link status input [3:0] port_up, // mdio_controller interface output reg mdio_cont_start, input mdio_cont_done, output reg [ADDR_SZ-1:0] mdio_routine_addr, input mdio_run, // the mdio controller is active // mdio_data params output reg [4:0] mdio_page, output reg [4:0] mdio_reg_addr, output reg [7:0] mdio_w_data_h, output reg [7:0] mdio_w_data_l, // bin_to_ascii interface for MDIO or CONT -> bin_to_ascii -> FIFO -> I2C input bin_to_ascii_run, // asserted if block is active and writing FIFO // sync_fifo interface: controller to fifo output fifo_mux_sel, // 0 is self, 1 is mdio_controller output reg fifo_we, output reg [6:0] read_fifo_d_o, // i2c interface: i2c to controller input i2c_rx_we, input i2c_rx_done, input [7:0] i2c_d_in, // DCU Resets output [1:0] pcs_rst_dual, output [1:0] serdes_rst_dual, output [1:0] tx_serdes_rst, // Channel output [3:0] phy_resetn, output [3:0] mac_reset, output [3:0] tx_pcs_rst, output [3:0] rx_serdes_rst, output [3:0] rx_pcs_rst, output reg [1:0] mdio_mux_sel, // TX custom packet output reg tx_custom ); // state encoding localparam BUFFER_SZ = 8; // index starts at 1, 0 is cmd localparam S0= 4'h0, S1=4'h1, S2=4'h2, S3=4'h3, S4= 4'h4, S5=4'h5, S6=4'h6, S7=4'h7, S8= 4'h8, S9=4'h9, S10=4'ha, S11=4'hb, S12= 4'hc, S13=4'hd, S14=4'he, S15=4'hf; // ascii codes we use localparam ASCIIc = 7'h63, // channel resets ASCIId = 7'h64, // set and dump page ASCIIf = 7'h66, // FIFO control ASCIIi = 7'h69, // init ( sgmii mode ) ASCIIl = 7'h6c, // link status ASCIIm = 7'h6d, // mdio mux ASCIIp = 7'h70, // PCS /SERDES block status ASCIIq = 7'h71, ASCIIr = 7'h72, // set address and read reg ASCIIs = 7'h73, // show mdio line status ASCIIt = 7'h74, // transmit test packet ASCIIu = 7'h75, ASCIIw = 7'h77, // write reg at preset page and address ASCIIx = 7'h78, // control word ASCIIy = 7'h79, // extended read ASCIIz = 7'h7a, // extended write ASCII_ = 7'h5f, LF = 7'h0a, CR = 7'h0d; reg [1:0] x_reg[0:3]; // ASCIIx: phy_reset and mac_reset reg [2:0] c_reg[0:3]; // ASCIIc one reg per channel reg [2:0] u_reg[0:1]; // 2 DCU's: pcs_rst_dual, serdes_rst_dual, tx_serdes_rst reg [3:0] cont_state; reg [3:0] cnt; // input cnt, 0 is cmd, 1-8 are buffer, beyond are thrown out & flag is set reg [6:0] cmd; integer i; reg [6:0] buffer [1:BUFFER_SZ]; // data buffer reg mdio_cmd, cont_cmd; wire rx_cmd; reg mdio_cont_busy; wire[5:0] pcs_s; wire[3:0] link_s; reg cont_start, cont_busy, cont_done; assign pcs_s = { pll_lol, pcs_rx_error }; assign link_s = port_up; /* * main state machine for controller */ always @(posedge clk or negedge rstn) begin if ( !rstn ) cont_state <= S0; else case (cont_state) S0: cont_state <= S1; // S0 is default on reset S1: if ( rx_cmd && cont_cmd ) cont_state <= S2; else if ( rx_cmd ) cont_state <= S3; S2: cont_state <= S3; // respond to cont cmd S3: if ( !cont_busy ) cont_state <= S4; S4: if ( !mdio_cont_busy ) cont_state <= S1; default: cont_state <= cont_state; endcase end /* * Controller Tasks, controller always runs upon rx_cmd. * Other tasks will hold off until cont_done asserts */ always @(posedge clk or negedge rstn) begin if ( !rstn ) cont_start <= 1'b0; else if ( rx_cmd ) cont_start <= 1'b1; else cont_start <= 1'b0; end // cont_busy always @(posedge clk or negedge rstn) begin if ( !rstn ) cont_busy <= 1'b0; else if ( cont_start ) cont_busy <= 1'b1; else if ( cont_done ) cont_busy <= 1'b0; end // cont_done always @(posedge clk or negedge rstn) begin if ( !rstn ) cont_done <= 1'b0; else if ( cont_busy ) cont_done <= 1'b1; else cont_done <= 1'b0; end always @(cmd) begin if ( cmd == ASCIIp || cmd == ASCIIl ) cont_cmd <= 1'b1; else cont_cmd <= 1'b0; end /* * MDIO controller related */ always @(cmd) begin if ( cmd == ASCIIi || cmd == ASCIIs || cmd == ASCIId || cmd == ASCIIr || cmd == ASCIIw || cmd == ASCIIy || cmd == ASCIIz ) mdio_cmd <= 1'b1; else mdio_cmd <= 1'b0; end always @(posedge clk or negedge rstn) begin if ( !rstn ) mdio_cont_start <= 1'b0; else if ( cont_done && mdio_cmd ) mdio_cont_start <= 1'b1; else mdio_cont_start <= 1'b0; end /* * driver: mdio_route_addr */ always @(posedge clk or negedge rstn) begin if ( !rstn ) mdio_routine_addr <= 'd0; else if ( cont_done && mdio_cmd ) if ( cmd == ASCIIi ) mdio_routine_addr <= 'd0; else if ( cmd == ASCIIs ) mdio_routine_addr <= 'd20; else if ( cmd == ASCIId ) mdio_routine_addr <= 'd25; else if ( cmd == ASCIIr ) mdio_routine_addr <= 'd48; else if ( cmd == ASCIIw ) mdio_routine_addr <= 'd50; else if ( cmd == ASCIIy ) mdio_routine_addr <= 'd60; else if ( cmd == ASCIIz ) mdio_routine_addr <= 'd80; end // mdio_cont_busy always @(posedge clk or negedge rstn) begin if ( !rstn ) mdio_cont_busy <= 1'b0; else if ( mdio_cont_start ) mdio_cont_busy <= 1'b1; else if ( mdio_cont_done ) mdio_cont_busy <= 1'b0; end /* fifo_mux_sel = 1 when controller does NOT have FIFO bus */ assign fifo_mux_sel = mdio_cont_busy | mdio_run; /* set mdio page */ always @(posedge clk or negedge rstn) begin if ( !rstn ) mdio_page <= 'd0; else if ( rx_cmd && cmd == ASCIId ) mdio_page <= { buffer[1][0], buffer[2][3:0] }; end /* set mdio_reg_addr */ always @(posedge clk or negedge rstn) begin if ( !rstn ) mdio_reg_addr <= 'd0; else if ( rx_cmd && cmd == ASCIIr ) mdio_reg_addr <= { buffer[1][0], buffer[2][3:0] }; end /* set mdio_w_data_l and mdio_w_data_h */ always @(posedge clk or negedge rstn) begin if ( !rstn ) begin mdio_w_data_l <= 'd0; mdio_w_data_h <= 'd0; end else if ( rx_cmd && (cmd == ASCIIw || cmd == ASCIIy || cmd == ASCIIz ) ) begin mdio_w_data_h <= { buffer[1][3:0], buffer[2][3:0] }; mdio_w_data_l <= { buffer[3][3:0], buffer[4][3:0] }; end end // Channel Resets always @(posedge clk or negedge rstn) begin if ( !rstn ) begin c_reg[0] <= 'hff; c_reg[1] <= 'hff; c_reg[2] <= 'hff; c_reg[3] <= 'hff; end else if ( rx_cmd && cmd == ASCIIc ) begin c_reg[0] <= buffer[4][2:0]; c_reg[1] <= buffer[3][2:0]; c_reg[2] <= buffer[2][2:0]; c_reg[3] <= buffer[1][2:0]; end end assign rx_pcs_rst[0] = c_reg[0][0]; assign rx_serdes_rst[0] = c_reg[0][1]; assign tx_pcs_rst[0]= c_reg[0][2]; assign rx_pcs_rst[1] = c_reg[1][0]; assign rx_serdes_rst[1] = c_reg[1][1]; assign tx_pcs_rst[1]= c_reg[1][2]; assign rx_pcs_rst[2] = c_reg[2][0]; assign rx_serdes_rst[2] = c_reg[2][1]; assign tx_pcs_rst[2]= c_reg[2][2]; assign rx_pcs_rst[3] = c_reg[3][0]; assign rx_serdes_rst[3] = c_reg[3][1]; assign tx_pcs_rst[3]= c_reg[3][2]; /* DCU resets */ always @(posedge clk or negedge rstn) begin if ( !rstn ) begin u_reg[0] <= 'hff; u_reg[1] <= 'hff; end else if ( rx_cmd && cmd == ASCIIu ) begin u_reg[0] <= buffer[2][2:0]; u_reg[1] <= buffer[1][2:0]; end end // DCU0 Reset assignments assign pcs_rst_dual[0] = u_reg[0][2]; assign serdes_rst_dual[0] = u_reg[0][1]; assign tx_serdes_rst[0] = u_reg[0][0]; // DCU1 Reset assignments assign pcs_rst_dual[1] = u_reg[1][2]; assign serdes_rst_dual[1] = u_reg[1][1]; assign tx_serdes_rst[1] = u_reg[1][0]; /* X control */ always @(posedge clk or negedge rstn) begin if ( !rstn ) begin x_reg[0] <= 'hff; x_reg[1] <= 'hff; x_reg[2] <= 'hff; x_reg[3] <= 'hff; end else if ( rx_cmd && cmd == ASCIIx ) begin x_reg[0] <= buffer[4][1:0]; x_reg[1] <= buffer[3][1:0]; x_reg[2] <= buffer[2][1:0]; x_reg[3] <= buffer[1][1:0]; end end assign mac_reset[0] = x_reg[0][0]; assign phy_resetn[0] = ~x_reg[0][1]; assign mac_reset[1] = x_reg[1][0]; assign phy_resetn[1] = ~x_reg[1][1]; assign mac_reset[2] = x_reg[2][0]; assign phy_resetn[2] = ~x_reg[2][1]; assign mac_reset[3] = x_reg[3][0]; assign phy_resetn[3] = ~x_reg[3][1]; /* mdio_mux_sel */ always @(posedge clk or negedge rstn) begin if ( !rstn ) mdio_mux_sel <= 'h0; else if ( rx_cmd && cmd == ASCIIm ) mdio_mux_sel <= buffer[1][1:0]; end /* transmit custom packet */ always @(posedge clk or negedge rstn) begin if ( !rstn ) tx_custom <= 1'b0; else if ( rx_cmd && cmd == ASCIIt ) tx_custom <= 1'b1; else tx_custom <= 1'b0; end /* FIFO logic */ always @(posedge clk or negedge rstn) begin if ( !rstn ) fifo_we <= 1'b0; else if ( cont_state == S2 ) fifo_we <= 1'b1; else fifo_we <= 1'b0; end always @(posedge clk or negedge rstn) begin if ( !rstn ) read_fifo_d_o <= 0; else if ( cont_state == S2 && cmd == ASCIIp ) // pcs status read_fifo_d_o <= { 1'b0, pcs_s }; else if ( cont_state == S2 && cmd == ASCIIl ) // link status read_fifo_d_o <= { 3'b000, link_s }; end /* * capture the cmd and buffer * rx_cmd is a one shot at the end that triggers the state machine */ assign rx_cmd = i2c_rx_done; always @(posedge clk or negedge rstn) begin if ( !rstn ) cmd <= 7'h0; else if ( i2c_rx_we && cnt == 4'h0 ) cmd <= i2c_d_in; end always @(posedge clk or negedge rstn) begin if ( !rstn ) begin for (i=1; i<=BUFFER_SZ; i=i+1) begin buffer[i] <= 'h0; end end else if ( i2c_rx_we && cnt > 0 && cnt <= BUFFER_SZ ) buffer[cnt] <= i2c_d_in; end /* * counter for I2c rx buffer. */ always @(posedge clk or negedge rstn) begin if ( !rstn ) cnt <= 'h0; else if (i2c_rx_done) cnt <= 'h0; else if (i2c_rx_we ) cnt <= cnt + 1; end endmodule