-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- PicoBlaze compatible HOST interface for V5 embedded TEMAC
--
-- Access to Address Filtering register is NOT implemented!!!
--
----------------------------------------------------------------------------------
---------------------------------------------------------------------------
-- HOST_addr_H register:
-- [0] A(8)
-- [1] A(9)                 always '1' for MAC register access (auto set)
-- [2] HOSTMIIMSEL          '0' to access MAC registers, '1' to access MDIO registers
-- [3] HOSTEMAC1SEL         '0' to access EMAC0, '1' to access EMAC1
-- [4] -- Unused --
-- [5] -- Unused --
-- [6] Write command        write 1 to initiate write; cleared automaticly when write is completed
-- [7] Read command         write 1 to initiate read; cleared automaticly when read is completed
----------------------------------------------------------------------
----------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
----------------------------------------------------------------------------------
ENTITY Pico_HOST_Interface IS
  GENERIC(
    reg_HOST_data_0_ID   : STD_LOGIC_VECTOR( 7 DOWNTO 0) := X"01";
    reg_HOST_data_1_ID   : STD_LOGIC_VECTOR( 7 DOWNTO 0) := X"02";
    reg_HOST_data_2_ID   : STD_LOGIC_VECTOR( 7 DOWNTO 0) := X"04";
    reg_HOST_data_3_ID   : STD_LOGIC_VECTOR( 7 DOWNTO 0) := X"08";
    reg_HOST_addr_L_ID   : STD_LOGIC_VECTOR( 7 DOWNTO 0) := X"10";
    reg_HOST_addr_H_ID   : STD_LOGIC_VECTOR( 7 DOWNTO 0) := X"20");
  PORT(
    clk                 : IN    STD_LOGIC;      -- main clock, common to HOST and PicoBlaze; max 125 MHz
    rst                 : IN    STD_LOGIC;      -- system reset; active high

    -- PicoBlaze interface
    Pico_Address        : IN    STD_LOGIC_VECTOR(7 DOWNTO 0);     -- PicoBlaze Port ID
    Pico_Data_in        : IN    STD_LOGIC_VECTOR(7 DOWNTO 0);     -- Data IN (PicoBlaze OUT)
    Pico_rd             : IN    STD_LOGIC;                        -- PicoBlaze read strobe
    Pico_wr             : IN    STD_LOGIC;                        -- PicoBlaze write strobe

    -- Registers output
    reg_HOST_data_0     : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0);     -- LSByte
    reg_HOST_data_1     : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0);     -- 
    reg_HOST_data_2     : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0);     -- 
    reg_HOST_data_3     : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0);     -- MSByte

    reg_HOST_addr_L     : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0);     -- Data L reg OUT (to PicoBlaze input MUX)
    reg_HOST_addr_H     : OUT   STD_LOGIC_VECTOR(7 DOWNTO 0);     -- Data H reg OUT (to PicoBlaze input MUX)

    -- HOST interface
    HOSTOPCODE          : OUT   STD_LOGIC_VECTOR( 1 DOWNTO 0);    -- OPCODE of MDIO frame
                                                                  -- MAC register access: HOSTOPCODE(0) is ignored
                                                                  -- HOSTOPCODE(1) = '1': write command
                                                                  -- HOSTOPCODE(1) = '0': read command 
    HOSTREQ             : OUT   STD_LOGIC;                        -- pulse H to initiate MDIO transaction (read or write)
    HOSTMIIMSEL         : OUT   STD_LOGIC;                        -- set L to access MAC registers, set H to access MDIO interface
    HOSTADDR            : OUT   STD_LOGIC_VECTOR( 9 DOWNTO 0) := (OTHERS => '0'); 
                                                                  -- for MDIO:  PHYAD (the physical address) is HOSTADDR[9:5]
                                                                  --            REGAD (the register address) is HOSTADDR[4:0].
    HOSTWRDATA          : OUT   STD_LOGIC_VECTOR(31 DOWNTO 0);    -- for MDIO only HOSTWRDATA(15 DOWNTO 0) is used
    HOSTMIIMRDY         : IN    STD_LOGIC;                        -- When High, the MDIO interface has completed any pending transaction and is ready for a new transaction.
    HOSTRDDATA          : IN    STD_LOGIC_VECTOR(31 DOWNTO 0);    -- for MDIO only HOSTRDDATA(15 DOWNTO 0) is used; data valid after HOSTMIIMRDY goes high
    HOSTEMAC1SEL        : OUT   STD_LOGIC);                       -- set L to access EMAC0, set H to access EMAC1
END Pico_HOST_Interface;
----------------------------------------------------------------------------------
ARCHITECTURE Behavioral OF Pico_HOST_Interface IS
----------------------------------------------------------------------------------
  SIGNAL HOST_data_reg_wr   : STD_LOGIC_VECTOR(31 DOWNTO 0):= (OTHERS => '0');      -- data register for writing
  SIGNAL HOST_data_reg_rd   : STD_LOGIC_VECTOR(31 DOWNTO 0):= (OTHERS => '0');      -- data register for reading
  SIGNAL HOST_addr_reg      : STD_LOGIC_VECTOR(11 DOWNTO 0):= (OTHERS => '0');      -- address register
  SIGNAL HOST_stat_reg      : STD_LOGIC_VECTOR( 1 DOWNTO 0):= (OTHERS => '0');      -- status register

  SIGNAL wr_start           : STD_LOGIC := '0';                                     -- initiates SMI write cycle
  SIGNAL rd_start           : STD_LOGIC := '0';                                     -- initiates SMI read cycle

  SIGNAL clear_stat_reg     : STD_LOGIC := '0';                                     -- signal for clearing status register (busy flags)

  SIGNAL HOSTMIIMSEL_i      : STD_LOGIC := '0';                                     -- HOSTMIIMSEL internal signal (alias)

  TYPE t_state_HOST IS (st_idle, st_wr_MAC_1, st_rd_MAC_1,  st_rd_MAC_2,  st_rd_MAC_3, st_wr_MDIO_1, st_wr_MDIO_2, st_wr_MDIO_3, st_rd_MDIO_1, st_rd_MDIO_2, st_rd_MDIO_3);
  SIGNAL state_HOST         : t_state_HOST := st_idle;

----------------------------------------------------------------------------------
BEGIN
----------------------------------------------------------------------------------
  -- PicoBlaze interface

  -- Processor write to internal registers of the controller
  PROCESS(clk) BEGIN
    IF RISING_EDGE(clk) THEN
      IF Pico_wr = '1' AND HOST_stat_reg = "00" THEN
        CASE Pico_Address IS
          WHEN reg_HOST_data_3_ID => HOST_data_reg_wr(31 DOWNTO 24) <= Pico_Data_in;
          WHEN reg_HOST_data_2_ID => HOST_data_reg_wr(23 DOWNTO 16) <= Pico_Data_in;
          WHEN reg_HOST_data_1_ID => HOST_data_reg_wr(15 DOWNTO  8) <= Pico_Data_in;
          WHEN reg_HOST_data_0_ID => HOST_data_reg_wr( 7 DOWNTO  0) <= Pico_Data_in;
          WHEN reg_HOST_addr_H_ID => HOST_addr_reg   (11 DOWNTO  8) <= Pico_Data_in(3 DOWNTO 0);
          WHEN reg_HOST_addr_L_ID => HOST_addr_reg   ( 7 DOWNTO  0) <= Pico_Data_in;
          WHEN OTHERS       => NULL;
        END CASE;
      END IF;
    END IF;
  END PROCESS;

  HOSTMIIMSEL_i     <= HOST_addr_reg(10);    -- HOSTMIIMSEL signal for internal use (alias to "HOST_addr_reg(10)")
  HOSTEMAC1SEL      <= HOST_addr_reg(11);    -- '0' to access EMAC0, '1' to access EMAC1

  HOSTWRDATA        <= HOST_data_reg_wr;

  --------------------------------------------------------------------------------
  -- Processor read of controller internal registers

  reg_HOST_data_0 <= HOST_data_reg_rd( 7 DOWNTO  0);
  reg_HOST_data_1 <= HOST_data_reg_rd(15 DOWNTO  8);
  reg_HOST_data_2 <= HOST_data_reg_rd(23 DOWNTO 16);
  reg_HOST_data_3 <= HOST_data_reg_rd(31 DOWNTO 24);
  
  reg_HOST_addr_L <= HOST_addr_reg   ( 7 DOWNTO  0);
  reg_HOST_addr_H <= HOST_stat_reg & "00" & HOST_addr_reg(11 DOWNTO  8);

  --------------------------------------------------------------------------------
  -- Write or Read cycle start for MAC/MDIO interface

  PROCESS(clk) BEGIN
    IF RISING_EDGE(clk) THEN

      -- default assignment
      rd_start <= '0';
      wr_start <= '0';

      -- clear busy flags when transmission is completed
      IF clear_stat_reg = '1' THEN HOST_stat_reg <= "00"; END IF;

      IF HOST_stat_reg = "00" THEN
        -- access to status register (upper address byte) when last transaction is completed
        IF Pico_wr = '1' AND Pico_Address = reg_HOST_addr_H_ID AND HOST_stat_reg = "00" THEN
          -- write command
          IF Pico_Data_in(6) = '1' THEN
            HOST_stat_reg(1) <= '1';
            wr_start <= '1';
          -- read command
          ELSIF Pico_Data_in(7) = '1' THEN
            HOST_stat_reg(0) <= '1';
            rd_start <= '1';
          END IF;
        END IF;

      END IF;
    END IF;
  END PROCESS;

  --------------------------------------------------------------------------------
  -- HOST_OPCODE(0)                         -- don't care for MAC register access
  -- HOST_OPCODE(1)                         -- active LOW write enable for HOST bus
  --                                        -- for MDIO access;    read operation: HOST_OPCODE <= "10";
  --                                                               write operation: HOST_OPCODE <= "01";
  -- HOSTMIIMSEL  <= HOST_addr_reg(10);     -- '0' to access MAC registers, '1' to access MDIO registers
  -- HOSTEMAC1SEL <= HOST_addr_reg(11);     -- '0' to access EMAC0,         '1' to access EMAC1                 

  -- HOST Rx/Tx state machine
  PROCESS(clk) BEGIN
    IF RISING_EDGE(clk) THEN

      -- default assignment
      HOSTOPCODE     <= "11";
      HOSTMIIMSEL    <= '1';
      HOSTREQ        <= '0';
      clear_stat_reg <= '0';


      CASE state_HOST IS
        WHEN st_idle        => IF wr_start = '1' THEN
                                 IF HOSTMIIMSEL_i = '0' THEN state_HOST <= st_wr_MAC_1;             --  MAC register access
                                                        ELSE state_HOST <= st_wr_MDIO_1; END IF;    -- MDIO register access
                               END IF;

                               IF rd_start = '1' THEN
                                 IF HOSTMIIMSEL_i = '0' THEN state_HOST <= st_rd_MAC_1;             --  MAC register access
                                                        ELSE state_HOST <= st_rd_MDIO_1; END IF;    -- MDIO register access
                               END IF;

        -- MAC write -------------------------------------------------------------
        WHEN st_wr_MAC_1    =>  HOSTOPCODE(1) <= '0';
                                HOSTMIIMSEL   <= '0';
                                HOSTADDR(9)   <= '1';
                                HOSTADDR(8 DOWNTO 0) <= HOST_addr_reg(8 DOWNTO 0);
                                state_HOST <= st_idle;
                                clear_stat_reg <= '1';

        -- MAC read --------------------------------------------------------------
        WHEN st_rd_MAC_1    =>  HOSTOPCODE(1) <= '1';
                                HOSTMIIMSEL   <= '0';
                                HOSTADDR(8 DOWNTO 0) <= HOST_addr_reg(8 DOWNTO 0);
                                HOSTADDR(9) <= '1';
                                state_HOST <= st_rd_MAC_2;

        WHEN st_rd_MAC_2    =>  HOSTOPCODE(1) <= '1';
                                HOSTMIIMSEL   <= '0';
                                state_HOST <= st_rd_MAC_3;

        WHEN st_rd_MAC_3    =>  HOST_data_reg_rd <= HOSTRDDATA;
                                state_HOST <= st_idle;
                                clear_stat_reg <= '1';

        -- MDIO write ------------------------------------------------------------
        WHEN st_wr_MDIO_1   =>  HOSTREQ <= '1';
                                HOSTOPCODE <= "01";
                                HOSTADDR <= HOST_addr_reg(9 DOWNTO 0);
                                state_HOST <= st_wr_MDIO_2;

        WHEN st_wr_MDIO_2   =>  state_HOST <= st_wr_MDIO_3;

        WHEN st_wr_MDIO_3   =>  IF HOSTMIIMRDY = '1' THEN
                                  state_HOST <= st_idle;
                                  clear_stat_reg <= '1';
                                END IF;

        -- MDIO read -------------------------------------------------------------
        WHEN st_rd_MDIO_1   =>  HOSTREQ <= '1';
                                HOSTOPCODE <= "10";
                                HOSTADDR <= HOST_addr_reg(9 DOWNTO 0);
                                state_HOST <= st_rd_MDIO_2;

        WHEN st_rd_MDIO_2   =>  state_HOST <= st_rd_MDIO_3;

        WHEN st_rd_MDIO_3   =>  IF HOSTMIIMRDY = '1' THEN
                                  HOST_data_reg_rd <= HOSTRDDATA;
                                  state_HOST <= st_idle;
                                  clear_stat_reg <= '1';
                                END IF;

        --------------------------------------------------------------------------

      END CASE;

    END IF;
  END PROCESS;
----------------------------------------------------------------------------------
END Behavioral;
----------------------------------------------------------------------------------
