diff --git a/replacements/emp-fwk/boards/serenity/dc_ku15p/firmware/hdl/emp_device_decl_sm1_v1.vhd b/replacements/emp-fwk/boards/serenity/dc_ku15p/firmware/hdl/emp_device_decl_sm1_v1.vhd
index d73c017f37f49a63e397b681cbc01031acd70643..9a9ce1692dc406628ef1f398fc710d00bfa831c0 100644
--- a/replacements/emp-fwk/boards/serenity/dc_ku15p/firmware/hdl/emp_device_decl_sm1_v1.vhd
+++ b/replacements/emp-fwk/boards/serenity/dc_ku15p/firmware/hdl/emp_device_decl_sm1_v1.vhd
@@ -7,8 +7,6 @@
 library IEEE;
 use IEEE.STD_LOGIC_1164.all;
 
-use work.tcds2_interface_pkg;
-
 use work.emp_framework_decl.all;
 
 
@@ -16,7 +14,9 @@ package emp_device_decl is
 
   constant BOARD_DESIGN_ID : std_logic_vector(7 downto 0) := X"41";
 
-  constant TCDS2_MGT_TYPE : tcds2_interface_pkg.mgt_type_t := tcds2_interface_pkg.MGT_TYPE_GTYE4;
+  constant TCDS2_MGT_TYPE : io_gt_kind_t     := io_gty;
+  constant TCDS2_SPEED    : io_tcds2_speed_t := io_tcds2_10g;
+
 
   constant N_REGION     : integer := 18;
   constant N_REFCLK     : integer := 14;
diff --git a/replacements/emp-fwk/components/top/firmware/hdl/emp_top_pcie_both_ttc.vhd b/replacements/emp-fwk/components/top/firmware/hdl/emp_top_pcie_both_ttc.vhd
index 3d5e02b00af5a34691d92c1f12c81055e19ae684..4325d53fdd4647e92239ece5bceb818d1ba7c0e2 100644
--- a/replacements/emp-fwk/components/top/firmware/hdl/emp_top_pcie_both_ttc.vhd
+++ b/replacements/emp-fwk/components/top/firmware/hdl/emp_top_pcie_both_ttc.vhd
@@ -97,10 +97,10 @@ architecture rtl of top is
   signal rsts_aux : std_logic_vector(2 downto 0);
 
   -- TTC signals  
-  signal ttc_l1a, ttc_l1a_dist, dist_lock, oc_flag, ec_flag, payload_bc0, ttc_l1a_throttle : std_logic;
+  signal ttc_l1a, ttc_l1a_dist, dist_lock, oc_flag, ec_flag, ttc_l1a_throttle              : std_logic;
   signal ttc_cmd, ttc_cmd_dist                                                             : ttc_cmd_t;
   signal bunch_ctr                                                                         : bctr_t;
-  signal evt_ctr, orb_ctr                                                                  : eoctr_t;
+  signal orb_ctr                                                                           : eoctr_t;
 
   -- Others
   signal nuke, soft_rst : std_logic;
@@ -243,7 +243,7 @@ begin
       ttc_l1a_dist => ttc_l1a_dist,
       dist_lock    => dist_lock,
       bunch_ctr    => bunch_ctr,
-      evt_ctr      => evt_ctr,
+      evt_ctr      => (others => '0'),
       orb_ctr      => orb_ctr,
       oc_flag      => oc_flag,
       ec_flag      => ec_flag,
@@ -269,10 +269,6 @@ begin
       ctrs_out    => ctrs,
       rst_out     => rst_loc,
       clken_out   => clken_loc,
-      --cap_bus     => cap_bus,
-      --daq_bus_in  => daq_bus_top,
-      --daq_bus_out => daq_bus_bot,
-      payload_bc0 => payload_bc0,
       refclkp     => refclkp,
       refclkn     => refclkn,
       clkmon      => clkmon,
@@ -290,6 +286,7 @@ begin
       rst         => ipb_rst,
       ipb_in      => ipb_w_array(N_SLV_PAYLOAD),
       ipb_out     => ipb_r_array(N_SLV_PAYLOAD),
+      clk40       => clk40,	        
       clk_payload => clks_aux,
       rst_payload => rsts_aux,
       clk_p       => clk_p,
diff --git a/replacements/emp-fwk/components/top/firmware/ucf/clock_constraints_common.tcl b/replacements/emp-fwk/components/top/firmware/ucf/clock_constraints_common.tcl
new file mode 100644
index 0000000000000000000000000000000000000000..fb9fdc0fe2a22ed881a0b2d5809858c7e6df4a02
--- /dev/null
+++ b/replacements/emp-fwk/components/top/firmware/ucf/clock_constraints_common.tcl
@@ -0,0 +1,52 @@
+# 40MHz & payload I/O clocks derived from board-local source (for tests without external clock source)
+create_generated_clock -name clk_40_pseudo_i [get_pins ttc/bufgce_clk_40_rx/O]
+create_generated_clock -name clk_40_pseudo   -source [get_pins ttc/clocks/mmcm/CLKIN2] [get_pins ttc/clocks/mmcm/CLKOUT1]
+create_generated_clock -name clk_payload_pseudo -source [get_pins ttc/clocks/mmcm/CLKIN2] [get_pins ttc/clocks/mmcm/CLKOUT3]
+
+# Names of the LHC-synchronised derived clocks that are used by payload and infra (outside of TTC block)
+lappend lMainInternalClocks clk_40_pseudo clk_payload_pseudo
+
+
+if {[llength [get_pins -quiet ttc/gen_master_legacy.osc_clock/mmcm/CLKOUT1]] != 0} {
+    create_generated_clock -name clk_40_extern_i0 [get_pins ttc/gen_master_legacy.osc_clock/gen_mmcm/mmcm/CLKOUT1]
+}
+
+if {[llength [get_pins -quiet ttc/gen_master_tcds2.tcds2/tcds2_interface/bufgce_clk_40_rx/O]] != 0} {
+    create_generated_clock -name clk_40_extern_i1 [get_pins ttc/gen_master_tcds2.tcds2/tcds2_interface/bufgce_clk_40_rx/O]
+}
+
+foreach i [list 0 1] {
+    if {[llength [get_clocks -quiet clk_40_extern_i$i]] != 0 } {
+        create_generated_clock -name clk_40_extern$i -master_clock [get_clocks clk_40_extern_i$i] [get_pins ttc/clocks/mmcm/CLKOUT1]
+        # Payload I/O clock derived from external oscillator
+        create_generated_clock -name clk_payload_extern$i -master_clock [get_clocks clk_40_extern_i$i] [get_pins ttc/clocks/mmcm/CLKOUT3]
+
+        lappend lMainInternalClocks clk_40_extern_i$i clk_40_extern$i clk_payload_extern$i
+    }
+}
+
+
+# Clock groups: Asynchronous
+puts "lMainInternalClocks = $lMainInternalClocks"
+set_clock_groups -asynch -group [get_clocks axi_clk] -group [get_clocks -include_generated_clocks ipbus_clk]
+set_clock_groups -asynch -group [get_clocks -include_generated_clocks ipbus_clk] -group $lMainInternalClocks
+
+# Clock groups: Logically exclusive
+# The 40MHz, 160MHz and payload clocks used across the chip are generated by an MMCM with two clock sources:
+#  * an externally-provided LHC clock (typically from TCDS), which itself could come from one of two interfaces (clk_40_externN); and
+#  * an internally-generated 'pseudo-LHC' clock (clk_40_pseudo)
+# Must set the copies of the clocks generated from those sources as logically exclusive
+foreach lMasterA [list clk_40_pseudo_i clk_40_pseudo_i clk_40_extern_i0] lMasterB [list clk_40_extern_i0 clk_40_extern_i1 clk_40_extern_i1] {
+    if {[llength [get_clocks -quiet [list $lMasterA $lMasterB]]] == 2} {
+        puts "Declaring clocks derived from $lMasterA and $lMasterB as logically exclusive"
+        set_clock_groups -logically_exclusive -group [get_clocks -filter "MASTER_CLOCK == $lMasterA"] -group [get_clocks -filter "MASTER_CLOCK == $lMasterB"]
+    }
+}
+
+
+# Remove timing constraints for heartbeat LED output port
+set_false_path -to [get_ports -regexp "(.+_led|led_.+)"]
+
+# Print clock report for the record (in case build fails, or need to debug clock-related issues)
+report_clocks
+
diff --git a/replacements/emp-fwk/components/top/firmware/ucf/clock_declarations_pcie.tcl b/replacements/emp-fwk/components/top/firmware/ucf/clock_declarations_pcie.tcl
new file mode 100644
index 0000000000000000000000000000000000000000..e49c9d01bf70044ca92097b07f2b08af5d7cfb57
--- /dev/null
+++ b/replacements/emp-fwk/components/top/firmware/ucf/clock_declarations_pcie.tcl
@@ -0,0 +1,12 @@
+
+# 100 Mhz PCIe system clock
+create_clock -period 10.000 -name pcie_sys_clk [get_ports pcie_sys_clk_p]
+
+# IPbus clock
+create_generated_clock -name ipbus_clk -source [get_pins infra/clocks/mmcm/CLKIN1] [get_pins infra/clocks/mmcm/CLKOUT1]
+
+# AXI clock
+create_generated_clock -name axi_clk [get_pins -hierarchical -filter {NAME =~infra/dma/xdma/*/phy_clk_i/bufg_gt_userclk/O}]
+
+# Approx 40MHz clock derived from AXI clock (for tests without external clock source)
+#create_generated_clock -name clk_40_pseudo_i [get_pins infra/clocks/mmcm/CLKOUT2]
diff --git a/replacements/emp-fwk/components/ttc/firmware/hdl/emp_ttc.vhd b/replacements/emp-fwk/components/ttc/firmware/hdl/emp_ttc.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..bf1c409c13f1737afb61e6a7174fac6e8e9c0552
--- /dev/null
+++ b/replacements/emp-fwk/components/ttc/firmware/hdl/emp_ttc.vhd
@@ -0,0 +1,589 @@
+-- ttc
+--
+-- TTC decoder, counters, LHC clock distribution, etc
+--
+-- Dave Newbold, June 2013
+
+library IEEE;
+use IEEE.STD_LOGIC_1164.all;
+use ieee.numeric_std.all;
+
+library unisim;
+use unisim.VComponents.all;
+
+use work.ipbus.all;
+use work.ipbus_reg_types.all;
+use work.ipbus_decode_emp_ttc.all;
+use work.emp_ttc_decl.all;
+use work.emp_ttc_utils;
+
+use work.emp_device_decl;
+use work.emp_project_decl.all;
+
+use work.tcds2_interface_pkg;
+use work.tcds2_streams_pkg;
+
+
+-------------------------------------------------------------------------------
+entity emp_ttc is
+  generic(
+    ENABLE_LEGACY_TTC : boolean;
+    ENABLE_TCDS2      : boolean;
+    EXT_CLK_FREQ      : real
+    );
+  port(
+    -- ipbus clock & rst
+    clk_ipb      : in  std_logic;
+    rst_ipb      : in  std_logic;
+    -- IPBus
+    ipb_in       : in  ipb_wbus;
+    ipb_out      : out ipb_rbus;
+    -- 125MHz clock
+    clk125       : in  std_logic;
+    -- internal pseudo-40MHz clock
+    clk40ish_in  : in  std_logic;
+    -- clock outputs
+    clk40        : out std_logic;
+    -- clock domain reset outputs
+    rst40        : out std_logic;
+    clk_p        : out std_logic;
+    rst_p        : out std_logic;
+    clks_aux     : out std_logic_vector(2 downto 0);
+    rsts_aux     : out std_logic_vector(2 downto 0);
+    -- Legacy TTC interface
+    ttc_clk_p    : in  std_logic;
+    ttc_clk_n    : in  std_logic;
+    ttc_rx_p     : in  std_logic;
+    ttc_rx_n     : in  std_logic;
+    -- TCDS2 interface
+    tcds_clk_p   : in  std_logic;
+    tcds_clk_n   : in  std_logic;
+    tcds_rx_p    : in  std_logic;
+    tcds_rx_n    : in  std_logic;
+    tcds_tx_p    : out std_logic;
+    tcds_tx_n    : out std_logic;
+    -- TTC b command output
+    ttc_cmd      : out ttc_cmd_t;
+    ttc_cmd_dist : out ttc_cmd_t;
+    -- L1A output
+    ttc_l1a      : out std_logic;
+    -- L1A qualifier output
+    ttc_l1a_dist : out std_logic;
+    dist_lock    : in  std_logic;
+    bunch_ctr    : out bctr_t;
+    evt_ctr      : in  eoctr_t;
+    orb_ctr      : out eoctr_t;
+    oc_flag      : out std_logic;
+    ec_flag      : out std_logic;
+    -- clock monitoring inputs from MGTs
+    monclk       : in  std_logic_vector(3 downto 0);
+    clk125_o     : out std_logic
+    );
+
+end emp_ttc;
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+architecture rtl of emp_ttc is
+
+  signal clk40_ext, clk40_ext_legacy, clk40_ext_tcds2                                     : std_logic;
+  signal tcds_refclk_b, tcds_refclk_div                                                   : std_logic;
+  signal clk40_i, rst40_i, clk160s, clk40_div, rsti, rsti_40, clk40_a, rst40_a, clk_p_i, rst_p_i   : std_logic;
+  signal lock, stop                                                                       : std_logic;
+  signal clks_aux_i, rsts_aux_i                                                           : std_logic_vector(2 downto 0);
+  signal l1a_ext_legacy, l1a_ext_tcds2                                                    : std_logic;
+  signal l1a, l1a_ttc, l1a_del, l1a_ext, l1a_pend, cmd_bx, cmd_pend, l1a_issue, cmd_issue : std_logic;
+  signal cmd, cmd_ttc, cmd_del, cmd_ext_tcds2, cmd_ext_legacy                             : ttc_cmd_t;
+  signal psok, bc0_fr, ctr_clr                                                            : std_logic;
+  signal bunch_ctr_i                                                                      : bctr_t;
+  signal req_bx                                                                           : unsigned(bctr_t'range);
+  signal orb_ctr_i                                                                        : eoctr_t;
+  signal clk40_mmcm_sel                                                                   : std_logic;
+
+  signal stat_clk40 : ipb_reg_v(0 downto 0);
+  signal ctrl_clk40 : ipb_reg_v(0 downto 0);
+
+  signal stat_common : ipb_reg_v(2 downto 0);
+  signal ctrl_common : ipb_reg_v(0 downto 0);
+  signal stb_common  : std_logic_vector(0 downto 0);
+
+  signal bc0_lock : std_logic;
+  signal ipbw     : ipb_wbus_array(N_SLAVES - 1 downto 0);
+  signal ipbr     : ipb_rbus_array(N_SLAVES - 1 downto 0);
+  
+  signal clk_40_to_mmcm : std_logic;
+
+begin
+
+
+
+-- ipbus address decode
+
+  fabric : entity work.ipbus_fabric_sel
+    generic map(
+      NSLV      => N_SLAVES,
+      SEL_WIDTH => IPBUS_SEL_WIDTH)
+    port map(
+      ipb_in          => ipb_in,
+      ipb_out         => ipb_out,
+      sel             => ipbus_sel_emp_ttc(ipb_in.ipb_addr),
+      ipb_to_slaves   => ipbw,
+      ipb_from_slaves => ipbr
+      );
+
+-- TTC control registers
+
+  reg_clk40 : entity work.ipbus_ctrlreg_v
+    generic map (
+      N_CTRL => 1,
+      N_STAT => 1
+      )
+    port map (
+      clk       => clk_ipb,
+      reset     => rst_ipb,
+      ipbus_in  => ipbw(N_SLV_CLK40),
+      ipbus_out => ipbr(N_SLV_CLK40),
+      d         => stat_clk40,
+      q         => ctrl_clk40,
+      stb       => open
+      );
+
+  stat_clk40(0)(0) <= lock;
+  stat_clk40(0)(1) <= stop;
+
+  reg_common : entity work.ipbus_syncreg_v
+    generic map(
+      N_CTRL => 1,
+      N_STAT => 3
+      )
+    port map(
+      clk     => clk_ipb,
+      rst     => rst_ipb,
+      ipb_in  => ipbw(N_SLV_MASTER_COMMON),
+      ipb_out => ipbr(N_SLV_MASTER_COMMON),
+      slv_clk => clk40_a,
+      d       => stat_common,
+      q       => ctrl_common,
+      stb     => stb_common
+      );
+
+  stat_common(0)(0) <= '1' when ENABLE_LEGACY_TTC else '0';
+  stat_common(0)(1) <= '1' when ENABLE_TCDS2      else '0';
+
+
+  -- Mux for external clocks
+  gen_ext_mux : if ENABLE_TCDS2 and ENABLE_LEGACY_TTC generate
+
+    signal sel_pipeline : std_logic_vector(3 downto 0);
+ 
+  begin
+
+    process (clk_ipb)
+    begin
+      if rising_edge(clk_ipb) then
+        sel_pipeline(0) <= ctrl_clk40(0)(2);
+        sel_pipeline(1) <= sel_pipeline(0);
+        sel_pipeline(2) <= sel_pipeline(1);
+        sel_pipeline(3) <= sel_pipeline(2);
+      end if;
+    end process;
+
+    clk40mux : BUFGMUX
+      port map (
+        S  => sel_pipeline(3),
+        I0 => clk40_ext_legacy,
+        I1 => clk40_ext_tcds2,
+        O  => clk40_ext
+        );
+
+  elsif ENABLE_TCDS2 generate
+
+    clk40_ext <= clk40_ext_tcds2;
+
+  else generate
+
+    clk40_ext <= clk40_ext_legacy;
+
+  end generate;
+
+
+-- MMCM for clock multiplication / phase adjustment
+
+  rsti           <= rst_ipb or ctrl_common(0)(2);
+  clk40_mmcm_sel <= ctrl_clk40(0)(1) or ctrl_clk40(0)(2);
+  
+  bufgce_clk_40_rx : bufgce_div
+    generic map (
+      BUFGCE_DIVIDE => 8
+    )
+    port map (
+      i   => tcds_refclk_b,
+      o   => clk_40_to_mmcm,
+      ce  => '1',
+      clr => '0'
+    );  
+
+  clocks : entity work.emp_ttc_clocks
+    port map(
+      clk_40       => clk40_ext,
+      clk_40pseudo => clk_40_to_mmcm,
+      clko_40      => clk40_i,
+      clko_p       => clk_p_i,
+      clko_aux     => clks_aux_i,
+      rsto_40      => rst40_i,
+      rsto_p       => rst_p_i,
+      rsto_aux     => rsts_aux_i,
+      clko_160s    => clk160s,
+      stopped      => stop,
+      locked       => lock,
+      rst_mmcm     => ctrl_clk40(0)(0),
+      rsti         => rsti_40,
+      clksel       => clk40_mmcm_sel,
+      psval        => ctrl_common(0)(23 downto 12),
+      psok         => psok,
+      psen         => ctrl_common(0)(24)
+      );
+
+  clk40_a  <= clk40_i;  -- Needed to make sure delta delays line up in simulation!
+  rst40_a  <= rst40_i;
+  clk40    <= clk40_i;
+  rst40    <= rst40_i;
+  clk_p    <= clk_p_i;
+  rst_p    <= rst_p_i;
+  clks_aux <= clks_aux_i;
+  rsts_aux <= rsts_aux_i;
+
+-- TTC protocol decoder
+
+  gen_master_tcds2 : if ENABLE_TCDS2 generate
+
+    signal clk125_i, clk125_b : std_logic;
+    signal tcds_refclk        : std_logic;
+    signal tcds_refclk_i      : std_logic;
+    signal tcds_orbit_pulse   : std_logic;
+
+    signal channel0_ttc2, channel1_ttc2 : tcds2_streams_pkg.tcds2_ttc2;
+    signal tcds2_interface_ctrl : tcds2_interface_pkg.tcds2_interface_ctrl_t;
+    signal tcds2_interface_stat : tcds2_interface_pkg.tcds2_interface_stat_t;
+
+  begin
+
+    clk125_o <= clk125_b;
+
+    ibufds_gt : IBUFDS_GTE4
+      port map (
+        i     => tcds_clk_p,
+        ib    => tcds_clk_n,
+        o     => tcds_refclk,
+        odiv2 => tcds_refclk_i,
+        ceb   => '0'
+        );
+
+    ibuf_osc : IBUF
+      port map (
+        O => clk125_i,
+        I => clk125
+        );
+
+    bufg_osc : BUFG
+      port map (
+        I => clk125_i,
+        O => clk125_b
+        );
+
+    bufg_ref : BUFG_GT
+      port map (
+        i       => tcds_refclk_i,
+        o       => tcds_refclk_b,
+        ce      => '1',
+        clr     => '0',
+        div     => "000",
+        cemask  => '1',
+        clrmask => '0'
+        );
+
+    tcds2 : entity work.tcds2_interface_with_mgt
+      generic map (
+        G_MGT_TYPE => emp_ttc_utils.get_tcds2_mgt_type(emp_device_decl.TCDS2_MGT_TYPE),
+        G_LINK_SPEED => emp_ttc_utils.get_tcds2_link_speed(emp_device_decl.TCDS2_SPEED),
+        G_INCLUDE_PRBS_LINK_TEST => true
+        )
+      port map (
+        ctrl_i => tcds2_interface_ctrl,
+        stat_o => tcds2_interface_stat,
+
+        clk_sys_125mhz => clk125_b,
+
+        mgt_tx_p_o => tcds_tx_p,
+        mgt_tx_n_o => tcds_tx_n,
+        mgt_rx_p_i => tcds_rx_n,
+        mgt_rx_n_i => tcds_rx_p,
+
+        clk_320_mgt_ref_i => tcds_refclk,
+
+        clk_40_o => clk40_ext_tcds2,
+
+        orbit_o => tcds_orbit_pulse,
+
+        channel0_ttc2_o    => channel0_ttc2,
+        channel0_tts2_i(0) => tcds2_streams_pkg.C_TCDS2_TTS2_VALUE_IGNORED,
+        channel1_ttc2_o    => channel1_ttc2,
+        channel1_tts2_i(0) => tcds2_streams_pkg.C_TCDS2_TTS2_VALUE_IGNORED
+        );
+
+    process (clk40_a)
+    begin
+      if rising_edge(clk40_a) then
+        -- Only consider a BC0 signal to be received if I see it on both of those channels in the same BX
+        -- Reason: Improve robustness against bitflips in TTC path for Serenity
+        -- TODO: Set this behvaviour using SW-settable register in v0.7.0, rather than hardcoding
+        if (channel0_ttc2.sync_flags_and_commands(0) = '1') and (channel1_ttc2.sync_flags_and_commands(0) = '1') then
+          cmd_ext_tcds2 <= TTC_BCMD_BC0;
+        else
+          cmd_ext_tcds2 <= TTC_BCMD_NULL;
+        end if;
+
+        rsti_40 <= rsti;
+
+      end if;
+    end process;
+
+    l1a_ext_tcds2 <= '0';
+
+    csr : entity work.ipbus_tcds2_interface_accessor
+      port map (
+        clk_ipb => clk_ipb,
+        rst_ipb => rst_ipb,
+        ipb_in  => ipbw(N_SLV_MASTER_TCDS2),
+        ipb_out => ipbr(N_SLV_MASTER_TCDS2),
+
+        ctrl_o => tcds2_interface_ctrl,
+        stat_i => tcds2_interface_stat
+        );
+
+  else generate
+
+    tcds_tx_p <= '0';
+    tcds_tx_n <= '0';
+
+    tcds_refclk_b   <= '0';
+    clk40_ext_tcds2 <= '0';
+    cmd_ext_tcds2   <= TTC_BCMD_NULL;
+    l1a_ext_tcds2   <= '0';
+
+    ipbr(N_SLV_MASTER_TCDS2).ipb_ack   <= '0';
+    ipbr(N_SLV_MASTER_TCDS2).ipb_err   <= ipbw(N_SLV_MASTER_TCDS2).ipb_strobe;
+    ipbr(N_SLV_MASTER_TCDS2).ipb_rdata <= (others => '0');
+
+  end generate;
+
+
+  gen_master_legacy : if ENABLE_LEGACY_TTC generate
+
+    signal err_rst                : std_logic;
+    signal sinerr_ctr, dblerr_ctr : std_logic_vector(15 downto 0);
+    signal stat_legacy            : ipb_reg_v(0 downto 0);
+    signal ctrl_legacy            : ipb_reg_v(0 downto 0);
+    signal stb_legacy             : std_logic_vector(0 downto 0);
+
+  begin
+
+    osc_clock : entity work.emp_oscclk
+      generic map (
+        OSC_FREQ => EXT_CLK_FREQ
+        )
+      port map (
+        clk_p => ttc_clk_p,
+        clk_n => ttc_clk_n,
+        clk40 => clk40_ext_legacy
+        );
+
+    reg : entity work.ipbus_syncreg_v
+      generic map(
+        N_CTRL => 1,
+        N_STAT => 1
+        )
+      port map(
+        clk     => clk_ipb,
+        rst     => rst_ipb,
+        ipb_in  => ipbw(N_SLV_MASTER_LEGACY),
+        ipb_out => ipbr(N_SLV_MASTER_LEGACY),
+        slv_clk => clk40_a,
+        d       => stat_legacy,
+        q       => ctrl_legacy,
+        stb     => stb_legacy
+        );
+
+    interface : entity work.emp_ttc_legacy
+      port map(
+        clk         => clk40_a,
+        rst         => rst40_a,
+        sclk        => clk160s,
+        sclk_locked => lock,
+        ttc_in_p    => ttc_rx_p,
+        ttc_in_n    => ttc_rx_n,
+        l1a         => l1a_ext_legacy,
+        cmd         => cmd_ext_legacy,
+        sinerr_ctr  => sinerr_ctr,
+        dblerr_ctr  => dblerr_ctr,
+        err_rst     => err_rst
+        );
+
+    err_rst        <= ctrl_legacy(0)(0) and stb_legacy(0);
+    stat_legacy(0) <= dblerr_ctr & sinerr_ctr;
+
+
+  else generate
+
+    clk40_ext_legacy <= '0';
+    cmd_ext_legacy   <= TTC_BCMD_NULL;
+    l1a_ext_legacy   <= '0';
+
+    ipbr(N_SLV_MASTER_LEGACY).ipb_ack   <= '0';
+    ipbr(N_SLV_MASTER_LEGACY).ipb_err   <= ipbw(N_SLV_MASTER_LEGACY).ipb_strobe;
+    ipbr(N_SLV_MASTER_LEGACY).ipb_rdata <= (others => '0');
+
+  end generate;
+
+
+  -- FIXME: Select between each of two external sources and internal
+  with ctrl_common(0)(1 downto 0) select cmd_ttc <=
+    cmd_ext_legacy when "01",
+    cmd_ext_tcds2  when "10",
+    TTC_BCMD_NULL  when others;
+
+  with ctrl_common(0)(1 downto 0) select l1a_ttc <=
+    l1a_ext_legacy when "01",
+    l1a_ext_tcds2  when "10",
+    '0'            when others;
+
+  -- L1A generation
+  l1a       <= l1a_ttc or l1a_pend;
+  l1a_issue <= l1a and not l1a_ttc;
+
+  -- TTC command generation
+  req_bx <= to_unsigned(LHC_BUNCH_COUNT, req_bx'length) - to_unsigned(TTC_DEL, req_bx'length) - 1;
+  cmd_bx <= '1' when std_logic_vector(req_bx) = bunch_ctr_i else '0';
+
+  process(cmd_ttc, bc0_fr, cmd_pend, cmd_bx)
+  begin
+    cmd_issue <= '0';
+    if cmd_ttc /= TTC_BCMD_NULL then
+      cmd <= cmd_ttc;
+    elsif bc0_fr = '1' then
+      cmd <= TTC_BCMD_BC0;
+    elsif cmd_pend = '1' then
+      cmd       <= ctrl_common(0)(15 downto 8);
+      cmd_issue <= '1';
+    else
+      cmd <= TTC_BCMD_NULL;
+    end if;
+  end process;
+
+  process(clk40_a)
+  begin
+    if rising_edge(clk40_a) then
+      cmd_pend     <= (cmd_pend or (ctrl_common(0)(5) and stb_common(0))) and not (rst40_a or cmd_issue);
+      l1a_pend     <= l1a_pend and not (rst40_a or l1a_issue);
+      ttc_cmd_dist <= cmd;
+    end if;
+  end process;
+
+  ttc_l1a_dist <= l1a;
+
+-- Counters
+
+  ctr_clr <= ctrl_common(0)(4) and stb_common(0);
+
+  ttcctr : entity work.ttc_ctrs
+    port map(
+      clk         => clk40_a,
+      rst         => rst40_a,
+      ttc_cmd     => cmd,
+      l1a         => l1a,
+      clr         => '0',
+      en_int_bc0  => ctrl_common(0)(3),
+      bc0_lock    => bc0_lock,
+      bc0_fr      => bc0_fr,
+      ttc_cmd_out => cmd_del,
+      l1a_out     => l1a_del,
+      bunch_ctr   => bunch_ctr_i,
+      orb_ctr     => orb_ctr_i
+      );
+
+  ttc_cmd   <= cmd_del;
+  ttc_l1a   <= l1a_del;
+  bunch_ctr <= bunch_ctr_i;
+  orb_ctr   <= orb_ctr_i;
+  oc_flag   <= '1' when orb_ctr_i(13 downto 0) = (13 downto 0 => '0') and bc0_lock = '1' else '0';
+  ec_flag   <= '1' when evt_ctr(16 downto 0) = (16 downto 0   => '0')                    else '0';
+
+-- Status reg
+
+  stat_common(0)(7 downto 2)   <= (l1a_pend or cmd_pend) & psok & dist_lock & bc0_lock & "00";
+  stat_common(0)(19 downto 8)  <= bunch_ctr_i;
+  stat_common(0)(31 downto 20) <= std_logic_vector(to_unsigned(LHC_BUNCH_COUNT, 12));
+  stat_common(1)               <= evt_ctr;
+  stat_common(2)               <= orb_ctr_i;
+
+-- clk40 frequency monitoring   
+
+  div : entity work.freq_ctr_div
+    generic map(
+      N_CLK => 2
+      )
+    port map(
+      clk(0)    => clk40_a,
+      clk(1)    => tcds_refclk_b,
+      clkdiv(0) => clk40_div,
+      clkdiv(1) => tcds_refclk_div
+      );
+
+-- Clock frequency monitor
+
+  ctr : entity work.ipbus_freq_ctr
+    generic map(
+      N_CLK => 6
+      )
+    port map(
+      clk                => clk_ipb,
+      rst                => rst_ipb,
+      ipb_in             => ipbw(N_SLV_FREQ),
+      ipb_out            => ipbr(N_SLV_FREQ),
+      clkdiv(0)          => clk40_div,
+      clkdiv(1)          => tcds_refclk_div,
+      clkdiv(5 downto 2) => monclk
+      );
+
+-- TTC history buffer
+
+  hist : entity work.ttc_history_new
+    port map(
+      clk     => clk_ipb,
+      rst     => rst_ipb,
+      ipb_in  => ipbw(N_SLV_HIST),
+      ipb_out => ipbr(N_SLV_HIST),
+      ttc_clk => clk40_a,
+      ttc_rst => rst40_a,
+      ttc_l1a => l1a_del,
+      ttc_cmd => cmd_del,
+      ttc_bx  => bunch_ctr_i,
+      ttc_orb => orb_ctr_i,
+      ttc_evt => evt_ctr
+      );
+
+-- Command counters
+
+  cmdctrs : entity work.ttc_cmd_ctrs
+    port map(
+      clk     => clk_ipb,
+      rst     => rst_ipb,
+      ipb_in  => ipbw(N_SLV_CMD_CTRS),
+      ipb_out => ipbr(N_SLV_CMD_CTRS),
+      ttc_clk => clk40_a,
+      clr     => ctr_clr,
+      ttc_cmd => cmd_del
+      );
+
+end rtl;
+-------------------------------------------------------------------------------
diff --git a/top/firmware/hdl/emp_project_decl.vhd b/top/firmware/hdl/emp_project_decl.vhd
index d120be630a0c147e378a46406c133295bd9e9920..b4d2cac9c9071f718ce527041444b7e55c92fa5c 100644
--- a/top/firmware/hdl/emp_project_decl.vhd
+++ b/top/firmware/hdl/emp_project_decl.vhd
@@ -23,15 +23,13 @@ package emp_project_decl is
 
   constant PAYLOAD_REV         : std_logic_vector(31 downto 0) := X"d451d001";
 
-  -- Number of LHC bunches
-  constant LHC_BUNCH_COUNT    : integer             := 3564;
   -- Latency buffer size
   constant LB_ADDR_WIDTH      : integer             := 10;
 
   -- Clock setup
   constant CLOCK_COMMON_RATIO : integer             := 32;
   constant CLOCK_RATIO        : integer             := 8;
-  constant CLOCK_AUX_RATIO    : clock_ratio_array_t := (2, 4, 8);
+  constant CLOCK_AUX_DIV      : clock_divisor_array_t := (16, 8, 4); -- Dividers of CLOCK_COMMON_RATIO * 40 MHz
 
   -- Only used by nullalgo
   constant PAYLOAD_LATENCY    : integer             := 5;