-------------------------------------------------------------------------
-- 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.sdram_types.all;
use work.sdram_config.all;

ENTITY mips_sys IS
	GENERIC 
	(
		ICACHE_SIZE			: natural	:= 4096;		-- words
		DCACHE_SIZE			: natural	:= 4096;		-- words
		F_CLK_BRD			: real		:= 50.0;
		F_CLK_SYS			: real		:= 64.0
	);
	PORT
	(
		sys_nmi_n			: in  std_logic;
		rst_brd_n			: in  std_logic;
		clk_brd				: in  std_logic;
		
		-- SDRAM signals
		sys_sdram_clk		: out  STD_LOGIC;
		sys_sdram_cke		: out  STD_LOGIC;
		sys_sdram_cs_n		: out  STD_LOGIC;
		sys_sdram_cas_n		: out  STD_LOGIC;
		sys_sdram_ras_n		: out  STD_LOGIC;
		sys_sdram_we_n		: out  STD_LOGIC;
		sys_sdram_addr		: out  sdr_addr_t;
		sys_sdram_ba		: out  sdr_ba_t;
		sys_sdram_dqm		: out unsigned(PART_DM_WIDTH-1 downto 0);
		sys_sdram_data		: inout unsigned(PART_DATA_WIDTH-1 downto 0);

		spi_ss_n			: out std_logic;
		spi_clk				: out std_logic;
		spi_miso			: in std_logic;
		spi_mosi			: out std_logic;
		
		uart_rx				: in unsigned(1 downto 0);
		uart_tx				: out unsigned(1 downto 0);
		dip_sw				: in unsigned(3 downto 0);
		sys_leds			: out unsigned(7 downto 0)
	);
END mips_sys;

ARCHITECTURE behavior OF mips_sys IS 

	signal cpu_debug		: unsigned(1 downto 0);
	signal INT				: unsigned (5 downto 0) := (others => '0');
	
	-- J-BUS Master
	signal ACK_I			: STD_LOGIC;
	signal SRDY_I			: STD_LOGIC;
	signal ADDR_O			: unsigned(31 downto 0);
	signal DAT_I			: unsigned(31 downto 0);
	signal DAT_O			: unsigned(31 downto 0);
	signal WE_O				: STD_LOGIC;
	signal SEL_O			: unsigned(3 downto 0);
	signal CYC_O			: STD_LOGIC;
	signal STB_O			: STD_LOGIC;
	signal MRDY_O			: STD_LOGIC;
	
	-- J-BUS Slaves
	signal CYC_I_rom		: std_logic;
	signal ACK_O_rom		: std_logic;
	signal SRDY_O_rom		: std_logic;
	signal DAT_O_rom		: unsigned(31 downto 0);

	signal CYC_I_ram		: std_logic;
	signal ACK_O_ram		: std_logic;
	signal SRDY_O_ram		: std_logic;
	signal DAT_O_ram		: unsigned(31 downto 0);

	signal CYC_I_gpio		: std_logic;
	signal ACK_O_gpio		: std_logic;
	signal SRDY_O_gpio		: std_logic;
	signal DAT_O_gpio		: unsigned(31 downto 0);

	signal CYC_I_uart0		: std_logic;
	signal ACK_O_uart0		: std_logic;
	signal SRDY_O_uart0		: std_logic;
	signal DAT_O_uart0		: unsigned(31 downto 0);

	signal CYC_I_uart1		: std_logic;
	signal ACK_O_uart1		: std_logic;
	signal SRDY_O_uart1		: std_logic;
	signal DAT_O_uart1		: unsigned(31 downto 0);

	signal CYC_I_flash		: std_logic;
	signal ACK_O_flash		: std_logic;
	signal SRDY_O_flash		: std_logic;
	signal DAT_O_flash		: unsigned(31 downto 0);

	signal spi_ss			: std_LOGIC;
	signal cpu_tx_data		: unsigned(7 downto 0);
	signal cpu_tx_data_vld	: std_logic;

	signal int_timer		: std_logic;
	signal int_uart0		: std_logic;
	signal int_uart1		: std_logic;
	signal int_flash		: std_logic;

	type mem_area_t is (mem_dead, mem_flash, mem_ram, mem_rom, mem_gpio, mem_uart0, mem_uart1);
	signal mem_area : mem_area_t;

	signal gpo0				: unsigned(31 downto 0);
	signal gpi0				: unsigned(31 downto 0) := (others => '0');

	signal sys_nmi			: STD_LOGIC;
	signal sys_rst			: STD_LOGIC;
	signal sys_clk			: STD_LOGIC;
	signal rst_brd			: STD_LOGIC;
	
	signal cpu_rst			: STD_LOGIC := '1';		-- pre-init for POR
	signal cpu_rst_count	: unsigned(24 downto 0) := (others => '1');		-- pre-init for POR
	signal sdram_clk		: STD_LOGIC;
	
BEGIN
	rst_brd				<= not rst_brd_n;
	sys_nmi				<= not sys_nmi_n;
	sys_leds			<= cpu_debug & gpo0(5 downto 0);
	spi_ss_n			<= not spi_ss;
	gpi0(11 downto 8)	<= dip_sw;
	sys_sdram_clk		<= sdram_clk;
	
inst_sys_clk : entity work.sdram_clk
	GENERIC MAP
	(
		F_SDRCLK_IN				=> F_CLK_BRD,
		F_SDRCLK_OUT			=> F_CLK_SYS,
		clk0_out_phaseshift_ps	=> 0,
		clk1_out_phaseshift_ps	=> 3000
	)
	PORT MAP 
	(
		-- Clocks and Reset
		rst				=> rst_brd,   -- external async reset, low active
		clk_in			=> clk_brd,   -- system clock (e.g. 100MHz), from board
		clk_fb_in		=> sys_clk,   -- feedback clock
		clk0_0_out		=> sys_clk,  -- System clock #0, dcm#0 output 0°
		clk1_0_out		=> open,  -- System clock #1 (e.g. clock for DDR-SDRAM data capture), dcm#1 output 0°
		rst0_0_out		=> sys_rst  -- Reset out
	);

cpu_reset:
	process(sys_clk)
	begin
		if rising_edge(sys_clk) then
			if sys_rst = '1' then
				cpu_rst         <= '1';
				cpu_rst_count <= (others => '1');
			elsif (cpu_rst_count /= 0) then
				cpu_rst_count <= cpu_rst_count - 1;
			else
				cpu_rst <= '0';
			end if;
		end if;
	end process;		

------------------------------------------------------------------
-- Memory mux
------------------------------------------------------------------
mem_mux:
	process(ADDR_O)
	begin
		mem_area	<= mem_dead;
		if ADDR_O(31 downto 28) = X"0" then
			mem_area	<= mem_flash;
		elsif ADDR_O(31 downto 28) = X"A" then
			if ADDR_O(27 downto 26) = "00" then
				if ADDR_O(18 downto 16) = "000" then
					mem_area	<= mem_gpio;
				elsif ADDR_O(18 downto 16) = "001" then
					mem_area	<= mem_uart0;
				elsif ADDR_O(18 downto 16) = "011" then
					mem_area	<= mem_uart1;
				end if;	
			elsif ADDR_O(27 downto 26) = "01" then
				mem_area	<= mem_flash;
			end if;	
		elsif (ADDR_O(31 downto 28) = X"B" and ADDR_O(15) = '0') then
			mem_area	<= mem_rom;
		elsif (ADDR_O(31 downto 28) = X"8" or ADDR_O(30) = '1') then
			mem_area	<= mem_ram;
		end if;
	end process;

signal_mux:
	process(mem_area, CYC_O)
	begin
	
		CYC_I_gpio		<= '0';
		CYC_I_uart0		<= '0';
		CYC_I_uart1		<= '0';
		CYC_I_flash		<= '0';
		CYC_I_rom		<= '0';
		CYC_I_ram		<= '0';
		
		case mem_area is

		when mem_gpio =>
			CYC_I_gpio		<= CYC_O;

		when mem_uart0 =>
			CYC_I_uart0		<= CYC_O;

		when mem_uart1 =>
			CYC_I_uart1		<= CYC_O;

		when mem_flash =>
			CYC_I_flash		<= CYC_O;

		when mem_rom =>
			CYC_I_rom		<= CYC_O;

		when mem_ram =>
			CYC_I_ram		<= CYC_O;
		
		when others => null;
		
		end case;
		
	end process;

 	SRDY_I		<=	SRDY_O_ram or SRDY_O_rom or SRDY_O_uart0 or SRDY_O_uart1 or SRDY_O_flash or SRDY_O_gpio;
 	ACK_I		<=	ACK_O_ram or ACK_O_rom or ACK_O_uart0 or ACK_O_uart1 or ACK_O_flash or ACK_O_gpio;
 	DAT_I		<=	DAT_O_ram when CYC_I_ram = '1' else
					DAT_O_rom when CYC_I_rom = '1' else
					DAT_O_uart0 when CYC_I_uart0 = '1' else
					DAT_O_uart1 when CYC_I_uart1 = '1' else
					DAT_O_flash when CYC_I_flash = '1' else
					DAT_O_gpio when CYC_I_gpio = '1' else X"DEADBEEF";

------------------------------------------------------------------
inst_mips_top: entity work.mips_top
	GENERIC MAP
	(
		icache_size	=> ICACHE_SIZE,		-- words
		dcache_size	=> DCACHE_SIZE		-- words
	)
	PORT MAP
	(
		debug		=> cpu_debug,
		nmi			=> sys_nmi,
		eb			=> '1',
		cpu_clk		=> sys_clk,
		RST_I		=> cpu_rst,
		CLK_I		=> sys_clk,
		ACK_I		=> ACK_I,
		SRDY_I		=> SRDY_I,
		ADDR_O		=> ADDR_O,
		DAT_I		=> DAT_I,
		DAT_O		=> DAT_O,
		WE_O		=> WE_O,
		SEL_O		=> SEL_O,
		CYC_O		=> CYC_O,
		STB_O		=> STB_O,
		MRDY_O		=> MRDY_O,
		INT			=> INT
	);

	INT	<= "00" & int_flash & int_uart1 & int_uart0 & int_timer;
	
inst_rom : entity work.rom_wb
	PORT MAP
	(
		CLK_I		=> sys_clk,
		RST_I		=> sys_rst,
		CYC_I		=> CYC_I_rom,
		STB_I		=> STB_O,
		WE_I		=> WE_O,
		ACK_O		=> ACK_O_rom,
		MRDY_I		=> MRDY_O,
		SRDY_O		=> SRDY_O_rom,
		ADDR_I		=> ADDR_O,
		DAT_O		=> DAT_O_rom
	);

inst_gpio : entity work.gpio_wb
	GENERIC MAP
	(
		f_sysclk 	=> F_CLK_SYS
	)
	PORT MAP
	(
		CLK_I		=> sys_clk,
		RST_I		=> sys_rst,
		CYC_I		=> CYC_I_gpio,
		STB_I		=> STB_O,
		SEL_I		=> SEL_O,
		WE_I		=> WE_O,
		ACK_O		=> ACK_O_gpio,
		SRDY_O		=> SRDY_O_gpio,
		MRDY_I		=> MRDY_O,
		ADDR_I		=> ADDR_O,
		DAT_I		=> DAT_O,
		DAT_O		=> DAT_O_gpio,
		INT_TIM_O	=> int_timer,
		sys_gpi_0	=> gpi0,
		sys_gpo_0	=> gpo0
	);

inst_uart0 : entity work.uart_wb
	GENERIC MAP 
	(
		f_sysclk			=> F_CLK_SYS,
		baudrate_default	=> 460800.0,
		fifo_depth_bits		=> 4
	)
	PORT MAP
	(
		CLK_I		=> sys_clk,
		RST_I		=> sys_rst,
		CYC_I		=> CYC_I_uart0,
		STB_I		=> STB_O,
		SEL_I		=> SEL_O,
		WE_I		=> WE_O,
		ACK_O		=> ACK_O_uart0,
		SRDY_O		=> SRDY_O_uart0,
		MRDY_I		=> MRDY_O,
		ADDR_I		=> ADDR_O,
		DAT_I		=> DAT_O,
		DAT_O		=> DAT_O_uart0,
		INT_O		=> int_uart0,
		ser_rx		=> uart_rx(0),
		ser_tx		=> uart_tx(0)
	);

inst_uart1 : entity work.uart_wb
	GENERIC MAP 
	(
		f_sysclk			=> F_CLK_SYS,
		baudrate_default	=> 460800.0,
		fifo_depth_bits		=> 4
	)
	PORT MAP
	(
		CLK_I		=> sys_clk,
		RST_I		=> sys_rst,
		CYC_I		=> CYC_I_uart1,
		STB_I		=> STB_O,
		SEL_I		=> SEL_O,
		WE_I		=> WE_O,
		ACK_O		=> ACK_O_uart1,
		SRDY_O		=> SRDY_O_uart1,
		MRDY_I		=> MRDY_O,
		ADDR_I		=> ADDR_O,
		DAT_I		=> DAT_O,
		DAT_O		=> DAT_O_uart1,
		INT_O		=> int_uart1,
		ser_rx		=> uart_rx(1),
		ser_tx		=> uart_tx(1)
	);

inst_spi_flash : entity work.spi_flash_wb
	GENERIC MAP
	(
		FLASH_ADDR_BITS	=> 24,
		CACHE_ADDR_BITS	=> 8
	)
	PORT MAP
	(
		CLK_I		=> sys_clk,
		RST_I		=> sys_rst,
		CYC_I		=> CYC_I_flash,
		STB_I		=> STB_O,
		SEL_I		=> SEL_O,
		WE_I		=> WE_O,
		ACK_O		=> ACK_O_flash,
		SRDY_O		=> SRDY_O_flash,
		MRDY_I		=> MRDY_O,
		ADDR_I		=> ADDR_O,
		DAT_I		=> DAT_O,
		DAT_O		=> DAT_O_flash,
		INT_O		=> int_flash,
		spi_hold	=> '0',
		spi_ss		=> spi_ss,
		spi_clk		=> spi_clk,
		spi_miso	=> spi_miso,
		spi_mosi	=> spi_mosi
	);
	
-- DDR SDRAM Controller Core
inst_ctrl_sdr_wb32 : entity work.ctrl_sdr_wb32
	GENERIC MAP
	(
		F_SYSCLK	 	=> F_CLK_SYS,
		F_SDRCLK_IN	 	=> F_CLK_BRD,
		F_SDRCLK	 	=> 100.000,
		FIFO_DEPTH		=> 4,
		TPD_BOARD_SIM	=> 3100 ps
	)
	PORT MAP
	(
		RST_I				=> sys_rst,
		CLK_I				=> sys_clk,
		SDRAM_CLK_I			=> clk_brd,
		SDRAM_CLK_FB_I		=> sdram_clk,
		
		CYC_I				=> CYC_I_ram,
		STB_I				=> STB_O,
		SEL_I				=> SEL_O,
		WE_I				=> WE_O,
		ACK_O				=> ACK_O_ram,
		SRDY_O				=> SRDY_O_ram,
		MRDY_I				=> MRDY_O,
		ADDR_I				=> ADDR_O,
		DAT_I				=> DAT_O,
		DAT_O				=> DAT_O_ram,
		
		-- SDRAM signals
		sd_clk				=> sdram_clk,
		sd_cke				=> sys_sdram_cke,
		sd_cs_n				=> sys_sdram_cs_n,
		sd_cas_n			=> sys_sdram_cas_n,
		sd_ras_n			=> sys_sdram_ras_n,
		sd_we_n				=> sys_sdram_we_n,
		sd_addr				=> sys_sdram_addr,
		sd_ba				=> sys_sdram_ba,
		sd_dqm				=> sys_sdram_dqm,
		sd_data				=> sys_sdram_data
		
	);
	
END;
