module altera_reset_controller
#(
    parameter NUM_RESET_INPUTS        = 6,
    parameter OUTPUT_RESET_SYNC_EDGES = "deassert",
    parameter SYNC_DEPTH              = 2,
    parameter RESET_REQUEST_PRESENT   = 0
)
(
    // --------------------------------------
    // We support up to 16 reset inputs, for now
    // --------------------------------------
    input reset_in0,
    input reset_in1,
    input reset_in2,
    input reset_in3,
    input reset_in4,
    input reset_in5,
    input reset_in6,
    input reset_in7,
    input reset_in8,
    input reset_in9,
    input reset_in10,
    input reset_in11,
    input reset_in12,
    input reset_in13,
    input reset_in14,
    input reset_in15,

    input  clk,
    output reg reset_out,
    output reg reset_req
);

   localparam ASYNC_RESET = (OUTPUT_RESET_SYNC_EDGES == "deassert");

   localparam DEPTH = 2;
   localparam CLKEN_LAGS_RESET = 0;
   localparam EARLY_RST_TAP = (CLKEN_LAGS_RESET != 0) ? 0 : 1;

   wire merged_reset;
   wire reset_out_pre;

   // Registers and Interconnect
   (*preserve*) reg  [SYNC_DEPTH: 0]   altera_reset_synchronizer_int_chain;
   reg   [(SYNC_DEPTH-1): 0]           r_sync_rst_chain;
   reg                                 r_sync_rst_dly;
   reg                                 r_sync_rst;
   reg                                 r_early_rst;
   
    // --------------------------------------
    // "Or" all the input resets together
    // --------------------------------------
    assign merged_reset = (  
                              reset_in0 | 
                              reset_in1 | 
                              reset_in2 | 
                              reset_in3 | 
                              reset_in4 | 
                              reset_in5 | 
                              reset_in6 | 
                              reset_in7 | 
                              reset_in8 | 
                              reset_in9 | 
                              reset_in10 | 
                              reset_in11 | 
                              reset_in12 | 
                              reset_in13 | 
                              reset_in14 | 
                              reset_in15
                          );

    // --------------------------------------
    // And if required, synchronize it to the required clock domain,
    // with the correct synchronization type
    // --------------------------------------
    generate if (OUTPUT_RESET_SYNC_EDGES == "none") begin

        assign reset_out_pre = merged_reset;

    end else begin

        altera_reset_synchronizer
        #(
            .DEPTH      (SYNC_DEPTH),
            .ASYNC_RESET(ASYNC_RESET)
        )
        alt_rst_sync_uq1
        (
            .clk        (clk),
            .reset_in   (merged_reset),
            .reset_out  (reset_out_pre)
        );

    end
    endgenerate

    generate if (RESET_REQUEST_PRESENT == 0) begin
        always @* begin
            reset_out = reset_out_pre;
            reset_req = 1'b0;
        end
    end
    else begin

    // 3-FF Metastability Synchronizer
    initial
    begin
        altera_reset_synchronizer_int_chain <= 3'b111;
    end

    always @(posedge clk)
    begin
        altera_reset_synchronizer_int_chain[2:0] <= {altera_reset_synchronizer_int_chain[1:0], reset_out_pre}; 
    end


    // Synchronous reset pipe
    initial
    begin
        r_sync_rst_chain <= {DEPTH{1'b1}};
    end

    always @(posedge clk)
    begin
        if (altera_reset_synchronizer_int_chain[2] == 1'b1)
        begin
            r_sync_rst_chain <= {DEPTH{1'b1}};
    end
    else
    begin
        r_sync_rst_chain <= {1'b0, r_sync_rst_chain[DEPTH-1:1]};
    end
    end

    // Standard synchronous reset output.  From 0-1, the transition lags the early output.  For 1->0, the transition
    // matches the early input.
    initial
    begin
        r_sync_rst_dly <= 1'b1;
        r_sync_rst     <= 1'b1;
        r_early_rst    <= 1'b1;
    end

    always @(posedge clk)
    begin
        // Delayed reset pipeline register
        r_sync_rst_dly <= r_sync_rst_chain[DEPTH-1];

        case ({r_sync_rst_dly, r_sync_rst_chain[1], r_sync_rst})
            3'b000:   r_sync_rst <= 1'b0; // Not reset
            3'b001:   r_sync_rst <= 1'b0;
            3'b010:   r_sync_rst <= 1'b0;
            3'b011:   r_sync_rst <= 1'b1;
            3'b100:   r_sync_rst <= 1'b1; 
            3'b101:   r_sync_rst <= 1'b1;
            3'b110:   r_sync_rst <= 1'b1;
            3'b111:   r_sync_rst <= 1'b1; // In Reset
            default:  r_sync_rst <= 1'b1;
        endcase

        case ({r_sync_rst_chain[DEPTH-1], r_sync_rst_chain[EARLY_RST_TAP]})
            2'b00:   r_early_rst <= 1'b0; // Not reset
            2'b01:   r_early_rst <= 1'b1; // Coming out of reset
            2'b10:   r_early_rst <= 1'b0; // Spurious reset - should not be possible via synchronous design.
            2'b11:   r_early_rst <= 1'b1; // Held in reset
            default: r_early_rst <= 1'b1;
        endcase
    end

    always @* begin
        reset_out = r_sync_rst;
        reset_req = r_early_rst;
    end

    end
    endgenerate

endmodule