-------------------------------------------------------------------------
-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL
-- This file: 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 UNISIM;
use UNISIM.vcomponents.all;

library work;
use work.cpu_pkg.all;

ENTITY systest IS
PORT
(
	sys_rst_n_in	: in std_logic;
	sys_clk_in		: in std_logic;
	sys_btn			: in std_logic_vector(4 downto 0);
	sys_dip			: in std_logic_vector(7 downto 0);
	sys_led			: out std_logic_vector(8 downto 0);
	sys_rx			: in std_logic;
	sys_tx			: out std_logic;
	sys_lcd_d		: inout std_logic_vector(3 downto 0);
	sys_lcd_e		: out std_logic;
	sys_lcd_rs		: out std_logic;
	sys_lcd_rw		: out std_logic
);
END systest;


ARCHITECTURE behavior OF systest IS 

	COMPONENT embedded_cpu
	Port (
		rst			: in  STD_LOGIC;
		clk			: in  STD_LOGIC;
		ce			: in  STD_LOGIC;
		int_in		: in  STD_LOGIC;
		int_ack		: out  STD_LOGIC;
		xmem_we		: out STD_LOGIC;
		xmem_re		: out STD_LOGIC;
		xmem_din	: in  unsigned (DMEM_WIDTH-1 downto 0);
		xmem_dout	: out unsigned (DMEM_WIDTH-1 downto 0);
		xmem_addr	: out unsigned (DMEM_WIDTH-1 downto 0)
	);
	END COMPONENT;

--
-- declaration of UART transmitter with integral 16 byte FIFO buffer
--
  component uart_tx
    Port (            data_in : in std_logic_vector(7 downto 0);
                 write_buffer : in std_logic;
                 reset_buffer : in std_logic;
                 en_16_x_baud : in std_logic;
                   serial_out : out std_logic;
                  buffer_full : out std_logic;
             buffer_half_full : out std_logic;
                          clk : in std_logic);
    end component;
--
-- declaration of UART Receiver with integral 16 byte FIFO buffer
--
  component uart_rx
    Port (            serial_in : in std_logic;
                       data_out : out std_logic_vector(7 downto 0);
                    read_buffer : in std_logic;
                   reset_buffer : in std_logic;
                   en_16_x_baud : in std_logic;
            buffer_data_present : out std_logic;
                    buffer_full : out std_logic;
               buffer_half_full : out std_logic;
                            clk : in std_logic);
  end component;

	signal rst : std_logic;
	signal clk, clk0 : std_logic;
	signal ce : std_logic;
	signal int_req, int_in, int_ack, int_en : std_logic;

	signal cpu_din : data_t;
	signal cpu_dout : data_t;
	signal cpu_addr : unsigned (DMEM_WIDTH-1 downto 0);
	signal cpu_we, reg_sel : std_logic;
	signal cpu_re : std_logic;
	signal led_reg_we, lcd_reg_we, ctrl_reg_we : std_logic;
	signal rom_dout, reg_dout, ctrl_reg : data_t;
	signal led_reg : data_t;
	signal lcd_reg_out, lcd_reg_in: data_t;
	--
	-- Signals for connection of peripherals
	--
	signal uart_status_port : data_t;
	--
	-- Signals to form an timer generating an interrupt every microsecond
	--
	constant timer_reload : integer := 99999;
	signal timer_count   : integer range 0 to 99999 :=0;
	signal timer_pulse, timer_en   : std_logic;
	--
	-- Signals for UART connections
	--
	signal          baud_count : integer range 0 to 255 :=0;
	signal        en_16_x_baud : std_logic;
	signal       write_to_uart : std_logic;
	signal             tx_full : std_logic;
	signal        tx_half_full : std_logic;
	signal      read_from_uart : std_logic;
	signal             rx_data : std_logic_vector(7 downto 0);
	signal     rx_data_present : std_logic;
	signal             rx_full : std_logic;
	signal        rx_half_full : std_logic;
	
	signal rom_addr : unsigned(5 downto 0);
	signal reg_addr : unsigned(5 downto 0);

	constant rom_data : string(1 to 64) := 
	(
		"J-CPU V1.0      " &
		' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' &
		' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' &
		' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' &
		' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' ' & ' '
		
	);
	constant lcd_e : integer := 7;
	constant lcd_rs : integer := 6;
	constant lcd_rw : integer := 5;
	
BEGIN

	int_in <= sys_btn(4) or sys_dip(7) or timer_pulse or ctrl_reg(7) or rx_data_present;
	clk0 <= sys_clk_in;
	ce <= not rst;
	int_en <= ctrl_reg(0);
	timer_en <= ctrl_reg(1);
	sys_led(8) <= int_in;
	sys_led (7 downto 0) <= std_logic_vector(led_reg(7 downto 0));
	uart_status_port <= "000" & rx_data_present & rx_full & rx_half_full & tx_full & tx_half_full ;
	sys_lcd_d <= std_logic_vector(lcd_reg_out(3 downto 0)) when lcd_reg_out(lcd_rw) = '0' else (others => 'Z');
	sys_lcd_e <= lcd_reg_out(lcd_e);
	sys_lcd_rw <= lcd_reg_out(lcd_rw);
	sys_lcd_rs <= lcd_reg_out(lcd_rs);
	rom_addr <= cpu_addr(5 downto 0);
	reg_addr <= cpu_addr(5 downto 0);

   BUFG_inst : BUFG
   port map (
      O => clk,     -- Clock buffer output
      I => clk0      -- Clock buffer input
   );

clock_halbe:
	process(sys_rst_n_in, sys_clk_in)
	variable reset_latency : integer range 0 to 999 := 999;
	begin
		if sys_rst_n_in = '0' then
--			clk0 <= '0';
			rst <= '1';
			reset_latency := 999;
		elsif rising_edge(sys_clk_in) then
			rst <= '1';
--			clk0 <= not clk0;
			if reset_latency /= 0 then
				reset_latency := reset_latency - 1;
			else
				rst <= '0';
			end if;	
		end if;
	end process;	
	
timer:
	process(rst, clk, timer_count, timer_en)
	begin
		if rst = '1' then
			timer_count <= timer_reload;
			timer_pulse <= '0';
		elsif rising_edge(clk) then
			timer_pulse <= '0';
			if timer_count /= 0 then
				if timer_en = '1' then
					timer_count <= timer_count - 1;
				end if;	
			else
				timer_pulse <= '1';
				timer_count <= timer_reload;
			end if;	
		end if;
	end process;	

reg_mux:
	process(clk, reg_addr, reg_sel, cpu_we, cpu_re, sys_btn, sys_dip, lcd_reg_in, led_reg, uart_status_port)
	variable data : data_t;
	begin
		led_reg_we <= '0';
		lcd_reg_we <= '0';
		ctrl_reg_we <= '0';
		write_to_uart <= '0';
		read_from_uart <= '0';
		data := (others => '-');
		
		if reg_sel = '1' then 
			case reg_addr(2 downto 0) is

			when "000" =>	-- C0
				data := "0000" & unsigned(sys_btn(3 downto 0));
			when "001" =>	-- C1 
				data := led_reg;
				led_reg_we <= cpu_we;
			when "010" =>	-- C2
				data := unsigned(rx_data);
				write_to_uart <= cpu_we;
				read_from_uart <= cpu_re;
			when "011" =>	-- C3
				data := lcd_reg_in;
				lcd_reg_we <= cpu_we;
			when "100" =>	-- C4
				data := ctrl_reg;
				ctrl_reg_we <= cpu_we;
			when "101" =>	-- C5 
				data := unsigned(sys_dip);
			when "110" =>	-- C6 
				data := uart_status_port;
			when others => null;
			end case;

		end if;	

		if rising_edge(clk) then
			reg_dout <= data;
		end if;	

	end process;

din_mux:
	process(clk, cpu_addr, cpu_we, rom_dout, reg_dout)
	variable data : data_t;
	begin
		reg_sel <= '0';
		
		case cpu_addr(7 downto 6) is
		when "00" | "01" =>	-- 00 .. 7F 
			data := X"AA";
		when "10" =>		-- 80 .. BF
			data := rom_dout;
		when "11" =>		-- C0 .. FF
			data := reg_dout;
			reg_sel <= '1';

		when others =>
			data := (others => '-');
		end case;
		
		cpu_din <= data;
		
	end process;
	
ctrl_reg_write:
	process(rst, clk, ctrl_reg_we, cpu_dout)
	begin
		if (rst = '1') then
			ctrl_reg <= (others => '0');
		elsif rising_edge(clk) then
			if ctrl_reg_we = '1' then	
				ctrl_reg <= cpu_dout;
			end if;
		end if;
	end process;

led_reg_write:
	process(rst, clk, led_reg_we, cpu_dout)
	begin
		if (rst = '1') then
			led_reg <= (others => '0');
		elsif rising_edge(clk) then
			if led_reg_we = '1' then	
				led_reg <= cpu_dout;
			end if;
		end if;
	end process;

proc_lcd_reg_write:
	process(rst, clk, lcd_reg_we, cpu_dout)
	begin
		if (rst = '1') then
			lcd_reg_out <= (others => '0');
		elsif rising_edge(clk) then
			if lcd_reg_we = '1' then	
				lcd_reg_out <= cpu_dout;
			end if;
		end if;
	end process;

proc_lcd_reg_sample:
	process(rst, clk, sys_lcd_d, lcd_reg_out)
	begin
		if (rst = '1') then
			lcd_reg_in <= (others => '0');
		elsif rising_edge(clk) then
			if lcd_reg_out(lcd_rw) = '1' then	
				lcd_reg_in <= unsigned("0000" & sys_lcd_d);
			end if;
		end if;
	end process;


int_register:
	process(rst, clk, int_in, int_ack)
	begin
		if (rst = '1') then
			int_req <= '0';
		elsif rising_edge(clk) then
			if int_ack = '1' then
				int_req <= '0';
			elsif int_en = '1' and int_in = '1' then
				int_req <= '1';
			end if;
		end if;
	end process;

	
the_embedded_cpu: embedded_cpu
	PORT MAP(
		rst => rst,
		clk => clk,
		ce => ce,
		int_in => int_req,
		int_ack => int_ack,
		xmem_we => cpu_we,
		xmem_re => cpu_re,
		xmem_din => cpu_din,
		xmem_dout => cpu_dout,
		xmem_addr => cpu_addr
	);

  transmit: uart_tx 
  port map (            data_in => std_logic_vector(cpu_dout), 
                   write_buffer => write_to_uart,
                   reset_buffer => rst,
                   en_16_x_baud => en_16_x_baud,
                     serial_out => sys_tx,
                    buffer_full => tx_full,
               buffer_half_full => tx_half_full,
                            clk => clk );
	
  receive: uart_rx
  port map (            serial_in => sys_rx,
                         data_out => rx_data,
                      read_buffer => read_from_uart,
                     reset_buffer => rst,
                     en_16_x_baud => en_16_x_baud,
              buffer_data_present => rx_data_present,
                      buffer_full => rx_full,
                 buffer_half_full => rx_half_full,
                              clk => clk );  
  --
  -- Set baud rate to 38400 for the UART communications
  -- Requires en_16_x_baud to be 614400Hz which is a single cycle pulse every 163 cycles at 100MHz 
  --
  -- NOTE : If the highest value for baud_count exceeds 127 you will need to adjust 
  --        the range of integers in the signal declaration for baud_count.
  --

  baud_timer: process(clk)
  begin
    if clk'event and clk='1' then
      if baud_count=162 then
           baud_count <= 0;
         en_16_x_baud <= '1';
       else
           baud_count <= baud_count + 1;
         en_16_x_baud <= '0';
      end if;
    end if;
  end process baud_timer;

ROM_RD:
	process(rst, clk, rom_addr)
	begin
		if rising_edge(clk) then
			rom_dout <= to_unsigned(character'pos(rom_data(to_integer(rom_addr)+1)), data_t'length);
		end if;
	end process;

END;
