---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2004
--
-- Filename: SramWriteCtrl.vhd
-- Language: VHDL '93
-- Project:  Digitiser Card DSP FPGA
--
-- Design Description:
-- Module to Control write access to SRAM
---------------------------------------------------------------------------

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


entity SramWriteCtrl is
  port (pnReset         : in  std_ulogic;
        pADC_2xClk      : in  std_ulogic;  -- Frequency doubled version of clock
        pADC_Data       : in  std_logic_vector(11 downto 0);  -- Combined data stream from ADC
        pSampleEn       : in  std_ulogic;  -- Enable signal for sample data
        pAllTriggers    : in  std_ulogic;
        pLastTrigger    : in  std_ulogic;
        pAScanDone      : out std_ulogic;
        pGateEnables    : in  std_logic_vector(7 downto 0);
        pStartCapture   : in  std_ulogic;  -- Start capture strobe
        pCaptureDone    : out std_ulogic;  -- Capture has completed
        p12BitPack      : in  std_ulogic;
        p8BitStore      : in  std_ulogic;
        p8BitValues     : in  std_ulogic;
        pNumOfSamples   : in  std_logic_vector(14 downto 0);
        pCScanStartAddr : in  std_logic_vector(19 downto 0);
        pStorageMode    : in  std_logic_vector(1 downto 0);
        pResetElemNum   : in  std_ulogic;
        pResetAScanAddr : in  std_ulogic;
        pResetCScanAddr : in  std_ulogic;
        pEnc0Data       : in  std_logic_vector(31 downto 0);
        pEnc1Data       : in  std_logic_vector(31 downto 0);
        pEnc2Data       : in  std_logic_vector(31 downto 0);
        pEnc3Data       : in  std_logic_vector(31 downto 0);
        pEnc4Data       : in  std_logic_vector(31 downto 0);
        pEnc5Data       : in  std_logic_vector(31 downto 0);
        pGateData       : in  std_logic_vector(31 downto 0);
        pGateSelect     : out std_logic_vector(7 downto 0);
        pSram_nCS       : out std_logic_vector(3 downto 0);   -- Chip selects for SRAM
        pSram_nWEEn     : in std_ulogic;
        pSram_nWE       : out std_logic_vector(1 downto 0);   -- Write Enable for SRAM
        pSramData       : out std_logic_vector(31 downto 0);  -- Data bus for SRAM
        pSramAddr       : out std_logic_vector(17 downto 0)); -- Address bus for SRAM
end entity SramWriteCtrl;


architecture Rtl of SramWriteCtrl is

  signal InitialDelay1:    std_logic_vector(11 downto 0);
  signal InitialDelay2:    std_logic_vector(11 downto 0);
  signal InitialDelay3:    std_logic_vector(11 downto 0);
  signal ADC_Data:         std_logic_vector(11 downto 0);
  signal ADC_Data_delayed: std_logic_vector(11 downto 0);

  signal DataCount:      std_logic_vector(14 downto 0);
  signal DataCountEqOne: std_ulogic;

  -- Signals to generate SRAM address
  signal SramAddrCount:  std_logic_vector(19 downto 0);
  signal AScanAddr:      std_logic_vector(19 downto 0);
  signal CScanAddr:      std_logic_vector(19 downto 0);
  signal AddrCountSel:   std_ulogic;
  signal AddrCountLoad:  std_ulogic;
  signal AddrCountInc:   std_ulogic;
  signal AddrCount:      std_logic_vector(19 downto 0);
  signal AScanAddrSave:  std_ulogic;
  signal CScanAddrSave:  std_ulogic;

  signal Sram_nCS:       std_logic_vector(3 downto 0);
  signal CsActive:       std_ulogic;
  signal WeActive:       std_ulogic;
  signal Sram_nWE:       std_logic_vector(1 downto 0);
  signal SramDataSel:    std_logic_vector(2 downto 0);
  signal SramDataSel3:   std_ulogic;
  signal SramData:       std_logic_vector(31 downto 0);
  signal PackCount:      std_logic_vector(1 downto 0);
  signal IndexCount:     std_logic_vector(7 downto 0);
  signal StoredCount:    std_logic_vector(7 downto 0);
  signal CurrentGate:    std_logic_vector(7 downto 0);
  signal GateEnables:    std_logic_vector(7 downto 0);
  signal NoMoreGates:    std_ulogic;
  signal NextEncoderData: std_logic_vector(31 downto 0);
  signal EncoderData:    std_logic_vector(31 downto 0);
  signal EncDataSel:     std_logic_vector(7 downto 0);
  signal NextDataReady:  std_ulogic;

  signal StoreCScanEn:   std_ulogic;

  -- Control logic States
  type tCtrlStates is (sIdle, sStoreAScan1, sStoreAScan2, sStoreAEncData1, sStoreAEncData2,
                       sStoreAEncDelay, sStoreCEncData1, sStoreCEncData2, sStoreHeader1, sStoreHeader2,
                       sStoreCDelay, sStoreData1, sStoreData2, sEndStore, sStoreDone);
  signal CtrlState: tCtrlStates;
  attribute uselowskewlines: string;
  attribute uselowskewlines of AddrCountLoad : signal is "yes";

begin


  -- Generate a signal when C-Scan data is to be stored
  StoreCScanEn <= '1' when pStorageMode /= "00" else '0';

  -- Process to produce initial 4 clock cycle delay of sample datastream
  -- This is required to match data in memory with processed data values
  DelayAdcData: process(pADC_2xClk)
  begin
    -- By not resetting this, shift register can use a Xilinx SRL16 and use 1/4 of the slices
    if rising_edge(pADC_2xClk) then
      if pSampleEn = '1' then
        InitialDelay1    <= pADC_Data;
        InitialDelay2    <= InitialDelay1;
        InitialDelay3    <= InitialDelay2;
        ADC_Data         <= InitialDelay3;
        ADC_Data_delayed <= ADC_Data;
      end if;
    end if;
  end process DelayAdcData;


  -- Generate Index count
  GenIndexCount: process(pnReset, pADC_2xClk)
    variable vLastAllTriggers: std_ulogic;
  begin
    if pnReset = '0' then
      IndexCount       <= X"FF";
      vLastAllTriggers := '0';
    elsif rising_edge(pADC_2xClk) then
      if pResetElemNum = '1' then
        IndexCount <= X"FF";

      elsif pAllTriggers = '1' and vLastAllTriggers = '0' then
        -- Increment index count for all triggers, not just valid triggers
        IndexCount <= (unsigned(IndexCount) + 1) and X"FF";
      end if;

      vLastAllTriggers := pAllTriggers;
    end if;
  end process GenIndexCount;


  WriteCtrl: process(pnReset, pADC_2xClk)
  begin
    if pnReset = '0' then
      SramDataSel     <= "000";
      SramDataSel3    <= '0';
      DataCount       <= (others => '0');
      DataCountEqOne  <= '1';
      CsActive        <= '0';
      WeActive        <= '0';
      CtrlState       <= sIdle;
      pCaptureDone    <= '0';
      CurrentGate     <= "00000001";
      GateEnables     <= "00000000";
      EncDataSel      <= "00000001";

      AddrCountSel    <= '0';
      AddrCountLoad   <= '0';
      AScanAddrSave   <= '0';
      CScanAddrSave   <= '0';
      AddrCountInc    <= '0';

    elsif rising_edge(pADC_2xClk) then

      -- Create DataCount = 1 flag to reduce decode time
      if DataCount = "000000000000001" then
        DataCountEqOne <= '1';
      else
        DataCountEqOne <= '0';
      end if;

      SramDataSel     <= "000";
      SramDataSel3    <= '0';
      AddrCountSel    <= '0';
      AddrCountLoad   <= '0';
      AScanAddrSave   <= '0';
      CScanAddrSave   <= '0';
      AddrCountInc    <= '0';
      WeActive        <= '0';

      case CtrlState is
        when sIdle =>
          -- We are waiting to store A-Scan data
          pCaptureDone <= '0';
          GateEnables  <= pGateEnables;
 --         NoMoreGates  <= '0';
          -- Load A-Scan address in to address counter
          AddrCountLoad <= '1';
          AddrCountSel  <= '0';
          SramDataSel   <= "001";
          EncDataSel    <= "00000001";

          if pStartCapture = '1' and pSampleEn = '1' then
            CsActive   <= '1';
            WeActive   <= '1';
            DataCount  <= pNumOfSamples;

            -- Make first the word of sample data an index count
            -- To allow missing scan data to be spotted
            CtrlState <= sStoreAScan2;
          end if;

        when sStoreAScan1 =>
          if pSampleEn = '1' then
            DataCount    <= unsigned(DataCount) - 1;
            CtrlState    <= sStoreAScan2;
            CsActive     <= '1';
            WeActive     <= '1';
          else
            CtrlState    <= sStoreAScan1;
            CsActive     <= '1';
          end if;

          if DataCountEqOne = '1' then
            -- Correct amount of data written, A-Scan storing operation complete
            if pLastTrigger = '1' then
              -- This is the last A-Scan of the element array
              --Store Shaft encoder data
              CtrlState      <= sStoreAEncDelay;
              CsActive       <= '0';
              WeActive       <= '0';
              -- Select Encoder data for SRAM
              SramDataSel   <= "100";
            else
              if StoreCScanEn = '1' then
                -- C-Scan data storage is enabled
                CtrlState   <= sStoreHeader1;
                CsActive    <= '0';
                WeActive    <= '0';
                -- Select C-Scan header for SRAM data
                SramDataSel   <= "010";
                -- Load C-Scan address in to address counter
                AddrCountLoad <= '1';
                AddrCountSel  <= '1';
                -- Save A-Scan address
                AScanAddrSave <= '1';
              else
                -- C-CScan data storage is disabled
                -- Go back to the idle state
                CtrlState      <= sIdle;
                CsActive       <= '0';
                WeActive       <= '0';
                AScanAddrSave  <= '1';
                pCaptureDone   <= '1';
              end if;
            end if;
          end if;

        when sStoreAScan2 =>
          if pSampleEn = '1' and NextDataReady = '1' then
            CtrlState    <= sStoreAScan1;
            SramDataSel  <= "011";  -- Select A-Scan data for SRAM
            SramDataSel3 <= '1';
            -- Increment the address count
            CsActive     <= '1';
            AddrCountInc <= '1';
          else
            CtrlState    <= sStoreAScan2;
            CsActive     <= '1';
          end if;

        when sStoreAEncDelay =>
          CtrlState    <= sStoreAEncData2;
          CsActive     <= '1';
          WeActive     <= '1';
          EncDataSel   <= EncDataSel(6 downto 0) & '0';

        when sStoreAEncData1 =>
          if EncDataSel(0) = '1' then
          -- Encoder values have been stored in the A-SCan data area
            if StoreCScanEn = '1' then
              -- C-Scan data storage is enabled
              CtrlState   <= sStoreHeader1;
              CsActive    <= '0';
              WeActive    <= '0';
              -- Select C-Scan header for SRAM data
              SramDataSel   <= "010";
              -- Load C-Scan address in to address counter
              AddrCountLoad <= '1';
              AddrCountSel  <= '1';
              -- Save A-Scan address
              AScanAddrSave <= '1';
            else
              -- C-Scan data storage is disabled
              -- GO back to the idle state
              CtrlState      <= sIdle;
              CsActive       <= '0';
              WeActive       <= '0';
              AScanAddrSave  <= '1';
              pCaptureDone   <= '1';
            end if;
          else
            WeActive    <= '1';
            CtrlState   <= sStoreAEncData2;
          end if;

        when sStoreAEncData2 =>
          CtrlState    <= sStoreAEncData1;
          SramDataSel  <= "100";  -- Select Encoder Data for SRAM
          AddrCountInc <= '1';
          if EncDataSel(6) /= '1' then
            EncDataSel   <= EncDataSel(6 downto 0) & '0';
          else
            EncDataSel <= "00000001";
          end if;

        when sStoreCDelay =>
          CsActive    <= '1';
          CtrlState   <= sStoreCEncData2;
          WeActive    <= '1';
          EncDataSel  <= EncDataSel(6 downto 0) & '0';

        when sStoreCEncData1 =>
            if EncDataSel(7) = '1' then
            -- Store C-Scan data
            CtrlState  <= sStoreDone;
            CsActive    <= '0';
            -- Select C-Scan header for SRAM data
            SramDataSel   <= "010";
          else
            CtrlState   <= sStoreCEncData2;
            WeActive    <= '1';
          end if;

        when sStoreCEncData2 =>
          CtrlState    <= sStoreCEncData1;
          EncDataSel   <= EncDataSel(6 downto 0) & '0';
          AddrCountInc <= '1';
          -- Select Encoder Data for SRAM data
          SramDataSel   <= "100";

        when sStoreHeader1 =>
          -- Write header word to SRAM
          -- To allow missing scan data to be spotted
          CsActive  <= '1';
          WeActive  <= '1';
          CtrlState <= sStoreHeader2;

        when sStoreHeader2 =>
          CsActive       <= '1';
          AddrCountInc   <= '1';
          CtrlState      <= sStoreData1;
          -- Select Gate data for SRAM data
          SramDataSel   <= "101";

        when sStoreData1 =>
          -- Check for enabled gate
          if GateEnables(0) = '1' then
            -- The current gate is enabled, write its results to SRAM
            -- Begin store operation
            CsActive  <= '1';
            WeActive  <= '1';
            CtrlState <= sStoreData2;
          else
            -- The current gate is not enabled, check the next one
            -- If there is one
            if NoMoreGates = '0' then
              CurrentGate <= CurrentGate(6 downto 0) & '0';
              GateEnables <= '0' & GateEnables(7 downto 1);

              CtrlState  <= sStoreData1;
              -- Select Gate data for SRAM data
              SramDataSel   <= "101";

            else
              -- All data for enabled enabled gates has been written to SRAM
              -- Store C-Scan Encoder data
              if pLastTrigger = '1' then
                CtrlState   <= sStoreCDelay;
                CsActive    <= '0';
                -- Select Encoder data for SRAM
                SramDataSel   <= "100";
                EncDataSel    <= "00000001";
              else
                -- Store C-Scan data
                CtrlState   <= sStoreDone;
                CsActive    <= '0';
                WeActive    <= '0';
              end if;
            end if;
          end if;

        when sStoreData2 =>
          -- Complete store operation
          CsActive       <= '1';
          CtrlState      <= sEndStore;
          AddrCountInc   <= '1';

        when sEndStore =>
          CsActive      <= '0';
          CurrentGate   <= CurrentGate(6 downto 0) & '0';
          GateEnables   <= '0' & GateEnables(7 downto 1);
          CtrlState     <= sStoreData1;
          SramDataSel   <= "101";

        when sStoreDone =>
          CsActive       <= '0';
          pCaptureDone   <= '1';
          CtrlState      <= sIdle;
          -- Save C-Scan address
          CScanAddrSave  <= '1';
          CurrentGate    <= "00000001";

        when others =>
          null;
      end case;
    end if;
  end process WriteCtrl;

  pAScanDone <= DataCountEqOne;

  GenNoMoreGates: process(pnReset, pADC_2xClk)
  begin
    if pnReset = '0' then
      NoMoreGates <= '0';
    elsif rising_edge(pADC_2xClk) then
      if GateEnables(7 downto 1) = "0000000" then
        NoMoreGates <= '1';
      else
        NoMoreGates <= '0';
      end if;
    end if;
  end process GenNoMoreGates;


  -- Create the SRAM address
  GenSramAddr: process(pnReset, pADC_2xClk)
  begin
    if pnReset = '0' then
      SramAddrCount <= X"00000";
      AScanAddr     <= X"00000";
      CScanAddr     <= X"00000";
    elsif rising_edge(pADC_2xClk) then
      if AddrCountInc = '1' then
        SramAddrCount <= (unsigned(SramAddrCount) + 1) and X"FFFFF";
      elsif AddrCountLoad = '1' then
        case AddrCountSel is
          when '0' => SramAddrCount <= AScanAddr;
          when '1' => SramAddrCount <= CScanAddr;
          when others => null;
        end case;
      end if;

      if pResetAScanAddr = '1' then
        AScanAddr <= X"00000";
      elsif AScanAddrSave = '1' then
        AScanAddr <= SramAddrCount;
      end if;

      if pResetCScanAddr = '1' then
        CScanAddr <= pCScanStartAddr;
      elsif CScanAddrSave = '1' then
        CScanAddr <= SramAddrCount;
      end if;
    end if;
  end process GenSramAddr;


  AddrCount    <= SramAddrCount;
  pGateSelect  <= CurrentGate;


  GenSram_nCS: process(CsActive, AddrCount)
  begin
    Sram_nCS <= "1111";

    if CsActive = '1' then
      case AddrCount(19 downto 18) is
        when "00"   => Sram_nCS <= "1110";
        when "01"   => Sram_nCS <= "1101";
        when "10"   => Sram_nCS <= "1011";
        when others => Sram_nCS <= "0111";  -- "11"
      end case;
    end if;
  end process GenSram_nCS;

  NextEncoderData <= pEnc0Data when EncDataSel(0) = '1' else (others => 'Z');
  NextEncoderData <= pEnc1Data when EncDataSel(1) = '1' else (others => 'Z');
  NextEncoderData <= pEnc2Data when EncDataSel(2) = '1' else (others => 'Z');
  NextEncoderData <= pEnc3Data when EncDataSel(3) = '1' else (others => 'Z');
  NextEncoderData <= pEnc4Data when EncDataSel(4) = '1' else (others => 'Z');
  NextEncoderData <= pEnc5Data when EncDataSel(5) = '1' else (others => 'Z');

  GenEncoderData: process(pnReset, pADC_2xClk)
  begin
    if pnReset = '0' then
      EncoderData <= X"00000000";
    elsif rising_edge(pADC_2xClk) then
      EncoderData <= NextEncoderData;
    end if;
  end process GenEncoderData;


  GenNextDataReady: process(pnReset, pADC_2xClk)
    variable vCount: integer range 0 to 3;
  begin
    if pnReset = '0' then
      NextDataReady <= '1';
      vCount := 0;
      PackCount <= "00";
    elsif rising_edge(pADC_2xClk) then
      if pSampleEn = '1' then
        if p8BitStore = '0' and p12BitPack = '0' then
          NextDataReady <= '1';
        else
          if vCount = 0 then
            NextDataReady <= '1';
          else
            NextDataReady <= '0';
          end if;
        end if;
      end if;

      if SramDataSel3 = '1' then
        if PackCount = "10" or p12BitPack = '0' then
          vCount := 2;
        end if;
        if p12BitPack = '1' then
          PackCount <= unsigned(PackCount) + 1;
        end if;
      elsif pSampleEn = '1' then
        if vCount /= 0 then
          vCount := vCount - 1;
        end if;
      end if;
      if AddrCountLoad = '1' then
        PackCount <= "00";
      end if;
    end if;
  end process GenNextDataReady;

  -- Generate the SRAM data as controlled by the main state machine
  GenSramData: process(pnReset, pADC_2xClk)
  begin
    if pnReset = '0' then
      SramData    <= (others => '0');
      StoredCount <= (others => '0');
    elsif rising_edge(pADC_2xClk) then
      case SramDataSel is
        when "000" => null;

        when "001" =>  -- A-Scan Data Header
          SramData(7 downto 0)  <= IndexCount;
          SramData(31 downto 8) <= X"41534E";  -- ASCII 'A' 'S' 'N' for A-SCAN data
          StoredCount <= IndexCount;

        when "010" =>  -- C-Scan Data Header
          SramData(7 downto 0) <= StoredCount;
          SramData(31 downto 8) <= X"43534E";  -- ASCII 'C' 'S' 'N' for C-SCAN data

        when "011" =>  -- A-Scan Data
--          if p8BitStore = '0' then
--            if p12BitPack = '0' then
              -- Create datawords with 12 bit data
              SramData(15 downto 11) <= (others => ADC_Data_delayed(11));
              SramData(10 downto 0)  <= ADC_Data_delayed(10 downto 0);
              SramData(31 downto 27) <= (others => ADC_Data(11));
              SramData(26 downto 16) <= ADC_Data(10 downto 0);
--            elsif PackCount = "00" then
--              -- Pack datawords with 12 bit data
--              SramData(11 downto 0)  <= ADC_Data_delayed;
--              SramData(23 downto 12) <= ADC_Data;
--              SramData(31 downto 24) <= InitialDelay3(7 downto 0);
--            elsif PackCount = "01" then
--              SramData(3 downto 0)   <= ADC_Data_delayed(11 downto 8);
--              SramData(15 downto 4)  <= ADC_Data;
--              SramData(27 downto 16) <= InitialDelay3;
--              SramData(31 downto 28) <= InitialDelay2(3 downto 0);
--            else
--              SramData(7 downto 0)   <= ADC_Data(11 downto 4);
--              SramData(19 downto 8)  <= InitialDelay3;
--              SramData(31 downto 20) <= InitialDelay2;
--            end if;
--          else
--            if p8BitValues = '0' then
--              -- Create datawords with 8 bit data (with 12-bit values)
--              SramData(7 downto 0)   <= ADC_Data_delayed(11 downto 4);
--              SramData(15 downto 8)  <= ADC_Data(11 downto 4);
--              SramData(23 downto 16) <= InitialDelay3(11 downto 4);
--              SramData(31 downto 24) <= InitialDelay2(11 downto 4);
--            else
--              -- Create datawords with 8 bit data (with 8-bit values)
--              SramData(7 downto 0)   <= ADC_Data_delayed(7 downto 0);
--              SramData(15 downto 8)  <= ADC_Data(7 downto 0);
--              SramData(23 downto 16) <= InitialDelay3(7 downto 0);
--              SramData(31 downto 24) <= InitialDelay2(7 downto 0);
--            end if;
--          end if;

        when "100" =>  -- Encoder Data
          SramData<= EncoderData;

        when "101" =>  -- Gate Data
          SramData  <= pGateData;

        when others => null;
      end case;
    end if;
  end process GenSramData;


  -- Clock the SRAM signals out through D-types
  -- This helps the timing a lot
  RegisterOutputs: process(pnReset, pADC_2xClk)
  begin
    if pnReset = '0' then
      pSramAddr <= (others => '0');
      pSram_nCS <= "1111";
    elsif falling_edge(pADC_2xClk) then
      pSramAddr <= AddrCount(17 downto 0);
      pSram_nCS <= Sram_nCS;
    end if;
  end process RegisterOutputs;

  RegisterData: process(pnReset, pADC_2xClk)
  begin
    if pnReset = '0' then
      pSramData <= (others => '0');
    elsif rising_edge(pADC_2xClk) then
      pSramData <= SramData;
    end if;
  end process RegisterData;

  -- Delay the SRAM nWE strobe until the falling edge of the clock
  RegisternWE: process(pnReset, pADC_2xClk)
  begin
    if pnReset = '0' then
      Sram_nWE  <= "11";
      pSram_nWE <= "11";
    elsif falling_edge(pADC_2xClk) then
      Sram_nWE  <= "11";
      if pSram_nWEEn = '0' then
        Sram_nWE(0) <= not WeActive;
      else
        Sram_nWE(1) <= not WeActive;
      end if;
      pSram_nWE <= Sram_nWE;
    end if;
  end process RegisternWE;


end Rtl;

