// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// 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.
// SPDX-License-Identifier: Apache-2.0

`default_nettype none
// This routine synchronizes the 

module caravel_clocking(
`ifdef USE_POWER_PINS
    input VPWR,
    input VGND,
`endif
    input porb,		// Master (negative sense) reset from power-on-reset
    input resetb, 	// Master (negative sense) reset
    input ext_clk_sel,	// 0=use PLL clock, 1=use external (pad) clock
    input ext_clk,	// External pad (slow) clock
    input pll_clk,	// Internal PLL (fast) clock
    input pll_clk90,	// Internal PLL (fast) clock, 90 degree phase
    input [2:0] sel,	// Select clock divider value (0=thru, 1=divide-by-2, etc.)
    input [2:0] sel2,	// Select clock divider value for 90 degree phase divided clock
    input ext_reset,	// Positive sense reset from housekeeping SPI.
    output core_clk,	// Output core clock
    output user_clk,	// Output user (secondary) clock
    output resetb_sync	// Output propagated and buffered reset
);

    wire pll_clk_sel;
    wire pll_clk_divided;
    wire pll_clk90_divided;
    wire core_ext_clk;
    reg  use_pll_first;
    reg  use_pll_second;
    reg	 ext_clk_syncd_pre;
    reg	 ext_clk_syncd;

    wire resetb_async;

    assign pll_clk_sel = ~ext_clk_sel;

    assign resetb_async = porb & resetb & (!ext_reset);
    // Note that this implementation does not guard against switching to
    // the PLL clock if the PLL clock is not present.

    always @(posedge pll_clk or negedge resetb_async) begin
	if (resetb_async == 1'b0) begin
	    use_pll_first <= 1'b0;
	    use_pll_second <= 1'b0;
	    ext_clk_syncd <= 1'b0;
	end else begin
	    use_pll_first <= pll_clk_sel;
	    use_pll_second <= use_pll_first;
	    ext_clk_syncd_pre <= ext_clk;	// Sync ext_clk to pll_clk
	    ext_clk_syncd <= ext_clk_syncd_pre;	// Do this twice (resolve metastability)
	end
    end

    // Apply PLL clock divider

    clock_div #(
	.SIZE(3)
    ) divider (
	.in(pll_clk),
	.out(pll_clk_divided),
	.N(sel),
	.resetb(resetb_async)
    ); 

    // Secondary PLL clock divider for user space access

    clock_div #(
	.SIZE(3)
    ) divider2 (
	.in(pll_clk90),
	.out(pll_clk90_divided),
	.N(sel2),
	.resetb(resetb_async)
    ); 


    // Multiplex the clock output

    assign core_ext_clk = (use_pll_first) ? ext_clk_syncd : ext_clk;
    assign core_clk = (use_pll_second) ? pll_clk_divided : core_ext_clk;
    assign user_clk = (use_pll_second) ? pll_clk90_divided : core_ext_clk;

    // Reset assignment.  "reset" comes from POR, while "ext_reset"
    // comes from standalone SPI (and is normally zero unless
    // activated from the SPI).

    // Staged-delay reset
    reg [2:0] reset_delay;

    always @(negedge core_clk or negedge resetb_async) begin
        if (resetb_async == 1'b0) begin
        reset_delay <= 3'b111;
        end else begin
        reset_delay <= {1'b0, reset_delay[2:1]};
        end
    end

    assign resetb_sync = ~reset_delay[0];

endmodule
`default_nettype wire