summaryrefslogtreecommitdiffhomepage
path: root/source/controller.v
diff options
context:
space:
mode:
Diffstat (limited to 'source/controller.v')
-rw-r--r--source/controller.v474
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