----------------------------------------------------------------------------------
-- Author:         Armin Diehl
--
-- Create Date:    12/11/2006
-- Last Change:    12/22/2006 AD
-- Module Name:    gide - Behavioral
-- Project Name:   ecb ide interface
-- Target Devices: xc9572
-- Tool versions:
-- Description:    ECB IDE Interface based on the idea of Tillmann Reh's GIDE
--                 The original design was implemented using PAL's and latches
--                 Needs the fitter option "Optimize Density" otherwise the fitter
--                 will not work (Unable to map all desired signals into function block)
-- Revision        0.03 - works (but has sometimes spikes on IDE_A0
--                 0.04 - Latched the addresses to the ide disk
--                 0.05 - Latched IDE_WR
----------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
--
-- for debug, Pin18 and Pin50 can be used
--
entity gide is
    port ( ECBRes   : in    std_logic;
           A        : in    std_logic_vector (7 downto 0);       -- ECB Address Bus
           JUMPER_A : in    std_logic_vector (3 downto 0);       -- upper 4 bit Jumper for IO Address
           IORQ     : in    std_logic;
           RD       : in    std_logic;
           WR       : in    std_logic;
           D        : inout std_logic_vector (7 downto 0);       -- ECB data bus
           CLK      : in    std_logic;                           -- ECB clock
           IDE_D    : inout std_logic_vector (15 downto 0);      -- 16 Bit data bus to/from ide
           IDE_CS0  : out   std_logic;                           -- CS0 to ide
           IDE_CS1  : out   std_logic;                           -- CS1 to ide
           IDE_RD   : out   std_logic;                           -- /IORD to ide
           IDE_WR   : out   std_logic;                           -- /IOWR to ide
           IDE_A    : out   std_logic_vector (2 downto 0)        -- A0,A1,A2 to ide
           );
end gide;
--
architecture Behavioral of gide is
signal DOUT        : std_logic_vector(7 downto 0);
signal DIDEOUT     : std_logic_vector(15 downto 0);
signal CS_READ     : std_logic;
signal CS          : std_logic;
signal CS_16BIT    : std_logic;
signal WAS_16BIT   : std_logic;
signal READ_ACTIVE : std_logic;
signal READ_BUFFER : std_logic_vector(7 downto 0);
signal CS_WRITE    : std_logic;
signal ACCESS_HIGH : std_logic;
signal ADDRESSOK   : std_logic;
signal SELECTDISK_WR: std_logic;
signal READSTATE : integer range 0 to 2;
--
signal TOGGLERSTATE : integer range 0 to 2;
signal IDE_RD_INT   : std_logic;
--
begin
  -- address comparator, needed for IDECS0 and 1
  ADDRESSOK <= '1' when A (7 downto 4) = JUMPER_A else '0';
--
  --CS <= '1' WHEN (IORQ = '0') AND (ADDRESSOK = '1') AND ((RD = '0') OR (WR = '0')) ELSE '0';
  CS <= '1' when (IORQ = '0') and (ADDRESSOK = '1') else '0';
--
  CS_16BIT <= '1' when (CS = '1') and (A(3 downto 0) = "1000") else '0';
--
  -- generate CS_READ that will be 1 if IO read with matched address is active
  CS_READ <= '1' when (CS = '1') and (RD = '0') else '0';
--
  -- generate CS_WRITE that will be 1 if IO write with matched address is active
  -- will activate process "writeData"
  CS_WRITE <= '1' when (CS = '1') and (WR = '0') else '0';
--
  -- IDE_WR only on second access (16 Bit) or on any access (8 Bit)
  --IDE_WR <= '0' WHEN (SELECTDISK_WR = '1') AND (CS = '1') AND ((ACCESS_HIGH = '1') OR (CS_16BIT = '0')) ELSE '1';
--
  LatchIDE_WR:process(SELECTDISK_WR,WR)
  begin
    if WR = '1' then
      IDE_WR <= '1';
    elsif rising_edge(SELECTDISK_WR) then
      IDE_WR <= '0';
    end if;
  end process;
--
  -- Always set A0..A2 and CS0 and CS1 to the drive
  -- this gives spikes, IDE_A(0) will sometimes flip during writes
  --IDE_CS0 <= NOT A(3);
  --IDE_CS1 <= A(3);
  --IDE_A <= A(2 downto 0);
--
  LatchAddr:process(ECBRES,CLK)
  begin
    if (ECBRES = '0') then
      IDE_CS0 <= '0';
      IDE_CS1 <= '0';
      IDE_A <= "000";
    elsif falling_edge(CLK) then
      -- the Z80 address is valid short after a rising edge, latch it on falling edge
      IDE_CS0 <= not A(3);
      IDE_CS1 <= A(3);
      IDE_A <= A(2 downto 0);
    end if;
  end process;
--
  -- flop flop for IDE_RD to avoid glitches
  LatchIdeRd:process(IDE_RD_INT,IORQ)
  begin
    if IORQ = '1' then
      IDE_RD <= '1';
    elsif falling_edge(IDE_RD_INT) then
      IDE_RD <= '0';
    end if;
  end process;
--
--
  -- swaps ACCESS_HIGH for 16 bit accesses after the IO read or write has been completed
  -- do the toggle on rising edge after the IO has been completed
  HighLowToggler:process(CLK,ECBRes,CS)
  begin
    if ECBRes = '0' then
      ACCESS_HIGH <= '0';
      TOGGLERSTATE <= 0;
      WAS_16BIT <= '0';
    elsif rising_edge(CLK) then
        case TOGGLERSTATE is
          when 0 => if CS = '1' then
                      TOGGLERSTATE <= 1;              -- IO now active
                      if A(3 downto 0) = "1000" then
                        WAS_16BIT <= '1';
                      else
                        WAS_16BIT <= '0';
                      end if;
                    end if;
          when 1 => if CS = '0' then                  -- IO finished ?
                      TOGGLERSTATE <= 2;              -- toggle the state on next clock
                    end if;
          when 2 => if WAS_16BIT = '1' then           -- was it an access to 16 bit data ?
                      ACCESS_HIGH <= not ACCESS_HIGH; -- then toggle
                    else
                      ACCESS_HIGH <= '0';             -- otherwise lower byte
                    end if;
                    TOGGLERSTATE <= 0;
        end case;
    end if;
  end process;
--
--
  writeData:process(CS_WRITE)          -- sets Data to the IDE drive depending
  begin                                -- on 8 or 16 bit access
    if rising_edge(CS_WRITE) then
      if A(3 downto 0) = "1000" then   -- 16 Bit Access
        if ACCESS_HIGH = '0' then      -- first byte
          DIDEOUT(7 downto 0) <= D;
        else
          DIDEOUT(15 downto 8) <= D;   -- second byte
        end if;
      else
        DIDEOUT(15 downto 8) <= "00000000";  -- not really needed
        DIDEOUT(7 downto 0) <= D;
      end if;
    end if;
  end process;
--
--
    -- This sets the RW signal to the disk at a time when the
    -- Data to the IDE drive is valid
    SetWriteActive:process(CLK,ECBRes,CS_WRITE,ACCESS_HIGH)
    begin
      if ECBRes = '0' then
        SELECTDISK_WR <= '0';
      elsif falling_edge(CLK) then
        if CS_WRITE = '1' then                          -- IOWR to 8 or 16 bit register
          if A(3 downto 0) = "1000" then                -- access to data register (16 Bit)
            if ACCESS_HIGH = '1' then                   -- if second byte is written
              SELECTDISK_WR <= '1';                     -- Enable /IOWR to ide
            else
              SELECTDISK_WR <= '0';                     -- no disk access for first byte
            end if;
          else                                          -- access to normal register
            SELECTDISK_WR <= '1';                       -- Enable /IOWR to ide
          end if;
        else                                            -- falling edge of clk with no sctive write
          SELECTDISK_WR <= '0';                         -- Disable /IOWR to ide
        end if;
      end if;
    end process;
--
--
    SetReadActive:process(CLK,ECBRes)                   -- toggle CS_READ sync to clock
    begin                                               -- to be able to read the ide data some time
      if ECBRes = '0' then
        READ_Active <= '0';
        IDE_RD_INT <= '1';
        READSTATE <= 0;
      elsif falling_edge(CLK) then                      -- after CS0/CS1 are set
        if CS_READ = '1' then
          if READ_Active = '0' then
            READ_Active <= '1';
          end if;
          if READSTATE = 0 then                         -- first falling edge of clock in read
            if (ACCESS_HIGH = '0') or (CS_16BIT = '0') then
              IDE_RD_INT <= '0';                            -- enable IDE read
            else
              IDE_RD_INT <= '1';
            end if;
            READSTATE <= READSTATE + 1;
          elsif READSTATE = 1 then                      -- second falling edge of read
            IDE_RD_INT <= '1';                              -- disable read, we got the data on RisingEdge
            READSTATE <= READSTATE + 1;
          end if;
        else
          READ_Active <= '0';
          IDE_RD_INT <= '1';
          READSTATE <= 0;
        end if;
      end if;
    end process;
--
    -- on first access
    --  Output lower 8 bit to host and save upper 8 bit
    -- on second access output saved upper 8 bit
    READER:process(CLK,CS_16BIT,ACCESS_HIGH)
    begin
      if rising_edge(CLK) then
        if READSTATE = 1 then                            -- indicates /READ was set on prev falling clock edge
          if CS_16BIT = '1' then                         -- access to data register
            if ACCESS_HIGH = '0' then                    -- read from IDE
              DOUT <= IDE_D(7 downto 0);                 -- lower 8 bit to data bus
              READ_BUFFER <= IDE_D(15 downto 8);         -- and save upper 8 bit
            else
              DOUT <= READ_BUFFER;                       -- 2nd access, upper 8 bit to host
            end if;
          else                                           -- not data register
            DOUT <= IDE_D(7 downto 0);                   -- and output lower byte
          end if;
        end if;
      end if;
    end process;
--
  -- set host data bus
  D <= DOUT when (CS_READ = '1') else "ZZZZZZZZ";
--
  -- set IDE data bus
  IDE_D <= DIDEOUT when (CS_WRITE = '1') else "ZZZZZZZZZZZZZZZZ";
--
end Behavioral;
--