--------------------------------------------------------------------------
-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL
-- This file: JIPS top file 
--
-- Copyright (C) 2008 J. Ahrensfeld
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
--
-- For questions and ideas, please contact the author at jens@jayfield.org
--
--------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;

library work;
use work.mips_types.all;
use work.mips_util_pkg.all;
use work.busmaster_types.all;

entity biu is
	Generic
	(
		ICACHE_SIZE		: natural := 2048;	-- words
		ICACHE_LINE		: natural := 8;		-- words
		DCACHE_SIZE		: natural := 2048;	-- words
		DCACHE_LINE		: natural := 8;		-- words
		WRITE_FIFO_SIZE	: natural := 4;		-- words
		WITH_TLB		: boolean
	);
	Port
	(
		clk				: in  STD_LOGIC;
		busy			: out STD_LOGIC;

		imem_err		: out STD_LOGIC;
		imem_rdy		: out STD_LOGIC;
		imem_en			: in  STD_LOGIC;
		imem_addr		: in  word_t;
		imem_dout		: out word_t;
		dmem_err		: out STD_LOGIC;
		dmem_rdy		: out STD_LOGIC;
		dmem_en			: in  STD_LOGIC;
		dmem_we			: in  STD_LOGIC;
		dmem_be			: in  unsigned(3 downto 0);
		dmem_din		: in  word_t;
		dmem_dout		: out word_t;
		dmem_addr		: in  word_t;

		RST_I			: in  STD_LOGIC;
		CLK_I			: in  STD_LOGIC;
		ACK_I			: in  STD_LOGIC;
		SRDY_I			: in  STD_LOGIC;
		ADDR_O			: out unsigned(31 downto 0);
		MDAT_I			: in  unsigned(31 downto 0);
		MDAT_O			: out unsigned(31 downto 0);
		WE_O			: out STD_LOGIC;
		SEL_O			: out unsigned(3 downto 0);
		CYC_O			: out STD_LOGIC;
		STB_O			: out STD_LOGIC;
		MRDY_O			: out STD_LOGIC;
		
		ctrl_in			: in  biu_ctrl_in_t;
		ctrl_out		: out biu_ctrl_out_t
		
	);
end biu;

architecture behavior of biu is

	type bus_state_t is (init, ready, icache_bus_access, dcache_bus_access, write_bus, read_bus, read_finish);
	
	signal s, sn 			: bus_state_t;
	signal bus_idle			: std_logic;

	type timeout_cnt_t is range 0 to 1E5-1;	
	signal bus_timeout_cnt	: timeout_cnt_t;
	signal bus_timeout		: std_logic;
	
	-------------------------------------------------
	signal RST				: std_logic;
	signal CPU_CLK			: std_logic;

	-- busmaster signals
	signal bus_din						: word_t;
	signal bus_din_rdy					: std_logic;
	signal bus_din_we					: std_logic;

	signal bus_dout						: word_t;
	signal bus_dout_vld					: std_logic;
	signal bus_dout_re					: std_logic;

	signal bus_cmd_rdy					: std_logic;
	signal bus_cmd_we					: std_logic;
	signal bus_cmd_cycle_en				: std_logic;
	signal bus_cmd						: bm_cmd_t;
	signal bus_cyc_complete				: std_logic;

	signal i_bus_din_rdy				: std_logic;
	signal i_bus_cmd_rdy				: std_logic;
	signal i_bus_cmd_we					: std_logic;
	signal i_bus_cmd_cycle_en			: std_logic;
	signal i_bus_cmd					: bm_cmd_t;

	signal i_tlb_ctrl_in				: tlb_ctrl_in_t;
	signal i_tlb_ctrl_out				: tlb_ctrl_out_t;
	signal i_tlb_qry_in					: tlb_query_in_t;
	signal i_tlb_qry_out				: tlb_query_out_t;
	signal i_tlb_rdy					: std_logic;
	signal i_cache_rdy					: std_logic;

	signal d_bus_din_rdy				: std_logic;
	signal d_bus_cmd_rdy				: std_logic;
	signal d_bus_cmd_we					: std_logic;
	signal d_bus_cmd_cycle_en			: std_logic;
	signal d_bus_cmd					: bm_cmd_t;

	signal d_tlb_ctrl_in				: tlb_ctrl_in_t;
	signal d_tlb_ctrl_out				: tlb_ctrl_out_t;
	signal d_tlb_qry_in					: tlb_query_in_t;
	signal d_tlb_qry_out				: tlb_query_out_t;
	signal d_tlb_rdy					: std_logic;
	signal d_cache_rdy					: std_logic;

	signal icache_bus_gnt				: std_logic;
	signal dcache_bus_gnt				: std_logic;


begin

	CPU_CLK					<= clk;
	RST						<= RST_I;
	imem_rdy				<= i_cache_rdy;
	dmem_rdy				<= d_cache_rdy;

	ctrl_out.itlb			<= i_tlb_ctrl_out;
	ctrl_out.dtlb			<= d_tlb_ctrl_out;
	
	
biu_busy_state:
	process(clk)
	begin
		if rising_edge(clk) then
			if RST_I = '1' then
				busy	<= '1';
			elsif (i_tlb_rdy and i_cache_rdy and d_cache_rdy and d_tlb_rdy) = '1' then
				busy	<= '0';
			end if;
		end if;
	end process;	

inst_busmaster : entity work.busmaster_async
	GENERIC MAP
	(
		DATA_WIDTH			=> 32,
		FIFO_DEPTH			=> 16
	)
	PORT MAP
	(
		-- System signals
		rst					=> RST,
		clk					=> CPU_CLK,

		cmd_cycle_finished	=> bus_cyc_complete,
		cmd_cycle_en		=> bus_cmd_cycle_en,
		
		cmd_rdy				=> bus_cmd_rdy,
		cmd_we				=> bus_cmd_we,
		cmd_in				=> bus_cmd,
		
		din_rdy				=> bus_din_rdy,
		din_we				=> bus_din_we,
		din					=> bus_din,

		dout				=> bus_dout,
		dout_vld			=> bus_dout_vld,
		dout_re				=> bus_dout_re,

		-- JBUS Master signals
		RST_I				=> RST_I,
		CLK_I				=> CLK_I,
		CYC_O				=> CYC_O,
		STB_O				=> STB_O,
		WE_O				=> WE_O,
		SEL_O				=> SEL_O,
		ACK_I				=> ACK_I,
		SRDY_I				=> SRDY_I,
		MRDY_O				=> MRDY_O,
		ADDR_O				=> ADDR_O,
		MDAT_I				=> MDAT_I,
		MDAT_O				=> MDAT_O
	);

inst_i_tlb : entity work.tlb
	GENERIC MAP
	(
		NUM_ENTRIES			=> TLB_NUM_ENTRIES,
		CACHE_KSEG1			=>	false,
		USE_TLB				=>	WITH_TLB
	)
	PORT MAP
	(
		-- System signals
		rst					=> RST,
		clk					=> CPU_CLK,
		ce					=> '1',

		rdy					=> i_tlb_rdy,

		ctrl_in				=> i_tlb_ctrl_in,
		ctrl_out			=> i_tlb_ctrl_out,

		query_in			=> i_tlb_qry_in,
		query_out			=> i_tlb_qry_out

	);

	i_tlb_ctrl_in			<= ctrl_in.itlb;
	
inst_icache: entity work.icache
	GENERIC MAP
	(
		CACHE_SIZE			=>	ICACHE_SIZE,
		LINE_SIZE			=>	ICACHE_LINE
	)
	PORT MAP
	(
		rst					=>	RST,
		clk					=>	CPU_CLK,

		-- CPU control/data
		en					=>	imem_en,
		addr				=>	imem_addr,
		dout				=>	imem_dout,
		rdy					=>	i_cache_rdy,

		-- Cache control
		ctrl_in				=>	ctrl_in.icache,
		tlb_query_in		=>  i_tlb_qry_out,
		tlb_query_out		=>  i_tlb_qry_in,
		
		-- busmaster data
		bus_din				=>	bus_dout,
		bus_din_rdy			=>	i_bus_din_rdy,
		bus_din_vld			=>	bus_dout_vld,

		-- busmaster command
		bus_cmd_rdy			=>	i_bus_cmd_rdy,
		bus_cmd_we			=>	i_bus_cmd_we,
		bus_cmd_cycle_en	=>	i_bus_cmd_cycle_en,
		bus_cmd_out			=>	i_bus_cmd,
		bus_cyc_complete	=>	bus_cyc_complete
	);

	
inst_d_tlb : entity work.tlb
	GENERIC MAP
	(
		NUM_ENTRIES			=> TLB_NUM_ENTRIES,
		CACHE_KSEG1			=>	false,
		USE_TLB				=>	WITH_TLB
	)
	PORT MAP
	(
		-- System signals
		rst					=> RST,
		clk					=> CPU_CLK,
		ce					=> '1',

		rdy					=> d_tlb_rdy,

		ctrl_in				=> d_tlb_ctrl_in,
		ctrl_out			=> d_tlb_ctrl_out,

		query_in			=> d_tlb_qry_in,
		query_out			=> d_tlb_qry_out

	);

	d_tlb_ctrl_in			<= ctrl_in.dtlb;

inst_dcache: entity work.dcache
	GENERIC MAP
	(
		CACHE_SIZE			=>	DCACHE_SIZE,
		LINE_SIZE			=>	DCACHE_LINE,
		WRITE_FIFO_SIZE		=>	WRITE_FIFO_SIZE
	)
	PORT MAP
	(
		rst					=>	RST,
		clk					=>	CPU_CLK,

		-- CPU control/data
		en					=>	dmem_en,
		we					=>	dmem_we,
		addr				=>	dmem_addr,
		be					=>	dmem_be,
		din					=>	dmem_din,
		dout				=>	dmem_dout,
		rdy					=>	d_cache_rdy,

		-- Cache control
		ctrl_in				=>	ctrl_in.dcache,
		tlb_query_in		=>  d_tlb_qry_out,
		tlb_query_out		=>  d_tlb_qry_in,

		-- busmaster data
		bus_din				=>	bus_dout,
		bus_din_rdy			=>	d_bus_din_rdy,
		bus_din_vld			=>	bus_dout_vld,

		bus_dout			=>	bus_din,
		bus_dout_vld		=>	bus_din_we,
		bus_dout_rdy		=>	bus_din_rdy,

		-- busmaster command
		bus_cmd_rdy			=>	d_bus_cmd_rdy,
		bus_cmd_we			=>	d_bus_cmd_we,
		bus_cmd_cycle_en	=>	d_bus_cmd_cycle_en,
		bus_cmd_out			=>	d_bus_cmd,
		bus_cyc_complete	=>	bus_cyc_complete
	);

bus_state_next:
	process(clk)
	begin
		if rising_edge(clk) then
			if RST_I = '1' then
				s	<= init;
			else
				s	<= sn;
			end if;
		end if;
	end process;	
	
bus_state:
	process(s, i_bus_cmd_cycle_en, d_bus_cmd_cycle_en, i_bus_cmd_we, i_bus_cmd, d_bus_cmd_we, d_bus_cmd, bus_cyc_complete, i_bus_din_rdy, d_bus_din_rdy, bus_cmd_rdy)
	begin
		
		icache_bus_gnt		<= '0';
		dcache_bus_gnt		<= '0';

		i_bus_cmd_rdy		<= '0';
		d_bus_cmd_rdy		<= '0';
		
		bus_cmd_cycle_en	<= '0';
		bus_cmd_we			<= '0';	
		bus_cmd				<= i_bus_cmd;
		bus_dout_re			<= '0';
		bus_idle			<= '0';
		
		sn					<= s;
		case s is
		when init =>
			sn		<= ready;
		when ready =>
			bus_idle	<= '1';
			if i_bus_cmd_cycle_en = '1' then
				sn			<= icache_bus_access;
			elsif d_bus_cmd_cycle_en = '1' then
				sn			<= dcache_bus_access;
			end if;
		when icache_bus_access =>
			icache_bus_gnt		<= '1';
			i_bus_cmd_rdy		<= bus_cmd_rdy;
			bus_cmd_cycle_en	<= i_bus_cmd_cycle_en;
			bus_cmd_we			<= i_bus_cmd_we;
			bus_dout_re			<= i_bus_din_rdy;
			if i_bus_cmd_cycle_en = '0' then
				sn			<= ready;
			end if;	
		when dcache_bus_access =>
			dcache_bus_gnt		<= '1';
			d_bus_cmd_rdy		<= bus_cmd_rdy;
			bus_cmd_cycle_en	<= d_bus_cmd_cycle_en;
			bus_cmd				<= d_bus_cmd;	
			bus_cmd_we			<= d_bus_cmd_we;
			bus_dout_re			<= d_bus_din_rdy;
			if d_bus_cmd_cycle_en = '0' then
				sn			<= ready;
			end if;	
		when others =>
			sn	<= ready;
		end case;		
				
	end process;	

bus_timeout_counter:
	process(clk)
	begin
		if rising_edge(clk) then
			if bus_idle = '0' then
				if bus_timeout_cnt /= 0 then
					bus_timeout_cnt	<= bus_timeout_cnt - 1;
				else
					bus_timeout	<= '1';
				end if;	
			else
				bus_timeout_cnt	<= timeout_cnt_t'high;
				bus_timeout		<= '0';
			end if;
		end if;
	end process;	

bus_err:
	process(clk)
	begin
		if rising_edge(clk) then
			if RST_I = '1' then
				imem_err	<= '0';
				dmem_err	<= '0';
			elsif bus_timeout = '1' then
				imem_err	<= icache_bus_gnt;
				dmem_err	<= dcache_bus_gnt;
			end if;
		end if;
	end process;	

-------------------------------------------------------------------
	
end behavior;
