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

`default_nettype none

module sysmgr_sim (
	// 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
	// -------

	reg       clk_base = 1'b0;

	wire      pll_lock;

	reg [2:0] clk_div = 3'b000;
	reg       clk_sys_i;
	wire      clk_led_i;

	reg [7:0] rst_cnt = 8'h00;
	reg       rst_i = 1;
	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_ena_dly;
	wire      usb_ena_i;

	reg       sys_off;


	// SB_HFOSC
	// -------
	// Generates 48 MHz

	always #10.42 clk_base = ~clk_base;


	// PLL
	// ---

	assign clk_usb  = usb_ena_i & clk_base; 
	assign pll_lock = usb_ena_i;


	// 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_ena_dly <= usb_ena ? 4'b1111 : { usb_ena_dly[2:0], 1'b0 };

	assign usb_ena_i = usb_ena_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_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)
		rst_cnt <= rst_cnt + rst_i;

	always @(posedge clk_led)
		rst_i <= (rst_cnt[7:4] != 4'hf);

	// 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
