-- EMACS settings: -*-  tab-width: 4; indent-tabs-mode: t -*-
-- vim: tabstop=4:shiftwidth=4:noexpandtab
-- kate: tab-width 4; replace-tabs off; indent-width 4;
-- =============================================================================
-- Authors:					Paul Genssler
--
-- Entity:					Simple ICAP wrapper with a fifo interface and a few status signals
--
-- Description:
-- 
-- This module was designed to connect the Xilinx "Internal Configuration Access Port" (ICAP)
-- to a PCIe endpoint on a Dini board. Tested on:
--
-- tbd
--
-- 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 UNISIM;
use UNISIM.vcomponents.all;

library poc;
use poc.utils.all;

entity [docs]reconfig_icap_wrapper is
	generic (
		MIN_DEPTH_OUT     : positive := 256;
		MIN_DEPTH_IN      : positive := 256
	);
	port (
		clk 			: in	std_logic;
		reset			: in	std_logic;
		clk_icap		: in	std_logic;		-- clock signal for ICAP, max 100 MHz (double check with manual)

		icap_busy		: out	std_logic;		-- the ICAP is processing the data
		icap_readback	: out	std_logic;		-- high during a readback
		icap_partial_res: out	std_logic;		-- high during reconfiguration

		-- data in
		write_put		: in	std_logic;
		write_full		: out	std_logic;
		write_data		: in	std_logic_vector(31 downto 0);
		write_done		: in	std_logic;		-- high pulse/edge after all data was written

		-- data out
		read_got		: in	std_logic;
		read_valid 		: out	std_logic;
		read_data 		: out	std_logic_vector(31 downto 0)
	);
end reconfig_icap_wrapper;

architecture [docs]Behavioral of reconfig_icap_wrapper is
	signal reset_icap				: std_logic;

	signal write_done_d				: std_logic;
	signal write_done_edge			: std_logic;
	signal write_done_icapclk		: std_logic;

	signal in_data_valid			: std_logic;
	constant STATE_BITS 			: positive := 2;
	constant state_almost_full		: std_logic_vector(STATE_BITS -1 downto 0) := (0 => '0', others => '1');
	signal in_data_fill_state		: std_logic_vector(STATE_BITS -1 downto 0);
	signal in_data_rden				: std_logic;
	signal in_data_start			: std_logic;		-- high after enough data was written into the pci->icap fifo
														-- or write done (status register)
	signal icap_rden				: std_logic;		-- icap wants some yummy data
	signal in_data					: std_logic_vector(31 downto 0);

	signal out_data_full			: std_logic;
	signal out_data_put				: std_logic;
	signal out_data					: std_logic_vector(31 downto 0);

	signal icap_data_config			: std_logic_vector(31 downto 0);
	signal icap_data_readback		: std_logic_vector(31 downto 0);
	signal icap_csb					: std_logic;
	signal icap_rw					: std_logic;

	signal icap_data_config_r		: std_logic_vector(31 downto 0);
	signal icap_data_readback_r		: std_logic_vector(31 downto 0);
	signal icap_csb_r				: std_logic;
	signal icap_rw_r				: std_logic;

	signal fsm_status				: std_logic_vector(31 downto 0);
	signal fsm_status_clk			: std_logic_vector(31 downto 0);
	signal fsm_ready				: std_logic;
	signal fsm_ready_d				: std_logic;
begin
	write_done_d <= write_done when rising_edge(clk);
	write_done_edge <= to_sl(write_done = '1' and write_done_d = '0');

	icap_busy			<= not fsm_status_clk(3);
	icap_readback		<= fsm_status_clk(1);
	icap_partial_res	<= fsm_status_clk(0);

	fsm_ready <= fsm_status(3);
	fsm_ready_d <= fsm_ready when rising_edge(clk_icap);

	-- buffer some data before starting the icap, icap needs to be sync'ed before it can be paused
	in_data_buffer_p : [docs]process (clk_icap) begin
		if rising_edge(clk_icap) then
			if (reset_icap = '1') then
				in_data_start <= '0';
			else
				if fsm_ready = '1' and fsm_ready_d = '0' then	-- reset after icap is done
					in_data_start <= '0';
				elsif in_data_fill_state = state_almost_full or write_done_icapclk = '1' then	-- set when fifo almost full or write already done
					in_data_start <= '1';
				end if;
			end if;
		end if;
	end process in_data_buffer_p;

	in_data_rden <= icap_rden and in_data_start and in_data_valid;

	-- sync the written pci data into the user clk
	-- writer: pci
	-- reader: core
	fifo_in : entity poc.fifo_ic_got
		generic map(
			D_BITS			=> 32,
			MIN_DEPTH		=> MIN_DEPTH_IN,
			OUTPUT_REG		=> false,
			FSTATE_RD_BITS	=> STATE_BITS
		)
		port map(
			clk_wr 			=> clk,
			rst_wr 			=> reset,
			put    			=> write_put,
			din    			=> write_data,
			full   			=> write_full,
			estate_wr		=> open,

			clk_rd 			=> clk_icap,
			rst_rd 			=> reset_icap,
			got    			=> in_data_rden,
			valid  			=> in_data_valid,
			dout   			=> in_data,
			fstate_rd		=> in_data_fill_state
		);

	-- sync data from this core to the pci bus
	-- writer: core
	-- reader: pci
	fifo_out : entity poc.fifo_ic_got
		generic map(
			D_BITS			=> 32,
			MIN_DEPTH		=> MIN_DEPTH_OUT,
			OUTPUT_REG		=> false
		)
		port map(
			clk_wr 			=> clk_icap,
			rst_wr 			=> reset_icap,
			put    			=> out_data_put,
			din    			=> out_data,
			full   			=> out_data_full,

			clk_rd 			=> clk,
			rst_rd 			=> reset,
			got    			=> read_got,
			valid  			=> read_valid,
			dout   			=> read_data
		);

	icap_fsm_inst: entity poc.reconfig_icap_fsm port map(
		clk => clk_icap,
		reset => reset_icap,
		icap_in => icap_data_config_r,
		icap_out => icap_data_readback_r,
		icap_csb => icap_csb_r,
		icap_rw => icap_rw_r,
		in_data => in_data,
		in_data_valid => in_data_rden,		-- TODO start one clock cycle later
		in_data_rden => icap_rden,
		out_data => out_data,
		out_data_valid => out_data_put,
		out_data_full => out_data_full,
		status => fsm_status
	);

	-- icap
	icap_reg_p : [docs]process (clk_icap) begin
		if rising_edge(clk_icap) then
			icap_data_readback_r <= icap_data_readback;
			icap_csb <= icap_csb_r;
			icap_rw <= icap_rw_r;
			icap_data_config <= icap_data_config_r;
		end if;
	end process icap_reg_p;

	icap_inst : entity poc.xil_ICAP
	port map (
		clk			=> clk_icap,
		disable		=> icap_csb,
		busy		=> open,
		data_in		=> icap_data_config,
		data_out	=> icap_data_readback,
		rd_wr		=> icap_rw
	);

	strobe_sync : entity poc.sync_Strobe
	port map (
		clock1 => clk,
		clock2 => clk_icap,
		input(0) => write_done_edge,
		output(0) => write_done_icapclk,
		busy => open
	);

	reset_sync : entity poc.sync_Bits
	port map (
		clock => clk_icap,
		input(0) => reset,
		output(0) => reset_icap
	);

	fsm_status_sync : entity poc.sync_vector
	generic map (
		master_bits => 32
	) port map (
		clock1 => clk_icap,
		clock2 => clk,
		input => fsm_status,
		output => fsm_status_clk,
		busy => open,
		changed => open
	);


end Behavioral;