# diff from elhep/Fast-Servo-Firmmware commit ID 7fae40c:
# https://github.com/elhep/Fast-Servo-Firmware/commit/7fae40c0f872a91218be378f8289b98b1e366729
# Fix for migen add_source deprecation and removed xilinx bootgen command
# .bin file is being generated by bit2bin.py from Linien repository
# https://github.com/linien-org/linien/blob/master/gateware/bit2bin.py 

diff --git a/fast_servo/gateware/fast_servo_platform.py b/fast_servo/gateware/fast_servo_platform.py
index 13b4aa3..89a8103 100644
--- a/fast_servo/gateware/fast_servo_platform.py
+++ b/fast_servo/gateware/fast_servo_platform.py
@@ -324,7 +324,12 @@ class Platform(XilinxPlatform):
         self.ps7_config = ps7_config
 
         verilog_sources = os.listdir(verilog_dir)
-        self.add_sources(verilog_dir, *verilog_sources)
+        self.add_source_dir(verilog_dir)
+
+    def build(self, *args, **kwargs):
+        build_dir = kwargs.get('build_dir', 'build')
+        self.copy_sources(build_dir)
+        super().build(*args, **kwargs)
 
     def do_finalize(self, fragment):
         try:
diff --git a/fast_servo/gateware/fast_servo_soc.py b/fast_servo/gateware/fast_servo_soc.py
index 02128f5..abfc583 100644
--- a/fast_servo/gateware/fast_servo_soc.py
+++ b/fast_servo/gateware/fast_servo_soc.py
@@ -282,9 +282,3 @@ if __name__ == "__main__":
     os.chdir(os.path.join(root_path, build_dir))
     with open(f"{build_name}.bif", "w") as f:
         f.write(f"all:\n{{\n\t{build_name}.bit\n}}")
-    
-    cmd = f"bootgen -image {build_name}.bif -arch zynq -process_bitstream bin -w on".split(" ")
-    subprocess.run(cmd)
-
-    
-

# diff between linen-org/linien commit ID 93f1f50:
# https://github.com/linien-org/linien/commit/93f1f50ebd86fe3314cab5a549462d0fcbf6a658
# and elhep/linien commit ID b73eea0:
# https://github.com/elhep/linien/commit/b73eea07889dda8b55f0cf4c2afde96cf4c3efd1

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b3f3683..98c6e51 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,7 +3,7 @@ repos:
     rev: 23.11.0
     hooks:
       - id: black
-        exclude: ^(gateware/logic/|gateware/lowlevel/|gateware/linien_module.py|linien-server/linien_server/csrmap.py)
+        exclude: ^(gateware/logic/|gateware/lowlevel/|gateware/linien_module.py|linien-server/linien_server/csrmap.py|gateware/targets)
 
   - repo: https://github.com/pycqa/isort
     rev: 5.12.0
diff --git a/gateware/fpga_image_helper.py b/gateware/fpga_image_helper.py
index c3e20e7..ebead1d 100644
--- a/gateware/fpga_image_helper.py
+++ b/gateware/fpga_image_helper.py
@@ -1,6 +1,7 @@
 # This file is part of Linien and based on redpid.
 #
 # Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+# Copyright 2023 Jakub Matyas <jakubk.m@gmail.com>
 #
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -20,16 +21,18 @@
 from pathlib import Path
 
 from .bit2bin import bit2bin
-from .hw_platform import Platform
-from .linien_module import RootModule
 
 REPO_ROOT_DIR = Path(__file__).resolve().parents[1]
 
 
 def py_csrconstants(map, fil):
     fil.write("csr_constants = {\n")
-    for k, v in root.linien.csrbanks.constants:
-        fil.write("    '{}_{}': {},\n".format(k, v.name, v.value.value))
+    for k, v in root.csrbanks.constants:
+        if k == "linien":
+            # compaitbility layer
+            fil.write("    '{}': {},\n".format(v.name, v.value.value))
+        else:
+            fil.write("    '{}_{}': {},\n".format(k, v.name, v.value.value))
     fil.write("}\n\n")
 
 
@@ -51,26 +54,51 @@ def get_csrmap(banks):
 def py_csrmap(it, fil):
     fil.write("csr = {\n")
     for reg in it:
-        fil.write("    '{}_{}': ({}, 0x{:03x}, {}, {}),\n".format(*reg))
+        main_name = reg[0]
+        secondary_name = reg[1]
+        # compaitbility layer
+        if main_name == "linien" or secondary_name.startswith(main_name):
+            fil.write("    '{}': ({}, 0x{:03x}, {}, {}),\n".format(*reg[1:]))
+        else:
+            fil.write("    '{}_{}': ({}, 0x{:03x}, {}, {}),\n".format(*reg))
     fil.write("}\n")
 
 
 if __name__ == "__main__":
-    platform = Platform()
-    root = RootModule(platform)
+    import argparse
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-p", "--platform", default=None)
+    args = parser.parse_args()
+    if args.platform is None or args.platform.lower() == "redpitaya":
+        from gateware.hw_platform import Platform
+        from gateware.targets.red_pitaya import PitayaSoC
+
+        platform = Platform()
+        root = PitayaSoC(platform)
+    elif args.platform.lower() == "fastservo":
+        from fast_servo.gateware.fast_servo_platform import Platform
+
+        from gateware.targets.fast_servo import LinienFastServo
+
+        platform = Platform()
+        root = LinienFastServo(platform)
+    else:
+        raise ValueError("Unknown platform")
+    
+    platform.add_source_dir(REPO_ROOT_DIR / "gateware" / "verilog")
+    build_dir = REPO_ROOT_DIR / "gateware" / "build"
+    platform.build(root, build_name="top", build_dir=build_dir, run=True)
 
     with open(
         REPO_ROOT_DIR / "linien-server" / "linien_server" / "csrmap.py", "w"
     ) as fil:
-        py_csrconstants(root.linien.csrbanks.constants, fil)
-        csr = get_csrmap(root.linien.csrbanks.banks)
+        py_csrconstants(root.csrbanks.constants, fil)
+        csr = get_csrmap([*root.csrbanks.banks, *root.linien.csrbanks.banks])
         py_csrmap(csr, fil)
         fil.write("states = {}\n".format(repr(root.linien.state_names)))
         fil.write("signals = {}\n".format(repr(root.linien.signal_names)))
 
-    platform.add_source_dir(REPO_ROOT_DIR / "gateware" / "verilog")
-    build_dir = REPO_ROOT_DIR / "gateware" / "build"
-    platform.build(root, build_name="top", build_dir=build_dir)
     bit2bin(
         build_dir / "top.bit",
         REPO_ROOT_DIR / "linien-server" / "linien_server" / "gateware.bin",
diff --git a/gateware/linien_module.py b/gateware/linien_module.py
index 16ca186..6905ac0 100644
--- a/gateware/linien_module.py
+++ b/gateware/linien_module.py
@@ -1,6 +1,7 @@
 # This file is part of Linien and based on redpid.
 #
 # Copyright (C) 2016-2024 Linien Authors (https://github.com/linien-org/linien#license)
+# Copyright 2023 Jakub Matyas <jakubk.m@gmail.com>
 #
 # Linien is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -36,19 +37,13 @@ from misoc.interconnect.csr import AutoCSR, CSRStatus, CSRStorage
 from .logic.autolock import FPGAAutolock
 from .logic.chains import FastChain, SlowChain, cross_connect
 from .logic.decimation import Decimate
-from .logic.delta_sigma import DeltaSigma
 from .logic.iir import Iir
 from .logic.limit import LimitCSR
 from .logic.modulate import Modulate
 from .logic.pid import PID
 from .logic.sweep import SweepCSR
-from .lowlevel.analog import PitayaAnalog
-from .lowlevel.crg import CRG
-from .lowlevel.dna import DNA
-from .lowlevel.gpio import Gpio
-from .lowlevel.pitaya_ps import PitayaPS, Sys2CSR, SysCDC, SysInterconnect
+from .lowlevel.pitaya_ps import Sys2CSR
 from .lowlevel.scopegen import ScopeGen
-from .lowlevel.xadc import XADC
 
 
 class LinienLogic(Module, AutoCSR):
@@ -156,45 +151,24 @@ class LinienLogic(Module, AutoCSR):
 
 
 class LinienModule(Module, AutoCSR):
-    def __init__(self, platform):
+    def __init__(self, soc):
         width = 14
         signal_width = 25
         coeff_width = 25
         chain_factor_bits = 8
 
         self.init_submodules(
-            width, signal_width, coeff_width, chain_factor_bits, platform
+            width, signal_width, coeff_width, chain_factor_bits, soc
         )
-        self.connect_everything(width, signal_width, coeff_width, chain_factor_bits)
+        self.connect_everything(width, signal_width, coeff_width, chain_factor_bits, soc)
 
     def init_submodules(
-        self, width, signal_width, coeff_width, chain_factor_bits, platform
+        self, width, signal_width, coeff_width, chain_factor_bits, soc
     ):
-        sys_double = ClockDomainsRenamer("sys_double")
 
         self.submodules.logic = LinienLogic(
             coeff_width=coeff_width, chain_factor_width=chain_factor_bits
         )
-        self.submodules.analog = PitayaAnalog(
-            platform.request("adc"), platform.request("dac")
-        )
-        self.submodules.xadc = XADC(platform.request("xadc"))
-
-        for i in range(4):
-            pwm = platform.request("pwm", i)
-            ds = sys_double(DeltaSigma(width=15))
-            self.comb += pwm.eq(ds.out)
-            setattr(self.submodules, f"ds{i}", ds)
-
-        exp = platform.request("exp")
-        self.submodules.gpio_n = Gpio(exp.n)
-        self.submodules.gpio_p = Gpio(exp.p)
-
-        leds = Cat(*(platform.request("user_led", i) for i in range(8)))
-        self.comb += leds.eq(self.gpio_n.o)
-
-        self.submodules.dna = DNA(version=2)
-
         self.submodules.fast_a = FastChain(
             width,
             signal_width,
@@ -210,18 +184,22 @@ class LinienModule(Module, AutoCSR):
             offset_signal=self.logic.chain_b_offset_signed,
         )
 
+        # FIXME: does it do anything?!
         _ = ClockDomainsRenamer("sys_slow")
         sys_double = ClockDomainsRenamer("sys_double")
         max_decimation = 16
         self.submodules.decimate = sys_double(Decimate(max_decimation))
         self.clock_domains.cd_decimated_clock = ClockDomain()
         decimated_clock = ClockDomainsRenamer("decimated_clock")
+        # TODO: No support for slow Analog Out on Fast Servo
         self.submodules.slow_chain = decimated_clock(SlowChain())
-
         self.submodules.scopegen = ScopeGen(signal_width)
 
+        soc.add_interconnect_slave(self.scopegen.scope_sys)
+        soc.add_interconnect_slave(self.scopegen.asg_sys)
+
         self.state_names, self.signal_names = cross_connect(
-            self.gpio_n,
+            soc.gpio_n,
             [
                 ("fast_a", self.fast_a),
                 ("fast_b", self.fast_b),
@@ -233,10 +211,6 @@ class LinienModule(Module, AutoCSR):
         )
 
         csr_map = {
-            "dna": 28,
-            "xadc": 29,
-            "gpio_n": 30,
-            "gpio_p": 31,
             "fast_a": 0,
             "fast_b": 1,
             "slow_chain": 2,
@@ -251,19 +225,13 @@ class LinienModule(Module, AutoCSR):
                 name if mem is None else name + "_" + mem.name_override
             ],
         )
-        self.submodules.sys2csr = Sys2CSR()
-        self.submodules.csrcon = csr_bus.Interconnect(
-            self.sys2csr.csr, self.csrbanks.get_buses()
-        )
-        self.submodules.syscdc = SysCDC()
-        self.comb += self.syscdc.target.connect(self.sys2csr.sys)
 
-    def connect_everything(self, width, signal_width, coeff_width, chain_factor_bits):
+    def connect_everything(self, width, signal_width, coeff_width, chain_factor_bits, soc):
         s = signal_width - width
 
         self.comb += [
-            self.fast_a.adc.eq(self.analog.adc_a),
-            self.fast_b.adc.eq(self.analog.adc_b),
+            self.fast_a.adc.eq(soc.analog.adc_a),
+            self.fast_b.adc.eq(soc.analog.adc_b),
         ]
 
         # now, we combine the output of the two paths, with a variable factor each.
@@ -297,7 +265,7 @@ class LinienModule(Module, AutoCSR):
         self.comb += [
             If(
                 self.logic.pid_only_mode.storage,
-                self.logic.pid.input.eq(self.analog.adc_a << s),
+                self.logic.pid.input.eq(soc.analog.adc_a << s),
             ).Else(
                 self.logic.pid.input.eq(mixed_limited),
             ),
@@ -347,40 +315,42 @@ class LinienModule(Module, AutoCSR):
         # ANALOG OUTPUTS ---------------------------------------------------------------
         # ANALOG OUT 0 gets a special treatment because it may contain signal of  slow
         # pid or sweep
-        analog_out = Signal((width + 3, True))
-        self.comb += [
-            analog_out.eq(
-                Mux(
-                    self.logic.sweep_channel.storage == OutputChannel.ANALOG_OUT0,
-                    self.logic.sweep.y,
-                    0,
-                )
-                + Mux(
-                    self.logic.sweep_channel.storage == OutputChannel.ANALOG_OUT0,
-                    self.logic.out_offset_signed,
-                    0,
-                )
-                + Mux(
-                    self.logic.slow_control_channel.storage
+
+        if soc.soc_name == "RedPitaya":
+            analog_out = Signal((width + 3, True))
+            self.comb += [
+                analog_out.eq(
+                    Mux(
+                        self.logic.sweep_channel.storage == OutputChannel.ANALOG_OUT0,
+                        self.logic.sweep.y,
+                        0,
+                    )
+                    + Mux(
+                        self.logic.sweep_channel.storage == OutputChannel.ANALOG_OUT0,
+                        self.logic.out_offset_signed,
+                        0,
+                    )
+                    + Mux(
+                        self.logic.slow_control_channel.storage
                     == OutputChannel.ANALOG_OUT0,
                     self.slow_chain.output,
-                    0,
-                )
-            ),
-        ]
-        # NOTE: not sure why limit is used
-        self.comb += self.slow_chain.limit.x.eq(analog_out)
-        # ds0 apparently has 16 bit, but only allowing positive  values --> "15 bit"?
-        slow_out_shifted = Signal(15)
-        self.sync += slow_out_shifted.eq((self.slow_chain.limit.y << 1) + (1 << 14))
-        self.comb += self.ds0.data.eq(slow_out_shifted)
-
-        # connect other analog outputs
-        self.comb += [
-            self.ds1.data.eq(self.logic.analog_out_1.storage),
-            self.ds2.data.eq(self.logic.analog_out_2.storage),
-            self.ds3.data.eq(self.logic.analog_out_3.storage),
-        ]
+                        0,
+                    )
+                ),
+            ]
+            # NOTE: not sure why limit is used
+            self.comb += self.slow_chain.limit.x.eq(analog_out)
+            # ds0 apparently has 16 bit, but only allowing positive  values --> "15 bit"?
+            slow_out_shifted = Signal(15)
+            self.sync += slow_out_shifted.eq((self.slow_chain.limit.y << 1) + (1 << 14))
+            self.comb += soc.ds0.data.eq(slow_out_shifted)
+
+            # connect other analog outputs
+            self.comb += [
+                soc.ds1.data.eq(self.logic.analog_out_1.storage),
+                soc.ds2.data.eq(self.logic.analog_out_2.storage),
+                soc.ds3.data.eq(self.logic.analog_out_3.storage),
+            ]
 
         # ------------------------------------------------------------------------------
 
@@ -395,7 +365,7 @@ class LinienModule(Module, AutoCSR):
 
         self.comb += [
             self.logic.autolock.robust.at_start.eq(self.logic.sweep.sweep.trigger),
-            self.scopegen.gpio_trigger.eq(self.gpio_p.i[0]),
+            self.scopegen.gpio_trigger.eq(soc.gpio_p.i[0]),
             self.scopegen.sweep_trigger.eq(self.logic.sweep.sweep.trigger),
             self.scopegen.automatically_rearm.eq(
                 self.logic.autolock.request_lock.storage
@@ -404,8 +374,8 @@ class LinienModule(Module, AutoCSR):
             self.scopegen.automatically_trigger.eq(
                 self.logic.autolock.lock_running.status
             ),
-            self.analog.dac_a.eq(self.logic.limit_fast1.y),
-            self.analog.dac_b.eq(self.logic.limit_fast2.y),
+            soc.analog.dac_a.eq(self.logic.limit_fast1.y),
+            soc.analog.dac_b.eq(self.logic.limit_fast2.y),
         ]
 
         # Having this in a comb statement caused errors. See PR #251.
@@ -428,23 +398,4 @@ class DummyHK(Module, AutoCSR):
         self.submodules.csrcon = csr_bus.Interconnect(
             self.sys2csr.csr, self.csrbanks.get_buses()
         )
-        self.sys = self.sys2csr.sys
-
-
-class RootModule(Module):
-    def __init__(self, platform):
-        self.submodules.ps = PitayaPS(platform.request("cpu"))
-        self.submodules.crg = CRG(
-            platform.request("clk125"), self.ps.fclk[0], ~self.ps.frstn[0]
-        )
-        self.submodules.linien = LinienModule(platform)
-
-        self.submodules.hk = ClockDomainsRenamer("sys_ps")(DummyHK())
-
-        self.submodules.ic = SysInterconnect(
-            self.ps.axi.sys,
-            self.hk.sys,
-            self.linien.scopegen.scope_sys,
-            self.linien.scopegen.asg_sys,
-            self.linien.syscdc.source,
-        )
+        self.sys = self.sys2csr.sys
\ No newline at end of file
diff --git a/gateware/targets/__init__.py b/gateware/targets/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/gateware/targets/fast_servo.py b/gateware/targets/fast_servo.py
new file mode 100644
index 0000000..da5bf3b
--- /dev/null
+++ b/gateware/targets/fast_servo.py
@@ -0,0 +1,85 @@
+# Copyright 2023 Jakub Matyas <jakubk.m@gmail.com>
+# Warsaw University of Technology
+#
+# This file is part of Linien and provides support for Linien on
+# Fast Servo platform.
+#
+# Linien is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Linien is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Linien.  If not, see <http://www.gnu.org/licenses/>.
+
+from fast_servo.gateware.fast_servo_soc import BaseSoC
+from migen import *
+from misoc.interconnect import csr_bus
+
+from gateware.linien_module import DummyHK, LinienModule
+from gateware.lowlevel.dna import DNA
+from gateware.lowlevel.gpio import Gpio
+from gateware.lowlevel.pitaya_ps import SysInterconnect
+
+
+class FastServoAnalog(Module):
+    def __init__(self, adc, dac):
+        size = 14       # length of DAC
+
+        self.adc_a = Signal(size)
+        self.adc_b = Signal(size)
+        self.dac_a = Signal(size)
+        self.dac_b = Signal(size)
+
+        self.comb += [
+            self.adc_a.eq(adc.data_out[0][2:]),
+            self.adc_b.eq(adc.data_out[1][2:]),        
+        ]
+
+        self.sync += [
+            dac.data_in[0].eq(self.dac_a),
+            dac.data_in[1].eq(self.dac_b),
+        ]
+
+
+class LinienFastServo(BaseSoC):
+    def __init__(self, platform):
+        super().__init__(platform)
+    
+        self.submodules.dna = DNA(version=2)
+
+        self.submodules.analog = FastServoAnalog(self.adc, self.dac)
+        gpios = platform.request("gpio")
+        self.submodules.gpio_n = Gpio(gpios.n)
+        # self.csr_devices.append("gpio_n")
+        self.submodules.gpio_p = Gpio(gpios.p)
+        # self.csr_devices.append("gpio_p")
+        self.csr_map.update({
+            "dna": 28,
+            "gpio_n": 30,
+            "gpio_p": 31,
+        })
+
+        # ---------------------------------------------
+        # 
+        # FIXME - passing self to LinienModule
+        self.submodules.linien = LinienModule(self)
+
+    def soc_finalize(self):
+        self.add_interconnect_slave(self.syscdc.source)
+        self.submodules.csrbanks = csr_bus.CSRBankArray(self,
+            self.get_csr_dev_address)
+        self.submodules.csrcon = csr_bus.Interconnect(
+            self.sys2csr.csr, [*self.csrbanks.get_buses(), *self.linien.csrbanks.get_buses()]
+        )
+        self.submodules.hk = DummyHK()
+        self.submodules.interconnect = SysInterconnect(
+            self.axi2sys.sys,
+            self.hk.sys,
+            *self.interconnect_slaves
+        )
\ No newline at end of file
diff --git a/gateware/targets/red_pitaya.py b/gateware/targets/red_pitaya.py
new file mode 100644
index 0000000..c029e81
--- /dev/null
+++ b/gateware/targets/red_pitaya.py
@@ -0,0 +1,103 @@
+# Copyright 2014-2015 Robert Jördens <jordens@gmail.com>
+# Copyright 2018-2022 Benjamin Wiegand <benjamin.wiegand@physik.hu-berlin.de>
+# Copyright 2021-2023 Bastian Leykauf <leykauf@physik.hu-berlin.de>
+# Copyright 2022 Christian Freier <christian.freier@nomadatomics.com>
+# Copyright 2023 Jakub Matyas <jakubk.m@gmail.com>
+#
+# This file is part of Linien and based on redpid.
+#
+# Linien is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Linien is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Linien.  If not, see <http://www.gnu.org/licenses/>.
+
+from migen import *
+from misoc.interconnect import csr_bus
+from misoc.interconnect.csr import AutoCSR
+
+from gateware.linien_module import DummyHK, LinienModule
+from gateware.logic.delta_sigma import DeltaSigma
+from gateware.lowlevel.analog import PitayaAnalog
+from gateware.lowlevel.crg import CRG
+from gateware.lowlevel.dna import DNA
+from gateware.lowlevel.gpio import Gpio
+from gateware.lowlevel.pitaya_ps import PitayaPS, Sys2CSR, SysCDC, SysInterconnect
+from gateware.lowlevel.xadc import XADC
+
+
+class PitayaSoC(Module, AutoCSR):
+    def __init__(self, platform):
+        self.csr_map = {
+            "gpio_n": 30,
+            "gpio_p": 31,
+            "dna": 28,
+            "xadc": 29,
+        }
+        self.soc_name = "RedPitaya"
+        self.interconnect_slaves = []
+
+        self.submodules.ps = PitayaPS(platform.request("cpu"))
+        self.submodules.crg = CRG(
+            platform.request("clk125"), self.ps.fclk[0], ~self.ps.frstn[0]
+        )
+        self.submodules.sys2csr = Sys2CSR()
+        self.submodules.syscdc = SysCDC()
+        self.comb += self.syscdc.target.connect(self.sys2csr.sys)
+
+        self.submodules.xadc = XADC(platform.request("xadc"))
+        self.submodules.analog = PitayaAnalog(platform.request("adc"), platform.request("dac"))
+
+        for i in range(4):
+            pwm = platform.request("pwm", i)
+            ds = ClockDomainsRenamer("sys_double")(DeltaSigma(width=15))
+            self.comb += pwm.eq(ds.out)
+            setattr(self.submodules, f"ds{i}", ds)
+
+        exp = platform.request("exp")
+        self.submodules.gpio_n = Gpio(exp.n)
+        self.submodules.gpio_p = Gpio(exp.p)
+
+        leds = Cat(*(platform.request("user_led", i) for i in range(8)))
+        self.comb += leds.eq(self.gpio_n.o)
+
+        self.submodules.dna = DNA(version=2)
+        
+        # ---------------------------------------------
+        # 
+        # FIXME - passing self to LinienModule
+        self.submodules.linien = LinienModule(self)
+        self.add_interconnect_slave(self.syscdc.source)
+        self.run_finalize()
+
+
+    def add_interconnect_slave(self, slave):
+        self.interconnect_slaves.append(slave)
+
+    def get_csr_dev_address(self, name, memory):
+        if memory is not None:
+            name = name + "_" + memory.name_override
+        try:
+            return self.csr_map[name]
+        except KeyError:
+            return None
+
+    def run_finalize(self):
+        self.submodules.csrbanks = csr_bus.CSRBankArray(self,
+            self.get_csr_dev_address)
+        self.submodules.csrcon = csr_bus.Interconnect(
+            self.sys2csr.csr, [*self.csrbanks.get_buses(), *self.linien.csrbanks.get_buses()]
+        )
+        self.submodules.hk = DummyHK()
+        self.submodules.interconnect = SysInterconnect(
+            self.ps.axi.sys,
+            self.hk.sys,
+            *self.interconnect_slaves
+        )
\ No newline at end of file