rename examples to src
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 13 Feb 2022 14:26:07 +0000 (14:26 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 13 Feb 2022 14:26:07 +0000 (14:26 +0000)
examples/crg.py [deleted file]
examples/ls2.py [deleted file]
src/crg.py [new file with mode: 0644]
src/ls2.py [new file with mode: 0644]

diff --git a/examples/crg.py b/examples/crg.py
deleted file mode 100644 (file)
index eb3c651..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-# Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
-# Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
-
-from nmigen import (Module, Elaboratable, Instance, Signal, ClockDomain,
-                    ClockSignal, ResetSignal)
-
-__ALL__ = ["ECPIX5CRG"]
-
-
-class PLL(Elaboratable):
-    def __init__(self, clkin, clksel=Signal(shape=2, reset=2),
-                        clkout1=Signal(), clkout2=Signal(),
-                        clkout3=Signal(), clkout4=Signal(), lock=Signal(),
-                        CLKI_DIV=1, CLKFB_DIV=2, CLK1_DIV=3, CLK2_DIV=24):
-        self.clkin = clkin
-        self.clkout1 = clkout1
-        self.clkout2 = clkout2
-        self.clkout3 = clkout3
-        self.clkout4 = clkout4
-        self.clksel = clksel
-        self.lock = lock
-        self.CLKI_DIV = CLKI_DIV
-        self.CLKFB_DIV = CLKFB_DIV
-        self.CLKOP_DIV = CLK1_DIV
-        self.CLKOS_DIV = CLK2_DIV
-        self.ports = [
-            self.clkin,
-            self.clkout1,
-            self.clkout2,
-            self.clkout3,
-            self.clkout4,
-            self.clksel,
-            self.lock,
-        ]
-
-    def elaborate(self, platform):
-        clkfb = Signal()
-        pll = Instance("EHXPLLL",
-                       p_OUTDIVIDER_MUXA='DIVA',
-                       p_OUTDIVIDER_MUXB='DIVB',
-                       p_CLKOP_ENABLE='ENABLED',
-                       p_CLKOS_ENABLE='ENABLED',
-                       p_CLKOS2_ENABLE='DISABLED',
-                       p_CLKOS3_ENABLE='DISABLED',
-                       p_CLKOP_DIV=self.CLKOP_DIV,
-                       p_CLKOS_DIV=self.CLKOS_DIV,
-                       p_CLKFB_DIV=self.CLKFB_DIV,
-                       p_CLKI_DIV=self.CLKI_DIV,
-                       p_FEEDBK_PATH='INT_OP',
-                       p_CLKOP_TRIM_POL="FALLING",
-                       p_CLKOP_TRIM_DELAY=0,
-                       p_CLKOS_TRIM_POL="FALLING",
-                       p_CLKOS_TRIM_DELAY=0,
-                       i_CLKI=self.clkin,
-                       i_RST=0,
-                       i_STDBY=0,
-                       i_PHASESEL0=0,
-                       i_PHASESEL1=0,
-                       i_PHASEDIR=0,
-                       i_PHASESTEP=0,
-                       i_PHASELOADREG=0,
-                       i_PLLWAKESYNC=0,
-                       i_ENCLKOP=1,
-                       i_ENCLKOS=1,
-                       i_ENCLKOS2=0,
-                       i_ENCLKOS3=0,
-                       o_CLKOP=self.clkout1,
-                       o_CLKOS=self.clkout2,
-                       o_CLKOS2=self.clkout3,
-                       o_CLKOS3=self.clkout4,
-                       o_LOCK=self.lock,
-                       )
-        m = Module()
-        m.submodules += pll
-        return m
-
-
-class ECPIX5CRG(Elaboratable):
-    def __init__(self):
-        ...
-
-    def elaborate(self, platform):
-        m = Module()
-
-        # Get 100Mhz from oscillator
-        clk100 = platform.request("clk100")
-        cd_rawclk = ClockDomain("rawclk", local=True, reset_less=True)
-        m.d.comb += cd_rawclk.clk.eq(clk100)
-        m.domains += cd_rawclk
-
-        # Reset
-        reset = platform.request(platform.default_rst).i
-        gsr0 = Signal()
-        gsr1 = Signal()
-
-        m.submodules += [
-            Instance("FD1S3AX", p_GSR="DISABLED", i_CK=ClockSignal("rawclk"),
-                     i_D=~reset, o_Q=gsr0),
-            Instance("FD1S3AX", p_GSR="DISABLED", i_CK=ClockSignal("rawclk"),
-                     i_D=gsr0,   o_Q=gsr1),
-            Instance("SGSR", i_CLK=ClockSignal("rawclk"), i_GSR=gsr1),
-        ]
-
-        # Power-on delay (655us)
-        podcnt = Signal(16, reset=2**16-1)
-        pod_done = Signal()
-        with m.If(podcnt != 0):
-            m.d.rawclk += podcnt.eq(podcnt-1)
-        m.d.rawclk += pod_done.eq(podcnt == 0)
-
-        # Generating sync2x (200Mhz) and init (25Mhz) from clk100
-        cd_sync2x = ClockDomain("sync2x", local=False)
-        cd_sync2x_unbuf = ClockDomain("sync2x_unbuf", local=False,
-                     reset_less=True)
-        cd_init = ClockDomain("init", local=False)
-        cd_sync = ClockDomain("sync", local=False)
-        cd_dramsync = ClockDomain("dramsync", local=False)
-        m.submodules.pll = pll = PLL(ClockSignal("rawclk"),
-                                     CLKI_DIV=1, CLKFB_DIV=2,
-                                     CLK1_DIV=3, CLK2_DIV=24,
-                                     clkout1=ClockSignal("sync2x_unbuf"),
-                                     clkout2=ClockSignal("init"))
-        m.submodules += Instance("ECLKSYNCB",
-                i_ECLKI = ClockSignal("sync2x_unbuf"),
-                i_STOP  = 0,
-                o_ECLKO = ClockSignal("sync2x"))
-        m.domains += cd_sync2x_unbuf
-        m.domains += cd_sync2x
-        m.domains += cd_init
-        m.domains += cd_sync
-        m.domains += cd_dramsync
-        m.d.comb += ResetSignal("init").eq(~pll.lock|~pod_done)
-        m.d.comb += ResetSignal("sync").eq(~pll.lock|~pod_done)
-        m.d.comb += ResetSignal("dramsync").eq(~pll.lock|~pod_done)
-
-        # # Generating sync (100Mhz) from sync2x
-
-        m.submodules += Instance("CLKDIVF",
-            p_DIV="2.0",
-            i_ALIGNWD=0,
-            i_CLKI=ClockSignal("sync2x"),
-            i_RST=0,
-            o_CDIVX=ClockSignal("sync"))
-        m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
-
-        return m
diff --git a/examples/ls2.py b/examples/ls2.py
deleted file mode 100644 (file)
index 711299e..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-# Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
-# Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
-#
-# Based on code from LambaConcept, from the gram example which is BSD-2-License
-# https://github.com/jeanthom/gram/tree/master/examples
-#
-# Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
-# under EU Grants 871528 and 957073, under the LGPLv3+ License
-
-from nmigen import (Module, Elaboratable, DomainRenamer)
-from nmigen.lib.cdc import ResetSynchronizer
-from nmigen_soc import wishbone, memory
-from nmigen_soc.memory import MemoryMap
-from nmigen_stdio.serial import AsyncSerial
-
-from lambdasoc.cpu.minerva import MinervaCPU
-from lambdasoc.periph.intc import GenericInterruptController
-from lambdasoc.periph.sram import SRAMPeripheral
-from lambdasoc.periph.timer import TimerPeripheral
-from lambdasoc.periph import Peripheral
-from lambdasoc.soc.base import SoC
-from soc.bus.uart_16550 import UART16550 # opencores 16550 uart
-
-from gram.core import gramCore
-from gram.phy.ecp5ddrphy import ECP5DDRPHY
-from gram.modules import MT41K256M16
-from gram.frontend.wishbone import gramWishbone
-
-from nmigen_boards.versa_ecp5 import VersaECP5Platform
-from nmigen_boards.ulx3s import ULX3S_85F_Platform
-from nmigen_boards.arty_a7 import ArtyA7_100Platform
-from nmigen_boards.test.blinky import Blinky
-
-from crg import ECPIX5CRG
-
-import sys
-import os
-
-
-class DDR3SoC(SoC, Elaboratable):
-    def __init__(self, *,
-                 uart_pins, ddr_pins,
-                 ddrphy_addr, dramcore_addr,
-                 ddr_addr, fw_addr=0x0000_0000,
-                 firmware=None,
-                 clk_freq=40e6):
-
-        # set up wishbone bus arbiter and decoder. arbiter routes,
-        # decoder maps local-relative addressed satellites to global addresses
-        self._arbiter = wishbone.Arbiter(addr_width=30, data_width=32,
-                                         granularity=8,
-                                         features={"cti", "bte"})
-        self._decoder = wishbone.Decoder(addr_width=30, data_width=32,
-                                         granularity=8,
-                                         features={"cti", "bte"})
-
-        # default firmware name
-        if firmware is None:
-            firmware = "firmware/main.bin"
-
-        # set up clock request generator
-        self.crg = ECPIX5CRG()
-
-        # set up CPU, and interrupt interface
-        if False:
-            self.cpu = MinervaCPU(reset_address=0)
-            self._arbiter.add(self.cpu.ibus) # I-Cache Master
-            self._arbiter.add(self.cpu.dbus) # D-Cache Master. TODO JTAG master
-            self.intc = GenericInterruptController(width=len(self.cpu.ip))
-
-        # SRAM (but actually a ROM, for firmware), at address 0x0
-        if fw_addr is not None:
-            self.rom = SRAMPeripheral(size=4096, writable=False)
-            with open(firmware, "rb") as f:
-                words = iter(lambda: f.read(self.cpu.data_width // 8), b'')
-                bios  = [int.from_bytes(w, self.cpu.byteorder) for w in words]
-            self.rom.init = bios
-            self._decoder.add(self.rom.bus, addr=fw_addr) # ROM at fw_addr
-
-        # SRAM (read-writeable BRAM)
-        self.ram = SRAMPeripheral(size=4096)
-        self._decoder.add(self.ram.bus, addr=0x8000000) # SRAM at 0x8000_000
-
-        # UART
-        self.uart = UART16550()
-        umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
-        #umap.add_resource(self._mem, name="mem", size=1<<5)
-        self.uart.bus.memory_map = umap
-
-        self._decoder.add(self.uart.bus, addr=0xc0002000) # 16550 UART address
-
-        # DRAM Module
-        self.ddrphy = DomainRenamer("dramsync")(ECP5DDRPHY(ddr_pins,
-                                                sys_clk_freq=100e6))
-        self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr)
-
-        ddrmodule = MT41K256M16(clk_freq, "1:2") # match DDR3 ASIC P/N
-
-        drs = DomainRenamer("dramsync")
-        dramcore = gramCore(phy=self.ddrphy,
-                            geom_settings=ddrmodule.geom_settings,
-                            timing_settings=ddrmodule.timing_settings,
-                            clk_freq=clk_freq)
-        self.dramcore = drs(dramcore)
-        self._decoder.add(self.dramcore.bus, addr=dramcore_addr)
-
-        # map the DRAM onto Wishbone
-        self.drambone = drs(gramWishbone(self.dramcore))
-        self._decoder.add(self.drambone.bus, addr=ddr_addr)
-
-        self.memory_map = self._decoder.bus.memory_map
-
-        self.clk_freq = clk_freq
-
-    def elaborate(self, platform):
-        m = Module()
-        comb = m.d.comb
-
-        # add the peripherals and clock-reset-generator
-        m.submodules.sysclk = self.crg
-
-        if hasattr(self, "rom"):
-            m.submodules.rom = self.rom
-        m.submodules.ram = self.ram
-        m.submodules.uart = self.uart
-        if False:
-            m.submodules.intc = self.intc
-            m.submodules.cpu = self.cpu
-        m.submodules.arbiter = self._arbiter
-        m.submodules.decoder = self._decoder
-        m.submodules.ddrphy = self.ddrphy
-        m.submodules.dramcore = self.dramcore
-        m.submodules.drambone = self.drambone
-
-        # add blinky lights so we know FPGA is alive
-        m.submodules.blinky = Blinky()
-
-        # connect the arbiter (of wishbone masters)
-        # to the decoder (addressing wishbone slaves)
-        comb += self._arbiter.bus.connect(self._decoder.bus)
-
-        if False:
-            # wire up the CPU interrupts
-            comb += self.cpu.ip.eq(self.intc.ip)
-
-        # add uart16550 verilog source. assumes a directory
-        # structure where ls2 has been checked out in a common
-        # subdirectory as https://github.com/freecores/uart16550
-        opencores_16550 = "../../uart16550/rtl/verilog"
-        pth = os.path.split(__file__)[0]
-        pth = os.path.join(pth, opencores_16550)
-        fname = os.path.abspath(pth)
-        print (fname)
-        self.uart.add_verilog_source(fname, platform)
-
-        return m
-
-
-if __name__ == "__main__":
-
-    # create a platform selected from the toolchain. defaults to VERSA_ECP5
-    # only VERSA_ECP5 will work for now because of the DDR3 module
-    fpga = "versa_ecp5"
-    if len(sys.argv) >= 2:
-        fpga = sys.argv[1]
-    platform_kls =  {'versa_ecp5': VersaECP5Platform,
-                     'ulx3s': ULX3S_85F_Platform,
-                     'arty_a7': ArtyA7_100Platform,
-                    }[fpga]
-    toolchain = {'arty_a7': "yosys_nextpnr",
-                 'versa_ecp5': 'Trellis',
-                 'ulx3s': 'Trellis'
-                }.get(fpga, None)
-    platform = platform_kls(toolchain=toolchain)
-
-    # select a firmware file
-    firmware = None
-    fw_addr = None
-    if len(sys.argv) >= 3:
-        firmware = sys.argv[2]
-        fw_addr = 0x0000_0000
-
-    # get DDR and UART resource pins
-    ddr_pins = platform.request("ddr3", 0,
-                                dir={"dq":"-", "dqs":"-"},
-                                xdr={"clk":4, "a":4, "ba":4, "clk_en":4,
-                                     "odt":4, "ras":4, "cas":4, "we":4})
-    uart_pins = platform.request("uart", 0)
-
-    # set up the SOC
-    soc = DDR3SoC(ddrphy_addr=0xff000000, # DRAM firmware init base
-                  dramcore_addr=0x80000000,
-                  ddr_addr=0x10000000,
-                  fw_addr=fw_addr,
-                  ddr_pins=ddr_pins,
-                  uart_pins=uart_pins,
-                  firmware=firmware)
-
-    # build and upload it
-    platform.build(soc, do_program=True)
diff --git a/src/crg.py b/src/crg.py
new file mode 100644 (file)
index 0000000..eb3c651
--- /dev/null
@@ -0,0 +1,146 @@
+# Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
+# Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+
+from nmigen import (Module, Elaboratable, Instance, Signal, ClockDomain,
+                    ClockSignal, ResetSignal)
+
+__ALL__ = ["ECPIX5CRG"]
+
+
+class PLL(Elaboratable):
+    def __init__(self, clkin, clksel=Signal(shape=2, reset=2),
+                        clkout1=Signal(), clkout2=Signal(),
+                        clkout3=Signal(), clkout4=Signal(), lock=Signal(),
+                        CLKI_DIV=1, CLKFB_DIV=2, CLK1_DIV=3, CLK2_DIV=24):
+        self.clkin = clkin
+        self.clkout1 = clkout1
+        self.clkout2 = clkout2
+        self.clkout3 = clkout3
+        self.clkout4 = clkout4
+        self.clksel = clksel
+        self.lock = lock
+        self.CLKI_DIV = CLKI_DIV
+        self.CLKFB_DIV = CLKFB_DIV
+        self.CLKOP_DIV = CLK1_DIV
+        self.CLKOS_DIV = CLK2_DIV
+        self.ports = [
+            self.clkin,
+            self.clkout1,
+            self.clkout2,
+            self.clkout3,
+            self.clkout4,
+            self.clksel,
+            self.lock,
+        ]
+
+    def elaborate(self, platform):
+        clkfb = Signal()
+        pll = Instance("EHXPLLL",
+                       p_OUTDIVIDER_MUXA='DIVA',
+                       p_OUTDIVIDER_MUXB='DIVB',
+                       p_CLKOP_ENABLE='ENABLED',
+                       p_CLKOS_ENABLE='ENABLED',
+                       p_CLKOS2_ENABLE='DISABLED',
+                       p_CLKOS3_ENABLE='DISABLED',
+                       p_CLKOP_DIV=self.CLKOP_DIV,
+                       p_CLKOS_DIV=self.CLKOS_DIV,
+                       p_CLKFB_DIV=self.CLKFB_DIV,
+                       p_CLKI_DIV=self.CLKI_DIV,
+                       p_FEEDBK_PATH='INT_OP',
+                       p_CLKOP_TRIM_POL="FALLING",
+                       p_CLKOP_TRIM_DELAY=0,
+                       p_CLKOS_TRIM_POL="FALLING",
+                       p_CLKOS_TRIM_DELAY=0,
+                       i_CLKI=self.clkin,
+                       i_RST=0,
+                       i_STDBY=0,
+                       i_PHASESEL0=0,
+                       i_PHASESEL1=0,
+                       i_PHASEDIR=0,
+                       i_PHASESTEP=0,
+                       i_PHASELOADREG=0,
+                       i_PLLWAKESYNC=0,
+                       i_ENCLKOP=1,
+                       i_ENCLKOS=1,
+                       i_ENCLKOS2=0,
+                       i_ENCLKOS3=0,
+                       o_CLKOP=self.clkout1,
+                       o_CLKOS=self.clkout2,
+                       o_CLKOS2=self.clkout3,
+                       o_CLKOS3=self.clkout4,
+                       o_LOCK=self.lock,
+                       )
+        m = Module()
+        m.submodules += pll
+        return m
+
+
+class ECPIX5CRG(Elaboratable):
+    def __init__(self):
+        ...
+
+    def elaborate(self, platform):
+        m = Module()
+
+        # Get 100Mhz from oscillator
+        clk100 = platform.request("clk100")
+        cd_rawclk = ClockDomain("rawclk", local=True, reset_less=True)
+        m.d.comb += cd_rawclk.clk.eq(clk100)
+        m.domains += cd_rawclk
+
+        # Reset
+        reset = platform.request(platform.default_rst).i
+        gsr0 = Signal()
+        gsr1 = Signal()
+
+        m.submodules += [
+            Instance("FD1S3AX", p_GSR="DISABLED", i_CK=ClockSignal("rawclk"),
+                     i_D=~reset, o_Q=gsr0),
+            Instance("FD1S3AX", p_GSR="DISABLED", i_CK=ClockSignal("rawclk"),
+                     i_D=gsr0,   o_Q=gsr1),
+            Instance("SGSR", i_CLK=ClockSignal("rawclk"), i_GSR=gsr1),
+        ]
+
+        # Power-on delay (655us)
+        podcnt = Signal(16, reset=2**16-1)
+        pod_done = Signal()
+        with m.If(podcnt != 0):
+            m.d.rawclk += podcnt.eq(podcnt-1)
+        m.d.rawclk += pod_done.eq(podcnt == 0)
+
+        # Generating sync2x (200Mhz) and init (25Mhz) from clk100
+        cd_sync2x = ClockDomain("sync2x", local=False)
+        cd_sync2x_unbuf = ClockDomain("sync2x_unbuf", local=False,
+                     reset_less=True)
+        cd_init = ClockDomain("init", local=False)
+        cd_sync = ClockDomain("sync", local=False)
+        cd_dramsync = ClockDomain("dramsync", local=False)
+        m.submodules.pll = pll = PLL(ClockSignal("rawclk"),
+                                     CLKI_DIV=1, CLKFB_DIV=2,
+                                     CLK1_DIV=3, CLK2_DIV=24,
+                                     clkout1=ClockSignal("sync2x_unbuf"),
+                                     clkout2=ClockSignal("init"))
+        m.submodules += Instance("ECLKSYNCB",
+                i_ECLKI = ClockSignal("sync2x_unbuf"),
+                i_STOP  = 0,
+                o_ECLKO = ClockSignal("sync2x"))
+        m.domains += cd_sync2x_unbuf
+        m.domains += cd_sync2x
+        m.domains += cd_init
+        m.domains += cd_sync
+        m.domains += cd_dramsync
+        m.d.comb += ResetSignal("init").eq(~pll.lock|~pod_done)
+        m.d.comb += ResetSignal("sync").eq(~pll.lock|~pod_done)
+        m.d.comb += ResetSignal("dramsync").eq(~pll.lock|~pod_done)
+
+        # # Generating sync (100Mhz) from sync2x
+
+        m.submodules += Instance("CLKDIVF",
+            p_DIV="2.0",
+            i_ALIGNWD=0,
+            i_CLKI=ClockSignal("sync2x"),
+            i_RST=0,
+            o_CDIVX=ClockSignal("sync"))
+        m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
+
+        return m
diff --git a/src/ls2.py b/src/ls2.py
new file mode 100644 (file)
index 0000000..711299e
--- /dev/null
@@ -0,0 +1,200 @@
+# Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
+# Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+#
+# Based on code from LambaConcept, from the gram example which is BSD-2-License
+# https://github.com/jeanthom/gram/tree/master/examples
+#
+# Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
+# under EU Grants 871528 and 957073, under the LGPLv3+ License
+
+from nmigen import (Module, Elaboratable, DomainRenamer)
+from nmigen.lib.cdc import ResetSynchronizer
+from nmigen_soc import wishbone, memory
+from nmigen_soc.memory import MemoryMap
+from nmigen_stdio.serial import AsyncSerial
+
+from lambdasoc.cpu.minerva import MinervaCPU
+from lambdasoc.periph.intc import GenericInterruptController
+from lambdasoc.periph.sram import SRAMPeripheral
+from lambdasoc.periph.timer import TimerPeripheral
+from lambdasoc.periph import Peripheral
+from lambdasoc.soc.base import SoC
+from soc.bus.uart_16550 import UART16550 # opencores 16550 uart
+
+from gram.core import gramCore
+from gram.phy.ecp5ddrphy import ECP5DDRPHY
+from gram.modules import MT41K256M16
+from gram.frontend.wishbone import gramWishbone
+
+from nmigen_boards.versa_ecp5 import VersaECP5Platform
+from nmigen_boards.ulx3s import ULX3S_85F_Platform
+from nmigen_boards.arty_a7 import ArtyA7_100Platform
+from nmigen_boards.test.blinky import Blinky
+
+from crg import ECPIX5CRG
+
+import sys
+import os
+
+
+class DDR3SoC(SoC, Elaboratable):
+    def __init__(self, *,
+                 uart_pins, ddr_pins,
+                 ddrphy_addr, dramcore_addr,
+                 ddr_addr, fw_addr=0x0000_0000,
+                 firmware=None,
+                 clk_freq=40e6):
+
+        # set up wishbone bus arbiter and decoder. arbiter routes,
+        # decoder maps local-relative addressed satellites to global addresses
+        self._arbiter = wishbone.Arbiter(addr_width=30, data_width=32,
+                                         granularity=8,
+                                         features={"cti", "bte"})
+        self._decoder = wishbone.Decoder(addr_width=30, data_width=32,
+                                         granularity=8,
+                                         features={"cti", "bte"})
+
+        # default firmware name
+        if firmware is None:
+            firmware = "firmware/main.bin"
+
+        # set up clock request generator
+        self.crg = ECPIX5CRG()
+
+        # set up CPU, and interrupt interface
+        if False:
+            self.cpu = MinervaCPU(reset_address=0)
+            self._arbiter.add(self.cpu.ibus) # I-Cache Master
+            self._arbiter.add(self.cpu.dbus) # D-Cache Master. TODO JTAG master
+            self.intc = GenericInterruptController(width=len(self.cpu.ip))
+
+        # SRAM (but actually a ROM, for firmware), at address 0x0
+        if fw_addr is not None:
+            self.rom = SRAMPeripheral(size=4096, writable=False)
+            with open(firmware, "rb") as f:
+                words = iter(lambda: f.read(self.cpu.data_width // 8), b'')
+                bios  = [int.from_bytes(w, self.cpu.byteorder) for w in words]
+            self.rom.init = bios
+            self._decoder.add(self.rom.bus, addr=fw_addr) # ROM at fw_addr
+
+        # SRAM (read-writeable BRAM)
+        self.ram = SRAMPeripheral(size=4096)
+        self._decoder.add(self.ram.bus, addr=0x8000000) # SRAM at 0x8000_000
+
+        # UART
+        self.uart = UART16550()
+        umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
+        #umap.add_resource(self._mem, name="mem", size=1<<5)
+        self.uart.bus.memory_map = umap
+
+        self._decoder.add(self.uart.bus, addr=0xc0002000) # 16550 UART address
+
+        # DRAM Module
+        self.ddrphy = DomainRenamer("dramsync")(ECP5DDRPHY(ddr_pins,
+                                                sys_clk_freq=100e6))
+        self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr)
+
+        ddrmodule = MT41K256M16(clk_freq, "1:2") # match DDR3 ASIC P/N
+
+        drs = DomainRenamer("dramsync")
+        dramcore = gramCore(phy=self.ddrphy,
+                            geom_settings=ddrmodule.geom_settings,
+                            timing_settings=ddrmodule.timing_settings,
+                            clk_freq=clk_freq)
+        self.dramcore = drs(dramcore)
+        self._decoder.add(self.dramcore.bus, addr=dramcore_addr)
+
+        # map the DRAM onto Wishbone
+        self.drambone = drs(gramWishbone(self.dramcore))
+        self._decoder.add(self.drambone.bus, addr=ddr_addr)
+
+        self.memory_map = self._decoder.bus.memory_map
+
+        self.clk_freq = clk_freq
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+
+        # add the peripherals and clock-reset-generator
+        m.submodules.sysclk = self.crg
+
+        if hasattr(self, "rom"):
+            m.submodules.rom = self.rom
+        m.submodules.ram = self.ram
+        m.submodules.uart = self.uart
+        if False:
+            m.submodules.intc = self.intc
+            m.submodules.cpu = self.cpu
+        m.submodules.arbiter = self._arbiter
+        m.submodules.decoder = self._decoder
+        m.submodules.ddrphy = self.ddrphy
+        m.submodules.dramcore = self.dramcore
+        m.submodules.drambone = self.drambone
+
+        # add blinky lights so we know FPGA is alive
+        m.submodules.blinky = Blinky()
+
+        # connect the arbiter (of wishbone masters)
+        # to the decoder (addressing wishbone slaves)
+        comb += self._arbiter.bus.connect(self._decoder.bus)
+
+        if False:
+            # wire up the CPU interrupts
+            comb += self.cpu.ip.eq(self.intc.ip)
+
+        # add uart16550 verilog source. assumes a directory
+        # structure where ls2 has been checked out in a common
+        # subdirectory as https://github.com/freecores/uart16550
+        opencores_16550 = "../../uart16550/rtl/verilog"
+        pth = os.path.split(__file__)[0]
+        pth = os.path.join(pth, opencores_16550)
+        fname = os.path.abspath(pth)
+        print (fname)
+        self.uart.add_verilog_source(fname, platform)
+
+        return m
+
+
+if __name__ == "__main__":
+
+    # create a platform selected from the toolchain. defaults to VERSA_ECP5
+    # only VERSA_ECP5 will work for now because of the DDR3 module
+    fpga = "versa_ecp5"
+    if len(sys.argv) >= 2:
+        fpga = sys.argv[1]
+    platform_kls =  {'versa_ecp5': VersaECP5Platform,
+                     'ulx3s': ULX3S_85F_Platform,
+                     'arty_a7': ArtyA7_100Platform,
+                    }[fpga]
+    toolchain = {'arty_a7': "yosys_nextpnr",
+                 'versa_ecp5': 'Trellis',
+                 'ulx3s': 'Trellis'
+                }.get(fpga, None)
+    platform = platform_kls(toolchain=toolchain)
+
+    # select a firmware file
+    firmware = None
+    fw_addr = None
+    if len(sys.argv) >= 3:
+        firmware = sys.argv[2]
+        fw_addr = 0x0000_0000
+
+    # get DDR and UART resource pins
+    ddr_pins = platform.request("ddr3", 0,
+                                dir={"dq":"-", "dqs":"-"},
+                                xdr={"clk":4, "a":4, "ba":4, "clk_en":4,
+                                     "odt":4, "ras":4, "cas":4, "we":4})
+    uart_pins = platform.request("uart", 0)
+
+    # set up the SOC
+    soc = DDR3SoC(ddrphy_addr=0xff000000, # DRAM firmware init base
+                  dramcore_addr=0x80000000,
+                  ddr_addr=0x10000000,
+                  fw_addr=fw_addr,
+                  ddr_pins=ddr_pins,
+                  uart_pins=uart_pins,
+                  firmware=firmware)
+
+    # build and upload it
+    platform.build(soc, do_program=True)