-------------------------------------------------------------------------
-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL
-- This file: testbench for system test using Xilinx ML-402

-- Copyright (C) 2007 J. Ahrensfeld

-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.

-- This library 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
-- Lesser General Public License for more details.

-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

-- 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;
use std.textio.all; --  Imports the standard textio package.

library work;
use work.mips_types.all;
use work.sdram_config.all;
use work.sdram_types.all;
use work.vga_types.all;

ENTITY tb_mips_sys IS
END tb_mips_sys;

ARCHITECTURE behavior OF tb_mips_sys IS 

	constant CLK_PERIOD			: time := 10 ns;
	constant AC97_CLK_PERIOD	: time := 83 ns;
	constant ETH_CLK_PERIOD		: time := 7.7 ns;
	constant LOOP_BACK_EN		: std_logic := '1';
	signal sys_rst_n_in : std_logic := '0';
	signal sys_clk_in : std_logic := '1';
	signal dip  : unsigned(7 downto 0) := (others => '0');
	signal btn  : unsigned(4 downto 0) := (others => '0');
	signal led : unsigned(8 downto 0);
	signal sys_rx : std_logic := '1';
	signal sys_tx : std_logic;
	signal sys_rx1 : std_logic := '1';
	signal sys_tx1 : std_logic;
	signal sys_rx2 : std_logic := '1';
	signal sys_tx2 : std_logic;
	
	signal sys_lcd_d			: unsigned(3 downto 0);
	signal sys_lcd_e			: std_logic;
	signal sys_lcd_rs			: std_logic;
	signal sys_lcd_rw			: std_logic;

	signal refresh        : boolean:= true;

	signal sys_sdr_clk_p		: std_logic;    -- ddr_sdram_clock
	signal sys_sdr_clk_n		: std_logic;    -- /ddr_sdram_clock
	signal sys_sdr_cke_q		: std_logic;    -- clock enable
	signal sys_sdr_cs_qn		: std_logic;    -- /chip select
	signal sys_sdr_ras_qn		: std_logic;    -- /ras
	signal sys_sdr_cas_qn		: std_logic;    -- /cas
	signal sys_sdr_we_qn		: std_logic;    -- /write enable
	signal sys_sdr_dm_q			: unsigned(PART_DM_WIDTH-1 downto 0);     -- data mask bits, set to "00"
	signal sys_sdr_dqs_q		: unsigned(PART_DQS_WIDTH-1 downto 0);    -- data strobe, only for write
	signal sys_sdr_ba_q			: unsigned(PART_BANK_WIDTH-1 downto 0);   -- bank select
	signal sys_sdr_a_q			: unsigned(PART_ADDR_WIDTH-1 downto 0);   -- address bus 
	signal sys_sdr_data			: unsigned(PART_DATA_WIDTH-1 downto 0); -- bidir data bus

	signal sys_error			: unsigned(1 downto 0); -- indicates DCM Errors
	signal sys_sdr_clk_fb		: std_logic;

	signal sys_usb_d			: unsigned(15 downto 0) := (others => '0');
	signal sys_usb_a			: unsigned(1 downto 0);
	signal sys_usb_csn			: std_logic;
	signal sys_usb_wrn			: std_logic;
	signal sys_usb_rdn			: std_logic;
	signal sys_usb_rstn			: std_logic;
	signal sys_usb_int			: std_logic := '0';
	
	signal sys_flash_ssram_d	: word_t;
	signal sys_flash_ssram_a	: unsigned(24 downto 0);
	signal sys_flash_ssram_we_n	: std_logic;
	signal sys_flash_ssram_oe_n	: std_logic;

	signal sys_flash_ce			: std_logic;
	signal sys_flash_ac97_rstn	: std_logic;
	signal sys_flash_byten		: std_logic;

	signal sys_ssram_clk_fb		: std_logic;
	signal sys_ssram_clk		: std_logic;
	signal sys_ssram_cke_n		: std_logic;
	signal sys_ssram_ce_n		: std_logic;
	signal sys_ssram_bw_n		: unsigned(3 downto 0);
	signal sys_ssram_dp			: unsigned(3 downto 0);
	signal sys_ssram_adv		: std_logic;
	signal sys_ssram_mode		: std_logic;
	signal sys_ssram_zz			: std_logic;

	SIGNAL sys_ac97_bit_clk		:  std_logic := '0';
	SIGNAL sys_ac97_sdata_out	:  std_logic;
	SIGNAL sys_ac97_ssync		:  std_logic;
	SIGNAL sys_ac97_sdata_in	:  std_logic;

	SIGNAL sys_ps2_clk			:  std_logic := 'H';
	SIGNAL sys_ps2_data			:  std_logic := 'H';

	SIGNAL sys_phy_rstn			:  std_logic;
	SIGNAL sys_phy_mdc			:  std_logic;
	SIGNAL sys_phy_mdio			:  std_logic := 'H';
	SIGNAL sys_phy_int			:  std_logic := '0';

	SIGNAL sys_phy_rx_clk		: STD_LOGIC := '0';
	SIGNAL sys_phy_rx_dv		: STD_LOGIC	:= '0';
	SIGNAL sys_phy_rx_er		: STD_LOGIC := '0';
	SIGNAL sys_phy_rx			: unsigned(7 downto 0) := (others => '0');
	SIGNAL sys_phy_tx_clk		: STD_LOGIC := '0';
	SIGNAL sys_phy_tx_en		: STD_LOGIC;
	SIGNAL sys_phy_tx_er		: STD_LOGIC;
	SIGNAL sys_phy_tx			: unsigned(7 downto 0);
	SIGNAL sys_phy_gtx_clk		: STD_LOGIC;
	SIGNAL sys_phy_crs			: STD_LOGIC := '0';
	SIGNAL sys_phy_col			: STD_LOGIC := '0';

	signal flash_reg			: word_t;
	signal eth_tx_reg			: unsigned(7 downto 0);
	
	type flash_data_t is array (natural range 0 to 2**16-1) of word_t;
	signal flash_data : flash_data_t;

	signal	ac_frame_reg		: unsigned(255 downto 0) := (others => '0');
	signal	ac_left				: unsigned(17 downto 0) := X"0000" & "00";
	signal	ac_right			: unsigned(17 downto 0) := X"0000" & "01";
	signal  ac_synced			:  std_logic;
	
BEGIN

uut: entity work.mips_sys
	GENERIC MAP
	(
		sys_freq_in			=> 100.000,
		cpu_freq			=> 100.000,
		tsvga				=> ts_vga_800_600_72
	)
	PORT MAP
	(
		sys_rst_n_in			=> sys_rst_n_in,
		sys_clk_in				=> sys_clk_in,
		sys_btn					=> btn,
		sys_dip					=> dip,
		sys_led					=> led,

		sys_uart0_rx			=> sys_rx,
		sys_uart0_tx			=> sys_tx,
		sys_uart1_rx			=> sys_rx1,
		sys_uart1_tx			=> sys_tx1,
		sys_uart2_rx			=> sys_rx2,
		sys_uart2_tx			=> sys_tx2,

		sys_lcd_d				=> sys_lcd_d,
		sys_lcd_e				=> sys_lcd_e,
		sys_lcd_rs				=> sys_lcd_rs,
		sys_lcd_rw				=> sys_lcd_rw,

		sys_sdr_clk_p			=> sys_sdr_clk_p,
		sys_sdr_clk_n			=> sys_sdr_clk_n,
		sys_sdr_cke_q			=> sys_sdr_cke_q,
		sys_sdr_clk_fb			=> sys_sdr_clk_fb,
		sys_sdr_cs_qn			=> sys_sdr_cs_qn,
		sys_sdr_ras_qn			=> sys_sdr_ras_qn,
		sys_sdr_cas_qn			=> sys_sdr_cas_qn,
		sys_sdr_we_qn			=> sys_sdr_we_qn,
		sys_sdr_dm_q			=> sys_sdr_dm_q,
		sys_sdr_dqs_q			=> sys_sdr_dqs_q,
		sys_sdr_ba_q			=> sys_sdr_ba_q,
		sys_sdr_a_q				=> sys_sdr_a_q,
		sys_sdr_data			=> sys_sdr_data,

		sys_usb_d				=> sys_usb_d,
		sys_usb_a				=> sys_usb_a,
		sys_usb_csn				=> sys_usb_csn,
		sys_usb_wrn				=> sys_usb_wrn,
		sys_usb_rdn				=> sys_usb_rdn,
		sys_usb_rstn			=> sys_usb_rstn,
		sys_usb_int				=> sys_usb_int,

		sys_flash_ssram_d		=> sys_flash_ssram_d,
		sys_flash_ssram_a		=> sys_flash_ssram_a,
		sys_flash_ssram_we_n	=> sys_flash_ssram_we_n,
		sys_flash_ssram_oe_n	=> sys_flash_ssram_oe_n,
		sys_flash_ce			=> sys_flash_ce,
		sys_flash_ac97_rstn		=> sys_flash_ac97_rstn,
		sys_flash_byten			=> sys_flash_byten,

		sys_ssram_clk_fb		=> sys_ssram_clk_fb,
		sys_ssram_clk			=> sys_ssram_clk,
		sys_ssram_cke_n			=> sys_ssram_cke_n,
		sys_ssram_ce_n			=> sys_ssram_ce_n,
		sys_ssram_bw_n			=> sys_ssram_bw_n,
		sys_ssram_dp			=> sys_ssram_dp,
		sys_ssram_adv			=> sys_ssram_adv,
		sys_ssram_mode			=> sys_ssram_mode,
		sys_ssram_zz			=> sys_ssram_zz,

		sys_ac97_bit_clk		=> sys_ac97_bit_clk,
		sys_ac97_sdata_out		=> sys_ac97_sdata_out,
		sys_ac97_ssync			=> sys_ac97_ssync,
		sys_ac97_sdata_in		=> sys_ac97_sdata_in,

		sys_ps2_clk				=> sys_ps2_clk,
		sys_ps2_data			=> sys_ps2_data,
		
		sys_phy_rstn			=> sys_phy_rstn,
		sys_phy_mdc				=> sys_phy_mdc,
		sys_phy_mdio			=> sys_phy_mdio,
		sys_phy_int				=> sys_phy_int,

		sys_phy_rx_clk			=> sys_phy_rx_clk,
		sys_phy_rx_dv			=> sys_phy_rx_dv,
		sys_phy_rx_er			=> sys_phy_rx_er,
		sys_phy_rx				=> sys_phy_rx,
		sys_phy_tx_clk			=> sys_phy_tx_clk,
		sys_phy_tx_en			=> sys_phy_tx_en,
		sys_phy_tx_er			=> sys_phy_tx_er,
		sys_phy_tx				=> sys_phy_tx,
		sys_phy_gtx_clk			=> sys_phy_gtx_clk,
		sys_phy_crs				=> sys_phy_crs,
		sys_phy_col				=> sys_phy_col,

		sys_error 				=> sys_error
	);

	sys_phy_rx		<= sys_phy_tx when LOOP_BACK_EN = '1' else X"00";
	sys_phy_rx_dv	<= sys_phy_tx_en when LOOP_BACK_EN = '1' else '0';
	sys_phy_rx_er	<= sys_phy_tx_er when LOOP_BACK_EN = '1' else '0';
	sys_phy_crs		<= '0';
	sys_phy_col		<= '0';

inst_ssram : entity work.cy7c1354
    PORT MAP 
    (
		Dq		=> STD_LOGIC_VECTOR(sys_flash_ssram_d),   	-- Data I/O
		Dpq		=> STD_LOGIC_VECTOR(sys_ssram_dp),   	-- Data I/O
		Addr	=> STD_LOGIC_VECTOR(sys_flash_ssram_a(18 downto 1)),   	-- Address
		Mode	=> sys_ssram_mode, 	-- Burst Mode
		Clk		=> sys_ssram_clk,      -- Clk
		CEN_n	=> sys_ssram_cke_n,                                   -- CEN#
		AdvLd_n	=> sys_ssram_adv,                                   -- Adv/Ld#
		Bwa_n	=> sys_ssram_bw_n(0),                                   -- Bwa#
		Bwb_n	=> sys_ssram_bw_n(1),                                   -- BWb#
		Bwc_n	=> sys_ssram_bw_n(2),                                   -- Bwc#
		Bwd_n	=> sys_ssram_bw_n(3),                                   -- BWd#
		Rw_n	=> sys_flash_ssram_we_n,                                   -- RW#
		Oe_n	=> sys_flash_ssram_oe_n,                                   -- OE#
		Ce1_n	=> sys_ssram_ce_n,                                   -- CE1#
		Ce2		=> '1',                                   -- CE2
		Ce3_n	=> '0',                                   -- CE3#
		Zz		=> sys_ssram_zz
    );
    
-- MICRON DDR SDRAM Simulation Model
i_mt46v16m16_0 : entity work.mt46v16m16 
      port map (
         unsigned(dq)    => std_logic_vector(sys_sdr_data(15 downto 0)),
         unsigned(dqs)   => std_logic_vector(sys_sdr_dqs_q(1 downto 0)),
         addr  => std_logic_vector(sys_sdr_a_q),
         ba    => std_logic_vector(sys_sdr_ba_q),
         clk   => sys_sdr_clk_p,
         clk_n => sys_sdr_clk_n,
         cke   => sys_sdr_cke_q,
         cs_n  => sys_sdr_cs_qn,
         ras_n => sys_sdr_ras_qn,
         cas_n => sys_sdr_cas_qn,
         we_n  => sys_sdr_we_qn,
         dm    => std_logic_vector(sys_sdr_dm_q(1 downto 0))
         );

-- MICRON DDR SDRAM Simulation Model
i_mt46v16m16_1 : entity work.mt46v16m16 
      port map (
         unsigned(dq)    => std_logic_vector(sys_sdr_data(31 downto 16)),
         unsigned(dqs)   => std_logic_vector(sys_sdr_dqs_q(3 downto 2)),
         addr  => std_logic_vector(sys_sdr_a_q),
         ba    => std_logic_vector(sys_sdr_ba_q),
         clk   => sys_sdr_clk_p,
         clk_n => sys_sdr_clk_n,
         cke   => sys_sdr_cke_q,
         cs_n  => sys_sdr_cs_qn,
         ras_n => sys_sdr_ras_qn,
         cas_n => sys_sdr_cas_qn,
         we_n  => sys_sdr_we_qn,
         dm    => std_logic_vector(sys_sdr_dm_q(3 downto 2))
         );

	sys_sdr_clk_fb		<= sys_sdr_clk_p after 1300 ps;
	sys_ssram_clk_fb	<= sys_ssram_clk after 0.5 ns;
	
	refresh <= true when falling_edge(sys_sdr_ras_qn) and falling_edge(sys_sdr_cas_qn) and sys_sdr_we_qn='1' else false;

	sys_usb_d	<= X"1234" after 20 ns when sys_usb_csn = '0' and sys_usb_rdn = '0' else (others => 'Z') after 5 ns;
	
CLK_GEN: process
	begin
		wait for CLK_PERIOD/2;
		sys_clk_in <= not sys_clk_in;
	end process;

ETH_CLK_GEN: process
	begin
		wait for ETH_CLK_PERIOD/2;
		sys_phy_rx_clk <= not sys_phy_rx_clk;
		sys_phy_tx_clk <= not sys_phy_tx_clk;
	end process;

ETH_TX_REGISTER: 
	PROCESS(sys_phy_tx_clk)
	begin
		if rising_edge(sys_phy_tx_clk) then
			if (sys_phy_tx_en = '1') then
				eth_tx_reg <= sys_phy_tx;
			end if;
		end if;
	end process;

	sys_ac97_sdata_in <= ac_frame_reg(ac_frame_reg'left); --sys_ac97_sdata_out;

ac97_data_gen :
	PROCESS(sys_ac97_bit_clk)
	begin
		if rising_edge(sys_ac97_bit_clk) then
			ac_frame_reg <= ac_frame_reg(ac_frame_reg'left-1 downto 0) & '0';
			if sys_flash_ac97_rstn = '0' then
				ac_synced	<= '0';
			elsif sys_ac97_ssync = '1' then
				if ac_synced = '0' then
					ac_frame_reg <= X"F800_00000_00000" & ac_left & "00" & ac_right & "00" & X"00000_00000_00000_00000_00000_00000_00000_00000";
					ac_left <= ac_left + 2;
					ac_right <= ac_right + 2;
					ac_synced	<= '1';
				end if;	
			else	
				ac_synced	<= '0';
			end if;
		end if;	
	END PROCESS;

ac97_clk_gen : PROCESS
	begin
		if (sys_flash_ac97_rstn = '0') then
			sys_ac97_bit_clk <= '0';
			wait until sys_flash_ac97_rstn = '1';
			wait for 150 us;
		end if;	
		wait for AC97_CLK_PERIOD/2;
		sys_ac97_bit_clk <= not sys_ac97_bit_clk;
	END PROCESS;

FLASH_READ: process(sys_rst_n_in, sys_flash_ce, sys_flash_ssram_oe_n, sys_flash_ssram_a)
	type file_t is file of integer;
	file load_flash : file_t open read_mode is "hello_sim.elf.flash.bin";
	variable instr : integer;
	variable index : natural;
	variable temp : signed(31 downto 0);
	begin
		if sys_rst_n_in = '0' 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
	 		sys_flash_ssram_d <= (others => 'Z') after 10 ns;
			if sys_flash_ssram_oe_n = '0' and sys_flash_ce = '1' then
				index := to_integer(sys_flash_ssram_a(24 downto 1));
				sys_flash_ssram_d <= flash_data(index) after 10 ns;
			end if;	
		end if;	
	end process;

FLASH_WRITE: process(sys_flash_ce, sys_flash_ssram_we_n, sys_flash_ssram_d)
	begin
		if rising_edge(sys_flash_ssram_we_n) and sys_flash_ce = '1' then
			flash_reg <= sys_flash_ssram_d;
		end if;
	end process;
		
STIMULUS: process

	begin

		dip(7) <= '1';			-- EB
		btn(0) <= '1';
		btn(2) <= '1';
		wait for 3*CLK_PERIOD;
		sys_rst_n_in <= '1';
		btn(0) <= '0';
		btn(2) <= '0';
		wait for 20000000*CLK_PERIOD;
		
		loop
		wait for 251*CLK_PERIOD;
		btn(4)		<= '1';			
		wait for 311*CLK_PERIOD;
		btn(4)		<= '0';			
		end loop;
		wait; 
	
	end process;
	
END;
