Commit f402d342 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

wr_softpll_ng: initial version of DMTD-based external reference PLL

parent 4177c4b6
etherbone-core @ b29565ac
Subproject commit b29565ac63ca92987cd9a9a754b6add857fc5351
general-cores @ 45cf9797
Subproject commit 45cf97977fdb5e451491a5a24af408ea6df4858a
gn4124-core @ 5fd1a8b1
Subproject commit 5fd1a8b14063464eef714be14d79d36550080cb4
......@@ -6,7 +6,7 @@
-- Author : Tomasz Wlostowski
-- Company : CERN BE-Co-HT
-- Created : 2010-02-25
-- Last update: 2013-07-29
-- Last update: 2014-07-15
-- Platform : FPGA-generic
-- Standard : VHDL '93
-------------------------------------------------------------------------------
......@@ -132,7 +132,6 @@ architecture rtl of dmtd_with_deglitcher is
signal stab_cntr : unsigned(15 downto 0);
signal free_cntr : unsigned(g_counter_bits-1 downto 0);
signal in_d0, in_d1 : std_logic;
signal s_one : std_logic;
signal clk_in : std_logic;
......
......@@ -6,7 +6,7 @@
-- Author : Tomasz Wlostowski
-- Company : CERN BE-Co-HT
-- Created : 2010-09-02
-- Last update: 2013-08-05
-- Last update: 2014-07-15
-- Platform : FPGA-generics
-- Standard : VHDL
-------------------------------------------------------------------------------
......@@ -180,49 +180,14 @@ architecture behavioral of wr_pps_gen is
signal retime_counter : unsigned(4 downto 0);
signal pps_valid_int : std_logic;
signal pps_out_int : std_logic;
signal pps_out_int : std_logic;
signal pps_in_refclk : std_logic;
component chipscope_icon
port (
CONTROL0 : inout std_logic_vector(35 downto 0));
end component;
component chipscope_ila
port (
CONTROL : inout std_logic_vector(35 downto 0);
CLK : in std_logic;
TRIG0 : in std_logic_vector(31 downto 0);
TRIG1 : in std_logic_vector(31 downto 0);
TRIG2 : in std_logic_vector(31 downto 0);
TRIG3 : in std_logic_vector(31 downto 0));
end component;
signal control0 : std_logic_vector(35 downto 0);
signal trig0, trig1, trig2, trig3 : std_logic_vector(31 downto 0);
begin -- behavioral
--CS_ICON : chipscope_icon
-- port map (
-- CONTROL0 => CONTROL0);
--CS_ILA : chipscope_ila
-- port map (
-- CONTROL => CONTROL0,
-- CLK => clk_sys_i,
-- TRIG0 => TRIG0,
-- TRIG1 => TRIG1,
-- TRIG2 => TRIG2,
-- TRIG3 => TRIG3);
TRIG0(cntr_pps_ext'length-1 downto 0) <= std_logic_vector(cntr_pps_ext);
TRIG1(0) <= pps_ext_retimed;
TRIG1(1) <= pps_in_i;
TRIG1(6 downto 2) <= std_logic_vector(retime_counter);
TRIG1(7) <= pps_ext_d0;
resized_addr(4 downto 0) <= wb_adr_i;
resized_addr(c_wishbone_address_width-1 downto 5) <= (others => '0');
......@@ -250,7 +215,7 @@ begin -- behavioral
sl_stall_o => wb_stall_o);
sync_reset_refclk : gc_sync_ffs
U_Sync_reset_refclk : gc_sync_ffs
generic map (
g_sync_edge => "positive")
port map (
......@@ -261,6 +226,15 @@ begin -- behavioral
npulse_o => open,
ppulse_o => open);
U_Sync_pps_refclk : gc_sync_ffs
generic map (
g_sync_edge => "positive")
port map (
clk_i => clk_ref_i,
rst_n_i => '1',
data_i => pps_in_i,
ppulse_o => pps_in_refclk);
ppsg_cntr_nsec <= std_logic_vector(cntr_nsec);
ppsg_cntr_utclo <= std_logic_vector(cntr_utc(31 downto 0));
......@@ -290,80 +264,36 @@ begin -- behavioral
end if;
end process;
gen_with_external_clock_input : if(g_with_ext_clock_input) generate
-- retime the external PPS pulse. The output (pps_ext_retimed) is:
-- single clk_ext_i cycle-wide
-- produced one cycle in advance with respect to the original PPS
p_retime_external_pps : process(clk_ext_i)
begin
if rising_edge(clk_ext_i) then
if rst_n_i = '0' then
cntr_pps_ext <= (others => '0');
pps_ext_d0 <= '0';
pps_ext_retimed <= '0';
else
pps_ext_d0 <= pps_in_i;
if(cntr_pps_ext = g_ext_clock_rate-1) then
pps_ext_retimed <= '1';
else
pps_ext_retimed <= '0';
end if;
if(pps_in_i = '1' and pps_ext_d0 = '0') then
cntr_pps_ext <= to_unsigned(1, cntr_pps_ext'length);
elsif(cntr_pps_ext /= g_ext_clock_rate) then
cntr_pps_ext <= cntr_pps_ext + 1;
end if;
end if;
end if;
end process;
p_retime_counter : process(clk_ref_i)
begin
if falling_edge(clk_ref_i) then
if rst_synced_refclk = '0' or sync_in_progress = '0' or pps_ext_retimed = '0' then
retime_counter <= (others => '0');
else
retime_counter <= retime_counter + 1;
end if;
end if;
end process;
gen_without_external_clock_input : if(not g_with_ext_clock_input) generate
ext_sync_p <= '0';
end generate gen_without_external_clock_input;
gen_with_external_clock_input : if(g_with_ext_clock_input) generate
-- Warning! this state machine inputs pps_ext_retimed signal,
-- which is produced in different clock domain than clk_ref_i.
-- Run only when EXT channel of the SoftPLL is LOCKED!
p_external_sync : process(clk_ref_i)
begin
if falling_edge(clk_ref_i) then
if(rst_synced_refclk = '0') then
ext_sync_p <= '0';
sync_in_progress <= '0';
ppsg_escr_sync_in <= '0';
else
if(ppsg_escr_sync_load = '1') then
sync_in_progress <= ppsg_escr_sync_out;
ppsg_escr_sync_in <= '0';
end if;
-- retime counter == last faster clock edge inside the retimed PPS
-- pulse -> we should sync ourselves
if(sync_in_progress = '1' and pps_ext_retimed = '1' and retime_counter = (g_ref_clock_rate / g_ext_clock_rate - 1)) then
ext_sync_p <= '1';
sync_in_progress <= '0';
ppsg_escr_sync_in <= '1';
else
ext_sync_p <= '0';
if(sync_in_progress = '1' and pps_in_refclk = '1')
then
ext_sync_p <= '1';
sync_in_progress <= '0';
ppsg_escr_sync_in <= '1';
else
ext_sync_p <= '0';
end if;
end if;
end if;
end if;
end process;
end generate gen_with_external_clock_input;
-- Nanosecond counter. Counts from 0 to c_PERIOD-1 every clk_ref_i cycle.
......
files = ["spll_period_detect.vhd",
"spll_bangbang_pd.vhd",
# "spll_bangbang_pd.vhd",
"spll_wbgen2_pkg.vhd",
"spll_aligner.vhd",
"wr_softpll_ng.vhd",
"xwr_softpll_ng.vhd",
"softpll_pkg.vhd",
......
......@@ -6,18 +6,24 @@ use ieee.numeric_std.all;
package softpll_pkg is
constant c_softpll_max_aux_clocks : integer := 8;
type t_softpll_phase_detector_type is (CH_DDMTD, CH_BANGBANG);
type t_softpll_channel_config_array is array(0 to c_softpll_max_aux_clocks-1) of t_softpll_phase_detector_type;
constant c_softpll_default_channel_config : t_softpll_channel_config_array := (others => CH_DDMTD);
-- External 10 MHz input divider parameters.
constant c_softpll_ext_div_ref : integer := 8;
constant c_softpll_ext_div_fb : integer := 50;
constant c_softpll_ext_log2_gating : integer := 13;
constant c_softpll_out_status_off : std_logic_vector(3 downto 0) := "0000";
constant c_softpll_out_status_locking : std_logic_vector(3 downto 0) := "0001";
constant c_softpll_out_status_locked : std_logic_vector(3 downto 0) := "0010";
constant c_softpll_out_status_aligning : std_logic_vector(3 downto 0) := "0011";
constant c_softpll_out_status_holdover : std_logic_vector(3 downto 0) := "0100";
end package;
package body softpll_pkg is
......
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.gencores_pkg.all;
entity spll_aligner is
generic (
g_counter_width : integer := 28;
g_ref_clock_rate : integer := 125000000;
g_in_clock_rate : integer := 10000000;
g_sample_rate : integer := 100
);
port (
clk_sys_i : in std_logic;
clk_in_i : in std_logic;
clk_ref_i : in std_logic;
rst_n_sys_i : in std_logic;
pps_ext_a_i : in std_logic;
pps_csync_p1_i : in std_logic;
sample_cref_o : out std_logic_vector(g_counter_width-1 downto 0);
sample_cin_o : out std_logic_vector(g_counter_width-1 downto 0);
sample_pps_o : out std_logic;
sample_valid_o : out std_logic;
sample_ack_i : in std_logic
);
end spll_aligner;
architecture rtl of spll_aligner is
constant c_div_ticks : integer := g_ref_clock_rate / g_sample_rate;
signal cnt_ref_bin, cnt_in_bin, cnt_in_bin_x : unsigned(g_counter_width-1 downto 0);
signal cnt_in_gray, cnt_in_gray_x, cnt_in_gray_xd : std_logic_vector(g_counter_width-1 downto 0);
signal cnt_ref_div : unsigned(g_counter_width-1 downto 0);
signal pps_ext_p, pps_ext_d0 : std_logic;
signal rst_n_in, rst_n_ref : std_logic;
signal ref_div_p : std_logic;
signal sample_ready_p : std_logic;
begin
U_Reset_IN : gc_sync_ffs
port map (
clk_i => clk_in_i,
rst_n_i => '1',
data_i => rst_n_sys_i,
synced_o => rst_n_in);
U_Reset_REF : gc_sync_ffs
port map (
clk_i => clk_ref_i,
rst_n_i => '1',
data_i => rst_n_sys_i,
synced_o => rst_n_ref);
p_ref_counter : process(clk_ref_i)
begin
if rising_edge(clk_ref_i) then
if pps_csync_p1_i = '1' or rst_n_ref = '0' then
cnt_ref_bin <= to_unsigned(0, g_counter_width);
elsif(cnt_ref_bin = g_ref_clock_rate - 1) then
cnt_ref_bin <= (others => '0');
else
cnt_ref_bin <= cnt_ref_bin + 1;
end if;
end if;
end process;
p_samplerate_divider : process(clk_ref_i)
begin
if rising_edge(clk_ref_i) then
if pps_csync_p1_i = '1' or rst_n_ref = '0' then
ref_div_p <= '0';
cnt_ref_div <= to_unsigned(0, g_counter_width);
elsif (cnt_ref_div = c_div_ticks - 2) then
ref_div_p <= '1';
cnt_ref_div <= cnt_ref_div + 1;
elsif (cnt_ref_div = c_div_ticks - 1) then
ref_div_p <= '0';
cnt_ref_div <= (others => '0');
else
ref_div_p <= '0';
cnt_ref_div <= cnt_ref_div + 1;
end if;
end if;
end process;
p_delay_ext_pps : process(clk_in_i)
begin
if rising_edge(clk_in_i) then
pps_ext_d0 <= pps_ext_a_i;
end if;
end process;
pps_ext_p <= not pps_ext_d0 and pps_ext_a_i;
p_in_counter : process(clk_in_i)
begin
if rising_edge(clk_in_i) then
if pps_ext_p = '1' or rst_n_in = '0' then
cnt_in_bin <= to_unsigned(2, g_counter_width);
elsif(cnt_in_bin = g_in_clock_rate - 1) then
cnt_in_bin <= (others => '0');
else
cnt_in_bin <= cnt_in_bin + 1;
end if;
end if;
end process;
p_in_bin2gray : process (clk_in_i)
begin
if rising_edge(clk_in_i) then
cnt_in_gray <= f_gray_encode (std_logic_vector(cnt_in_bin));
end if;
end process;
p_sample_difference : process(clk_ref_i)
begin
if rising_edge(clk_ref_i) then
cnt_in_gray_x <= cnt_in_gray;
cnt_in_gray_xd <= cnt_in_gray_x;
if(ref_div_p = '1') then
sample_cin_o <= f_gray_decode(cnt_in_gray_xd, 1);
sample_cref_o <= std_logic_vector (cnt_ref_bin);
end if;
end if;
end process;
U_sync_sampling : gc_pulse_synchronizer2
port map (
clk_in_i => clk_ref_i,
rst_in_n_i => rst_n_ref,
clk_out_i => clk_sys_i,
rst_out_n_i => rst_n_sys_i,
d_p_i => ref_div_p,
q_p_o => sample_ready_p);
p_gen_sample_valid : process(clk_sys_i)
begin
if rising_edge(clk_sys_i) then
if rst_n_sys_i = '0' then
sample_valid_o <= '0';
else
if sample_ready_p = '1' then
sample_valid_o <= '1';
elsif sample_ack_i = '1' then
sample_valid_o <= '0';
end if;
end if;
end if;
end process;
end rtl;
This diff is collapsed.
......@@ -54,17 +54,15 @@ peripheral {
reg {
name = "External Clock Control Register";
prefix = "ECCR";
prefix = "ECCR";
field {
name = "Enable External Clock BB Detector";
field {
name = "Enable External Clock PLL";
prefix = "EXT_EN";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
field {
name = "External Clock Input Available";
description = "1: This instance of wr_softpll_ng supports external 10MHz clock input\
......@@ -75,28 +73,6 @@ peripheral {
access_dev = WRITE_ONLY;
};
field {
name = "Enable PPS/phase alignment";
description = "write 1: starts aligning the external and local oscillator clock edges to be in phase\
right after the pulse on SYNC (PPS) input.\
write 0: no effect.";
prefix = "ALIGN_EN";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
field {
name = "PPS/phase alignment done";
description = "1: phase alignment triggered by writing to ALIGN_EN done.\
0: phase alignment in progress.";
prefix = "ALIGN_DONE";
type = BIT;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "External Clock Reference Present";
description = "1: Reference clock present on the input\
......@@ -106,54 +82,121 @@ peripheral {
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
reg {
name = "Aligner Control Register";
prefix = "AL_CR";
field {
name = "Aligner sample valid/select on channel";
prefix = "VALID";
type = SLV;
size = 9;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "Aligner required on channel";
prefix = "REQUIRED";
type = SLV;
size = 9;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
reg {
name = "Aligner Counter REF register";
prefix = "AL_CREF";
field {
name = "Aligner reference counter";
type = SLV;
size = 32;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
reg {
name = "Aligner Counter IN register";
prefix = "AL_CIN";
field {
name = "Aligner reference counter";
type = SLV;
size = 32;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
reg {
align = 4;
name = "Output Channel Control Register";
prefix = "OCCR";
name = "DMTD VCO Frequency";
prefix = "F_DMTD";
field {
align = 8;
name = "Output Channel HW enable flag";
prefix = "OUT_EN";
name = "FREQ";
prefix = "FREQ";
type = SLV;
size = 8;
size = 28;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "Output Channel locked flag";
prefix = "OUT_LOCK";
type = SLV;
size = 8;
name = "VALID";
prefix = "VALID";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
};
field {
name = "Output Channel Phase Detector Type";
description = "Phase detector type used by corresponding output: 0 = DDMTD, 1 = BangBang";
prefix = "OUT_DET_TYPE";
reg {
name = "REF VCO Frequency";
prefix = "F_REF";
field {
name = "FREQ";
prefix = "FREQ";
type = SLV;
size = 8;
size = 28;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "VALID";
prefix = "VALID";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
};
reg {
name = "Reference Channel Tagging Enable Register";
prefix = "RCER";
name = "EXT VCO Frequency";
prefix = "F_EXT";
field {
name = "Reference Channel Enable";
description = "write 1: enables tag generation on the input channel corresponding to the written bit\
write 0: disables tag generation";
name = "FREQ";
prefix = "FREQ";
type = SLV;
size = 32;
size = 28;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "VALID";
prefix = "VALID";
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
......@@ -161,21 +204,62 @@ peripheral {
};
reg {
name = "Output Channel Tagging Enable Register";
prefix = "OCER";
align = 4;
name = "Output Channel Control Register";
prefix = "OCCR";
field {
name = "Output Channel Enable";
description = "write 1: enables tag generation on the output channel corresponding to the written bit\
write 0: disables tag generation";
align = 8;
name = "Output Channel HW enable flag";
prefix = "OUT_EN";
type = SLV;
size = 8;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "Output Channel locked flag";
prefix = "OUT_LOCK";
type = SLV;
size = 8;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
access_dev = READ_ONLY;
};
};
reg {
name = "Reference Channel Tagging Enable Register";
prefix = "RCER";
field {
name = "Reference Channel Enable";
description = "write 1: enables tag generation on the input channel corresponding to the written bit\
write 0: disables tag generation";
type = SLV;
size = 32;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
};
reg {
name = "Output Channel Tagging Enable Register";
prefix = "OCER";
field {
name = "Output Channel Enable";
description = "write 1: enables tag generation on the output channel corresponding to the written bit\
write 0: disables tag generation";
type = SLV;
size = 8;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
};
reg {
align = 8;
name = "Helper DAC Output";
......@@ -240,41 +324,8 @@ peripheral {
};
};
reg {
name = "Counter Resync Register - input channels";
prefix = "CRR_IN";
field {
name = "Counter Resync";
description = "write 1: triggers resynchronization of this channel's DDMTD free-running counter with Out Clock 0\
write 0: no effect\
read 1: resync in progress\
read 0: resync done";
size = 32;
type = SLV;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
};
reg {
name = "Counter Resync Register - output channels";
prefix = "CRR_OUT";
field {
name = "Counter Resync";
description = "write 1: triggers resynchronization of this channel's DDMTD free-running counter with Out Clock 0\
write 0: no effect";
size = 16;
type = SLV;
access_bus = READ_WRITE;
access_dev = READ_WRITE;
load = LOAD_EXT;
};
};
fifo_reg {
fifo_reg {
name = "Debug FIFO Register - Host side";
prefix = "DFR_HOST";
direction = CORE_TO_BUS;
......@@ -331,43 +382,6 @@ peripheral {
};
};
reg {
name = "Aux clock configuration register";
prefix = "AUX_CR";
field {
name = "Aux output select";
prefix = "AUX_SEL";
size = 3;
type = PASS_THROUGH;
};
field {
name = "BB reference divider";
description = "Reference clock division factor. Applicable only for aux channels with BB phase detector.";
prefix = "DIV_REF";