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;