---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2004
--
-- Filename: FpgaProg.vhd
-- Language: VHDL '93
-- Project:  Digitiser Card System FPGA
--
-- Design Description:
-- FPGA programming module for Digitiser Card System FPGA
--
-- fpgaprog.vhd
-- writes the formatted contents of system ram sequentially into a selected
-- pair of sampling / peripheral fpga's.
-- system ram contents formatted as follows...
-- All data written to low byte of each 32 bit word.
-- first 166980 (0x28c44) bytes are written to the sampling fpga
-- following 69900 (0x1110c) are written to corresponding peripheral fpga.
--
-- System fpga MUST be BUS MASTER or PLX chippy and system FPGA may end
-- up holding hands in silicon heaven.
--
-- ** NB ** program pulse min 300ns it says on spec sheet - poooo
--
---------------------------------------------------------------------------


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


entity FpgaProg is
  port (pnReset      : in  std_ulogic;
        pLCLKA       : in  std_ulogic;               -- local bus clock
        pLAout       : out std_logic_vector(31 downto 2);  -- local bus address output
        pFpgaLoad    : in  std_ulogic;             -- high starts the fpga configuration cogs
        pFpgaDone    : out std_ulogic;              -- high indicates completion of configuration
        pCClk        : out std_ulogic;               -- configuration clock
        pProgram     : out std_logic_vector(3 downto 0); -- trigger program sequence in fpga
        pDone        : in  std_ulogic;                  -- flags end of sucessfull programming sequence
        pInit        : in  std_ulogic;                  -- flags end of fpga mem clear or programming balls up
        pBusy        : in  std_ulogic;                  -- fpga busy
        pWhatFpga    : in  std_logic_vector(3 downto 0);  -- target fpga pair
        pFpgaWrite   : out std_ulogic;           -- fpga write
        pDspFpgaCS   : out std_logic_vector(3 downto 0); -- sample fpga cs
        pPerFpgaCS   : out std_logic_vector(3 downto 0);  -- peripheral fpga cs
        pSramOeReq   : out std_ulogic;
        pTest        : out std_logic);      -- test flag
end FpgaProg;



architecture Rtl of FpgaProg is
  signal address_counter: std_logic_vector(19 downto 0);
  signal program_toggle:  std_ulogic;
  signal done_toggle:     std_ulogic;
  signal init_toggle:     std_ulogic;
  signal load_toggle:     std_ulogic;
  signal program_counter: std_logic_vector(8 downto 0);

  signal state_var:       std_logic_vector(7 downto 0);

  signal DSPFpgaCS:       std_logic_vector(3 downto 0);
  signal PerFpgaCS:       std_logic_vector(3 downto 0);
  signal FpgaDone:        std_ulogic;

  signal Donei: std_ulogic;
  signal Done:  std_ulogic;

begin

  process(pFpgaLoad, pLCLKA)
  begin
    if pFpgaLoad = '0'  then
      pSramOeReq <= '0';               -- system ram OE not required
      pProgram <= "1111";   -- no program lines selected
      program_toggle <= '0';
      done_toggle <= '0';
      init_toggle <= '0';
      load_toggle <= '0';
      program_counter <= (others => '0');
--      FpgaDone <= '0';

    elsif rising_edge(pLCLKA) then
      --********************************************************************************************************
      --  the following sets the FPGA's up for programming and detects the signals from FPGA's indicating
      -- internal memory clearance, ready for programming and sucessful termination of programming
      --********************************************************************************************************
      pSramOeReq <= '1';

      if program_toggle = '0' then                   -- generate program pulse - min 300nS
        pProgram <= pWhatFpga;                       -- select a program line

        if(program_counter < X"C0") then                -- set program pulse width  - clock dependent
          program_counter <= unsigned(program_counter) + '1';       -- gives 64 * 15ns = 970ns - yonks :o)
        else
          program_toggle <= '1';                    -- flag to exit this block and enter next
        end if;

      else
        pProgram <= "1111";
      end if;

      if((program_toggle = '1') and (done_toggle = '0')) then   -- wait for the done flag from fpga
        if(Done = '0') then
          done_toggle <= '1';                     -- flag to exit this block and enter next
        else
          null;
        end if;
      end if;

      if((done_toggle = '1') and (init_toggle = '0')) then      -- wait for init to go down
        if(pInit = '0') then
          init_toggle <= '1';                     -- flag to exit this block and enter next
        else
          null;
        end if;
      end if;

      if((init_toggle = '1') and (load_toggle = '0'))then     -- wait for init to come back up
        if(pInit = '1') then
          load_toggle <= '1';                       -- ready to load the sucker
        else
          null;
        end if;
      end if;

      if((load_toggle = '1') and (FpgaDone = '1')) then  -- Raising of done flag indicates sucessful completion
--        FpgaDone <= '1';                              -- of programming FPGA's
        pSramOeReq <= '0';
        pProgram <= "1111";
        program_toggle <= '0';
        done_toggle <= '0';
        init_toggle <= '0';
        load_toggle <= '0';
        program_counter <= (others => '0');
      end if;                                         -- first fpga pulls the done flag - stopping second loading

      if((address_counter > X"3A000") and (Done = '0'))then   -- emergency exit
--        FpgaDone <= '1';
        pSramOeReq <= '0';
        pProgram <= "1111";
        program_toggle <= '0';
        done_toggle <= '0';
        init_toggle <= '0';
        load_toggle <= '0';
        program_counter <= (others => '0');
      end if;

    end if;
  end process;


  pFpgaDone <= FpgaDone;


  -- Light System FPGA LED when both FPGA done pins have gone high
  process(pnReset, pLCLKA)
  begin
    if pnReset  = '0' then
      pTest <= '1';
      Donei <= '0';
      Done  <= '0';
    elsif rising_edge(pLCLKA) then
      pTest <= not pDone;
      Donei <= pDone;
      Done  <= Donei;
    end if;
  end process;


  pDspFpgaCS <= DspFpgaCS;
  pPerFpgaCS <= PerFpgaCS;


  process(pnReset, pLCLKA)
    variable vLastLoadToggle: std_ulogic;
  begin
    if pnReset = '0' then
      state_var <= X"0F";
      address_counter <= (others => '0');
      pCClk <= '0';
      DspFpgaCS <= "1111";   -- de-select sampling fpga
      PerFpgaCS <= "1111";    -- de-select peripheral fpga
      pFpgaWrite <= '1';    -- no fpga write
      pLAout <= (others => '0');
      vLastLoadToggle := '0';
      FpgaDone <= '0';
    elsif rising_edge(pLCLKA) then
      case state_var is
        when X"0F" =>
          state_var <= X"0F";
          address_counter <= (others => '0');
          pCClk <= '0';
          DspFpgaCS <= "1111";   -- de-select sampling fpga
          PerFpgaCS <= "1111";    -- de-select peripheral fpga
          pFpgaWrite <= '1';    -- no fpga write
          FpgaDone <= '0';

          if load_toggle = '1' and vLastLoadToggle = '0' then
            -- Rising edge of load_toggle - begin programming
            state_var <= X"00";
          end if;

        when X"00" =>
          state_var <= X"0E";
          DspFpgaCS <= "1110";

        when X"0E" =>
          state_var <= X"01";

        when X"01" =>
          pFpgaWrite <= '0';        -- enable write to fpga
          state_var <= X"0D";

        when X"0D" =>
          state_var <= X"02";

        when X"02" =>
          pLAout(22 downto 2) <= '0' & address_counter(19 downto 0);   -- set ram address to current address counter value
          pCClk <= '0';                              -- make cclk harmless
          state_var <= X"03";                         -- next state
          if Done = '1' and  address_counter = X"00002" then
            state_var <= X"0F";  -- Early exit for simulation purposes only
            FpgaDone  <= '1';
          end if;

        when X"03" =>
            state_var <= X"04";

        when X"04" =>
          pCClk <= '1'; -- Clock data into FPGA
          state_var <= X"02"; -- next state
          address_counter <= unsigned(address_counter) + '1';         -- increment address counter
          case address_counter is
            when X"00000" =>
              DspFpgaCS <= "1110";       -- select sampling fpga
              PerFpgaCS <= "1111";       -- de-select peripheral fpga
            when X"28C43" =>
              DspFpgaCS <= "1111";        -- de-select sampling fpga
              PerFpgaCS <= "1110";        -- select peripheral fpga
            when X"39D49" =>
            --when X"39D50" =>
              -- This should be 0x39D50 in order to write 69900 bytes to the Peripheral FPGA
              -- with its CS low.  However 'correct' value doesn't work, whereas this 'wrong'
              -- value does...
              pCClk <= '0';  -- Don't clock the FPGA this time
              DspFpgaCS <= "1111";
              PerFpgaCS <= "1111";
              pFpgaWrite <= '1';
            when X"39D60" =>
              FpgaDone  <= '1';
              address_counter <= address_counter;  -- Do not count anymore
              state_var <= X"04";  -- Stay in this state
              state_var <= X"0F";  -- All done
            when others => null;
          end case;

        when others =>
          null;
      end case;

      vLastLoadToggle := load_toggle;
    end if;
  end process;


end Rtl;
