---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2004
--
-- Filename: LocalBusModel.vhd
-- Language: VHDL '93
-- Project:  Digitiser Card FPGAs
--
-- Design Description:
-- Bus model based on the PLX 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(-- Control signals to/from main simulation module
       ToBusModel  : in  tToBusModel;
       FromBusModel: out tFromBusModel;
       -- Bus Signals
       pnLRESET    : out   std_ulogic;
       pLCLKA      : out   std_ulogic;
       pLWnR       : out   std_ulogic;
       pnADS       : out   std_ulogic;
       pnBLAST     : out   std_ulogic;
       pnREADY     : in    std_ulogic;
       pLA         : inout std_logic_vector(31 downto 0);
       pLD         : inout std_logic_vector(31 downto 0);
       pLHOLD      : out   std_ulogic;
       pLHOLDA     : in    std_ulogic;
       pnBTERM     : in    std_ulogic;
       pnCCS       : in    std_ulogic;
       pBREQi      : in    std_ulogic);
end entity LocalBusModel;


architecture beh of LocalBusModel is

  constant cOutDelay: time := 9 ns;  -- 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 vHexOut: string(1 to NibblesOut + 2);
  begin
    vUnsigned := conv_unsigned(IntIn, NibblesOut*4);

    for count in NibblesOut downto 1 loop
      case unsigned(3 downto 0)(vUnsigned(Count*4 - 1 downto Count*4 - 4)) 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

  -- 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
    pLWnR    <= '1';
    pnADS    <= '1';
    pnBLAST  <= '1';
    pLA      <= (others => 'Z');
    pLD      <= (others => 'Z');
    pLHOLD   <= '0';

    wait until falling_edge(ToBusModel.Start);

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

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

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

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

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

      wait on iBusClk until rising_edge(iBusClk);
      pnADS <= '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
          pnBLAST <= '0' after cOutDelay;
        end if;

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

        while pnREADY = '1' loop
          wait on iBusClk until rising_edge(iBusClk);

          if ToBusModel.BurstMode /= '1' then
            FromBusModel.Data <= pLD 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(pLD)), 8)));
          writeline (OUTPUT, vLine);
        end if;

        if ToBusModel.BurstMode = '1' then
          FromBusModel.BurstData(BurstCount) <= conv_integer(unsigned(pLD));
          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(pLD)), 8)));
          writeline (OUTPUT, vLine);
        end if;

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

      end loop;

      pLA <= (others => 'Z') after cOutDelay;
      pnBLAST <= '1' after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);
      pLWnR <= '1' after cOutDelay;
      pLHOLD <= '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
      pLHOLD <= '1' after cOutDelay;

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

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

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

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

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

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

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

        if ToBusModel.BurstMode = '1' then
          pLD <= 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(pLD)), 8)));
        writeline (OUTPUT, vLine);
      end loop;

      pLA <= (others => 'Z') after cOutDelay;
      pLD <= (others => 'Z') after cOutDelay;
      pnBLAST <= '1' after cOutDelay;

      wait on iBusClk until rising_edge(iBusClk);
      pLWnR <= '1' after cOutDelay;
      pLHOLD <= '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
  pLCLKA <= 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
  pnLRESET <= not iReset;

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


end architecture beh;
