URL
https://opencores.org/ocsvn/irig_regenerator/irig_regenerator/trunk
Subversion Repositories irig_regenerator
[/] [irig_regenerator/] [trunk/] [rtl/] [irig_time_pack.vhd] - Rev 2
Compare with Previous | Blame | View Log
-------------------------------------------------------------------------- -- Package of IRIG time pattern generator components -- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; package irig_time_pack is -- This component includes the capability to output both DC Level and modulated timecode streams. -- It has been coded with 32 bit wide registers component irig_time_generator generic ( SYS_CLK_RATE : real; -- Needed for carrier generation DEF_R_0 : unsigned(31 downto 0); -- Years, Days DEF_R_1 : unsigned(31 downto 0); -- Hours, Minutes, Seconds DEF_R_2 : unsigned(31 downto 0); -- Control(31:0) DEF_R_3 : unsigned(31 downto 0); -- Control(44:32) DEF_R_4 : unsigned(31 downto 0); -- Rate Setting DEF_R_5 : unsigned(31 downto 0); -- Fields Used [ctrl,sbs,year] DEF_R_6 : unsigned(31 downto 0); -- Carrier Frequency DEF_R_Z : unsigned(31 downto 0) -- Value returned for nonexistent registers ); port ( sys_rst_n : in std_logic; sys_clk : in std_logic; sys_clk_en : in std_logic; -- 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; -- Pulse per second time reference pps_i : in std_logic; pps_ext_i : in std_logic; -- IRIG select inputs -- Active only when register 0x4 set to zero. rate_i : in unsigned(2 downto 0); -- Rates : 0,1=>B, 2=>A, 3=>G, 4=>NASA 36-bit carrier_i : in unsigned(2 downto 0); -- Carriers : 0=DCLS, 1=100Hz, 2=1kHz, 3=10kHz, 4=100kHz codes_i : in unsigned(2 downto 0); -- Codes : (2)=> control, (1)=> SBS, (0)=> year -- Serial IRIG time stream, LVTTL level output time_dcl_o : out std_logic; cap_gnd_o : out unsigned(2 downto 0); ref_o : out std_logic; carrier_o : out unsigned(1 downto 0) ); end component; -- This component includes the capability to receive DC Level timecode streams. -- External circuitry should low-pass filter the timecode signal to remove the -- carrier frequency, and pass the envelope signal directly -- to this module, where it is treated as a digital input. -- Although it is a receiver, it also acts as a "synchronized transmitter." component irig_time_regenerator generic ( SYS_CLK_RATE : real; LOCK_THRESHOLD : integer; -- Packets received before entering locked state DEF_R_0 : unsigned(31 downto 0); -- Years, Days DEF_R_1 : unsigned(31 downto 0); -- Hours, Minutes, Seconds DEF_R_2 : unsigned(31 downto 0); -- Control(31:0) DEF_R_3 : unsigned(31 downto 0); -- Control(44:32) DEF_R_4 : unsigned(31 downto 0); -- Rate Setting DEF_R_5 : unsigned(31 downto 0); -- Fields Used [ctrl,sbs,year] DEF_R_6 : unsigned(31 downto 0); -- Carrier Frequency DEF_R_A : unsigned(31 downto 0); -- Initial value of carrier detect bias DEF_R_Z : unsigned(31 downto 0) -- Value returned for nonexistent registers ); port ( sys_rst_n : in std_logic; sys_clk : in std_logic; sys_clk_en : in std_logic; -- 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; -- IRIG select inputs -- Active only when register 0x4 set to zero. rate_i : in unsigned(2 downto 0); -- Rates : 0,1=>B, 2=>A, 3=>G, 4=>NASA 36-bit carrier_i : in unsigned(2 downto 0); -- Carriers : 0=DCLS, 1=100Hz, 2=1kHz, 3=10kHz, 4=100kHz codes_i : in unsigned(2 downto 0); -- Codes : (2)=> control, (1)=> SBS, (0)=> year -- IRIG time output, parallel form time_o : out unsigned(67 downto 0); -- Carrier Detect Select output, PPS input, -- IRIG time input, tracking and lock indicators cdet_sel_o : out std_logic; pps_i : in std_logic; time_cdet_i : in std_logic; cdet_val_o : out unsigned(1 downto 0); active_o : out std_logic; tracking_o : out std_logic; lock_o : out std_logic; neverlocked_o : out std_logic; -- Serial IRIG time stream output, LVTTL level time_dcl_o : out std_logic; cap_gnd_o : out std_logic; ref_o : out std_logic; carrier_o : out unsigned(1 downto 0) ); end component; end irig_time_pack; package body irig_time_pack is end irig_time_pack; ------------------------------------------------------------------------------- -- IRIG time pattern generator core ------------------------------------------------------------------------------- -- -- Author: John Clayton -- Date : Oct. 14, 2010 Copied code from irig_time, changed registers to -- a 32-bit format. -- Nov. 24, 2010 Added DEF_R_Z generic parameter -- Mar. 28, 2012 Moved clk_en_1mhz logic inside this module, updated -- description. Added "pps_ext_i" input. Changed name -- to "irig_time_generator" -- Apr. 4, 2012 Swapped R0 and R1, so that years & days are first. -- This arrangement will be more congruent with the -- IRIG time receiver. -- Apr. 5, 2012 Added support for NASA 36-bit (WWV) format, by -- extending the rate setting to include a new value, -- of 4. -- Apr. 30, 2013 Changed SYS_CLK_RATE from integer to real type. -- Dec. 2, 2013 Decreased SQF_BITS from 6 to 4, in order to increase -- the frequency of the square_fast signal by a factor -- of 4. -- Mar. 11, 2015 Added leap_year bit to register zero. Prevented -- day values higher than 366 from persisting. -- -- Description ------------------------------------------------------------------------------- -- This time pattern generator outputs DC Level data, plus a pair of digital outputs -- which contain a modulated carrier signal. The modulated carrier frequency is -- the low-frequency heterodyne signal generated by digital mixing of a 500 kHz -- squarewave with a 501 kHz (IRIG "B"), 510 kHz (IRIG "A") or 600 kHz (IRIG "G") -- squarewave, as generated by a DDS. As such, the digital output needs to be -- low-pass filtered in order to cut down the amplitude of the high-frequency -- heterodyne signal, plus the 3rd and higher order harmonics of the squarewaves -- used. In order to modulate the amplitude of the resulting low-pass filtered -- carrier over a 3:1 ratio (An AM modulation index of 0.5, meaning 50% modulation -- depth) a 2-bit R-2R resistor ladder network DAC is recommended for use outside -- the FPGA. Also, in order to keep the modulated signal balanced, a reference -- voltage other than ground is used. The reference voltage is nominally 1/2 of -- the FPGA VCCo supply voltage (3.3V * 0.5 = 1.65V). The FPGA generates this -- reference voltage through the use of an output which provides a 50% duty cycle -- squarewave. The schematic is as follows: -- -- R 1.65V R R -- ref_o o---/\/\--.--/\/\--/\/\--. -- | | -- Cref - + | -- 15uF - | -- | | -- GND | -- R R | -- carrier_o(0) o------------/\/\--/\/\--. -- (LSB) | -- R < -- < Cout -- R R | 4.7uF -- carrier_o(1) o------------/\/\--/\/\--.-----|(-------o AC coupled, Modulated Output -- (MSB) | -- Cfilt | -- 0.01uF | -- cap_gnd_o(0) o------------|(----------. -- | -- Cfilt | -- 0.01uF | -- cap_gnd_o(1) o------------|(----------. -- | -- Cfilt | -- 1300pF | -- cap_gnd_o(2) o------------|(----------. -- -- In practice, R=270 ohms has been successfully used. -- The Cfilt values are not exact, but the values shown worked well enough. -- -- The two carrier signals are identical, except that the LSB is constantly driving -- the heterodyne signal out, while the MSB is only driving the heterodyne signal -- during times when the irig DC level pulse is high. The cap_gnd_o outputs serves -- to adjust the corner frequency of the RC low pass filter. They are not pulsed -- with any high frequency signals. They simply apply ground or else high impedance, -- thereby effectively bringing the Cfilt capacitors in and out of the circuit. -- -- It is recommended to use the Cout capacitor in series with the output to provide -- AC coupling of the signal, and some DC isolation for the FPGA. -- -- This core uses a 1MHz clock enable pulse, to count the passage of time -- and output a pulse stream which conforms to IRIG standard 200-04, for the -- following IRIG formats: -- -- First letter: B (100 pps), A (1000 pps) or G (10000 pps) -- First digit : 0 = DC Level shift output (no modulation) -- Second digit : 0 = No carrier -- Third digit : [0..7] (BCD plus combinations of Year, CF and SBS fields) -- -- The core can generate its own 1 MHz clock enable by dividing the sys_clk -- by an integer divisor. If no integer divisor can be found, then then -- perhaps an NCO should be used to come arbitrarily close to the desired -- 1 MHz rate. No NCO is currently provided, so if you run into this situation -- you may have to buckle up, and start coding. -- -- Keeping the time accurate over the longer term is accomplished by using -- the pps_i input, a single pulse per second, one sys_clk in duration. -- The pps_i input may be supplied by an external GPS module for accurate time, -- although in the absence of GPS reference, the local system clock can be -- used to create a somewhat less accurate reference. An internal pps -- generator creates a pps signal by counting one million of the 1 MHz clock -- enable pulses, which is useful as a surrogate when there is no external pps -- reference. To use the external pps_i input, simply make "pps_ext_i" high. -- -- All settings for the core are contained in read/write registers, which -- have default values determined by generics. In the case of a "code" -- selection which includes straight binary seconds ("SBS"), the control -- bits for the P8 and P9 portions of the output are not used, and the -- internally calculated SBS field is used instead. -- -- The year field also displaces control bits when it is selected. The two -- digit year is fully supported, and will increment properly at the end of -- 23:59:59 on day 364. (No leap year). -- -- For rates A and G, there is a tenths of a second BCD digit present in the -- latter end of the P4 period. -- -- For rate G, there is a hundredths of a second BCD digit present in the -- former end of the P5 period. -- -- If the year is desired, it can be placed in the proper position within -- the control bits, as shown here... -- Format B or A : -- Year_1 (the most significant BCD digit) => -- control_1(0) & control_0(7 downto 5) -- control_0(4) remains '0' -- Year_0 (the least significant BCD digit) => -- control_0(3 downto 0) -- -- Format G : -- Year_1 (the most significant BCD digit) => -- control_2(1 downto 0) & control_1(7 downto 6) -- control_1(5) remains '0' -- Year_0 (the least significant BCD digit) => -- control_1(4 downto 1) -- -- Bus interface writes to the registers cannot be given higher priority -- than internal updates for timekeeping based on pps_i, since bus writes -- only affect one value, while timekeeping can occasionally alter all of -- them. Thus corrupt and undesired behavior could result if bus writes -- were to override the timekeeping update values. Therefore, the ack_o -- signal is used to ensure that the timekeeping is finished before bus -- writes take place. -- -- The registers are summarized as follows: -- -- Address Structure Function -- ------- ---------- ---------------------------------------- -- 0x0 [**YY*DDD] BCD Year, Day of Year (Day is 1..365 or 366) -- 0x1 [**HHMMSS] BCD Hours, Minutes, Seconds. (*=unused nibble) -- 0x2 [CCCCCCCC] Control Bits (31:0) -- 0x3 [**CCCCCC] Control Bits (44:32) -- 0x4 (2:0) Rate Setting (See Notes below) -- 0x5 (2:0) Codes Used [ctrl,SBS,year] -- 0x6 (2:0) Carrier Modulation Frequency -- -- Notes: -- -- Register 0x0 (Leap Year, Year, Day of Year) -- -- Bit 28 controls the leap year setting: -- This register currently has only a single bit for setting leap year -- behavior. When the leap year bit is set, the day field advances to 366 -- before rolling over back to 1. When the bit is clear, the day field -- to 1 after the 365 day value. Note that the DDD field can be written -- with values higher than 366, but it will immediately roll over. -- -- Register 0x4 (Rate) -- 000 => (Use external input for Rate/Form/Carrier/Codes)* -- 001 => IRIG-B (1 second updates) -- 010 => IRIG-A (0.1 second updates) -- 011 => IRIG-G (0.01 second updates) -- 100 => NASA 36-bit (WWV) time code -- -- The "rate_i" input is used along with the register setting, in such -- a way that the register Rate value being zero, causes the "rate_i" and -- "codes_i" inputs to be used. If, however, the register Rate is set to -- a non-zero value, then the external input is ignored. -- -- When external rate input is used, a setting of "000" results -- in IRIG-B (1 second updates.) All other settings remain -- as outlined above. -- -- Register 0x5 (Codes) -- Each bit position, when set, activates the use of a specific field -- within the output, according to the form: -- -- [ctrl,SBS,year] -- -- Where ctrl = Control field -- SBS = Straight Binary Seconds -- year = Year field -- -- If year and control are both selected, then the year is used in its -- appropriate spot, preventing the control bits there from being used. -- -- For Rate "G", the hundredths of a second field occludes the first four -- control bits, so that they are not used. The hundredths of a second field -- is encoded in the register area for "year" since year is not used in -- Rate "G" -- -- Register 0x6 (Carrier) -- 000 => No carrier (DC Level Shift) -- 001 => 100 Hz / 10 ms. resolution -- 010 => 1 kHz / 1 millisecond resolution -- 011 => 10 kHz / 100 microsecond resolution -- 100 => 100 kHz / 10 microsecond resolution -- -- Internal logic takes the day, hour, minute and second values and derives -- a value for the SBS field (Straight Binary Second). -- -- The generics used as defaults for the registers are the full width of the -- data bus. However, only those bits which are actually present in the actual -- registers are used. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use IEEE.MATH_REAL.ALL; library work; use work.convert_pack.all; entity irig_time_generator is generic ( SYS_CLK_RATE : real := 40000000.0; -- Needed for carrier generation DEF_R_0 : unsigned(31 downto 0) := str2u("00100123",32); DEF_R_1 : unsigned(31 downto 0) := str2u("00000000",32); DEF_R_2 : unsigned(31 downto 0) := str2u("00000000",32); DEF_R_3 : unsigned(31 downto 0) := str2u("00000000",32); DEF_R_4 : unsigned(31 downto 0) := str2u("00000001",32); DEF_R_5 : unsigned(31 downto 0) := str2u("00000007",32); DEF_R_6 : unsigned(31 downto 0) := str2u("00000002",32); DEF_R_Z : unsigned(31 downto 0) := str2u("00000000",32) -- Value returned for nonexistent registers ); port ( sys_rst_n : in std_logic; sys_clk : in std_logic; sys_clk_en : in std_logic; -- 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; -- Pulse per second time reference pps_i : in std_logic; pps_ext_i : in std_logic; -- IRIG select inputs -- Active only when register 0xC set to zero. rate_i : in unsigned(2 downto 0); -- Rates : 0,1=>B, 2=>A, 3=>G, 4=> NASA 36-bit carrier_i : in unsigned(2 downto 0); -- Carriers : 0=DCLS, 1=100Hz, 2=1kHz, 3=10kHz, 4=100kHz codes_i : in unsigned(2 downto 0); -- Codes : (2)=> control, (1)=> SBS, (0)=> year -- serial IRIG time stream, LVTTL level output time_dcl_o : out std_logic; cap_gnd_o : out unsigned(2 downto 0); ref_o : out std_logic; carrier_o : out unsigned(1 downto 0) ); end irig_time_generator; architecture beh of irig_time_generator is -- Constants constant DDS_WIDTH : integer := 32; constant INCREMENT_600KHZ : unsigned(DDS_WIDTH-1 downto 0) := to_unsigned(integer(2.0**DDS_WIDTH/SYS_CLK_RATE*600000.0),DDS_WIDTH); constant INCREMENT_510KHZ : unsigned(DDS_WIDTH-1 downto 0) := to_unsigned(integer(2.0**DDS_WIDTH/SYS_CLK_RATE*510000.0),DDS_WIDTH); constant INCREMENT_501KHZ : unsigned(DDS_WIDTH-1 downto 0) := to_unsigned(integer(2.0**DDS_WIDTH/SYS_CLK_RATE*501000.0),DDS_WIDTH); constant INCREMENT_500100HZ : unsigned(DDS_WIDTH-1 downto 0) := to_unsigned(integer(2.0**DDS_WIDTH/SYS_CLK_RATE*500100.0),DDS_WIDTH); constant DIVISOR_PPS : real := 1000000.0; constant DIVISOR_1MHZ : real := SYS_CLK_RATE/DIVISOR_PPS; constant DIV_COUNT_WIDTH : natural := bit_width(DIVISOR_1MHZ); constant PPS_COUNT_WIDTH : natural := bit_width(DIVISOR_PPS); constant SQF_BITS : natural := 4; -- square_fast frequency is Fsys_clk/2^(SQF_BITS+1) -- Internal signal declarations -- State Machine type FSM_STATE_TYPE is (RESET, IDLE, P0_REF, PR_REF, P0_DOUT, P1_PLUS_REF, P1_PLUS_DOUT); signal fsm_state : FSM_STATE_TYPE; signal fsm_ack : std_logic; -- Related to SBS calculation -- SBS Calculation State type SBS_STATE_TYPE is (RESET, IDLE, TIMEKEEPING_DELAY, SBS_10H, SBS_1H, SBS_10M, SBS_1M, SBS_10S); signal sbs_state : SBS_STATE_TYPE; -- Related to Registers signal leap_year : std_logic; signal secs_0 : unsigned(3 downto 0); signal secs_1 : unsigned(2 downto 0); signal mins_0 : unsigned(3 downto 0); signal mins_1 : unsigned(2 downto 0); signal hours_0 : unsigned(3 downto 0); signal hours_1 : unsigned(1 downto 0); signal days_0 : unsigned(3 downto 0); signal days_1 : unsigned(3 downto 0); signal days_2 : unsigned(1 downto 0); signal years_0 : unsigned(3 downto 0); signal years_1 : unsigned(3 downto 0); signal tenths : unsigned(3 downto 0); signal hundredths : unsigned(3 downto 0); signal micros : unsigned(13 downto 0); -- Microsecond count signal sbs : unsigned(16 downto 0); signal sbs_next : unsigned(16 downto 0); signal sbs_add_count : unsigned(3 downto 0); signal control : unsigned(44 downto 0); signal reg_rate : unsigned(2 downto 0); signal reg_carrier : unsigned(2 downto 0); signal reg_codes : unsigned(2 downto 0); signal irig_rate : unsigned(2 downto 0); signal irig_carrier : unsigned(2 downto 0); signal irig_codes : unsigned(2 downto 0); -- Related to pulse per second and 1MHz clock enable signal clk_en_1mhz : std_logic; signal clk_div_count : unsigned(DIV_COUNT_WIDTH-1 downto 0); signal pps : std_logic; signal pps_local : std_logic; signal pps_count : unsigned(PPS_COUNT_WIDTH-1 downto 0); -- Related to generation of the output pulse stream signal pulse_clk_count : unsigned(9 downto 0); -- Divides by factor of 10, 100 or 1000 signal pulse_clk_en : std_logic; signal pulse_sub_count : unsigned(3 downto 0); -- Advances when pulse_clk_en=1 signal sub_count_nine : std_logic; signal p_count : unsigned(3 downto 0); -- Advances when sub_count=9 and pulse_sr_count=9 signal cycle_count : unsigned(6 downto 0); -- Counts output cycles during the current second. signal pulse_sr : unsigned(8 downto 0); signal pulse_sr_count : unsigned(3 downto 0); -- Advances when sub_count=9 signal sr_load_bus : unsigned(8 downto 0); signal p0 : unsigned(8 downto 0); -- sr load value signal p1 : unsigned(8 downto 0); -- sr load value signal p2 : unsigned(8 downto 0); -- sr load value signal p3 : unsigned(8 downto 0); -- sr load value signal p4 : unsigned(8 downto 0); -- sr load value signal p5 : unsigned(8 downto 0); -- sr load value signal p6 : unsigned(8 downto 0); -- sr load value signal p7 : unsigned(8 downto 0); -- sr load value signal p8 : unsigned(8 downto 0); -- sr load value signal p9 : unsigned(8 downto 0); -- sr load value signal pulse_extent : unsigned(3 downto 0); signal time_pulses : std_logic; -- Related to generation of the sinewave carrier signal dds_sync : std_logic; signal sqf_count : unsigned(SQF_BITS-1 downto 0); signal square_fast : std_logic; signal square_500khz : std_logic; signal heterodyne : std_logic; signal modulated_carrier : std_logic; signal phase_dds : unsigned(DDS_WIDTH-1 downto 0); signal increment_dds : unsigned(DDS_WIDTH-1 downto 0); ----------------------------------------------------------------------------- begin -------------------------- -- Clock enable generation -- This creates output pulses at 1MHz rate, for use by the IRIG time generator -- It also creates a 1 Hz "pps" signal in lieu of such a signal from a GPS reference. clk_en_1mhz_proc: Process(sys_rst_n,sys_clk) begin if (sys_rst_n = '0') then clk_div_count <= (others=>'0'); -- pps_count <= (others=>'0'); pps_count <= to_unsigned(integer(DIVISOR_PPS-200.0),pps_count'length); -- For better simulation clk_en_1mhz <= '0'; pps_local <= '0'; elsif (sys_clk'event AND sys_clk='1') then if (sys_clk_en='1') then clk_en_1mhz <= '0'; -- Default pps_local <= '0'; -- Default if (clk_div_count=integer(DIVISOR_1MHZ-1.0)) then clk_div_count <= (others=>'0'); clk_en_1mhz <= '1'; if (pps_count=integer(DIVISOR_PPS-1.0)) then pps_count <= (others=>'0'); pps_local <= '1'; else pps_count <= pps_count+1; end if; else clk_div_count <= clk_div_count+1; end if; end if; -- sys_clk_en end if; -- sys_clk end process; -- Select which PPS signal to use pps <= pps_local when pps_ext_i='0' else pps_i; -- Select which settings to use irig_rate <= reg_rate when (reg_rate/=0) else rate_i; irig_carrier <= reg_carrier when (reg_rate/=0) else carrier_i; irig_codes <= reg_codes when (reg_rate/=0) else codes_i; -- Register read mux with (adr_i) select dat_o <= "000" & leap_year & "0000" & years_1 & years_0 & "000000" & days_2 & days_1 & days_0 when "0000", -- "0"; "0000000000" & hours_1 & hours_0 & "0" & mins_1 & mins_0 & "0" & secs_1 & secs_0 when "0001", -- "1"; control(31 downto 0) when "0010", -- "2"; "0000000000000000000" & control(44 downto 32) when "0011", -- "3"; u_resize(reg_rate,32) when "0100", -- "4"; u_resize(reg_carrier,32) when "0101", -- "5"; u_resize(reg_codes,32) when "0110", -- "6"; DEF_R_Z when others; -- Default -- Create acknowledge signal ack_o <= '1' when (sel_i='1' and we_i='0') else fsm_ack; -------------------------- -- Heterodyning Signals -- The idea here is to multiply or "mix" two "squarewave" sequences -- by using an XOR gate, to produce a sum and difference frequency. -- The sum frequency should be easily filtered out by an RC filter -- external to the FPGA. proc_square: Process(sys_rst_n,sys_clk) begin if (sys_rst_n = '0') then square_fast <= '0'; sqf_count <= (others=>'0'); elsif (sys_clk'event AND sys_clk='1') then if (sys_clk_en='1') then sqf_count <= sqf_count+1; if (sqf_count=0) then square_fast <= not square_fast; end if; end if; end if; end process; proc_square_500khz: Process(sys_rst_n,sys_clk) begin if (sys_rst_n = '0') then square_500khz <= '0'; elsif (sys_clk'event AND sys_clk='1') then if (sys_clk_en='1') then if (clk_en_1mhz='1') then square_500khz <= not square_500khz; end if; end if; end if; end process; proc_dds: Process(sys_rst_n,sys_clk) begin if (sys_rst_n = '0') then phase_dds <= (phase_dds'length-2=>'1',others=>'0'); -- Set to 1/4 phase point (sinewave "zero" output) elsif (sys_clk'event AND sys_clk='1') then if (sys_clk_en='1') then if (dds_sync='1') then phase_dds <= (phase_dds'length-2=>'1',others=>'0'); -- Set to 1/4 phase point (sinewave "zero" output) else phase_dds <= phase_dds + increment_dds; end if; end if; -- sys_clk_en end if; -- sys_clk end process; increment_dds <= INCREMENT_600KHZ when irig_carrier=4 else INCREMENT_510KHZ when irig_carrier=3 else INCREMENT_501KHZ when irig_carrier=2 else INCREMENT_500100HZ; heterodyne <= square_500khz xor phase_dds(31); -- A sort of digital signal multiplication modulated_carrier <= heterodyne when time_pulses='1' else square_fast; -- Output midpoint when pulse is low carrier_o(0) <= '0' when irig_carrier=0 else heterodyne; carrier_o(1) <= time_pulses when irig_carrier=0 else modulated_carrier; ref_o <= '0' when irig_carrier=0 else square_fast; -- Used as a "midpoint" voltage reference -------------------------- -- Setting for output filter capacitor. -- The idea here is to ground the particular capacitor needed to -- low pass filter the output. Three different outputs are provided. cap_gnd_o <= "ZZZ" when irig_carrier=0 or irig_carrier=1 else "Z00" when irig_carrier=2 else -- Two capacitors in parallel "Z0Z" when irig_carrier=3 else "0ZZ"; -- This process is where the different BCD values are compiled into -- a "straight binary seconds" (SBS) field. -- The calculations are done in a multiply accumulate fashion (MAC) -- wherein the multiply is implemented as a series of additions. -- An SBS accumulator stores the intermediate calculation total, -- while a state variable tracks the current stage of calculation, -- beginning at "tens of hours" and finishing at "seconds." -- All the adds performed here are unsigned... sbs_proc : process(sys_clk,sys_rst_n) begin if (sys_rst_n='0') then sbs_state <= RESET; sbs <= (others=>'0'); sbs_next <= (others=>'0'); sbs_add_count <= (others=>'0'); elsif (sys_clk'event and sys_clk='1') then if (sys_clk_en='1') then case (sbs_state) is when RESET => sbs_state <= IDLE; when IDLE => if (pps='1') then sbs_state <= TIMEKEEPING_DELAY; end if; -- This state waits for changes to the BCD time values to propagate, -- so they will be stable before this process begins calculation of -- the SBS field. when TIMEKEEPING_DELAY => sbs <= (others=>'0'); -- Clear accumulator sbs_next <= u_resize(hours_1,sbs'length); sbs_add_count <= "1010"; -- Multiply by 10 sbs_state <= SBS_10H; -- The following series of states performs multiply and accumulate -- calculations to generate the SBS value. when SBS_10H => if (sbs_add_count=0) then sbs <= (others=>'0'); sbs_next <= sbs + u_resize(hours_0,sbs'length); sbs_add_count <= "0110"; -- Multiply by 6 sbs_state <= SBS_1H; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when SBS_1H => if (sbs_add_count=0) then sbs <= (others=>'0'); sbs_next <= sbs + u_resize(mins_1,sbs'length); sbs_add_count <= "1010"; -- Multiply by 10 sbs_state <= SBS_10M; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when SBS_10M => if (sbs_add_count=0) then sbs <= (others=>'0'); sbs_next <= sbs + u_resize(mins_0,sbs'length); sbs_add_count <= "0110"; -- Multiply by 6 sbs_state <= SBS_1M; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when SBS_1M => if (sbs_add_count=0) then sbs <= (others=>'0'); sbs_next <= sbs + u_resize(secs_1,sbs'length); sbs_add_count <= "1010"; -- Multiply by 10 sbs_state <= SBS_10S; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when SBS_10S => if (sbs_add_count=0) then sbs <= sbs + u_resize(secs_0,sbs'length); -- Add seconds (no more multiplication.) sbs_state <= IDLE; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when others => null; end case; end if; -- sys_clk_en end if; -- sys_clk end process; -- sr load values p0 <= "00000" & secs_0 when (irig_rate=4) else "0" & secs_1 & "0" & secs_0; p1 <= "000000" & secs_1 when (irig_rate=4) else "0" & mins_1 & "0" & mins_0; p2 <= "00000" & mins_0 when (irig_rate=4) else "00" & hours_1 & "0" & hours_0; p3 <= "000000" & mins_1 when (irig_rate=4) else days_1 & "0" & days_0; p4 <= "00000" & hours_0 when (irig_rate=4) else "0000000" & days_2 when (irig_rate=0 or irig_rate=1) else tenths & "000" & days_2; p5 <= "0000000" & hours_1 when (irig_rate=4) else control(8 downto 4) & hundredths when (irig_rate=3) else years_1 & '0' & years_0 when (irig_codes(2)='1') else control(8 downto 0) when (irig_codes(0)='1') else "000000000"; p6 <= "00000" & days_0 when (irig_rate=4) else years_1 & '0' & years_0 when (irig_rate=3 and irig_codes(2)='1') else control(17 downto 9) when (irig_codes(0)='1') else "000000000"; p7 <= "00000" & days_1 when (irig_rate=4) else control(26 downto 18) when (irig_codes(0)='1') else "000000000"; p8 <= "0000000" & days_2 when (irig_rate=4) else sbs(8 downto 0) when (irig_codes(1)='1') else control(35 downto 27) when (irig_codes(0)='1') else "000000000"; p9 <= "111110000" when (irig_rate=4) else '0' & sbs(16 downto 9) when (irig_codes(1)='1') else control(44 downto 36) when (irig_codes(0)='1') else "000000000"; -- sr_load_bus mux with (p_count) select sr_load_bus <= p0 when "0000", -- "0"; p1 when "0001", -- "1"; p2 when "0010", -- "2"; p3 when "0011", -- "3"; p4 when "0100", -- "4"; p5 when "0101", -- "5"; p6 when "0110", -- "6"; p7 when "0111", -- "7"; p8 when "1000", -- "8"; p9 when "1001", -- "9"; "000000000" when others; -- Not needed for synthesis... -- Generate a clock enable for advancing the output carrier state -- The rate of this pulse is determined by the selected IRIG rate. -- The rate has been selected to allow 10 digital samples per cycle -- of carrier output. -- -- Selected Carrier carrier_clk rate Carrier Frequency -- ---------------- ---------------- ----------------- -- 000 DC Level -None- -- 001 1 kHz 100 Hz -- 010 10 kHz 1 kHz -- 011 100 kHz 10 kHz -- 100 1 MHz 100 kHz -- -- Generate a clock enable for advancing the output pulse state -- The rate of this pulse is determined by the selected IRIG rate. -- The rate has been selected to allow output pulse duty cycle -- to be set in increments of 1/10th the total pulse time. -- -- Selected Rate pulse_clk rate Pulse Frequency -- ------------- -------------- ----------------- -- 000 1 kHz 100 Hz -- 001 (B) 1 kHz 100 Hz -- 010 (A) 10 kHz 1 kHz -- 011 (G) 100 kHz 10 kHz -- 100 (NASA 36) 1 kHz 100 Hz pulse_clk_proc : process(sys_clk,sys_rst_n) begin if (sys_rst_n='0') then pulse_clk_count <= (others=>'0'); elsif (sys_clk'event and sys_clk='1') then if (sys_clk_en='1') then if (pulse_clk_en='1') then pulse_clk_count <= (others=>'0'); elsif (clk_en_1mhz='1') then pulse_clk_count <= pulse_clk_count + 1; end if; end if; -- sys_clk_en end if; -- sys_clk end process; pulse_clk_en <= '1' when (clk_en_1mhz='1' and (irig_rate=0 or irig_rate=1 or irig_rate=4) and pulse_clk_count=999) else '1' when (clk_en_1mhz='1' and irig_rate=2 and pulse_clk_count=99) else '1' when (clk_en_1mhz='1' and irig_rate=3 and pulse_clk_count=9) else '0'; -- This is the state machine. -- It handles updates to the timekeeping registers, based on the "pps" signal, -- and creates the output pulse stream. fsm_proc: process(sys_clk, sys_rst_n) begin if (sys_rst_n='0') then fsm_state <= RESET; -- asynchronous reset leap_year <= '0'; micros <= (others=>'0'); hundredths <= (others=>'0'); tenths <= (others=>'0'); years_1 <= DEF_R_0(23 downto 20); years_0 <= DEF_R_0(19 downto 16); days_2 <= DEF_R_0(9 downto 8); days_1 <= DEF_R_0(7 downto 4); days_0 <= DEF_R_0(3 downto 0); hours_1 <= DEF_R_1(21 downto 20); hours_0 <= DEF_R_1(19 downto 16); mins_1 <= DEF_R_1(14 downto 12); mins_0 <= DEF_R_1(11 downto 8); secs_1 <= DEF_R_1(6 downto 4); secs_0 <= DEF_R_1(3 downto 0); control <= DEF_R_3(12 downto 0) & DEF_R_2; reg_rate <= DEF_R_4(2 downto 0); reg_carrier <= DEF_R_5(2 downto 0); reg_codes <= DEF_R_6(2 downto 0); p_count <= (others=>'0'); pulse_sr <= (others=>'0'); pulse_sr_count <= to_unsigned(1,pulse_sr_count'length); -- To avoid sending out an initial stream of reference pulses while waiting for pps_local pulse_sub_count <= (others=>'0'); cycle_count <= (others=>'0'); fsm_ack <= '0'; dds_sync <= '0'; elsif (sys_clk'event and sys_clk='1') then if (sys_clk_en='1') then -- Default values fsm_ack <= '0'; dds_sync <= '0'; -- Handle timekeeping for fractions of seconds, based on 1MHz clock enable. -- These updates are overridden by pps, which resets all the time count values. if (clk_en_1mhz='1') then if (micros=9999) then micros <= (others=>'0'); if (hundredths=9) then tenths <= tenths+1; hundredths <= (others=>'0'); if (tenths=9) then -- No need to increment seconds here. pps does that. tenths <= (others=>'0'); else tenths <= tenths+1; end if; else hundredths <= hundredths+1; end if; else micros <= micros+1; end if; end if; -- Handle the timekeeping items, based on pps if (pps='1') then secs_0 <= secs_0+1; tenths <= (others=>'0'); hundredths <= (others=>'0'); micros <= (others=>'0'); if (secs_0=9) then secs_1 <= secs_1+1; secs_0 <= (others=>'0'); if (secs_1=5) then mins_0 <= mins_0+1; secs_1 <= (others=>'0'); if (mins_0=9) then mins_1 <= mins_1+1; mins_0 <= (others=>'0'); if (mins_1=5) then hours_0 <= hours_0+1; mins_1 <= (others=>'0'); if (hours_1=2 and hours_0=3) then days_0 <= days_0+1; hours_1 <= (others=>'0'); hours_0 <= (others=>'0'); if (days_2>=3 and days_1>=6 and ((days_0>=5 and leap_year='0') or (days_0>=6 and leap_year='1'))) then years_0 <= years_0+1; days_0 <= "0001"; days_1 <= (others=>'0'); days_2 <= (others=>'0'); if (years_0=9) then years_1 <= years_1+1; years_0 <= (others=>'0'); if (years_1=9) then years_1 <= (others=>'0'); end if; end if; elsif (days_0=9) then days_1 <= days_1+1; days_0 <= (others=>'0'); if (days_1=9) then days_2 <= days_2+1; days_1 <= (others=>'0'); end if; end if; elsif (hours_0=9) then hours_1 <= hours_1+1; hours_0 <= (others=>'0'); end if; end if; end if; end if; end if; elsif (sel_i='1' and we_i='1') then -- Handle bus writes to registers -- Cannot do this during pps, since this only writes one value, while -- pps timekeeping can potentially affect all the values. Therefore, -- generate an acknowledge signal for the register writes... so that -- incoming bus cycles are extended when they collide with pps... fsm_ack <= '1'; case (adr_i) is when "0000" => leap_year <= dat_i(28); years_1 <= dat_i(23 downto 20); years_0 <= dat_i(19 downto 16); days_2 <= dat_i(9 downto 8); days_1 <= dat_i(7 downto 4); days_0 <= dat_i(3 downto 0); when "0001" => hours_1 <= dat_i(21 downto 20); hours_0 <= dat_i(19 downto 16); mins_1 <= dat_i(14 downto 12); mins_0 <= dat_i(11 downto 8); secs_1 <= dat_i(6 downto 4); secs_0 <= dat_i(3 downto 0); when "0010" => control(31 downto 0) <= dat_i; when "0011" => control(44 downto 32) <= dat_i(12 downto 0); when "0100" => reg_rate <= dat_i(2 downto 0); when "0101" => reg_carrier <= dat_i(2 downto 0); when "0110" => reg_codes <= dat_i(2 downto 0); when others => null; end case; end if; -- Handle pulse_sub_count if (pulse_clk_en='1') then if (sub_count_nine='1') then pulse_sub_count <= (others=>'0'); dds_sync <= '1'; -- Set the carrier wave phase. else pulse_sub_count <= pulse_sub_count + 1; end if; end if; -- Handle state transitions case (fsm_state) is when RESET => fsm_state <= IDLE; when IDLE => if (pps='1') then fsm_state <= P0_REF; p_count <= (others=>'0'); cycle_count <= (others=>'0'); pulse_sr_count <= (others=>'0'); end if; when P0_REF => if (pulse_clk_en = '1' and sub_count_nine='1') then pulse_sr_count <= pulse_sr_count+1; pulse_sr <= sr_load_bus; if (irig_rate=4) then fsm_state <= P0_DOUT; -- No special reference pulse for IRIG 36-bit else fsm_state <= PR_REF; end if; end if; when PR_REF => if (pulse_clk_en = '1' and sub_count_nine='1') then pulse_sr_count <= pulse_sr_count+1; fsm_state <= P0_DOUT; end if; when P0_DOUT => if (pulse_clk_en = '1' and sub_count_nine='1') then if (pulse_sr_count=9) then pulse_sr_count <= (others=>'0'); p_count <= p_count+1; fsm_state <= P1_PLUS_REF; else pulse_sr <= '0' & pulse_sr(8 downto 1); pulse_sr_count <= pulse_sr_count+1; end if; end if; when P1_PLUS_REF => if (pulse_clk_en = '1' and sub_count_nine='1') then pulse_sr_count <= pulse_sr_count+1; pulse_sr <= sr_load_bus; fsm_state <= P1_PLUS_DOUT; end if; when P1_PLUS_DOUT => if (p_count=9 and pps='1') then -- pps should not occur before p_count=9 anyway... fsm_state <= P0_REF; p_count <= (others=>'0'); cycle_count <= (others=>'0'); pulse_sr_count <= (others=>'0'); elsif (pulse_clk_en = '1' and sub_count_nine='1') then if (pulse_sr_count=9) then if (p_count=9) then if (cycle_count=99 and irig_rate=3) then fsm_state <= IDLE; elsif (cycle_count=9 and irig_rate=2) then fsm_state <= IDLE; elsif (irig_rate=0 or irig_rate=1 or irig_rate=4) then fsm_state <= IDLE; else cycle_count <= cycle_count+1; p_count <= (others=>'0'); pulse_sr_count <= (others=>'0'); fsm_state <= P0_REF; end if; else pulse_sr_count <= (others=>'0'); p_count <= p_count+1; pulse_sr <= sr_load_bus; fsm_state <= P1_PLUS_REF; end if; else pulse_sr <= '0' & pulse_sr(8 downto 1); pulse_sr_count <= pulse_sr_count+1; end if; end if; --when others => -- fsm_state <= IDLE; end case; end if; -- sys_clk_en end if; -- sys_clk end process; sub_count_nine <= '1' when (pulse_sub_count=9) else '0'; -- Form output pulses pulse_extent <= "0010" when (irig_rate=4 and pulse_sr_count=0 and p_count=0) else "0101" when (irig_rate=4 and pulse_sr_count=0) else "1000" when (pulse_sr_count=0 or fsm_state=PR_REF) else "0101" when (pulse_sr(0)='1') else "0010"; time_pulses <= '1' when (pulse_sub_count<pulse_extent) else '0'; time_dcl_o <= time_pulses; end beh; ------------------------------------------------------------------------------- -- IRIG time pattern receiver-generator core ------------------------------------------------------------------------------- -- -- Author: John Clayton -- Date : Apr. 4, 2012 Copied code from irig_time_generator. Updated -- description. -- Oct. 12, 2012 Added read only status registers, that allow -- reporting of the lock condition, as well as the -- "slow_err" and "fast_err" signals. These were -- not being used, and were being optimized away -- ("pruned") at synthesis time. -- Apr. 30, 2013 Changed SYS_CLK_RATE from integer to real type. -- June 12, 2013 Changed low pass filter circuit topology, reducing -- the number of "cap_gnd_o" lines. Increased the -- heterodyne input frequency from 500kHz to 2MHz. -- Oct. 10, 2013 Added the cdet_sel_o line to activate carrier -- detect circuitry by register control. Added leaky -- integrator based lowpass filter from -- "signal_conditioning_pack.vhd" -- Oct. 14, 2013 Cleaned up the design, changing the heterodyne -- from 2MHz to 1MHz, eliminating "square_fast" and -- using the 1MHz squarewave instead. Eliminated -- rx_phase and rx_dds_r1 by using "dds_squarewave" -- module. Changed rx_phase_incr -> rx_freq, -- rx_dds_rising -> rx_1mhz_pulse. Eliminated -- "phase_dds" and "increment_dds" by using -- dds_squarewave_phase_load module. -- Nov. 6, 2013 Divided receive and generate logic into separate -- processes. removed rx_lock_state state machine. -- Eliminated rx_frame_count. Added rx_c_count, -- gave priority to external PPS tracking over -- carrier count PPS (IRIG) tracking. Added -- hdyne_base_square so that modulation frequency -- is not "warped" or affected by the tracking -- mechanism. Added "set_time" signal. Removed -- rx_fast_err and rx_slow_err. Removed rx_speed_sense. -- Dec. 6, 2013 Moved timecode gated pulse stretcher to a higher level, -- and introduced time_cdet_i. Now, "time_dcl_i" really -- will be a DC-level timecode input, and "time_cdet_i" -- is a squarewave at the carrier rate, during times -- when cdet_sel_o is high. Removed the -- "rx_pulse_stretcher" register bit. -- Dec. 9, 2013 Removed the time_dcl_i input, in favor of using the -- time_cdet_i input always. Added reg_time_lp lowpass -- filter enable bit. -- Dec. 10, 2013 Revised the data detection logic to use "rx_lo_count" -- and tested in hardware. All seems to be working! -- O, happy day! -- Dec. 11, 2013 Added rx_active_count to separate rx activity detect -- from the data receive counter "rx_lo_count." This -- allows activity detect to be on edges, while the -- data receive is on levels, thus allowing reception of -- true dc-level timecode input data. -- Nov. 14, 2014 Reduced cdet_val from 4 bits down to 2 bits, and -- provided it as an output signal for inclusion in -- a status stream. -- Mar. 11, 2015 Added leap_year bit to register zero. Prevented -- day values higher than 366 from persisting. -- -- Description ------------------------------------------------------------------------------- -- This time pattern receiver is also a time pattern generator. -- More correctly, it is a time pattern generator which synchronizes itself to -- an incoming IRIG time signal. Also, it provides parallel output of the -- current time so that timestamps may be taken when telemetered data is being -- sent to an archive. -- -- Note that during playback, "one time" syncronization is used. The IRIG -- time is set once, at the start of the playback, to match the first minor -- frame timestamp. Thereafter, the freewheeling quartz crystal based -- IRIG time is sent out along with the playback data. This is necessary -- because there is no feasible way to simultaneously adjust the IRIG time -- and the data playback rate so as to keep them both matched to some outside -- time reference, without introducing discontinuities in the IRIG time -- signal. -- -- The incoming IRIG time signal is applied to a pulse period meter, which is -- built using a binary counter. As the period is measured, decisions are -- made about what type of bit is being encountered. A period close to 20 -- counts is considered a zero, close to 50 counts, a one, and close to 80 -- counts, a "reference" pulse. The counter is clocked by a pre-scaler so -- that it can measure the period in units of 100, 10 or 1 microsecond, for -- IRIG types B, A and G, respectively. For rapid acquisition of all types, -- the default is to use units of 1 microsecond (type G.) If the count -- exceeds 85, then the prescaler is adjusted to provide units of 10 -- microseconds (type A). If, once again, the count exceeds 85, then the -- prescaler is adjusted to use units of 100 microseconds (type B.) Since -- type B is the slowest supported type, if the count once again exceeds 85 -- counts, then a "slow" error condition is flagged. -- -- Similar converse adjustments are made for period measurements less than -- 10 counts. If pulses of under 10 microseconds are found, then a "fast" -- error condition is flagged. -- -- This unit is set up to receive DC Level data, and no attempt is made to -- use the modulating carrier for timing reference information. -- -- The unit outputs DC Level data, plus a pair of digital outputs which -- contain a modulated carrier signal. The modulated carrier frequency is the -- low-frequency heterodyne signal generated by digital mixing of a 1 MHz -- squarewave with a 1001 kHz (IRIG "B"), 1010 kHz (IRIG "A") or 1600 kHz -- (IRIG "G") squarewave, as generated by a DDS. As such, the digital output -- needs to be low-pass filtered in order to cut down the amplitude of the -- high-frequency components. -- -- In order to modulate the amplitude of the resulting low-pass filtered -- carrier over a 3:1 ratio (An AM modulation index of 0.5, meaning 50% modulation -- depth) a 2-bit R-2R resistor ladder network DAC is recommended for use outside -- the FPGA. Also, in order to keep the modulated signal balanced, a reference -- voltage other than ground is used. The reference voltage is nominally 1/2 of -- the FPGA VCCo supply voltage (3.3V * 0.5 = 1.65V). The FPGA generates this -- reference voltage through the use of an output which provides a 50% duty cycle -- squarewave. The schematic is as follows: -- -- R 1.65V R R -- ref_o o---/\/\--.--/\/\--/\/\--. -- | | -- Cref - + | -- 15uF - | -- | | -- GND | -- R R | -- carrier_o(0) o------------/\/\--/\/\--. -- (LSB) | -- R < -- < Cout -- R R | 4.7uF -- carrier_o(1) o------------/\/\--/\/\--.-----|(-------o AC coupled, Modulated Output -- (MSB) | -- Cfilt | -- 0.1uF | -- cap_gnd_o o------.-----|(----------. -- | -- .-------------. -- | -- Cfilt | -- 0.01uF | -- GND o------------|(------. -- -- In practice, R=270 ohms has been successfully used. -- The Cfilt values are not exact, but the values shown worked well enough. -- -- The two carrier signals are identical, except that the LSB is constantly driving -- the heterodyne signal out, while the MSB is only driving the heterodyne signal -- during times when the irig DC level pulse is high, otherwise is outputs a 50% -- duty cycle signal, similar to ref_o. The cap_gnd_o outputs serves to adjust -- the corner frequency of the RC low pass filter. They are not pulsed with any -- high frequency signals. They simply apply ground or else high impedance, -- thereby effectively bringing the Cfilt capacitors in and out of the circuit. -- -- It is recommended to use the Cout capacitor in series with the output to provide -- AC coupling of the signal, and some DC isolation for the FPGA. -- -- This core uses a 1MHz clock enable pulse, to count the passage of time -- and output a pulse stream which conforms to IRIG standard 200-04, for the -- following IRIG formats: -- -- First letter: B (100 pps), A (1000 pps) or G (10000 pps) -- First digit : 0 = DC Level shift output (no modulation) -- Second digit : 0 = No carrier -- Third digit : [0..7] (BCD plus combinations of Year, CF and SBS fields) -- -- The core generates its own 1 MHz clock enable by using an NCO, with a phase -- accumulator register whose size is set by generics. When locked to an -- incoming timecode signal, the "integrated residual" (a.k.a. residusum) -- is checked and adjusted each second in order to "warp" the timebase rate -- to match the incoming timecode signal. In this way, the timecode -- regenerator tracks variations in the timing of the incoming IRIG signal. -- -- When not locked to an incoming timecode signal, there is an alternate method -- for keeping the time accurate over the longer term, namely the use of -- the pps_i input, a single pulse per second, detected on its rising edge. -- The pps_i input may be supplied by an external GPS module for accurate time. -- In the absence of incoming IRIG time signal or outside GPS reference, the -- local quartz crystal based system clock is left to "freewheel" on its own, -- which inevitably results in the buildup of inaccuracies in timekeeping, -- depending on temperature, aging and physical handling of the quartz crystal -- itself. -- -- All settings for the core are contained in read/write registers, which -- have default values determined by generics. In the case of a "code" -- selection which includes straight binary seconds ("SBS"), the control -- bits for the P8 and P9 portions of the output are not used, and the -- internally calculated SBS field is used instead. -- -- The year field also displaces control bits when it is selected. The two -- digit year is fully supported, and will increment properly at the end of -- 23:59:59 on day 364. (No leap year). -- -- For rates A and G, there is a tenths of a second BCD digit present in the -- latter end of the P4 period. -- -- For rate G, there is a hundredths of a second BCD digit present in the -- former end of the P5 period. -- -- If the year is desired, it can be placed in the proper position within -- the control bits, as shown here... -- Format B or A : -- Year_1 (the most significant BCD digit) => -- control_1(0) & control_0(7 downto 5) -- control_0(4) remains '0' -- Year_0 (the least significant BCD digit) => -- control_0(3 downto 0) -- -- Format G : -- Year_1 (the most significant BCD digit) => -- control_2(1 downto 0) & control_1(7 downto 6) -- control_1(5) remains '0' -- Year_0 (the least significant BCD digit) => -- control_1(4 downto 1) -- -- Bus interface writes to the registers cannot be given higher priority -- than internal updates for timekeeping based on pps_i, since bus writes -- only affect one value, while timekeeping can occasionally alter all of -- them. Thus corrupt and undesired behavior could result if bus writes -- were to override the timekeeping update values. Therefore, the ack_o -- signal is used to ensure that the timekeeping is finished before bus -- writes take place. -- -- The registers are summarized as follows: -- -- Address Structure Function -- ------- ---------- ---------------------------------------- -- 0x0 [**YY*DDD] BCD Year, Day of Year (Day is 1..365) -- 0x1 [**HHMMSS] BCD Hours, Minutes, Seconds. (*=unused nibble) -- 0x2 [CCCCCCCC] Control Bits (31:0) -- 0x3 [**CCCCCC] Control Bits (44:32) -- 0x4 (2:0) Rate Setting (See Notes below) -- 0x5 (2:0) Carrier Modulation Frequency -- 0x6 (2:0) Codes Used [ctrl,SBS,year] -- 0x7 (6:0) Time receiver control and status -- 0x8 (31:0) Statistical report (read only) -- 0x9 (31:0) PPS residusum, a 25 bit signed value, (sign extended) -- 0xA (8:0) Lowpass filter enable, CDET bias setting -- -- Notes: -- -- Register 0x0 (Leap Year, Year, Day of Year) -- -- Bit 28 controls the leap year setting: -- This register currently has only a single bit for setting leap year -- behavior. When the leap year bit is set, the day field advances to 366 -- before rolling over back to 1. When the bit is clear, the day field -- to 1 after the 365 day value. Note that the DDD field can be written -- with values higher than 366, but it will immediately roll over. -- -- Register 0x4 (Rate) -- 000 => (Use external input for Rate/Form/Carrier/Codes)* -- 001 => IRIG-B (1 second updates) -- 010 => IRIG-A (0.1 second updates) -- 011 => IRIG-G (0.01 second updates) -- 100 => NASA 36-bit (WWV) time code -- -- The "rate_i" input is used along with the register setting, in such -- a way that the register Rate value being zero, causes the "rate_i" and -- "codes_i" inputs to be used. If, however, the register Rate is set to -- a non-zero value, then the external input is ignored. -- -- When external rate input is used, a setting of "000" results -- in IRIG-B (1 second updates.) All other settings remain -- as outlined above. -- -- Register 0x5 (Carrier) -- -- 000 => No carrier (DC Level Shift) -- 001 => 100 Hz / 10 ms. resolution -- 010 => 1 kHz / 1 millisecond resolution -- 011 => 10 kHz / 100 microsecond resolution -- 100 => 100 kHz / 10 microsecond resolution -- -- Internal logic takes the day, hour, minute and second values and derives -- a value for the SBS field (Straight Binary Second). -- -- Register 0x6 (Codes) -- Each bit position, when set, activates the use of a specific field -- within the output, according to the form: -- -- [ctrl,SBS,year] -- -- Where ctrl = Control field -- SBS = Straight Binary Seconds -- year = Year field -- -- If year and control are both selected, then the year is used in its -- appropriate spot, preventing the control bits there from being used. -- -- For Rate "G", the hundredths of a second field occludes the first four -- control bits, so that they are not used. The hundredths of a second field -- is encoded in the register area for "year" since year is not used in -- Rate "G" -- -- Register 0x07 -- Time Receiver Status register -- bits (1:0) = cdet_val. These bits are the carrier detect value, -- which relates to the detected IRIG timecode speed. -- cdet_val Meaning -- -------- ----------------------------- -- 3 IRIG G speed (100kHz carrier) -- 2 IRIG A speed (10 kHz carrier) -- 1 IRIG B speed (1 kHz carrier) -- 0 no carrier detected. -- -- bits (3:2) = reserved (value is set to "00"). -- -- bit (4) = active status bit. '1' means activity was detected on the -- irig_dcl_i input. -- bit (5) = lock status bit. '1' indicates the receiver is in the -- TRACK_IRIG state -- bit (6) = IRIG tracking status bit. Read only bit that indicates the -- received IRIG timecode is valid for time tracking -- bit (7) = PPS tracking status bit. Read only bit that indicates the -- received PPS signal is valid for time tracking -- bit (8) = neverlocked status bit. '1' indicates the unit has not locked -- since reset. This bit can only be set after Power On Reset. -- Read only. -- bit (9) = rx_nasa36 bit. This bit is set whenever NASA 36 timecode is -- detected and used. Read only. -- bit (10)= cdet_sel_l bit. This bit is read only, and indicates when -- the receiver state machine is asserting carrier detect mode. -- When this bit is set, external circuitry is configured to -- strip out the time information, and only provide a -- "zero crossing" detected carrier on the time_cdet_i input. -- The "zero crossing" detected carrier is a squarewave at the -- frequency of the carrier. When this bit is clear, pulses arrive -- corresponding to the times when the carrier is at high-amplitude. -- bits (31:24) = rx_pulse_width. This is the most recently measured pulse -- low value as a percentage. Nominal IRIG timecode pulses -- should be high: 20% for zero, 50% for one, and 80% for ref. -- The rx_pulse_width is a count of the low time after the -- trailing edge of the received pulse. Thus, values >96 -- represent pulses which are too short, >72 are zeros, -- >48 are ones and >16 are ref pulses. These thresholds are -- defined as constants within the code. -- -- Register 0x08 -- Receiver additional information -- bits (3:0) = receiver current state -- value name meaning -- ----- ----------- -------------------------------------------------------------- -- 0 SETTLE_DOWN Awaiting capacitor discharge following cdet_sel_o being cleared -- 1 CDET_1 Awaiting carrier speed detect synchronization -- 2 CDET_2 Awaiting carrier speed detect -- 3 SETTLE_UP Awaiting capacitor charge following cdet_sel_o being set -- 4 SEEK_REF_1 Seeking a longer "reference" type pulse -- 5 SEEK_REF_2 Seeking a second CONSECUTIVE "reference" type pulse -- 6 RECEIVE_PULSES Receiving and decoding IRIG time pulses -- 7 SETTLE_IN Awaiting capacitor discharge heading into TRACK_IRIG -- 8 TRACK_IRIG Tracking incoming IRIG time carrier based PPS -- -- bits (7:4) = reserved -- -- bits (15:8) = number of times rx_active has been asserted. Write to clear. -- bits (23:16) = number of times RECEIVE_PULSES has been entered. Write to clear. -- bits (31:24) = number of times TRACK_IRIG has been entered. Write to clear. -- -- Register 0x09 -- Receiver PPS tracking "residusum" -- This is a signed quantity which represents the speed adjustment to the internally -- generated IRIG time output. It is based on measurements against a incoming pulse -- per second time reference. There are two PPS references: pps_i and an internal -- irig_pps which is based on dividing down the received carrier frequency whenever -- the receiver is in the TRACK_IRIG state. -- This is a signed quantity, which has been sign extended to 32 bits. It is limited -- by the constant RESIDUSUM_BITS to be at most +/- 1/128 the magnitude of the frequency -- setting (An allowed frequency variation window of +/- 0.7825 percent.) -- -- Register 0x0A -- Carrier Detect Bias, and time lowpass filter enable -- bits (5:0) = Carrier detect bias value. Ranging from 0x00 to 0x20, this value -- drives a PWM output to an RC filter network, creating a voltage used -- as a threshold to detect either the timecode carrier, or the carrier -- "high amplitude peaks" depending on cdet_sel_o. -- bit (8) = time_cdet_i lowpass filter enable. When set, this bit enables use of -- a leaky-integrator based lowpass filter. -- -- The generics used as defaults for the registers are the full width of the -- data bus. However, only those bits which are actually present in the actual -- registers are used. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; use IEEE.MATH_REAL.ALL; library work; use work.dds_pack.all; use work.pwm_pack.all; use work.convert_pack.all; use work.signal_conditioning_pack.all; entity irig_time_regenerator is generic ( SYS_CLK_RATE : real := 40000000.0; -- Needed for carrier generation LOCK_THRESHOLD : integer := 8; -- Frames received before entering locked state DEF_R_0 : unsigned(31 downto 0) := str2u("00100123",32); DEF_R_1 : unsigned(31 downto 0) := str2u("00000000",32); DEF_R_2 : unsigned(31 downto 0) := str2u("00000000",32); DEF_R_3 : unsigned(31 downto 0) := str2u("00000000",32); DEF_R_4 : unsigned(31 downto 0) := str2u("00000001",32); DEF_R_5 : unsigned(31 downto 0) := str2u("00000002",32); DEF_R_6 : unsigned(31 downto 0) := str2u("00000007",32); DEF_R_A : unsigned(31 downto 0) := str2u("00000014",32); -- Initial value of carrier detect bias DEF_R_Z : unsigned(31 downto 0) := str2u("00000000",32) -- Value returned for nonexistent registers ); port ( sys_rst_n : in std_logic; sys_clk : in std_logic; sys_clk_en : in std_logic; -- 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; -- Pulse per second time reference -- IRIG select inputs -- Active only when register 0x4 set to zero. rate_i : in unsigned(2 downto 0); -- Rates : 0,1=>B, 2=>A, 3=>G, 4=>NASA 36-bit carrier_i : in unsigned(2 downto 0); -- Carriers : 0=DCLS, 1=100Hz, 2=1kHz, 3=10kHz, 4=100kHz codes_i : in unsigned(2 downto 0); -- Codes : (2)=> control, (1)=> SBS, (0)=> year -- IRIG time output, parallel form time_o : out unsigned(67 downto 0); -- Carrier Detect Select output, PPS input, -- IRIG time input, tracking and lock indicators cdet_sel_o : out std_logic; pps_i : in std_logic; time_cdet_i : in std_logic; cdet_val_o : out unsigned(1 downto 0); active_o : out std_logic; tracking_o : out std_logic; lock_o : out std_logic; neverlocked_o : out std_logic; -- Serial IRIG time stream output, LVTTL level time_dcl_o : out std_logic; cap_gnd_o : out std_logic; ref_o : out std_logic; carrier_o : out unsigned(1 downto 0) ); end irig_time_regenerator; architecture beh of irig_time_regenerator is -- Constants -- IRIG Generator Constants -- DDS size constant RX_1MHZ_DDS_BITS : natural := 32; constant HDYNE_DDS_BITS : integer := 32; -- Digital Heterodyne constants constant INCREMENT_1000KHZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1000000.0),HDYNE_DDS_BITS); constant INCREMENT_1100KHZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1100000.0),HDYNE_DDS_BITS); constant INCREMENT_1010KHZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1010000.0),HDYNE_DDS_BITS); constant INCREMENT_1001KHZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1001000.0),HDYNE_DDS_BITS); constant INCREMENT_1000100HZ : unsigned(HDYNE_DDS_BITS-1 downto 0) := to_unsigned(integer(2.0**HDYNE_DDS_BITS/SYS_CLK_RATE*1000100.0),HDYNE_DDS_BITS); -- IRIG receiver constants constant FRAME_COUNT_BITS : natural := bit_width(LOCK_THRESHOLD); constant PHASE_INCR_1MHZ : natural := natural(1000000.0/SYS_CLK_RATE*(2**real(RX_1MHZ_DDS_BITS))); constant RESIDUSUM_BITS : natural := RX_1MHZ_DDS_BITS-7; -- Restrict variation to approx. 1% (1/128) of frequency setting constant RX_EDGE_VAL : natural := 16; constant RX_ONE_VAL : natural := 48; constant RX_ZERO_VAL : natural := 72; constant RX_TOO_SHORT : natural := 96; constant RX_C_SEED_B : integer := 1000; -- Counts carrier pulses constant RX_C_SEED_A : integer := 10000; -- Counts carrier pulses constant RX_C_SEED_G : integer := 100000; -- Counts carrier pulses constant PPS_R_COUNT_SEED : integer := 999999; -- Counts 1 million microseconds... constant PPS_TRACK_LIMIT : integer := 1000; constant CDET_SETTLE_TIME : integer := 24; -- Milliseconds of settling when cdet_sel_o changes. --constant CDET_SETTLE_TIME : integer := 0; -- For fast simulation -- NOTE: It was discovered during testing that, with the enhancements to the -- IRIG timecode receiver, settling time is not needed in order to obtain a -- lock on the incoming signal. Therefore, apart from a slight initial -- inaccuracy in frequency tracking for <24ms in the TRACK_IRIG state, there -- is no effect from skipping the settling time. It will be left at zero ms -- unless a compelling reason is found to increase it. -- Internal signal declarations -- State Machine type FSM_STATE_TYPE is (RESET, IDLE, P0_REF, PR_REF, P0_DOUT, P1_PLUS_REF, P1_PLUS_DOUT); signal fsm_state : FSM_STATE_TYPE; signal fsm_ack : std_logic; signal ack_l : std_logic; -- Related to SBS calculation -- SBS Calculation State type SBS_STATE_TYPE is (RESET, IDLE, TIMEKEEPING_DELAY, SBS_10H, SBS_1H, SBS_10M, SBS_1M, SBS_10S); signal sbs_state : SBS_STATE_TYPE; -- Related to Registers signal leap_year : std_logic; signal secs_0 : unsigned(3 downto 0); signal secs_1 : unsigned(2 downto 0); signal mins_0 : unsigned(3 downto 0); signal mins_1 : unsigned(2 downto 0); signal hours_0 : unsigned(3 downto 0); signal hours_1 : unsigned(1 downto 0); signal days_0 : unsigned(3 downto 0); signal days_1 : unsigned(3 downto 0); signal days_2 : unsigned(1 downto 0); signal years_0 : unsigned(3 downto 0); signal years_1 : unsigned(3 downto 0); signal tenths : unsigned(3 downto 0); signal hundredths : unsigned(3 downto 0); signal sbs : unsigned(16 downto 0); signal sbs_next : unsigned(16 downto 0); signal sbs_add_count : unsigned(3 downto 0); signal control : unsigned(44 downto 0); signal reg_rate : unsigned(2 downto 0); signal reg_carrier : unsigned(2 downto 0); signal reg_codes : unsigned(2 downto 0); signal irig_rate : unsigned(2 downto 0); signal irig_carrier : unsigned(2 downto 0); signal irig_codes : unsigned(2 downto 0); -- Related to generation of the output pulse stream signal tx_1mhz_pulse : std_logic; -- 1MHz pulse stream which can warp when tracking. signal set_time : std_logic; signal pulse_clk_count : unsigned(9 downto 0); -- Divides by factor of 10, 100 or 1000 signal pulse_clk_en : std_logic; signal pulse_sub_count : unsigned(3 downto 0); -- Advances when pulse_clk_en=1 signal sub_count_nine : std_logic; signal p_count : unsigned(3 downto 0); -- Advances when sub_count=9 and pulse_sr_count=9 signal cycle_count : unsigned(6 downto 0); -- Counts output cycles during the current second. signal pulse_sr : unsigned(8 downto 0); signal pulse_sr_count : unsigned(3 downto 0); -- Advances when sub_count=9 signal sr_load_bus : unsigned(8 downto 0); signal p0 : unsigned(8 downto 0); -- sr load value signal p1 : unsigned(8 downto 0); -- sr load value signal p2 : unsigned(8 downto 0); -- sr load value signal p3 : unsigned(8 downto 0); -- sr load value signal p4 : unsigned(8 downto 0); -- sr load value signal p5 : unsigned(8 downto 0); -- sr load value signal p6 : unsigned(8 downto 0); -- sr load value signal p7 : unsigned(8 downto 0); -- sr load value signal p8 : unsigned(8 downto 0); -- sr load value signal p9 : unsigned(8 downto 0); -- sr load value signal pulse_extent : unsigned(3 downto 0); signal time_pulses : std_logic; signal pps : std_logic; -- Related to generation of the sinewave carrier signal hdyne_dds_sync : std_logic; signal hdyne_base_square : std_logic; signal hdyne_in_square : std_logic; signal hdyne_out : std_logic; signal hdyne_in_freq : unsigned(HDYNE_DDS_BITS-1 downto 0); signal modulated_carrier : std_logic; -- Related to Timecode Receiver type RX_STATE_TYPE is (WAIT_ACTIVE, SEEK_REF_1, SEEK_REF_2, RECEIVE_PULSES, SETTLE_IN, TRACK_IRIG); signal rx_state : RX_STATE_TYPE; signal rx_nasa36 : std_logic; -- High means NASA 36-bit code detected signal rx_sr : unsigned(9 downto 0); signal rx_scale_factor : unsigned(7 downto 0); signal rx_scale_count : unsigned(7 downto 0); signal rx_lo_count : unsigned(7 downto 0); signal rx_pulse_width : unsigned(7 downto 0); signal rx_pulse_count : unsigned(3 downto 0); signal rx_group_count : unsigned(3 downto 0); signal rx_freq : unsigned(RX_1MHZ_DDS_BITS-1 downto 0); -- Sets frequency signal rx_1mhz_pulse : std_logic; signal rx_posedge : std_logic; signal rx_pstart : std_logic; signal rx_hi_count : unsigned(7 downto 0); signal rx_raise_count : unsigned(3 downto 0); signal rx_active_wdog : unsigned(7 downto 0); signal rx_active_count : unsigned(3 downto 0); signal rx_active : std_logic; signal rx_pps_tracking : std_logic; signal rx_irig_tracking : std_logic; signal rx_lock : std_logic; signal micro_bcd_0 : unsigned(3 downto 0); signal micro_bcd_1 : unsigned(3 downto 0); signal micro_bcd_2 : unsigned(3 downto 0); signal micro_bcd_3 : unsigned(3 downto 0); signal micro_bcd_4 : unsigned(3 downto 0); signal micro_bcd_5 : unsigned(3 downto 0); signal rx_c_count : unsigned(16 downto 0); signal pps_r_count : signed(20 downto 0); -- Counts down 1 sec. worth of microseconds. signal pps_residusum : signed(RESIDUSUM_BITS-1 downto 0); signal rx_secs_0 : unsigned(3 downto 0); signal rx_secs_1 : unsigned(2 downto 0); signal rx_mins_0 : unsigned(3 downto 0); signal rx_mins_1 : unsigned(2 downto 0); signal rx_hours_0 : unsigned(3 downto 0); signal rx_hours_1 : unsigned(1 downto 0); signal rx_days_0 : unsigned(3 downto 0); signal rx_days_1 : unsigned(3 downto 0); signal rx_days_2 : unsigned(1 downto 0); signal rx_years_0 : unsigned(3 downto 0); signal rx_years_1 : unsigned(3 downto 0); signal rx_neverlocked : std_logic; signal cdet_fixed : std_logic; signal cdet_val_l : unsigned(1 downto 0); signal cdet_sel_l : std_logic; signal pulse_1ms : std_logic; signal cdet_ms_timer : unsigned(4 downto 0); signal cdet_bias : unsigned(5 downto 0); signal cdet_dac_clk_en : std_logic; signal time_cleaner_input : signed(15 downto 0); signal time_cleaned : std_logic; signal time_rx : std_logic; signal time_rx_r1 : std_logic; signal time_rx_r2 : std_logic; signal reg_time_lp : std_logic; signal pps_r1 : std_logic; signal pps_r2 : std_logic; signal rx_state_report : unsigned(3 downto 0); signal rx_stat_1 : unsigned(7 downto 0); signal rx_stat_2 : unsigned(7 downto 0); signal rx_stat_3 : unsigned(7 downto 0); ----------------------------------------------------------------------------- begin -- Select which settings to use irig_rate <= reg_rate when (reg_rate/=0) else rate_i; irig_carrier <= reg_carrier when (reg_rate/=0) else carrier_i; irig_codes <= reg_codes when (reg_rate/=0) else codes_i; -- Register read mux with (adr_i) select dat_o <= "000" & leap_year & "0000" & years_1 & years_0 & "000000" & days_2 & days_1 & days_0 when "0000", -- "0"; "0000000000" & hours_1 & hours_0 & "0" & mins_1 & mins_0 & "0" & secs_1 & secs_0 when "0001", -- "1"; control(31 downto 0) when "0010", -- "2"; "0000000000000000000" & control(44 downto 32) when "0011", -- "3"; u_resize(reg_rate,32) when "0100", -- "4"; u_resize(reg_carrier,32) when "0101", -- "5"; "00000000000000000000000000000" & reg_codes when "0110", -- "6"; rx_pulse_width & "0000000000000" & cdet_fixed & rx_nasa36 & rx_neverlocked & rx_pps_tracking & rx_irig_tracking & rx_lock & rx_active & "00" & cdet_val_l when "0111", -- "7"; rx_stat_3 & rx_stat_2 & rx_stat_1 & "0000" & rx_state_report when "1000", -- "8"; unsigned(s_resize_se(pps_residusum,32)) when "1001", -- "9"; "00000000000000000000000" & reg_time_lp & u_resize(cdet_bias,8) when "1010", -- "A"; DEF_R_Z when others; -- Default -- Create acknowledge signal ack_l <= fsm_ack when (sel_i='1' and we_i='1' and adr_i<7) else -- special ack for tx fsm registers '1' when (sel_i='1') else '0'; ack_o <= ack_l; -- Produce parallel output time_o <= years_1 & years_0 & "00" & days_2 & days_1 & days_0 & "00" & hours_1 & hours_0 & '0' & mins_1 & mins_0 & '0' & secs_1 & secs_0 & micro_bcd_5 & micro_bcd_4 & micro_bcd_3 & micro_bcd_2 & micro_bcd_1 & micro_bcd_0; -------------------------- -- Heterodyning Signals -- The idea here is to multiply or "mix" two "squarewave" sequences -- by using an XOR gate, to produce a sum and difference frequency. -- The sum frequency should be easily filtered out by an RC filter -- external to the FPGA. hdyne_base_squarewave : dds_squarewave generic map( ACC_BITS => HDYNE_DDS_BITS ) port map( sys_rst_n => sys_rst_n, sys_clk => sys_clk, sys_clk_en => sys_clk_en, -- Frequency setting freq_i => INCREMENT_1000KHZ, -- Output pulse_o => open, squarewave_o => hdyne_base_square ); hdyne_in_squarewave : dds_squarewave_phase_load generic map( ACC_BITS => HDYNE_DDS_BITS ) port map( sys_rst_n => sys_rst_n, sys_clk => sys_clk, sys_clk_en => sys_clk_en, -- Frequency setting freq_i => hdyne_in_freq, -- Synchronous load phase_i => ((HDYNE_DDS_BITS-2)=>'1', others=>'0'), -- Set to 1/4 phase point (sinewave "zero" output) phase_ld_i => hdyne_dds_sync, -- Output pulse_o => open, squarewave_o => hdyne_in_square ); hdyne_in_freq <= INCREMENT_1100KHZ when irig_carrier=4 else INCREMENT_1010KHZ when irig_carrier=3 else INCREMENT_1001KHZ when irig_carrier=2 else INCREMENT_1000100HZ; hdyne_out <= hdyne_base_square xor hdyne_in_square; -- A sort of digital signal multiplication modulated_carrier <= hdyne_out when time_pulses='1' else hdyne_base_square; -- Output midpoint when pulse is low carrier_o(0) <= '0' when irig_carrier=0 else hdyne_out; carrier_o(1) <= time_pulses when irig_carrier=0 else modulated_carrier; ref_o <= '0' when irig_carrier=0 else hdyne_base_square; -- Used as a "midpoint" voltage reference -------------------------- -- Setting for output filter capacitor. -- The idea here is to ground the larger capacitor for low frequency -- carriers, and for the highest frequency, not ground it. It will -- be connected in series with a smaller capacitor then. cap_gnd_o <= '0' when irig_carrier=4 else '1'; -- This process is where the different BCD values are compiled into -- a "straight binary seconds" (SBS) field. -- The calculations are done in a multiply accumulate fashion (MAC) -- wherein the multiply is implemented as a series of additions. -- An SBS accumulator stores the intermediate calculation total, -- while a state variable tracks the current stage of calculation, -- beginning at "tens of hours" and finishing at "seconds." -- All the adds performed here are unsigned... sbs_proc : process(sys_clk,sys_rst_n) begin if (sys_rst_n='0') then sbs_state <= RESET; sbs <= (others=>'0'); sbs_next <= (others=>'0'); sbs_add_count <= (others=>'0'); elsif (sys_clk'event and sys_clk='1') then if (sys_clk_en='1') then case (sbs_state) is when RESET => sbs_state <= IDLE; when IDLE => if (pps='1') then sbs_state <= TIMEKEEPING_DELAY; end if; -- This state waits for changes to the BCD time values to propagate, -- so they will be stable before this process begins calculation of -- the SBS field. when TIMEKEEPING_DELAY => sbs <= (others=>'0'); -- Clear accumulator sbs_next <= u_resize(hours_1,sbs'length); sbs_add_count <= "1010"; -- Multiply by 10 sbs_state <= SBS_10H; -- The following series of states performs multiply and accumulate -- calculations to generate the SBS value. when SBS_10H => if (sbs_add_count=0) then sbs <= (others=>'0'); sbs_next <= sbs + u_resize(hours_0,sbs'length); sbs_add_count <= "0110"; -- Multiply by 6 sbs_state <= SBS_1H; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when SBS_1H => if (sbs_add_count=0) then sbs <= (others=>'0'); sbs_next <= sbs + u_resize(mins_1,sbs'length); sbs_add_count <= "1010"; -- Multiply by 10 sbs_state <= SBS_10M; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when SBS_10M => if (sbs_add_count=0) then sbs <= (others=>'0'); sbs_next <= sbs + u_resize(mins_0,sbs'length); sbs_add_count <= "0110"; -- Multiply by 6 sbs_state <= SBS_1M; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when SBS_1M => if (sbs_add_count=0) then sbs <= (others=>'0'); sbs_next <= sbs + u_resize(secs_1,sbs'length); sbs_add_count <= "1010"; -- Multiply by 10 sbs_state <= SBS_10S; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when SBS_10S => if (sbs_add_count=0) then sbs <= sbs + u_resize(secs_0,sbs'length); -- Add seconds (no more multiplication.) sbs_state <= IDLE; else sbs <= sbs + sbs_next; sbs_add_count <= sbs_add_count-1; end if; when others => null; end case; end if; -- sys_clk_en end if; -- sys_clk end process; -- sr load values p0 <= "00000" & secs_0 when (irig_rate=4) else "0" & secs_1 & "0" & secs_0; p1 <= "000000" & secs_1 when (irig_rate=4) else "0" & mins_1 & "0" & mins_0; p2 <= "00000" & mins_0 when (irig_rate=4) else "00" & hours_1 & "0" & hours_0; p3 <= "000000" & mins_1 when (irig_rate=4) else days_1 & "0" & days_0; p4 <= "00000" & hours_0 when (irig_rate=4) else "0000000" & days_2 when (irig_rate=0 or irig_rate=1) else tenths & "000" & days_2; p5 <= "0000000" & hours_1 when (irig_rate=4) else control(8 downto 4) & hundredths when (irig_rate=3) else years_1 & '0' & years_0 when (irig_codes(2)='1') else control(8 downto 0) when (irig_codes(0)='1') else "000000000"; p6 <= "00000" & days_0 when (irig_rate=4) else years_1 & '0' & years_0 when (irig_rate=3 and irig_codes(2)='1') else control(17 downto 9) when (irig_codes(0)='1') else "000000000"; p7 <= "00000" & days_1 when (irig_rate=4) else control(26 downto 18) when (irig_codes(0)='1') else "000000000"; p8 <= "0000000" & days_2 when (irig_rate=4) else sbs(8 downto 0) when (irig_codes(1)='1') else control(35 downto 27) when (irig_codes(0)='1') else "000000000"; p9 <= "111110000" when (irig_rate=4) else '0' & sbs(16 downto 9) when (irig_codes(1)='1') else control(44 downto 36) when (irig_codes(0)='1') else "000000000"; -- sr_load_bus mux with (p_count) select sr_load_bus <= p0 when "0000", -- "0"; p1 when "0001", -- "1"; p2 when "0010", -- "2"; p3 when "0011", -- "3"; p4 when "0100", -- "4"; p5 when "0101", -- "5"; p6 when "0110", -- "6"; p7 when "0111", -- "7"; p8 when "1000", -- "8"; p9 when "1001", -- "9"; "000000000" when others; -- Not needed for synthesis... -- Generate a clock enable for advancing the output pulse state -- The rate of this pulse is determined by the selected IRIG rate. -- The rate has been selected to allow output pulse duty cycle -- to be set in increments of 1/10th the total pulse time. -- -- Selected Rate pulse_clk rate Pulse Frequency -- ------------- -------------- ----------------- -- 000 1 kHz 100 Hz -- 001 (B) 1 kHz 100 Hz -- 010 (A) 10 kHz 1 kHz -- 011 (G) 100 kHz 10 kHz -- 100 (NASA 36) 1 kHz 100 Hz -- -- Generate a clock enable for advancing the output carrier state -- The rate of this pulse is determined by the selected IRIG rate. -- The rate has been selected to allow 10 digital samples per cycle -- of carrier output. -- -- Selected Carrier carrier_clk rate Carrier Frequency -- ---------------- ---------------- ----------------- -- 000 DC Level -None- -- 001 1 kHz 100 Hz -- 010 10 kHz 1 kHz -- 011 100 kHz 10 kHz -- 100 1 MHz 100 kHz -- pulse_clk_proc : process(sys_clk,sys_rst_n) begin if (sys_rst_n='0') then pulse_clk_count <= (others=>'0'); elsif (sys_clk'event and sys_clk='1') then if (sys_clk_en='1') then if (pulse_clk_en='1') then pulse_clk_count <= (others=>'0'); elsif (tx_1mhz_pulse='1') then pulse_clk_count <= pulse_clk_count + 1; end if; end if; -- sys_clk_en end if; -- sys_clk end process; pulse_clk_en <= '1' when (tx_1mhz_pulse='1' and (irig_rate=0 or irig_rate=1 or irig_rate=4) and pulse_clk_count=999) else '1' when (tx_1mhz_pulse='1' and irig_rate=2 and pulse_clk_count=99) else '1' when (tx_1mhz_pulse='1' and irig_rate=3 and pulse_clk_count=9) else '0'; -- This is the IRIG timecode generating state machine. -- It handles updates to the timekeeping registers, based on the "pps" signal, -- and creates the output pulse stream. fsm_proc: process(sys_clk, sys_rst_n) begin if (sys_rst_n='0') then fsm_state <= RESET; -- asynchronous reset leap_year <= '0'; micro_bcd_0 <= to_unsigned(0,4); micro_bcd_1 <= to_unsigned(9,4); micro_bcd_2 <= to_unsigned(9,4); micro_bcd_3 <= to_unsigned(9,4); micro_bcd_4 <= to_unsigned(9,4); micro_bcd_5 <= to_unsigned(9,4); hundredths <= (others=>'0'); tenths <= (others=>'0'); days_0 <= DEF_R_0(3 downto 0); days_1 <= DEF_R_0(7 downto 4); days_2 <= DEF_R_0(9 downto 8); years_0 <= DEF_R_0(19 downto 16); years_1 <= DEF_R_0(23 downto 20); secs_0 <= DEF_R_1(3 downto 0); secs_1 <= DEF_R_1(6 downto 4); mins_0 <= DEF_R_1(11 downto 8); mins_1 <= DEF_R_1(14 downto 12); hours_0 <= DEF_R_1(19 downto 16); hours_1 <= DEF_R_1(21 downto 20); control <= DEF_R_3(12 downto 0) & DEF_R_2; reg_rate <= DEF_R_4(2 downto 0); reg_carrier <= DEF_R_5(2 downto 0); reg_codes <= DEF_R_6(2 downto 0); p_count <= (others=>'0'); pulse_sr <= (others=>'0'); pulse_sr_count <= (others=>'0'); pulse_sub_count <= (others=>'0'); cycle_count <= (others=>'0'); fsm_ack <= '0'; hdyne_dds_sync <= '0'; elsif (sys_clk'event and sys_clk='1') then if (sys_clk_en='1') then -- Default values fsm_ack <= '0'; hdyne_dds_sync <= '0'; -- Use DDS to advance the six digit microsecond BCD counter. -- This can be overridden by other statements further below. if (tx_1mhz_pulse='1') then if (micro_bcd_0=9) then micro_bcd_0 <= (others=>'0'); if (micro_bcd_1=9) then micro_bcd_1 <= (others=>'0'); if (micro_bcd_2=9) then micro_bcd_2 <= (others=>'0'); if (micro_bcd_3=9) then micro_bcd_3 <= (others=>'0'); if (micro_bcd_4=9) then micro_bcd_4 <= (others=>'0'); if (micro_bcd_5=9) then micro_bcd_5 <= (others=>'0'); else micro_bcd_5 <= micro_bcd_5+1; end if; else micro_bcd_4 <= micro_bcd_4+1; end if; else micro_bcd_3 <= micro_bcd_3+1; end if; else micro_bcd_2 <= micro_bcd_2+1; end if; else micro_bcd_1 <= micro_bcd_1+1; end if; else micro_bcd_0 <= micro_bcd_0+1; end if; end if; -- Handle timekeeping for fractions of seconds, based on 1MHz clock enable. -- These updates are overridden by pps, which resets all the time count values. if (tx_1mhz_pulse='1') then if ( -- Check for .01 second transition micro_bcd_0=9 and micro_bcd_1=9 and micro_bcd_2=9 and micro_bcd_3=9 ) then if (hundredths=9) then tenths <= tenths+1; hundredths <= (others=>'0'); if (tenths=9) then -- No need to increment seconds here. pps does that. tenths <= (others=>'0'); else tenths <= tenths+1; end if; else hundredths <= hundredths+1; end if; end if; end if; -- Handle the timekeeping items, based on pps -- The highest priority is "set_time" which immediately resets all time -- quantities. Next highest priority is normal timekeeping, and finally, -- the lowest priority is register writes to time quantities. if (set_time='1') then micro_bcd_0 <= (others=>'0'); micro_bcd_1 <= (others=>'0'); micro_bcd_2 <= (others=>'0'); micro_bcd_3 <= (others=>'0'); micro_bcd_4 <= (others=>'0'); micro_bcd_5 <= (others=>'0'); secs_0 <= rx_secs_0; secs_1 <= rx_secs_1; mins_0 <= rx_mins_0; mins_1 <= rx_mins_1; hours_0 <= rx_hours_0; hours_1 <= rx_hours_1; days_0 <= rx_days_0; days_1 <= rx_days_1; days_2 <= rx_days_2; years_0 <= rx_years_0; years_1 <= rx_years_1; elsif (pps='1') then secs_0 <= secs_0+1; tenths <= (others=>'0'); hundredths <= (others=>'0'); if (secs_0=9) then secs_1 <= secs_1+1; secs_0 <= (others=>'0'); if (secs_1=5) then mins_0 <= mins_0+1; secs_1 <= (others=>'0'); if (mins_0=9) then mins_1 <= mins_1+1; mins_0 <= (others=>'0'); if (mins_1=5) then hours_0 <= hours_0+1; mins_1 <= (others=>'0'); if (hours_1=2 and hours_0=3) then days_0 <= days_0+1; hours_1 <= (others=>'0'); hours_0 <= (others=>'0'); if (days_2>=3 and days_1>=6 and ((days_0>=5 and leap_year='0') or (days_0>=6 and leap_year='1'))) then years_0 <= years_0+1; days_0 <= "0001"; days_1 <= (others=>'0'); days_2 <= (others=>'0'); if (years_0=9) then years_1 <= years_1+1; years_0 <= (others=>'0'); if (years_1=9) then years_1 <= (others=>'0'); end if; end if; elsif (days_0=9) then days_1 <= days_1+1; days_0 <= (others=>'0'); if (days_1=9) then days_2 <= days_2+1; days_1 <= (others=>'0'); end if; end if; elsif (hours_0=9) then hours_1 <= hours_1+1; hours_0 <= (others=>'0'); end if; end if; end if; end if; end if; elsif (sel_i='1' and we_i='1') then -- Handle bus writes to registers -- Cannot do this during pps, since this only writes one value, while -- pps timekeeping can potentially affect all the values. Therefore, -- generate an acknowledge signal for the register writes... so that -- incoming bus cycles are extended when they collide with pps... fsm_ack <= '1'; case (adr_i) is when "0000" => leap_year <= dat_i(28); days_0 <= dat_i(3 downto 0); days_1 <= dat_i(7 downto 4); days_2 <= dat_i(9 downto 8); years_0 <= dat_i(19 downto 16); years_1 <= dat_i(23 downto 20); when "0001" => secs_0 <= dat_i(3 downto 0); secs_1 <= dat_i(6 downto 4); mins_0 <= dat_i(11 downto 8); mins_1 <= dat_i(14 downto 12); hours_0 <= dat_i(19 downto 16); hours_1 <= dat_i(21 downto 20); when "0010" => control(31 downto 0) <= dat_i; when "0011" => control(44 downto 32) <= dat_i(12 downto 0); when "0100" => reg_rate <= dat_i(2 downto 0); when "0101" => reg_carrier <= dat_i(2 downto 0); when "0110" => reg_codes <= dat_i(2 downto 0); when others => null; end case; end if; -- Handle pulse_sub_count if (pulse_clk_en='1') then if (sub_count_nine='1') then pulse_sub_count <= (others=>'0'); hdyne_dds_sync <= '1'; -- Set the carrier wave phase. else pulse_sub_count <= pulse_sub_count + 1; end if; end if; -- Handle state transitions case (fsm_state) is when RESET => fsm_state <= IDLE; when IDLE => if (pps='1') then fsm_state <= P0_REF; p_count <= (others=>'0'); cycle_count <= (others=>'0'); pulse_sr_count <= (others=>'0'); end if; when P0_REF => if (pulse_clk_en = '1' and sub_count_nine='1') then pulse_sr_count <= pulse_sr_count+1; pulse_sr <= sr_load_bus; if (irig_rate=4) then fsm_state <= P0_DOUT; -- No special reference pulse for IRIG 36-bit else fsm_state <= PR_REF; end if; end if; when PR_REF => if (pulse_clk_en = '1' and sub_count_nine='1') then pulse_sr_count <= pulse_sr_count+1; fsm_state <= P0_DOUT; end if; when P0_DOUT => if (pulse_clk_en = '1' and sub_count_nine='1') then if (pulse_sr_count=9) then pulse_sr_count <= (others=>'0'); p_count <= p_count+1; fsm_state <= P1_PLUS_REF; else pulse_sr <= '0' & pulse_sr(8 downto 1); pulse_sr_count <= pulse_sr_count+1; end if; end if; when P1_PLUS_REF => if (pulse_clk_en = '1' and sub_count_nine='1') then pulse_sr_count <= pulse_sr_count+1; pulse_sr <= sr_load_bus; fsm_state <= P1_PLUS_DOUT; end if; when P1_PLUS_DOUT => if (p_count=9 and pps='1') then -- pps should not occur before p_count=9 anyway... fsm_state <= P0_REF; p_count <= (others=>'0'); cycle_count <= (others=>'0'); pulse_sr_count <= (others=>'0'); elsif (pulse_clk_en = '1' and sub_count_nine='1') then if (pulse_sr_count=9) then if (p_count=9) then if (cycle_count=99 and irig_rate=3) then fsm_state <= IDLE; elsif (cycle_count=9 and irig_rate=2) then fsm_state <= IDLE; elsif (irig_rate=0 or irig_rate=1 or irig_rate=4) then fsm_state <= IDLE; else cycle_count <= cycle_count+1; p_count <= (others=>'0'); pulse_sr_count <= (others=>'0'); fsm_state <= P0_REF; end if; else pulse_sr_count <= (others=>'0'); p_count <= p_count+1; pulse_sr <= sr_load_bus; fsm_state <= P1_PLUS_REF; end if; else pulse_sr <= '0' & pulse_sr(8 downto 1); pulse_sr_count <= pulse_sr_count+1; end if; end if; --when others => -- fsm_state <= IDLE; end case; end if; -- sys_clk_en end if; -- sys_clk end process; -- This pulse per second signal is for IRIG timecode generation pps <= '1' when (tx_1mhz_pulse='1' and ( micro_bcd_0=9 and micro_bcd_1=9 and micro_bcd_2=9 and micro_bcd_3=9 and micro_bcd_4=9 and micro_bcd_5=9 ) ) else '0'; -- Create signal which indicates last portion of time generator pulse. sub_count_nine <= '1' when (pulse_sub_count=9) else '0'; -- Form output pulses pulse_extent <= "0010" when (irig_rate=4 and pulse_sr_count=0 and p_count=0) else "0101" when (irig_rate=4 and pulse_sr_count=0) else "1000" when (pulse_sr_count=0 or fsm_state=PR_REF) else "0101" when (pulse_sr(0)='1') else "0010"; time_pulses <= '1' when (pulse_sub_count<pulse_extent) else '0'; time_dcl_o <= time_pulses; -- Related to time receiver ---------------------------- -------------------------- -- generate pulses at 1 MHz nominal rate. -- The term nominal is used here, because the actual rate can be "warped" up/down -- to track an external PPS reference. rx_mega_pulser : dds_squarewave generic map( ACC_BITS => RX_1MHZ_DDS_BITS ) port map( sys_rst_n => sys_rst_n, sys_clk => sys_clk, sys_clk_en => sys_clk_en, -- Frequency setting freq_i => rx_freq, -- Output pulse_o => rx_1mhz_pulse, squarewave_o => open ); tx_1mhz_pulse <= rx_1mhz_pulse; -- Create the frequency setting -- It is composed of two terms: the constant term for 1 MHz, and -- the fine adjustment term, for varying slightly above or below -- the constant value. The varying term is "pps_residusum". rx_freq <= to_unsigned(PHASE_INCR_1MHZ,rx_freq'length) + unsigned(s_resize_se(pps_residusum,rx_freq'length)); -- An input signal conditioner for low pass filtering the -- carrier squarewave clock signal signal_cleanup : multi_stage_leaky_integrator generic map( STAGES => 2, LEAK_FACTOR_BITS => 5, -- Inversely related to LP corner frequency. (Min=1) HYSTERESIS_BITS => 8, INTEGRATOR_BITS => time_cleaner_input'length ) port map( -- System Clock and Clock Enable sys_rst_n => sys_rst_n, sys_clk => sys_clk, sys_clk_en => sys_clk_en, -- Settings input => time_cleaner_input, hysteresis => str2u("10",8), -- Integration Result integrator => open, -- Conditioned Digital Output Signal output => time_cleaned ); time_cleaner_input <= to_signed(64,time_cleaner_input'length) when time_cdet_i='1' else to_signed(-64,time_cleaner_input'length); -- Select the appropriate time_rx signal time_rx <= time_cdet_i when reg_time_lp='0' else time_cleaned; -- Create IRIG time receiver state report signal with (rx_state) select rx_state_report <= "0011" when WAIT_ACTIVE, "0100" when SEEK_REF_1, "0101" when SEEK_REF_2, "0110" when RECEIVE_PULSES, "0111" when SETTLE_IN, "1000" when TRACK_IRIG, "1111" when others; -- Assert outputs when in the correct state rx_lock <= '1' when rx_state=TRACK_IRIG else '0'; lock_o <= rx_lock; active_o <= rx_active; -- tracking_o <= rx_irig_tracking when rx_state=TRACK_IRIG else rx_pps_tracking; tracking_o <= rx_irig_tracking; neverlocked_o <= rx_neverlocked; cdet_sel_o <= '1' when rx_state=SETTLE_IN or rx_state=TRACK_IRIG else cdet_sel_l; dac_clk1 : dds_clk_en_gen generic map( ACC_BITS => 24 ) port map( -- Reset, System Clock and Clock Enable sys_rst_n => sys_rst_n, sys_clk => sys_clk, sys_clk_en => sys_clk_en, -- Frequency setting freq_i => str2u("001A37",24), -- Output clk_en_o => cdet_dac_clk_en ); dac1 : pwm_unsigned generic map( PERIOD => 32, COUNT_BITS => 6 ) port map( -- Reset, System Clock and Clock Enable sys_rst_n => sys_rst_n, sys_clk => sys_clk, sys_clk_en => cdet_dac_clk_en, -- Input Data dat_i => cdet_bias, -- Output Signal dat_o => cdet_sel_l ); -- Create pulse that indicates the rising edge of receive data rx_posedge <= '1' when time_rx_r2='0' and time_rx_r1='1' else '0'; -- Create pulse that indicates the start of a new timecode pulse -- (whether carrier modulated or DC level) rx_pstart <= '1' when rx_posedge='1' and rx_lo_count>RX_EDGE_VAL else '0'; -- Select the rx_scale_factor based on carrier detect value with (to_integer(cdet_val_l)) select rx_scale_factor <= to_unsigned(1,rx_scale_factor'length) when 3, to_unsigned(10,rx_scale_factor'length) when 2, to_unsigned(100,rx_scale_factor'length) when 1, to_unsigned(100,rx_scale_factor'length) when others; -- Provide cdet_val_l as an output signal cdet_val_o <= cdet_val_l; -- This is the IRIG timecode receive process -- It handles updates to the receive time values rx_proc: process(sys_clk, sys_rst_n) begin if (sys_rst_n='0') then rx_state <= WAIT_ACTIVE; rx_nasa36 <= '0'; rx_pulse_count <= (others=>'0'); rx_group_count <= (others=>'0'); time_rx_r1 <= '0'; time_rx_r2 <= '0'; rx_raise_count <= (others=>'0'); rx_active_count <= (others=>'0'); rx_active_wdog <= (others=>'0'); rx_hi_count <= (others=>'0'); rx_lo_count <= (others=>'0'); rx_pulse_width <= (others=>'0'); pps_r1 <= '0'; pps_r2 <= '0'; rx_sr <= (others=>'0'); rx_c_count <= (others=>'0'); pps_r_count <= to_signed(PPS_R_COUNT_SEED,pps_r_count'length); pps_residusum <= (others=>'0'); rx_secs_0 <= (others=>'0'); rx_secs_1 <= (others=>'0'); rx_mins_0 <= (others=>'0'); rx_mins_1 <= (others=>'0'); rx_hours_0 <= (others=>'0'); rx_hours_1 <= (others=>'0'); rx_days_0 <= (others=>'0'); rx_days_1 <= (others=>'0'); rx_days_2 <= (others=>'0'); rx_years_0 <= (others=>'0'); rx_years_1 <= (others=>'0'); rx_active <= '0'; rx_pps_tracking <= '0'; rx_irig_tracking <= '0'; rx_neverlocked <= '1'; cdet_fixed <= '0'; cdet_ms_timer <= (others=>'0'); cdet_val_l <= (others=>'0'); rx_scale_count <= to_unsigned(1,rx_scale_count'length); cdet_bias <= DEF_R_A(cdet_bias'length-1 downto 0); reg_time_lp <= DEF_R_A(8); set_time <= '0'; rx_stat_1 <= (others=>'0'); rx_stat_2 <= (others=>'0'); rx_stat_3 <= (others=>'0'); elsif (sys_clk'event and sys_clk='1') then if (sys_clk_en='1') then -- Default values set_time <= '0'; -- pulses high to set the IRIG generator time -- Handle carrier detect millisecond timer -- Used to time the settling period when cdet_sel_o is changed if (pulse_1ms='1') then cdet_ms_timer <= cdet_ms_timer+1; end if; -- Service residual counter if (rx_1mhz_pulse='1') then pps_r_count <= pps_r_count-1; end if; -- Capture incoming carrier pulses if (rx_1mhz_pulse='1') then time_rx_r1 <= time_rx; end if; time_rx_r2 <= time_rx_r1; -- Apply selected scale factor to produce 1 MHz, 100 kHz or 10 kHz rx_sample pulses -- rx_posedge is used to synchronize the sample pulse to each new rising edge of -- the rx line. if (rx_1mhz_pulse='1' and rx_scale_count=1) or rx_posedge='1' then rx_scale_count <= rx_scale_factor; elsif (rx_1mhz_pulse='1') then rx_scale_count <= rx_scale_count-1; end if; -- An up-counter that advances each sample time, but resets when -- the rx line is high... rx_lo_count is to be thresholded for data detection. -- Keeping rx_lo_count reset when the receive line is high enables -- reception of "dc level" irig time data as well as the -- "gated carrier pulse" data from the demodulator circuit. if (time_rx_r2='1') then rx_lo_count <= (others=>'0'); elsif (rx_1mhz_pulse='1' and rx_scale_count=1) then if (rx_lo_count<255) then -- Do not let counter roll over. rx_lo_count <= rx_lo_count+1; end if; end if; -- Capture the pulse width for reporting purposes only. if (rx_pstart='1') then rx_pulse_width <= rx_lo_count; end if; -- Handle receive shift register if (rx_pstart='1') then if (rx_lo_count > RX_ZERO_VAL) then rx_sr(rx_sr'length-2 downto 0) <= rx_sr(rx_sr'length-1 downto 1); rx_sr(rx_sr'length-1) <= '0'; else rx_sr(rx_sr'length-2 downto 0) <= rx_sr(rx_sr'length-1 downto 1); rx_sr(rx_sr'length-1) <= '1'; end if; end if; -- Detect timecode activity here, by examining the rx_lo_count, and -- adjusting the sample rate to seek a sensible input signal. -- 1) If, at rx_posedge, rx_lo_count>RX_TOO_SHORT even once select a -- lower sample rate. -- 2) If, at rx_posedge, rx_lo_count<RX_EDGE_COUNT 10 times in a row, -- select a higher sample rate. -- 3) Require a duty cycle between 10% and 80% -- 4) Once locked, require rx_posedge activity during an N sample -- interval. -- If, at rx_pstart, all the above conditions are met ten -- times in a row, then the input signal and sample rate are -- considered correct, and an input "active" condition is declared. -- The "active" condition is rescinded if any of the conditions -- are not met, at any time. if (rx_1mhz_pulse='1' and rx_scale_count=1 and time_rx_r2='1') then if (rx_hi_count<((2**rx_hi_count'length)-1)) then -- Do not let counter roll over. rx_hi_count <= rx_hi_count+1; end if; end if; -- Count number of times all conditions are met if (rx_pstart='1') then rx_hi_count <= (others=>'0'); if (rx_active='0' and rx_hi_count>9 and rx_hi_count<81) then rx_active_count <= rx_active_count+1; rx_raise_count <= (others=>'0'); if (rx_active_count=9) then rx_active <= '1'; if (cdet_fixed='0' and cdet_val_l=0) then cdet_val_l <= cdet_val_l+1; end if; end if; end if; end if; -- Handle sample rate adjustments automatically, until a successful -- reception of all pulse groups is achieved. Thereafter, only lose -- lock due to watchdog. if (rx_posedge='1' and not (rx_state=SETTLE_IN or rx_state=TRACK_IRIG)) then if (rx_lo_count>RX_TOO_SHORT) then rx_active_count <= (others=>'0'); rx_active <= '0'; rx_stat_1 <= rx_stat_1+1; rx_pulse_width <= to_unsigned(16#AB#,rx_pulse_width'length); if (cdet_fixed='0' and cdet_val_l>0) then cdet_val_l <= cdet_val_l-1; -- Lower the sample rate end if; elsif (rx_lo_count<3) then rx_raise_count <= rx_raise_count+1; rx_active_count <= (others=>'0'); rx_active <= '0'; rx_stat_2 <= rx_stat_2+1; rx_pulse_width <= to_unsigned(16#AA#,rx_pulse_width'length); if (rx_raise_count=9) then rx_raise_count <= (others=>'0'); if (cdet_fixed='0' and cdet_val_l<3) then cdet_val_l <= cdet_val_l+1; -- Raise the sample rate end if; end if; end if; end if; -- Handle activity watchdog if (rx_posedge='1') then rx_active_wdog <= (others=>'0'); -- reset activity watchdog end if; if (rx_1mhz_pulse='1' and rx_scale_count=1) then rx_active_wdog <= rx_active_wdog+1; if (rx_active='1' and rx_active_wdog=200) then cdet_val_l <= (others=>'0'); rx_nasa36 <= '0'; rx_active_count <= (others=>'0'); rx_active <= '0'; rx_stat_3 <= rx_stat_3+1; end if; end if; -- Handle state transitions case (rx_state) is when WAIT_ACTIVE => if (rx_active='1') then rx_state <= SEEK_REF_1; end if; when SEEK_REF_1 => if (rx_pstart='1') then if (rx_sr="0111110000") then rx_pulse_count <= to_unsigned(0,rx_pulse_count'length); rx_group_count <= to_unsigned(0,rx_group_count'length); rx_state <= RECEIVE_PULSES; rx_nasa36 <= '1'; elsif (rx_lo_count<=RX_ONE_VAL) then rx_state <= SEEK_REF_2; end if; end if; when SEEK_REF_2 => if (rx_pstart='1') then if (rx_lo_count<=RX_ONE_VAL) then rx_pulse_count <= to_unsigned(0,rx_pulse_count'length); rx_group_count <= to_unsigned(0,rx_group_count'length); rx_state <= RECEIVE_PULSES; else rx_state <= SEEK_REF_1; end if; end if; when RECEIVE_PULSES => if (rx_pstart='1') then -- Service the pulse, group and frame counters -- Must obtain "group lock" before using received data rx_pulse_count <= rx_pulse_count+1; -- default action if (rx_pulse_count=9) then rx_pulse_count <= (others=>'0'); rx_group_count <= rx_group_count+1; -- Default is to advance the count. if (rx_group_count=9) then rx_group_count <= (others=>'0'); cdet_ms_timer <= (others=>'0'); rx_state <= SETTLE_IN; -- At the end of the full reading, move on rx_neverlocked <= '0'; set_time <= '1'; end if; -- Initialize the time fields as they are received... case (to_integer(rx_group_count)) is when 0 => if (rx_sr(4 downto 1)<9) then rx_secs_0 <= rx_sr(4 downto 1)+1; else rx_state <= SEEK_REF_1; -- Adding one during rollover case is too hard. Just try again later. end if; if (rx_nasa36='0') then rx_secs_1 <= rx_sr(8 downto 6); end if; when 1 => if (rx_nasa36='0') then rx_mins_0 <= rx_sr(3 downto 0); rx_mins_1 <= rx_sr(7 downto 5); else rx_secs_1 <= rx_sr(2 downto 0); end if; when 2 => if (rx_nasa36='0') then rx_hours_0 <= rx_sr(3 downto 0); rx_hours_1 <= rx_sr(6 downto 5); else rx_mins_0 <= rx_sr(3 downto 0); end if; when 3 => if (rx_nasa36='0') then rx_days_0 <= rx_sr(3 downto 0); rx_days_1 <= rx_sr(8 downto 5); else rx_mins_1 <= rx_sr(2 downto 0); end if; when 4 => if (rx_nasa36='0') then rx_days_2 <= rx_sr(1 downto 0); else rx_hours_0 <= rx_sr(3 downto 0); end if; when 5 => if (cdet_val_l/=3) then rx_years_0 <= rx_sr(3 downto 0); rx_years_1 <= rx_sr(8 downto 5); end if; if (rx_nasa36='1') then rx_hours_1 <= rx_sr(1 downto 0); end if; when 6 => if (cdet_val_l=3) then rx_years_0 <= rx_sr(3 downto 0); rx_years_1 <= rx_sr(8 downto 5); end if; if (rx_nasa36='1') then rx_days_0 <= rx_sr(3 downto 0); end if; when 7 => if (rx_nasa36='1') then rx_days_1 <= rx_sr(3 downto 0); end if; when 8 => if (rx_nasa36='1') then rx_days_2 <= rx_sr(1 downto 0); end if; when 9 => null; when others => null; end case; end if; end if; -- if rx_pstart -- Spend some time waiting for the demodulator circuit to settle. -- There are capacitors in it which need to discharge before it works -- following a switching event. when SETTLE_IN => if (cdet_ms_timer=CDET_SETTLE_TIME) then rx_state <= TRACK_IRIG; case (to_integer(cdet_val_l)) is when 3 => rx_c_count <= to_unsigned(RX_C_SEED_G,rx_c_count'length); when 2 => rx_c_count <= to_unsigned(RX_C_SEED_A,rx_c_count'length); when 1 => rx_c_count <= to_unsigned(RX_C_SEED_B,rx_c_count'length); when others => rx_c_count <= to_unsigned(RX_C_SEED_B,rx_c_count'length); end case; end if; when TRACK_IRIG => -- Tracking on received IRIG time signal based PPS -- Service carrier pulse countdown. -- Counter is reloaded by statements below. if (rx_posedge='1') then if (rx_c_count=1) then case (to_integer(cdet_val_l)) is when 3 => rx_c_count <= to_unsigned(RX_C_SEED_G,rx_c_count'length); when 2 => rx_c_count <= to_unsigned(RX_C_SEED_A,rx_c_count'length); when 1 => rx_c_count <= to_unsigned(RX_C_SEED_B,rx_c_count'length); when others => rx_c_count <= to_unsigned(RX_C_SEED_B,rx_c_count'length); end case; -- Service time tracking counters pps_r_count <= to_signed(PPS_R_COUNT_SEED,pps_r_count'length); if (abs(pps_r_count) > PPS_TRACK_LIMIT) then rx_irig_tracking <= '0'; else pps_residusum <= pps_residusum + s_resize_se(s_resize_l(pps_r_count,pps_r_count'length-1),pps_residusum'length); -- divide by two rx_irig_tracking <= '1'; end if; else rx_c_count <= rx_c_count-1; end if; end if; --when others => -- fsm_state <= IDLE; end case; -- Restart acquisition if there is no incoming activity -- It is not helpful to increment the stats counter for this... if (rx_state/=WAIT_ACTIVE and rx_active='0') then pps_r_count <= to_signed(PPS_R_COUNT_SEED,pps_r_count'length); rx_irig_tracking <= '0'; cdet_ms_timer <= (others=>'0'); cdet_val_l <= (others=>'0'); rx_state <= WAIT_ACTIVE; end if; -- Metastability mitigation chain. -- Also used to detect rising edge of pps_i pps_r1 <= pps_i; pps_r2 <= pps_r1; -- When not tracking IRIG input, track PPS input if present if (rx_state/=TRACK_IRIG and pps_r2='0' and pps_r1='1') then if (abs(pps_r_count) > PPS_TRACK_LIMIT) then rx_pps_tracking <= '0'; else pps_r_count <= to_signed(PPS_R_COUNT_SEED,pps_r_count'length); pps_residusum <= pps_residusum + s_resize_se(s_resize_l(pps_r_count,pps_r_count'length-1),pps_residusum'length); -- divide by two rx_pps_tracking <= '1'; end if; end if; -- Reset stats registers if (sel_i='1' and we_i='1' and ack_l='1') then -- Handle bus writes to registers if (adr_i=7) then cdet_fixed <= dat_i(10); if (cdet_fixed='1') then rx_nasa36 <= dat_i(9); cdet_val_l <= dat_i(cdet_val_l'length-1 downto 0); end if; end if; if (adr_i=8) then rx_stat_1 <= (others=>'0'); rx_stat_2 <= (others=>'0'); rx_stat_3 <= (others=>'0'); end if; if (adr_i=10) then cdet_bias <= dat_i(cdet_bias'length-1 downto 0); reg_time_lp <= dat_i(8); end if; end if; end if; -- sys_clk_en end if; -- sys_clk end process; -- A pulse that occurs once every millisecond, based on transmit signals. pulse_1ms <= '1' when (micro_bcd_0=9 and micro_bcd_1=9 and micro_bcd_2=9 and micro_bcd_3=9 and tx_1mhz_pulse='1') else '0'; end beh;