diff --git a/.gitignore b/.gitignore
index 1bb46147d7feb65673794f3a0c412fb42fdff549..1c77988a33ea10ae53e852b37efbc5b8338cdb5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-*.code-workspace
\ No newline at end of file
+*.code-workspace
+**/ipbus_decode*
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8fe1eba72dc0d40bd1487ec04e8051687700043a..076d88b59f137d756eba8b378e4406af50c9e15a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,12 +3,12 @@ include:
     file: "/ci/templates/vivado-jobs.yml"
 
 variables:
-  VIVADO_VER: "2022.2"
+  VIVADO_VER: "2023.2"
   PROJECT_DEPFILE: top.dep
-  PROJECT_ALGORITHM: top
+  PROJECT_ALGORITHM: top/charm
   PROJECT_NAME: mprocessor
   EMPHUB_TAG: $CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA
-  IPBB_VER: "dev-2021j-v1.2"
+  IPBB_VER: "dev-2023a-v1.3"
 
 
 .vivado-job-build:
diff --git a/common/firmware/hdl/LinkCombinerCore.vhd b/common/firmware/hdl/LinkCombinerCore.vhd
index 54a09f1ec60364be15b0bcdc15783de00d89c54c..5770215a5f77e1218f812ccbd46bb9df3e6f70df 100644
--- a/common/firmware/hdl/LinkCombinerCore.vhd
+++ b/common/firmware/hdl/LinkCombinerCore.vhd
@@ -3,7 +3,7 @@ use IEEE.STD_LOGIC_1164.ALL;
 use IEEE.NUMERIC_STD.ALL;
 use work.emp_data_types.all;
 use work.front_end_data_types.all;
-use work.gbt_module_constants.all;
+use work.module_constants.all;
 use work.dtc_link_maps.all;
 use work.dtc_data_types.all;
 use work.dtc_constants.all;
diff --git a/common/firmware/hdl/LinkCombinerIPBus.vhd b/common/firmware/hdl/LinkCombinerIPBus.vhd
index 18a08325379e68a329f95f0f85b175af2318052c..4270a665761cea862179ce7abde47dfd9da80518 100644
--- a/common/firmware/hdl/LinkCombinerIPBus.vhd
+++ b/common/firmware/hdl/LinkCombinerIPBus.vhd
@@ -7,7 +7,7 @@ use work.ipbus.all;
 use work.ipbus_reg_types.all;
 
 use work.front_end_data_types.all;
-use work.gbt_module_constants.all;
+use work.module_constants.all;
 use work.dtc_link_maps.all;
 use work.dtc_data_types.all;
 use work.dtc_constants.all;
diff --git a/common/firmware/hdl/MProcessor.vhd b/common/firmware/hdl/MProcessor.vhd
index 27f7176539dd8bfff702ee0dea536fafdc2f0dd0..c6b2ec042a243d8235e7458bd70f41335648d59d 100644
--- a/common/firmware/hdl/MProcessor.vhd
+++ b/common/firmware/hdl/MProcessor.vhd
@@ -10,7 +10,7 @@ use work.ipbus_decode_mprocessor.all;
 
 use work.dtc_link_maps.all;
 use work.front_end_data_types.all;
-use work.gbt_module_constants.all;
+use work.module_constants.all;
 use work.mprocessor_constants.all;
 use work.dtc_data_types.all;
 use work.dtc_constants.all;
diff --git a/dependencies.yml b/dependencies.yml
index 0cbe131cbe6e15a919fa141564d1e7e6163a8e5b..1585d8bb24ae2a4bffcf5e6f13db2f0be0091923 100644
--- a/dependencies.yml
+++ b/dependencies.yml
@@ -1,7 +1,7 @@
 ---
 sources:
     emp-fwk:
-        branch: feature/lpgbtv1_merge_rebase
+        branch: v0.9.0-alpha2
         url: https://gitlab.cern.ch/p2-xware/firmware/emp-fwk.git
 
     ttc_legacy:
@@ -17,7 +17,7 @@ sources:
         url: https://gitlab.cern.ch/gbt-fpga/lpgbt-fpga.git
 
     ipbus:
-        tag: v1.9
+        tag: v1.10
         url: https://github.com/ipbus/ipbus-firmware
 
     histogram:
@@ -45,7 +45,7 @@ sources:
         url: https://gitlab.cern.ch/gbtsc-fpga-support/gbt-sc.git
 
     dtc:
-        branch: master
+        branch: 25-master-branch-uses-old-emp-branch
         url: https://gitlab.cern.ch/cms-tracker-phase2-data-processing/BE_firmware/dtc.git
 
     dtc-stub-processing:
@@ -63,3 +63,8 @@ sources:
     10gb-ethernet:
         branch: master
         url: https://gitlab.cern.ch/cms-tracker-phase2-data-processing/BE_firmware/10gb-ethernet.git
+    
+    daqpath-toolkit:
+        branch: master
+        url: https://gitlab.cern.ch/dmonk/daqpath-toolkit.git
+
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 2ac77db506624757e959ec1c66b214630cca78cd..bb48838f1a700742281480df98d233187f537e41 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
@@ -64,6 +64,8 @@ entity top is
     -- Transceiver ref-clocks
     refclkp        : in  std_logic_vector(N_REFCLK - 1 downto 0);
     refclkn        : in  std_logic_vector(N_REFCLK - 1 downto 0);
+    -- Site ID pins
+    site_id        : in std_logic_vector(1 downto 0);
     -- Heartbeat LED
     heartbeat_led  : out std_logic
     );
@@ -126,11 +128,11 @@ begin
 
 -- Infrastructure
 
-pcie_rst_invert : if emp_slim_top_decl.PCIE_RST_ACTIVE_LEVEL = '1' generate
-pcie_sys_rst_n <= not pcie_sys_rst;
-else generate -- pcie_rst_invert
-pcie_sys_rst_n <= pcie_sys_rst;  
-end generate pcie_rst_invert;
+  pcie_rst_invert : if emp_slim_top_decl.PCIE_RST_ACTIVE_LEVEL = '1' generate
+    pcie_sys_rst_n <= not pcie_sys_rst;
+  else generate -- pcie_rst_invert
+    pcie_sys_rst_n <= pcie_sys_rst;  
+  end generate pcie_rst_invert;
 
   infra : entity work.emp_infra_pcie
     port map(
@@ -182,7 +184,8 @@ end generate pcie_rst_invert;
       clk     => ipb_clk,
       rst     => ipb_rst,
       ipb_in  => ipb_w_array(N_SLV_INFO),
-      ipb_out => ipb_r_array(N_SLV_INFO)
+      ipb_out => ipb_r_array(N_SLV_INFO),
+      site_id => "000000" & site_id
       );
 
   ctrl : entity work.emp_ctrl
@@ -271,7 +274,7 @@ end generate pcie_rst_invert;
       rst           => ipb_rst,
       ipb_in        => ipb_w_array(N_SLV_PAYLOAD),
       ipb_out       => ipb_r_array(N_SLV_PAYLOAD),
-      clk40         => clk40,	        
+      clk40         => clk40,
       clk_payload   => clks_aux,
       rst_payload   => rsts_aux,
       clk_p         => clk_p,
@@ -289,31 +292,30 @@ end generate pcie_rst_invert;
       -- 10g
       gbe_q             => gbe_ldata,
       gbe_backpressure  => gbe_backpressure
+      );
 
-    );
-
-    slink_check : if slink_quads_enabled(SLINK_CONF) > 0 generate
-      slink : entity work.emp_slink_interface
-        generic map (
-          throughput      => SLINK_THROUGHPUT,
-          technology      => SLINK_XCVR_TYPE,
-          ref_clock_freq  => SLINK_REFCLK_FREQ,
-          ref_clock_index => IO_REGION_SPEC(SLINK_CONF(0).region).io_async_refclk
+  slink_check : if slink_quads_enabled(SLINK_CONF) > 0 generate
+    slink : entity work.emp_slink_interface
+      generic map (
+        throughput      => SLINK_THROUGHPUT,
+        technology      => SLINK_XCVR_TYPE,
+        ref_clock_freq  => SLINK_REFCLK_FREQ,
+        ref_clock_index => IO_REGION_SPEC(SLINK_CONF(0).region).io_async_refclk
         )
-        port map (
-          ipb_clk      => ipb_clk,
-          ipb_rst      => ipb_rst,
-          ipb_in       => ipb_w_array(N_SLV_SLINK),
-          ipb_out      => ipb_r_array(N_SLV_SLINK),
+      port map (
+        ipb_clk      => ipb_clk,
+        ipb_rst      => ipb_rst,
+        ipb_in       => ipb_w_array(N_SLV_SLINK),
+        ipb_out      => ipb_r_array(N_SLV_SLINK),
 
-          clk_40       => clk40ish,
-          clk_p        => clk_p,
+        clk_40       => clk40ish,
+        clk_p        => clk_p,
 
-          slink_d      => slink_payload_data,
-          backpressure => slink_backpressure,
+        slink_d      => slink_payload_data,
+        backpressure => slink_backpressure,
 
-          refclkp      => refclkp,
-          refclkn      => refclkn
+        refclkp      => refclkp,
+        refclkn      => refclkn
         );
   end generate;
 
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..4faaa1bb5976d64cfa98a255410f24242849eb9f
--- /dev/null
+++ b/replacements/emp-fwk/components/top/firmware/ucf/clock_constraints_common.tcl
@@ -0,0 +1,70 @@
+
+# 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 lMainPayloadClocks 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 lMainPayloadClocks clk_40_extern_i$i clk_40_extern$i clk_payload_extern$i
+    }
+}
+
+
+# Clock groups: Asynchronous
+puts "lMainPayloadClocks = $lMainPayloadClocks"
+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 $lMainPayloadClocks
+
+# 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 || NAME == $lMasterA"] -group [get_clocks -filter "MASTER_CLOCK == $lMasterB || NAME == $lMasterB"]
+    }
+}
+
+
+# Inform Vivado that skew between 40MHz and payload clocks should be minimised
+set_property CLOCK_DELAY_GROUP lhc_sync_clkgroup [get_nets -of_objects [get_pins ttc/clocks/bufg_40/O]] [get_nets -of_objects [get_pins ttc/clocks/bufg_p/O]]
+
+
+# SLink: Give SLink clocks nicer names, and declare them as async to LHC clocks, main data I/O clocks and IPbus clock
+if { [llength [get_cells slink_check.slink]] > 0 } { 
+    create_generated_clock -name slink_fed_clk [get_pins slink_check.slink/clocks/bufg_fed/O]
+    create_generated_clock -name slink_fmt_clk [get_pins slink_check.slink/clocks/mmcm/CLKOUT1]
+
+    set_clock_groups -asynch -group [get_clocks -include_generated_clocks ipbus_clk] -group [get_clocks -include_generated_clocks {slink_fmt_clk slink_fed_clk}]
+    set_clock_groups -asynch -group [get_clocks -include_generated_clocks $lMainPayloadClocks] -group [get_clocks -include_generated_clocks {slink_fmt_clk slink_fed_clk}]
+
+    # Remove clock constraints on the paths to the slink clock frequency measurement block
+    set_false_path -to [get_pins slink_check.slink/ctr/t_in_reg/D]
+}
+
+
+# Print clock report for the record (in case build fails, or need to debug clock-related issues)
+report_clocks
+
+
+# Remove timing constraints for heartbeat LED output port
+set_false_path -to [get_ports -regexp "(.+_led|led_.+)"]
diff --git a/replacements/emp-fwk/components/top/firmware/ucf/clock_constraints_tcds2.tcl b/replacements/emp-fwk/components/top/firmware/ucf/clock_constraints_tcds2.tcl
deleted file mode 100644
index 606c6caf8b35ccc2b6da32e9c7fc317d2310a392..0000000000000000000000000000000000000000
--- a/replacements/emp-fwk/components/top/firmware/ucf/clock_constraints_tcds2.tcl
+++ /dev/null
@@ -1,12 +0,0 @@
-
-# Define 125MHz clock, and declare as async to other clocks
-# Note: In some designs 125MHz clock is top-level port (e.g. Serenity DC),
-#       otherwise its a derived clock from the infra block (e.g. Apollo)
-if {[llength [get_ports -quiet clk125]] != 0} {
-    create_clock -period 8.000 -name sys_clk125 [get_ports clk125]
-}
-set_clock_groups -asynch -group [get_clocks -include_generated_clocks sys_clk125]
-
-# Paths from TCDS core to EMP TTC logic: Only perform timing analysis when clk40 in EMP logic comes from TCDS core
-# Commented out as it throws error in Vivado 2022
-# set_false_path -through [get_nets {ttc/*tcds2_interface_stat[channel0_ttc2]*}] -to [get_clocks {clk_40_extern0 clk_40_pseudo}]
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..22736af01d5101834d1c36f7350be36b01814909
--- /dev/null
+++ b/replacements/emp-fwk/components/top/firmware/ucf/clock_declarations_pcie.tcl
@@ -0,0 +1,24 @@
+# From Vivado 2020.2.2 (or from IP core 4.1 revision 10) there appears to be a bug which causes Vivado
+# to think that the AXI clock frequency is half of what it actually is (i.e. 62.5MHz rather than 125MHz)
+# if it's configured to use a CPLL rather than QPLL. Solution: Tell Vivado what the frequency really is.
+# When using the create_clock command you to fix this, you lose the relationship with any parent clocks, # so to avoid fallout from that, we actually fix the frequency of the TXOUTCLK (which should be 500MHz)
+# See https://gitlab.cern.ch/p2-xware/firmware/emp-fwk/-/issues/31
+# This bug was apparently fixed in Vivado 2023.2
+if {([get_property CORE_REVISION [get_ips xdma_0]] >= 10) && ([get_property CORE_REVISION [get_ips xdma_0]] < 26) && ([get_property CONFIG.plltype [get_ips xdma_0]] == "CPLL")} {
+  create_clock -period 2.000 -name xdma_txoutclk [get_pins -hierarchical -filter {NAME =~infra/dma/xdma/*/*_CHANNEL_PRIM_INST/TXOUTCLK}]
+}
+
+# 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]
+
+set lAxiClockSource [get_pins -quiet -hierarchical -filter {NAME =~infra/dma/xdma/*/phy_clk_i/bufg_gt_userclk/O}]
+if {[llength $lAxiClockSource] == 0} {
+  set lAxiClockSource [get_pins -hierarchical -filter {NAME =~infra/dma/xdma/*/phy_clk_i/bufg_gt_coreclk/O}]
+}
+create_generated_clock -name axi_clk $lAxiClockSource
+
+# 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..f40112bbff7ebf380ac551f23cb7ca9dc422afa7
--- /dev/null
+++ b/replacements/emp-fwk/components/ttc/firmware/hdl/emp_ttc.vhd
@@ -0,0 +1,764 @@
+-- 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;
+use work.tcds2_link_pkg;
+
+
+-------------------------------------------------------------------------------
+entity emp_ttc is
+  generic (
+    ENABLE_LEGACY_TTC : boolean;
+    ENABLE_TCDS2      : boolean;
+    EXT_CLK_FREQ      : real;
+    ENABLE_TCDS_RELAY : boolean := false
+    );
+  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;
+    -- KH for FPGA#1<->FPGA#2 TCDS
+    tcds_secondary_clk_p      : in  std_logic := '0';
+    tcds_secondary_clk_n      : in  std_logic := '0';
+    tcds_secondary_rx_p       : in  std_logic := '0';
+    tcds_secondary_rx_n       : in  std_logic := '0';
+    tcds_secondary_tx_p       : out std_logic := '0';
+    tcds_secondary_tx_n       : out std_logic := '0';
+    -- 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 tcds_secondary_clk, tcds_secondary_clk_b, tcds_secondary_clk_i, tcds_secondary_clk_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_mux : ipb_reg_v(0 downto 0);
+  signal ctrl_mux : ipb_reg_v(0 downto 0);
+  signal ctrl_ttc2_delay : ipb_reg_v(0 downto 0);
+
+  signal stat_common : ipb_reg_v(5 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;
+  signal ttc2_frame_secondary       : tcds2_link_pkg.tcds2_frame_t;
+  signal tts2_frame_secondary       : tcds2_link_pkg.tcds2_frame_t;
+  signal tcds_secondary_clk40_recv  : std_logic_vector(0 downto 0);
+  signal secondary_clk40_ext_tcds2  : std_logic;
+  signal tcds_secondary_orbit_pulse : std_logic;
+
+  signal channel0_ttc2_dly, channel1_ttc2_dly : tcds2_streams_pkg.tcds2_ttc2;
+  signal ttc2_delay : std_logic_vector(6 downto 0); -- 7b
+  type ttc2_array_t is array (50 downto 1) of tcds2_streams_pkg.tcds2_ttc2;
+  signal channel0_ttc2_array, channel1_ttc2_array : ttc2_array_t;
+
+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
+      );
+
+
+    -- KH : a stand-alone register for delaying ttc2 on the
+    -- primary fpga.  Maybe the official interface will
+    -- handle the sync of bc0 etc across the two fpgas.  For
+    -- now the important thing is to have a register where the
+    -- rst is tied to zero so resetting ipb doesn't interfere
+    reg_fpga1 : entity work.ipbus_ctrlreg_v
+    generic map (
+        N_CTRL => 1,
+        N_STAT => 1
+    )
+    port map (
+        clk       => clk_ipb,
+
+        -- JRF returned this to rst_ipb, having said that Kristian specifically
+        -- set this to '0' so we should be prepared to revert this change if
+        -- it causes issues with TCDS2 on FPFGA2
+        reset     => rst_ipb, --'0',
+
+        ipbus_in  => ipbw(N_SLV_TTC2_DELAY),
+        ipbus_out => ipbr(N_SLV_TTC2_DELAY),
+        d         => open,
+        q         => ctrl_ttc2_delay,
+
+        stb       => open
+    );
+
+-- 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_MUX),
+      ipbus_out => ipbr(N_SLV_MUX),
+      d         => stat_mux,
+      q         => ctrl_mux,
+      stb       => open
+      );
+
+  stat_mux(0)(0) <= lock;
+  stat_mux(0)(1) <= stop;
+
+  reg_common : entity work.ipbus_syncreg_v
+    generic map(
+      N_CTRL => 1,
+      N_STAT => 6
+      )
+    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_mux(0)(2) <= '1' when ENABLE_LEGACY_TTC else '0';
+      stat_mux(0)(3) <= '1' when ENABLE_TCDS2      else '0';
+      stat_mux(0)(4) <= '1' when ENABLE_TCDS_RELAY 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_mux(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 -- gen_ext_mux
+
+    clk40_ext <= clk40_ext_tcds2;
+
+  else generate -- gen_ext_mux
+
+    clk40_ext <= clk40_ext_legacy;
+
+  end generate gen_ext_mux;
+
+
+-- MMCM for clock multiplication / phase adjustment
+
+  rsti           <= rst_ipb or ctrl_common(0)(0);
+  clk40_mmcm_sel <= ctrl_mux(0)(1) or ctrl_mux(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_mux(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;
+
+    attribute dont_touch : string;
+    attribute dont_touch of tcds2_interface_stat : signal is "true";
+
+    signal channel0_tts2, channel1_tts2 : tcds2_streams_pkg.tcds2_tts2;
+    signal secondary_channel0_tts2, secondary_channel1_tts2 : tcds2_streams_pkg.tcds2_tts2;
+    signal secondary_channel0_ttc2, secondary_channel1_ttc2 : tcds2_streams_pkg.tcds2_ttc2;
+    signal tcds2_secondary_interface_ctrl : tcds2_interface_pkg.tcds2_interface_ctrl_t;
+    signal tcds2_secondary_interface_stat : tcds2_interface_pkg.tcds2_interface_stat_t;
+
+  begin
+
+    clk125_o <= clk125_b;
+    --KH
+    ttc2_delay  <= ctrl_ttc2_delay(0)(6 downto 0);
+
+    ibufds_gt : IBUFDS_GTE4
+      port map (
+        i     => tcds_clk_p,
+        ib    => tcds_clk_n,
+        o     => tcds_refclk,
+        odiv2 => tcds_refclk_i,
+        ceb   => '0'
+        );
+
+        --KH : refclk for fgpa#1/2 tcds
+        gen_secondary_bufds : if ENABLE_TCDS_RELAY generate
+        begin
+            ibufds_gt_2 : IBUFDS_GTE4
+            port map (
+                i     => tcds_secondary_clk_p,
+                ib    => tcds_secondary_clk_n,
+                o     => tcds_secondary_clk,   -- to the xcvr
+                odiv2 => tcds_secondary_clk_i, -- for logic monitoring
+                ceb   => '0'
+            );
+        end generate;
+
+
+    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'
+        );
+    
+        gen_secondary_bufgt : if ENABLE_TCDS_RELAY generate
+        begin
+            bufg_ref2 : BUFG_GT
+            port map (
+                i       => tcds_secondary_clk_i,
+                o       => tcds_secondary_clk_b,
+                ce      => '1',
+                clr     => '0',
+                div     => "000",
+                cemask  => '1',
+                clrmask => '0'
+            );
+        end generate; 
+
+
+    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
+        );
+
+
+        gen_relay : if ENABLE_TCDS_RELAY generate
+        begin
+
+            -- have the streams, make a frame ...
+            ttc2_frame_builder : entity work.ttc2_frame_builder
+            generic map (
+                G_LINK_SPEED => emp_ttc_utils.get_tcds2_link_speed(emp_device_decl.TCDS2_SPEED)
+            )
+            port map (
+                stream0_i => channel0_ttc2,
+                stream1_i => channel1_ttc2,
+
+                ic_i => tcds2_streams_pkg.C_TCDS2_IC_NULL,
+                ec_i => tcds2_streams_pkg.C_TCDS2_EC_NULL,
+                lm_i => tcds2_streams_pkg.C_TCDS2_LM_NULL,
+
+                frame_o => ttc2_frame_secondary
+            );
+
+
+            -- get the streams from the TTS2 frame
+            tts2_frame_splitter : entity work.tts2_frame_splitter
+            generic map (
+                G_LINK_SPEED => emp_ttc_utils.get_tcds2_link_speed(emp_device_decl.TCDS2_SPEED)
+            )
+            port map (
+                frame_i =>  tts2_frame_secondary,
+
+                stream0_o => channel0_tts2,
+                stream1_o => channel1_tts2,
+
+                ic_o => open,
+                ec_o => open,
+                lm_o => open
+            );
+
+            tcds2_relay : entity work.tcds2_relay
+            port map (
+                clk_ipb => clk_ipb,
+                rst_ipb => rst_ipb,
+                ipb_in  => ipbw(N_SLV_TCDS2_RELAY),
+                ipb_out => ipbr(N_SLV_TCDS2_RELAY),
+                ------------
+                clk_gp_125mhz => clk125_b,
+                clk_40_tx => clk40_a,
+                clk_40_rx => tcds_secondary_clk40_recv,
+                mgt_refclk_relay => tcds_secondary_clk,
+                ------------
+                tcds2_relay_tx_p => tcds_secondary_tx_p,
+                tcds2_relay_tx_n => tcds_secondary_tx_n,
+                tcds2_relay_rx_p => tcds_secondary_rx_p,
+                tcds2_relay_rx_n => tcds_secondary_rx_n,
+                ------------
+                ttc2_frame_relay => ttc2_frame_secondary,
+                ------------
+                tts2_frame_relay => tts2_frame_secondary
+            );
+
+        end generate; -- ENABLE_TCDS_RELAY
+
+
+        -- Delay TTC outputs if running on the first Apollo FPGA, default is no delay
+        tcds_delay : process(clk40_a)
+        begin
+            if rising_edge(clk40_a) then
+                channel0_ttc2_array <= channel0_ttc2_array(channel0_ttc2_array'high - 1 downto channel0_ttc2_array'low) & channel0_ttc2;
+                channel1_ttc2_array <= channel1_ttc2_array(channel1_ttc2_array'high - 1 downto channel1_ttc2_array'low) & channel1_ttc2;
+            end if;
+        end process;
+
+        with ttc2_delay select channel0_ttc2_dly  <=
+            channel0_ttc2 when b"0000000",
+            channel0_ttc2_array(to_integer(unsigned(ttc2_delay)))  when others;
+        with ttc2_delay select channel1_ttc2_dly  <=
+            channel1_ttc2 when b"0000000",
+            channel1_ttc2_array(to_integer(unsigned(ttc2_delay)))  when others;
+        --KH
+
+    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
+        --KH use the delay       if (channel0_ttc2.sync_flags_and_commands(0) = '1') and (channel1_ttc2.sync_flags_and_commands(0) = '1') then
+        if (channel0_ttc2_dly.sync_flags_and_commands(0) = '1') and (channel1_ttc2_dly.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 -- gen_master_tcds2
+
+        tcds_tx_p <= '0';
+        tcds_tx_n <= '0';
+
+        tcds_refclk_b   <= '0';
+        tcds_secondary_clk_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_tcds2;
+
+
+  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 -- gen_master_legacy
+
+    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 gen_master_legacy;
+
+
+  -- FIXME: Select between each of two external sources and internal
+  with ctrl_mux(0)(4 downto 3) select cmd_ttc <=
+    cmd_ext_legacy when "01",
+    cmd_ext_tcds2  when "10",
+    TTC_BCMD_NULL  when others;
+
+  with ctrl_mux(0)(4 downto 3) 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)(11 downto 4);
+      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)(3) 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)(2) 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)(1),
+      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,
+      bc0_on_time_count => stat_common(3),
+      bc0_invalid_count => stat_common(4),
+      bc0_missing_count => stat_common(5)
+  );
+
+  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)(3 downto 0)   <= (l1a_pend or cmd_pend) & psok & dist_lock & bc0_lock;
+  stat_common(0)(15 downto 4)  <= bunch_ctr_i;
+  stat_common(0)(27 downto 16) <= 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 => 3 --KH 2
+    )
+    port map(
+      clk(0)    => clk40_a,
+      clk(1)    => tcds_refclk_b,
+      clk(2)    => tcds_secondary_clk_b,
+
+      clkdiv(0) => clk40_div,
+      clkdiv(1) => tcds_refclk_div,
+      clkdiv(2) => tcds_secondary_clk_div
+    );
+
+-- Clock frequency monitor
+
+  ctr : entity work.ipbus_freq_ctr
+    generic map(
+      N_CLK => 7 --KH 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,
+      clkdiv(6)          => tcds_secondary_clk_div
+    );
+
+-- 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/addr_table/emp_payload.xml b/top/charm/addr_table/emp_payload.xml
similarity index 87%
rename from top/addr_table/emp_payload.xml
rename to top/charm/addr_table/emp_payload.xml
index f37df61257a4f900107b812560a831791898e26a..6565fe20613346e07da6f821af442fd0cb72fd54 100644
--- a/top/addr_table/emp_payload.xml
+++ b/top/charm/addr_table/emp_payload.xml
@@ -1,7 +1,8 @@
 <node description="Payload ipbus registers and RAM" fwinfo="endpoint">
 
-    <node id="fe" address="0x0000" description="FE channel select" fwinfo="endpoint;width=0">
-        <node id="chan_sel" mask="0x7f"/>
+    <node id="fe" address="0x0000" description="FE channel select" fwinfo="endpoint;width=1">
+        <node id="chan_sel" address="0x00" mask="0x7f"/>
+        <node id="num_links" address="0x01"/>
     </node>
 
     <node id="fe_chan" address="0x200" module="file://dtc_link_interface.xml" fwinfo="endpoint;width=9"/>
diff --git a/top/firmware/cfg/top.dep b/top/charm/firmware/cfg/top.dep
similarity index 87%
rename from top/firmware/cfg/top.dep
rename to top/charm/firmware/cfg/top.dep
index 2b37dc34fb52519cea4a6724b0c16a47e881702b..fa18f1e2134c88f7ab42329e53cfe65796958dfa 100644
--- a/top/firmware/cfg/top.dep
+++ b/top/charm/firmware/cfg/top.dep
@@ -1,6 +1,6 @@
 include -c histogram:top histogram.dep
-include -c dtc:dtc-fe/2S/5G-FEC12 module.dep
-include -c dtc:dtc-fe/2S/5G-FEC12 framer.dep
+include -c dtc:dtc-fe common.dep
+include -c dtc:dtc-fe framers.dep
 include -c dtc:dtc-be/common daqpath.dep
 include -c dtc:dtc-be/common common.dep
 include -c dtc-stub-processing:common tools.dep
@@ -12,7 +12,7 @@ src -c ipbus-firmware:components/ipbus_slaves ipbus_ctrlreg_v.vhd
 src -c ipbus-firmware:components/ipbus_slaves ipbus_reg_types.vhd
 src -c ipbus-firmware:components/ipbus_core ipbus_dc_node.vhd
 src -c ipbus-firmware:components/ipbus_core ipbus_dc_fabric_sel.vhd
-include -c emp-fwk:boards/serenity/dc_ku15p dc_ku15p_sm1_v1.dep
+include -c emp-fwk:boards/serenity/dc_ku15p dc_ku15p_sm1_v2.dep
 include -c emp-fwk:components/links/fe_mgt/interface interface.dep
 include -c emp-fwk:components/links/fe_mgt/lpgbt/interface interface_lpgbt.dep
 
diff --git a/top/firmware/hdl/emp_payload.vhd b/top/charm/firmware/hdl/emp_payload.vhd
similarity index 92%
rename from top/firmware/hdl/emp_payload.vhd
rename to top/charm/firmware/hdl/emp_payload.vhd
index 2455f84bd25b63f082daf137b51e2bcdaee1156d..1ff76fb2aeb5777f60d1b1acb922dd7b42074b65 100644
--- a/top/firmware/hdl/emp_payload.vhd
+++ b/top/charm/firmware/hdl/emp_payload.vhd
@@ -14,7 +14,7 @@ use work.emp_slink_types.all;
 ---
 use work.dtc_link_maps.all;
 use work.front_end_data_types.all;
-use work.gbt_module_constants.all;
+use work.module_constants.all;
 use work.mprocessor_constants.all;
 use work.dtc_constants.all;
 use work.dtc_data_types.all;
@@ -59,7 +59,9 @@ architecture rtl of emp_payload is
     signal ipb_to_slaves                : ipb_wbus_array(N_SLAVES - 1 downto 0);
     signal ipb_from_slaves              : ipb_rbus_array(N_SLAVES - 1 downto 0);
 
-    signal channel_sel                  : ipb_reg_v(0 downto 0);
+    signal fe_control_registers       : ipb_reg_v(0 downto 0);
+    signal fe_status_registers        : ipb_reg_v(0 downto 0);
+
     signal mproc_channel_sel            : ipb_reg_v(0 downto 0);
     signal link_aggr_control            : ipb_reg_v(0 downto 0);
 
@@ -122,20 +124,23 @@ begin
         );
 
     --==============================--
-    fe_channel_ctrl: entity work.ipbus_reg_v
+    channel_ctrl : entity work.ipbus_ctrlreg_v
     --==============================--
     generic map (
-        N_REG => 1
+        N_CTRL            => 1,
+        N_STAT            => 1
     )
     port map (
         clk       => clk,
         reset     => rst,
         ipbus_in  => ipb_to_slaves(N_SLV_FE),
         ipbus_out => ipb_from_slaves(N_SLV_FE),
-        q         => channel_sel,
-        qmask     => (0 => X"0000007f")
+        d         => fe_status_registers,
+        q		  => fe_control_registers
     );
 
+    fe_status_registers(0) <= std_logic_vector(to_unsigned(cNumberOfFEModules, 32));
+
     --==============================--
     channel_select : entity work.ipbus_dc_fabric_sel
     --==============================--
@@ -145,7 +150,7 @@ begin
     port map (
         clk       => clk,
         rst       => rst,
-        sel       => channel_sel(0)(6 downto 0),
+        sel       => fe_control_registers(0)(6 downto 0),
         ipb_in    => ipb_to_slaves(N_SLV_FE_CHAN),
         ipb_out   => ipb_from_slaves(N_SLV_FE_CHAN),
         ipbdc_out => ipb_chain(0),
@@ -205,17 +210,19 @@ begin
         LinkInterfaceInstance : entity work.LinkInterface
         --==============================--
         generic map (
-            module_type => "2S"
+            module_type => cDTCInputLinkMap(i).module_type,
+            bandwidth   => cDTCInputLinkMap(i).bandwidth,
+            cic_type    => cDTCInputLinkMap(i).cic_type
         )
         port map (
             --- Input Ports ---
             clk_p               => clk_p,
             clk40               => clk40,
-            link_in             => d(cDTCInputLinkMap(i)),
+            link_in             => d(cDTCInputLinkMap(i).index),
             daq_read            => daq_read(i),
             global_fcmd         => global_fcmd,
             --- Output Ports ---
-            link_out            => q(cDTCInputLinkMap(i)),
+            link_out            => q(cDTCInputLinkMap(i).index),
             stub_out            => stubs(i),
             header_out          => header_array(cNumberOfCICs * i + cNumberOfCICs - 1 downto cNumberOfCICs * i),
             daq_out             => daq_data(i),
@@ -288,8 +295,8 @@ begin
     begin
         if rising_edge(clk_p) then
             for i in 0 to cNumberOfOutputLinks - 1 loop
-                q(cDTCOutputLinkMap(i)).valid  <= d(cDTCInputLinkMap(i)).valid;
-                q(cDTCOutputLinkMap(i)).data   <= d(cDTCInputLinkMap(i)).data;
+                q(cDTCOutputLinkMap(i)).valid  <= d(cDTCInputLinkMap(i).index).valid;
+                q(cDTCOutputLinkMap(i)).data   <= d(cDTCInputLinkMap(i).index).data;
                 q(cDTCOutputLinkMap(i)).strobe <= '1';
             end loop;
         end if;
@@ -301,14 +308,14 @@ begin
     LinkMonitorInterfaceInstance : entity work.LinkMonitorInterface
     --==============================--
     generic map (
-        N_LINKS => 12
+        N_LINKS => 3
     )
     port map (
         --- Input Ports ---
         clk_p => clk_p,
-        stubs => stubs(3 downto 0),
-        headers => header_array(7 downto 0),
-        header_start => header_start_array(7 downto 0),
+        stubs => stubs(2 downto 0),
+        headers => header_array(5 downto 0),
+        header_start => header_start_array(5 downto 0),
         --- IPBus Ports ---
         clk     => clk,
         rst     => rst,
@@ -345,7 +352,7 @@ begin
 
         signal ipb_to_channel        : ipb_wbus;
         signal ipb_from_channel      : ipb_rbus;
-        constant cNumberofInputLinks : integer := 4;
+        constant cNumberofInputLinks : integer := 3;
 
     begin
 
@@ -436,10 +443,10 @@ begin
     ttc_resync_reset <= mproc_channel_sel(0)(8);
     srst <= mproc_channel_sel(0)(7) or ttc_oc0;
 
-    q(20).valid  <= '1';
-    q(20).data   <= "0000" & std_logic_vector(bunch_counter) & "0000000000000000" & std_logic_vector(orbit_counter);
-    q(20).start  <= '1';
-    q(20).strobe <= '1';
+    q(24).valid  <= '1';
+    q(24).data   <= "0000" & std_logic_vector(bunch_counter) & "0000000000000000" & std_logic_vector(orbit_counter);
+    q(24).start  <= '1';
+    q(24).strobe <= '1';
 
 
 
diff --git a/top/firmware/hdl/emp_project_decl.vhd b/top/charm/firmware/hdl/emp_project_decl.vhd
similarity index 76%
rename from top/firmware/hdl/emp_project_decl.vhd
rename to top/charm/firmware/hdl/emp_project_decl.vhd
index c4e9c7c16c0407b76b60156fe5e9fa722d6c91da..561f318b1c79e563f1f9e0f66fdaf583ba9afc21 100644
--- a/top/firmware/hdl/emp_project_decl.vhd
+++ b/top/charm/firmware/hdl/emp_project_decl.vhd
@@ -34,10 +34,10 @@ package emp_project_decl is
 
   -- mgt -> buf -> fmt -> (algo) -> (fmt) -> buf -> mgt -> clk -> altclk
   constant REGION_CONF : region_conf_array_t := (
---    0  => (lpgbt, buf, no_fmt, buf, lpgbt),   --Bank 225 -- Right Column
---    1  => (lpgbt, buf, no_fmt, buf, lpgbt),   --Bank 226
-    2  => (lpgbt, buf, no_fmt, buf, lpgbt),   --Bank 227
-    3  => (lpgbt, buf, no_fmt, buf, lpgbt),   --Bank 228
+    -- 0  => (lpgbt, buf, no_fmt, buf, lpgbt),   --Bank 225 -- Right Column
+    1  => (lpgbt, buf, no_fmt, buf, lpgbt),   --Bank 226
+    -- 2  => (lpgbt, buf, no_fmt, buf, lpgbt),   --Bank 227
+    3  => (no_mgt, buf, no_fmt, buf, no_mgt),   --Bank 228
     4  => (no_mgt, buf, no_fmt, buf, no_mgt),   --Bank 229
     5  => (no_mgt, buf, no_fmt, buf, no_mgt),   --Bank 230
     6  => (no_mgt, buf, no_fmt, buf, no_mgt),   --Bank 231
@@ -60,15 +60,17 @@ package emp_project_decl is
 
   -- for data framer (ic_simple, no_ec, n_ec_spare, ec_broadcast)
   constant REGION_DATA_FRAMER_CONF : region_data_framer_conf_array_t := (
-    2 => ( 0=>(false, true, 0, false, lpgbtv0), 1=>(false, true, 0, false, lpgbtv0), 2=>(false, true, 0, false, lpgbtv1), 3=>(false, true, 0, false, lpgbtv0)),
-    3 => ( 0=>(false, true, 0, false, lpgbtv0), 1=>(false, true, 0, false, lpgbtv0), 2=>(false, true, 0, false, lpgbtv0), 3=>(false, true, 0, false, lpgbtv0)),
+    -- 0 => ( 0=>(false, true, 0, false, lpgbtv1), 1=>(false, true, 0, false, lpgbtv1), 2=>(false, true, 0, false, lpgbtv1), 3=>(false, true, 0, false, lpgbtv1)),
+    1 => ( 0=>(false, true, 0, false, lpgbtv1), 1=>(false, true, 0, false, lpgbtv1), 2=>(false, true, 0, false, lpgbtv1), 3=>(false, true, 0, false, lpgbtv1)),
+    -- 2 => ( 0=>(false, true, 0, false, lpgbtv1), 1=>(false, true, 0, false, lpgbtv1), 2=>(false, true, 0, false, lpgbtv1), 3=>(false, true, 0, false, lpgbtv1)),
     others => kDummyRegionDataFramer
   );
 
   -- for lpgbt
   constant REGION_LPGBT_CONF : region_lpgbt_conf_array_t := (
-    2  => (FEC5, DATARATE_5G12, PCS),
-    3  => (FEC5, DATARATE_5G12, PCS),
+    -- 0  => (FEC5, DATARATE_5G12, PCS),
+    1  => (FEC5, DATARATE_5G12, PCS),
+    -- 2  => (FEC5, DATARATE_5G12, PCS),
     others => kDummyRegionLpgbt
   );
 
diff --git a/top/charm/firmware/hdl/link_maps.vhd b/top/charm/firmware/hdl/link_maps.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..75269aa6132d16e3e4ca58adec6e1f5192d97a0e
--- /dev/null
+++ b/top/charm/firmware/hdl/link_maps.vhd
@@ -0,0 +1,23 @@
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+use work.dtc_link_maps_func.all;
+
+package dtc_link_maps is
+
+    constant cNumberOfFEModules   : integer := 3;
+    constant cNumberOfOutputLinks : integer := 3;
+
+    type tDTCInputLinkMap is array(0 to cNumberOfFEModules - 1) of tDTCInputLinkConfig;
+    constant cDTCInputLinkMap     : tDTCInputLinkMap := (
+        0  => (4 , "2S", 5, "CIC2"),
+        1  => (5 , "2S", 5, "CIC2"),
+        2  => (6 , "2S", 5, "CIC2")
+    );
+
+    type tDTCOutputLinkMap is array(0 to cNumberOfOutputLinks - 1) of integer;
+    constant cDTCOutputLinkMap    : tDTCOutputLinkMap := (
+        12, 13, 14
+    );
+
+end package dtc_link_maps;
\ No newline at end of file
diff --git a/top/firmware/hdl/link_maps.vhd b/top/firmware/hdl/link_maps.vhd
deleted file mode 100644
index d0ca6517cd88b2c93ed21c23763bf86053a0588d..0000000000000000000000000000000000000000
--- a/top/firmware/hdl/link_maps.vhd
+++ /dev/null
@@ -1,14 +0,0 @@
-library IEEE;
-use IEEE.STD_LOGIC_1164.ALL;
-use IEEE.NUMERIC_STD.ALL;
-
-package dtc_link_maps is
-    constant cNumberOfFEModules   : integer := 4;
-    constant cNumberOfOutputLinks : integer := 4;
-
-    type tDTCInputLinkMap is array(0 to cNumberOfFEModules - 1) of integer;
-    constant cDTCInputLinkMap     : tDTCInputLinkMap := (8, 9, 10, 11);
-
-    type tDTCOutputLinkMap is array(0 to cNumberOfOutputLinks - 1) of integer;
-    constant cDTCOutputLinkMap    : tDTCOutputLinkMap := (16, 17, 18, 19);
-end package dtc_link_maps;