-- EMACS settings: -*-  tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
-- =============================================================================
-- Authors:         Patrick Lehmann
--                  Steffen Koehler
--
-- Entity:          Synchronizes a command signal across clock-domain boundaries
--
-- Description:
-- 
-- This module synchronizes a vector of bits from clock-domain ``Clock1`` to
-- clock-domain ``Clock2``. The clock-domain boundary crossing is done by a
-- change comparator, a T-FF, two synchronizer D-FFs and a reconstructive
-- XOR indicating a value change on the input. This changed signal is used
-- to capture the input for the new output. A busy flag is additionally
-- calculated for the input clock-domain. The output has strobe character
-- and is reset to it's ``INIT`` value after one clock cycle.
--
-- Constraints:
--   This module uses sub modules which need to be constrained. Please
--   attend to the notes of the instantiated sub modules.
--
-- License:
-- =============================================================================
-- Copyright 2007-2015 Technische Universitaet Dresden - Germany
--                     Chair of VLSI-Design, Diagnostics and Architecture
--
-- 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.
-- =============================================================================

library IEEE;
use     IEEE.STD_LOGIC_1164.all;
use     IEEE.NUMERIC_STD.all;

library PoC;
use     PoC.utils.all;
use     PoC.sync.all;


entity [docs]sync_Command is
	generic (
		BITS          : positive            := 8;                       -- number of bit to be synchronized
		INIT          : std_logic_vector    := x"00000000";             --
		SYNC_DEPTH    : T_MISC_SYNC_DEPTH   := T_MISC_SYNC_DEPTH'low    -- generate SYNC_DEPTH many stages, at least 2
	);
	port (
		Clock1        : in  std_logic;                                  -- <Clock>  input clock
		Clock2        : in  std_logic;                                  -- <Clock>  output clock
		Input         : in  std_logic_vector(BITS - 1 downto 0);        -- @Clock1: input vector
		Output        : out std_logic_vector(BITS - 1 downto 0);        -- @Clock2: output vector
		Busy          : out std_logic;                                  -- @Clock1: busy bit
		Changed       : out std_logic                                   -- @Clock2: changed bit
	);
end entity;


architecture [docs]rtl of sync_Command is
	attribute SHREG_EXTRACT        : string;

	constant INIT_I                : std_logic_vector                        := descend(INIT)(BITS - 1 downto 0);

	signal D0                      : std_logic                                := '0';
	signal D1                      : std_logic_vector(BITS - 1 downto 0)      := INIT_I;
	signal T2                      : std_logic                                := '0';
	signal D3                      : std_logic                                := '0';
	signal D4                      : std_logic                                := '0';
	signal D5                      : std_logic_vector(BITS - 1 downto 0)      := INIT_I;

	signal IsCommand_Clk1          : std_logic;
	signal Changed_Clk1            : std_logic;
	signal Changed_Clk2            : std_logic;
	signal Busy_i                  : std_logic;

	-- Prevent XST from translating two FFs into SRL plus FF
	attribute SHREG_EXTRACT of D0  : signal is "NO";
	attribute SHREG_EXTRACT of T2  : signal is "NO";
	attribute SHREG_EXTRACT of D3  : signal is "NO";
	attribute SHREG_EXTRACT of D4  : signal is "NO";
	attribute SHREG_EXTRACT of D5  : signal is "NO";

	signal syncClk1_In    : std_logic;
	signal syncClk1_Out    : std_logic;
	signal syncClk2_In    : std_logic;
	signal syncClk2_Out    : std_logic;

begin
	-- input D-FF @Clock1 -> changed detection
	process(Clock1)
	begin
		if rising_edge(Clock1) then
			if (Busy_i = '0') then
				D0  <= IsCommand_Clk1;        -- delay detected IsCommand signal for rising edge detection; gated by busy flag
				D1  <= Input;
				T2  <= T2 xor Changed_Clk1;   -- toggle T2 if input vector has changed
			end if;
		end if;
	end process;

	-- D-FF for level change detection (both edges)
	process(Clock2)
	begin
		if rising_edge(Clock2) then
			D3    <= syncClk2_Out;
			D4    <= Changed_Clk2;

			if (D4 = '1') then
				D5  <= INIT_I;
			elsif (Changed_Clk2 = '1') then
				D5  <= D1;
			end if;
		end if;
	end process;

	-- assign syncClk*_In signals
	syncClk2_In    <= T2;
	syncClk1_In    <= D3;

	IsCommand_Clk1  <= to_sl(Input /= INIT_I);      -- input command detection
	Changed_Clk1    <= not D0 and IsCommand_Clk1;   -- input rising edge detection
	Changed_Clk2    <= syncClk2_Out xor D3;         -- level change detection; restore strobe signal from flag
	Busy_i          <= T2 xor syncClk1_Out;         -- calculate busy signal

	-- output signals
	Output        <= D5;
	Busy          <= Busy_i;
	Changed        <= D4;

	syncClk2 : entity PoC.sync_Bits
		generic map (
			BITS        => 1,             -- number of bit to be synchronized
			SYNC_DEPTH  => SYNC_DEPTH
		)
		port map (
			Clock       => Clock2,        -- <Clock>  output clock domain
			Input(0)    => syncClk2_In,   -- @async:  input bits
			Output(0)   => syncClk2_Out   -- @Clock:  output bits
		);

	syncClk1 : entity PoC.sync_Bits
		generic map (
			BITS        => 1,             -- number of bit to be synchronized
			SYNC_DEPTH  => SYNC_DEPTH
		)
		port map (
			Clock       => Clock1,        -- <Clock>  output clock domain
			Input(0)    => syncClk1_In,   -- @async:  input bits
			Output(0)   => syncClk1_Out   -- @Clock:  output bits
		);
end architecture;