-- A simple Wishbone interface, controllable via UART.
-- Author: Markus Koch <markus-oliver.koch@thalesgroup.com>
--
-- Instructions:
--   Write: TX [Write-Count] [32b Addr] [32b Data] -------- [32b Addr] [32b Data] -------- ... [Write-Count] [32b Addr] [32b Data] ...
--          RX ----------------------------------- [8b CKS] --------------------- [8b CKS] ... ----------------------------------- ... 
--
--    Read: TX [8b 0x0] [32b Addr] ---------- [32b Addr] ... 
--          RX ------------------- [32b Data] ---------- ...
--
--   Stall: TX [8b 0xFF]
--          RX ---------
--
--   Reset: TX [8b 0xFE]
--             ---------
--
-- Log:
--   2015/06/24: Created file
--   2015/06/24: Removed logic for tx/rx buffers
--   2015/07/14: Added stall/reset logic

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

library ip;
use ip.wishbone_package.all;

entity jinn is
	port(
		clk_i          : in  std_logic;
		rst_i          : in  std_logic;

		-- Wishbone
		master_i       : in  wishbone_v3_master_in;
		master_o       : out wishbone_v3_master_out;

		-- CPU Control
		cpu_stall      : out std_logic;
		cpu_reset      : out std_logic;

		-- Data link
		data_i         : in  std_logic_vector(7 downto 0); -- Data in from UART
		data_available : in  std_logic; -- UART RX strobe

		data_o         : out std_logic_vector(7 downto 0); -- Data out to UART
		data_valid_o   : out std_logic; -- Data out strobe
		output_busy_i  : in  std_logic  -- Transmit busy -> stall core
	);
end entity jinn;

architecture RTL of jinn is
	signal write_counter : integer range 0 to 255;
	signal position      : integer range 0 to 7;
	signal address       : wishbone_address;
	signal data          : wishbone_data;
	signal cks           : unsigned(7 downto 0);

	type state_t is (COMMAND, PARSE, EXECUTE, TXSERIAL);
	signal state   : state_t;
	signal txDelay : std_logic;
begin
	jinn_p : process(clk_i, rst_i) is
	begin
		if rst_i = '1' then
			-- RESET!
			position      <= 0;
			state         <= COMMAND;
			write_counter <= 0;
			data_valid_o  <= '0';
			address       <= (others => '0');
			data          <= (others => '0');
			master_o.CYC  <= '0';
			master_o.STB  <= '0';
			master_o.ADR  <= (others => '0');
			master_o.DAT  <= (others => '0');
			master_o.WE   <= '0';
			txDelay       <= '0';
			cks           <= (others => '0');
			cpu_stall     <= '0';
			cpu_reset     <= '1';
		elsif rising_edge(clk_i) then
			data_valid_o <= '0';
			master_o.CYC <= '0';
			master_o.STB <= '0';

			cpu_reset <= '0';

			case state is
				when COMMAND =>
					cks      <= (others => '0');
					position <= 0;
					txDelay  <= '0';
					if data_available = '1' then
						if unsigned(data_i) = x"FF" then
							cpu_stall <= '1';
						elsif unsigned(data_i) = x"FE" then
							cpu_reset <= '1';
							cpu_stall <= '0';
						else
							write_counter <= to_integer(unsigned(data_i));
							state         <= PARSE;
						end if;
					end if;
				when PARSE =>
					if data_available = '1' then
						cks <= cks + unsigned(data_i); -- Sum everything, address and data 
						if position < 7 then
							position <= position + 1;
						end if;

						if position < 4 then -- addr word
							--address(8 * (position + 1) - 1 downto 8 * position) <= data_i;
							address <= address(23 downto 0) & data_i;
						else            -- data word
							--data(8 * (position + 1 - 4) - 1 downto 8 * (position - 4)) <= data_i;
							data <= data(23 downto 0) & data_i;
						end if;

						if (position = 7) or (position = 3 and write_counter = 0) then -- only retrieve address in read mode
							position <= 0;
							state    <= EXECUTE;

						--							master_o.ADR <= address;
						--							master_o.DAT <= data;
						--							master_o.CYC <= '1';
						--							master_o.STB <= '1';
						--							if write_counter = 0 then
						--								master_o.WE <= '0';
						--							else
						--								master_o.WE <= '1';
						--							end if;
						end if;
					end if;
				when EXECUTE =>
					master_o.ADR <= address;
					master_o.DAT <= data;
					master_o.CYC <= '1';
					master_o.STB <= '1';
					if write_counter = 0 then
						master_o.WE <= '0';
					else
						master_o.WE <= '1';
					end if;

					if master_i.ERR = '1' or master_i.ACK = '1' then -- END OF transmission
						master_o.CYC <= '0';
						master_o.STB <= '0';

						if write_counter = 0 then -- read
							data     <= master_i.DAT;
							state    <= TXSERIAL;
							position <= 0;
						else
							write_counter <= write_counter - 1;
							if master_i.ACK = '1' then
								data <= std_logic_vector(cks) & x"000000"; -- ACK!
							else
								data <= not std_logic_vector(cks) & x"FFFFFF"; -- NAK! by sending a probably woring checksum
							end if;
							position <= 3; -- Only transmit one byte (the checksum)
							state    <= TXSERIAL;
							cks      <= (others => '0');
						end if;
					elsif master_i.RTY = '1' then
						null;
					-- wait
					end if;
				when TXSERIAL =>
					txDelay <= not txDelay;
					if txDelay = '0' and output_busy_i = '0' then -- only every odd cycle (uart has a busy-activate delay of 1)
						--data_o       <= address(8 * (position + 1) - 1 downto 8 * position);
						data_o       <= data(31 downto 24);
						data         <= data(23 downto 0) & x"00";
						data_valid_o <= '1';

						if position = 3 then
							position <= 0;
							if write_counter = 0 then
								state <= COMMAND; -- R/W selector (Write-Count)
							else
								state <= PARSE; -- more writes are following directly 
							end if;
						else
							position <= position + 1;
						end if;
					end if;
			end case;

		--master_o.CYC <= '0'; -- DEBUG ONLY - diasble moduel
		--master_o.STB <= '0'; -- DEBUG ONYL
		end if;
	end process jinn_p;
	master_o.SEL <= "1111";

	master_o.BTE  <= (others => '0');
	master_o.CTI  <= (others => '0');
	master_o.LOCK <= '0';
end architecture RTL;
