library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
--
use work.control_x_pkg.all;
use work.vlib_commonfunc_pkg.all;

entity trigger is
  port (
    clk, reset    : in  std_logic;
    etr, enw      : in  std_logic;
    set_trgin     : in  std_logic;
    trigger_in    : in  std_logic;
    trg_bypass    : in  std_logic;
    veto_long     : in  std_logic;
    add           : in  std_logic_vector(1 downto 0);
    grp           : in  std_logic_vector(5 downto 0);
    db_in         : in  std_logic_vector(15 downto 0);
    db_out        : out std_logic_vector(15 downto 0);
    int_trg       : out std_logic;
    trigger_out   : out std_logic;
    T0OutSelector : out std_logic;
    autoTrgOut    : out std_logic;
    trg_dly_p     : out std_logic
  );
end entity;

architecture arch of trigger is

  -- Tipo para registro
  type reg_type is record
    set_trgin_ap    : std_logic;
    q_in            : std_logic;
    tr_0            : std_logic;
    tr_1            : std_logic;
    v_0             : std_logic;
    v_1             : std_logic;
    selTrigger4Shift: std_logic;
    triggerOutSel   : std_logic_vector(1 downto 0);
    autoTriggerFreq : std_logic_vector(3 downto 0);
    T0OutSelector   : std_logic;
    --
    -- Longitud de "trg_delay" ha de ser concordante con MEMBLOCKS en "shift":
    --    long = log2(16 * MEMBLOCKS)
    --
    trg_delay       : std_logic_vector(7 downto 0);
    trg_maxcnt      : std_logic_vector(15 downto 0);
    trg_cntoutreg   : std_logic_vector(31 downto 0);
    trg_cntout      : std_logic_vector(31 downto 0);
  end record;

  constant regDefault : reg_type := (
    set_trgin_ap    => '0',
    q_in            => '0',
    tr_0            => '0',
    tr_1            => '0',
    v_0             => '0',
    v_1             => '0',
    selTrigger4Shift=> '0',
    triggerOutSel   => (others => '0'),
    autoTriggerFreq => (others => '0'),
    T0OutSelector   => '0',
    trg_delay       => "00001000",
    trg_maxcnt      => (others => '0'),
    trg_cntoutreg   => (others => '0'),
    trg_cntout      => (others => '0')
  );
  signal reg, nextReg : reg_type := regDefault;

  -- Multiplexor de salida de trigger
  signal muxOut: std_logic_vector(3 downto 0);

  -- Señales auxiliares
  signal tr_2             : std_logic;
  signal end_count        : std_logic;
  signal tr_dly           : std_logic;
  signal autoTrgOutAux    : std_logic;
  signal triggerShifterIn : std_logic;
  
begin
  --**********************************************************
  -- Proceso combinacional
  --
  COMB : process(reg, etr, enw, set_trgin, trigger_in, trg_bypass, add,
                 grp, db_in, end_count, veto_long, tr_dly, autoTrgOutAux, 
                 triggerShifterIn)
    variable var : reg_type;
    variable v_2 : std_logic;

  begin
    var := reg; -- Inicialización de variable
  
    --*****************************************
    -- Gestión de cada señal registrada
    var.set_trgin_ap := set_trgin;

    var.v_0 := veto_long;
    var.v_1 := reg.v_0;
    
    v_2 := reg.v_0 and not reg.v_1;
    if set_trgin = '1' then 
      var.q_in := '1';
    elsif end_count = '1' or v_2 = '1' then
      var.q_in := '0';
    end if;

    if enw='1' and grp(0)='1' and add=2 then --2
      var.trg_maxcnt := db_in;
    end if;
    
    if reg.set_trgin_ap = '1' then
      var.trg_cntoutreg  := reg.trg_cntout;
    elsif enw = '1' and grp(1) = '1' and add = 0 then --4
      var.trg_cntoutreg(15 downto 0)  := db_in;
    elsif enw = '1' and grp(1) = '1' and add = 1 then --5
      var.trg_cntoutreg(31 downto 16) := db_in;
    end if;
    
    if reg.set_trgin_ap = '1' then              -- ap
      var.trg_cntout := (others => '0');
    elsif enw = '1' and grp(1) = '1' and add = 2 then --6
      var.trg_cntout(15 downto 0)  := db_in;
    elsif enw = '1' and grp(1 )= '1' and add = 3 then --7
      var.trg_cntout(31 downto 16) := db_in;
    elsif reg.q_in = '0' and tr_dly = '1' then
      var.trg_cntout := reg.trg_cntout + 1;
    end if;

    if enw='1' and grp(0)='1' and add=1 then --1
      var.trg_delay := db_in(7 downto 0);
    end if;

    if enw='1' and grp(5)='1' and add=0 then --1
      var.T0OutSelector     := db_in(7);
      var.triggerOutSel     := db_in(6 downto 5);
      var.selTrigger4Shift  := db_in(4);
      var.autoTriggerFreq   := db_in(3 downto 0);
    end if;
    
    -- Registros para conformar el pulso de trigger a un ancho de un ciclo.
    var.tr_0 := trigger_in;
    var.tr_1 := reg.tr_0;
    
    --*****************************************
    -- Resultado del proceso combinacional como siguiente valor de registro
    nextReg <= var;

    --*****************************************
    -- Actualización de salidas
    int_trg <= end_count;
    tr_2    <= reg.tr_0 and not reg.tr_1;       -- Pulso conformado

    -- Selector para decidir que "trigger" se pasa por el retardador ("shift")
    if reg.selTrigger4Shift = '0'
    then triggerShifterIn <= tr_2;          -- Trigger externo conformado
    else triggerShifterIn <= autoTrgOutAux; -- Trigger autogenerado
    end if;
 
    --
    -- Conexión de la entradas del multiplexor de salida de trigger:
    --    0 -> se saca el mismo pulso de entrada de trigger
    --    1 -> pulso de trigger de entrada, pero conformado
    --    2 -> se saca el pulso generado por el "auto-trigger"
    --    3 -> se saca el pulso que venga del retardador ("shift")
    --
    muxOut(0) <= trigger_in;
    muxOut(1) <= tr_2;
    muxOut(2) <= autoTrgOutAux;
    muxOut(3) <= tr_dly;

    -- Salida auxiliar sobre el PCB.
    trg_dly_p   <= muxOut(to_integer(unsigned(reg.triggerOutSel)));
    autoTrgOut  <= autoTrgOutAux;
    --
    -- Gestión del selector del trigger de salida.
    -- Este selector debería estar en el módulo principal (ctrl_x4.vhd) pero
    -- con el galimatías de registros que hay, el control de selector se 
    -- hace con el registro 0 del grupo 5, que se gestiona en este módulo.
    --
    T0OutSelector <= reg.T0OutSelector;

    -- Salida de trigger en el frontal de la tarjeta
    if (trg_bypass = '0' and reg.v_1  = '0') or --v_1 = veto sincronizado
       (trg_bypass = '1' and reg.q_in = '1')
    then trigger_out <= muxOut(to_integer(unsigned(reg.triggerOutSel)));
    else trigger_out <= '0';
    end if;

    if    etr = '0' and grp(0) = '1' and add = 1 then --1
      db_out <= "00000000" & reg.trg_delay;
    elsif etr = '0' and grp(0) = '1' and add = 2 then --2
      db_out <= reg.trg_maxcnt;
    elsif etr = '0' and grp(1) = '1' and add = 0 then --4 
      db_out <= reg.trg_cntoutreg(15 downto 0);
    elsif etr = '0' and grp(1) = '1' and add = 1 then --5
      db_out <= reg.trg_cntoutreg(31 downto 16);
    elsif etr = '0' and grp(1) = '1' and add = 2 then --6
      db_out <= reg.trg_cntout(15 downto 0);
    elsif etr = '0' and grp(1) = '1' and add = 3 then --7
      db_out <= reg.trg_cntout(31 downto 16);   
    elsif etr = '0' and grp(5) = '1' and add = 0 then 
      db_out <= "00000000" & reg.T0OutSelector & reg.triggerOutSel & 
                reg.selTrigger4Shift & reg.autoTriggerFreq;
    else
      db_out <= (others => 'Z');
    end if;

  end process;
  
  --**********************************************************
  -- Proceso secuencial
  --
  SEC : process(clk, reset)
  begin
    if reset = '1' then
      reg <= regDefault;
    elsif clk = '1' and clk'event then
      reg <= nextReg;
    end if;
  end process;

  --**********************************************************
  -- Componentes adicionales
  --
  S1 : shift generic map(MEMBLOCKS => 16) --Potencias de 2
  port map(
    clk         => clk,
    trigger     => triggerShifterIn,
    trgDelaySel => reg.trg_delay,
    trg_dly     => tr_dly
  );

  S2: cuenta port map( 
    clk         => clk, 
    reset       => reset, 
    etr         => etr, 
    set_trgin   => set_trgin, 
    trigger     => tr_dly, 
    enable      => reg.q_in,
    add         => add,
    grp         => grp,
    trg_maxcnt  => reg.trg_maxcnt,
    db_out      => db_out,
    end_count   => end_count
  );

  ATT: autotrigger port map(
    clk         => clk,
    reset       => reset,
    triggerFreq => reg.autoTriggerFreq,
    triggerOut  => autoTrgOutAux
  );

end architecture;
	
