-------------------------------------------------------------------------
-- 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 work.spi_types.all;

ENTITY tb_spi_master IS
END tb_spi_master;

ARCHITECTURE behavior OF tb_spi_master IS 

	constant CLK_PERIOD		: time := 20 ns;

	signal CLK				: std_logic := '1';
	signal RST				: std_logic := '1';

	signal mst_cmd_vld		: STD_LOGIC := '0';
	signal mst_cmd_rdy		: STD_LOGIC;
	signal mst_cmd			: cmd_t;
	signal mst_din_vld		: STD_LOGIC := '0';
	signal mst_din_rdy		: STD_LOGIC;
	signal mst_din			: unsigned(31 downto 0) := (others => '0');
	signal mst_dout			: unsigned(31 downto 0);
	signal mst_dout_vld		: STD_LOGIC;
	signal mst_dout_re		: STD_LOGIC := '0';
	signal mst_shift_en		: STD_LOGIC := '1';
	signal mosi				: STD_LOGIC;
	signal miso				: STD_LOGIC := '0';
	signal mst_clk			: STD_LOGIC;
	signal ctrl				: ctrl_t;
	signal status			: status_t;
	signal mso				: STD_LOGIC;
	signal mso_n			: STD_LOGIC;
	signal HOLDNeg			: STD_LOGIC := '1';
	signal WPNeg			: STD_LOGIC := '1';

	signal mst_rx_data			: unsigned(31 downto 0);
	signal slv_rx_shiftreg		: unsigned(31 downto 0) := (others => '0');
	signal slv_tx_shiftreg		: unsigned(31 downto 0) := (others => '0');
	signal slv_rx_valid_pipe	: unsigned(31 downto 0) := (others => '0');
	signal slv_rx_data			: unsigned(31 downto 0) := (others => '0');
	signal slv_rx_data_vld		: STD_LOGIC;
	
BEGIN

	ctrl.cpol			<= '0';
	ctrl.cpha			<= '0';
	ctrl.msb_first		<= '1';
	ctrl.clk_div		<= X"00";
	mso_n				<= not mso;
	HOLDNeg				<= '1';
	WPNeg				<= '1';
	
inst_spi_master : entity work.spi_master
	GENERIC MAP
	(
		word_width	=> 32
	)
	PORT MAP
	(
		rst			=> RST,
		clk			=> CLK,
		cmd_vld		=> mst_cmd_vld,
		cmd_rdy		=> mst_cmd_rdy,
		cmd			=> mst_cmd,
		din_vld		=> mst_din_vld,
		din_rdy		=> mst_din_rdy,
		din			=> mst_din,
		dout_re		=> mst_dout_re,
		dout_vld	=> mst_dout_vld,
		dout		=> mst_dout,
		shift_en	=> mst_shift_en,
		mosi		=> mosi,
		miso		=> miso,
		sclk		=> mst_clk,
		mso			=> mso,
		ctrl		=> ctrl,
		status		=> status
		
	);

inst_spi_slave: entity work.S25fl064k
    PORT MAP (
        SCK             => mst_clk, -- serial clock input
        SI              => mosi,    -- serial data input
        CSNeg           => mso_n,   -- chip select input
        HOLDNeg         => HOLDNeg, -- hold input
        WPNeg           => WPNeg,   -- write protect input
        SO              => miso     -- SO
    );

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

mst_dout_re_GEN: process
	begin
		wait for 10 us;
		mst_dout_re <= '1';
		wait for CLK_PERIOD;
		mst_dout_re <= '1';
	end process;


RX_SLAVE_SIM_SHIFT_REG: process(mst_clk)
	begin
		if rising_edge(mst_clk) then
			slv_rx_data_vld <= '0';
			if mso = '1' then
				if slv_rx_valid_pipe(slv_rx_valid_pipe'left) = '1' then
					slv_rx_valid_pipe <= (slv_rx_valid_pipe'left downto 1 => '0') & '1';
					slv_rx_data_vld <= '1';
				else
					slv_rx_valid_pipe	<= slv_rx_valid_pipe(slv_rx_valid_pipe'left-1 downto 0) & '1';
				end if;
				slv_rx_shiftreg		<= slv_rx_shiftreg(slv_rx_shiftreg'left-1 downto 0) & mosi;
			end if;
		end if;
	end process;

TX_SLAVE_SIM_SHIFT_REG: process(mst_clk)
	begin
		if falling_edge(mst_clk) then
			if slv_rx_data_vld = '1' then
				slv_tx_shiftreg		<= slv_rx_shiftreg;
			else
				slv_tx_shiftreg		<= slv_tx_shiftreg(slv_tx_shiftreg'left-1 downto 0) & '0';
			end if;
		end if;
	end process;

--	miso	<= slv_tx_shiftreg(slv_tx_shiftreg'left);
	
RX_SLAVE_SIM_DATA_REG: process(clk)
	variable data_vld : unsigned(1 downto 0);
	begin
		if mso = '0' then
			data_vld := "00";
		elsif rising_edge(clk) then
			data_vld(1)	:= data_vld(0);
			data_vld(0) := slv_rx_valid_pipe(slv_rx_valid_pipe'left);
			if data_vld(0) = '1' and data_vld(1) = '0' then
				slv_rx_data	<= slv_rx_shiftreg;
			end if;
		end if;
	end process;

MASTER_TX_DATA_REG: process(clk)
	begin
		if rising_edge(clk) then
			if mst_dout_vld = '1' then
				mst_rx_data	<= mst_dout;
			end if;
		end if;
	end process;

-- Master
STIMULUS: process


	begin

		wait for 600*CLK_PERIOD;
		RST <= '0';
		wait for 6*CLK_PERIOD;
		
		wait for 600*CLK_PERIOD;
		wait until rising_edge(CLK);
		mst_din			<= X"9F55AAC1";
		mst_din_vld		<= '1';
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din_vld		<= '0';

		wait until rising_edge(CLK);
		mst_cmd		<= to_cmd(32, 8);
		mst_cmd_vld	<= '1';
		wait until rising_edge(CLK) and mst_cmd_rdy = '1';
		mst_cmd_vld	<= '0';
		
--------------------------------------------------------
		wait until rising_edge(CLK);
		mst_din			<= X"9F00_0000";
		mst_din_vld		<= '1';
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din_vld		<= '0';

		wait until rising_edge(CLK);
		mst_cmd		<= to_cmd(32, 8);
		mst_cmd_vld	<= '1';
		wait until rising_edge(CLK) and mst_cmd_rdy = '1';
		mst_cmd_vld	<= '0';

--------------------------------------------------------
		wait until rising_edge(CLK);
		mst_din			<= X"9F00_0000";
		mst_din_vld		<= '1';
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din_vld		<= '0';

		wait until rising_edge(CLK);
		mst_cmd		<= to_cmd(656, 8);
		mst_cmd_vld	<= '1';
		wait until rising_edge(CLK) and mst_cmd_rdy = '1';
		mst_cmd_vld	<= '0';

--------------------------------------------------------
		wait until rising_edge(CLK);
		mst_din			<= X"030155C1";
		mst_din_vld		<= '1';
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din_vld		<= '0';

		wait until rising_edge(CLK);
		mst_cmd		<= to_cmd(64, 32);
		mst_cmd_vld	<= '1';
		wait until rising_edge(CLK) and mst_cmd_rdy = '1';
		mst_cmd_vld	<= '0';

		wait for 200*CLK_PERIOD;
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din			<= X"0311FF05";
		mst_din_vld		<= '1';
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din_vld		<= '0';

--------------------------------------------------------
		mst_din			<= X"03000000";
		for i in 0 to 16 loop
		
			wait until rising_edge(CLK);
			mst_din_vld		<= '1';
			wait until rising_edge(CLK) and mst_din_rdy = '1';
			mst_din_vld		<= '0';
			mst_din			<= mst_din + 1;

			wait until rising_edge(CLK);
			mst_cmd		<= to_cmd(64, 32);
			mst_cmd_vld	<= '1';
			wait until rising_edge(CLK) and mst_cmd_rdy = '1';
			mst_cmd_vld	<= '0';

			wait until rising_edge(CLK) and mso = '1';
			wait until rising_edge(CLK) and mso = '0';
		end loop;
		
--------------------------------------------------------
		wait until rising_edge(CLK);
		mst_din			<= X"0355AAC1";
		mst_din_vld		<= '1';
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din_vld		<= '0';

		wait until rising_edge(CLK);
		mst_cmd		<= to_cmd(64, 32);
		mst_cmd_vld	<= '1';
		wait until rising_edge(CLK) and mst_cmd_rdy = '1';
		mst_cmd_vld	<= '0';

--------------------------------------------------------
		wait until rising_edge(CLK);
		mst_din			<= X"0355AA00";
		mst_din_vld		<= '1';
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din_vld		<= '0';

		wait until rising_edge(CLK);
		mst_cmd		<= to_cmd(32, 32);
		mst_cmd_vld	<= '1';
		wait until rising_edge(CLK) and mst_cmd_rdy = '1';
		mst_cmd_vld	<= '0';

--------------------------------------------------------
		wait until rising_edge(CLK);
		mst_din			<= X"0311AAC1";
		mst_din_vld		<= '1';
		wait until rising_edge(CLK) and mst_din_rdy = '1';
		mst_din_vld		<= '0';

		wait until rising_edge(CLK);
		mst_cmd		<= to_cmd(24, 32);
		mst_cmd_vld	<= '1';
		wait until rising_edge(CLK) and mst_cmd_rdy = '1';
		mst_cmd_vld	<= '0';

		wait; 
	
	end process;
	
END;
