---------------------------------------------------------------------------
-- NDT Solutions Ltd. Proprietary Information
-- Company Confidential
-- Copyright 2007
--
-- Filename: DspLBusInterface.vhd
-- Language: VHDL '93
-- Project:  NDTS2100 DSP FPGA
--
-- Design Description:
-- Module to interface the local bus with the rest of the system
---------------------------------------------------------------------------

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;

library work;
use work.DspFpgaPkg.all;

entity DspLBusInterface is
  port (-- Local bus
        nReset            : in  std_ulogic;
        LClk              : in  std_ulogic;
        LWnR              : in  std_ulogic;
        LDIn              : in  std_logic_vector(31 downto 0);
        LDOut             : out std_logic_vector(31 downto 0);
        nLDOe             : out std_ulogic;
        LAin              : in  std_logic_vector(31 downto 2);
        nAds              : in  std_ulogic;
        nBLast            : in  std_ulogic;
        nReady            : out std_ulogic;
        nBTerm            : out std_ulogic;
        LHold             : in  std_ulogic;
        LHoldA            : out std_ulogic;
        BReqi             : out std_ulogic;
        nLINTi            : out std_ulogic;
        -- TCG RAM processor bus
        TCGRamWrData      : out std_logic_vector(11 downto 0);
        TCGRamRdData      : in  std_logic_vector(11 downto 0);
        TCGRamAddr        : out std_logic_vector(17 downto 0);
        TCGRamWr          : out std_ulogic;
        TCGRamRd          : out std_ulogic;
        TCGRamAck         : in  std_ulogic;
        -- SDRAM processor bus
        LRamWrData        : out std_logic_vector(31 downto 0);
        LRamRdData        : in  std_logic_vector(31 downto 0);
        LRamAddr          : out std_logic_vector(30 downto 0);
        LRamWr            : out std_ulogic;
        LRamRd            : out std_ulogic;
        LRamAck           : in  std_ulogic;
        -- ADC controls
        RawAdcRd          : out std_ulogic;
        RawAdcData        : in  std_logic_vector(11 downto 0);
        AdcOffset         : out std_logic_vector(12 downto 0);
        DropSamples       : out std_logic_vector(15 downto 0);
        RectMode          : out std_logic_vector(1 downto 0);
        RectMod8Bit       : out std_ulogic;
        TestPatternMode   : out std_ulogic;
        SignExtendMode    : out std_ulogic;
        SignAndMagMode    : out std_ulogic;
        AdcSerWr          : out std_ulogic;
        AdcSerInst        : out std_logic_vector(15 downto 0);
        AdcSerDOut        : out std_logic_vector(7 downto 0);
        AdcSerDIn         : in  std_logic_vector(7 downto 0);
        AdcResetWr        : out std_ulogic;
        AdcBusy           : in  std_ulogic;
        -- DDS  Controls
        DdsData           : out std_logic_vector(15 downto 0);
        DdsWr             : out std_ulogic;
        DdsBusy           : in  std_ulogic;
        DdsSleep          : out std_ulogic;
        DdsReset          : out std_ulogic;
        DdsPSelect        : out std_ulogic;
        DdsFSelect        : out std_ulogic;
        nDdsOscInh        : out std_ulogic;
        -- HV Controls
        HVDACData         : out std_logic_vector(15 downto 0);
        HVDACWr           : out std_ulogic;
        HVDACBusy         : in  std_ulogic;
        nHVEnable         : out std_ulogic;
        nHVDisChg         : out std_ulogic;
        HVFeedback        : in  std_ulogic;
        -- Gate controls
        Holdoff           : out std_logic_vector(7 downto 0);
        GateStart         : out tGateData24;
        GateStop          : out tGateData24;
        GateAttrib        : out tGateData32;
        -- Data capture controls
        AScanRepRate      : out std_logic_vector(23 downto 0);
        AScanRange        : out std_logic_vector(23 downto 0);
        BankSize          : out std_logic_vector(30 downto 0);
        BankSel           : in  std_ulogic;
        CScanOffsetAddr   : out std_logic_vector(30 downto 0);
        StorageMode       : out std_logic_vector(1 downto 0);
        SetDataReady      : in  std_ulogic;
        SetNewTrigger     : in  std_ulogic;
        SetEarlyTrigError : in  std_ulogic;
        SetDataFullError  : in  std_ulogic;
        SomeDataReady     : in  std_ulogic;
        DataReady         : out std_ulogic;
        EarlyDataReq      : out std_ulogic;
        ResetSampling     : out std_ulogic;
        -- Sequence controls
        SequenceRepRate   : out std_logic_vector(31 downto 0);
        SequencesPerBank  : out std_logic_vector(15 downto 0);
        SequenceSteps     : out std_logic_vector(4 downto 0);
        StartSequence     : out std_ulogic;
        SequencesInRam    : in  std_logic_vector(15 downto 0);
        SequenceStep      : in  std_logic_vector(4 downto 0);
        -- Trigger pulse controls
        TriggerDelay      : out std_logic_vector(23 downto 0);
        PulseWidth        : out std_logic_vector(15 downto 0);
        PulseTrain        : out std_logic_vector(3 downto 0);
        ExtTriggerEn      : out std_ulogic;
        FreeRunningEn     : out std_ulogic;
        EncoderTriggerEn  : out std_ulogic;
        ForceTrigger      : out std_ulogic;
        SyncPulseEn       : out std_ulogic;
        -- TCG controls
        TCGAbsoluteTrig   : out std_ulogic;
        TCGEnable         : out std_ulogic;
        TCGBEAGate        : out std_logic_vector(2 downto 0);
        TCGBEAEnable      : out std_ulogic;
        TCGPoints         : out std_logic_vector(18 downto 0);
        TCGSamples        : out std_logic_vector(15 downto 0);
        -- Encoder Controls
        EncoderReset      : out std_logic_vector(8 downto 0);
        EncoderSelect     : out std_logic_vector(3 downto 0);
        ClicksPerSeq      : out std_logic_vector(23 downto 0);
        FractClicks       : out std_logic_vector(15 downto 0);
        EncoderCount0     : in  std_logic_vector(31 downto 0);
        EncoderCount1     : in  std_logic_vector(31 downto 0);
        EncoderCount2     : in  std_logic_vector(31 downto 0);
        EncoderCount3     : in  std_logic_vector(31 downto 0);
        DualEncoderCount  : in  std_logic_vector(31 downto 0);
        PulseCount0       : in  std_logic_vector(31 downto 0);
        PulseCount1       : in  std_logic_vector(31 downto 0);
        PulseCount2       : in  std_logic_vector(31 downto 0);
        PulseCount3       : in  std_logic_vector(31 downto 0);
        -- Misc Controls
        InputSelUpdate    : out std_ulogic;
        FilterCtrlUpdate  : out std_ulogic;
        IpSelReg          : out std_logic_vector(31 downto 0);
        FilterCtrlReg     : out std_logic_vector(15 downto 0);
        Relay             : out std_ulogic;
        Ident             : in  std_logic_vector(3 downto 0);
        SoDimmSize        : in  std_logic_vector(3 downto 0);
        SoDimmVal         : in  std_ulogic);
end entity DspLBusInterface;


architecture Rtl of DspLBusInterface is

  component StrobeExtender is
    generic(gLfsrBits: integer range 3 to 32 := 3);
    port(nReset    : in  std_ulogic;
         Clk       : in  std_ulogic;
         StrobeIn  : in  std_ulogic;
         StrobeOut : out std_ulogic);
  end component StrobeExtender;

  component DPRam32
    generic(gDataWidth:    integer range 1 to 32 := 32);
    port(Clk     : in  std_ulogic;
         WrEn    : in  std_ulogic;
         WrAddr  : in  std_logic_vector(4 downto 0);
         RdAddr  : in  std_logic_vector(4 downto 0);
         WrData  : in  std_logic_vector(gDataWidth-1 downto 0);
         RdData  : out std_logic_vector(gDataWidth-1 downto 0);
         DataOut : out std_logic_vector(gDataWidth-1 downto 0));
  end component;

  signal nReadyPulse  : std_ulogic;
  signal nReadyOe     : std_ulogic;

  signal Addr     : std_logic_vector(31 downto 0);
  signal WrnRd    : std_ulogic;
  signal SysWr    : std_ulogic;
  signal SysRd    : std_ulogic;
  signal WrData   : std_logic_vector(31 downto 0);
  signal RdData   : std_logic_vector(31 downto 0);

  type tState is (Idle, Add, Wr, RdDel, Rd, BurstNext, WrRam, RdRam, WrTCGRam, RdTCGRam);
  signal State    : tState;

  -- Internal registers
  type tReg is array (0 to cLastReg) of std_logic_vector(31 downto 0);
  signal Reg: tReg;
  signal RegSel: std_logic_vector(cLastReg downto 0);

  signal IpSelWrEn: std_ulogic;
  signal FilterCtrlWrEn: std_ulogic;
  signal RectifierModeWrEn: std_ulogic;
  signal AdcOffsetWrEn: std_ulogic;
  signal DropSamplesWrEn: std_ulogic;
  signal AScanRepRateWrEn: std_ulogic;
  signal PulseWidthWrEn: std_ulogic;
  signal PulseTrainWrEn: std_ulogic;
  signal AScanRangeWrEn: std_ulogic;
  signal TriggerDelayWrEn: std_ulogic;
  signal TCGConfigureWrEn: std_ulogic;
  signal TCGPointsWrEn: std_ulogic;
  signal TCGSamplesWrEn: std_ulogic;
  signal HoldoffWrEn: std_ulogic;
  signal SeqWrAddr: std_logic_vector(31 downto 0);

  signal IpSelWrData : std_logic_vector(31 downto 0);
  signal IpSelRegRd : std_logic_vector(31 downto 0);
  signal FilterCtrlRegRd: std_logic_vector(15 downto 0);
  signal RectifierMode: std_logic_vector(2 downto 0);
  signal RectifierModeRd: std_logic_vector(2 downto 0);
  signal AdcOffsetRd: std_logic_vector(12 downto 0);
  signal DropSamplesRd: std_logic_vector(15 downto 0);
  signal AScanRepRateRd: std_logic_vector(23 downto 0);
  signal PulseWidthRd: std_logic_vector(15 downto 0);
  signal PulseTrainRd: std_logic_vector(3 downto 0);
  signal AScanRangeRd: std_logic_vector(23 downto 0);
  signal TriggerDelayRd: std_logic_vector(23 downto 0);
  signal TCGConfigure: std_logic_vector(11 downto 0);
  signal TCGConfigureRd: std_logic_vector(11 downto 0);
  signal TCGPointsRd: std_logic_vector(18 downto 0);
  signal TCGSamplesRd: std_logic_vector(15 downto 0);
  signal HoldoffRd: std_logic_vector(7 downto 0);

  signal GateStartWrEn: std_logic_vector(cLastGate downto 0);
  signal GateStopWrEn: std_logic_vector(cLastGate downto 0);
  signal GateAttribWrEn: std_logic_vector(cLastGate downto 0);
  signal GateSeqWrAddr: std_logic_vector(31 downto 0);

  signal GateWrData: std_logic_vector(31 downto 0);
  signal GateAttribWrData: std_logic_vector(31 downto 0);
  signal GateStartRdAll: tGateData24;
  signal GateStopRdAll: tGateData24;
  signal GateAttribRdAll: tGateData32;
  signal GateStartRd: std_logic_vector(23 downto 0);
  signal GateStopRd: std_logic_vector(23 downto 0);
  signal GateAttribRd: std_logic_vector(31 downto 0);

  signal InputSelWr   : std_ulogic;
  signal FilterCtrlWr : std_ulogic;
  signal DdsResetPulse: std_ulogic;

  signal DataReadyBit   : std_ulogic;
  signal BankBit        : std_ulogic;
  signal NewTrigger     : std_ulogic;
  signal EarlyTrigError : std_ulogic;
  signal DataFullError  : std_ulogic;

  attribute iob : string;

  attribute iob of nReadyPulse: signal is "true";
  attribute iob of nLdOe      : signal is "true";

begin

  -- Bus Hold handled by System FPGA
  LHoldA <= 'Z';

  -- We don't need to request bus until DSP FPGA wants to be a Bus Master
  BReqi <= 'Z';

  nBTerm <= 'Z';

  nReady <= nReadyPulse when nReadyOe = '0' else 'Z';

  -- Bus access state machine
  LBusSM : process (LClk) is
  begin
    if LClk'event and LClk = '1' then
      nReadyPulse <= '1';
      nReadyOe <= '1';
      SysWr <= '0';
      SysRd <= '0';
      nLdOe <= '1';
      LRamWr <= '0';
      LRamRd <= '0';
      TCGRamWr <= '0';
      TCGRamRd <= '0';
      case State is
        when Idle =>
          if nAds = '0' then
            Addr <= LAin & "00";
            WrnRd <= LWnR;
            State <= Add;
          end if;
        when Add => 
          if Addr(31) = '1' or -- 0x8000_0000 - 0xFFFF_FFFF = 2GBytes for DMA
             Addr(31 downto cSampRAMOffsetBits) = cSampRAMAddr(31 downto cSampRAMOffsetBits) then
            if WrnRd = '1' then
              State <= WrRam;
              LRamWr <= '1';
            else
              State <= RdRam;
              LRamRd <= '1';
            end if;
          elsif Addr(31 downto cTCGRAMOffsetBits) = cTCGRAMAddr(31 downto cTCGRAMOffsetBits) then
            if WrnRd = '1' then
              State <= WrTCGRam;
              TCGRamWr <= '1';
            else
              State <= RdTCGRam;
              TCGRamRd <= '1';
            end if;
          elsif Addr(31 downto cDspRegOffsetBits) = cRegSet(0).Address(31 downto cDspRegOffsetBits) and 
              Addr(cDspRegOffsetBits-1 downto cDspRegOffsetBits-3) /= "000" then
            if WrnRd = '1' then
              SysWr <= '1';
              State <= Wr;
            else
              SysRd <= '1';
              State <= RdDel;
            end if;
          elsif nAds = '1' then
            State <= Idle;
          end if;
           WrData <= LdIn;
        when Wr => 
          nReadyPulse <= '0';
          nReadyOe <= '0';
          if nBLast = '0' then
            State <= Idle;
          else
            State <= BurstNext;
          end if;
        when RdDel =>
          State <= Rd;
        when Rd =>
          nReadyPulse <= '0';
          nReadyOe <= '0';
          LDOut <= RdData;
          nLdOe <= '0';
          if nBLast = '0' then
            State <= Idle;
          else
            State <= BurstNext;
          end if;
        when BurstNext => 
          Addr <= LAin & "00";
          WrnRd <= LWnR;
          State <= Add;

        when WrRam =>
          if LRamAck = '1' then
            nReadyPulse <= '0';
            nReadyOe <= '0';
            if nBLast = '0' then
              State <= Idle;
            else
              State <= BurstNext;
            end if;
          else
            LRamWr <= '1';
          end if;
        when RdRam =>
          if LRamAck = '1' then
            nReadyPulse <= '0';
            nReadyOe <= '0';
            LDOut <= LRamRdData;
            nLdOe <= '0';
            if nBLast = '0' then
              State <= Idle;
            else
              State <= BurstNext;
            end if;
          else
            LRamRd <= '1';
          end if;

        when WrTCGRam =>
          if TCGRamAck = '1' then
            nReadyPulse <= '0';
            nReadyOe <= '0';
            if nBLast = '0' then
              State <= Idle;
            else
              State <= BurstNext;
            end if;
          else
            TCGRamWr <= '1';
          end if;
        when RdTCGRam =>
          if TCGRamAck = '1' then
            nReadyPulse <= '0';
            nReadyOe <= '0';
            LDOut <= x"00000" & TCGRamRdData;
            nLdOe <= '0';
            if nBLast = '0' then
              State <= Idle;
            else
              State <= BurstNext;
            end if;
          else
            TCGRamRd <= '1';
          end if;

        when others =>
      end case;
      if nReadyPulse = '0' then
        nReadyOe <= '0'; -- active negatiation of nReady
      end if;
    end if;  
  end process LBusSM;

  LRamWrData <= WrData;
  LRamAddr <= Addr(30 downto 0) when Addr(31) = '1' else
              Reg(cRAMPageOffset)(30 downto cSampRAMOffsetBits) & Addr(cSampRAMOffsetBits-1 downto 0);

  TCGRamWrData <= WrData(11 downto 0);
  TCGRamAddr <= Addr(cTCGRAMOffsetBits-1 downto 2);

  -- Decode Register addresses
  AddrDec : process (LClk) is
  begin
    if (LClk'event and LClk = '1') then
      RegSel <= (others => '0');

      -- Sequence Table
      for N in 1 to cLastSeqReg loop
        if unsigned(Addr(cDspRegOffsetBits downto cDspSeqRegOffsetBits+1)) >= unsigned(cRegSet(cInputSel).Address(cDspRegOffsetBits downto cDspSeqRegOffsetBits+1)) and
            unsigned(Addr(cDspRegOffsetBits downto cDspSeqRegOffsetBits+1)) < unsigned(cRegSet(cCheckReg).Address(cDspRegOffsetBits downto cDspSeqRegOffsetBits+1)) then
          if cRegSet(N).RegCount = 1 then
            if Addr(cDspSeqRegOffsetBits downto 2) = cRegSet(N).Address(cDspSeqRegOffsetBits downto 2) then
              RegSel(N) <= '1';
            end if;
          else
            if unsigned(Addr(cDspSeqRegOffsetBits downto 2)) >= unsigned(cRegSet(N).Address(cDspSeqRegOffsetBits downto 2)) and
                unsigned(Addr(cDspSeqRegOffsetBits downto 2)) < unsigned(cRegSet(N).Address(cDspSeqRegOffsetBits downto 2)) + cRegSet(N).RegCount then
              RegSel(N) <= '1';
            end if;
          end if;
        end if;
      end loop;
      
      -- other regs
      for N in cLastSeqReg+1 to cLastReg loop
        if cRegSet(N).RegCount = 1 then
          if Addr(cDspRegOffsetBits downto 2) = cRegSet(N).Address(cDspRegOffsetBits downto 2) then
            RegSel(N) <= '1';
          end if;
        else
          if unsigned(Addr(cDspRegOffsetBits downto 2)) >= unsigned(cRegSet(N).Address(cDspRegOffsetBits downto 2)) and
              unsigned(Addr(cDspRegOffsetBits downto 2)) < unsigned(cRegSet(N).Address(cDspRegOffsetBits downto 2)) + cRegSet(N).RegCount then
            RegSel(N) <= '1';
          end if;
        end if;
      end loop;
    end if;
  end process AddrDec;

  -- Generate signal containing all read register data
  ReadDataMux: process (RegSel, Reg, IpSelRegRd, FilterCtrlRegRd, RectifierModeRd, AdcOffsetRd, DropSamplesRd,
                        AScanRepRateRd, PulseWidthRd, PulseTrainRd, AScanRangeRd, TriggerDelayRd, TCGConfigureRd,
                        TCGPointsRd, TCGSamplesRd, GateStartRd, GateStopRd, GateAttribRd, HoldoffRd,
                        AdcSerDIn, AdcBusy, RawAdcData, DdsBusy, HVDACBusy, HVFeedback, SequencesInRam,
                        DataReadyBit, SomeDataReady, NewTrigger, EarlyTrigError, DataFullError, BankBit,
                        EncoderCount0, EncoderCount1, EncoderCount2, EncoderCount3, DualEncoderCount, 
                        PulseCount0, PulseCount1, PulseCount2, PulseCount3, Ident)
    variable vRegMask : tReg;
    variable vRdData  : std_logic_vector(31 downto 0);
  begin
    -- Default to simple register read applying reg mask
    for N in 1 to cLastReg loop
      if RegSel(N) = '1' then
        vRegMask(N) := Reg(N) and cRegSet(N).ReadMask;
      else
        vRegMask(N) := (others => '0');
      end if;
    end loop;

    -- Special cases
    if RegSel(cInputSel) = '1' then
      vRegMask(cInputSel) := IpSelRegRd and cRegSet(cInputSel).ReadMask;
    end if;
    if RegSel(cFilterCtrl) = '1' then
      vRegMask(cFilterCtrl)(15 downto 0) := FilterCtrlRegRd;
    end if;
    if RegSel(cRectifierMode) = '1' then
      vRegMask(cRectifierMode)(2 downto 0) := RectifierModeRd;
    end if;
    if RegSel(cAdcOffset) = '1' then
      vRegMask(cAdcOffset)(12 downto 0) := AdcOffsetRd;
    end if;
    if RegSel(cGateStart) = '1' then
      vRegMask(cGateStart)(23 downto 0) := GateStartRd;
    end if;
    if RegSel(cGateStop) = '1' then
      vRegMask(cGateStop)(23 downto 0) := GateStopRd;
    end if;
    if RegSel(cGateAttributes) = '1' then
      vRegMask(cGateAttributes)(31 downto 0) := GateAttribRd and cRegSet(cGateAttributes).ReadMask;
    end if;
    if RegSel(cGateTrailFlankHO) = '1' then
      vRegMask(cGateTrailFlankHO)(7 downto 0) := HoldOffRd;
    end if;
    if RegSel(cDropSamples) = '1' then
      vRegMask(cDropSamples)(15 downto 0) := DropSamplesRd;
    end if;
    if RegSel(cAScanRepRate) = '1' then
      vRegMask(cAScanRepRate)(23 downto 0) := AScanRepRateRd;
    end if;
    if RegSel(cPulseWidth) = '1' then
      vRegMask(cPulseWidth)(15 downto 0) := PulseWidthRd;
    end if;
    if RegSel(cPulseTrain) = '1' then
      vRegMask(cPulseTrain)(3 downto 0) := PulseTrainRd;
    end if;
    if RegSel(cAScanRange) = '1' then
      vRegMask(cAScanRange)(23 downto 0) := AScanRangeRd;
    end if;
    if RegSel(cTriggerDelay) = '1' then
      vRegMask(cTriggerDelay)(23 downto 0) := TriggerDelayRd;
    end if;
    if RegSel(cTCGConfigure) = '1' then
      vRegMask(cTCGConfigure)(1 downto 0) := TCGConfigureRd(1 downto 0);
      vRegMask(cTCGConfigure)(11 downto 8) := TCGConfigureRd(11 downto 8);
    end if;
    if RegSel(cTCGPoints) = '1' then
      vRegMask(cTCGPoints)(18 downto 0) := TCGPointsRd;
    end if;
    if RegSel(cTCGNoOfSamples) = '1' then
      vRegMask(cTCGNoOfSamples)(15 downto 0) := TCGSamplesRd;
    end if;
    if RegSel(cCheckReg) = '1' then
      vRegMask(cCheckReg) := not Reg(cCheckReg);
    end if;
    if RegSel(cADCSerialCtrl) = '1' then
      vRegMask(cADCSerialCtrl)(7 downto 0) := AdcSerDIn;
      vRegMask(cADCSerialCtrl)(31) := AdcBusy;
    end if;
    if RegSel(cRawADCData) = '1' then
      vRegMask(cRawADCData)(11 downto 0) := RawAdcData;
    end if;
    if RegSel(cDDSCtrl) = '1' then
      vRegMask(cDDSCtrl)(31) := DdsBusy;
    end if;
    if RegSel(cHVDACCtrl) = '1' then
      vRegMask(cHVDACCtrl)(31) := HVDACBusy;
    end if;
    if RegSel(cHVCtrl) = '1' then
      vRegMask(cHVCtrl)(31) := HVFeedback;
    end if;
    if RegSel(cSeqInRam) = '1' then
      vRegMask(cSeqInRam)(15 downto 0) := SequencesInRam;
    end if;
    if RegSel(cDataSamplingStatus) = '1' then
      vRegMask(cDataSamplingStatus)(0) := DataReadyBit;
      vRegMask(cDataSamplingStatus)(1) := SomeDataReady;
      vRegMask(cDataSamplingStatus)(2) := NewTrigger;
      vRegMask(cDataSamplingStatus)(3) := EarlyTrigError;
      vRegMask(cDataSamplingStatus)(4) := DataFullError;
      vRegMask(cDataSamplingStatus)(5) := BankBit;
    end if;
    if RegSel(cShaftEncoder0Op) = '1' then
      vRegMask(cShaftEncoder0Op) := EncoderCount0;
    end if;
    if RegSel(cShaftEncoder1Op) = '1' then
      vRegMask(cShaftEncoder1Op) := EncoderCount1;
    end if;
    if RegSel(cShaftEncoder2Op) = '1' then
      vRegMask(cShaftEncoder2Op) := EncoderCount2;
    end if;
    if RegSel(cShaftEncoder3Op) = '1' then
      vRegMask(cShaftEncoder3Op) := EncoderCount3;
    end if;
    if RegSel(cDualEncoderCounter) = '1' then
      vRegMask(cDualEncoderCounter) := DualEncoderCount;
    end if;
    if RegSel(cDigIOPulseCounter0) = '1' then
      vRegMask(cDigIOPulseCounter0) := PulseCount0;
    end if;
    if RegSel(cDigIOPulseCounter1) = '1' then
      vRegMask(cDigIOPulseCounter1) := PulseCount1;
    end if;
    if RegSel(cDigIOPulseCounter2) = '1' then
      vRegMask(cDigIOPulseCounter2) := PulseCount2;
    end if;
    if RegSel(cDigIOPulseCounter3) = '1' then
      vRegMask(cDigIOPulseCounter3) := PulseCount3;
    end if;
    if RegSel(cMultiBoardCtrl) = '1' then
      vRegMask(cMultiBoardCtrl)(3 downto 0) := Ident;
    end if;
    if RegSel(cDspFpgaVer) = '1' then
      vRegMask(cDspFpgaVer)(15 downto 0) := cFpgaVersion & cFpgaRevision;
    end if;

    vRdData := (others => '0');

    -- Default to simple register read applying reg mask
    for N in 1 to cLastReg loop
      vRdData := vRdData or vRegMask(N);
    end loop;

    RdData <= vRdData;

  end process ReadDataMux;

  -- Process to generate registers and RAM write strobes
  GenRegAccess: process(nReset, LClk)
  begin
    if nReset = '0' then
      for i in 1 to cLastReg loop
        Reg(i) <= cRegSet(i).ResetVal;
      end loop;
      InputSelWr <= '0';
      InputSelUpdate <= '0';
      FilterCtrlWr <= '0';
      FilterCtrlUpdate <= '0';
      AdcSerWr <= '0';
      AdcResetWr <= '0';
      RawAdcRd <= '0';
      DdsResetPulse <= '0';
      DdsWr <= '0';
      HVDACWr <= '0';
      ForceTrigger <= '0';
      DataReadyBit <= '0';
      BankBit <= '0';
      DataReady <= '0';
      EarlyDataReq <= '0';
      ResetSampling <= '0';
      EncoderReset <= (others => '0');

    elsif LClk'event and LClk = '1' then
      InputSelWr <= '0';
      InputSelUpdate <= InputSelWr;
      FilterCtrlWr <= '0';
      FilterCtrlUpdate <= FilterCtrlWr;
      AdcSerWr <= '0';
      AdcResetWr <= '0';
      RawAdcRd <= '0';
      DdsResetPulse <= '0';
      DdsWr <= '0';
      HVDACWr <= '0';
      ForceTrigger <= '0';
      DataReady <= '0';
      EarlyDataReq <= '0';
      ResetSampling <= '0';
      EncoderReset <= (others => '0');

      if SetDataReady = '1' then
        DataReadyBit <= '1';
        BankBit <= not BankSel;
      end if;

      if SoDimmVal = '1' then
        Reg(cRAMBankSize) <= '0' & SoDimmSize & "000" & x"000000";
      end if;

      if SysWr = '1' then
        -- write to writable bits
        for N in 1 to cLastReg loop
          if RegSel(N) = '1' then
            Reg(N) <= WrData and cRegSet(N).WriteMask;
          end if;
        end loop;

        -- special cases
        if RegSel(cInputSel) = '1' then
          InputSelWr <= '1';
        end if;
        if RegSel(cFilterCtrl) = '1' then
          FilterCtrlWr <= '1';
        end if;
        if RegSel(cADCSerialCtrl) = '1' then
          AdcSerWr <= WrData(24);
          AdcResetWr <= WrData(25);
        end if;
        if RegSel(cRawADCData) = '1' then
          RawAdcRd <= '1';
        end if;
        if RegSel(cDDSCtrl) = '1' then
          DdsResetPulse <= WrData(16);
          DdsWr <= WrData(30);
        end if;
        if RegSel(cHVDACCtrl) = '1' then
          HVDACWr <= WrData(0);
        end if;
        if RegSel(cSeqPerBank) = '1' then
          DataReadyBit <= '0';
        end if;
        if RegSel(cRAMControl) = '1' then
          ResetSampling <= '1';
        end if;
        if RegSel(cDataSamplingStatus) = '1' then
          if WrData(0) = '1' then
            DataReadyBit <= '0';
            DataReady <= '1';
          end if;
          if WrData(1) = '1' then
            EarlyDataReq <= '1';
          end if;
        end if;
        if RegSel(cSeqTriggerForce) = '1' then
          ForceTrigger <= WrData(0);
        end if;
        if RegSel(cShaftEncoder0Op) = '1' then
          EncoderReset(0) <= '1';
        end if;
        if RegSel(cShaftEncoder1Op) = '1' then
          EncoderReset(1) <= '1';
        end if;
        if RegSel(cShaftEncoder2Op) = '1' then
          EncoderReset(2) <= '1';
        end if;
        if RegSel(cShaftEncoder3Op) = '1' then
          EncoderReset(3) <= '1';
        end if;
        if RegSel(cDualEncoderCounter) = '1' then
          EncoderReset(4) <= '1';
        end if;
        if RegSel(cDigIOPulseCounter0) = '1' then
          EncoderReset(5) <= '1';
        end if;
        if RegSel(cDigIOPulseCounter1) = '1' then
          EncoderReset(6) <= '1';
        end if;
        if RegSel(cDigIOPulseCounter2) = '1' then
          EncoderReset(7) <= '1';
        end if;
        if RegSel(cDigIOPulseCounter3) = '1' then
          EncoderReset(8) <= '1';
        end if;
      end if;

    end if;
  end process GenRegAccess;


  GenSamplingStatus: process(nReset, LClk)
  begin
    if nReset = '0' then
      NewTrigger <= '0';
      EarlyTrigError <= '0';
      DataFullError <= '0';
    elsif LClk'event and LClk = '1' then

      if SetNewTrigger = '1' then
        NewTrigger <= '1';
      end if;
      if SetEarlyTrigError = '1' then
        EarlyTrigError <= '1';
      end if;
      if SetDataFullError = '1' then
        DataFullError <= '1';
      end if;

      if (SysRd = '1' and RegSel(cDataSamplingStatus) = '1') or
          (SysWr = '1' and RegSel(cSeqPerBank) = '1') then
        NewTrigger <= '0';
        EarlyTrigError <= '0';
        DataFullError <= '0';
      end if;

    end if;
  end process GenSamplingStatus;


  -- write decodes
  IpSelWrEn <= SysWr when RegSel(cInputSel) = '1' else '0';
  FilterCtrlWrEn <= SysWr when RegSel(cFilterCtrl) = '1' else '0';
  RectifierModeWrEn <= SysWr when RegSel(cRectifierMode) = '1' else '0';
  AdcOffsetWrEn <= SysWr when RegSel(cAdcOffset) = '1' else '0';
  DropSamplesWrEn <= SysWr when RegSel(cDropSamples) = '1' else '0';
  AScanRepRateWrEn <= SysWr when RegSel(cAScanRepRate) = '1' else '0';
  PulseWidthWrEn <= SysWr when RegSel(cPulseWidth) = '1' else '0';
  PulseTrainWrEn <= SysWr when RegSel(cPulseTrain) = '1' else '0';
  AScanRangeWrEn <= SysWr when RegSel(cAScanRange) = '1' else '0';
  TriggerDelayWrEn <= SysWr when RegSel(cTriggerDelay) = '1' else '0';
  TCGConfigureWrEn <= SysWr when RegSel(cTCGConfigure) = '1' else '0';
  TCGPointsWrEn <= SysWr when RegSel(cTCGPoints) = '1' else '0';
  TCGSamplesWrEn <= SysWr when RegSel(cTCGNoOfSamples) = '1' else '0';
  HoldoffWrEn <= SysWr when RegSel(cGateTrailFlankHO) = '1' else '0';

  SeqWrAddr <= unsigned(Addr) - unsigned(cDspBaseAddr);

  -- Seq step rams
  IpSelSeq: DPRam32
    generic map (gDataWidth => 32)
    port map (Clk => LClk,
              WrEn => IpSelWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => IpSelWrData,
              RdData => IpSelRegRd,
              DataOut => IpSelReg);

  IpSelWrData <= WrData and cRegSet(cInputSel).WriteMask;

  FilterCtrlSeq: DPRam32
    generic map (gDataWidth => 16)
    port map (Clk => LClk,
              WrEn => FilterCtrlWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(15 downto 0),
              RdData => FilterCtrlRegRd,
              DataOut => FilterCtrlReg);

  RectifierModeSeq: DPRam32
    generic map (gDataWidth => 3)
    port map (Clk => LClk,
              WrEn => RectifierModeWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(2 downto 0),
              RdData => RectifierModeRd,
              DataOut => RectifierMode);

  RectMode    <= RectifierMode(1 downto 0);
  RectMod8Bit <= RectifierMode(2);

  AdcOffsetSeq: DPRam32
    generic map (gDataWidth => 13)
    port map (Clk => LClk,
              WrEn => AdcOffsetWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(12 downto 0),
              RdData => AdcOffsetRd,
              DataOut => AdcOffset);
   
  DropSamplesSeq: DPRam32
    generic map (gDataWidth => 16)
    port map (Clk => LClk,
              WrEn => DropSamplesWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(15 downto 0),
              RdData => DropSamplesRd,
              DataOut => DropSamples);

  AScanRepRateSeq: DPRam32
    generic map (gDataWidth => 24)
    port map (Clk => LClk,
              WrEn => AScanRepRateWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(23 downto 0),
              RdData => AScanRepRateRd,
              DataOut => AScanRepRate);

  PulseWidthSeq: DPRam32
    generic map (gDataWidth => 16)
    port map (Clk => LClk,
              WrEn => PulseWidthWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(15 downto 0),
              RdData => PulseWidthRd,
              DataOut => PulseWidth);

  PulseTrainSeq: DPRam32
    generic map (gDataWidth => 4)
    port map (Clk => LClk,
              WrEn => PulseTrainWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(3 downto 0),
              RdData => PulseTrainRd,
              DataOut => PulseTrain);

  AScanRangeSeq: DPRam32
    generic map (gDataWidth => 24)
    port map (Clk => LClk,
              WrEn => AScanRangeWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(23 downto 0),
              RdData => AScanRangeRd,
              DataOut => AScanRange);

  TriggerDelaySeq: DPRam32
    generic map (gDataWidth => 24)
    port map (Clk => LClk,
              WrEn => TriggerDelayWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(23 downto 0),
              RdData => TriggerDelayRd,
              DataOut => TriggerDelay);

  TCGConfigureSeq: DPRam32
    generic map (gDataWidth => 12)
    port map (Clk => LClk,
              WrEn => TCGConfigureWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(11 downto 0),
              RdData => TCGConfigureRd,
              DataOut => TCGConfigure);

  TCGAbsoluteTrig <= TCGConfigure(0);
  TCGEnable       <= TCGConfigure(1);
  TCGBEAGate      <= TCGConfigure(10 downto 8);
  TCGBEAEnable    <= TCGConfigure(11);

  TCGPointsSeq: DPRam32
    generic map (gDataWidth => 19)
    port map (Clk => LClk,
              WrEn => TCGPointsWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(18 downto 0),
              RdData => TCGPointsRd,
              DataOut => TCGPoints);

  TCGSamplesSeq: DPRam32
    generic map (gDataWidth => 16)
    port map (Clk => LClk,
              WrEn => TCGSamplesWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(15 downto 0),
              RdData => TCGSamplesRd,
              DataOut => TCGSamples);

  HoldoffSeq: DPRam32
    generic map (gDataWidth => 8)
    port map (Clk => LClk,
              WrEn => HoldoffWrEn,
              WrAddr => SeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => WrData(7 downto 0),
              RdData => HoldoffRd,
              DataOut => Holdoff);

  -- Gate RAMs
  GateFF: process(nReset, LClk)
  begin
    if nReset = '0' then
      GateStartWrEn <= (others => '0');
      GateStopWrEn <= (others => '0');
      GateAttribWrEn <= (others => '0');
    elsif LClk'event and LClk = '1' then
      for N in 0 to cLastGate loop
        if RegSel(cGateStart) = '1' and unsigned(Addr(4 downto 2)) = N then
          GateStartWrEn(N) <= SysWr;
        else
          GateStartWrEn(N) <= '0';
        end if;
        if RegSel(cGateStop) = '1' and unsigned(Addr(4 downto 2)) = N then
          GateStopWrEn(N) <= SysWr;
        else
          GateStopWrEn(N) <= '0';
        end if;
        if RegSel(cGateAttributes) = '1' and unsigned(Addr(4 downto 2)) = N then
          GateAttribWrEn(N) <= SysWr;
        else
          GateAttribWrEn(N) <= '0';
        end if;
      end loop;
        
      GateSeqWrAddr <= unsigned(Addr) - unsigned(cDspBaseAddr);

      GateWrData <= WrData;
      GateAttribWrData <= WrData and cRegSet(cGateAttributes).WriteMask;
    end if;
  end process GateFF;

  GateRAM: for N in 0 to cLastGate generate
    GateStartSeq: DPRam32
      generic map (gDataWidth => 24)
      port map (Clk => LClk,
                WrEn => GateStartWrEn(N),
                WrAddr => GateSeqWrAddr(13 downto 9),
                RdAddr => SequenceStep,
                WrData => GateWrData(23 downto 0),
                RdData => GateStartRdAll(N),
                DataOut => GateStart(N));

  GateStopSeq: DPRam32
    generic map (gDataWidth => 24)
    port map (Clk => LClk,
              WrEn => GateStopWrEn(N),
              WrAddr => GateSeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => GateWrData(23 downto 0),
              RdData => GateStopRdAll(N),
              DataOut => GateStop(N));

  GateAttribSeq: DPRam32
    generic map (gDataWidth => 32)
    port map (Clk => LClk,
              WrEn => GateAttribWrEn(N),
              WrAddr => GateSeqWrAddr(13 downto 9),
              RdAddr => SequenceStep,
              WrData => GateAttribWrData,
              RdData => GateAttribRdAll(N),
              DataOut => GateAttrib(N));

  end generate;

  GateStartRd  <= GateStartRdAll(conv_integer(unsigned(Addr(4 downto 2))));
  GateStopRd   <= GateStopRdAll(conv_integer(unsigned(Addr(4 downto 2))));
  GateAttribRd <= GateAttribRdAll(conv_integer(unsigned(Addr(4 downto 2))));


  -- Single regs
  AdcSerInst <= Reg(cADCSerialCtrl)(23 downto 8);
  AdcSerDOut <= Reg(cADCSerialCtrl)(7 downto 0);

  TestPatternMode <= Reg(cSampleCtrl)(0);
  SignExtendMode  <= Reg(cSampleCtrl)(1);
  SignAndMagMode  <= Reg(cSampleCtrl)(2);

  BankSize         <= Reg(cRAMBankSize)(30 downto 0);
  SequenceRepRate  <= Reg(cSeqRepRate);
  SequencesPerBank <= Reg(cSeqPerBank)(15 downto 0);
  StorageMode      <= Reg(cRAMControl)(1 downto 0);
  CScanOffsetAddr  <= Reg(cCScanDataRamOffset)(30 downto 0);

  SyncPulseEn <= Reg(cRAMControl)(4);

  -- Reset is clocked by DDS MCLK (50MHz)
  StrobeExtender1: StrobeExtender
    generic map(gLfsrBits => 3)
    port map (nReset    => nReset,
              Clk       => LClk,
              StrobeIn  => DdsResetPulse,
              StrobeOut => DdsReset);

  DdsData    <= Reg(cDDSCtrl)(15 downto 0);
  DdsSleep   <= Reg(cDDSCtrl)(17);
  DdsFSelect <= Reg(cDDSCtrl)(18);
  DdsPSelect <= Reg(cDDSCtrl)(19);
  nDdsOscInh <= Reg(cDDSCtrl)(20);

  HVDACData <= Reg(cHVDACData)(15 downto 0);

  Relay     <= Reg(cRelayCtrl)(0);
  -- Do not enable when dischange active
  nHVEnable <= not (Reg(cHVCtrl)(0) and not Reg(cHVCtrl)(1));
  nHVDisChg <= not Reg(cHVCtrl)(1);
  
  EncoderSelect    <= Reg(cSeqTriggerEncSel)(3 downto 0);
  ClicksPerSeq     <= Reg(cEncoderClickPerSeq)(23 downto 0);
  FractClicks      <= Reg(cFractEncClickPerSeq)(15 downto 0);
  ExtTriggerEn     <= Reg(cSeqTriggerMode)(0);
  FreeRunningEn    <= Reg(cSeqTriggerMode)(1);
  EncoderTriggerEn <= Reg(cSeqTriggerMode)(2);

  SequenceSteps <= Reg(cSeqCtrl)(4 downto 0);
  StartSequence <= Reg(cSeqCtrl)(31);

  GenIrq: process(nReset, LCLK)
  begin
    if nReset = '0' then
      nLINTi <= '1';
    elsif LClk'event and LClk = '1' then
      nLINTi <= '1';
      if Reg(cRAMControl)(3) = '1' and DataReadyBit = '1' then
        nLINTi <= '0';
      end if;
      if Reg(cRAMControl)(2) = '1' and SomeDataReady = '1' then
        nLINTi <= '0';
      end if;
    end if;
  end process GenIrq;

end Rtl;
