---------------------------------------------------------------------------
-- $Id: FpgaProg.vhd 46 2005-03-08 09:53:50Z phil $
--
-- 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;
