// ***********************************************************************/
// Microsemi Corporation Proprietary and Confidential
// Copyright 2012 Microsemi Corporation.  All rights reserved.
//
// ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN
// ACCORDANCE WITH THE ACTEL LICENSE AGREEMENT AND MUST BE APPROVED
// IN ADVANCE IN WRITING.
//
// Description:	CoreResetP
//				Soft IP reset controller.
//              Sequences various reset signals to peripheral blocks in a
//              SmartFusion2 or IGLOO2 device.
//
//
// SVN Revision Information:
// SVN $Revision: 20739 $
// SVN $Date: 2013-07-16 14:00:02 -0700 (Tue, 16 Jul 2013) $
//
// Notes:
//
// ***********************************************************************/

module CoreResetP (
    // Clock from RC oscillator
    input           RCOSC_25_50MHZ,
    // Clock from fabric CCC
    input           CLK_BASE,
    // Power on reset signal from g4m_control
    input           POWER_ON_RESET_N,
    // Signals from/to reset pad (open drain, bidirectional pad,
    //  connects to external reset controller.)
    input           EXT_RESET_IN_N,
    output  reg     EXT_RESET_OUT,
    // Signals to/from MSSDDR
    output  reg     MSS_RESET_N_F2M,
    output  reg     M3_RESET_N,
    input           MSS_RESET_N_M2F,
    input           FIC_2_APB_M_PRESET_N,
    output  reg     MDDR_DDR_AXI_S_CORE_RESET_N,
    // MSS_FAB_RESET_N is essentially driven by either MSS_RESET_N_M2F (at
    // power on) or FIC_2_APB_M_PRESET_N (after power on, during warm
    // resets).
    output  reg     MSS_FAB_RESET_N,
    // Signals to/from CoreConfigP
    output  reg     INIT_DONE,
    input           CLR_INIT_DONE,
    input           CONFIG_DONE,
    // Reset input from fabric. This is ANDed with EXT_RESET_IN_N in this
    // core.
    input           USER_FAB_RESET_IN_N,
    // Reset to (AHB/AXI based) user logic in fabric
    output  reg     USER_FAB_RESET_N,
    // FDDR signals
    input           FPLL_LOCK,
    output  reg     FDDR_CORE_RESET_N,
    // SERDESIF_0 signals
    input           SDIF0_SPLL_LOCK,
    output  reg     SDIF0_PHY_RESET_N,
    output  reg     SDIF0_CORE_RESET_N,
    // SERDESIF_1 signals
    input           SDIF1_SPLL_LOCK,
    output  reg     SDIF1_PHY_RESET_N,
    output  reg     SDIF1_CORE_RESET_N,
    // SERDESIF_2 signals
    input           SDIF2_SPLL_LOCK,
    output  reg     SDIF2_PHY_RESET_N,
    output  reg     SDIF2_CORE_RESET_N,
    // SERDESIF_3 signals
    input           SDIF3_SPLL_LOCK,
    output  reg     SDIF3_PHY_RESET_N,
    output  reg     SDIF3_CORE_RESET_N
    );

    parameter FAMILY = 19;

    // EXT_RESET_CFG is used to determine what can cause the external reset
    // to be driven (by asserting EXT_RESET_OUT).
    //    0 = EXT_RESET_OUT is never asserted
    //    1 = EXT_RESET_OUT is asserted if power up reset
    //        (POWER_ON_RESET_N) is asserted
    //    2 = EXT_RESET_OUT is asserted if MSS_FAB_RESET_N is
    //        asserted
    //    3 = EXT_RESET_OUT is asserted if power up reset
    //        (POWER_ON_RESET_N) or MSS_FAB_RESET_N is asserted.
    parameter EXT_RESET_CFG = 3;

    // DEVICE_VOLTAGE is set to according to the supply voltage to the
    // device. The supply voltage determines the RC oscillator frequency.
    // This can be 25 or 50 MHz.
    //    1 = 1.0 V (RC osc freq = 25 MHz)
    //    2 = 1.2 V (RC osc freq = 50 MHz)
    parameter DEVICE_VOLTAGE = 2;

    // Use the following parameters to indicate whether or not a particular
    // peripheral block is being used (and connected to this core).
    parameter MDDR_IN_USE  = 1;
    parameter FDDR_IN_USE  = 1;
    parameter SDIF0_IN_USE = 1;
    parameter SDIF1_IN_USE = 1;
    parameter SDIF2_IN_USE = 1;
    parameter SDIF3_IN_USE = 1;

    // DDR_WAIT specifies the time in microseconds that must have elapsed
    // between release of the reset to the FDDR block (FDDR_CORE_RESET_N)
    // and release of the reset to user logic (USER_FAB_RESET_N) (and
    // assertion of INIT_DONE output).
    parameter DDR_WAIT = 200;

    localparam RCOSC_MEGAHERTZ = 25 * DEVICE_VOLTAGE;

    localparam SERDES_INTERVAL = 130      * RCOSC_MEGAHERTZ;
    localparam DDR_INTERVAL    = DDR_WAIT * RCOSC_MEGAHERTZ;


    function integer calc_count_width;
        input x;
        integer x;
        begin
            if      (x > 2147483647) calc_count_width =  32;
            else if (x > 1073741823) calc_count_width =  31;
            else if (x >  536870911) calc_count_width =  30;
            else if (x >  268435455) calc_count_width =  29;
            else if (x >  134217727) calc_count_width =  28;
            else if (x >   67108863) calc_count_width =  27;
            else if (x >   33554431) calc_count_width =  26;
            else if (x >   16777215) calc_count_width =  25;
            else if (x >    8388607) calc_count_width =  24;
            else if (x >    4194303) calc_count_width =  23;
            else if (x >    2097151) calc_count_width =  22;
            else if (x >    1048575) calc_count_width =  21;
            else if (x >     524287) calc_count_width =  20;
            else if (x >     262143) calc_count_width =  19;
            else if (x >     131071) calc_count_width =  18;
            else if (x >      65535) calc_count_width =  17;
            else if (x >      32767) calc_count_width =  16;
            else if (x >      16383) calc_count_width =  15;
            else if (x >       8191) calc_count_width =  14;
            else if (x >       4095) calc_count_width =  13;
            else if (x >       2047) calc_count_width =  12;
            else if (x >       1023) calc_count_width =  11;
            else if (x >        511) calc_count_width =  10;
            else if (x >        255) calc_count_width =   9;
            else if (x >        127) calc_count_width =   8;
            else if (x >         63) calc_count_width =   7;
            else if (x >         31) calc_count_width =   6;
            else if (x >         15) calc_count_width =   5;
            else if (x >          7) calc_count_width =   4;
            else if (x >          3) calc_count_width =   3;
            else                     calc_count_width =   2;
        end
    endfunction


    localparam COUNT_WIDTH_SERDES = calc_count_width(SERDES_INTERVAL);
    localparam COUNT_WIDTH_DDR    = calc_count_width(DDR_INTERVAL);

    // Parameters for state machine states
    localparam S0 = 0;
    localparam S1 = 1;
    localparam S2 = 2;
    localparam S3 = 3;
    localparam S4 = 4;
    localparam S5 = 5;
    localparam S6 = 6;

    // Signals
    reg     [2:0]   sm0_state;
    //reg     [2:0]   sm1_state;
    reg     [2:0]   sm2_state;
    reg     [2:0]   next_sm0_state;
    //reg     [2:0]   next_sm1_state;
    reg     [2:0]   next_sm2_state;
    reg             next_ext_reset_out;
    reg             next_user_fab_reset_n;
    reg             next_fddr_core_reset_n;
    reg             next_sdif0_phy_reset_n;
    reg             next_sdif0_core_reset_n;
    reg             next_sdif1_phy_reset_n;
    reg             next_sdif1_core_reset_n;
    reg             next_sdif2_phy_reset_n;
    reg             next_sdif2_core_reset_n;
    reg             next_sdif3_phy_reset_n;
    reg             next_sdif3_core_reset_n;
    reg             next_mddr_core_reset_n;
    reg             next_count_serdes_enable;
    reg             next_count_ddr_enable;
    reg             next_init_done_clk_base;
    reg             release_ext_reset;
    reg             next_release_ext_reset;

    reg             sm0_areset_n;
    reg             sm1_areset_n;
    reg             sm2_areset_n;

    reg             sm0_areset_n_q1;
    reg             sm0_areset_n_clk_base;
    reg             sm1_areset_n_q1;
    reg             sm1_areset_n_clk_base;
    reg             sm2_areset_n_q1;
    reg             sm2_areset_n_clk_base;

    reg             sm0_areset_n_rcosc_q1;
    reg             sm0_areset_n_rcosc;

    reg             fpll_lock_q1;
    reg             fpll_lock_q2;
    reg             sdif0_spll_lock_q1;
    reg             sdif0_spll_lock_q2;
    reg             sdif1_spll_lock_q1;
    reg             sdif1_spll_lock_q2;
    reg             sdif2_spll_lock_q1;
    reg             sdif2_spll_lock_q2;
    reg             sdif3_spll_lock_q1;
    reg             sdif3_spll_lock_q2;

    reg             release_serdes_core;
    reg             ddr_ready;
    reg             count_serdes_enable;
    reg             count_ddr_enable;

    reg             init_done_clk_base;

    reg [COUNT_WIDTH_SERDES-1:0] count_serdes;
    reg [COUNT_WIDTH_DDR   -1:0] count_ddr;

    reg             FPLL_LOCK_int;
    reg             SDIF0_SPLL_LOCK_int;
    reg             SDIF1_SPLL_LOCK_int;
    reg             SDIF2_SPLL_LOCK_int;
    reg             SDIF3_SPLL_LOCK_int;

    reg             CLR_INIT_DONE_q1;
    reg             CLR_INIT_DONE_clk_base;
    reg             CONFIG_DONE_q1;
    reg             CONFIG_DONE_clk_base;

    reg             POWER_ON_RESET_N_q1;
    reg             POWER_ON_RESET_N_clk_base;
    reg             MSS_RESET_N_M2F_q1;
    reg             MSS_RESET_N_M2F_clk_base;
    reg             FIC_2_APB_M_PRESET_N_q1;
    reg             FIC_2_APB_M_PRESET_N_clk_base;
    reg             mss_reset_state;
    reg             mss_reset_select;
    reg             MSS_FAB_RESET_N_int;

    reg             count_serdes_enable_q1;
    reg             count_serdes_enable_rcosc;
    reg             count_ddr_enable_q1;
    reg             count_ddr_enable_rcosc;
    reg             release_serdes_core_q1;
    reg             release_serdes_core_clk_base;
    reg             ddr_ready_q1;
    reg             ddr_ready_clk_base;


    // If a peripheral block is not in use, internally tie its PLL lock
    // signal high.
    always @(*)
    begin
        if (FDDR_IN_USE)
        begin
            FPLL_LOCK_int = FPLL_LOCK;
        end
        else
        begin
            FPLL_LOCK_int = 1'b1;
        end
    end
    always @(*)
    begin
        if (SDIF0_IN_USE)
        begin
            SDIF0_SPLL_LOCK_int = SDIF0_SPLL_LOCK;
        end
        else
        begin
            SDIF0_SPLL_LOCK_int = 1'b1;
        end
    end
    always @(*)
    begin
        if (SDIF1_IN_USE)
        begin
            SDIF1_SPLL_LOCK_int = SDIF1_SPLL_LOCK;
        end
        else
        begin
            SDIF1_SPLL_LOCK_int = 1'b1;
        end
    end
    always @(*)
    begin
        if (SDIF2_IN_USE)
        begin
            SDIF2_SPLL_LOCK_int = SDIF2_SPLL_LOCK;
        end
        else
        begin
            SDIF2_SPLL_LOCK_int = 1'b1;
        end
    end
    always @(*)
    begin
        if (SDIF3_IN_USE)
        begin
            SDIF3_SPLL_LOCK_int = SDIF3_SPLL_LOCK;
        end
        else
        begin
            SDIF3_SPLL_LOCK_int = 1'b1;
        end
    end

    //---------------------------------------------------------------------
    // The following code creates a reset signal named MSS_FAB_RESET_N.
    //
    // After a power on reset, MSS_FAB_RESET_N will not go high until
    // FIC_2_APB_M_PRESET_N has gone high, and MSS_RESET_N_M2F has been
    // seen to be high for at least one cycle of RCOSC_25_50MHZ at some
    // stage.
    //
    // For subsequent warm resets, MSS_FAB_RESET_N will essentially follow
    // FIC_2_APB_M_PRESET_N and MSS_RESET_N_M2F is not considered at all.
    //---------------------------------------------------------------------
    // Synchronize POWER_ON_RESET_N to CLK_BASE domain.
    always @(posedge CLK_BASE or negedge POWER_ON_RESET_N)
    begin
        if (!POWER_ON_RESET_N)
        begin
            POWER_ON_RESET_N_q1         <= 1'b0;
            POWER_ON_RESET_N_clk_base   <= 1'b0;
        end
        else
        begin
            POWER_ON_RESET_N_q1         <= 1'b1;
            POWER_ON_RESET_N_clk_base   <= POWER_ON_RESET_N_q1;
        end
    end

    // Synchronize MSS_RESET_N_M2F to CLK_BASE domain.
    always @(posedge CLK_BASE or negedge MSS_RESET_N_M2F)
    begin
        if (!MSS_RESET_N_M2F)
        begin
            MSS_RESET_N_M2F_q1          <= 1'b0;
            MSS_RESET_N_M2F_clk_base    <= 1'b0;
        end
        else
        begin
            MSS_RESET_N_M2F_q1          <= 1'b1;
            MSS_RESET_N_M2F_clk_base    <= MSS_RESET_N_M2F_q1;
        end
    end

    // Synchronize FIC_2_APB_M_PRESET_N to CLK_BASE domain.
    always @(posedge CLK_BASE or negedge FIC_2_APB_M_PRESET_N)
    begin
        if (!FIC_2_APB_M_PRESET_N)
        begin
            FIC_2_APB_M_PRESET_N_q1         <= 1'b0;
            FIC_2_APB_M_PRESET_N_clk_base   <= 1'b0;
        end
        else
        begin
            FIC_2_APB_M_PRESET_N_q1         <= 1'b1;
            FIC_2_APB_M_PRESET_N_clk_base   <= FIC_2_APB_M_PRESET_N_q1;
        end
    end

    // At power on, state = '0' and select = '0'.
    // state will advance to '1' (and remain at '1') when MSS_RESET_N_M2F
    // is seen to be high.
    // When state = '1', select will move to '1' (and remain at '1') when
    // FIC_2_APB_M_PRESET_N is seen to be high.
    always @(posedge CLK_BASE or negedge POWER_ON_RESET_N_clk_base)
    begin
        if (!POWER_ON_RESET_N_clk_base)
        begin
            mss_reset_state  <= 1'b0;
            mss_reset_select <= 1'b0;
        end
        else
        begin
            if (MSS_RESET_N_M2F_clk_base)
            begin
                mss_reset_state  <= 1'b1;
            end
            if (FIC_2_APB_M_PRESET_N_clk_base && mss_reset_state == 1'b1)
            begin
                mss_reset_select <= 1'b1;
            end
        end
    end

    always @(posedge CLK_BASE or negedge POWER_ON_RESET_N_clk_base)
    begin
        if (!POWER_ON_RESET_N_clk_base)
        begin
            MSS_FAB_RESET_N_int <= 1'b0;
        end
        else
        begin
            if (mss_reset_select == 1'b0)
            begin
                MSS_FAB_RESET_N_int <= FIC_2_APB_M_PRESET_N_clk_base && MSS_RESET_N_M2F_clk_base;
            end
            else
            begin
                MSS_FAB_RESET_N_int <= FIC_2_APB_M_PRESET_N_clk_base;
            end
        end
    end

    always @(*)
    begin
        MSS_FAB_RESET_N = MSS_FAB_RESET_N_int;
    end

    //---------------------------------------------------------------------
    // Create a number of asynchronous resets.
    // Some source reset signals may be "combined" to create a single
    // asynchronous reset.
    //---------------------------------------------------------------------
    always @(*)
    begin
        sm0_areset_n = EXT_RESET_IN_N && USER_FAB_RESET_IN_N
                       && POWER_ON_RESET_N && MSS_FAB_RESET_N_int;
    end

    // Assertion of resets to MSS (MSS_RESET_N_F2M and M3_RESET_N) caused
    // by assertion of external reset input or assertion of reset signal
    // from fabric.
    always @(*)
    begin
        sm1_areset_n = EXT_RESET_IN_N && USER_FAB_RESET_IN_N;
    end

    // There are a number of options for what can cause the external reset
    // to be asserted.
generate
  if (EXT_RESET_CFG == 0)
  begin
    always @(*)
    begin
        sm2_areset_n = POWER_ON_RESET_N;
    end
  end
  else if (EXT_RESET_CFG == 1)
  begin
    always @(*)
    begin
        sm2_areset_n = POWER_ON_RESET_N;
    end
  end
  else if (EXT_RESET_CFG == 2)
  begin
    always @(*)
    begin
        sm2_areset_n = MSS_FAB_RESET_N_int;
    end
  end
  else
  begin
    always @(*)
    begin
        sm2_areset_n = POWER_ON_RESET_N && MSS_FAB_RESET_N_int;
    end
  end
endgenerate

    //---------------------------------------------------------------------
    // Create versions of asynchronous resets that are released
    // synchronous to CLK_BASE.
    //---------------------------------------------------------------------
    always @(posedge CLK_BASE or negedge sm0_areset_n)
    begin
        if (!sm0_areset_n)
        begin
            sm0_areset_n_q1         <= 1'b0;
            sm0_areset_n_clk_base   <= 1'b0;
        end
        else
        begin
            sm0_areset_n_q1         <= 1'b1;
            sm0_areset_n_clk_base   <= sm0_areset_n_q1;
        end
    end

    always @(posedge CLK_BASE or negedge sm1_areset_n)
    begin
        if (!sm1_areset_n)
        begin
            sm1_areset_n_q1         <= 1'b0;
            sm1_areset_n_clk_base   <= 1'b0;
        end
        else
        begin
            sm1_areset_n_q1         <= 1'b1;
            sm1_areset_n_clk_base   <= sm1_areset_n_q1;
        end
    end

    always @(posedge CLK_BASE or negedge sm2_areset_n)
    begin
        if (!sm2_areset_n)
        begin
            sm2_areset_n_q1         <= 1'b0;
            sm2_areset_n_clk_base   <= 1'b0;
        end
        else
        begin
            sm2_areset_n_q1         <= 1'b1;
            sm2_areset_n_clk_base   <= sm2_areset_n_q1;
        end
    end

    //---------------------------------------------------------------------
    // Synchronize sm0_areset_n to RCOSC_25_50MHZ domain.
    //---------------------------------------------------------------------
    always @(posedge RCOSC_25_50MHZ or negedge sm0_areset_n)
    begin
        if (!sm0_areset_n)
        begin
            sm0_areset_n_rcosc_q1   <= 1'b0;
            sm0_areset_n_rcosc      <= 1'b0;
        end
        else
        begin
            sm0_areset_n_rcosc_q1   <= 1'b1;
            sm0_areset_n_rcosc      <= sm0_areset_n_rcosc_q1;
        end
    end

    //---------------------------------------------------------------------
    // Synchronize CLR_INIT_DONE input to CLK_BASE domain.
    //---------------------------------------------------------------------
    always @(posedge CLK_BASE or negedge sm0_areset_n_clk_base)
    begin
        if (!sm0_areset_n_clk_base)
        begin
            CLR_INIT_DONE_q1        <= 1'b0;
            CLR_INIT_DONE_clk_base  <= 1'b0;
        end
        else
        begin
            CLR_INIT_DONE_q1        <= CLR_INIT_DONE;
            CLR_INIT_DONE_clk_base  <= CLR_INIT_DONE_q1;
        end
    end

    //---------------------------------------------------------------------
    // Synchronize CONFIG_DONE input to CLK_BASE domain.
    //---------------------------------------------------------------------
    always @(posedge CLK_BASE or negedge sm0_areset_n_clk_base)
    begin
        if (!sm0_areset_n_clk_base)
        begin
            CONFIG_DONE_q1          <= 1'b0;
            CONFIG_DONE_clk_base    <= 1'b0;
        end
        else
        begin
            CONFIG_DONE_q1          <= CONFIG_DONE;
            CONFIG_DONE_clk_base    <= CONFIG_DONE_q1;
        end
    end

    //---------------------------------------------------------------------
    // Synchronize PLL lock signals to CLK_BASE domain.
    //---------------------------------------------------------------------
    always @(posedge CLK_BASE or negedge sm0_areset_n_clk_base)
    begin
        if (!sm0_areset_n_clk_base)
        begin
            fpll_lock_q1       <= 1'b0;
            fpll_lock_q2       <= 1'b0;
            sdif0_spll_lock_q1 <= 1'b0;
            sdif0_spll_lock_q2 <= 1'b0;
            sdif1_spll_lock_q1 <= 1'b0;
            sdif1_spll_lock_q2 <= 1'b0;
            sdif2_spll_lock_q1 <= 1'b0;
            sdif2_spll_lock_q2 <= 1'b0;
            sdif3_spll_lock_q1 <= 1'b0;
            sdif3_spll_lock_q2 <= 1'b0;
        end
        else
        begin
            fpll_lock_q1       <= FPLL_LOCK_int;
            fpll_lock_q2       <= fpll_lock_q1;
            sdif0_spll_lock_q1 <= SDIF0_SPLL_LOCK_int;
            sdif0_spll_lock_q2 <= sdif0_spll_lock_q1;
            sdif1_spll_lock_q1 <= SDIF1_SPLL_LOCK_int;
            sdif1_spll_lock_q2 <= sdif1_spll_lock_q1;
            sdif2_spll_lock_q1 <= SDIF2_SPLL_LOCK_int;
            sdif2_spll_lock_q2 <= sdif2_spll_lock_q1;
            sdif3_spll_lock_q1 <= SDIF3_SPLL_LOCK_int;
            sdif3_spll_lock_q2 <= sdif3_spll_lock_q1;
        end
    end

    //---------------------------------------------------------------------
    // State machine 0
    // Controls all output signals except MSS_RESET_N_F2M, M3_RESET_N and
    // EXT_RESET_OUT.
    //---------------------------------------------------------------------
    // State machine 0 - combinational part
    always @(*)
    begin
        next_sm0_state = sm0_state;
        next_user_fab_reset_n = USER_FAB_RESET_N;
        next_fddr_core_reset_n = FDDR_CORE_RESET_N;
        next_sdif0_phy_reset_n = SDIF0_PHY_RESET_N;
        next_sdif0_core_reset_n = SDIF0_CORE_RESET_N;
        next_sdif1_phy_reset_n = SDIF1_PHY_RESET_N;
        next_sdif1_core_reset_n = SDIF1_CORE_RESET_N;
        next_sdif2_phy_reset_n = SDIF2_PHY_RESET_N;
        next_sdif2_core_reset_n = SDIF2_CORE_RESET_N;
        next_sdif3_phy_reset_n = SDIF3_PHY_RESET_N;
        next_sdif3_core_reset_n = SDIF3_CORE_RESET_N;
        next_mddr_core_reset_n = MDDR_DDR_AXI_S_CORE_RESET_N;
        next_count_serdes_enable = count_serdes_enable;
        next_count_ddr_enable = count_ddr_enable;
        next_init_done_clk_base = init_done_clk_base;
        next_release_ext_reset = release_ext_reset;
        case (sm0_state)
            S0:
            begin
                next_sm0_state = S1;
            end
            S1:
            begin
                next_sm0_state = S2;
                // Release resets to FDDR and MDDR blocks and start
                // DDR counter
                next_fddr_core_reset_n = 1'b1;
                next_mddr_core_reset_n = 1'b1;
                next_count_ddr_enable  = 1'b1;
            end
            S2:
            begin
                // Wait for CONFIG_DONE and PLL lock signals
                if (CONFIG_DONE_clk_base && fpll_lock_q2 && sdif0_spll_lock_q2 && sdif1_spll_lock_q2
                    && sdif2_spll_lock_q2 && sdif3_spll_lock_q2)
                begin
                    next_sm0_state = S3;
                    // Release serdes phy resets and start SERDES counter
                    next_sdif0_phy_reset_n   = 1'b1;
                    next_sdif1_phy_reset_n   = 1'b1;
                    next_sdif2_phy_reset_n   = 1'b1;
                    next_sdif3_phy_reset_n   = 1'b1;
                    next_count_serdes_enable = 1'b1;
                end
            end
            S3:
            begin
                if (ddr_ready_clk_base && release_serdes_core_clk_base)
                begin
                    next_sm0_state = S3;
                    next_sdif0_core_reset_n  = 1'b1;
                    next_sdif1_core_reset_n  = 1'b1;
                    next_sdif2_core_reset_n  = 1'b1;
                    next_sdif3_core_reset_n  = 1'b1;

                    next_user_fab_reset_n    = 1'b1;
                    next_init_done_clk_base  = 1'b1;
                    next_count_ddr_enable    = 1'b0;
                    next_count_serdes_enable = 1'b0;
                    next_release_ext_reset   = 1'b1;
                end
            end
            default:
            begin
                next_sm0_state = S0;
            end
        endcase
    end

    // State machine 0 - sequential part
    always @(posedge CLK_BASE or negedge sm0_areset_n_clk_base)
    begin
        if (!sm0_areset_n_clk_base)
        begin
            sm0_state                   <= S0;
            USER_FAB_RESET_N            <= 1'b0;
            FDDR_CORE_RESET_N           <= 1'b0;
            SDIF0_PHY_RESET_N           <= 1'b0;
            SDIF0_CORE_RESET_N          <= 1'b0;
            SDIF1_PHY_RESET_N           <= 1'b0;
            SDIF1_CORE_RESET_N          <= 1'b0;
            SDIF2_PHY_RESET_N           <= 1'b0;
            SDIF2_CORE_RESET_N          <= 1'b0;
            SDIF3_PHY_RESET_N           <= 1'b0;
            SDIF3_CORE_RESET_N          <= 1'b0;
            MDDR_DDR_AXI_S_CORE_RESET_N <= 1'b0;
            count_ddr_enable            <= 1'b0;
            count_serdes_enable         <= 1'b0;
            release_ext_reset           <= 1'b0;
        end
        else
        begin
            sm0_state                   <= next_sm0_state;
            USER_FAB_RESET_N            <= next_user_fab_reset_n;
            FDDR_CORE_RESET_N           <= next_fddr_core_reset_n;
            SDIF0_PHY_RESET_N           <= next_sdif0_phy_reset_n;
            SDIF0_CORE_RESET_N          <= next_sdif0_core_reset_n;
            SDIF1_PHY_RESET_N           <= next_sdif1_phy_reset_n;
            SDIF1_CORE_RESET_N          <= next_sdif1_core_reset_n;
            SDIF2_PHY_RESET_N           <= next_sdif2_phy_reset_n;
            SDIF2_CORE_RESET_N          <= next_sdif2_core_reset_n;
            SDIF3_PHY_RESET_N           <= next_sdif3_phy_reset_n;
            SDIF3_CORE_RESET_N          <= next_sdif3_core_reset_n;
            MDDR_DDR_AXI_S_CORE_RESET_N <= next_mddr_core_reset_n;
            count_ddr_enable            <= next_count_ddr_enable;
            count_serdes_enable         <= next_count_serdes_enable;
            release_ext_reset           <= next_release_ext_reset;
        end
    end

    always @(posedge CLK_BASE or negedge sm0_areset_n_clk_base)
    begin
        if (!sm0_areset_n_clk_base)
        begin
            init_done_clk_base <= 1'b0;
        end
        else
        begin
            if (next_init_done_clk_base)
            begin
                init_done_clk_base <= 1'b1;
            end
            if (CLR_INIT_DONE_clk_base)
            begin
                init_done_clk_base <= 1'b0;
            end
        end
    end
    // End of state machine 0.
    //---------------------------------------------------------------------

    //---------------------------------------------------------------------
    // Control of MSS_RESET_N_F2M and M3_RESET_N signals.
    //---------------------------------------------------------------------
    always @(posedge CLK_BASE or negedge sm1_areset_n_clk_base)
    begin
        if (!sm1_areset_n_clk_base)
        begin
            MSS_RESET_N_F2M     <= 1'b0;
            M3_RESET_N          <= 1'b0;
        end
        else
        begin
            MSS_RESET_N_F2M     <= 1'b1;
            M3_RESET_N          <= 1'b1;
        end
    end
    //---------------------------------------------------------------------

    //---------------------------------------------------------------------
    // State machine 2
    // Controls EXT_RESET_OUT signal.
    //---------------------------------------------------------------------
    // State machine 2 - combinational part
    always @(*)
    begin
        next_sm2_state = sm2_state;
        next_ext_reset_out = EXT_RESET_OUT;
        case (sm2_state)
            S0:
            begin
                next_sm2_state = S1;
            end
            S1:
            begin
                // release_ext_reset is controlled by state machine 0.
                if (release_ext_reset)
                begin
                    next_ext_reset_out = 1'b0;
                end
            end
            default:
            begin
                next_sm2_state = S0;
            end
        endcase
    end

    // State machine 2 - sequential part
    always @(posedge CLK_BASE or negedge sm2_areset_n_clk_base)
    begin
        if (!sm2_areset_n_clk_base)
        begin
            sm2_state       <= S0;
            EXT_RESET_OUT   <= (EXT_RESET_CFG > 0) ? 1'b1 : 1'b0;
        end
        else
        begin
            sm2_state       <= next_sm2_state;
            EXT_RESET_OUT   <= next_ext_reset_out;
        end
    end
    // End of state machine 2.
    //---------------------------------------------------------------------

    //---------------------------------------------------------------------
    // Counters clocked by RCOSC_25_50MHZ used to time SERDES and DDR
    // intervals.
    //

    // Synchronize enable signals to RCOSC_25_50MHZ domain
    always @(posedge RCOSC_25_50MHZ or negedge sm0_areset_n_rcosc)
    begin
        if (!sm0_areset_n_rcosc)
        begin
            count_serdes_enable_q1      <= 1'b0;
            count_serdes_enable_rcosc   <= 1'b0;
            count_ddr_enable_q1         <= 1'b0;
            count_ddr_enable_rcosc      <= 1'b0;
        end
        else
        begin
            count_serdes_enable_q1      <= count_serdes_enable;
            count_serdes_enable_rcosc   <= count_serdes_enable_q1;
            count_ddr_enable_q1         <= count_ddr_enable;
            count_ddr_enable_rcosc      <= count_ddr_enable_q1;
        end
    end

    always @(posedge RCOSC_25_50MHZ or negedge sm0_areset_n_rcosc)
    begin
        if (!sm0_areset_n_rcosc)
        begin
            count_serdes <= 'd0;
            release_serdes_core <= 1'b0;
        end
        else
        begin
            // If none of the SERDES interfaces are being used, then set
            // release_serdes_core high. This ensures that there is no
            // unnecessary waiting for SERDES.
            if (SDIF0_IN_USE == 0 && SDIF1_IN_USE == 0
                && SDIF2_IN_USE == 0 && SDIF3_IN_USE == 0)
            begin
                release_serdes_core <= 1'b1;
            end
            else
            begin
                if (count_serdes_enable_rcosc)
                begin
                    count_serdes <= count_serdes + 'd1;
                end
                // Detect when enough time has elapsed since release of SERDES
                // PHY resets to allow release of SERDES core resets.
                if (count_serdes == SERDES_INTERVAL)
                begin
                    release_serdes_core <= 1'b1;
                end
            end
        end
    end

    always @(posedge RCOSC_25_50MHZ or negedge sm0_areset_n_rcosc)
    begin
        if (!sm0_areset_n_rcosc)
        begin
            count_ddr <= 'd0;
            ddr_ready <= 1'b0;
        end
        else
        begin
            // If DDR is not being used, then set ddr_ready high. This
            // ensures that there is no unnecessary waiting for DDR
            // readiness.
            if (MDDR_IN_USE == 0 && FDDR_IN_USE == 0)
            begin
                ddr_ready <= 1'b1;
            end
            else
            begin
                if (count_ddr_enable_rcosc)
                begin
                    count_ddr <= count_ddr + 'd1;
                end
                // Detect when enough time has elapsed since release of DDR
                // core reset to allow DDR memory to be ready for use.
                if (count_ddr == DDR_INTERVAL)
                begin
                    ddr_ready <= 1'b1;
                end
            end
        end
    end

    // Synchronize "ready" signals to CLK_BASE domain
    always @(posedge CLK_BASE or negedge sm0_areset_n_clk_base)
    begin
        if (!sm0_areset_n_clk_base)
        begin
            release_serdes_core_q1          <= 1'b0;
            release_serdes_core_clk_base    <= 1'b0;
            ddr_ready_q1                    <= 1'b0;
            ddr_ready_clk_base              <= 1'b0;
        end
        else
        begin
            release_serdes_core_q1          <= release_serdes_core;
            release_serdes_core_clk_base    <= release_serdes_core_q1;
            ddr_ready_q1                    <= ddr_ready;
            ddr_ready_clk_base              <= ddr_ready_q1;
        end
    end


    always @(*)
    begin
        INIT_DONE = init_done_clk_base;
    end

endmodule
