-- 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:					Martin Zabel
--									Patrick Lehmann
--
-- Entity:					Chip-Specific DDR Input and Output Registers
--
-- Description:
-- 
-- Instantiates chip-specific :abbr:`DDR (Double Data Rate)` input and output
-- registers.
--
-- Both data ``DataOut_high/low`` as well as ``OutputEnable`` are sampled with
-- the ``rising_edge(Clock)`` from the on-chip logic. ``DataOut_high`` is brought
-- out with this rising edge. ``DataOut_low`` is brought out with the falling
-- edge.
--
-- ``OutputEnable`` (Tri-State) is high-active. It is automatically inverted if
-- necessary. Output is disabled after power-up.
--
-- Both data ``DataIn_high/low`` are synchronously outputted to the on-chip logic
-- with the rising edge of ``Clock``. ``DataIn_high`` is the value at the ``Pad``
-- sampled with the same rising edge. ``DataIn_low`` is the value sampled with
-- the falling edge directly before this rising edge. Thus sampling starts with
-- the falling edge of the clock as depicted in the following waveform.
--
-- @WAVEDROM_START
--
--    { signal: [
--      ['DataOut',
--        {name: 'ClockOut',        wave: 'LH.L.H.L.H.L.H.L.H.L.H.'},
--        {name: 'ClockOutEnable',  wave: '0..1...................'},
--        {name: 'OutputEnable',    wave: '0.......1.......0......'},
--        {name: 'DataOut_low',     wave: 'x.......2...4...x......', data: ['4',      '6'],      node: '........k...m...o..'},
--        {name: 'DataOut_high',    wave: 'x.......3...5...x......', data: ['5',      '7'],      node: '........l...n...p..'}
--        ],
--        {},
--        {name: 'Pad',             wave: 'x2.3.4.5.z...2.3.4.5.z.', data: ['0', '1', '2', '3', '4', '5', '6', '7'], node: '.a.b.c.d.....e.f.g.h.'},
--        {},
--      ['DataIn',
--        {name: 'ClockIn',         wave: 'L.H.L.H.L.H.L.H.L.H.L.H'},
--        {name: 'ClockInEnable',   wave: '01.......0.............'},
--        {name: 'DataIn_low',      wave: 'x.....2...4...z...2...4', data: ['0',      '2',      '4'],      node: '......u...w.......y..'},
--        {name: 'DataIn_high',     wave: 'x.....3...5...z...3...5', data: ['1',      '3',      '5'],      node: '......v...x.......z..'}
--      ]
--      ],
--      edge: ['a~>u', 'b~>v', 'c~>w', 'd~>x', 'k~>e', 'l~>f', 'm~>g', 'n~>h', 'e~>y', 'f~>z'],
--      foot: {
--        text: ['tspan',
--          ['tspan', {'font-weight': 'bold'}, 'PoC.io.ddrio.inout'],
--          ' -- DDR Data Input/Output sampled from pad.'
--        ]
--      }
--    }
--
-- @WAVEDROM_END
--
-- ``Pad`` must be connected to a PAD because FPGAs only have these registers in
-- IOBs.


library	IEEE;
use			IEEE.std_logic_1164.all;

library	PoC;
use			PoC.utils.all;
use			PoC.config.all;
use			PoC.ddrio.all;


entity [docs]ddrio_inout is
	generic (
		BITS					: positive
	);
	port (
		ClockOut				: in		std_logic;
		ClockOutEnable	: in		std_logic;
		OutputEnable		: in		std_logic;
		DataOut_high		: in		std_logic_vector(BITS - 1 downto 0);
		DataOut_low			: in		std_logic_vector(BITS - 1 downto 0);

		ClockIn					: in		std_logic;
		ClockInEnable		: in		std_logic;
		DataIn_high			: out		std_logic_vector(BITS - 1 downto 0);
		DataIn_low			: out		std_logic_vector(BITS - 1 downto 0);

		Pad							: inout	std_logic_vector(BITS - 1 downto 0)
	);
end entity;


architecture [docs]rtl of ddrio_inout is

begin
	assert ((VENDOR = VENDOR_ALTERA) or ((SIMULATION = TRUE) and (VENDOR = VENDOR_GENERIC)) or (VENDOR = VENDOR_XILINX))
		report "PoC.io.ddrio.inout is not implemented for given DEVICE."
		severity FAILURE;

	genXilinx : if VENDOR = VENDOR_XILINX generate
		inst : ddrio_inout_xilinx
			generic map (
				BITS						=> BITS
			)
			port map (
				ClockOut				=> ClockOut,
				ClockOutEnable	=> ClockOutEnable,
				OutputEnable		=> OutputEnable,
				DataOut_high		=> DataOut_high,
				DataOut_low			=> DataOut_low,
				ClockIn					=> ClockIn,
				ClockInEnable		=> ClockInEnable,
				DataIn_high			=> DataIn_high,
				DataIn_low			=> DataIn_low,
				Pad							=> Pad
			);
	end generate;

	genAltera : if VENDOR = VENDOR_ALTERA generate
		inst : ddrio_inout_altera
			generic map (
				BITS						=> BITS
			)
			port map (
				ClockOut				=> ClockOut,
				ClockOutEnable	=> ClockOutEnable,
				OutputEnable		=> OutputEnable,
				DataOut_high		=> DataOut_high,
				DataOut_low			=> DataOut_low,
				ClockIn					=> ClockIn,
				ClockInEnable		=> ClockInEnable,
				DataIn_high			=> DataIn_high,
				DataIn_low			=> DataIn_low,
				Pad							=> Pad
			);
	end generate;

	genGeneric : if SIMULATION  and (VENDOR = VENDOR_GENERIC) generate
		signal DataOut_high_d	: std_logic_vector(BITS - 1 downto 0);
		signal DataOut_low_d	: std_logic_vector(BITS - 1 downto 0);
		signal OutputEnable_d	: std_logic;
		signal Pad_o					: std_logic_vector(BITS - 1 downto 0);

		signal Pad_d_fe				: std_logic_vector(BITS - 1 downto 0);
		signal DataIn_high_d	: std_logic_vector(BITS - 1 downto 0);
		signal DataIn_low_d		: std_logic_vector(BITS - 1 downto 0);
	begin
		DataOut_high_d	<= DataOut_high		when rising_edge(ClockOut) and (ClockOutEnable = '1');
		DataOut_low_d		<= DataOut_low		when rising_edge(ClockOut) and (ClockOutEnable = '1');
		OutputEnable_d	<= OutputEnable		when rising_edge(ClockOut) and (ClockOutEnable = '1');

		process(ClockOut, OutputEnable_d, DataOut_high_d, DataOut_low_d)
			type T_MUX is array(bit) of std_logic_vector(BITS - 1 downto 0);
			variable MuxInput		: T_MUX;
		begin
			MuxInput('1')	:= DataOut_high_d;
			MuxInput('0')	:= DataOut_low_d;

			if (OutputEnable_d = '1') then
				Pad_o		<= MuxInput(to_bit(ClockOut));
			else
				Pad_o		<= (others => 'Z');
			end if;
		end process;

		Pad			<= Pad_o;

		Pad_d_fe				<= Pad			when falling_edge(ClockIn)	and (ClockInEnable = '1');
		DataIn_high_d		<= Pad			when rising_edge(ClockIn)		and (ClockInEnable = '1');
		DataIn_low_d		<= Pad_d_fe	when rising_edge(ClockIn)		and (ClockInEnable = '1');

		DataIn_high			<= DataIn_high_d;
		DataIn_low			<= DataIn_low_d;
	end generate;
end architecture;