--------------------------------------------------------------------------
-- 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;

entity mips_top is
	Generic
	(
		icache_size	: natural := 8192;	-- words
		icache_line	: natural := 8;		-- words
		dcache_size	: natural := 4096;	-- words
		dcache_line	: natural := 8;		-- words
		WITH_TLB	: boolean := true
	);
	Port
	(
		debug		: out chip_debug_t;
		eb			: in  STD_LOGIC;
		nmi			: in  STD_LOGIC;
		cpu_clk		: in  STD_LOGIC;
		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);
		DAT_I		: in  unsigned(31 downto 0);
		DAT_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;
		INT			: in  unsigned (5 downto 0)
	);
	
end mips_top;

architecture rtl of mips_top is

--------------------------------------------------------------------------
-- Pipeline
--------------------------------------------------------------------------
	COMPONENT pipeline is
	Port 
	(
		rst			: in  STD_LOGIC;
		clk			: in  STD_LOGIC;
		ce			: in  STD_LOGIC;
		imem_err	: in  STD_LOGIC;
		imem_rdy	: in  STD_LOGIC;
		imem_en		: out STD_LOGIC;
		imem_addr	: out word_t;
		imem_data	: in  word_t;
		dmem_err	: in  STD_LOGIC;
		dmem_rdy	: in  STD_LOGIC;
		dmem_en		: out STD_LOGIC;
		dmem_we		: out STD_LOGIC;
		dmem_be		: out unsigned(3 downto 0);
		dmem_addr	: out word_t;
		dmem_din	: in  word_t;
		dmem_dout	: out word_t;
		cop_ir		: out word_t;
		cop_ir_en	: out STD_LOGIC;
		cop_din		: in  word_t;
		cop_dout	: out word_t;
		ctrl_out	: out pipe_ctrl_out_t;
		ctrl_in		: in  pipe_ctrl_in_t
	);
	END COMPONENT;
	signal imem_err			: std_logic;
	signal imem_rdy			: std_logic;
	signal imem_en			: std_logic;
	signal imem_addr		: word_t;
	signal imem_din			: word_t;
	signal dmem_err			: std_logic;
	signal dmem_rdy			: std_logic;
	signal dmem_en			: std_logic;
	signal dmem_we			: std_logic;
	signal dmem_addr		: word_t;
	signal dmem_dout		: word_t;
	signal dmem_din			: word_t;
	signal dmem_be			: unsigned(3 downto 0);

	signal biu_busy			: STD_LOGIC;
	signal biu_rst			: STD_LOGIC;
	signal cpu_rst			: STD_LOGIC;
	signal cpu_run			: STD_LOGIC;

	signal pipe_cop_ir		: word_t;
	signal pipe_cop_ir_en	: STD_LOGIC;
	signal pipe_cop_din		: word_t;
	signal pipe_cop_dout	: word_t;
	signal pipe_ctrl_in		: pipe_ctrl_in_t;
	signal pipe_ctrl_out	: pipe_ctrl_out_t;

	signal cop0_ctrl_in		: cop0_ctrl_in_t;
	signal cop0_ctrl_out	: cop0_ctrl_out_t;

	signal biu_ctrl_in		: biu_ctrl_in_t;
	signal biu_ctrl_out		: biu_ctrl_out_t;
	
--------------------------------------------------------------------------
-- Coprocessor
--------------------------------------------------------------------------
	COMPONENT cop is
	GENERIC
	(
		icache_size	: natural;
		icache_line	: natural;
		dcache_size	: natural;
		dcache_line	: natural
	);
	Port 
	(
		rst			: in  STD_LOGIC;
		clk			: in  STD_LOGIC;
		nmi			: in  STD_LOGIC;
		eb			: in  STD_LOGIC;
		int			: in  unsigned (5 downto 0);
		ir_en		: in  STD_LOGIC;
		ir			: in  word_t;
		ctrl_in		: in  cop0_ctrl_in_t;
		ctrl_out	: out cop0_ctrl_out_t;
		din			: in  word_t;
		dout		: out word_t
	);
	END COMPONENT;
	
--------------------------------------------------------------------------
-- Bus Interface Unit
--------------------------------------------------------------------------
	COMPONENT biu
	GENERIC
	(
		icache_size		: natural;
		icache_line		: natural;
		dcache_size		: natural;
		dcache_line		: natural;
		WRITE_FIFO_SIZE	: natural;
		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 COMPONENT;

begin

-------------------------------------------------------------------
	process(cpu_clk)
	variable reset_delay : unsigned (31 downto 0);
	begin
		if rising_edge(cpu_clk) then
			if RST_I = '1' then
				reset_delay := (others => '0');
				biu_rst	<= '1';
				cpu_rst <= '1';
				cpu_run	<= '0';
			else
				reset_delay := reset_delay(reset_delay'left-1 downto 0) & '1';
				if reset_delay(20) = '1' then
					biu_rst <= '0';
				end if;	
				if reset_delay(31) = '1' then
					cpu_rst <= '0';
				end if;	
				if reset_delay(16) = '1' and biu_busy = '0' then
					cpu_run	<= '1';
				end if;	
			end if;
		end if;
	end process;

inst_pipeline: pipeline
	PORT MAP
	(
		rst			=> cpu_rst,
		clk			=> cpu_clk,
		ce			=> cpu_run,
		imem_err	=> imem_err,
		imem_rdy	=> imem_rdy,
		imem_en		=> imem_en,
		imem_addr	=> imem_addr,
		imem_data	=> imem_din,
		dmem_err	=> dmem_err,
		dmem_rdy	=> dmem_rdy,
		dmem_en		=> dmem_en,
		dmem_we		=> dmem_we,
		dmem_be		=> dmem_be,
		dmem_addr	=> dmem_addr,
		dmem_din	=> dmem_din,
		dmem_dout	=> dmem_dout,
		cop_ir		=> pipe_cop_ir,
		cop_ir_en	=> pipe_cop_ir_en,
		cop_din		=> pipe_cop_din,
		cop_dout	=> pipe_cop_dout,
		ctrl_out	=> pipe_ctrl_out,
		ctrl_in		=> pipe_ctrl_in
	);

	pipe_ctrl_in		<= cop0_ctrl_out.pipe;

inst_cop: cop
	GENERIC MAP
	(
		icache_size	=> icache_size,	-- words
		icache_line	=> icache_line,	-- words
		dcache_size	=> dcache_size,	-- words
		dcache_line	=> dcache_line	-- words
	)
	PORT MAP
	(
		rst			=> cpu_rst,
		clk			=> cpu_clk,
		nmi			=> nmi,
		eb			=> eb,
		int			=> INT,
		ir_en		=> pipe_cop_ir_en,
		ir			=> pipe_cop_ir,
		ctrl_in		=> cop0_ctrl_in,
		ctrl_out	=> cop0_ctrl_out,
		dout		=> pipe_cop_din,
		din			=> pipe_cop_dout
	);

	cop0_ctrl_in.pipe	<= pipe_ctrl_out;
	cop0_ctrl_in.itlb	<= biu_ctrl_out.itlb;
	cop0_ctrl_in.dtlb	<= biu_ctrl_out.dtlb;

inst_biu: biu
	GENERIC MAP
	(
		icache_size		=> icache_size,	-- words
		icache_line		=> icache_line,	-- words
		dcache_size		=> dcache_size,	-- words
		dcache_line		=> dcache_line,	-- words
		WRITE_FIFO_SIZE	=> 4,
		WITH_TLB		=> WITH_TLB
	)
	PORT MAP
	(

		clk				=> cpu_clk,
		busy			=> biu_busy,
		
		imem_err		=> imem_err,
		imem_rdy		=> imem_rdy,
		imem_en			=> imem_en,
		imem_addr		=> imem_addr,
		imem_dout		=> imem_din,
		dmem_err		=> dmem_err,
		dmem_rdy		=> dmem_rdy,
		dmem_en			=> dmem_en,
		dmem_we			=> dmem_we,
		dmem_be			=> dmem_be,
		dmem_addr		=> dmem_addr,
		dmem_din		=> dmem_dout,
		dmem_dout		=> dmem_din,

		RST_I			=> biu_rst,
		CLK_I			=> CLK_I,
		ACK_I			=> ACK_I,
		SRDY_I			=> SRDY_I,
		ADDR_O			=> ADDR_O,
		MDAT_I			=> DAT_I,
		MDAT_O			=> DAT_O,
		WE_O			=> WE_O,
		SEL_O			=> SEL_O,
		CYC_O			=> CYC_O,
		STB_O			=> STB_O,
		MRDY_O			=> MRDY_O,

		ctrl_in			=> biu_ctrl_in,
		ctrl_out		=> biu_ctrl_out

	);

	biu_ctrl_in.dcache	<= cop0_ctrl_out.dcache;
	biu_ctrl_in.icache	<= cop0_ctrl_out.icache;
	biu_ctrl_in.dtlb	<= cop0_ctrl_out.dtlb;
	biu_ctrl_in.itlb	<= cop0_ctrl_out.itlb;

-------------------------------------------
-- debug
-------------------------------------------
	debug.imem_err	<= imem_err;
	debug.dmem_err	<= dmem_err;

debug_imem_din_register:
	process(cpu_clk)
	variable en_r: std_logic;
	variable addr_r: word_t;
	begin
		if rising_edge(cpu_clk) then
			debug.imem_din_vld	<= '0';
			if imem_rdy = '1' then
				if en_r = '1' then
					debug.imem_addr_reg	<= addr_r;
					debug.imem_din_reg	<= imem_din;
					debug.imem_din_vld	<= '1';
				end if;
				if imem_en = '1' then
					addr_r	:= imem_addr;
				end if;
				en_r	:= imem_en;
			end if;
		end if;
	end process;	

debug_dmem_din_register:
	process(cpu_clk)
	variable en_r: std_logic;
	variable we_r: std_logic;
	variable addr_r: word_t;
	variable dout_r: word_t;
	begin
		if rising_edge(cpu_clk) then
			debug.dmem_din_vld	<= '0';
			debug.dmem_dout_vld	<= '0';
			if dmem_rdy = '1' then
				if en_r = '1' then
					debug.dmem_addr_reg	<= addr_r;
					debug.dmem_din_reg	<= dmem_din;
					debug.dmem_din_vld	<= not we_r;
					debug.dmem_dout_reg	<= dout_r;
					debug.dmem_dout_vld	<= we_r;
				end if;
				if dmem_en = '1' then
					dout_r	:= dmem_dout;
					addr_r	:= dmem_addr;
				end if;
				en_r	:= dmem_en;
				we_r	:= dmem_we;
			end if;
		end if;
	end process;	

end rtl;
