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

library design;
use design.all;

entity bench_flashrom_spi is
end entity bench_flashrom_spi;

architecture rtl of bench_flashrom_spi is
	signal clk : std_logic;
	signal rst : 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;

	constant max_word_length : integer := 16;
	constant max_dummy_bits  : integer := 16;

	signal clr                 : std_logic;
	signal spi_si              : std_logic;
	signal spi_so              : std_logic := '0';
	signal spi_sck             : std_logic;
	signal spi_cs_n            : std_logic;
	signal data_in_valid       : std_logic;
	signal data_in             : std_logic_vector(max_word_length - 1 downto 0);
	signal data_next           : std_logic;
	signal data_out            : std_logic_vector(max_word_length - 1 downto 0);
	signal data_out_valid      : std_logic;
	signal data_in_length      : integer range 0 to max_word_length;
	signal data_out_length     : integer range 0 to max_word_length;
	signal data_out_dummy_bits : integer range 0 to max_dummy_bits;
	signal transmission_active : std_logic;

begin
	flashrom_spi_inst : entity design.flashrom_spi
		generic map(
			clk_divider     => 4,
			max_word_length => max_word_length,
			max_dummy_bits  => max_dummy_bits)
		port map(
			data_out_dummy_bits => data_out_dummy_bits,
			data_out_length     => data_out_length,
			clk                 => clk,
			rst                 => rst,
			clr                 => clr,
			spi_si              => spi_si,
			spi_so              => spi_so,
			spi_sck             => spi_sck,
			spi_cs_n            => spi_cs_n,
			data_in_valid       => data_in_valid,
			data_in             => data_in,
			data_next           => data_next,
			data_out            => data_out,
			data_out_valid      => data_out_valid,
			data_in_length      => data_in_length,
			transmission_active => transmission_active
		);

	-- clock driver
	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;

	--testbench
	bench : process is
	begin
		rst           <= '1';
		data_in       <= x"0000";
		data_in_valid <= '0';
		wait for 10 ns * 2;
		wait until (rising_edge(clk));
		rst <= '0';
		wait for 10 ns * 2;
		wait until (rising_edge(clk));

		-- bench code here
		data_out_dummy_bits <= 0;

		data_in_length  <= 8;
		data_out_length <= 8;
		data_in         <= x"FF00";
		data_in_valid   <= '1';
		wait until data_next = '1';

		data_in_length  <= 16;
		data_out_length <= 16;
		data_in         <= x"0055";
		data_in_valid   <= '1';
		wait until data_next = '1';

		data_in_valid <= '0';
		wait until transmission_active = '0';
		waitnclk(10);                   -- new SPI transaction

		data_out_dummy_bits <= 16;
		data_in_length      <= 16;
		data_out_length     <= 16;
		data_in             <= x"0055";
		data_in_valid       <= '1';
		wait until data_next = '1';
		wait until data_next = '1';     -- 32 bits total

		data_in_valid <= '0';

		wait;
	end process bench;

	spi_so_p : process is
		procedure spitx(constant value : std_logic) is
		begin
			spi_so <= value;
			wait until rising_edge(spi_sck);
		end procedure spitx;
	begin
		spi_so <= '0';
		wait until rising_edge(spi_sck);
		wait until rising_edge(spi_sck);

		-- 8 bit word
		spitx('1');
		for i in 1 to 7 loop
			spitx('0');
		end loop;

		-- 8 bit word
		spitx('0');
		spitx('1');
		for i in 1 to 6 loop
			spitx('0');
		end loop;

		-- 8 bit word
		spitx('0');
		spitx('0');
		spitx('1');
		for i in 1 to 5 loop
			spitx('0');
		end loop;

		wait until rising_edge(spi_sck);
		
		-- 0x80 (dummy)
		spitx('1');
		spitx('0');
		spitx('0');
		spitx('0');
		spitx('0');
		spitx('0');
		spitx('0');
		spitx('0');
		-- 0x88
		spitx('1');
		spitx('0');
		spitx('0');
		spitx('0');
		spitx('1');
		spitx('0');
		spitx('0');
		spitx('0');
		-- 0x44
		spitx('0');
		spitx('1');
		spitx('0');
		spitx('0');
		spitx('0');
		spitx('1');
		spitx('0');
		spitx('0');
		-- 0x22
		spitx('0');
		spitx('0');
		spitx('1');
		spitx('0');
		spitx('0');
		spitx('0');
		spitx('1');
		spitx('0');

		wait;
	end process spi_so_p;

end architecture rtl;
