OpenCores
URL https://opencores.org/ocsvn/p9813_rgb_led_string_driver/p9813_rgb_led_string_driver/trunk

Subversion Repositories p9813_rgb_led_string_driver

[/] [p9813_rgb_led_string_driver/] [trunk/] [rtl/] [VHDL/] [bit_sync_pack.vhd] - Rev 2

Compare with Previous | Blame | View Log

--------------------------------------------------------------------------
-- Package of bit sync and DPLL components
--
--
--
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
package bit_sync_pack is
 
-- Component declarations not provided any more.
-- With VHDL '93 and newer, component declarations are allowed,
-- but not required.
--
-- Please to use direct instantiation instead, for example:
--
--   instance_name : entity work.entity_name(beh)
--
 
end bit_sync_pack;
 
-------------------------------------------------------------------------------
-- Maximum-cycle Linear Feedback Shift Register (MLFSR)
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : Dec. 18, 2015 Started Coding, drawing inspiration from online
--                       resources.
--         Apr. 19, 2018 Corrected the entity name to actually match the
--                       module name : mlfsr
--
-- Description
-------------------------------------------------------------------------------
-- This module applies a modulo 2 polynomial as feedback to a shift register
-- in order to generate a maximum-cycle pseudo random sequence.
-- The maximum cycle is length 2^N-1 bits, where N is the number of bits
-- in the shift register.
--
-- Generic parameters control the length of the shift register, and the
-- particular polynomial taps applied.  The polynomial is not completely
-- generic, in the sense that only up to four taps can be implemented.
-- This constraint was deemed acceptable, since for any shift register
-- of length N stages, a maximum cycle output can be obtained using either
-- two or four taps.
--
-- The polynomials are assumed to have two or four non-zero terms, in addition
-- to the zeroth order term, which is always a 1.
--
-- For those who are curious, the binary Galois representation is used here.
--
-- The tap inputs are natural numbers which represent the position of the tap
-- within the shift register.  If tap position is set to zero, then the tap is
-- not used.  Please don't get funky with this.  If you are implementing a
-- polynomial with two terms only, set POLY_C and POLY_D to zero.  POLY_A
-- sets the length of the shift register, so it must not be set to zero.
-- Is this copascetic with you?
--
-- In order to obtain a maximum cycle output, there must always be an even
-- number of taps used, and the positions of the taps must be relatively prime.
-- Note that these conditions are necessary, but not sufficient, to produce
-- the maximum-cycle output.  Any polynomial which *does* produce the maximum
-- cycle output is called "primitive." 
--
-- Many examples of primitive polynomials are listed in various tables.
-- This module was coded while referring to "Table of Linear Feedback Shift
-- Registers" by Roy Ward and Tim Molteno.
--
-- Some "primitive polynomials" are:
--   2,1
--   3,2
--   4,3
--   5,3
--   5,4,3,2
--   6,5
--   6,5,3,2
--   7,6
--   7,6,5,4
--   16,14,13,11
--   24,23,21,20
--   32,30,26,25
--   64,63,61,60
--   128,127,126,121
--   255,203
--   256,254,251,246
--   511,501
--   512,510,507,504
--   785,693
--   1024,1015,1002,1001
--   2048,2035,2034,2029
--   4096,4095,4081,4069
--
--   The number of bits in the shift register is equal to the polynomial's
--   order, and also equal to the highest tap position number.
-- 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
entity mlfsr is
    generic(
      POLY_A : natural := 8; -- Polynomial tap, also number of bits in shift register
      POLY_B : natural := 6; -- Polynomial tap, less than POLY_A
      POLY_C : natural := 5; -- Polynomial tap, less than POLY_B
      POLY_D : natural := 4  -- Polynomial tap, less than POLY_C
    );
    port (
      -- System Clock and Clock Enable
      sys_rst_n  : in  std_logic;
      sys_clk    : in  std_logic;
      sys_clk_en : in  std_logic;
 
      -- Sequence Output
      pn_seq_o   : out std_logic
 
    );
end mlfsr;
 
architecture beh of mlfsr is
 
  -- Constants
 
  -- Functions & associated types
 
  -- Signal Declarations
  signal sr : unsigned(POLY_A-1 downto 0);
  signal sr_next : unsigned(POLY_A-1 downto 0);
 
begin
 
process (sys_clk, sys_rst_n)
begin
  if (sys_rst_n='0') then
    sr <= (others=>'1');
  elsif (sys_clk'event and sys_clk='1') then
    if (sys_clk_en='1') then
      sr <= sr_next;
    end if; -- sys_clk_en
  end if; -- sys_clk
end process;
 
gen_sr_next : for i in 0 to POLY_A-2 generate
  sr_next(i) <= sr(i+1) xor sr(0) when (i=POLY_B or i=POLY_C or i=POLY_D) else
                sr(i+1);
end generate gen_sr_next;
sr_next(sr_next'length-1) <= sr(0);
 
pn_seq_o <= sr(0);
 
end beh;
 
 
-------------------------------------------------------------------------------
-- PCM signal bit period detector
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : Jan. 31, 2012 Started Coding, drawing from various other sources.
--                       Created description.
--         Feb.  6, 2012 Simulated and refined code.  Added output register.
--         Mar. 16, 2012 Made load_o a registered signal, so that the load
--                       pulses emerge when the new period information does.
--                 
--
-- Description
-------------------------------------------------------------------------------
-- This module applies a finite state machine controlled series of
-- measurements to an incoming digital data signal, and uses the measurements
-- to arrive at an estimate of the incoming signal's bit rate.
--
-- A successive approximation technique is used.
--
-- The way it works is as follows:  There are synchronizing flip-flops placed
-- to prevent metastability issues with the incoming signal.  Edge detectors
-- are then created using the outputs of these flip-flops.
--
-- The number of sys_clks between edges is then directly measured using a
-- counter, and these counts are fed into a successive approximation loop by
-- which the Baud interval (shortest interval between pulses) is first measured.
-- Then, based on this measurement, another measurement is taken over two Baud
-- intervals.  This measurement is then used as the basis for a new measurement
-- covering four Baud intervals, and so forth.  Each new measurement covers the
-- a period of 2^N Baud intervals, or bit times, where the highest N is set by
-- generics.
--
-- Note that noise can cause the initial Baud interval to be incorrect, which
-- will eventually cause the subsequent measurements to fail, and the state
-- machine will revert back and take a new Baud interval measurement.  Therefore,
-- no attempt is made to average several initial Baud interval measurements.
--
-- The result of the ultimate 2^N bit times measurement can be interpreted as a
-- measurement of the bit-period of the incoming data or clock signal, made
-- more accurate through averaging over 2^N intervals.  Therefore, the final
-- measurement of 2^Nmax Baud intervals is composed of an integer portion and
-- a fractional portion, the fractional portion being the Nmax least significant
-- bits of the period output.
--
-- By setting Nmax and taking the requisite amount of time to make the full 2^Nmax
-- bit-period measurement, results of "arbitrary precision" can therefore be
-- obtained.
--
-- The successive approximation approach is used in order to effectively home
-- in on the correct measurement, incrementally approaching the value by starting
-- with a direct measurement of the shortest interval - which is understood to
-- represent a single bit-period.  Since the signal might not have a transition
-- at every bit-period, there is a statistical component to the way in which the
-- successive approximation works.
--
-- Essentially, a number of attempts are made, starting at any given edge, and
-- extending through 2^N of the best-estimate "baud intervals" and looking for
-- another edge at around the expected time.  The edge may occur earlier or later
-- than the predicted time, and so the approach takes this into account.  Each
-- successful measurement is then multiplied by two via bit-shifting, and used
-- as the basis for the succeeding measurement.  So each measurement uses the
-- current best estimate of period, to take a new measurement which covers twice
-- as many bit times, so that each new result is essentially twice as precise
-- as its predecessor.
--
-- The number of attempts to be made for each measurement step is determined by
-- a generic setting.  The assumption is that edges are expected to be present
-- approximately 50% of the time, and so the probability of performing any given
-- measurement successfully is 1-(1/2^READS).  By setting READS sufficiently
-- high, as compared to Nmax, the overall probability of successfully 
-- "bootstrapping" all the way into a precise final bit-period measurement can
-- be made high enough to become practical and useful.
--
-- One of the important practical assumptions made about the incoming signal is
-- that the amount of jitter present in the signal edges is small compared to
-- the overall desired measurement precision.  Another assumption is that the
-- frequency drift of the incoming signal is low.  There is a "window" of
-- allowed variation from the expected edge location which this module considers
-- as "valid" measurements.  When the sum of jitter and phase variation due to
-- frequency drift produces edges that fall outside that window, the measurement
-- will fail despite the reasonably high probability that an edge is lurking
-- somewhere just outside the allowed window.
--
-- The size of the window is set via generics, and it is implemented as a +/-
-- tolerance through a clever technique in which the measurement counter during
-- the "HONE" state is allowed to count down to zero, after which it begins to
-- count up again.  At the time of the closing edge, the value in the counter is
-- compared with the window threshold, and the counter value then represents the
-- absolute value of the variation of the overall measured 2^N bit-time interval
-- from its expectation value.
--
-- It is intended that this module be instantiated multiple times inside a digital
-- bit-sync module, once for measuring the bit-period of the reference PCM data
-- signal, and once for measuring the bit-period of the generated clock.  In that
-- way the measurements will be "apples to apples" and any bias present in one
-- measurement should be identical in the other.
--
-- By comparing the measured periods, the clock signal can then be adjusted so that
-- its frequency tracks that of the incoming data signal for slow variations,
-- without requiring Fourier analysis, or the use of mathematical multipliers or
-- dividers.
--
-- This frequency tracking loop is to be a slow outer loop within the bit-sync,
-- with a faster phase tracking loop as the inner feedback loop.
-- 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.function_pack.all;
 
entity bit_period_detector is
    generic(
      USE_ANY_EDGE   : integer := 1; -- 0=Rising edges only, 1=Use any edge
      WINDOW_SIZE    : integer := 2; -- sys_clk variation allowed around closing edge
      IDLE_CLOCKS    : integer := 511; -- sys_clk periods before input "idle" is called
      BAUD_READS     : integer := 255; -- Number of baud interval transition measurements tried
      INTERVAL_READS : integer := 16; -- Number of 2^N interval read attempts to make
      INTEGER_WIDTH  : integer := 16; -- Bits in integer part of period measurement
      FRACTION_WIDTH : integer := 4  -- Bits in fractional part of period measurement
    );
    port (
      -- System Clock and Clock Enable
      sys_rst_n  : in  std_logic;
      sys_clk    : in  std_logic;
      sys_clk_en : in  std_logic;
 
      -- Signal input
      signal_i   : in  std_logic;
 
      -- outputs
      s_edge_o   : out std_logic; -- Strobe marking edges of signal_i which are used.
      period_o   : out unsigned(INTEGER_WIDTH+FRACTION_WIDTH-1 downto 0);
      load_o     : out std_logic;
      idle_o     : out std_logic
    );
end bit_period_detector;
 
architecture beh of bit_period_detector is
 
  -- Constants
  constant IDLE_COUNT_WIDTH     : integer := timer_width(IDLE_CLOCKS);
  constant BAUD_COUNT_WIDTH     : integer := timer_width(BAUD_READS);
  constant INTERVAL_COUNT_WIDTH : integer := bit_width(INTERVAL_READS);
  constant PERIOD_WIDTH         : integer := INTEGER_WIDTH+FRACTION_WIDTH;
  constant N_WIDTH              : integer := timer_width(FRACTION_WIDTH);
 
  -- Functions & associated types
 
  -- Signal Declarations
  signal period       : unsigned(PERIOD_WIDTH-1 downto 0);
  signal s_r1         : std_logic;
  signal s_r2         : std_logic;
  signal s_edge       : std_logic;
  signal c_count      : unsigned(PERIOD_WIDTH-1 downto 0); -- Used to count sys_clks between transitions
  signal c_count_next : unsigned(PERIOD_WIDTH-1 downto 0); -- This is c_count+1
  signal c_count_x2   : unsigned(PERIOD_WIDTH-1 downto 0);
  signal p_count      : unsigned(PERIOD_WIDTH-1 downto 0); -- Used to count sys_clks to window closure
  signal p_count_dir  : std_logic; -- High=counting up, Low=counting down
  signal baud_tries   : unsigned(BAUD_COUNT_WIDTH-1 downto 0); -- Counts baud interval assessments
  signal period_tries : unsigned(INTERVAL_COUNT_WIDTH-1 downto 0); -- Counts period interval assessments
  signal idle_count   : unsigned(IDLE_COUNT_WIDTH-1 downto 0); -- Counts sys_clk periods with no signal transition
  type P_STATE_TYPE is (IDLE, MEASURE_BAUD, HONE);
  signal p_state      : P_STATE_TYPE;
  signal n            : unsigned(N_WIDTH-1 downto 0);
 
begin
 
c_count_next <= c_count+1;
--c_count_x2   <= c_count_next(c_count_next'length-2 downto 0) & '0';
--c_count_x2   <= c_count_next + c_count; -- An attempt to "dither" or eliminate bias...
c_count_x2 <= c_count(c_count'length-2 downto 0) & '1';
 
process (sys_clk, sys_rst_n)
begin
  if (sys_rst_n='0') then
    s_r1         <= '0';
    s_r2         <= '0';
    c_count      <= (others=>'0');
    p_count      <= (others=>'1');
    p_count_dir  <= '0'; -- Counts down, until zero is reached...
    baud_tries   <= (others=>'0');
    period_tries <= (others=>'0');
    idle_count   <= to_unsigned(IDLE_CLOCKS,idle_count'length);
    period       <= (others=>'0');
    p_state      <= IDLE;
    n            <= (others=>'0');
    period_o     <= (others=>'1');
    load_o       <= '0';
  elsif (sys_clk'event and sys_clk='1') then
    if (sys_clk_en='1') then
      -- Defaults
      load_o <= '0';
 
      -- Two layers of flip-flops, to mitigate metastability
      s_r1 <= signal_i;
      s_r2 <= s_r1;
 
      -- Update the period counter
      -- It can be cleared by other logic statements below
      c_count <= c_count_next;
 
      -- Update the window counter
      -- It can be cleared by other logic statements below
      if (p_count_dir='1') then
        p_count <= p_count+1;
      else
        -- When counting down, there is a counter direction reversal
        -- which is used for implementing the tracking window +/-
        if (p_count=0) then
          p_count_dir <= p_count_dir xor '1';
          p_count <= p_count+1;
        else
          p_count <= p_count-1;
        end if;
      end if;
 
      -- Period State Machine
      case (p_state) is
 
        when IDLE =>
          if (s_edge='1') then
            p_state     <= MEASURE_BAUD;
            period      <= (others=>'1');
            p_count     <= (others=>'1');
            p_count_dir <= '0';
            c_count     <= (others=>'0');
            n           <= (others=>'0');
          end if;
 
        when MEASURE_BAUD =>
          if (s_edge='1') then
            if (baud_tries=BAUD_READS) then
              baud_tries   <= (others=>'0');
              period_tries <= (others=>'0');
              p_state      <= HONE;
              p_count      <= period;
              p_count_dir  <= '0'; -- Count down for HONE
              c_count      <= (others=>'0');
            else
              baud_tries <= baud_tries+1;
              if (c_count_next<period) then
                period <= c_count_next;
              end if;
              c_count <= (others=>'0');
            end if;
          end if;
 
        when HONE =>
          -- look for edges
          if (s_edge='1') then
            if (p_count <= WINDOW_SIZE) then
              if (n<FRACTION_WIDTH) then
                period      <= c_count_x2;
                p_count     <= c_count_x2;
                p_count_dir <= '0'; -- Count down
                c_count     <= (others=>'0');
                n           <= n+1;                
              elsif n=FRACTION_WIDTH then
                p_state     <= MEASURE_BAUD;
                period      <= (others=>'1');
                p_count     <= (others=>'1');
                p_count_dir <= '0';
                c_count     <= (others=>'0');
                n           <= (others=>'0');
                period_o    <= c_count;
                load_o      <= '1';
              end if;
            elsif (p_count_dir='1') then -- If the window has passed...
              if (period_tries<INTERVAL_READS-1) then
                period_tries <= period_tries+1;
                -- Adjust the period slightly, to improve odds of success with a tight window...
                --p_count      <= period-1;
                p_count      <= period;
                p_count_dir  <= '0'; -- Count down
                c_count      <= (others=>'0');
              else
                p_state     <= MEASURE_BAUD;
                period      <= (others=>'1');
                p_count     <= (others=>'1');
                p_count_dir <= '0';
                c_count     <= (others=>'0');
                n           <= (others=>'0');
              end if;
            end if;
          end if; -- s_edge='1'
 
      end case;
 
      -- Update the idle counter
      -- This must follow the state transition logic, since it can
      -- override it, and force the state to IDLE
      if (s_edge='1') then
        idle_count <= to_unsigned(IDLE_CLOCKS,idle_count'length);
      else
        if (idle_count=0) then
          p_state <= IDLE;
        else
          idle_count <= idle_count-1;
        end if;
      end if;
 
    end if; -- sys_clk_en
  end if; -- sys_clk
end process;
 
-- Implement edge detector.  Generic determines if all edges are used, or
-- just the rising edges.
s_edge <= s_r1 xor s_r2 when (USE_ANY_EDGE=1) else s_r1 and not s_r2;
 
s_edge_o <= s_edge;
idle_o <= '1' when p_state=IDLE else '0';
 
end beh;
 
 
-------------------------------------------------------------------------------
-- PCM signal period histogram checker
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : May   8, 2013 Copied code from bit_period_detector,
--                       Created description.
--         May  16, 2013 Checked the unit using simulation, and it
--                       looks pretty good.  Added ODD_N_LIMIT generic
--                       to prevent long intervals from always ending
--                       up in the oddball bin, due to uncertainty
--                       in measurement of the Baud interval.
--         Aug.  8, 2013 Corrected mathematical error in the averaging
--                       signal, and added i_count_b resets during
--                       Baud interval measurements, to prevent idle
--                       detection logic from triggering erroneously.
--         July 21, 2015 Added logic to prevent freq accumulator from
--                       missing counts when i_count_b>=ODD_N_LIMIT.
--
-- Description
-------------------------------------------------------------------------------
-- This module uses a finite state machine and some counters and comparators
-- to measure the Baud interval of an incoming signal.  In other words, it
-- measures the shortest interval between signal edges, in units of sys_clks.
--
-- The unit takes measurements constantly.  However, when the input is
-- idle, then no histogram data is updated as the unit remains in the
-- Baud interval measurement mode.
--
-- Once activity is detected on the selected signal input, a series of
-- intrvls_i intervals is measured with the true signal to find a Baud
-- interval measurement.  Following this, another series of the same
-- number of intervals is taken, but this time with the input signal
-- inverted.  The two results are compared.  If the two results differ by
-- more than +/- window_i sys_clks, then the signal is declared to have a
-- duty cycle which is unacceptably far from 50%, and the bad_duty_o
-- output is asserted.
--
-- After an acceptable duty cycle is found, the Baud interval is calculated
-- as the average of both the true and inverted signal Baud interval
-- measurements, and this value is updated to the output baud_o.
--
-- Following the baud interval cycle, the unit switches into histogram
-- and frequency measurement mode.  The internal histogram bin counters
-- are reset, and a set of intrvls_i intervals is measured.  Each
-- interval is analyzed to see if it is within +/- window_i sys_clks of
-- an integer multiple N of the Baud interval.  Based on the N value
-- found by the analysis, the appropriate histogram bin count is incremented.
-- Each interval found which is outside the allowable +/- variation is
-- an "oddball" interval, resulting in incrementing the bo_count value.
--
-- <<<Editorial Note>>>
-- (Skip this note if you don't feel super excited about the analysis of
--  PCM telemetry waveforms.)
--
-- During simulation, it was found that the uncertainty in measuring long
-- intervals, being cumulative, causes the oddball count bin to be
-- incremented when in the purest sense it should not be.  This is due
-- to the inability of this module to measure Baud intervals with
-- fractional accuracy.  Although more precise Baud average measurements
-- could be performed fairly easily, it would neccesitate a complete
-- rewrite of this unit in order to divide a measured interval by the
-- precise Baud interval.  Such subtlety is beyond the scope of this
-- effort.  Therefore, the "oddball" bin count will only be incremented
-- for N values less than the constant ODD_N_LIMIT.  Intervals longer
-- than this will not be considered oddball, effectively allowing
-- them to be properly counted in the bn_count bin.
-- <<<End Editorial Note>>>
--
-- When the entire set of intrvls_i intervals have been measured and
-- analyzed, the histogram counts are updated to the outputs, and the
-- process is repeated again.
--
-- Histogram bins are defined as follows:
--
--   b1_count = # of intervals equal to the Baud interval
--   b2_count = # of intervals equal to 2x the Baud interval
--   b3_count = # of intervals in which N is in the range [3..bo_limit_i]
--   bn_count = # of intervals in which N exceeds bo_limit_i
--   bo_count = # of oddball intervals
--
-- The idea behind this histogram is that squarewave clocks, such as the
-- self-test 10kHz "IO_sense" signal, result in histograms with high
-- totals in the b1 bin only, while biphase signals result in histograms
-- with high totals in the b1 and b2 bins, with an expected equal split.
-- NRZ-L and RNRZ-L will produce non-zero counts in the b3 bin.  NRZ-L
-- can produce non-negligible counts in the bn bin, depending on the
-- longest runs of zero or one present in the signal, while the RNRZ-L
-- input will yield a zero value in the bn bin, for suitably chosen
-- n_value_i.
--
-- Whenever bo_count exceeds the bo_limit_i threshold, the unit exits
-- histogram and frequency measurement mode, and reverts back to the
-- baud interval and duty cycle checking mode, without erasing any
-- previous measurements which are present at the outputs.
-- 
-- While in histogram population mode, a simultaneous frequency
-- measurement is being performed.  After each successful interval analysis,
-- the resulting N value is accumulated into a running total.  After
-- one second of operation, the accumulator value represents the frequency
-- of baud intervals per second, hence the symbol frequency from which
-- bit rate can be immediately deduced.  Each one second interval frequency
-- reading is updated to the output register, the accumulator is cleared,
-- and the frequency measurement process proceeds onward for another
-- second.
-- 
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.function_pack.all;
 
entity period_histogram_checker is
    generic(
      SYS_CLK_RATE   : real    := 50000000.0;
      NUM_CHAN       : natural := 8;  -- Number of channels to select from
      LOG2_NUM_CHAN  : natural := 3;  -- Bits needed for channel selection
      ODD_N_LIMIT    : natural := 4;  -- Upper cutoff for bo_count_o intervals to be tallied
      PERIOD_BITS    : natural := 14; -- Number of bits in Baud interval measurement
      HIST_BITS      : natural := 8   -- Number of bits in histogram bin counters
    );
    port (
      -- System Clock and Clock Enable
      sys_rst_n  : in  std_logic;
      sys_clk    : in  std_logic;
      sys_clk_en : in  std_logic;
 
      -- PCM signal inputs
      pcm_i      : in  unsigned(NUM_CHAN-1 downto 0);
 
      -- Settings & Control
      pcm_sel_i  : in  unsigned(LOG2_NUM_CHAN-1 downto 0);
      window_i   : in  unsigned(3 downto 0); -- sys_clks of variation allowed when checking intervals
      intrvls_i  : in  unsigned(HIST_BITS-1 downto 0); -- Number of intervals per histogram
      bo_limit_i : in  unsigned(HIST_BITS-1 downto 0); -- Number of oddball intervals tolerated per histogram cycle.
      n_value_i  : in  unsigned(4 downto 0); -- N value for bn_count histogram bin
 
      -- outputs
      bad_duty_o : out std_logic;
      b1_count_o : out unsigned(HIST_BITS-1 downto 0);
      b2_count_o : out unsigned(HIST_BITS-1 downto 0);
      b3_count_o : out unsigned(HIST_BITS-1 downto 0);
      bn_count_o : out unsigned(HIST_BITS-1 downto 0);
      bo_count_o : out unsigned(HIST_BITS-1 downto 0);
      baud_o     : out unsigned(PERIOD_BITS-1 downto 0);
      freq_o     : out unsigned(31 downto 0);
      idle_o     : out std_logic; -- High indicates signal is inactive for too long.
      new_o      : out std_logic  -- High pulse indicates new measurements were posted.
    );
end period_histogram_checker;
 
architecture beh of period_histogram_checker is
 
  -- Constants
  constant SECOND_BITS : natural := timer_width(SYS_CLK_RATE);
 
  -- Functions & associated types
 
  -- Signal Declarations
  signal baud_count   : unsigned(PERIOD_BITS-1 downto 0);
  signal baud_p_val   : unsigned(PERIOD_BITS-1 downto 0);
  signal baud_n_val   : unsigned(PERIOD_BITS-1 downto 0);
  signal baud_delta   : unsigned(3 downto 0);
  signal intrvl_delta : unsigned(3 downto 0);
  signal baud_sum     : unsigned(PERIOD_BITS downto 0);
  signal baud_avg     : unsigned(PERIOD_BITS-1 downto 0);
  signal i_count_a    : unsigned(PERIOD_BITS-1 downto 0);
  signal i_count_b    : unsigned(10 downto 0); -- up to 2047 symbol periods per interval
  signal baud_num     : unsigned(HIST_BITS-1 downto 0);
  signal intrvl_num   : unsigned(HIST_BITS-1 downto 0);
  signal b1_count     : unsigned(HIST_BITS-1 downto 0);
  signal b2_count     : unsigned(HIST_BITS-1 downto 0);
  signal b3_count     : unsigned(HIST_BITS-1 downto 0);
  signal bn_count     : unsigned(HIST_BITS-1 downto 0);
  signal bo_count     : unsigned(HIST_BITS-1 downto 0);
  signal freq         : unsigned(31 downto 0);
  signal second_count : unsigned(SECOND_BITS-1 downto 0);
  type P_STATE_TYPE is (BAUD_P1, BAUD_P2, BAUD_N1, BAUD_N2, DUTY_CHECK, HISTO1, HISTO2);
  signal p_state      : P_STATE_TYPE;
  signal s_r1         : std_logic;
  signal s_r2         : std_logic;
  signal s_edge       : std_logic;
  signal s_rising     : std_logic;
  signal s_falling    : std_logic;
  signal idle         : std_logic;
  signal pcm_sel_r1   : unsigned(LOG2_NUM_CHAN-1 downto 0);
 
begin
 
-- Provide outputs
baud_sum <= resize(baud_p_val,baud_sum'length) + resize(baud_n_val,baud_sum'length);
baud_avg <= baud_sum(baud_sum'length-1 downto 1); -- Divide by 2
idle_o <= idle;
 
-- Form edge detectors
s_edge <= s_r1 xor s_r2;
s_rising <= '1' when s_r1='1' and s_r2='0' else '0';
s_falling <= '1' when s_r1='0' and s_r2='1' else '0';
 
-- Form difference between positive and negative Baud intervals
baud_delta <= resize(unsigned(abs(signed(baud_p_val)-signed(baud_n_val))),baud_delta'length);
-- Form difference between interval subtraction remainder and Baud intervals
intrvl_delta <= resize(baud_avg-i_count_a,intrvl_delta'length);
 
-- State machine
process (sys_clk, sys_rst_n)
begin
  if (sys_rst_n='0') then
    s_r1         <= '0';
    s_r2         <= '0';
    baud_count   <= (others=>'0');
    baud_p_val   <= (others=>'1');
    baud_n_val   <= (others=>'1');
    i_count_a    <= to_unsigned(1,i_count_a'length);
    i_count_b    <= (others=>'0');
    intrvl_num   <= to_unsigned(0,intrvl_num'length);
    baud_num     <= to_unsigned(0,baud_num'length);
    b1_count     <= (others=>'0');
    b2_count     <= (others=>'0');
    b3_count     <= (others=>'0');
    bn_count     <= (others=>'0');
    bo_count     <= (others=>'0');
    b1_count_o   <= (others=>'0');
    b2_count_o   <= (others=>'0');
    b3_count_o   <= (others=>'0');
    bn_count_o   <= (others=>'0');
    bo_count_o   <= (others=>'0');
    freq         <= (others=>'0');
    freq_o       <= (others=>'0');
    idle         <= '0';
    bad_duty_o   <= '0';
    second_count <= str2u("0000001",second_count'length);
    p_state      <= BAUD_P1;
    baud_o       <= (others=>'0');
    new_o        <= '0';
    pcm_sel_r1   <= (others=>'0');
  elsif (sys_clk'event and sys_clk='1') then
    if (sys_clk_en='1') then
 
      -- Handle one second timer
      second_count <= second_count+1;
 
      -- Defaults
      new_o <= '0';
 
      -- Two layers of flip-flops, to mitigate metastability
      s_r1 <= pcm_i(to_integer(pcm_sel_i));
      s_r2 <= s_r1;
 
      -- Keep last selection, to detect changes
      pcm_sel_r1 <= pcm_sel_i;
 
      -- Measure intervals when signal is not idle
      if (idle='0') then
        if i_count_b=((2**i_count_b'length)-1) then
          idle <= '1';
          i_count_a <= to_unsigned(1,i_count_a'length);
          i_count_b <= (others=>'0');
        elsif i_count_a>=baud_avg then
          i_count_a <= to_unsigned(1,i_count_a'length);
          i_count_b <= i_count_b+1;
        else
          i_count_a <= i_count_a+1;
        end if;
      end if;
      -- Exit idle condition at any edge
      if (s_edge='1') then
        idle <= '0';
      end if;
 
      -- Period State Machine
      case (p_state) is
 
        when BAUD_P1 =>
          if (s_rising='1') then
            p_state <= BAUD_P2;
            baud_count <= (others=>'0');
          end if;
 
        when BAUD_P2 =>
          baud_count <= baud_count+1;
          if (s_falling='1') then
            baud_num <= baud_num+1; -- record that another interval is measured
            if (baud_count+1<baud_p_val) then
              baud_p_val <= baud_count+1;
            end if;
            if (baud_num=intrvls_i) then
              p_state <= BAUD_N1;
              baud_num <= to_unsigned(1,baud_num'length);
            else
              p_state <= BAUD_P1;
              i_count_b <= (others=>'0');
            end if;
          end if;
 
        when BAUD_N1 =>
          if (s_falling='1') then
            p_state <= BAUD_N2;
            baud_count <= (others=>'0');
          end if;
 
        when BAUD_N2 =>
          baud_count <= baud_count+1;
          if (s_rising='1') then
            baud_num <= baud_num+1; -- record that another interval is measured
            if (baud_count+1<baud_n_val) then
              baud_n_val <= baud_count+1;
            end if;
            if (baud_num=intrvls_i) then
              p_state <= DUTY_CHECK;
              baud_num <= to_unsigned(1,baud_num'length);
            else
              p_state <= BAUD_N1;
              i_count_b <= (others=>'0');
            end if;
          end if;
 
        when DUTY_CHECK =>
          if (baud_delta>window_i) then
            bad_duty_o <= '1';
            baud_num <= to_unsigned(1,baud_num'length);
            p_state <= BAUD_P1;
          elsif (s_edge='1') then
            bad_duty_o <= '0';
            baud_o <= baud_avg;
            i_count_a <= to_unsigned(1,i_count_a'length);
            i_count_b <= (others=>'0');
            p_state <= HISTO1;
          end if;
 
        when HISTO1 =>
          -- If an edge is encountered, then
          -- close out the last Baud period.
          if (s_edge='1') then
            intrvl_num <= intrvl_num+1;
            i_count_a <= to_unsigned(1,i_count_a'length);
            -- The ODD_N_LIMIT term here allows "oddball" intervals with high N
            -- to be tallied normally, since measurement uncertainty renders the 
            -- window_i value check useless for large N anyway.
            if (i_count_a<=window_i or i_count_b>=ODD_N_LIMIT) then -- means i_count_b has already been incremented.
              if (i_count_a<=window_i) then
                freq <= freq+i_count_b;
              else
                freq <= freq+i_count_b+1;
              end if;
              i_count_b <= (others=>'0');
              if (i_count_b=1) then
                b1_count <= b1_count+1;
              elsif (i_count_b=2) then
                b2_count <= b2_count+1;
              elsif (i_count_b>2) and (i_count_b<n_value_i) then
                b3_count <= b3_count+1;
              else
                bn_count <= bn_count+1;
              end if;                
            elsif (intrvl_delta<=window_i) then -- means i_count_b hasn't been incremented yet, but needs to.
              i_count_b <= i_count_b+1;
              p_state <= HISTO2;
            else
              freq <= freq+i_count_b;
              i_count_b <= (others=>'0');
              bo_count <= bo_count+1;
            end if;
            -- Transferring histogram totals to the outputs after the
            -- final interval measurement has higher priority than the
            -- incrementing of counts in the bins, so this is placed
            -- after the increment logic.
            if (intrvl_num>=intrvls_i) then
              p_state <= HISTO1; -- Abandon any excursions to HISTO2 at this point!
              i_count_b <= (others=>'0'); -- starting a brand new measurement
              new_o <= '1';
              intrvl_num <= to_unsigned(0,intrvl_num'length);
              b1_count_o <= b1_count;
              b2_count_o <= b2_count;
              b3_count_o <= b3_count;
              bn_count_o <= bn_count;
              bo_count_o <= bo_count;
              b1_count   <= (others=>'0');
              b2_count   <= (others=>'0');
              b3_count   <= (others=>'0');
              bn_count   <= (others=>'0');
              bo_count   <= (others=>'0');
              if (bo_count>=bo_limit_i) then
                p_state <= BAUD_P1;
              end if;
            end if;
          end if;
          -- Transfer frequency count to outputs each second
          if (second_count>=integer(SYS_CLK_RATE)) then
            freq <= (others=>'0');
            freq_o <= freq;
            second_count <= to_unsigned(1,second_count'length);
          end if;
 
        -- This is an "extra" state used to allow i_count_b to increment,
        -- witout incurring an extra adder...
        when HISTO2 =>
          p_state <= HISTO1;
          i_count_b <= (others=>'0');
          -- increment histogram counts.
          freq <= freq+i_count_b;
          if (i_count_b=1) then
            b1_count <= b1_count+1;
          elsif (i_count_b=2) then
            b2_count <= b2_count+1;
          elsif (i_count_b>2) and (i_count_b<n_value_i) then
            b3_count <= b3_count+1;
          else
            bn_count <= bn_count+1;
          end if;                
          -- Transfer frequency count to outputs each second
          if (second_count>=integer(SYS_CLK_RATE)) then
            freq <= (others=>'0');
            freq_o <= freq;
            second_count <= to_unsigned(1,second_count'length);
          end if;
 
      end case;
 
      -- Force state to BAUD_P if idle input is detected
      if (idle='1' or pcm_sel_i/=pcm_sel_r1) then
        p_state <= BAUD_P1;
        baud_count <= (others=>'1');
        intrvl_num <= to_unsigned(0,intrvl_num'length);
        baud_num   <= to_unsigned(0,intrvl_num'length);
        b1_count   <= (others=>'0');
        b2_count   <= (others=>'0');
        b3_count   <= (others=>'0');
        bn_count   <= (others=>'0');
        bo_count   <= (others=>'0');
        b1_count_o <= (others=>'0');
        b2_count_o <= (others=>'0');
        b3_count_o <= (others=>'0');
        bn_count_o <= (others=>'0');
        bo_count_o <= (others=>'0');
        freq       <= (others=>'0');
        freq_o     <= (others=>'0');
        baud_p_val <= (others=>'1');
        baud_n_val <= (others=>'1');
      end if;
 
    end if; -- sys_clk_en
  end if; -- sys_clk
end process;
 
end beh;
 
---------------------------------------------------------------------------------
-- PCM Input front end
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : May   9, 2013 Started coding.  Wrote description.
--         May  11, 2013 Finished initial coding, simulated.
--         Nov.  1, 2013 Added bit synchronizer interface port.
--                       Added description of required clock polarity,
--                       since this unit requires a falling edge in the
--                       middle of the data bit, to work properly.
--                       Because of this dependency, added clock
--                       inversion support.
--         July 16, 2015 Modified clock output so that the rising edge
--                       is in the middle of the data bit.
--                 
--
-- Description
-------------------------------------------------------------------------------
-- This module selects from among the available inputs, and uses input
-- settings to operate on the incoming signal.  It can remove
-- biphase line coding, and derandomize PCM data.
--
-- The PCM clock must have a falling edge in the middle of the PCM data bit,
-- for this unit to work correctly.
-- 
-- The taps used in derandomizing are programmable based on the input
-- dr_taps_i.
--
-- The baud_i input is used during biphase line code removal, as a way
-- to count the correct number of sys_clk periods so that the FSM can
-- remove the biphase coding, resulting in NRZ data.
--
-- A digital bit synchronizer interface port is included in this module.
-- If use_sync_i='0' then the sync_clk_i and sync_dat_i inputs are
-- ignored.  If use_sync_i='1' then the clk_sel_i input no longer
-- affects the selected clock, since the bit synchronizer clock is
-- used instead of selecting an input from sig_i.
--
-- One final note:  Since biphase line code removal includes clock
-- recovery, when biphase_i='1' the sync_clk_i input is not used,
-- although the data is taken from sync_dat_i.
--
-- The sys_rst_n input is an asynchronous reset.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.function_pack.all;
 
entity pcm_input_front_end is
    generic(
      NUM_CHAN       : natural := 4;  -- Number of channels to select from
      LOG2_NUM_CHAN  : natural := 2;  -- Bits needed for channel selection
      BAUD_BITS      : natural := 10;  -- Bits used in Baud interval counting
      DERAND_BITS    : natural := 16
    );
    port (
      -- System Clock and Clock Enable
      sys_rst_n  : in  std_logic;
      sys_clk    : in  std_logic;
      sys_clk_en : in  std_logic;
      fast_clk   : in  std_logic; -- A clock faster than sys_clk, for biphase line code removal
 
      -- PCM signal inputs
      sig_i      : in  unsigned(NUM_CHAN-1 downto 0);
 
      -- Signal selection settings
      clk_sel_i  : in  unsigned(LOG2_NUM_CHAN-1 downto 0);
      dat_sel_i  : in  unsigned(LOG2_NUM_CHAN-1 downto 0);
 
      -- Line Code Settings
      dr_taps_i  : in  unsigned(DERAND_BITS-1 downto 0);
      baud_i     : in  unsigned(BAUD_BITS-1 downto 0);
      derandom_i : in  std_logic;
      clk_inv_i  : in  std_logic;
      dat_inv_i  : in  std_logic;
      biphase_i  : in  std_logic;
      mark_i     : in  std_logic;
      level_i    : in  std_logic;
 
      -- Bit Synchronizer Interface Port
      -- If no synchronizer is present, simply tie use_sync_i to '0'
      use_sync_i : in  std_logic;
      pcm_dat_o  : out std_logic;
      sync_dat_i : in  std_logic;
      sync_clk_i : in  std_logic;
 
      -- signal outputs
      nrzl_dat_o : out std_logic;
      nrzl_clk_o : out std_logic
    );
end pcm_input_front_end;
 
architecture beh of pcm_input_front_end is
 
  -- Constants
 
  -- Functions & associated types
 
  -- Signal Declarations
  signal pcm_in           : std_logic;
  signal pcm_in_r1        : std_logic;
  signal pcm_in_r2        : std_logic;
  signal pcm_edge         : std_logic;
  signal clk_in           : std_logic;
  signal clk_in_r1        : std_logic;
  signal clk_in_r2        : std_logic;
  signal clk_nrzl_falling : std_logic;
  signal baud_count       : unsigned(BAUD_BITS-1 downto 0);
  signal nrzl_clk_r1      : std_logic;
  signal nrzl_clk_r2      : std_logic;
  signal pcm_nrzl         : std_logic;
  signal pcm_a            : std_logic;
  signal pcm_a_r1         : std_logic;
  signal pcm_unbi         : std_logic;
  signal pcm_derand       : std_logic;
  signal derand_sr        : unsigned(DERAND_BITS-1 downto 0);
  signal bp_clk           : std_logic;
  signal bp_clk_r1        : std_logic;
  signal half_baud        : unsigned(BAUD_BITS-1 downto 0);
 
  signal pcm_dat_l        : std_logic;
  signal pcm_dat_lr1      : std_logic;
  signal pcm_dat_lr2      : std_logic;
 
  signal pcm_clk_l        : std_logic;
  signal pcm_clk_lr1      : std_logic;
  signal pcm_clk_lr2      : std_logic;
 
  type FSM_STATE_TYPE is (SCAN_BAUD1, SCAN_BAUD2, CLK1, CLK2, DAT1, DAT2);
  signal fsm_state        : FSM_STATE_TYPE;
 
  -- Signals used to synchronize the output to the sys_clk rate
  signal nrzl_clk_choice  : std_logic;
  signal nrzl_clk_s       : unsigned(1 downto 0);
  signal nrzl_dat_choice  : std_logic;
  signal nrzl_dat_s       : unsigned(1 downto 0);
 
begin
 
  -- Select the desired signals
  -- These selections are run through two stages of flip-flops for
  -- metastability mitigation.
  pcm_dat_l <= sig_i(to_integer(dat_sel_i)) xor dat_inv_i; -- data inversion support
  pcm_clk_l <= sig_i(to_integer(clk_sel_i)) xor clk_inv_i; -- clock inversion support
 
  -- Provide the selected data signal to the bit synchronizer port
  pcm_dat_o <= pcm_dat_lr2;
 
  -- Select desired inputs
  -- For biphase, the clock input gets totally ignored
  pcm_in <= pcm_dat_lr2 when use_sync_i='0' else sync_dat_i;
  clk_in <= pcm_clk_lr2 when use_sync_i='0' else sync_clk_i;
 
  -- Create PCM clock falling edge signal
  clk_nrzl_falling <= '1' when nrzl_clk_r1='0' and nrzl_clk_r2='1' else '0';
 
  -- Select which signals get sent out
  pcm_a <= pcm_in_r2 when biphase_i='0' else pcm_unbi;
  nrzl_dat_choice <= pcm_nrzl when derandom_i='0' else pcm_derand;
  nrzl_clk_r1 <= clk_in_r1 when biphase_i='0' else bp_clk;
  nrzl_clk_r2 <= clk_in_r2 when biphase_i='0' else bp_clk_r1;
  nrzl_clk_choice <= nrzl_clk_r2; -- (rising edge is in middle of data bit.)
 
  --------------------------
  -- Synchronize outputs to sys_clk
  proc_sync_out: Process(sys_rst_n,sys_clk)
  begin
    if (sys_rst_n = '0') then
      nrzl_clk_s <= (others=>'0');
      nrzl_dat_s <= (others=>'0');
    elsif (sys_clk'event AND sys_clk='1') then
      nrzl_clk_s(0) <= nrzl_clk_choice;
      nrzl_clk_s(1) <= nrzl_clk_s(0);
      nrzl_dat_s(0) <= nrzl_dat_choice;
      nrzl_dat_s(1) <= nrzl_dat_s(0);
    end if;
  end process;
  nrzl_clk_o <= nrzl_clk_s(1);
  nrzl_dat_o <= nrzl_dat_s(1);
 
  -- Create an "either edge" detector on pcm_in
  pcm_edge <= pcm_in_r1 xor pcm_in_r2;
 
  -- Formulate a signal which represents half the Baud interval
  half_baud <= '0' & baud_i(BAUD_BITS-1 downto 1);
 
  --------------------------
  -- Remove biphase and recover clock
  process (fast_clk, sys_rst_n)
  begin
    if (sys_rst_n='0') then
      fsm_state  <= SCAN_BAUD1;
      bp_clk     <= '0';
      bp_clk_r1  <= '0';
      pcm_in_r1  <= '0';
      pcm_in_r2  <= '0';
      clk_in_r1  <= '0';
      clk_in_r2  <= '0';
      pcm_unbi <= '0';
      baud_count <= to_unsigned(1,baud_count'length);
      pcm_dat_lr1 <= '0';
      pcm_dat_lr2 <= '0';
      pcm_clk_lr1 <= '0';
      pcm_clk_lr2 <= '0';
    elsif (fast_clk'event and fast_clk='1') then
      -- Metastability mitigation flip-flops
      pcm_dat_lr1 <= pcm_dat_l;
      pcm_dat_lr2 <= pcm_dat_lr1;
      pcm_clk_lr1 <= pcm_clk_l;
      pcm_clk_lr2 <= pcm_clk_lr1;
 
      -- Handle the Baud interval counter
      baud_count <= baud_count+1;
 
      --if (sys_clk_en='1') then
        -- default values
 
        -- delayed version of signals for edge detection
        bp_clk_r1  <= bp_clk;
        pcm_in_r1  <= pcm_in;
        pcm_in_r2  <= pcm_in_r1;
        clk_in_r1  <= clk_in;
        clk_in_r2  <= clk_in_r1;
 
        -- Finite State Machine
        case (fsm_state) is
 
          when SCAN_BAUD1 =>
            if (pcm_edge='1') then
              baud_count <= to_unsigned(1,baud_count'length);
            elsif (baud_count=baud_i) then
              baud_count <= to_unsigned(0,baud_count'length);
              fsm_state <= SCAN_BAUD2;
            end if;
 
          when SCAN_BAUD2 =>
            if (pcm_edge='1') then
              baud_count <= to_unsigned(1,baud_count'length);
              fsm_state <= SCAN_BAUD1;
            elsif (baud_count=half_baud) then
              baud_count <= to_unsigned(1,baud_count'length);
              fsm_state <= CLK1;
            end if;
 
          when CLK1 =>
            if (pcm_edge='1') then
              baud_count <= to_unsigned(0,baud_count'length);
              fsm_state <= CLK2;
              pcm_unbi <= pcm_in_r2;
              bp_clk <= '0';
            elsif (baud_count=baud_i) then
              baud_count <= to_unsigned(1,baud_count'length);
              fsm_state <= SCAN_BAUD1;
            end if;
 
          when CLK2 =>
            if (pcm_edge='1') then
              baud_count <= to_unsigned(1,baud_count'length);
              fsm_state <= SCAN_BAUD1;
            elsif (baud_count=half_baud) then
              baud_count <= to_unsigned(0,baud_count'length);
              fsm_state <= DAT1;
            end if;
 
          when DAT1 =>
            if (pcm_edge='1') then
              baud_count <= to_unsigned(1,baud_count'length);
              fsm_state <= DAT2;
              bp_clk <= '1';
            elsif (baud_count=half_baud) then
              baud_count <= to_unsigned(0,baud_count'length);
              fsm_state <= DAT2;
              bp_clk <= '1';
            end if;
 
          when DAT2 =>
            if (pcm_edge='1') then
              baud_count <= to_unsigned(1,baud_count'length);
              fsm_state <= SCAN_BAUD1;
            elsif (baud_count=half_baud) then
              baud_count <= to_unsigned(1,baud_count'length);
              fsm_state <= CLK1;
            end if;
 
          when others =>
            null;
        end case;
 
      --end if; -- sys_clk_en
    end if; -- sys_clk
  end process;
 
  --------------------------
  -- Process to remove mark/space coding
  proc_unmark: Process(sys_rst_n,fast_clk)
  begin
    if (sys_rst_n = '0') then
      pcm_nrzl <= '0';
      pcm_a_r1 <= '0';
    elsif (fast_clk'event AND fast_clk='1') then
      if (clk_nrzl_falling='1') then
        pcm_a_r1 <= pcm_a; -- pcm_a already synchronized... just need one flip-flop.
        if (level_i='1') then
          pcm_nrzl <= pcm_a_r1; -- Default : No coding to be removed, just delay the input.
        else
          if (mark_i='1') then
            pcm_nrzl <= pcm_a xor pcm_a_r1; -- In Mark, '1' is represented by change in level
          else
            pcm_nrzl <= not (pcm_a xor pcm_a_r1); -- In Space, '0' is represented by change in level
          end if;
        end if;
      end if;
    end if;
  end process;
 
  --------------------------
  -- Derandomizer shift register
  proc_derand_sr: Process(sys_rst_n,fast_clk)
  begin
    if (sys_rst_n = '0') then
      derand_sr <= (others=>'0');
    elsif (fast_clk'event AND fast_clk='1') then
      if (clk_nrzl_falling='1') then
        derand_sr <= derand_sr(derand_sr'length-2 downto 0) & pcm_nrzl;
      end if;
    end if;
  end process;
  pcm_derand <= pcm_nrzl xor u_recursive_parity(derand_sr and dr_taps_i);
 
end beh;
 
---------------------------------------------------------------------------------
-- Digital Phase Locked Loop (With Alexander "Bang-Bang" Phase Detector)
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : Mar. 26, 2014 Obtained code from Jacob Fenton, tested it in
--                       simulation.  Formatted the code to suit my own
--                       personal coding style, and added the constant
--                       lebensraum.
--         Sep. 20, 2017 Added kp_1 and ki_1 to extend kp_i and ki_i to
--                       P_BITS length, prior to applying shift_right.
--                       This restores the precision that was being
--                       truncated, and allows getting rid of the
--                       user-defined resize function.
--
-- Description
-------------------------------------------------------------------------------
-- This module includes an NCO (squarewave DDS), a lowpass filter and
-- an Alexander "bang-bang" type phase detector configured as a
-- digital PLL.
--
-- The original design was set up, coded and simulated by
-- Jacob Fenton.
--
-- This version includes a bandwidth reduction input to allow for
-- reducing the loop bandwidth once lock has been achieved.
--
-- -------------------------------------------------------------
-- Functional Description:
-- -------------------------------------------------------------
-- The following constants, taken from Jacob Fenton's testbench,
-- serve to illustrate how to adjust the settings:
--
--   constant NCO_BITS    : integer := 32;
--   constant P_BITS      : integer := 32;
--   constant I_BITS      : integer := 32;
--   constant sysclk      : real := 50.0E+6;--fpga sys_clk rate
--   constant baud        : real := 3.0E+6;--expected data rate
--   constant dmp_fctr    : real := 0.7071;
--   constant pi : real   := 3.14159;
--   constant bw : real   := (0.005*baud);--desired dpll bandwidth as percentage of baud rate
--   constant ko : real   := (pi*sysclk)/("**"(2,real(NCO_BITS-1)));--nco gain (rad/sec)
--   constant kd : real   := ((sysclk/baud)*2.0)/pi;--phase detector gain (1/rad)
--   constant kp : integer:= integer((dmp_fctr*2.0*2.0*pi*bw)/(ko*kd));
--   constant ki : integer:= integer(("**"(2.0*pi*bw,2))/(sysclk*ko*kd));--need to make sure that with settings this value > 0, at some point use fixed point numbers
--   constant w  : integer:= integer(2.0*("**"(2,real(NCO_BITS-1)))/(sysclk/baud));
--
-- -------------------------------------------------------------
 
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity dpll_alex_bw_adjust is
  generic (
    NCO_BITS    : integer := 32;
    KP_I_BITS   : integer := 24;
    KI_I_BITS   : integer := 24;
    K_FRAC_BITS : integer := 8;
    GAIN_DIV    : integer := 3
  );
  port(
    sys_clk    : in  std_logic;
    sys_rst_n  : in  std_logic;
    sys_clk_en : in  std_logic;
    clear_i    : in  std_logic;
    dat_i      : in  std_logic;
    w_i        : in  unsigned(NCO_BITS-1 downto 0);
    kp_i       : in  unsigned(KP_I_BITS-1 downto 0);
    ki_i       : in  unsigned(KI_I_BITS-1 downto 0);
    bit_lock_i : in std_logic;
    dat_o      : out std_logic;
    clk_o      : out std_logic
    );
end dpll_alex_bw_adjust;
 
architecture beh of dpll_alex_bw_adjust is
 
  constant P_BITS : integer := KP_I_BITS+K_FRAC_BITS;
  constant I_BITS : integer := KI_I_BITS+K_FRAC_BITS;
  constant zero   : unsigned(P_BITS-2 downto 0) := (others => '0');
 
  signal lpf_o    : unsigned(P_BITS-1 downto 0);
  signal int_acum : unsigned(P_BITS-1 downto 0);
  signal kp_1     : unsigned(P_BITS-1 downto 0);
  signal ki_1     : unsigned(I_BITS-1 downto 0);
  signal kp_new   : unsigned(P_BITS-1 downto 0);
  signal ki_new   : unsigned(I_BITS-1 downto 0);
  signal nco_acum : unsigned(NCO_BITS-1 downto 0);
  signal up       : std_logic;
  signal dn       : std_logic;
  signal reg1     : std_logic;
  signal reg2     : std_logic;
  signal reg3     : std_logic;
  signal reg4     : std_logic;
  signal nco_clk  : std_logic;
 
begin
 
  clk_o <= nco_clk;
  dat_o <= reg4;
 
  nco : process(sys_clk, sys_rst_n)
  begin
    if (sys_rst_n = '0') then
      nco_acum <= (others=>'0'); -- was w_i
      nco_clk <= '0';
    elsif rising_edge(sys_clk) then
      if (sys_clk_en='1') then
        if lpf_o(P_BITS-1) = '0' then --check sign of lpf_o
          nco_acum <= nco_acum + w_i + unsigned(shift_right(signed(lpf_o),K_FRAC_BITS)) + (zero & lpf_o(9));
        else
          nco_acum <= nco_acum + w_i - not(unsigned(shift_right(signed(lpf_o),K_FRAC_BITS))) + 1 - (zero & lpf_o(9));
        end if;
        nco_clk <= nco_acum(NCO_BITS-1);
      end if; -- sys_clk_en
    end if; -- sys_clk
  end process nco;
 
  lpf : process(sys_clk, sys_rst_n)
  begin
    if (sys_rst_n = '0') then
      lpf_o <= (others=>'0');
      int_acum <= (others=> '0');
    elsif rising_edge(sys_clk) then
      if (sys_clk_en='1') then
        if (up = '1' and dn = '0') then --indicates need to speed up
          lpf_o <= int_acum + kp_new;
          int_acum <= int_acum + ki_new;
        elsif (up = '0' and dn = '1') then --indicates need to slow down
          lpf_o <= int_acum - kp_new; 
          int_acum <= int_acum - ki_new;
        end if;
      end if; -- sys_clk_en
    end if; -- sys_clk
  end process lpf;
 
  -- Adjust the bandwidth based on the bit_lock_i input
  kp_1   <= kp_i & to_unsigned(0,K_FRAC_BITS);
  kp_new <= shift_right(kp_1,gain_div) when bit_lock_i='1' else kp_1;
    -- John Clayton noted: Mathematically, according to the formulae given, reducing bandwidth by
    -- a certain amount entails a direct division of kp_i, but the ki_i factor should be divided by
    -- the square of the reduction factor...
  ki_1   <= ki_i & to_unsigned(0,K_FRAC_BITS);
  ki_new <= shift_right(ki_1,2*gain_div) when bit_lock_i='1' else ki_1;
 
  up <= reg4 xor reg1;
  dn <= reg2 xor reg4;
 
  alex_pfd : process(nco_clk, sys_rst_n)
  begin
    if (sys_rst_n = '0') then
      reg1 <= '0';
      reg2 <= '0';
      reg3 <= '0';
      reg4 <= '0';
    elsif rising_edge(nco_clk) then
      reg1 <= dat_i;
      reg2 <= reg1;
      reg4 <= reg3;
    elsif falling_edge(nco_clk) then
      reg3 <= dat_i;
    end if;
  end process alex_pfd;
 
end beh;
 
 
---------------------------------------------------------------------------------
-- Bit Sync lock detector Module
-------------------------------------------------------------------------------
--
-- Author: Jacob Fenton, with modifications by John Clayton
-- Date  : Jan. 26, 2012 Obtained code from Jacob Fenton, wrote header
--                       and description.
--
-- Description
-------------------------------------------------------------------------------
-- This module implements a bit synchronization lock detection function.
--
-- The bit synchronizer lock detector makes the initial assumption that the
-- bit synchronizer is not locked.
-- The bit synchronizer lock detection mechanism starts a countdown from
-- BIT_CNT_MAX.  Whenever a change is detected on the data signal, the
-- accompanying clock signal is also checked for changes.  If a change
-- is present, the counter is decremented, otherwise the counter is
-- incremented.  However, the counter is not allowed to "roll over"
-- past zero or BIT_CNT_MAX.  Once the counter reaches a value below
-- BIT_CNT_1, the bit synchronizer is said to be in a locked state.
-- In order to be counted valid, the bit synchronizer clock edge must
-- be present either at the same or previous system clock edge as the
-- data transition.
--
-- When the count surpasses BIT_CNT_2, the bit synchronizer is said to
-- be in an unlocked state.  Thus, the BIT_CNT_2 and BIT_CNT_1 thresholds
-- can be set so as to produce a desired amount of hysteresis in the lock
-- determination algorithm.  Note that BIT_CNT_2 must be higher than
-- BIT_CNT_1 to be of any effect at all.  If BIT_CNT_2 is less than or
-- equal to BIT_CNT_1, then once lock is achieved, it will never be
-- declared lost until after a reset.
--
-- Another observation about this module is that it assumes the bit
-- synchronizer always produces a clock edge within one system clock
-- cycle of the time when a data edge occurs, which is not a strict
-- requirement on bit synchronizers in general.  In fact, the heuristic
-- "digital bit synchronizer" does not meet this requirement, implying
-- that this module's requirements on data transition and clock transition
-- proximity would need to be relaxed in order to use it with the
-- digital bit synchronizer.
--
-- One final observation is that the initial lock takes BIT_CNT_MAX 
-- bit-synchronizer clocks to occur, while reacquisition of lock after
-- losing it can be as rapid as (BIT_CNT_2-BIT_CNT_1) bit synchronizer clocks.
--
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.function_pack.all;
 
entity bitsync_lock_detector is
  generic (
    BIT_CNT_MAX   : integer := 255;
    BIT_CNT_1     : integer := 5;
    BIT_CNT_2     : integer := 15
  );
  port(
    sys_clk    : in  std_logic;
    sys_rst_n  : in  std_logic;
    sys_clk_en : in  std_logic;
    dat_i      : in  std_logic;
    pll_clk    : in  std_logic;
    lock_o     : out std_logic
    );
end bitsync_lock_detector;
 
architecture beh of bitsync_lock_detector is
 
signal bit_cntr : unsigned(bit_width(BIT_CNT_MAX)-1 downto 0);
signal dat_chng : std_logic;
signal bit_lock : std_logic;
signal dat_reg1 : std_logic;
signal dat_reg2 : std_logic;
signal pll_clk_chng : std_logic;
signal pll_clk_reg1 : std_logic;
signal pll_clk_reg2 : std_logic;
 
begin    
 
  dat_chng <= dat_i xor dat_reg2;
  pll_clk_chng <= pll_clk xor pll_clk_reg2;
  lock_o <= bit_lock;
 
  bit_lock_proc : process(sys_rst_n, sys_clk)
  begin
    if (sys_rst_n = '0') then
      bit_cntr <=  to_unsigned(bit_cnt_max,bit_cntr'length);
      dat_reg1 <= '0';
      dat_reg2 <= '0';
      pll_clk_reg1 <= '0';
      pll_clk_reg2 <= '0';
      bit_lock <= '0';
    elsif rising_edge(sys_clk) then
      if (sys_clk_en='1') then
        dat_reg1 <= dat_i;
        dat_reg2 <= dat_reg1;
        pll_clk_reg1 <= pll_clk;
        pll_clk_reg2 <= pll_clk_reg1;
        if bit_cntr < bit_cnt_1 then
          bit_lock <= '1';
        elsif bit_cntr > bit_cnt_2 then
          bit_lock <='0';
        end if; 
 
        if dat_chng = '1' then
          if (pll_clk_chng = '1') and (bit_cntr > 0) then
            bit_cntr <= bit_cntr-1;
          elsif (pll_clk_chng = '0') and (bit_cntr < bit_cnt_max) then
            bit_cntr <= bit_cntr+1;
          end if;
        end if;
      end if; -- sys_clk_en
    end if; -- sys_clk
 
 
  end process bit_lock_proc;
 
end beh;
 
 
---------------------------------------------------------------------------------
-- Digital Bit Sync Module
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : Jan. 26, 2012 Started Coding, drawing from various other sources.
--                       Created description.
--         Feb. 10, 2012 Achieved good results in simulation.  Cleaned up
--                       the code, and re-wrote the description.
--         Feb. 13, 2012 Tested via simulation.  Added new state transition
--                       from PHASE_TRACK to FREQ_SEEK state.  Added
--                       "tweak_bias_trigger" to prevent this transition from
--                       being taken too quickly after entry into PHASE_TRACK.
--         Mar. 16, 2012 Added logic to use delta phase zero crossings as the
--                       trigger for exiting FREQ_SEEK.  This facilitates
--                       proper exit with low frequency signals, when the
--                       period changes are large.  Removed ADJUST state.
--                       Added FREQ_SEEK2 state for medium granularity
--                       frequency search.
--         Oct. 30, 2013 Added "freq_seek_i" input to allow external enabling
--                       of the "auto frequency seeking" feature.  When
--                       "freq_seek_i" is low, the unit operates in the
--                       PHASE_TRACK state only.  Moved other generic settings
--                       over to signal inputs, in anticipation of making
--                       a register connected bit_sync module.  Changed module
--                       name to "bit_sync_digital."  Added "use_alex_i" input
--                       to select between heuristic and Alexander DPLL
--                       techniques.  Added Alexander DPLL version.
--         Nov. 14, 2014 Added alex_kp_i and alex_ki_i inputs.
--                 
--
-- Description
-------------------------------------------------------------------------------
-- This module applies a finite state machine controlled series of
-- phase and bit-period measurements to an incoming data signal, and 
-- uses the measurements to guide the operation of a direct digital 
-- synthesizer (DDS) which creates an output clock signal to accompany the
-- data stream.
--
-- Because the incoming data signal is sampled in a purely bi-level way,
-- and may contain sequences of adjacent '1' and '0' values, direct
-- measurement of the frequency is difficult.  Similarly, direct measurement
-- of the period is also difficult.  In this case, the decision was made
-- to measure period between transitions.  Using Fourier analysis to make
-- measurements in the frequency domain should also be possible in theory,
-- however this has been ruled out due to the complexity and resource 
-- utilization demanded by the FFT approach.
--
-- Precise bit-period measurements are performed on the incoming data stream
-- and the generated clock signal using a statistical successive
-- approximation technique described in the "bit_period_detector" module.
-- Two bit_period_detector modules are instantiated, one for the data
-- and one for the clock.  The bit_period_detector modules contain
-- synchronizing flip-flops to mitigate metastability issues when feeding
-- in signals from other clock domains.
-- 
-- The data signal is expected to be digital in nature, with transitions
-- that are rapid enough to be measured without significant noise-related
-- jitter.  The data is also required to be a bi-level signal in which the
-- symbols, or data bits, are of reasonably uniform symbol period.
-- It is expected that there be transitions at least every 2^PERIOD_I_WIDTH
-- symbol periods, and that there be at least one baud interval every 
-- BAUD_RD_TRIES intervals, where an interval is defined as the time
-- between two transitions, and a "baud interval" is defined as the
-- shortest expected interval.  Intervals shorter than the baud interval
-- would only be caused by noise...
-- It is also expected that the signal be stable in frequency, so that
-- any drift or frequency variation can be tracked by a slowly responding
-- frequency feedback loop, while the phase of the generated clock can
-- track the phase of the data signal in a much more rapidly responding
-- feedback loop.
--
-- For the phase locking loop, whenever a transition is found in the data,
-- the DDS phase accumulator is checked to see how close it is to producing
-- a transition.  Any value above or below zero at that point in time is
-- viewed as a "residual phase" which ideally should not be present.
-- A window of values is defined in which the residual phase is considered
-- small enough to ignore.  However, if the residula phase exceeds the
-- threshold, the DDS phase accumulator is reset to zero, thus keeping the
-- output clock in good phase syncronization with the data.  The closer the
-- DDS output frequency is to the actual frequency of the incoming data bits,
-- the more bit periods of "freewheeling drift" can be tolerated.
-- The drift time is defined as any time period during which there are no
-- transitions in the incoming data stream.
--
-- In order to continuously refine the frequency setting of the DDS during
-- phase tracking, at the time the phase accumulator is being reset to zero,
-- the sign of the residual phase is checked.  This is fed into an up/down
-- counter which integrates this residual phase "error" signal.  When the 
-- integrated error signal becomes high enough, the frequency setting is
-- adjusted to minimize the frequency error in the future.
--
-- The sys_rst_n input is an asynchronous reset.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.bit_sync_pack.all;
 
entity bit_sync_digital is
    generic(
      ALEX_K_BITS    : integer :=    32; -- Number of bits in Alexander DPLL coefficients
      ALEX_K_FRAC    : integer :=     8; -- How many of the ALEX_K_BITS are used for fractional representation
      ALEX_GAIN_ADJ  : integer :=     2; -- How many bits to reduce KP_i and KI_i during lock
      FSTEP_FINE     : integer :=  2**4; -- Used during PHASE_TRACK
      PHASE_WIDTH    : integer :=    33; -- Bits in the phase accumulator
      LOCK_VAL       : integer :=     4; -- Period mismatch limit to obtain lock
      DROP_LOCK_VAL  : integer :=    12; -- Period mismatch tolerance during lock
      PERIOD_I_WIDTH : integer :=    16; -- Integer width of period measurements
      PERIOD_F_WIDTH : integer :=     2; -- Fractional width of period measurements
      IDLE_CLOCKS    : integer := 2**16; -- sys_clk periods before signal is called idle
      BAUD_READS     : integer :=   127; -- Period baud interval read attempts
      INTERVAL_READS : integer :=    64; -- Period interval read attempts
      WINDOW_SIZE    : integer :=     3  -- Period variation window, in sys_clks
    );
    port (
      -- System Clock and Clock Enable
      sys_rst_n    : in  std_logic;
      sys_clk      : in  std_logic;
      sys_clk_en   : in  std_logic;
 
      -- Frequency calibration clk enable
      cal_clk_en   : in  std_logic;
 
      -- Settings
      freq_i       : in  unsigned(PHASE_WIDTH-2 downto 0);
      freq_seek_i  : in  std_logic;
      use_alex_i   : in  std_logic;
      alex_kp_i    : in  unsigned(ALEX_K_BITS-ALEX_K_FRAC-1 downto 0);
      alex_ki_i    : in  unsigned(ALEX_K_BITS-ALEX_K_FRAC-1 downto 0);
 
      -- Reference data input
      dat_i        : in  std_logic;
 
      -- indicators and outputs
      idle_o       : out std_logic;
      lock_o       : out std_logic;
      dat_o        : out std_logic;
      clk_o        : out std_logic
    );
end bit_sync_digital;
 
architecture beh of bit_sync_digital is
 
  -- Constants
  constant PERIOD_T_WIDTH   : integer := PERIOD_I_WIDTH+PERIOD_F_WIDTH;
  constant TWEAK_BIAS_WIDTH : integer := 6;
  constant ALEX_NCO_BITS    : integer := 33;
  constant ALEX_W_SHIFT     : integer := 2**(ALEX_NCO_BITS-PHASE_WIDTH);
 
  -- Functions & associated types
 
  -- Signal Declarations
  signal phase          : unsigned(PHASE_WIDTH-1 downto 0);
  signal phase_next     : unsigned(PHASE_WIDTH-1 downto 0);
  signal freq           : unsigned(PHASE_WIDTH-1 downto 0);
  signal locked         : std_logic;
  signal locked_a       : std_logic;
  signal delta_p        : signed(PERIOD_T_WIDTH-1 downto 0);
  signal phase_tweak    : std_logic;
  signal tweak_bias     : unsigned(TWEAK_BIAS_WIDTH-1 downto 0);
  signal tweak_bias_trigger : std_logic;
  signal lock           : std_logic;
  signal unlock         : std_logic;
 
  signal clk_period     : unsigned(PERIOD_T_WIDTH-1 downto 0);
  signal clk_p_load     : std_logic;
  signal clk_p_idle     : std_logic;
 
  signal dat_period     : unsigned(PERIOD_T_WIDTH-1 downto 0);
  signal dat_p_load     : std_logic;
  signal dat_p_idle     : std_logic;
  signal dat_s_edge     : std_logic;
 
  signal dat_r1         : std_logic;
  signal dat_r2         : std_logic;
 
  type FSM_STATE_TYPE is (INIT, PHASE_TRACK);
  signal fsm_state      : FSM_STATE_TYPE;
 
    -- Common output signals
  signal idle           : std_logic;
  signal clk_l          : std_logic;
  signal dat_l          : std_logic;
 
    -- Heuristic output signals
  signal dat_h          : std_logic;
  signal clk_h          : std_logic;
 
    -- Alexander output signals
  signal dat_a          : std_logic;
  signal clk_a          : std_logic;
 
    -- Miscellaneous Alexander signals
  signal clear_a        : std_logic;
  signal alex_w_i       : unsigned(ALEX_NCO_BITS-1 downto 0);
 
begin
 
 
  clk_p : entity work.bit_period_detector(beh)
    generic map(
      USE_ANY_EDGE   =>              0, -- 0=Rising edges only, 1=Use any edge
      WINDOW_SIZE    =>    WINDOW_SIZE,
      IDLE_CLOCKS    =>    IDLE_CLOCKS, -- sys_clk periods before input "idle" is called
      BAUD_READS     =>     BAUD_READS, -- Number of baud interval transition measurements tried
      INTERVAL_READS => INTERVAL_READS, -- Number of 2^N interval read attempts to make
      INTEGER_WIDTH  => PERIOD_I_WIDTH, -- Bits in integer part of period measurement
      FRACTION_WIDTH => PERIOD_F_WIDTH  -- Bits in fractional part of period measurement
    )
    port map(
      -- System Clock and Clock Enable
      sys_rst_n  => sys_rst_n,
      sys_clk    => sys_clk,
      sys_clk_en => sys_clk_en,
 
      -- Signal input
      signal_i   => clk_l,
 
      -- outputs
      s_edge_o   => open,
      period_o   => clk_period,
      load_o     => clk_p_load,
      idle_o     => clk_p_idle
    );
 
  dat_p : entity work.bit_period_detector(beh)
    generic map(
      USE_ANY_EDGE   =>              1,   -- 0=Rising edges only, 1=Use any edge
      WINDOW_SIZE    =>    WINDOW_SIZE,
      IDLE_CLOCKS    =>    IDLE_CLOCKS, -- sys_clk periods before input "idle" is called
      BAUD_READS     =>     BAUD_READS, -- Number of baud interval transition measurements tried
      INTERVAL_READS => INTERVAL_READS, -- Number of 2^N interval read attempts to make
      INTEGER_WIDTH  => PERIOD_I_WIDTH, -- Bits in integer part of period measurement
      FRACTION_WIDTH => PERIOD_F_WIDTH  -- Bits in fractional part of period measurement
    )
    port map(
      -- System Clock and Clock Enable
      sys_rst_n  => sys_rst_n,
      sys_clk    => sys_clk,
      sys_clk_en => sys_clk_en,
 
      -- Signal input
      signal_i   => dat_i,
 
      -- outputs
      s_edge_o   => dat_s_edge,
      period_o   => dat_period,
      load_o     => dat_p_load,
      idle_o     => dat_p_idle
    );
 
  -- Calculate the difference in period, used for frequency seeking
  -- and lock detection.
  delta_p <= signed(clk_period) - signed(dat_period);
 
  -- Calculate the next phase value, used in phase locking
  phase_next <= phase + freq_i when freq_seek_i='0' else
                phase + freq;
 
  -- Create a tweak bias "trigger" signal that indicates when the accumulated bias
  -- is sufficient to take action.  Currently the trigger is set to the middle of
  -- the range of possible tweak_bias values.
  tweak_bias_trigger <= '1' when (tweak_bias=(2**(tweak_bias'length-1))) else '0';
 
  process (sys_clk, sys_rst_n)
  begin
    if (sys_rst_n='0') then
      phase  <= to_unsigned(0,phase'length); -- Initial value
      fsm_state    <= INIT;
      locked       <= '0';
      phase_tweak  <= '0';
      tweak_bias   <= (others=>'0');
      dat_h        <= '0';
      dat_r1       <= '0';
      dat_r2       <= '0';
      freq         <= (others=>'0');
    elsif (sys_clk'event and sys_clk='1') then
      if (sys_clk_en='1') then
        -- default values
        phase_tweak <= '0';
 
        -- Delay the data going through
        dat_r1 <= dat_i;
        dat_r2 <= dat_r1;
        dat_h  <= dat_r2;
 
        -- Update phase register
        -- The phase value can be overwritten during phase tracking
        -- (see code below)
        if (cal_clk_en='1') then
          phase <= phase_next;
        end if;
 
        -- Finite State Machine
        case (fsm_state) is
 
          when INIT =>
            freq <= '0' & freq_i; -- Start at the requested frequency
            fsm_state <= PHASE_TRACK;
 
          when PHASE_TRACK =>
            if (clk_p_load='1' or dat_p_load='1') then
              if locked='0' and lock='1' then
                locked<='1';
              elsif locked='1' and unlock='1' then
                fsm_state <= INIT;
                locked<='0';
              end if;
            end if;
            if (locked='1' and (clk_p_idle='1' or dat_p_idle='1')) then
              locked    <= '0';
              fsm_state <= INIT;
            elsif (dat_s_edge='1') then
              -- Check to see if phase is outside the allowed window.
              -- If inside the window, no adjustment is needed.
              if (abs(signed(phase_next))>signed(freq)) then
 
                if (phase_next(phase_next'length-1)='1') then
                  tweak_bias <= tweak_bias+1;
                  if tweak_bias_trigger='1' then
                    if (freq_seek_i='1') then
                      freq <= freq + FSTEP_FINE;
                    end if;
                    tweak_bias<=(others=>'0'); -- Reset the bias integrator
                  end if;
                else
                  tweak_bias <= tweak_bias-1;
                  if tweak_bias_trigger='1' then
                    if (freq_seek_i='1') then
                      freq <= freq - FSTEP_FINE;
                    end if;
                    tweak_bias<=(others=>'0'); -- Reset the bias integrator
                  end if;
                end if;
                phase_tweak <= '1';
--                phase <= to_unsigned(0,phase'length); -- This adjustment works, but is rather "harsh"
                phase <= phase(phase'length-1) & phase(phase'length-1 downto 1); -- sign extended divide by 2.  This approach is "softer."
 
              end if;
            end if;
 
        end case;
 
      end if; -- sys_clk_en
    end if; -- sys_clk
  end process;
 
  idle   <= dat_p_idle;
  lock   <= '1' when abs(delta_p)<LOCK_VAL else '0';
  unlock <= '0' when abs(delta_p)<DROP_LOCK_VAL else '1';
  clk_h  <= not phase(PHASE_WIDTH-1);
 
 
  -- Instantiate the Alexander digital PLL
 
  dpll_1: entity work.dpll_alex_bw_adjust(beh)
  generic map(
    NCO_BITS    => ALEX_NCO_BITS,
    KP_I_BITS   => ALEX_K_BITS-ALEX_K_FRAC,
    KI_I_BITS   => ALEX_K_BITS-ALEX_K_FRAC,
    K_FRAC_BITS => ALEX_K_FRAC,
    GAIN_DIV    => ALEX_GAIN_ADJ
  )
  port map(
    sys_clk    => sys_clk,
    sys_rst_n  => sys_rst_n,
    sys_clk_en => cal_clk_en,
    clear_i    => clear_a,
    dat_i      => dat_i,
    w_i        => alex_w_i,
    kp_i       => alex_kp_i,
    ki_i       => alex_ki_i,
    bit_lock_i => locked_a, -- NOTE: ADD logical enable to this signal!!!
    dat_o      => dat_a,
    clk_o      => clk_a
    );
  alex_w_i <= to_unsigned(ALEX_W_SHIFT*to_integer(freq_i),alex_w_i'length);
 
  -- As it is currently constituted, this lock detector can not
  -- be used with the heuristic bit synchronizer, because its requirements
  -- for counting the clock valid to the data are based on times when the
  -- data is transitioning, and its requirements are too strict to work
  -- with the heuristic bit synchronizer outputs.
  fenton_lock_detector : entity work.bitsync_lock_detector(beh)
  generic map(
    BIT_CNT_MAX => 255,
    BIT_CNT_1   => 5,
    BIT_CNT_2   => 15
  )
  port map(
    sys_clk    => sys_clk,
    sys_rst_n  => sys_rst_n,
    sys_clk_en => cal_clk_en,
    dat_i      => dat_i,
    pll_clk    => clk_a,
    lock_o     => locked_a
  );
 
 
  -- Keep the DPLL cleared when not being used.
  -- The "use_alex_i" input can be pulsed low briefly to
  -- reset the Alexander PLL.
  clear_a <= not use_alex_i;
 
  -- Select the desired outputs
  idle_o <= idle;
  lock_o <= locked when use_alex_i='0' else locked_a;
  dat_l  <= dat_h  when use_alex_i='0' else dat_a;
  clk_l  <= clk_h  when use_alex_i='0' else clk_a;
  dat_o  <= dat_l;
  clk_o  <= clk_l;
 
end beh;
 
 
-------------------------------------------------------------------------------
-- PCM Analyzer - with 32 bit registers
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : May  17, 2013 Created this module by copying and modifying code
--                       from another module.
--         July 13, 2013 Revamped the register structure and added an instance
--                       of "pcm_input_front_end" to allow removal of line
--                       codes from incoming signals, prior to sending the
--                       NRZL data to a "Modally Allocated Temporal
--                       Correlation Histogram" (MATCH) unit for pattern
--                       detection and measurements of the period between
--                       pattern occurrences.
--
-- Description
-------------------------------------------------------------------------------
-- This module instantiates a "period_histogram_checker" and then adds registers
-- for control and readout of it.  In addition to the period_histogram_checker,
-- there is a "pcm_input_front_end" which can remove line codes, resulting in
-- NRZL data.  The NRZL data is then routed into a "Modally Allocated Temporal
-- Correlation Histogram" (M.A.T.C.H.) unit for detection of patterns and
-- measurement of the period between pattern occurrences.
--
-- The registers are summarized as follows:
--
-- Address      Structure   Function
-- -------      ---------   -----------------------------------------------------
--   0x0          (31:0)    pcm_input_front_end settings
--   0x1          (27:0)    PCM interval settings
--   0x2           (I:0)    Intervals to measure per histogram. I=HIST_BITS-1
--   0x3           (D:0)    Derandomizer taps. D=DERAND_BITS-1
--   0x4          (31:0)    M.A.T.C.H. interval and results (read only)
--   0x5          (31:0)    Pattern lower limit
--   0x6          (31:0)    Pattern upper limit
--   0x7          (31:0)    Bit synchronizer frequency
--   0x8           (1:0)    Binary Status bits
--   0x9           (H:0)    Histogram bin 1 result
--   0xA           (H:0)    Histogram bin 2 result
--   0xB           (H:0)    Histogram bin 3 result
--   0xC           (H:0)    Histogram bin N result
--   0xD           (H:0)    Histogram bin "oddball" result
--   0xE           (B:0)    Baud interval measurement. B=PERIOD_BITS-1.
--   0xF          (31:0)    Frequency measurement in Bauds per second.
--
--   Notes on Registers:
--
--   (0x0) pcm_input_front_end settings
--
--     Bits (31:16) contain the Baud interval expected for the incoming
--                  signal.  This is used only when decoding biphase.
--     Bit     (15) contains the setting for the derandomizer
--                  '0' means do not use derandomization
--                  '1' means use derandomization
--     Bit     (14) contains the setting for input data inversion.
--                  '0' means do not invert the data signal
--                  '1' means invert the data signal
--     Bit     (13) contains the setting for biphase line code removal
--                  Note that when this setting is used, the Baud interval
--                  setting is important.
--                  '0' means do not treat the data as biphase
--                  '1' means treat the data as biphase
--     Bit     (12) contains the setting for differential line code removal.
--                  The term "MARK" means that a change in level of the input
--                  data stream represents a '1', while no change represents
--                  a '0'.
--                  '0' means treat the data as SPACE encoded
--                  '1' means treat the data as MARK encoded
--     Bit     (11) contains the setting for LEVEL encoding
--                  '0' means treat the data as differentially encoded.
--                  '1' means treat the data as level encoded
--     Bit     (10) contains the setting for long search.  Set this bit
--                  to cause pattern timeouts after 2^INTRVL_BITS bits.
--                  When cleared, the pattern interval timeout is 16384 bits.
--     Bits   (9:4) contain the search pattern size
--     Bits   (3:0) contain the data input selection
--
--   (0x1) PCM Interval Settings
--
--     Bit 31 : Bad duty bit. (Read only)
--
--             This bit is set to indicate that the positive and negative Baud
--             intervals measured differ by more than the register 0x1
--             window setting.  In this case, it is assumed that the signal
--             has a duty cycle which is unacceptably far from the ideal of
--             50%, and measurements cannot continue.  However, while this
--             bit is set, Baud interval measurements continue.  If at any
--             time the result becomes favorable, then the bad duty bit
--             is cleared, and measurements proceed for a histogram.
--
--     Bit 30 : (Read only) 
--              Idle bit, set when the input has not transitioned for I sys_clk
--              periods, where:
--
--               I=1023*T
--
--               (where T is normally the average of the positive
--                and negative baud interval measurements, called "baud_avg")
--
--             Note that this means there are essentially two cases:
--               Case 1 : A signal is active, and then becomes idle.
--                        In this case the baud_avg value is already
--                        determined, and T is usually a small number of
--                        sys_clk periods.
--               Case 2 : The signal has never been active.
--                        In this case, the unit is trying to measure
--                        for the baud_avg value, but it cannot.  Therefore
--                        the default value of baud_avg is used.  Since the
--                        default is the highest possible value, being
--                        2^PERIOD_BITS-1 sys_clk periods, the resulting
--                        value of T can be quite large.
--                        For example, if PERIOD_BITS is 14, then we
--                        have I=1023*16383 = 16759809 sys_clk periods,
--                        which is approximately 336 milliseconds.
--
--     Bits  (27:16) Oddball interval limit
--
--     This field determines how many oddball intervals can occur during the
--     process of constructing the histogram, before the process is abandoned.
--     If too many intervals show up with oddball lengths, then the current histogram
--     counts are cleared and the unit reverts back to measuring the baud interval
--     in order to start the process over again.  When this happens, no results are
--     posted to the outputs.  This is an indicator of a noisy input signal.
--
--     Bits  (12:8) Bin N value
--
--     This field contains the N-value that is used when separating interval
--     measurements for storage into the histogram.  There are only four main
--     bins in this histogram:
--       Bin 1 : Counts intervals of 1 Baud
--       Bin 2 : Counts intervals of 2 Bauds
--       Bin 3 : Counts intervals of [3..(N-value)-1] Bauds, inclusive.
--       Bin N : Counts intervals of [N-value..1023] Bauds.
--
--     The Bin N catches any measurements that exceed N-value Bauds.  The longest
--     measured interval is currently limited to 2047 Bauds by the size of the
--     counter "i_count_b", which is not adjustable by generics.
--
--     Bits   (3:0) Interval uncertainty allowance window.
--
--     This field holds the setting for measurement uncertainty.  All interval
--     measurements are tallied to their appropriate histogram bin.  In the case
--     of intervals less than or equal to ODD_N_LIMIT Baud intervals, the interval
--     measurement must be within +/- W sys_clk periods of an integer multiple of
--     the Baud interval, or else it is considered "oddball" and must be tallied
--     in the bo_count result.  The measurement window is not used for intervals
--     longer than ODD_N_LIMIT Bauds because the measured Baud value is not
--     precise enough to be able to accurately determine whether a measurement
--     is "oddball" or not.  Therefore, all intervals greater than ODD_N_LIMIT
--     Bauds are considered valid measurements for the histogram.
--
--   (0x2) Intervals per histogram.
--
--     This number should be set to any desired value between one and 2^HIST_BITS-1
--     inclusive.  The number of intervals in the histogram can be adjusted by this
--     setting.
--
--   (0x3) pcm_input_front_end derandomizer taps
--
--     Bits (D:0)  where D=DERAND_BITS-1
--                 Contains the taps which are used in derandomization.
--                 For example, to remove +15RNRZL randomization, this
--                 register would be set to 0x00006000
--
--   (0x4) M.A.T.C.H. interval and results (read only)
--
--     M.A.T.C.H. = Modally Allocated Temporal Correlation Histogram
--                  This histogram is comprised of only three "bins",
--                  the center being the mode, and the flanking bins
--                  being the count of intervals measured less than
--                  or greater than the mode, respectively.
--
--     Bit  (31)    Match timeout.  It is set whenever an interval timed out.
--                  The timeout can be either 16384 bits (for minor frames) or
--                  2^21 bits (for major frames), depending on the
--                  "long_search" bit in register 0x0.
--                  Writes to register 0x0, 0x5 or 0x6 clear this bit.
--
--     Bits (15:12) contain the most recent measurement number.  Each time
--                  the measurement number increments, it implies that sixteen
--                  new intervals have been measured and tallyed into the
--                  histogram bins.  The reported mode can also change
--                  whenever the measurement number increments.  This field
--                  is useful for SW to determine if the current measurement
--                  is a newly reported one.
--
--     Bits (11:8)  Histogram bin for intervals less than the mode.
--
--     Bits  (7:4)  Histogram bin for intervals equaling the mode.
--
--     Bits  (3:0)  Histogram bin for intervals greater than the mode.
--
--   (0x5) Lower Limit register
--
--     The contents of this register are used when searching for patterns.
--     A mask of N ones (right justified) is generated and ANDed with this
--     value to arrive at a comparison lower threshold, which is compared
--     with the N least significant bits of the serial to parallel shift
--     register.
--
--   (0x6) Upper Limit register
--
--     The contents of this register are used when searching for patterns.
--     A mask of N ones (right justified) is generated and ANDed with this
--     value to arrive at a comparison upper threshold, which is compared
--     with the N least significant bits of the serial to parallel shift
--     register.
--
--   (0x7) bit_sync_digital frequency setting
--
--     This value is calibrated to be in units of Fsys_clk/(2^33) Hz, and it
--     can range up to 25 MHz with a 50 MHz system clock.  This bit 
--     synchronizer is not guaranteed to work well at the highest frequency
--     settings as it approaches the Nyquist limit, and DDS jitter noise
--     prevents correct timing.  Settings up to 8 MHz are expected to work
--     well.
--
--   (0x8) Pattern Timeout and Pattern Mode (Measured interval in bits)
--
--     Bits (23:0)  contain the most recently measured pattern occurrence
--                  interval in bits.  Since patterns can occur with
--                  different intervals, this field reports the most
--                  recent statistical mode of interval measurements,
--                  with the requirement that over 50% of the measurements
--                  must report the same interval before it is deemed to
--                  be the "new mode."
--
--   (0x9) Histogram bin 1
--   (0xA) Histogram bin 2
--   (0xB) Histogram bin 3
--   (0xC) Histogram bin N
--   (0xD) Histogram bin O
--
--     These registers contain the count of intervals which measured the designated
--     number of Bauds in length.  Bins 1 and 2 are for 1 and 2 Bauds, while bin 3
--     includes intervals of between [3 and N-1] Bauds.  Bin N contains the number
--     of intervals that equaled or exceeded N Bauds.  Bin O contains the number of
--     oddball length bins, which only include intervals of less than or equal to
--     ODD_N_LIMIT Bauds.
--
--   (0xE) Baud interval measurement
--
--     This register contains the measured average baud interval, in units of sys_clk
--     periods.
--
--   (0xF) Frequency measurement
--
--     This register contains the measured Baud frequency of the incoming signal.  This
--     measurement is in units of Bauds/second, and it takes an entire second for each
--     new measurement to be produced.
--
-- The sys_rst_n input is an asynchronous reset.
 
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
 
library work;
use work.function_pack.all;
use work.bit_sync_pack.all;
use work.signal_conditioning_pack.all;
 
  entity pcm_analyzer is
    generic(
      SYS_CLK_RATE  : real    := 50000000.0;
      NUM_CHAN      : natural := 8;  -- Number of channels to select from
      LOG2_NUM_CHAN : natural := 3;  -- Bits needed for channel selection
      ODD_N_LIMIT   : natural := 4;  -- Upper cutoff for bo_count_o intervals to be tallied
      PERIOD_BITS   : natural := 14; -- Number of bits in Baud interval measurement
      HIST_BITS     : natural := 12; -- Number of bits in histogram bin counters
      DEF_R_0       : unsigned(31 downto 0) := str2u("00042884",32); -- PCM front end settings
      DEF_R_1       : unsigned(31 downto 0) := str2u("00011006",32); -- PCM Interval settings
      DEF_R_2       : unsigned(31 downto 0) := str2u("00000400",32); -- Intervals per histogram
      DEF_R_3       : unsigned(31 downto 0) := str2u("00006000",32); -- Derandomizer taps
      DEF_R_5       : unsigned(31 downto 0) := str2u("00FAF300",32); -- lower threshold
      DEF_R_6       : unsigned(31 downto 0) := str2u("00FAF3FF",32); -- upper threshold
      DEF_R_7       : unsigned(31 downto 0) := str2u("33333333",32); -- bit synchronizer frequency
      DEF_R_Z       : unsigned(31 downto 0) := str2u("00000000",32)  -- Value returned for nonexistent reg.
    );
    port (
      -- System Clock and Clock Enable
      sys_rst_n  : in  std_logic;
      sys_clk    : in  std_logic;
      sys_clk_en : in  std_logic;
      fast_clk   : in  std_logic; -- A clock faster than sys_clk, for biphase line code removal
 
      -- Bus interface
      adr_i      : in  unsigned(3 downto 0);
      sel_i      : in  std_logic;
      we_i       : in  std_logic;
      dat_i      : in  unsigned(31 downto 0);
      dat_o      : out unsigned(31 downto 0);
      ack_o      : out std_logic;
 
      -- PCM signal inputs
      pcm_i      : in  unsigned(NUM_CHAN-1 downto 0);
 
      -- Indicator of new results
      new_o      : out std_logic
 
    );
end pcm_analyzer;
 
architecture beh of pcm_analyzer is
 
-- Constants
constant DAT_SIZE    : natural := 32;
constant DERAND_BITS : natural := 16;
constant BAUD_BITS   : natural := 14;
constant INTRVL_BITS : natural := 21;
constant CLK_TIMEOUT : natural := 65535;
 
-- Internal signal declarations
    -- PCM front end signals
  signal pcm_dr_taps       : unsigned(DERAND_BITS-1 downto 0);
  signal pcm_baud          : unsigned(BAUD_BITS-1 downto 0);
  signal pcm_baud_reg      : unsigned(15 downto 0);
  signal pcm_derandom      : std_logic;
  signal pcm_dat_inv       : std_logic;
  signal pcm_biphase       : std_logic;
  signal pcm_mark          : std_logic;
  signal pcm_level         : std_logic;
  signal pcm_dat_selection : unsigned(LOG2_NUM_CHAN-1 downto 0);
  signal pcm_dat_sel_reg   : unsigned(3 downto 0);
  signal pcm_selected_sig  : std_logic;
  signal nrzl_dat          : std_logic;
  signal nrzl_clk          : std_logic;
  signal nrzl_clk_edge     : std_logic;
    -- Bit synchronizer signals
  signal sync_dat          : std_logic;
  signal sync_clk          : std_logic;
  signal sync_freq         : unsigned(31 downto 0);
    -- "MATCH" signals
  signal reg_value_size    : unsigned(5 downto 0);
  signal reg_upper_limit   : unsigned(31 downto 0);
  signal reg_lower_limit   : unsigned(31 downto 0);
  signal value_mask        : unsigned(31 downto 0);
  signal value_match       : std_logic;
  signal match_measurement : unsigned(7 downto 0); -- 4 msbs reported
  signal match_sr          : unsigned(31 downto 0);
  signal match_icount      : unsigned(INTRVL_BITS-1 downto 0);
  signal match_mode_c_0    : unsigned(3 downto 0);
  signal match_mode_c_1    : unsigned(3 downto 0);
  signal match_bin_less    : unsigned(3 downto 0);
  signal match_bin_mode    : unsigned(3 downto 0);
  signal match_bin_more    : unsigned(3 downto 0);
  signal match_out_less    : unsigned(3 downto 0);
  signal match_out_mode    : unsigned(3 downto 0);
  signal match_out_more    : unsigned(3 downto 0);
  signal match_mode_reported     : unsigned(INTRVL_BITS-1 downto 0);
  signal match_mode_current      : unsigned(INTRVL_BITS-1 downto 0);
  signal match_mode_interval_0   : unsigned(INTRVL_BITS-1 downto 0);
  signal match_mode_interval_1   : unsigned(INTRVL_BITS-1 downto 0);
  signal match_timeout     : std_logic;
  signal match_tlimit      : unsigned(INTRVL_BITS-1 downto 0);
  signal long_search       : std_logic;
  signal clk_icount        : unsigned(bit_width(CLK_TIMEOUT)-1 downto 0);
 
    -- PCM signal analyzer signals
  signal reg_window        : unsigned(3 downto 0);
  signal reg_intrvls       : unsigned(HIST_BITS-1 downto 0);
  signal reg_bo_limit      : unsigned(HIST_BITS-1 downto 0);
  signal reg_n_value       : unsigned(4 downto 0);
  signal bad_duty          : std_logic;
  signal b1_count          : unsigned(HIST_BITS-1 downto 0);
  signal b2_count          : unsigned(HIST_BITS-1 downto 0);
  signal b3_count          : unsigned(HIST_BITS-1 downto 0);
  signal bn_count          : unsigned(HIST_BITS-1 downto 0);
  signal bo_count          : unsigned(HIST_BITS-1 downto 0);
  signal baud              : unsigned(PERIOD_BITS-1 downto 0);
  signal freq              : unsigned(31 downto 0);
  signal idle              : std_logic;
 
 
-----------------------------------------------------------------------------
begin
 
  -- Register read mux
  with (adr_i) select
  dat_o <=
    pcm_baud_reg & pcm_derandom & pcm_dat_inv & pcm_biphase & pcm_mark & pcm_level & long_search & reg_value_size & pcm_dat_sel_reg
                                         when "0000",
    bad_duty & idle & "00" & reg_bo_limit & "000" & reg_n_value & "0000" & reg_window
                                         when "0001",
    resize(reg_intrvls,DAT_SIZE)         when "0010",
    resize(pcm_dr_taps,DAT_SIZE)         when "0011",
    match_timeout & "000000000000000" & match_measurement(7 downto 4) & match_out_less & match_out_mode & match_out_more
                                         when "0100",
    reg_lower_limit                      when "0101",
    reg_upper_limit                      when "0110",
    sync_freq                            when "0111",
    resize(match_mode_reported,DAT_SIZE) when "1000",
    resize(b1_count,DAT_SIZE)            when "1001",
    resize(b2_count,DAT_SIZE)            when "1010",
    resize(b3_count,DAT_SIZE)            when "1011",
    resize(bn_count,DAT_SIZE)            when "1100",
    resize(bo_count,DAT_SIZE)            when "1101",
    resize(baud,DAT_SIZE)                when "1110",
    resize(freq,DAT_SIZE)                when "1111",
    DEF_R_Z when others;
 
  -- Create acknowledge signal
  ack_o <= sel_i;
 
  -- Handle bus writes to registers
  reg_proc: process(sys_clk, sys_rst_n)
  begin
    if (sys_rst_n='0') then
    elsif (sys_clk'event and sys_clk='1') then
      if (sys_clk_en='1') then
      end if;
    end if;
  end process;
 
  -- Resize selection fields appropriately
  pcm_dat_selection <= resize(pcm_dat_sel_reg,pcm_dat_selection'length);
  -- Resize baud interval bits appropriately
  pcm_baud <= resize(pcm_baud_reg,pcm_baud'length);
 
 
  pcm_ife : entity work.pcm_input_front_end(beh)
    generic map(
      NUM_CHAN       => NUM_CHAN,       -- Number of channels to select from
      LOG2_NUM_CHAN  => LOG2_NUM_CHAN,  -- Bits needed for channel selection
      BAUD_BITS      => BAUD_BITS,      -- Bits used in Baud interval counting
      DERAND_BITS    => DERAND_BITS
    )
    port map(
      -- System Clock and Clock Enable
      sys_rst_n  => sys_rst_n,
      sys_clk    => sys_clk,
      sys_clk_en => sys_clk_en,
      fast_clk   => fast_clk, -- A clock faster than sys_clk, for biphase line code removal
 
      -- PCM signal inputs
      sig_i      => pcm_i,
 
      -- Signal selection settings
      clk_sel_i  => (others=>'0'),
      dat_sel_i  => pcm_dat_selection,
 
      -- Line Code Settings
      dr_taps_i  => pcm_dr_taps,
      baud_i     => pcm_baud,
      derandom_i => pcm_derandom,
      clk_inv_i  => '0',
      dat_inv_i  => pcm_dat_inv,
      biphase_i  => pcm_biphase,
      mark_i     => pcm_mark,
      level_i    => pcm_level,
 
      -- Bit Synchronizer Interface Port
      -- If no synchronizer is present, simply tie use_sync_i to '0'
      use_sync_i => '1',
      pcm_dat_o  => pcm_selected_sig, -- feeds digital bit synchronizer
      sync_dat_i => sync_dat,
      sync_clk_i => sync_clk,
 
      -- signal outputs
      nrzl_dat_o => nrzl_dat,
      nrzl_clk_o => nrzl_clk
    );
 
  bit_sync : entity work.bit_sync_digital(beh)
    generic map(
      ALEX_K_BITS    =>    32, -- Number of bits in Alexander DPLL coefficients
      ALEX_K_FRAC    =>     8, -- How many of the ALEX_K_BITS are used for fractional representation
      ALEX_GAIN_ADJ  =>     2, -- How many bits to reduce KP_i and KI_i during lock
      FSTEP_FINE     =>  2**4, -- Frequency increment during phase track
      PHASE_WIDTH    => sync_freq'length+1, -- Bits in the phase accumulator
      LOCK_VAL       =>     4, -- Period mismatch to obtain lock
      DROP_LOCK_VAL  =>    12, -- Period mismatch to drop lock
      PERIOD_I_WIDTH =>    16, -- Integer width of period measurements
      PERIOD_F_WIDTH =>     2, -- Fractional width of period measurements
      IDLE_CLOCKS    => 2**16, -- sys_clk periods before signal is called idle
      BAUD_READS     =>   127, -- Period baud interval read attempts
      INTERVAL_READS =>    64, -- Period interval read attempts
      WINDOW_SIZE    =>     3  -- Period variation window, in sys_clks
    )
    port map(
      -- System Clock and Clock Enable
      sys_rst_n   => sys_rst_n,
      sys_clk     => sys_clk,
      sys_clk_en  => sys_clk_en,
 
      -- Frequency calibration clk enable
      cal_clk_en   => sys_clk_en,
 
      -- Settings
      freq_i       => sync_freq,
      freq_seek_i  => '0',
      use_alex_i   => '0',
      alex_kp_i    => "000000000001011111010101",
      alex_ki_i    => "000000000000000000000010",
 
      -- Reference data input
      dat_i        => pcm_selected_sig,
 
      -- indicators and outputs
      idle_o       => open,
      lock_o       => open,
      dat_o        => sync_dat,
      clk_o        => sync_clk
    );
 
  -- Detect rising edges of sync_clk
  nrzl_clk_edge_detector : entity work.edge_detector(beh)
    generic map(
      DETECT_RISING  => 1,
      DETECT_FALLING => 0
    )
    port map(
      -- System Clock and Clock Enable
      sys_rst_n   => sys_rst_n,
      sys_clk     => sys_clk,
      sys_clk_en  => sys_clk_en,
 
      -- Input Signal
      sig_i       => nrzl_clk,
 
      -- Output pulse
      pulse_o     => nrzl_clk_edge
    );
 
  -- Formulate an interval time limit based on the 
  -- "long_search" bit.
  match_tlimit <= to_unsigned((2**match_tlimit'length)-1,match_tlimit'length) when long_search='1' else
                  to_unsigned(16383,match_tlimit'length);
 
  --------------------------
  -- (Register writes are done here)
  -- M.A.T.C.H. logic
  matching_proc: Process(sys_rst_n,sys_clk)
  begin
    if (sys_rst_n = '0') then
      -- related to registers
      pcm_baud_reg    <= DEF_R_0(31 downto 16);
      pcm_derandom    <= DEF_R_0(15);
      pcm_dat_inv     <= DEF_R_0(14);
      pcm_biphase     <= DEF_R_0(13);
      pcm_mark        <= DEF_R_0(12);
      pcm_level       <= DEF_R_0(11);
      long_search     <= DEF_R_0(10);
      reg_value_size  <= DEF_R_0(9 downto 4);
      pcm_dat_sel_reg <= DEF_R_0(3 downto 0);
      reg_window      <= DEF_R_1(3 downto 0);
      reg_n_value     <= DEF_R_1(12 downto 8);
      reg_bo_limit    <= DEF_R_1(HIST_BITS-1+16 downto 16);
      reg_intrvls     <= DEF_R_2(HIST_BITS-1 downto 0);
      pcm_dr_taps     <= DEF_R_3(DERAND_BITS-1 downto 0);
      reg_lower_limit <= DEF_R_5;
      reg_upper_limit <= DEF_R_6;
      sync_freq       <= DEF_R_7;
      -- related to match unit
      match_measurement <= (others=>'0');
      match_sr          <= (others=>'0');
      match_icount      <= to_unsigned(1,match_icount'length);
      match_mode_c_0    <= (others=>'0');
      match_mode_c_1    <= (others=>'0');
      match_bin_less    <= (others=>'0');
      match_bin_mode    <= (others=>'0');
      match_bin_more    <= (others=>'0');
      match_out_less    <= (others=>'0');
      match_out_mode    <= (others=>'0');
      match_out_more    <= (others=>'0');
      match_mode_reported   <= (others=>'0');
      match_mode_current    <= (others=>'0');
      match_mode_interval_0 <= (others=>'0');
      match_mode_interval_1 <= (others=>'0');
      match_timeout     <= '0';
      clk_icount        <= (others=>'0');
    elsif (sys_clk'event AND sys_clk='1') then
      if (sys_clk_en='1') then
        if (nrzl_clk_edge='1') then
          clk_icount <= (others=>'0');
          match_sr <= match_sr(match_sr'length-2 downto 0) & nrzl_dat;
          match_icount <= match_icount+1;
          -- Check for a pattern match, or the maximum interval
          if (value_match='1' or match_icount=match_tlimit) then
            if (value_match='0') then
              match_timeout <= '1';
            end if;
            match_measurement <= match_measurement+1;
            match_icount <= to_unsigned(1,match_icount'length);
            -- Tally up the new result
            if (match_icount>match_mode_current) then
              match_bin_more <= match_bin_more+1;
            elsif (match_icount=match_mode_current) then
              match_bin_mode <= match_bin_mode+1;
            else
              match_bin_less <= match_bin_less+1;
            end if;
            -- Handle the "mode searching fingers"
            if (match_mode_interval_0=0) then
              match_mode_interval_0 <= match_icount;
              match_mode_c_0 <= match_mode_c_0+1;
            elsif (match_icount=match_mode_interval_0) then
              match_mode_c_0 <= match_mode_c_0+1;
            end if;
            if (match_mode_interval_1=0 and match_mode_interval_0/=0 and match_icount/=match_mode_interval_0) then
              match_mode_interval_1 <= match_icount;
              match_mode_c_1 <= match_mode_c_1+1;
            elsif (match_icount=match_mode_interval_1 and match_icount/=match_mode_interval_0) then
              match_mode_c_1 <= match_mode_c_1+1;
            end if;
            -- Provide output after every 16 counts
            if (match_measurement(3 downto 0)="1111") then
              match_out_more <= match_bin_more;
              match_out_mode <= match_bin_mode;
              match_out_less <= match_bin_less;
              match_bin_more <= (others=>'0');
              match_bin_mode <= (others=>'0');
              match_bin_less <= (others=>'0');
              -- Change to new mode if appropriate
              if (match_mode_c_0>7) then
                match_mode_current <= match_mode_interval_0;
                match_mode_reported <= match_mode_current;
              elsif (match_mode_c_1>7) then
                match_mode_current <= match_mode_interval_1;
                match_mode_reported <= match_mode_current;
              end if;
              match_mode_c_0 <= (others=>'0');
              match_mode_c_1 <= (others=>'0');
              match_mode_interval_0 <= (others=>'0');
              match_mode_interval_1 <= (others=>'0');
            end if;
          end if;
        else
          clk_icount <= clk_icount+1;
          if (clk_icount=CLK_TIMEOUT) then
            clk_icount <= (others=>'0');
            match_timeout <= '1';
            match_measurement <= match_measurement+1;
          end if;
        end if; -- nrzl_clk_edge
 
        -- Handle bus writes to registers
        if (sel_i='1' and we_i='1') then
          case (adr_i) is
            when "0000" =>
              pcm_baud_reg    <= dat_i(31 downto 16);
              pcm_derandom    <= dat_i(15);
              pcm_dat_inv     <= dat_i(14);
              pcm_biphase     <= dat_i(13);
              pcm_mark        <= dat_i(12);
              pcm_level       <= dat_i(11);
              long_search     <= dat_i(10);
              reg_value_size  <= dat_i(9 downto 4);
              pcm_dat_sel_reg <= dat_i(3 downto 0);
              match_timeout   <= '0';
            when "0001" =>
              reg_window  <= dat_i(3 downto 0);
              reg_n_value <= dat_i(12 downto 8);
              reg_bo_limit <= dat_i(27 downto 16);
            when "0010" =>
              reg_intrvls <= dat_i(HIST_BITS-1 downto 0);
            when "0011" =>
              pcm_dr_taps <= dat_i(pcm_dr_taps'length-1 downto 0);
            when "0101" =>
              reg_lower_limit <= dat_i;
              match_timeout   <= '0';
            when "0110" =>
              reg_upper_limit <= dat_i;
              match_timeout   <= '0';
            when "0111" =>
              sync_freq <= dat_i;
            when others => null;
          end case;
        end if;
 
      end if; -- sys_clk_en
    end if; -- sys_clk
  end process;
 
  --------------------------
  -- Check to see if the shift register value is a match
  value_mask <= Shift_Left(to_unsigned(1,value_mask'length),to_integer(reg_value_size))-1;
  value_match <= '1' when ((value_mask and match_sr)>=(value_mask and reg_lower_limit)) and ((value_mask and match_sr)<=(value_mask and reg_upper_limit)) else '0';
 
  ----------------------
  -- signal period histogram checker.
  phc_0 : entity work.period_histogram_checker(beh)
    generic map(
      SYS_CLK_RATE   => SYS_CLK_RATE,
      NUM_CHAN       => NUM_CHAN,
      LOG2_NUM_CHAN  => LOG2_NUM_CHAN,
      ODD_N_LIMIT    => ODD_N_LIMIT,
      PERIOD_BITS    => PERIOD_BITS,
      HIST_BITS      => HIST_BITS
    )
    port map(
      -- System Clock and Clock Enable
      sys_rst_n    => sys_rst_n,
      sys_clk      => sys_clk,
      sys_clk_en   => sys_clk_en,
 
      -- PCM signal inputs
      pcm_i      => pcm_i,
 
      -- Settings & Control
      pcm_sel_i  => pcm_dat_selection,
      window_i   => reg_window,
      intrvls_i  => reg_intrvls,
      bo_limit_i => reg_bo_limit,
      n_value_i  => reg_n_value,
 
      -- outputs
      bad_duty_o => bad_duty,
      b1_count_o => b1_count,
      b2_count_o => b2_count,
      b3_count_o => b3_count,
      bn_count_o => bn_count,
      bo_count_o => bo_count,
      baud_o     => baud,
      freq_o     => freq,
      idle_o     => idle,
      new_o      => new_o
    );
 
 
end beh;
 
 
---------------------------------------------------------------------------------
-- Digital Phase Locked Loop (With Alexander "Bang-Bang" Phase Detector)
-------------------------------------------------------------------------------
--
-- Author: John Clayton
-- Date  : Mar. 26, 2012 Having already tested the auto-generated code version
--                       of this module, and finding it satisfactory, I am
--                       recoding the module by hand, in order to streamline
--                       and parameterize it.
--         Mar. 28, 2012 Adding synchronization flip flop chain, also delaying
--                       the clock to line it up with the data output.
--                 
--
-- Description
-------------------------------------------------------------------------------
-- This module includes an NCO (squarewave DDS), a lowpass filter and
-- an Alexander "bang-bang" type phase detector configured as a
-- digital PLL.
--
-- The original design was set up, coded and simulated by
-- Jacob Fenton.
--
-- -------------------------------------------------------------
-- Functional Description:
-- -------------------------------------------------------------
-- The following constants, taken from Jacob Fenton's testbench,
-- serve to illustrate how to adjust the settings:
--
--   constant NCO_BITS    : integer := 32;
--   constant P_BITS      : integer := 32;
--   constant I_BITS      : integer := 32;
--   constant sysclk      : real := 50.0E+6;--fpga sys_clk rate
--   constant baud        : real := 3.0E+6;--expected data rate
--   constant dmp_fctr    : real := 0.7071;
--   constant pi : real   := 3.14159;
--   constant bw : real   := (0.005*baud);--desired dpll bandwidth as percentage of baud rate
--   constant ko : real   := (pi*sysclk)/("**"(2,real(NCO_BITS-1)));--nco gain (rad/sec)
--   constant kd : real   := ((sysclk/baud)*2.0)/pi;--phase detector gain (1/rad)
--   constant kp : integer:= integer((dmp_fctr*2.0*2.0*pi*bw)/(ko*kd));
--   constant ki : integer:= integer(("**"(2.0*pi*bw,2))/(sysclk*ko*kd));--need to make sure that with settings this value > 0, at some point use fixed point numbers
--   constant w  : integer:= integer(2.0*("**"(2,real(NCO_BITS-1)))/(sysclk/baud));
--
-- -------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
 
entity dpll_alex is
  generic (
    NCO_BITS   : integer := 32;
    P_BITS     : integer := 26;
    I_BITS     : integer := 12
  );
  port(
    sys_clk    : in  std_logic;
    sys_rst_n  : in  std_logic;
    sys_clk_en : in  std_logic;
    clear_i    : in  std_logic;
    dat_i      : in  std_logic;
    w_i        : in  unsigned(NCO_BITS-1 downto 0);
    kp_i       : in  unsigned(P_BITS-1 downto 0);
    ki_i       : in  unsigned(I_BITS-1 downto 0);
    dat_o      : out std_logic;
    clk_o      : out std_logic
    );
end dpll_alex;
 
 
architecture beh of dpll_alex is
 
  signal lpf_o    : unsigned(P_BITS-1 downto 0);
  signal int_acum : unsigned(P_BITS-1 downto 0);
  signal nco_acum : unsigned(NCO_BITS-1 downto 0);
  signal up       : std_logic;
  signal dn       : std_logic;
  signal reg1     : std_logic;
  signal reg2     : std_logic;
  signal reg3     : std_logic;
  signal reg4     : std_logic;
  signal nco_clk  : std_logic;
 
begin
 
  clk_o <= nco_clk;
  dat_o <= reg4;
 
  nco : process(sys_clk, sys_rst_n)
  begin
    if (sys_rst_n = '0') then
      nco_acum <= w_i;
      nco_clk <= '0';
    elsif rising_edge(sys_clk) then
      if (sys_clk_en='1') then
        if lpf_o(P_BITS-1) = '0' then --check sign of lpf_o
          nco_acum <= nco_acum + w_i + lpf_o;
        else
          nco_acum <= nco_acum + w_i - not(lpf_o) + 1;
        end if;
        nco_clk <= nco_acum(NCO_BITS-1);
      end if; -- sys_clk_en
    end if; -- sys_clk
  end process nco;
 
  lpf : process(sys_clk, sys_rst_n)
  begin
    if (sys_rst_n = '0') then
      lpf_o <= (others=>'0');
      int_acum <= (others=> '0');
    elsif rising_edge(sys_clk) then
      if (sys_clk_en='1') then
        if (up = '1' and dn = '0') then --indicates need to speed up
          lpf_o <= int_acum + kp_i;
          int_acum <= int_acum + ki_i;
        elsif (up = '0' and dn = '1') then --indicates need to slow down
          lpf_o <= int_acum - kp_i; 
          int_acum <= int_acum - ki_i;
        end if;
      end if; -- sys_clk_en
    end if; -- sys_clk
  end process lpf;
 
  up <= reg4 xor reg1;
  dn <= reg4 xor reg2;
 
  alex_pfd : process(nco_clk, sys_rst_n)
  begin
    if (sys_rst_n = '0') then
      reg1 <= '0';
      reg2 <= '0';
      reg3 <= '0';
      reg4 <= '0';
    elsif rising_edge(nco_clk) then
      reg1 <= dat_i;
      reg2 <= reg1;
      reg4 <= reg3;
    elsif falling_edge(nco_clk) then
      reg3 <= dat_i;
    end if;
  end process alex_pfd;
 
end beh;
 
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.