diff options
Diffstat (limited to 'source/controller.v')
-rw-r--r-- | source/controller.v | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/source/controller.v b/source/controller.v new file mode 100644 index 0000000..6ede860 --- /dev/null +++ b/source/controller.v @@ -0,0 +1,474 @@ +/* + * controller.v + * + * Copyright (C) 2018, 2109 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 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, + output i2c_fifo_priority, + + // TX custom packet + output reg tx_metrics + +); + + // 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 i2c_fifo_priority = 1'b1; + 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 and mux self + 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 test packet */ + always @(posedge clk or negedge rstn) + begin + if ( !rstn ) + tx_metrics <= 1'b0; + else if ( rx_cmd && cmd == ASCIIt ) + tx_metrics <= 1'b1; + else + tx_metrics <= 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 <= { 2'b00, 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 |