-------------------------------------------------------------------------
-- Project: JIPS, a portable 32-bit RISC CPU written in VHDL
-- This file: Testbench for JCPU
--            also writes 'opc.lst' for JASM-assembler
--
-- Copyright (C) 2007 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_instr.all;
use work.async_types.all;
use work.async_defs.all;

ENTITY tb_mips_sys IS
END tb_mips_sys;

ARCHITECTURE behavior OF tb_mips_sys IS 

	constant CLK_PERIOD 			: time := 10 ns;
	constant ICACHE_SIZE			: natural := 512;	-- words
	constant DCACHE_SIZE			: natural := 512;	-- words
	constant SRAM_ADDR_WIDTH		: integer := 17;	-- bits
	constant FLASH_ADDR_WIDTH		: integer := 17;	-- bits
	
	signal debug				: unsigned(1 downto 0);

	-- Master 
	signal nmi					: STD_LOGIC := '0';
	signal rst					: STD_LOGIC := '1';
	signal clk					: STD_LOGIC := '0';
	signal eb					: STD_LOGIC := '1';

	signal flash_cs_n			: std_logic;
	signal flash_we_n			: std_logic;
	signal flash_oe_n			: std_logic;
	signal flash_be_n			: unsigned(3 downto 0);
	signal sram_cs_n			: std_logic;
	signal sram_be_n			: unsigned(3 downto 0);
	signal sram_wr_n			: std_logic;
	signal sram_oe_n			: std_logic;
	signal sram_a				: unsigned(SRAM_ADDR_WIDTH-1 downto 0);
	signal sram_d				: unsigned(31 downto 0);
	signal flash_a				: unsigned(FLASH_ADDR_WIDTH-1 downto 0);
	signal flash_d				: unsigned(31 downto 0);
	
	signal gpo0					: unsigned(31 downto 0);
	signal gpo1					: unsigned(31 downto 0);
	signal gpi0					: unsigned(31 downto 0) := (others => '0');
	signal gpi1					: unsigned(31 downto 0) := (others => '0');

	signal int_timer			: std_logic;
	signal int_uart				: std_logic;
	signal rx					: std_logic := '1';
	signal tx					: std_logic;

	type word_array_t is array (natural range <>) of unsigned(31 downto 0);
	signal sram_data : word_array_t(0 to 2**SRAM_ADDR_WIDTH-1);
	signal flash_data : word_array_t(0 to 2**FLASH_ADDR_WIDTH-1);
	
BEGIN

------------------------------------------------------------------
CLK_GEN: process
	begin
		wait for CLK_PERIOD/2;
		clk <= not clk;
	end process;

------------------------------------------------------------------
uut : entity work.mips_sys
	GENERIC MAP 
	(
		ICACHE_SIZE			=> ICACHE_SIZE,	-- words
		DCACHE_SIZE			=> DCACHE_SIZE,	-- words
		SRAM_ADDR_WIDTH		=> SRAM_ADDR_WIDTH,
		FLASH_ADDR_WIDTH	=> FLASH_ADDR_WIDTH
	)
	PORT MAP
	(
		eb			=> eb,
		nmi			=> nmi,
		rst			=> rst,
		clk			=> clk,
		flash_cs_n	=> flash_cs_n,
		flash_oe_n	=> flash_oe_n,
		flash_we_n	=> flash_we_n,
		flash_be_n	=> flash_be_n,
		sram_cs_n	=> sram_cs_n,
		sram_we_n	=> sram_wr_n,
		sram_oe_n	=> sram_oe_n,
		sram_be_n	=> sram_be_n,
		sram_a		=> sram_a,
		sram_d		=> sram_d,
		flash_a		=> flash_a,
		flash_d		=> flash_d,
		
		gpo0		=> gpo0,
		gpo1		=> gpo1,
		gpi0		=> gpi0,
		gpi1		=> gpi1,

		rx			=> rx,
		tx			=> tx,

		debug		=> debug
	);

------------------------------------------------------------------
SRAM_READ:
	process(sram_a, sram_cs_n, sram_oe_n, sram_be_n)
	begin
		sram_d	<= (others => 'Z') after 10 ns;
		if sram_oe_n = '0' then
			if sram_cs_n = '0' then
				if sram_be_n(0) = '0' then
					sram_d(7 downto 0) <= sram_data(to_integer(sram_a(SRAM_ADDR_WIDTH-1 downto 2)))(7 downto 0) after 10 ns;
				end if;	
				if sram_be_n(1) = '0' then
					sram_d(15 downto 8) <= sram_data(to_integer(sram_a(SRAM_ADDR_WIDTH-1 downto 2)))(15 downto 8) after 10 ns;
				end if;	
				if sram_be_n(2) = '0' then
					sram_d(23 downto 16) <= sram_data(to_integer(sram_a(SRAM_ADDR_WIDTH-1 downto 2)))(23 downto 16) after 10 ns;
				end if;	
				if sram_be_n(3) = '0' then
					sram_d(31 downto 24) <= sram_data(to_integer(sram_a(SRAM_ADDR_WIDTH-1 downto 2)))(31 downto 24) after 10 ns;
				end if;	
			end if;				
		end if;	
	end process;

SRAM_WRITE:
	process(sram_wr_n)
	begin
		if rising_edge(sram_wr_n) then
			if sram_cs_n = '0' then
				if sram_be_n(0) = '0' then
					sram_data(to_integer(sram_a(SRAM_ADDR_WIDTH-1 downto 2)))(7 downto 0) <= sram_d(7 downto 0);
				end if;		
				if sram_be_n(1) = '0' then
					sram_data(to_integer(sram_a(SRAM_ADDR_WIDTH-1 downto 2)))(15 downto 8) <= sram_d(15 downto 8);
				end if;				
				if sram_be_n(2) = '0' then
					sram_data(to_integer(sram_a(SRAM_ADDR_WIDTH-1 downto 2)))(23 downto 16) <= sram_d(23 downto 16);
				end if;				
				if sram_be_n(3) = '0' then
					sram_data(to_integer(sram_a(SRAM_ADDR_WIDTH-1 downto 2)))(31 downto 24) <= sram_d(31 downto 24);
				end if;				
			end if;	
		end if;	
	end process;

------------------------------------------------------------------
FLASH_READ:
	process(rst, flash_cs_n, flash_oe_n, flash_a)
	type file_t is file of integer;
	file load_flash : file_t open read_mode is "hello.flash.bin";
	variable instr : integer;
	variable index : natural;
	variable temp : signed(31 downto 0);
	constant tpd : time := 25 ns;
	begin
		if rst = '1' then
			index := 0;
			while not endfile(load_flash) loop
				read(load_flash, instr);
				temp	:= to_signed(instr, word_t'length);
				flash_data(index) <= unsigned(temp);
				index := index + 1;
			end loop;
		else
	 		flash_d <= (others => 'Z') after tpd;
			index := to_integer(flash_a(FLASH_ADDR_WIDTH-1 downto 2));
			if flash_oe_n = '0' then
				if flash_cs_n = '0' then
					if flash_be_n(0) = '0' then
						flash_d(7 downto 0) <= flash_data(index)(7 downto 0) after tpd;
					end if;	
					if flash_be_n(1) = '0' then
						flash_d(15 downto 8) <= flash_data(index)(15 downto 8) after tpd;
					end if;	
					if flash_be_n(2) = '0' then
						flash_d(23 downto 16) <= flash_data(index)(23 downto 16) after tpd;
					end if;	
					if flash_be_n(3) = '0' then
						flash_d(31 downto 24) <= flash_data(index)(31 downto 24) after tpd;
					end if;	
				end if;				
			end if;	
		end if;	
	end process;

------------------------------------------------------------------
STIMULUS: process

	begin


		wait for 3*CLK_PERIOD;
		wait until rising_edge(clk);
		rst <= '0';
		wait for 778254*CLK_PERIOD;
		wait until rising_edge(clk);
		nmi <= '0';
		wait for 3000*CLK_PERIOD;
		wait until rising_edge(clk);
		nmi <= '0';

		wait; 
	
	end process;
	
END;
