--
-- PowerPC 405 APU FCM "timestamp"
-- record a time (counter value) of User Defined Instruction execution
--
-- Marek Peca <mp@duch.cz> 07/2008
-- KRT FEL CVUT http://dce.felk.cvut.cz/
--

library ieee;
	use ieee.std_logic_1164.all;
	use ieee.std_logic_arith.all;
	use ieee.std_logic_unsigned.all;
	use ieee.numeric_std.all;

library unisim;
	use unisim.vcomponents.ramb16;

entity TIMESTAMP is
	port (
		RESET               : in    std_logic;
		-- APU i/f:
		CPMFCMCLK           : in    std_logic;
		APUFCMFLUSH         : in    std_logic;
		APUFCMDECODED       : in    std_logic;
		APUFCMINSTRVALID    : in    std_logic;
		APUFCMDECUDIVALID   : in    std_logic;
		APUFCMDECUDI        : in    std_logic_vector(2 downto 0);
		APUFCMWRITEBACKOK   : in    std_logic;
		APUFCMRADATA        : in    std_logic_vector(31 downto 0);
		APUFCMRBDATA        : in    std_logic_vector(31 downto 0);
		FCMAPUDONE          : out   std_logic;
		FCMAPUSLEEPNOTREADY : out   std_logic;
		-- BRAM slave i/f:
		BRAM_RST_B          : in    std_logic;
		BRAM_CLK_B          : in    std_logic;
		BRAM_EN_B           : in    std_logic;
		BRAM_WEN_B          : in    std_logic_vector(7 downto 0);
		BRAM_ADDR_B         : in    std_logic_vector(31 downto 0);
		BRAM_DOUT_B         : in    std_logic_vector(63 downto 0);
		BRAM_DIN_B          : out   std_logic_vector(63 downto 0);
		-- etc.
		DEBUG               : out   std_logic_vector(3 downto 0)
	);
end entity TIMESTAMP;

architecture TIMESTAMP_FCM of TIMESTAMP is

	type state_type is (IDLE, WAIT_OPERAND);

	-- global
	signal clock             : std_logic;
	-- FSM
	signal state, next_state : state_type;
	signal counter           : std_logic_vector(31 downto 0);
	signal addr_counter      : std_logic_vector(9 downto 0);
	signal save_udi_code     : std_logic;
	signal udi_code          : std_logic_vector(2 downto 0);
	-- BRAM
	signal wea               : std_logic;
	signal dia0,  dia1       : std_logic_vector(31 downto 0);
	signal addra             : std_logic_vector(9 downto 0);

begin

	clock <= CPMFCMCLK;
	dia0  <= counter;
	dia1  <= APUFCMRADATA;
	addra <= addr_counter;

	-- debug(0) <= addr_counter(0);
	-- debug(1) <= APUFCMDECUDIVALID;
	-- debug(2) <= APUFCMWRITEBACKOK;
	-- debug(3) <= wea;
	debug(0) <= CPMFCMCLK;
	debug(1) <= APUFCMDECODED;
	debug(2) <= APUFCMDECUDIVALID;
	debug(3) <= APUFCMWRITEBACKOK;

	SEQ : process () is
	begin

		wait until clock'event and clock = '1';

		if (RESET = '1') then
			state        <= IDLE;
			counter      <= X"00000000";
			addr_counter <= "0000000000";
		else
			if (save_udi_code = '1') then
				udi_code <= APUFCMDECUDI;
			end if;
			state   <= next_state;
			counter <= counter + 1;
			if (wea = '1') then
				addr_counter <= addr_counter + 1;
			end if;
		end if;

	end process SEQ;

	COMB_APU : process (state, udi_code,
	                    APUFCMFLUSH, APUFCMINSTRVALID, APUFCMDECUDIVALID,
	                    APUFCMWRITEBACKOK, APUFCMDECUDI) is
	begin

		save_udi_code       <= '0';
		wea                 <= '0';
		FCMAPUSLEEPNOTREADY <= '0';
		FCMAPUDONE          <= '0';

		case state is

			when IDLE =>

				if (APUFCMFLUSH = '1') then
					next_state <= IDLE;
				elsif ((APUFCMINSTRVALID and APUFCMDECODED and APUFCMDECUDIVALID) = '1') then
					if (APUFCMWRITEBACKOK = '1') then
						-- operands are ready
						if (APUFCMDECUDI = "000") then
							wea        <= '1';
							FCMAPUDONE <= '1';
						end if;
					else
						save_udi_code <= '1';
						next_state    <= WAIT_OPERAND;
					end if;
				end if;

			when WAIT_OPERAND =>
				FCMAPUSLEEPNOTREADY <= '1';

				if (APUFCMFLUSH = '1') then
					next_state <= IDLE;
				elsif (APUFCMWRITEBACKOK = '1') then
					if (udi_code = "000") then
						wea        <= '1';
						FCMAPUDONE <= '1';
					end if;
				end if;

				next_state <= IDLE;

		end case;

	end process COMB_APU;

	--   comb_action: process (action, action_udi_code)
	-- -- following block causes "gated clock" warning
	-- -- after FCMAPUDONE removal, everything seems to be OK
	-- -- what is strange: the same construct above at FCMAPUSLEEPNOTREADY
	-- -- causes no warning
	--   begin
	--     wea <= '0';
	--     FCMAPUDONE <= '0';
	--     if (action = '1') and (action_udi_code = "111") then
	--       wea <= '1';
	--       FCMAPUDONE <= '1';
	--     end if;
	--   end process;

	BRAM0 : RAMB16
		generic map (
			INVERT_CLK_DOA_REG => false,
			INVERT_CLK_DOB_REG => false,
			RAM_EXTENSION_A    => "NONE",
			RAM_EXTENSION_B    => "NONE",
			READ_WIDTH_A       => 36,
			READ_WIDTH_B       => 36,
			WRITE_MODE_A       => "WRITE_FIRST",
			WRITE_MODE_B       => "WRITE_FIRST",
			WRITE_WIDTH_A      => 36,
			WRITE_WIDTH_B      => 36
		)
		port map (
			DOA                => open,
			DOB                => BRAM_DIN_B(31 downto 0),
			ADDRA(14 downto 5) => addra,
			ADDRA(4 downto 0)  => "00000",
			ADDRB(14 downto 2) => BRAM_ADDR_B(12 downto 0),
			ADDRB(1 downto 0)  => "00",
			CASCADEINA         => '0',
			CASCADEINB         => '0',
			CLKA               => clock,
			CLKB               => BRAM_CLK_B,
			DIA                => dia0,
			DIB                => BRAM_DOUT_B(31 downto 0),
			DIPA               => "0000",
			DIPB               => "0000",
			ENA                => '1',
			ENB                => BRAM_EN_B,
			REGCEA             => '1',
			REGCEB             => '1',
			SSRA               => '0',
			SSRB               => BRAM_RST_B,
			WEA(0)             => wea,
			WEA(1)             => wea,
			WEA(2)             => wea,
			WEA(3)             => wea,
			WEB                => BRAM_WEN_B(3 downto 0)
		);

	BRAM1 : RAMB16
		generic map (
			INVERT_CLK_DOA_REG => false,
			INVERT_CLK_DOB_REG => false,
			RAM_EXTENSION_A    => "NONE",
			RAM_EXTENSION_B    => "NONE",
			READ_WIDTH_A       => 36,
			READ_WIDTH_B       => 36,
			WRITE_MODE_A       => "WRITE_FIRST",
			WRITE_MODE_B       => "WRITE_FIRST",
			WRITE_WIDTH_A      => 36,
			WRITE_WIDTH_B      => 36
		)
		port map (
			DOA                => open,
			DOB                => BRAM_DIN_B(63 downto 32),
			ADDRA(14 downto 5) => addra,
			ADDRA(4 downto 0)  => "00000",
			ADDRB(14 downto 2) => BRAM_ADDR_B(12 downto 0),
			ADDRB(1 downto 0)  => "00",
			CASCADEINA         => '0',
			CASCADEINB         => '0',
			CLKA               => clock,
			CLKB               => BRAM_CLK_B,
			DIA                => dia1,
			DIB                => BRAM_DOUT_B(63 downto 32),
			DIPA               => "0000",
			DIPB               => "0000",
			ENA                => '1',
			ENB                => BRAM_EN_B,
			REGCEA             => '1',
			REGCEB             => '1',
			SSRA               => '0',
			SSRB               => BRAM_RST_B,
			WEA(0)             => wea,
			WEA(1)             => wea,
			WEA(2)             => wea,
			WEA(3)             => wea,
			WEB                => BRAM_WEN_B(7 downto 4)
		);

end architecture TIMESTAMP_FCM;

-- EOF
