/*
 * sysmgr.v
 *
 * vim: ts=4 sw=4
 *
 * System Clock / Reset generation
 *
 * Copyright (C) 2023  Sylvain Munaut <tnt@246tNt.com>
 * SPDX-License-Identifier: CERN-OHL-P-2.0
 */

`default_nettype none

module sysmgr (
	// Control
	input  wire sys_start,
	input  wire sys_stop,
	input  wire usb_ena,

	// LED clock ( 6 MHz )
	output wire clk_led,
	output wire rst_led,

	// System clock ( 24 MHz )
	output wire clk_sys,
	output wire rst_sys,

	// USB ( 48 MHz )
	output wire clk_usb,
	output wire rst_usb
);

	// Signals
	// -------

	wire      clk_osc;
	wire      clk_base;

	wire      pll_lock;

	reg [2:0] clk_div;
	reg       clk_sys_i;
	wire      clk_led_i;

	reg [3:0] rst_cnt;
	wire      rst_i;
	wire      rst_gbuf;

	reg [2:0] sys_start_s;
	reg [2:0] sys_stop_s;

	wire      usb_rst_trig;
	reg [3:0] usb_rst_cnt;
	wire      usb_rst_i;
	reg [3:0] usb_off_dly;

	wire      sys_off;
	wire      usb_off;


	// SB_HFOSC
	// -------
	// Generates 12 MHz

	(* ROUTE_THROUGH_FABRIC = 1 *)
	SB_HFOSC #(
		.CLKHF_DIV("0b10")
	) osc_I (
		.CLKHFPU(1'b1),
		.CLKHFEN(1'b1),
		.CLKHF(clk_osc)
	);


	// PLL
	// ---

	SB_PLL40_2F_CORE #(
		.DIVR                           (4'b0000),
		.DIVF                           (7'b0111111),
		.DIVQ                           (3'b100),
		.FILTER_RANGE                   (3'b001),
		.FEEDBACK_PATH                  ("SIMPLE"),
		.DELAY_ADJUSTMENT_MODE_FEEDBACK ("FIXED"),
		.FDA_FEEDBACK                   (4'b0000),
		.SHIFTREG_DIV_MODE              (2'b00),
		.PLLOUT_SELECT_PORTA            ("GENCLK"),
		.PLLOUT_SELECT_PORTB            ("GENCLK"),
		.ENABLE_ICEGATE_PORTA           (1'b1),
		.ENABLE_ICEGATE_PORTB           (1'b0),
    ) pll_I (
		.REFERENCECLK    (clk_osc),
		.PLLOUTCOREB     (),
		.PLLOUTGLOBALB   (clk_usb),
		.PLLOUTCOREA     (clk_base),
		.PLLOUTGLOBALA   (),
		.EXTFEEDBACK     (1'b0),
		.DYNAMICDELAY    (8'h00),
		.RESETB          (1'b1),
		.BYPASS          (1'b0),
		.LATCHINPUTVALUE (usb_off),
		.LOCK            (pll_lock),
		.SDI             (1'b0),
		.SDO             (),
		.SCLK            (1'b0)
    );


	// Dividers
	// --------

	// Counter
	always @(posedge clk_base)
		clk_div <= clk_div + 1;

	// SYS is div-by-2 + gated
	always @(posedge clk_base)
		clk_sys_i <= clk_div[0] & ~sys_off;

	// LED is div-by-8
	assign clk_led_i = clk_div[2];

	// Global buffers
	SB_GB clk_sys_gbuf_I (
		.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_sys_i),
		.GLOBAL_BUFFER_OUTPUT(clk_sys)
	);

	SB_GB clk_led_gbuf_I (
		.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_led_i),
		.GLOBAL_BUFFER_OUTPUT(clk_led)
	);


	// USB Enable
	// ----------

	// Delay falling latch signal
	always @(posedge clk_base)
		usb_off_dly <= usb_ena ? 4'b0000 : { usb_off_dly[2:0], 1'b1 };

	assign usb_off = usb_off_dly[3];


	// USB Reset
	// ---------

	// Reset trigger
	assign usb_rst_trig = ~usb_ena | ~pll_lock;

	// Reset counter
	always @(posedge clk_usb or posedge usb_rst_trig)
		if (usb_rst_trig)
			usb_rst_cnt <= 4'h8;
		else
			usb_rst_cnt <= usb_rst_cnt + usb_rst_i;

	assign usb_rst_i = usb_rst_cnt[3];

	// Global buffer
	SB_GB rst_usb_gbuf_I (
		.USER_SIGNAL_TO_GLOBAL_BUFFER(usb_rst_i),
		.GLOBAL_BUFFER_OUTPUT(rst_usb)
	);


	// SYS Enable
	// ----------

	// Synch triggers
	always @(posedge clk_base)
	begin
		sys_start_s <= {  sys_start_s[1],                  sys_start_s[0], sys_start };
		sys_stop_s  <= { ~sys_stop_s[1] &  sys_stop_s[0],  sys_stop_s[0],  sys_stop  };
	end

	always @(posedge clk_base)
		sys_off <= (sys_off & ~rst_i & ~sys_start_s[2]) | sys_stop_s[2];


	// Reset LED / SYS
	// ---------------

	// Counter
	always @(posedge clk_led or negedge pll_lock)
		if (~pll_lock)
			rst_cnt <= 4'h8;
		else
			rst_cnt <= rst_cnt + rst_i;

	assign rst_i = rst_cnt[3];

	// Global buffer
	SB_GB rst_gbuf_I (
		.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i),
		.GLOBAL_BUFFER_OUTPUT(rst_gbuf)
	);

	// Distribution
	assign rst_led = rst_gbuf;
	assign rst_sys = rst_gbuf;

endmodule // sysmgr
