library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library design;
use design.all;

library ip;
use ip.wishbone_package.all;

entity bench_flashrom_wb is
end entity bench_flashrom_wb;

architecture RTL of bench_flashrom_wb is
	signal clk         : std_logic;
	signal rst         : std_logic;
	signal clr         : std_logic;
	signal wb_in       : wishbone_v3_slave_in;
	signal wb_out      : wishbone_v3_slave_out;
	signal spi_si      : std_logic;
	signal spi_so      : std_logic;
	signal spi_sck     : std_logic;
	signal spi_reset_n : std_logic;
	signal spi_cs_n    : std_logic;
	signal spi_wp_n    : std_logic;
	signal spi_busy    : std_logic;

	procedure waitclk is
	begin
		wait until rising_edge(clk);
	end procedure waitclk;
	procedure waitnclk(n : integer) is
	begin
		for i in 1 to n loop
			wait until rising_edge(clk);
		end loop;
	end procedure waitnclk;
	procedure strobe(signal s : out std_logic) is
	begin
		s <= '1';
		waitclk;
		s <= '0';
		waitclk;
	end procedure strobe;
begin
	DataFlash_inst : entity work.DataFlash
		generic map(
			flashmemory     => "devicemodels/memory.txt",
			Rapid_interface => true,
			fsck            => 66,
			DEVICE          => "AT45DB011D", --AT45DB011D
			Tsck            => 13.6 ns)
		port map(
			SI     => spi_si,
			CSB    => spi_cs_n,
			SCK    => spi_sck,
			WPB    => spi_wp_n,
			RESETB => spi_reset_n,
			SO     => spi_so,
			RDYBSY => spi_busy
		);

	flashrom_wb_inst : entity design.flashrom_wb
		port map(
			clk         => clk,
			rst         => rst,
			clr         => clr,
			wb_in       => wb_in,
			wb_out      => wb_out,
			spi_si      => spi_si,
			spi_so      => spi_so,
			spi_sck     => spi_sck,
			spi_reset_n => spi_reset_n,
			spi_cs_n    => spi_cs_n,
			spi_wp_n    => spi_wp_n
		);

	clock_driver : process
		constant period : time := 10 ns;
	begin
		clk <= '0';
		wait for period / 2;
		clk <= '1';
		wait for period / 2;
	end process clock_driver;

	test : process is
		constant DEL : integer := 2;
		
	begin
		rst       <= '1';
		wb_in.ADR <= (others => '0');
		wait for 10 ns * 2;
		wait until (rising_edge(clk));
		rst <= '0';
		wait for 10 ns * 2;
		wait until (rising_edge(clk));

		wb_in.ADR <= std_logic_vector(to_unsigned(0, 32));
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '0';
		wait until wb_out.ACK = '1';
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		waitnclk(DEL);

		wb_in.ADR <= std_logic_vector(to_unsigned(4, 32));
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '0';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		waitnclk(22); --waitnclk(22); -- Time after read to write needs to be longer

		wb_in.ADR <= std_logic_vector(to_unsigned(4, 32));
		wb_in.DAT <= x"deadbeef";
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '1';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		waitnclk(DEL);
		
		wb_in.ADR <= std_logic_vector(to_unsigned(8, 32));
		wb_in.DAT <= x"600dbee2";
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '1';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		waitnclk(DEL);

		wb_in.ADR <= std_logic_vector(to_unsigned(4, 32));
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '0';
		wait until wb_out.ACK = '1';
		waitclk;
		assert wb_out.DAT = x"deadbeef" report "Readback error @ 4 [1]" severity error;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		waitnclk(DEL);

		wb_in.ADR <= std_logic_vector(to_unsigned(256, 32));
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '0';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';		
		waitnclk(DEL);

		wb_in.ADR <= std_logic_vector(to_unsigned(256, 32));
		wb_in.DAT <= x"b000b1e5";
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '1';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		waitnclk(DEL);

		wb_in.ADR <= std_logic_vector(to_unsigned(256, 32));
		wb_in.DAT <= x"12345678";
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '0';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		assert wb_out.DAT = x"b000b1e5" report "Readback error @ 256" severity error;
		waitnclk(DEL);

		wb_in.ADR <= std_logic_vector(to_unsigned(4, 32));
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '0';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		assert wb_out.DAT = x"deadbeef" report "Readback error @ 4 [2]" severity error;
		waitnclk(DEL);
		
		wb_in.ADR <= std_logic_vector(to_unsigned(0, 32));
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '0';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		assert wb_out.DAT = x"00010203" report "Readback error @ 0 [2]" severity error;
		waitnclk(DEL);
		
		wb_in.ADR <= std_logic_vector(to_unsigned(8, 32));
		wb_in.CYC <= '1';
		wb_in.STB <= '1';
		wb_in.WE  <= '0';
		wait until wb_out.ACK = '1';
		waitclk;
		wb_in.CYC <= '0';
		wb_in.STB <= '0';
		assert wb_out.DAT = x"600dbee2" report "Readback error @ 8 [2]" severity error;
		waitnclk(DEL);
		
		wait for 10 us;
		report "--- TEST FINISHED ---" severity failure; 
		
		wait;
	end process test;

end architecture RTL;
