--
--   Copyright (C) 2003 by J. Kearney, Bolton, Massachusetts
--
--   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 2 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, write to the Free Software Foundation, Inc.,
-- 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
--

-- VHDL Test Bench Created from source file iob.vhd -- 18:26:00 12/23/2002
--
-- Notes: 
-- This testbench has been automatically generated using types std_logic and
-- std_logic_vector for the ports of the unit under test.  Xilinx recommends 
-- that these types always be used for the top-level I/O of a design in order 
-- to guarantee that the testbench will bind correctly to the post-implementation 
-- simulation model.
--
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;

use work.IOB_Config.ALL;
use work.Image_Pkg.ALL;


ENTITY testbench IS
END testbench;

ARCHITECTURE behavior OF testbench IS 

	constant SERBIT: time := 104.167 us;

	COMPONENT iob
	PORT(
		clk_in : IN std_logic;
		cpu_ioclr_n : IN std_logic;
		clk_write_n_raw : IN std_logic;
		cpu_read_n : IN std_logic;
		cpu_sr_read_n : IN std_logic;
		cpu_sr_write_n : IN std_logic;
		clk_lxdar_n_raw : IN std_logic;
		cpu_intgrnt_n : INOUT std_logic;
		rxd : IN std_logic_vector(0 to 2);
		lpt_ack : IN std_logic;
		lpt_busy_n : IN std_logic;
		lpt_paper_end_n : IN std_logic;
		lpt_select_in_n : IN std_logic;
		lpt_error : IN std_logic;    
		dx : INOUT std_logic_vector(0 to 11);
		lpt_data : INOUT std_logic_vector(7 downto 0);
		cpu_c0_n : OUT std_logic;
		cpu_c1_n : OUT std_logic;
		cpu_skip_n : OUT std_logic;
		cpu_intreq_n : OUT std_logic;
		txd : OUT std_logic_vector(0 to 2);
		lpt_strobe : OUT std_logic;
		lpt_ddir : OUT std_logic;
		lpt_init : OUT std_logic;
		kb_clk: inout std_logic;
		kb_data: inout std_logic;
		vga_RGB: out std_logic_vector(0 to 2);
		vga_HS: out std_logic;
		vga_VS: out std_logic;
		iobits: inout std_logic_vector(0 to 35);
		reprogram: inout std_logic;	-- drive low to restart FPGA configuration
		cpreq_n: inout std_logic
		);
	END COMPONENT;

	SIGNAL clk :  std_logic;
	SIGNAL cpu_ioclr_n :  std_logic;
	SIGNAL dx :  std_logic_vector(0 to 11);
	SIGNAL clk_write_n :  std_logic;
	SIGNAL cpu_read_n :  std_logic;
	SIGNAL cpu_sr_read_n :  std_logic;
	SIGNAL cpu_sr_write_n :  std_logic;
	SIGNAL clk_lxdar_n :  std_logic;
	SIGNAL cpu_c0_n :  std_logic;
	SIGNAL cpu_c1_n :  std_logic;
	SIGNAL cpu_skip_n :  std_logic;
	SIGNAL cpu_intreq_n :  std_logic;
	SIGNAL cpu_intgrnt_n :  std_logic;
	SIGNAL txd :  std_logic_vector(0 to 2);
	SIGNAL rxd :  std_logic_vector(0 to 2);
	SIGNAL lpt_ack :  std_logic;
	SIGNAL lpt_busy_n :  std_logic;
	SIGNAL lpt_paper_end_n :  std_logic;
	SIGNAL lpt_select_in_n :  std_logic;
	SIGNAL lpt_error :  std_logic;
	SIGNAL lpt_strobe :  std_logic;
	SIGNAL lpt_ddir :  std_logic;
	SIGNAL lpt_data :  std_logic_vector(7 downto 0);
	SIGNAL lpt_init :  std_logic;
	SIGNAL reprogram :  std_logic;
	SIGNAL kb_clk: std_logic;
	SIGNAL kb_data: std_logic;
	SIGNAL vga_RGB: std_logic_vector(0 to 2);
	SIGNAL vga_HS: std_logic;
	SIGNAL vga_VS: std_logic;
	SIGNAL iobits: std_logic_vector(0 to 35);
	SIGNAL cpreq_n: std_logic;


	-- send a bit from the simulated keyboard
	procedure kbbitout( v: in std_logic; signal clk, data: inout std_logic ) is
	begin
		data <= v;
		wait for 20 us;
		clk <= '0';
		wait for 40 us;
		clk <= '1';
		wait for 20 us;
	end kbbitout;

	-- receive a byte from the host
	procedure kbturn(signal clk, data: inout std_logic) is
	begin
		wait until clk /= '0';
		for b in 1 to 10 loop
			clk <= '1';
			wait for 20 us;
			clk <= '0';
			wait for 40 us;
			clk <= '1';
			wait for 20 us;
		end loop;
		data <= '0';
		wait for 20 us;
		clk <= '0';
		wait for 40 us;
		clk <= '1';
		wait for 20 us;
		data <= '1';
		wait for 20 us;
		data <= 'Z';
		clk <= 'Z';
	end kbturn;

	-- send a byte from the device
	procedure kbbyteout( v: in std_logic_vector(7 downto 0); signal clk, data: inout std_logic ) is
	variable parity: std_logic := '1';
	variable timeout: time;
	begin
		wait for 150 us;
		clk <= '1';
		kbbitout('0', clk, data);
		for n in 0 to 7 loop
			kbbitout(v(n), clk, data);
			parity := parity xor v(n);
		end loop;
		kbbitout(parity, clk, data);
		kbbitout('1', clk, data);
		data <= 'Z';
		clk <= 'Z';
	end kbbyteout;

	-- simulate the CPU doing an IOT cycle
	-- returns the data read values, or U when no read
	-- returns skip flag
	procedure IOT
		(cmd: in std_logic_vector(0 to 11);
		 data: in std_logic_vector(0 to 11);
		 rdata: out std_logic_vector(0 to 11);
		 rskip: out std_logic;
		 signal dx: inout std_logic_vector(0 to 11);
		 signal clk_lxdar_n: out std_logic;
		 signal clk_write_n: out std_logic;
		 signal cpu_read_n: out std_logic;
		 signal cpu_c0_n: in std_logic;
		 signal cpu_c1_n: in std_logic;
		 signal cpu_skip_n: in std_logic) is
	variable c0, c1: boolean;
	begin
		rdata := (others => 'U');
		rskip := 'U';

		dx <= cmd;
		wait for 125 ns;
		clk_lxdar_n <= '0';
		wait for 180 ns;
		dx <= (others => 'Z');
		wait for 20 ns;
		clk_write_n <= '0';
		wait for 175 ns;
		dx <= data;
		wait for 200 ns;
		-- sample c0/c1/skip at rising edge of write_n --
		rskip := not cpu_skip_n;
		c0 := cpu_c0_n = '0';
		c1 := cpu_c1_n = '0';
		clk_write_n <= '1';
		wait for 127 ns;
		dx <= (others => 'Z');
		wait for 200 ns;
		if c1 then	-- read cycle requested
			-- c0 means "OR with AC" but we don't use that here
			cpu_read_n <= '0';
			wait for 425 ns;
			rdata := dx;
			cpu_read_n <= '1';
			wait for 220 ns;
		else
			wait for 200 ns;
		end if;
		clk_lxdar_n <= '1';
		wait for 392 ns;
	end IOT;

	procedure receive_char
		( addr: in std_logic_vector(0 to 5);
		 signal dx: inout std_logic_vector(0 to 11);
		 signal clk_lxdar_n: out std_logic;
		 signal clk_write_n: out std_logic;
		 signal cpu_read_n: out std_logic;
		 signal cpu_c0_n: in std_logic;
		 signal cpu_c1_n: in std_logic;
		 signal cpu_skip_n: in std_logic) is
	variable rdata: std_logic_vector(0 to 11);
	variable rskip: std_logic;
	begin
		wait for 1 us;
		IOT(O"6" & addr & O"1", O"0000", rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
		if rskip = '1' then
			wait for 12 us;
			IOT(O"6" & addr & O"6", O"0000", rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
			REPORT "## Received char on dev " & OctImage(addr) & " = "  & Image(rdata) & " ##";
		end if;
	end receive_char;

	procedure send_char
		( addr: in std_logic_vector(0 to 5);
		  data: in std_logic_vector(0 to 11);
		 signal dx: inout std_logic_vector(0 to 11);
		 signal clk_lxdar_n: out std_logic;
		 signal clk_write_n: out std_logic;
		 signal cpu_read_n: out std_logic;
		 signal cpu_c0_n: in std_logic;
		 signal cpu_c1_n: in std_logic;
		 signal cpu_skip_n: in std_logic) is
	variable rdata: std_logic_vector(0 to 11);
	variable rskip: std_logic;
	variable timeout: time;
	begin
		-- wait for output ready
		timeout := now + 12 ms;	-- down to 110 baud
		loop
			IOT(O"6" & addr & O"1", O"0000", rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
			exit when (rskip = '1') or (now > timeout);
		end loop;

		if now > timeout then
			report "*** send_char timeout on dev " & OctImage(addr) & " ***";
		else
			wait for 12 us;
			IOT(O"6" & addr & O"6", data, rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
		end if;

	end send_char;

	procedure send_string
		( addr: in std_logic_vector(0 to 5);
		  s: string;
		 signal dx: inout std_logic_vector(0 to 11);
		 signal clk_lxdar_n: out std_logic;
		 signal clk_write_n: out std_logic;
		 signal cpu_read_n: out std_logic;
		 signal cpu_c0_n: in std_logic;
		 signal cpu_c1_n: in std_logic;
		 signal cpu_skip_n: in std_logic) is
	variable rdata: std_logic_vector(0 to 11);
	variable rskip: std_logic;
	variable timeout: time;
	begin
		for i in s'range loop
			send_char(addr, std_logic_vector(to_unsigned(Character'pos(s(i)), dx'length)), dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
		end loop;
	end send_string;


BEGIN

	uut: iob PORT MAP(
		clk_in => clk,
		cpu_ioclr_n => cpu_ioclr_n,
		dx => dx,
		clk_write_n_raw => clk_write_n,
		cpu_read_n => cpu_read_n,
		cpu_sr_read_n => cpu_sr_read_n,
		cpu_sr_write_n => cpu_sr_write_n,
		clk_lxdar_n_raw => clk_lxdar_n,
		cpu_c0_n => cpu_c0_n,
		cpu_c1_n => cpu_c1_n,
		cpu_skip_n => cpu_skip_n,
		cpu_intreq_n => cpu_intreq_n,
		cpu_intgrnt_n => cpu_intgrnt_n,
		txd => txd,
		rxd => rxd,
		lpt_ack => lpt_ack,
		lpt_busy_n => lpt_busy_n,
		lpt_paper_end_n => lpt_paper_end_n,
		lpt_select_in_n => lpt_select_in_n,
		lpt_error => lpt_error,
		lpt_strobe => lpt_strobe,
		lpt_ddir => lpt_ddir,
		lpt_data => lpt_data,
		lpt_init => lpt_init,
		kb_clk => kb_clk,
		kb_data => kb_data,
		vga_RGB => vga_RGB,
		vga_HS => vga_HS,
		vga_VS => vga_VS,
		iobits => iobits,
		cpreq_n => cpreq_n,
		reprogram => reprogram
	);



	clock_stimulus : process
	begin
		clk <= '1';
		wait for 16954.2100694448 ps;
		clk <= '0';
		wait for 16954.2100694448 ps;
	end process;


	keyboard_stimulus: process
	begin
		kb_data <= 'Z';
		kb_clk <= 'Z';

		-- we know that we will be sent the LED data, 2 bytes and then we ack it
		wait until kb_clk = '0';
		kbturn(kb_clk, kb_data);
		kbturn(kb_clk, kb_data);
		kbbyteout("11111010", kb_clk, kb_data); -- ack

		-- 'a' key
		kbbyteout("00011100", kb_clk, kb_data);

		-- F1 key (two code sequence)
		kbbyteout("00000101", kb_clk, kb_data);

		-- shift down
		kbbyteout("01011001", kb_clk, kb_data);
		-- 'A' key
		kbbyteout("00011100", kb_clk, kb_data);
		-- shift up
		kbbyteout("11110000", kb_clk, kb_data);
		kbbyteout("01011001", kb_clk, kb_data);

		-- up arrow (ext) -> (two code sequence)
		kbbyteout("11100000", kb_clk, kb_data);
		kbbyteout("01110101", kb_clk, kb_data);

		wait;
	end process;

	receive_stimulus: process
	begin
		-- transmit a couple of serial characters
		-- !!! expand to other ports, string to send
		rxd <= (others => '1');
		wait for 2 us;
		rxd(0) <= '0';
		wait for SERBIT;
		rxd(0) <= '1';
		wait for 3*SERBIT;
		rxd(0) <= '0';
		wait for 5*SERBIT;
		rxd(0) <= '1';
		wait for SERBIT;

		rxd <= (others => '1');
		wait for 2 us;
		rxd(0) <= '0';
		wait for SERBIT;
		rxd(0) <= '0';
		wait for 2*SERBIT;
		rxd(0) <= '1';
		wait for 3*SERBIT;
		rxd(0) <= '0';
		wait for 3*SERBIT;
		rxd(0) <= '1';
		wait for SERBIT;

		wait;
	end process;

tb : PROCESS
	variable rdata: std_logic_vector(0 to 11);
	variable rskip: std_logic;
BEGIN
	-- start in reset
	cpu_ioclr_n <= '0';

	-- initial state of all inputs
	dx <= (others => 'Z');
	clk_write_n <= '1';
	cpu_read_n <= '1';
	cpu_sr_read_n <= '1';
	cpu_sr_write_n <= '1';
	clk_lxdar_n <= '1';
	lpt_ack <= '0';
	lpt_busy_n <= '1';
	lpt_paper_end_n <= '0';
	lpt_select_in_n <= '0';
	lpt_error <= '0';


	-- release reset
	wait for 500 ns;
	cpu_ioclr_n <= '1';
	wait for 1500 ns;

	-- make sure the output flags are set
	IOT(O"6660", O"0000", rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
	IOT(O"6310", O"0000", rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
	IOT(O"6330", O"0000", rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
	IOT(O"6350", O"0000", rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
	IOT(O"6370", O"0000", rdata, rskip, dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);

	-- write a character to serial out
	send_char(O"31", O"0125", dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
	-- write another character to serial port, testing double-buffering
	send_char(O"31", O"0125", dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);

	-- print a test message to the VT-52
	send_string
			(O"37",
				"First line " & so & " Inverse " & si &
				lf & "lf" & cr & "cr" & cr & lf & "Third Line" & lf & lf & lf &
				"*" & esc & "D" & esc & "D<" & esc & "A^" & esc & "B>" & esc & "B" & esc & "D" & esc & "Dv" &
				esc & "Y0#Line 16, Col 3",
			dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);

	-- simulate a polling loop looking for received characters
	while true loop
		wait for 26 us;
		receive_char(O"30", dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
		receive_char(O"32", dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
		receive_char(O"34", dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
		receive_char(O"36", dx, clk_lxdar_n, clk_write_n, cpu_read_n, cpu_c0_n, cpu_c1_n, cpu_skip_n);
	end loop;

END PROCESS;
-- *** End Test Bench - User Defined Section ***

END;
