-- 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
--
-- Entity:				 	Reconfiguration engine for DRP enabled Xilinx primtives
--
-- Description:
-- 
-- Many complex primitives in a Xilinx device offer a Dynamic Reconfiguration
-- Port (DRP) to reconfigure a primitive at runtime without reconfiguring the
-- whole FPGA.
--
-- This module is a DRP master that can be pre-configured at compile time with
-- different configuration sets. The configuration sets are mapped into a ROM.
-- The user can select a stored configuration with ``ConfigSelect``. Sending a
-- strobe to ``Reconfig`` will start the reconfiguration process. The operation
-- completes with another strobe on ``ReconfigDone``.
--
-- License:
-- =============================================================================
-- Copyright 2007-2016 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.config.all;
use			PoC.utils.all;
use			PoC.vectors.all;
use			PoC.physical.all;
use			PoC.xil.all;


entity [docs]xil_Reconfigurator is
	generic (
		DEBUG						: boolean										:= FALSE;																				--
		CLOCK_FREQ			: FREQ											:= 100 MHz;																			--
		CONFIG_ROM			: in	T_XIL_DRP_CONFIG_ROM	:= (0 downto 0 => C_XIL_DRP_CONFIG_SET_EMPTY)		--
	);
	port (
		Clock						: in	std_logic;
		Reset						: in	std_logic;

		Reconfig				: in	std_logic;																														--
		ReconfigDone		: out	std_logic;																														--
		ConfigSelect		: in	std_logic_vector(log2ceilnz(CONFIG_ROM'length) - 1 downto 0);					--

		DRP_en					: out	std_logic;																														--
		DRP_Address			: out	T_XIL_DRP_ADDRESS;																										--
		DRP_we					: out	std_logic;																														--
		DRP_DataIn			: in	T_XIL_DRP_DATA;																												--
		DRP_DataOut			: out	T_XIL_DRP_DATA;																												--
		DRP_Ack					: in	std_logic																															--
	);
end entity;


architecture [docs]rtl of xil_Reconfigurator is
	attribute KEEP								: boolean;
	attribute FSM_ENCODING				: string;
	attribute signal_ENCODING			: string;

	type T_STATE is (
		ST_IDLE,
		ST_READ_BEGIN,	ST_READ_WAIT,
		ST_WRITE_BEGIN,	ST_WRITE_WAIT,
		ST_DONE
	);

	-- DualConfiguration - Statemachine
	signal State											: T_STATE																:= ST_IDLE;
	signal NextState									: T_STATE;
	attribute FSM_ENCODING	of State	: signal is ite(DEBUG, "gray", "speed1");

	signal DataBuffer_en							: std_logic;
	signal DataBuffer_d								: T_XIL_DRP_DATA												:= (others => '0');

	signal ROM_Entry									: T_XIL_DRP_CONFIG;
	signal ROM_LastConfigWord					: std_logic;

	signal ConfigSelect_d 						: std_logic_vector(ConfigSelect'range);

	constant CONFIGINDEX_BITS					: positive															:= log2ceilnz(CONFIG_ROM'length);
	signal ConfigIndex_rst						: std_logic;
	signal ConfigIndex_en							: std_logic;
	signal ConfigIndex_us							: unsigned(CONFIGINDEX_BITS - 1 downto 0);

	attribute KEEP of ROM_LastConfigWord	: signal is DEBUG;

begin
	-- configuration ROM
	blkCONFIG_ROM : block
		signal SetIndex 						: integer range 0 to CONFIG_ROM'high;
		signal RowIndex 						: T_XIL_DRP_CONFIG_INDEX;

		attribute KEEP of SetIndex	: signal is DEBUG;
		attribute KEEP of RowIndex	: signal is DEBUG;
	begin
		SetIndex							<= to_index(ConfigSelect_d, CONFIG_ROM'high);
		RowIndex							<= to_index(ConfigIndex_us, T_XIL_DRP_CONFIG_INDEX'high);
		ROM_Entry							<= CONFIG_ROM(SetIndex).Configs(RowIndex);
		ROM_LastConfigWord		<= to_sl(RowIndex = CONFIG_ROM(SetIndex).LastIndex);
	end block;

	-- configuration index counter
	process(Clock)
	begin
		if rising_edge(Clock) then
			if (ConfigIndex_rst = '1') then
				ConfigIndex_us		<= (others => '0');
				ConfigSelect_d		<= ConfigSelect;
			elsif (ConfigIndex_en = '1') then
				ConfigIndex_us		<= ConfigIndex_us + 1;
			end if;
		end if;
	end process;

	-- data buffer for DRP configuration words
	process(Clock)
	begin
		if rising_edge(Clock) then
			if (Reset = '1') then
				DataBuffer_d	<= (others => '0');
			elsif (DataBuffer_en = '1') then
				DataBuffer_d	<= ((DRP_DataIn			and not ROM_Entry.Mask) or
													(ROM_Entry.Data	and			ROM_Entry.Mask));
			end if;
		end if;
	end process;

	-- assign DRP signals
	DRP_Address						<= ROM_Entry.Address;
	DRP_DataOut						<= DataBuffer_d;

	-- DRP read-modify-write statemachine
	process(Clock)
	begin
		if rising_edge(Clock) then
			if (Reset = '1') then
				State			<= ST_IDLE;
			else
				State			<= NextState;
			end if;
		end if;
	end process;

	[docs]process(State, Reconfig, ROM_LastConfigWord, DRP_Ack	)
	begin
		NextState								<= State;

		ReconfigDone						<= '0';

		-- Dynamic Reconfiguration Port
		DRP_en									<= '0';
		DRP_we									<= '0';

		-- internal modules
		ConfigIndex_rst					<= '0';
		ConfigIndex_en					<= '0';
		DataBuffer_en						<= '0';

		case State is
			when ST_IDLE =>
				if (Reconfig = '1') then
					ConfigIndex_rst		<= '1';
					NextState					<= ST_READ_BEGIN;
				end if;

			when ST_READ_BEGIN =>
				DRP_en							<= '1';
				DRP_we							<= '0';
				NextState						<= ST_READ_WAIT;

			when ST_READ_WAIT =>
				if (DRP_Ack = '1') then
					DataBuffer_en			<= '1';
					NextState					<= ST_WRITE_BEGIN;
				end if;

			when ST_WRITE_BEGIN =>
				DRP_en							<= '1';
				DRP_we							<= '1';
				NextState						<= ST_WRITE_WAIT;

			when ST_WRITE_WAIT =>
				if (DRP_Ack = '1') then
					if (ROM_LastConfigWord = '1') then
						NextState				<= ST_DONE;
					else
						ConfigIndex_en	<= '1';
						NextState				<= ST_READ_BEGIN;
					end if;
				end if;

			when ST_DONE =>
				ReconfigDone				<= '1';
				NextState						<= ST_IDLE;

		end case;
	end process;
end architecture;