----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    15:52:22 12/30/2008 
-- Design Name: 
-- Module Name:    toplevel - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
library UNISIM;
use UNISIM.VComponents.all;

entity uart is
  
  port (
    CLK_16M            : in  std_logic;
    DATA_I             : in  std_logic_vector(7 downto 0);
    ADDR_I             : in  std_logic_vector(1 downto 0);
    IORQ_n, RD_n, WR_n : in  std_logic;
    DATA_O             : out std_logic_vector(7 downto 0);
    -- SPI for Atmel Dataflash
    SPI_MISO              : in    std_logic;
    SPI_MOSI              : out   std_logic;
    SPI_SCK               : out   std_logic;
    SPI_SS_B              : out   std_logic := '1';
    DATAFLASH_WP          : out   std_logic;
    DATAFLASH_RST         : out   std_logic;
    -- LCD interface
    LCD_DB                : inout std_logic_vector(7 downto 0);
    LCD_E, LCD_RS, LCD_RW : out   std_logic;
    -- Buttons
    BTN_NORTH : in STD_LOGIC;
    BTN_SOUTH : in STD_LOGIC;
    BTN_EAST  : in STD_LOGIC;
    BTN_WEST  : in STD_LOGIC;
    ROT_CENTER : in STD_LOGIC);

end uart;

architecture Behavioral of uart is

component kcpsm3
    port (
      address       : out std_logic_vector(9 downto 0);
      instruction   : in  std_logic_vector(17 downto 0);
      port_id       : out std_logic_vector(7 downto 0);
      write_strobe  : out std_logic;
      out_port      : out std_logic_vector(7 downto 0);
      read_strobe   : out std_logic;
      in_port       : in  std_logic_vector(7 downto 0);
      interrupt     : in  std_logic;
      interrupt_ack : out std_logic;
      reset         : in  std_logic;
      clk           : in  std_logic);
  end component;

component uartprog
    port (
      address     : in  std_logic_vector(9 downto 0);
      instruction : out std_logic_vector(17 downto 0);
      clk         : in  std_logic);
  end component;

component fifo16x8
  port (
    DATAIN   : in  STD_LOGIC_VECTOR (7 downto 0);
    WRITESTB : in  STD_LOGIC;
    DATAOUT  : out STD_LOGIC_VECTOR (7 downto 0);
    READSTB  : in  STD_LOGIC;
    CLK      : in  STD_LOGIC;
    FULL     : out STD_LOGIC;
    EMPTY    : out STD_LOGIC);
end component;

component spi
  port (
    MISO      : in  std_logic;
    MOSI, SCK : out std_logic;
    DATA_I    : in  std_logic_vector(7 downto 0);
    DATA_O    : out std_logic_vector(7 downto 0);
    START     : in  std_logic;
    BUSY      : out std_logic;
    CLK       : in  std_logic);
end component;

signal receive_r, transmit_r : std_logic_vector(7 downto 0);
signal drt_r, drr_r : std_logic := '0';

signal kcpsm3_addr : std_logic_vector(9 downto 0);
signal kcpsm3_instr : std_logic_vector(17 downto 0);
signal kcpsm3_portid, kcpsm3_outport, kcpsm3_inport : std_logic_vector(7 downto 0);
signal kcpsm3_wrstb, kcpsm3_rdstb : std_logic;

attribute iob : string;
signal lcdctrl_r : std_logic_vector(2 downto 0) := "000";
signal lcdout_r : std_logic_vector(7 downto 0) := X"00";
attribute iob of lcdout_r : signal is "true";
attribute iob of lcdctrl_r : signal is "true";

signal spi_datao : std_logic_vector(7 downto 0);
signal spi_start, spi_busy : std_logic;

signal iorq_old : std_logic := '0';

signal fifo_t_wrstb, fifo_t_rdstb, fifo_t_full, fifo_t_empty, fifo_r_wrstb, fifo_r_rdstb, fifo_r_full, fifo_r_empty : std_logic;
signal fifo_r_din, fifo_r_dout, fifo_t_din, fifo_t_dout : std_logic_vector(7 downto 0);
begin  -- Behavioral

  kcpsm3_inst : kcpsm3 port map (
    address       => kcpsm3_addr,
    instruction   => kcpsm3_instr,
    port_id       => kcpsm3_portid,
    write_strobe  => kcpsm3_wrstb,
    out_port      => kcpsm3_outport,
    read_strobe   => kcpsm3_rdstb,
    in_port       => kcpsm3_inport,
    interrupt     => '0',
    interrupt_ack => open,
    reset         => '0',
    clk           => CLK_16M);

  prog_inst : uartprog port map (
    address     => kcpsm3_addr,
    instruction => kcpsm3_instr,
    clk         => CLK_16M);

  spi_inst : spi port map (
    MISO   => SPI_MISO,
    MOSI   => SPI_MOSI,
    SCK    => SPI_SCK,
    DATA_I => kcpsm3_outport,
    DATA_O => spi_datao,
    START  => spi_start,
    BUSY   => spi_busy,
    CLK    => CLK_16M);

  spi_start <= '1' when (kcpsm3_wrstb = '1') and (kcpsm3_portid = X"02") else
             '0';
  
  spi_iface: process (CLK_16M)
  begin  -- process spi_iface
    if rising_edge(CLK_16M) then
      if (kcpsm3_wrstb = '1') then
        if (kcpsm3_portid = X"03") then
          SPI_SS_B <= kcpsm3_outport(0);
        end if;
      end if;
    end if;
  end process;

  lcd_iface: process (CLK_16M)
  begin  -- process lcd_iface
    if rising_edge(CLK_16M) then
      if (kcpsm3_wrstb = '1') then
        if (kcpsm3_portid = X"04") then
          lcdctrl_r <= kcpsm3_outport(2 downto 0);
        elsif kcpsm3_portid = X"05" then
          lcdout_r <= kcpsm3_outport;
        end if;
      end if;
    end if;
  end process lcd_iface;
  
kcpsm3_inport <= fifo_t_dout when kcpsm3_portid = X"00" else
                 "000000" & fifo_t_empty & fifo_r_full when kcpsm3_portid = X"01" else
                 spi_datao when kcpsm3_portid = X"02" else
                 "0000000" & spi_busy when kcpsm3_portid = X"03" else
                 LCD_DB when kcpsm3_portid = X"05" else
                 "00000" & lcdctrl_r when kcpsm3_portid = X"04" else
                 "000" & ROT_CENTER & BTN_NORTH & BTN_EAST & BTN_SOUTH & BTN_WEST when kcpsm3_portid = X"06" else
                 "XXXXXXXX";

  process (CLK_16M)
  begin
    if rising_edge(CLK_16M) then
      fifo_r_wrstb <= '0';
      fifo_t_rdstb <= '0';
       if (kcpsm3_wrstb = '1') then
        if (kcpsm3_portid = X"00") then
          fifo_r_din <= kcpsm3_outport;
          fifo_r_wrstb <= '1';
        end if;
      elsif (kcpsm3_rdstb = '1') then
        if (kcpsm3_portid = X"00") then
          fifo_t_rdstb <= '1';    
        end if;
      end if;
    end if;
  end process;

 fifo16x8_inst_transmit : fifo16x8 port map (
   DATAIN   => fifo_t_din,
   WRITESTB => fifo_t_wrstb,
   DATAOUT  => fifo_t_dout,
   READSTB  => fifo_t_rdstb,
   CLK      => CLK_16M,
   FULL     => fifo_t_full,
   EMPTY    => fifo_t_empty);

 fifo16x8_inst_receive : fifo16x8 port map (
   DATAIN   => fifo_r_din,
   WRITESTB => fifo_r_wrstb,
   DATAOUT  => fifo_r_dout,
   READSTB  => fifo_r_rdstb,
   CLK      => CLK_16M,
   FULL     => fifo_r_full,
   EMPTY    => fifo_r_empty);   
  
  z80bus: process (CLK_16M)
  begin
    if rising_edge(CLK_16M) then
      fifo_r_rdstb <= '0';
      fifo_t_wrstb <= '0';
      if (iorq_old = '1') and (IORQ_n = '0') then
        if ADDR_I = "01" then
          if RD_n = '0' then            -- read receiver register
            DATA_O <= fifo_r_dout;
            fifo_r_rdstb <= '1';
          elsif WR_n = '0' then         -- transmitter buffer load
            fifo_t_din <= DATA_I;
            fifo_t_wrstb <= '1';
          end if;
        elsif ADDR_I = "10" then
          if RD_n = '0' then            -- read status flags
            DATA_O <= not fifo_r_empty & not fifo_t_full & "XX000X";       -- DR, TBRE, x, x, FE, PE, OE, x
          end if;
        end if;
      end if;
      iorq_old <= IORQ_n;
    end if;
  end process;

  LCD_DB <= lcdout_r when lcdctrl_r(2) = '0' else
          "ZZZZZZZZ";
  LCD_E <= lcdctrl_r(0);
  LCD_RS <= lcdctrl_r(1);
  LCD_RW <= lcdctrl_r(2);

  DATAFLASH_RST <= '1';
  DATAFLASH_WP <= '1';

end Behavioral;
