---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2004-7
--
-- Filename: LocalBusModel.vhd
-- Language: VHDL '93
-- Project:  NDTS2100
--
-- Design Description:
-- Bus model of the PEX8311 local bus
---------------------------------------------------------------------------

library IEEE;
use IEEE.Std_Logic_1164.all;
use IEEE.Std_Logic_Arith.all;
use std.textio.all;

library work;
use work.LocalBusModelPkg.all;


entity LocalBusModel is
  port(-- Signals to/from simulation control module
       ToBusModel   : in  tToBusModel;
       FromBusModel : out tFromBusModel;
       -- Bus Signals
       nLRESET      : out   std_ulogic;
       LCLKA        : out   std_ulogic;
       LWnR         : out   std_logic;
       nADS         : inout std_logic;
       nBLAST       : inout std_logic;
       nREADY       : inout std_logic;
       LA           : inout std_logic_vector(31 downto 0);
       LD           : inout std_logic_vector(31 downto 0);
       LHOLD        : out   std_ulogic;
       LHOLDA       : in    std_ulogic;
       nBTERM       : inout std_logic;
       nCCS         : in    std_ulogic;
       BREQi        : in    std_ulogic;
       nLINTi       : in    std_ulogic);
end entity LocalBusModel;


architecture Beh of LocalBusModel is

  constant cOutDelay: time := 7.5 ns;  -- Worst-case clock to output delay for bus model outputs

  -- Internal version of BusClk output
  signal iBusClk: std_ulogic;
  -- Internal version of Reset output
  signal iReset:  std_ulogic;

--  function Int2HexString(IntIn, NibblesOut: integer) return string is
--    variable vUnsigned: unsigned(NibblesOut*4 - 1 downto 0);
--    variable vHex: unsigned(3 downto 0);
--    variable vHexOut: string(1 to NibblesOut + 2);
--  begin
--    vUnsigned := conv_unsigned(IntIn, NibblesOut*4);
--
--    for count in NibblesOut downto 1 loop
--      vHex := vUnsigned(Count*4 - 1 downto Count*4 - 4);
--      case vHex is
--        when "0000" => vHexOut(NibblesOut - Count + 3) := '0';
--        when "0001" => vHexOut(NibblesOut - Count + 3) := '1';
--        when "0010" => vHexOut(NibblesOut - Count + 3) := '2';
--        when "0011" => vHexOut(NibblesOut - Count + 3) := '3';
--        when "0100" => vHexOut(NibblesOut - Count + 3) := '4';
--        when "0101" => vHexOut(NibblesOut - Count + 3) := '5';
--        when "0110" => vHexOut(NibblesOut - Count + 3) := '6';
--        when "0111" => vHexOut(NibblesOut - Count + 3) := '7';
--        when "1000" => vHexOut(NibblesOut - Count + 3) := '8';
--        when "1001" => vHexOut(NibblesOut - Count + 3) := '9';
--        when "1010" => vHexOut(NibblesOut - Count + 3) := 'A';
--        when "1011" => vHexOut(NibblesOut - Count + 3) := 'B';
--        when "1100" => vHexOut(NibblesOut - Count + 3) := 'C';
--        when "1101" => vHexOut(NibblesOut - Count + 3) := 'D';
--        when "1110" => vHexOut(NibblesOut - Count + 3) := 'E';
--        when others => vHexOut(NibblesOut - Count + 3) := 'F';
--      end case;
--    end loop;
--    vHexOut(1 to 2) := "0x";
--    return vHexOut;
--  end function Int2HexString;

begin

  -- Pull-ups
  nADS <= 'H';
  nBLAST <= 'H';
  nREADY <= 'H';
  nBTERM <= 'H';

  -- Main Process to generate the required cycles
  Main: process
    variable vLine: Line;
    variable vDone: boolean;
  begin
    -- Clear done flag
    vDone := false;

    -- Set Bus model done signal to 0
    FromBusModel.Done <= '0';

    -- Set bus signals to inactive level
    LWnR    <= '1';
    nADS    <= '1';
    nBLAST  <= '1';
    LA      <= (others => 'Z');
    LD      <= (others => 'Z');
    LHOLD   <= '0';

    wait until falling_edge(ToBusModel.Start);

    if ToBusModel.Write_nRead = '0' then
      ---------------------
      -- 32-bit read cycle
      ---------------------

      -- Drive lines for read cycle
      LHOLD <= '1' after cOutDelay;

      -- Wait for LHOLDA to be asserted
      wait on iBusClk until rising_edge(iBusClk);
      while LHOLDA /= '1' loop
        wait on iBusClk until rising_edge(iBusClk);
      end loop;

      wait on iBusClk until rising_edge(iBusClk);
      LWnR <= '0' after cOutDelay;
      LA <= ToBusModel.Addr after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);
      nADS <= '0' after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);
      nADS <= '1' after cOutDelay;

      if ToBusModel.BurstMode = '1' then
        write (vLine, string'("LBusBurstRead:"));
        writeline (OUTPUT, vLine);
      end if;

      for BurstCount in 1 to ToBusModel.NoOfWords loop
        if BurstCount = ToBusModel.NoOfWords then
          nBLAST <= '0' after cOutDelay;
        end if;

        -- Wait for nREADY signal to be asserted
        wait on iBusClk until rising_edge(iBusClk);
        FromBusModel.Data <= LD after cOutDelay;

        while nREADY /= '0' loop
          wait on iBusClk until rising_edge(iBusClk);

          if ToBusModel.BurstMode /= '1' then
            FromBusModel.Data <= LD after cOutDelay;
          end if;
        end loop;

        if ToBusModel.BurstMode = '0' then
          write (vLine, string'("LBusRead: Addr:"));
          write (vLine, string'(Int2HexString( conv_integer(unsigned(ToBusModel.Addr)), 8)));
          write (vLine, string'(" Data:"));
          write (vLine, string'(Int2HexString( conv_integer(unsigned(LD)), 8)));
          writeline (OUTPUT, vLine);
        end if;

        if ToBusModel.BurstMode = '1' then
          FromBusModel.BurstData(BurstCount) <= conv_integer(unsigned(LD));
          write (vLine, string'("    Addr:"));
          write (vLine, string'(Int2HexString( conv_integer(unsigned(ToBusModel.Addr)) + (BurstCount-1)*4, 8)));
          write (vLine, string'("    Data:"));
          write (vLine, string'(Int2HexString( conv_integer(unsigned(LD)), 8)));
          writeline (OUTPUT, vLine);
        end if;

        LA <= unsigned(ToBusModel.Addr) + BurstCount*4 after cOutDelay;

      end loop;

      LA <= (others => 'Z') after cOutDelay;
      nBLAST <= '1' after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);
      LWnR <= '1' after cOutDelay;
      LHOLD <= '0' after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);

      vDone := true;  -- We have done a cycle


    else  -- ToBusModel.Write_nRead = '1'
      ---------------------
      -- 32-bit write cycle
      ---------------------

      -- Drive lines for write cycle
      LHOLD <= '1' after cOutDelay;

      -- Wait for LHOLDA to be asserted
      wait on iBusClk until rising_edge(iBusClk);
      while LHOLDA /= '1' loop
        wait on iBusClk until rising_edge(iBusClk);
      end loop;

      wait on iBusClk until rising_edge(iBusClk);
      LWnR <= '1' after cOutDelay;
      LA <= ToBusModel.Addr after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);
      nADS <= '0' after cOutDelay;
      if ToBusModel.BurstMode = '1' then
        LD <= conv_std_logic_vector(ToBusModel.BurstData(1), 32) after cOutDelay;
      else
        LD <= ToBusModel.Data after cOutDelay;
      end if;

      wait on iBusClk until rising_edge(iBusClk);
      nADS <= '1' after cOutDelay;

      for BurstCount in 1 to ToBusModel.NoOfWords loop
        if BurstCount = ToBusModel.NoOfWords then
          nBLAST <= '0' after cOutDelay;
        end if;

        -- Wait for nREADY signal to be asserted
        wait on iBusClk until rising_edge(iBusClk);
        FromBusModel.Data <= LD;
        while nREADY /= '0' loop
          wait on iBusClk until rising_edge(iBusClk);
          FromBusModel.Data <= LD;
        end loop;

        LA <= unsigned(ToBusModel.Addr) + BurstCount*4 after cOutDelay;

        if ToBusModel.BurstMode = '1' then
          LD <= conv_std_logic_vector(ToBusModel.BurstData(BurstCount+1), 32) after cOutDelay;
        end if;

        -- Write out type of cycle details to terminal
        if ToBusModel.BurstMode = '1' then
          if BurstCount = 1 then
            write (vLine, string'("LBusBurstWrite:"));
            writeline (OUTPUT, vLine);
            write (vLine, string'("    Addr:"));
          else
            write (vLine, string'("    Addr:"));
          end if;
        else
            write (vLine, string'("LBusWrite: Addr:"));
        end if;


        write (vLine, string'(Int2HexString( conv_integer(unsigned(ToBusModel.Addr)) + (BurstCount-1)*4, 8)));
        write (vLine, string'(" Data:"));
        write (vLine, string'(Int2HexString( conv_integer(unsigned(LD)), 8)));
        writeline (OUTPUT, vLine);
      end loop;

      LA <= (others => 'Z') after cOutDelay;
      LD <= (others => 'Z') after cOutDelay;
      nBLAST <= '1' after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);
      LWnR <= '1' after cOutDelay;
      LHOLD <= '0' after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);

      vDone := true;  -- We have done a cycle

    end if;

    -- Warn in no cycle was done
    assert  vDone
      report "BusModel: Unknown operation requested"
      severity error;

    -- Signal that the cycle is complete
    FromBusModel.Done <= '1';
    wait for 1 ps;
    FromBusModel.Done <= '0';

  end process Main;



  -- Process to generate the main bus clock
  GenBusClk: process
  begin
    iBusClk <= '0';
    if ToBusModel.ClkPeriod <= 0 ns then
      -- Clock period has not been set yet
      wait on ToBusModel.ClkPeriod until ToBusModel.ClkPeriod /= 0 ns;
    end if;

    iBusClk <= '1';
    wait for ToBusModel.ClkPeriod / 2;
    iBusClk <= '0';
    wait for ToBusModel.ClkPeriod / 2;
  end process GenBusClk;

  -- Connect the internal version to output pLCLKA
  LCLKA <= iBusClk;

  -- Connect the internal iBusClk to the Sim return signal
  FromBusModel.LCLKA <= iBusClk;


  -- Process to generate the main bus reset
  GenReset: process
  begin
    iReset <= '0';
    for ResetCount in 0 to 8 loop
      wait until rising_edge(iBusClk);
      iReset <= '1';
    end loop;
    iReset <= '0';
    wait;
  end process GenReset;

  -- Connect the internal version to output pnLRESET
  nLRESET <= not iReset;

  -- Connect the internal iReset to the Sim return signal
  FromBusModel.nLRESET <= not iReset;

  -- Connect the interrupt to the Sim return signal
  FromBusModel.nInt <= nLINTi;

end architecture Beh;
