--==============================================================================
-- CERN (BE-CO-HT)
-- Pulse time-tagging core
--==============================================================================
--
-- author: Theodor Stana (t.stana@cern.ch)
--
-- date of creation: 2014-02-04
--
-- version: 1.0
--
-- description:
--    This module contains the internal timetag counter, counting on an 8 ns
--    clock. When a pulse arrives on the input, it triggers the writing of a
--    timetag to a FIFO memory external to the module.
--
-- dependencies:
--    gencores_pkg : git://ohwr.org/hdl-core-lib/general-cores.git
--
--==============================================================================
-- GNU LESSER GENERAL PUBLIC LICENSE
--==============================================================================
-- This source file is free software; you can redistribute it and/or modify it
-- under the terms of the GNU Lesser General Public License as published by the
-- Free Software Foundation; either version 2.1 of the License, or (at your
-- option) any later version. This source is distributed in the hope that it
-- will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-- See the GNU Lesser General Public License for more details. You should have
-- received a copy of the GNU Lesser General Public License along with this
-- source; if not, download it from http://www.gnu.org/licenses/lgpl-2.1.html
--==============================================================================
-- last changes:
--    2014-02-04   Theodor Stana     File created
--==============================================================================
-- TODO: -
--==============================================================================

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

use work.gencores_pkg.all;

entity conv_pulse_timetag is
  generic
  (
  -- Frequency in Hz of the clk_i signal
    g_clk_rate : positive := 125000000;

  -- Number of repetition channels
    g_nr_chan  : positive := 6
  );
  port
  (
  -- Clock and active-low reset
    clk_i            : in  std_logic;
    rst_n_i          : in  std_logic;

  -- Asynchronous pulse input
    pulse_a_i        : in  std_logic_vector(g_nr_chan downto 1);

  -- Time inputs from White Rabbit
    wr_tm_cycles_i   : in  std_logic_vector(27 downto 0);
    wr_tm_tai_i      : in  std_logic_vector(39 downto 0);
    wr_tm_valid_i    : in  std_logic;

  -- Timing inputs from Wishbone-mapped registers
    wb_tm_tai_l_i    : in  std_logic_vector(31 downto 0);
    wb_tm_tai_l_ld_i : in  std_logic;
    wb_tm_tai_h_i    : in  std_logic_vector( 7 downto 0);
    wb_tm_tai_h_ld_i : in  std_logic;

  -- Timing outputs
    tm_cycles_o      : out std_logic_vector(27 downto 0);
    tm_tai_o         : out std_logic_vector(39 downto 0);
    tm_wrpres_o      : out std_logic;
    chan_o           : out std_logic_vector(g_nr_chan downto 1);

  -- Ring buffer I/O
    buf_wr_req_p_o   : out std_logic
  );
end entity conv_pulse_timetag;


architecture behav of conv_pulse_timetag is

  --============================================================================
  -- Signal declarations
  --============================================================================
  signal cycles_cnt       : unsigned(27 downto 0);
  signal cycles_tick      : std_logic;
  signal tai_cnt          : unsigned(39 downto 0);

  signal tai_l_ld         : std_logic;
  signal tai_h_ld         : std_logic;

  signal pulse_redge_p    : std_logic_vector(g_nr_chan downto 1);
  signal pulse_redge_p_d0 : std_logic_vector(g_nr_chan downto 1);

--==============================================================================
--  architecture begin
--==============================================================================
begin

  --============================================================================
  -- Time counter logic
  --============================================================================
  -- The Wishbone bus may be in a different clock domain than the time tag core,
  -- so first we need to synchronize the LD signals
  cmp_sync_l_ld : gc_sync_ffs
    port map
    (
      clk_i    => clk_i,
      rst_n_i  => rst_n_i,
      data_i   => wb_tm_tai_l_ld_i,
      ppulse_o => tai_l_ld
    );

  cmp_sync_h_ld : gc_sync_ffs
    port map
    (
      clk_i    => clk_i,
      rst_n_i  => rst_n_i,
      data_i   => wb_tm_tai_h_ld_i,
      ppulse_o => tai_h_ld
    );

  -- Generate the counters
  p_cycle_cnt : process (clk_i)
  begin
    if rising_edge(clk_i) then
      if rst_n_i = '0' then
        cycles_cnt  <= (others => '0');
        cycles_tick <= '0';
      else
        cycles_cnt  <= cycles_cnt + 1;
        cycles_tick <= '0';
      -- TAI counter loaded from Wishbone
        if tai_l_ld = '1' or tai_h_ld = '1' then
          cycles_cnt <= (others => '0');
      -- Tick and reset on second
        elsif cycles_cnt = g_clk_rate-1 then
          cycles_cnt  <= (others => '0');
          cycles_tick <= '1';
        end if;
      end if;
    end if;
  end process p_cycle_cnt;

  p_tai_cnt : process (clk_i)
  begin
    if rising_edge(clk_i) then
      if rst_n_i = '0' then
        tai_cnt <= (others => '0');
    -- Load from Wishbone
      elsif tai_l_ld = '1' then
        tai_cnt(31 downto 0) <= unsigned(wb_tm_tai_l_i);
      elsif tai_h_ld = '1' then
        tai_cnt(39 downto 32) <= unsigned(wb_tm_tai_h_i);
    -- Increment on cycles second tick
      elsif cycles_tick = '1' then
        tai_cnt <= tai_cnt + 1;
      end if;
    end if;
  end process p_tai_cnt;

  --============================================================================
  -- Control logic for the FIFO
  --============================================================================
  -- First, synchronize the pulse inputs in the clk_i domain
  gen_sync_chains : for i in 1 to g_nr_chan generate
    cmp_pulse_sync : gc_sync_ffs
      generic map
      (
        g_sync_edge => "positive"
      )
      port map
      (
        clk_i    => clk_i,
        rst_n_i  => '1',
        data_i   => pulse_a_i(i),
        ppulse_o => pulse_redge_p(i)
      );
  end generate gen_sync_chains;

  -- Set the control signals to the ring buffer on the rising edge of any
  -- pulse channel
  p_buf_ctrl : process (clk_i)
  begin
    if rising_edge(clk_i) then
      if rst_n_i = '0' then
        buf_wr_req_p_o <= '0';
      else
        buf_wr_req_p_o <= '0';
        if not (pulse_redge_p = (pulse_redge_p'range => '0')) then
          buf_wr_req_p_o <= '1';
        end if;
      end if;
    end if;
  end process p_buf_ctrl;

  -- And delay the pulse rising edge for sampling (this is due to the delayed
  -- setting of the write signal to the FIFO)
  p_dly_pulse : process (clk_i)
  begin
    if rising_edge(clk_i) then
      if rst_n_i = '0' then
        pulse_redge_p_d0 <= (others => '0');
      else
        pulse_redge_p_d0 <= pulse_redge_p;
      end if;
    end if;
  end process p_dly_pulse;

  --============================================================================
  -- Output logic
  --============================================================================
  -- Multiplex the timing outputs between WR and internal counters
  tm_cycles_o <= wr_tm_cycles_i when wr_tm_valid_i = '1' else
                 std_logic_vector(cycles_cnt);
  tm_tai_o    <= wr_tm_tai_i when wr_tm_valid_i = '1' else
                 std_logic_vector(tai_cnt);
  tm_wrpres_o <= wr_tm_valid_i;
  chan_o      <= pulse_redge_p_d0;

end architecture behav;
--==============================================================================
--  architecture end
--==============================================================================
