soc.cpu: generate BIOS configuration from SoC constants.
authorJean-François Nguyen <jf@lambdaconcept.com>
Fri, 29 Oct 2021 17:31:23 +0000 (19:31 +0200)
committerJean-François Nguyen <jf@lambdaconcept.com>
Fri, 29 Oct 2021 17:36:53 +0000 (19:36 +0200)
Also, merge examples/{sdram,sram}_soc as minerva_soc.

examples/minerva_soc.py [new file with mode: 0644]
examples/sdram_soc.py [deleted file]
examples/sram_soc.py [deleted file]
lambdasoc/cores/litedram.py
lambdasoc/cores/utils.py [new file with mode: 0644]
lambdasoc/soc/base.py
lambdasoc/soc/cpu.py
setup.py

diff --git a/examples/minerva_soc.py b/examples/minerva_soc.py
new file mode 100644 (file)
index 0000000..02675f2
--- /dev/null
@@ -0,0 +1,395 @@
+import argparse
+from collections import OrderedDict
+
+from nmigen import *
+from nmigen.lib.cdc import ResetSynchronizer
+from nmigen.build import *
+
+from nmigen_soc import wishbone
+from nmigen_soc.periph import ConstantMap
+
+from nmigen_stdio.serial import AsyncSerial
+
+from nmigen_boards.arty_a7 import ArtyA7_35Platform
+from nmigen_boards.ecpix5 import ECPIX545Platform, ECPIX585Platform
+
+from lambdasoc.cpu.minerva import MinervaCPU
+from lambdasoc.periph.intc import GenericInterruptController
+from lambdasoc.periph.serial import AsyncSerialPeripheral
+from lambdasoc.periph.sram import SRAMPeripheral
+from lambdasoc.periph.timer import TimerPeripheral
+from lambdasoc.periph.sdram import SDRAMPeripheral
+
+from lambdasoc.soc.cpu import CPUSoC, BIOSBuilder
+
+from lambdasoc.cores.pll.lattice_ecp5 import PLL_LatticeECP5
+from lambdasoc.cores.pll.xilinx_7series import PLL_Xilinx7Series
+from lambdasoc.cores import litedram
+from lambdasoc.cores.utils import request_bare
+
+from lambdasoc.sim.blackboxes.serial import AsyncSerial_Blackbox
+from lambdasoc.sim.platform import CXXRTLPlatform
+
+
+__all__ = ["MinervaSoC"]
+
+
+class _ClockResetGenerator(Elaboratable):
+    def __init__(self, *, sync_clk_freq, with_sdram):
+        if not isinstance(sync_clk_freq, (int, float)) or sync_clk_freq <= 0:
+            raise ValueError("Sync domain clock frequency must be a positive integer or float, "
+                             "not {!r}"
+                             .format(sync_clk_freq))
+        self.sync_clk_freq = sync_clk_freq
+        self.with_sdram    = bool(with_sdram)
+
+    def elaborate(self, platform):
+        m = Module()
+
+        m.domains += [
+            ClockDomain("_ref", reset_less=platform.default_rst is None, local=True),
+            ClockDomain("sync"),
+        ]
+
+        m.d.comb += ClockSignal("_ref").eq(platform.request(platform.default_clk, 0).i)
+        if platform.default_rst is not None:
+            m.d.comb += ResetSignal("_ref").eq(platform.request(platform.default_rst, 0).i)
+
+        # The LiteDRAM core provides its own PLL, which drives the litedram_user clock domain.
+        # We reuse this clock domain as the sync domain, in order to avoid CDC between LiteDRAM
+        # and the SoC interconnect.
+        if self.with_sdram:
+            m.domains += ClockDomain("litedram_input")
+            m.d.comb += ClockSignal("litedram_input").eq(ClockSignal("_ref"))
+            if platform.default_rst is not None:
+                m.d.comb += ResetSignal("litedram_input").eq(ResetSignal("_ref"))
+
+            m.domains += ClockDomain("litedram_user")
+            m.d.comb += [
+                ClockSignal("sync").eq(ClockSignal("litedram_user")),
+                ResetSignal("sync").eq(ResetSignal("litedram_user")),
+            ]
+
+        # In simulation mode, the sync clock domain is directly driven by the platform clock.
+        elif isinstance(platform, CXXRTLPlatform):
+            assert self.sync_clk_freq == platform.default_clk_frequency
+            m.d.comb += ClockSignal("sync").eq(ClockSignal("_ref"))
+            if platform.default_rst is not None:
+                m.d.comb += ResetSignal("sync").eq(ResetSignal("_ref"))
+
+        # Otherwise, we use a PLL to drive the sync clock domain.
+        else:
+            if isinstance(platform, ArtyA7_35Platform):
+                sync_pll_params = PLL_Xilinx7Series.Parameters(
+                    i_domain     = "_ref",
+                    i_freq       = platform.default_clk_frequency,
+                    i_reset_less = platform.default_rst is None,
+                    o_domain     = "sync",
+                    o_freq       = self.sync_clk_freq,
+                )
+                m.submodules.sync_pll = sync_pll = PLL_Xilinx7Series(sync_pll_params)
+            elif isinstance(platform, (ECPIX545Platform, ECPIX585Platform)):
+                sync_pll_params = PLL_LatticeECP5.Parameters(
+                    i_domain     = "_ref",
+                    i_freq       = platform.default_clk_frequency,
+                    i_reset_less = platform.default_rst is None,
+                    o_domain     = "sync",
+                    o_freq       = self.sync_clk_freq,
+                )
+                m.submodules.sync_pll = sync_pll = PLL_LatticeECP5(sync_pll_params)
+            else:
+                assert False
+
+            if self.platform.default_rst is not None:
+                sync_pll_arst = ~sync_pll.locked | ResetSignal("_ref")
+            else:
+                sync_pll_arst = ~sync_pll.locked
+
+            m.submodules += ResetSynchronizer(sync_pll_arst, domain="sync")
+
+        return m
+
+
+class MinervaSoC(CPUSoC, Elaboratable):
+    def __init__(self,
+            sync_clk_freq,
+            cpu_core,
+            bootrom_addr,
+            bootrom_size,
+            scratchpad_addr,
+            scratchpad_size,
+            uart_core,
+            uart_addr,
+            uart_irqno,
+            timer_addr,
+            timer_width,
+            timer_irqno):
+        if not isinstance(sync_clk_freq, (int, float)) or sync_clk_freq <= 0:
+            raise ValueError("Sync domain clock frequency must be a positive integer or float, "
+                             "not {!r}"
+                             .format(sync_clk_freq))
+        self.sync_clk_freq = int(sync_clk_freq)
+
+        if not isinstance(cpu_core, MinervaCPU):
+            raise TypeError("CPU core must be an instance of MinervaCPU, not {!r}"
+                            .format(cpu_core))
+        self.cpu = cpu_core
+
+        self._arbiter = wishbone.Arbiter(addr_width=30, data_width=32, granularity=8,
+                                         features={"cti", "bte", "err"})
+        self._decoder = wishbone.Decoder(addr_width=30, data_width=32, granularity=8,
+                                         features={"cti", "bte", "err"})
+
+        self._arbiter.add(self.cpu.ibus)
+        self._arbiter.add(self.cpu.dbus)
+
+        self.intc = GenericInterruptController(width=len(self.cpu.ip))
+
+        self.bootrom = SRAMPeripheral(size=bootrom_size, writable=False)
+        self._decoder.add(self.bootrom.bus, addr=bootrom_addr)
+
+        self.scratchpad = SRAMPeripheral(size=scratchpad_size)
+        self._decoder.add(self.scratchpad.bus, addr=scratchpad_addr)
+
+        self.uart = AsyncSerialPeripheral(core=uart_core)
+        self._decoder.add(self.uart.bus, addr=uart_addr)
+        self.intc.add_irq(self.uart.irq, index=uart_irqno)
+
+        self.timer = TimerPeripheral(width=timer_width)
+        self._decoder.add(self.timer.bus, addr=timer_addr)
+        self.intc.add_irq(self.timer.irq, index=timer_irqno)
+
+        self._sdram  = None
+        self._sram   = None
+
+    @property
+    def memory_map(self):
+        return self._decoder.bus.memory_map
+
+    @property
+    def constants(self):
+        return super().constants.union(
+            SDRAM  = self.sdram .constant_map if self.sdram  is not None else None,
+            SOC    = ConstantMap(
+                WITH_SDRAM        = self.sdram  is not None,
+                MEMTEST_ADDR_SIZE = 8192,
+                MEMTEST_DATA_SIZE = 8192,
+            ),
+        )
+
+    @property
+    def mainram(self):
+        assert not (self._sdram and self.sram)
+        return self._sdram or self._sram
+
+    @property
+    def sdram(self):
+        return self._sdram
+
+    @property
+    def sram(self):
+        return self._sram
+
+    def add_sdram(self, core, *, addr, cache_size):
+        if self.mainram is not None:
+            raise AttributeError("Main RAM has already been set to {!r}".format(self.mainram))
+        if core.config.user_clk_freq != self.sync_clk_freq:
+            raise ValueError("LiteDRAM user domain clock frequency ({} MHz) must match sync "
+                             "domain clock frequency ({} MHz)"
+                             .format(core.config.user_clk_freq / 1e6, self.sync_clk_freq / 1e6))
+        self._sdram = SDRAMPeripheral(core=core, cache_size=cache_size)
+        self._decoder.add(self._sdram.bus, addr=addr)
+
+    def add_internal_sram(self, *, addr, size):
+        if self.mainram is not None:
+            raise AttributeError("Main RAM has already been set to {!r}".format(self.mainram))
+        self._sram = SRAMPeripheral(size=size)
+        self._decoder.add(self._sram.bus, addr=addr)
+
+    def elaborate(self, platform):
+        m = Module()
+
+        m.submodules.crg = _ClockResetGenerator(
+            sync_clk_freq = self.sync_clk_freq,
+            with_sdram    = self.sdram is not None,
+        )
+
+        m.submodules.cpu        = self.cpu
+        m.submodules.arbiter    = self._arbiter
+        m.submodules.decoder    = self._decoder
+        m.submodules.uart       = self.uart
+        m.submodules.timer      = self.timer
+        m.submodules.intc       = self.intc
+        m.submodules.bootrom    = self.bootrom
+        m.submodules.scratchpad = self.scratchpad
+
+        if self.sdram is not None:
+            m.submodules.sdram = self.sdram
+        if self.sram is not None:
+            m.submodules.sram = self.sram
+
+        m.d.comb += [
+            self._arbiter.bus.connect(self._decoder.bus),
+            self.cpu.ip.eq(self.intc.ip),
+        ]
+
+        return m
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--build-dir", type=str,
+            default="build/minerva_soc",
+            help="local build directory (default: 'build/sdram_soc')")
+    parser.add_argument("--platform", type=str,
+            choices=("sim", "arty_a7", "ecpix5_45", "ecpix5_85"),
+            default="sim",
+            help="target platform")
+    parser.add_argument("--sync-clk-freq", type=int,
+            default=75,
+            help="SoC clock frequency, in MHz. (default: 75)")
+    parser.add_argument("--with-sdram", action="store_true",
+            help="enable SDRAM")
+    parser.add_argument("--internal-sram-size", type=int,
+            default=8192,
+            help="Internal RAM size, in bytes. Ignored if --with-sdram is provided. "
+                 "(default: 8192)")
+    parser.add_argument("--baudrate", type=int,
+            default=9600,
+            help="UART baudrate (default: 9600)")
+    args = parser.parse_args()
+
+    # Platform selection
+
+    if args.platform == "sim":
+        platform = CXXRTLPlatform()
+    elif args.platform == "arty_a7":
+        platform = ArtyA7_35Platform()
+    elif args.platform == "ecpix5_45":
+        platform = ECPIX545Platform()
+    elif args.platform == "ecpix5_85":
+        platform = ECPIX585Platform()
+    else:
+        assert False
+
+    # LiteDRAM
+
+    if args.with_sdram:
+        if isinstance(platform, CXXRTLPlatform):
+            litedram_config = litedram.ECP5Config(
+                memtype        = "DDR3",
+                module_name    = "MT41K256M16",
+                module_bytes   = 2,
+                module_ranks   = 1,
+                input_clk_freq = int(platform.default_clk_frequency),
+                user_clk_freq  = int(platform.default_clk_frequency),
+                init_clk_freq  = int(1e6),
+            )
+        elif isinstance(platform, ArtyA7_35Platform):
+            litedram_config = litedram.Artix7Config(
+                memtype          = "DDR3",
+                speedgrade       = "-1",
+                cmd_latency      = 0,
+                module_name      = "MT41K128M16",
+                module_bytes     = 2,
+                module_ranks     = 1,
+                rtt_nom          = 60,
+                rtt_wr           = 60,
+                ron              = 34,
+                input_clk_freq   = int(platform.default_clk_frequency),
+                user_clk_freq    = int(args.sync_clk_freq * 1e6),
+                iodelay_clk_freq = int(200e6),
+            )
+        elif isinstance(platform, (ECPIX545Platform, ECPIX585Platform)):
+            litedram_config = litedram.ECP5Config(
+                memtype        = "DDR3",
+                module_name    = "MT41K256M16",
+                module_bytes   = 2,
+                module_ranks   = 1,
+                input_clk_freq = int(platform.default_clk_frequency),
+                user_clk_freq  = int(args.sync_clk_freq * 1e6),
+                init_clk_freq  = int(25e6),
+            )
+        else:
+            assert False
+
+        if isinstance(platform, CXXRTLPlatform):
+            litedram_pins = None
+        else:
+            litedram_pins = request_bare(platform, "ddr3", 0)
+
+        litedram_core = litedram.Core(litedram_config, pins=litedram_pins)
+        litedram_core.build(litedram.Builder(), platform, args.build_dir,
+                            sim=isinstance(platform, CXXRTLPlatform))
+        mainram_size  = litedram_core.size
+    else:
+        litedram_core = None
+        mainram_size  = args.internal_sram_size
+
+    # UART
+
+    if isinstance(platform, CXXRTLPlatform):
+        uart_core = AsyncSerial_Blackbox(
+            data_bits = 8,
+            divisor   = 1,
+        )
+    else:
+        uart_core = AsyncSerial(
+            data_bits = 8,
+            divisor   = int(args.sync_clk_freq * 1e6 // args.baudrate),
+            pins      = platform.request("uart", 0),
+        )
+
+    # SoC and BIOS
+
+    if isinstance(platform, CXXRTLPlatform):
+        sync_clk_freq = platform.default_clk_frequency
+    else:
+        sync_clk_freq = int(args.sync_clk_freq * 1e6)
+
+    soc = MinervaSoC(
+        sync_clk_freq = sync_clk_freq,
+
+        cpu_core = MinervaCPU(
+            reset_address = 0x00000000,
+            with_icache   = True,
+            icache_nlines = 16,
+            icache_nwords = 4,
+            icache_nways  = 1,
+            icache_base   = 0x40000000,
+            icache_limit  = 0x40000000 + mainram_size,
+            with_dcache   = True,
+            dcache_nlines = 16,
+            dcache_nwords = 4,
+            dcache_nways  = 1,
+            dcache_base   = 0x40000000,
+            dcache_limit  = 0x40000000 + mainram_size,
+            with_muldiv   = True,
+        ),
+
+        bootrom_addr    = 0x00000000,
+        bootrom_size    = 0x8000,
+        scratchpad_addr = 0x00008000,
+        scratchpad_size = 0x1000,
+
+        uart_addr       = 0x80000000,
+        uart_core       = uart_core,
+        uart_irqno      = 1,
+        timer_addr      = 0x80001000,
+        timer_width     = 32,
+        timer_irqno     = 0,
+    )
+
+    if args.with_sdram:
+        soc.add_sdram(litedram_core, addr=0x40000000, cache_size=4096)
+    else:
+        soc.add_internal_sram(addr=0x40000000, size=args.internal_sram_size)
+
+    soc.build(build_dir=args.build_dir, do_init=True)
+
+    if isinstance(platform, CXXRTLPlatform):
+        platform.build(soc, build_dir=args.build_dir, blackboxes={
+            "lambdasoc.sim.blackboxes.serial": "serial_pty",
+        })
+    else:
+        platform.build(soc, build_dir=args.build_dir, do_program=True)
diff --git a/examples/sdram_soc.py b/examples/sdram_soc.py
deleted file mode 100644 (file)
index d3f5dcb..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-import argparse
-
-from nmigen import *
-from nmigen.build import *
-from nmigen_soc import wishbone
-
-from lambdasoc.cpu.minerva import MinervaCPU
-from lambdasoc.periph.intc import GenericInterruptController
-from lambdasoc.periph.serial import AsyncSerialPeripheral
-from lambdasoc.periph.sram import SRAMPeripheral
-from lambdasoc.periph.timer import TimerPeripheral
-from lambdasoc.periph.sdram import SDRAMPeripheral
-from lambdasoc.soc.cpu import CPUSoC
-
-from lambdasoc.cores import litedram
-
-
-__all__ = ["SDRAMSoC"]
-
-
-class SDRAMSoC(CPUSoC, Elaboratable):
-    def __init__(self, *, reset_addr, clk_freq,
-                 rom_addr, rom_size,
-                 ram_addr, ram_size,
-                 uart_addr, uart_divisor, uart_pins,
-                 timer_addr, timer_width,
-                 sdram_addr, sdram_core, sdram_cache_size):
-        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"})
-
-        self.cpu = MinervaCPU(
-            reset_address=reset_addr,
-            with_icache=True, icache_nlines=64, icache_nwords=4, icache_nways=1,
-                              icache_base=sdram_addr, icache_limit=sdram_addr + sdram_core.size,
-            with_dcache=True, dcache_nlines=64, dcache_nwords=4, dcache_nways=1,
-                              dcache_base=sdram_addr, dcache_limit=sdram_addr + sdram_core.size,
-            with_muldiv=True,
-        )
-        self._arbiter.add(self.cpu.ibus)
-        self._arbiter.add(self.cpu.dbus)
-
-        self.rom = SRAMPeripheral(size=rom_size, writable=False)
-        self._decoder.add(self.rom.bus, addr=rom_addr)
-
-        self.ram = SRAMPeripheral(size=ram_size)
-        self._decoder.add(self.ram.bus, addr=ram_addr)
-
-        self.sdram = SDRAMPeripheral(core=sdram_core, cache_size=sdram_cache_size)
-        self._decoder.add(self.sdram.bus, addr=sdram_addr)
-
-        self.uart = AsyncSerialPeripheral(divisor=uart_divisor, pins=uart_pins)
-        self._decoder.add(self.uart.bus, addr=uart_addr)
-
-        self.timer = TimerPeripheral(width=timer_width)
-        self._decoder.add(self.timer.bus, addr=timer_addr)
-
-        self.intc = GenericInterruptController(width=len(self.cpu.ip))
-        self.intc.add_irq(self.timer.irq, 0)
-        self.intc.add_irq(self.uart .irq, 1)
-
-        self.memory_map = self._decoder.bus.memory_map
-
-        self.clk_freq = clk_freq
-
-    def elaborate(self, platform):
-        m = Module()
-
-        m.domains += [
-            ClockDomain("litedram_input"),
-            ClockDomain("litedram_user"),
-            ClockDomain("sync"),
-        ]
-
-        m.d.comb += [
-            ClockSignal("litedram_input").eq(platform.request("clk100", 0).i),
-
-            ClockSignal("sync").eq(ClockSignal("litedram_user")),
-            ResetSignal("sync").eq(ResetSignal("litedram_user")),
-        ]
-
-        m.submodules.arbiter = self._arbiter
-        m.submodules.cpu     = self.cpu
-
-        m.submodules.decoder = self._decoder
-        m.submodules.rom     = self.rom
-        m.submodules.ram     = self.ram
-        m.submodules.sdram   = self.sdram
-        m.submodules.uart    = self.uart
-        m.submodules.timer   = self.timer
-        m.submodules.intc    = self.intc
-
-        m.d.comb += [
-            self._arbiter.bus.connect(self._decoder.bus),
-            self.cpu.ip.eq(self.intc.ip),
-        ]
-
-        return m
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser()
-    parser.add_argument("--platform", type=str,
-            choices=("arty_a7", "ecpix5_85"),
-            help="target platform")
-    parser.add_argument("--baudrate", type=int,
-            default=9600,
-            help="UART baudrate (default: 9600)")
-    parser.add_argument("--build-dir", type=str,
-            default="build",
-            help="local build directory (default: 'build')")
-    args = parser.parse_args()
-
-    if args.platform == "arty_a7":
-        from nmigen_boards.arty_a7 import ArtyA7_35Platform
-        platform = ArtyA7_35Platform()
-        litedram_cfg = litedram.Artix7Config(
-            memtype          = "DDR3",
-            speedgrade       = "-1",
-            cmd_latency      = 0,
-            module_name      = "MT41K128M16",
-            module_bytes     = 2,
-            module_ranks     = 1,
-            rtt_nom          = 60,
-            rtt_wr           = 60,
-            ron              = 34,
-            input_clk_freq   = int(100e6),
-            user_clk_freq    = int(100e6),
-            iodelay_clk_freq = int(200e6),
-        )
-    elif args.platform == "ecpix5_85":
-        from nmigen_boards.ecpix5 import ECPIX585Platform
-        platform = ECPIX585Platform()
-        litedram_cfg = litedram.ECP5Config(
-            memtype        = "DDR3",
-            module_name    = "MT41K256M16",
-            module_bytes   = 2,
-            module_ranks   = 1,
-            input_clk_freq = int(100e6),
-            user_clk_freq  = int(70e6),
-            init_clk_freq  = int(25e6),
-        )
-    else:
-        assert False
-
-    litedram_pins = litedram_cfg.request_pins(platform, "ddr3", 0)
-    litedram_core = litedram.Core(litedram_cfg, pins=litedram_pins)
-
-    litedram_builder   = litedram.Builder()
-    litedram_build_dir = f"{args.build_dir}/litedram"
-    litedram_products  = litedram_core.build(litedram_builder, build_dir=litedram_build_dir)
-
-    litedram_core_v = f"{litedram_core.name}/{litedram_core.name}.v"
-    platform.add_file(litedram_core_v, litedram_products.get(litedram_core_v, mode="t"))
-
-    soc = SDRAMSoC(
-         reset_addr=0x30000000, clk_freq=litedram_cfg.user_clk_freq,
-          uart_addr=0x00005000, uart_divisor=int(litedram_cfg.user_clk_freq // args.baudrate),
-                                uart_pins=platform.request("uart", 0),
-         timer_addr=0x00006000, timer_width=32,
-
-           rom_addr=0x30000000, rom_size=0x8000,
-           ram_addr=0x30008000, ram_size=0x1000,
-         sdram_addr=0x40000000, sdram_core=litedram_core, sdram_cache_size=8192,
-    )
-
-    soc.build(build_dir=f"{args.build_dir}/soc", litedram_dir=litedram_build_dir, do_init=True)
-
-    platform.build(soc, build_dir=args.build_dir, do_program=True)
diff --git a/examples/sram_soc.py b/examples/sram_soc.py
deleted file mode 100644 (file)
index a3ecbc3..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-import argparse
-import importlib
-
-from nmigen import *
-from nmigen_soc import wishbone
-
-from lambdasoc.cpu.minerva import MinervaCPU
-from lambdasoc.periph.intc import GenericInterruptController
-from lambdasoc.periph.serial import AsyncSerialPeripheral
-from lambdasoc.periph.sram import SRAMPeripheral
-from lambdasoc.periph.timer import TimerPeripheral
-from lambdasoc.soc.cpu import CPUSoC
-
-
-__all__ = ["SRAMSoC"]
-
-
-class SRAMSoC(CPUSoC, Elaboratable):
-    def __init__(self, *, reset_addr, clk_freq,
-                 rom_addr, rom_size,
-                 ram_addr, ram_size,
-                 uart_addr, uart_divisor, uart_pins,
-                 timer_addr, timer_width):
-        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"})
-
-        self.cpu = MinervaCPU(reset_address=reset_addr)
-        self._arbiter.add(self.cpu.ibus)
-        self._arbiter.add(self.cpu.dbus)
-
-        self.rom = SRAMPeripheral(size=rom_size, writable=False)
-        self._decoder.add(self.rom.bus, addr=rom_addr)
-
-        self.ram = SRAMPeripheral(size=ram_size)
-        self._decoder.add(self.ram.bus, addr=ram_addr)
-
-        self.uart = AsyncSerialPeripheral(divisor=uart_divisor, pins=uart_pins)
-        self._decoder.add(self.uart.bus, addr=uart_addr)
-
-        self.timer = TimerPeripheral(width=timer_width)
-        self._decoder.add(self.timer.bus, addr=timer_addr)
-
-        self.intc = GenericInterruptController(width=len(self.cpu.ip))
-        self.intc.add_irq(self.timer.irq, 0)
-        self.intc.add_irq(self.uart .irq, 1)
-
-        self.memory_map = self._decoder.bus.memory_map
-
-        self.clk_freq = clk_freq
-
-    def elaborate(self, platform):
-        m = Module()
-
-        m.submodules.arbiter = self._arbiter
-        m.submodules.cpu     = self.cpu
-
-        m.submodules.decoder = self._decoder
-        m.submodules.rom     = self.rom
-        m.submodules.ram     = self.ram
-        m.submodules.uart    = self.uart
-        m.submodules.timer   = self.timer
-        m.submodules.intc    = self.intc
-
-        m.d.comb += [
-            self._arbiter.bus.connect(self._decoder.bus),
-            self.cpu.ip.eq(self.intc.ip),
-        ]
-
-        return m
-
-
-if __name__ == "__main__":
-    parser = argparse.ArgumentParser()
-    parser.add_argument("platform", type=str,
-            help="target platform (e.g. 'nmigen_boards.arty_a7.ArtyA7_35Platform')")
-    parser.add_argument("--baudrate", type=int,
-            default=9600,
-            help="UART baudrate (default: 9600)")
-    parser.add_argument("--build-dir", type=str,
-            default="build",
-            help="local build directory (default: 'build')")
-    args = parser.parse_args()
-
-    def get_platform(platform_name):
-        module_name, class_name = platform_name.rsplit(".", 1)
-        module = importlib.import_module(name=module_name)
-        platform_class = getattr(module, class_name)
-        return platform_class()
-
-    platform = get_platform(args.platform)
-
-    uart_divisor = int(platform.default_clk_frequency // args.baudrate)
-    uart_pins = platform.request("uart", 0)
-
-    soc = SRAMSoC(
-         reset_addr=0x00000000, clk_freq=int(platform.default_clk_frequency),
-           rom_addr=0x00000000, rom_size=0x4000,
-           ram_addr=0x00004000, ram_size=0x1000,
-          uart_addr=0x00005000, uart_divisor=uart_divisor, uart_pins=uart_pins,
-         timer_addr=0x00006000, timer_width=32,
-    )
-
-    soc.build(build_dir=f"{args.build_dir}/soc", do_init=True)
-
-    platform.build(soc, build_dir=args.build_dir, do_program=True)
index 497112fdb046adb36367030db791d1df2fea329a..227cddafe28d8ca7a887b940b4c12c9d12aae752 100644 (file)
@@ -7,6 +7,7 @@ import textwrap
 
 from nmigen import *
 from nmigen import tracer
+from nmigen.build.plat import Platform
 from nmigen.build.run import BuildPlan, BuildProducts
 from nmigen.utils import log2_int
 
@@ -152,32 +153,6 @@ class Config(metaclass=ABCMeta):
         assert module.memtype == self.memtype
         return module
 
-    def request_pins(self, platform, name, number):
-        """Request DRAM pins.
-
-        This helper requests the DRAM pins with `dir="-"` and `xdr=0`, because LiteDRAM already
-        provides its own I/O buffers.
-
-        Arguments
-        ---------
-        platform : :class:`nmigen.build.Platform`
-            Target platform.
-        name : str
-            DRAM resource name.
-        number : int
-            DRAM resource number.
-
-        Return value
-        ------------
-        A :class:`Record` providing raw access to DRAM pins.
-        """
-        res = platform.lookup(name, number)
-        return platform.request(
-            name, number,
-            dir={io.name: "-" for io in res.ios},
-            xdr={io.name: 0   for io in res.ios},
-        )
-
 
 class ECP5Config(Config):
     phy_name = "ECP5DDRPHY"
@@ -485,18 +460,19 @@ class Core(Elaboratable):
         )
         self._ctrl_bus.memory_map = ctrl_map
 
-    def build(self, builder, *, do_build=True, build_dir="build/litedram", sim=False,
-            name_force=False):
+    def build(self, builder, platform, build_dir, *, do_build=True, sim=False, name_force=False):
         """Build the LiteDRAM core.
 
         Arguments
         ---------
         builder: :class:`litedram.Builder`
             Builder instance.
+        platform: :class:`nmigen.build.Platform`
+            Target platform.
+        build_dir : str
+            Root build directory.
         do_build : bool
             Execute the build locally. Defaults to ``True``.
-        build_dir : str
-            Root build directory. Defaults to ``"build/litedram"``.
         sim : bool
             Do the build in simulation mode (i.e. by replacing the PHY with a model). Defaults to
             ``False``.
@@ -511,13 +487,20 @@ class Core(Elaboratable):
         if not isinstance(builder, Builder):
             raise TypeError("Builder must be an instance of litedram.Builder, not {!r}"
                             .format(builder))
+        if not isinstance(platform, Platform):
+            raise TypeError("Platform must be an instance of nmigen.build.Platform, not {!r}"
+                            .format(platform))
 
         plan = builder.prepare(self, sim=sim, name_force=name_force)
         if not do_build:
             return plan
 
-        products = plan.execute_local(build_dir)
+        products = plan.execute_local(f"{build_dir}/{__package__}")
         self._populate_ctrl_map(products)
+
+        core_src = f"{self.name}/{self.name}.v"
+        platform.add_file(core_src, products.get(core_src, mode="t"))
+
         return products
 
     def elaborate(self, platform):
diff --git a/lambdasoc/cores/utils.py b/lambdasoc/cores/utils.py
new file mode 100644 (file)
index 0000000..fa7acca
--- /dev/null
@@ -0,0 +1,25 @@
+def request_bare(platform, name, number):
+        """Request bare pins.
+
+        This helper requests pins with `dir="-"` and `xdr=0`, for use cases where implicit I/O
+        buffers are undesirable.
+
+        Arguments
+        ---------
+        platform : :class:`nmigen.build.plat.Platform`
+            Target platform.
+        name : str
+            Resource name.
+        number : int
+            Resource number.
+
+        Return value
+        ------------
+        A :class:`Record` providing raw access to pins.
+        """
+        res = platform.lookup(name, number)
+        return platform.request(
+            name, number,
+            dir={io.name: "-" for io in res.ios},
+            xdr={io.name: 0   for io in res.ios},
+        )
index 593d44e916e99a42c033fd2cb9f3619a8c76ce5f..401a9ec5611cc2c9982412563a3c91dbe5ed2047 100644 (file)
@@ -179,6 +179,8 @@ class ConfigBuilder:
                 "periph_size": periph_size,
                 "soc": soc,
                 "software_dir": os.path.dirname(software.__file__),
+                "constants": soc.constants,
+                "ConstantAddr": ConstantAddr,
                 **render_params,
             })
 
index 5f26054582886a070d6414694035fa604780ebcf..24149f4a98987f63ed016ff15d9cf951b6124e50 100644 (file)
@@ -1,10 +1,12 @@
 import os
 
+from nmigen_soc.periph import ConstantMap, ConstantBool, ConstantInt
+
 from .base import *
 from ..cpu import CPU
+from ..cores import litedram
 from ..periph.intc import InterruptController
 from ..periph.sram import SRAMPeripheral
-from ..periph.sdram import SDRAMPeripheral
 from ..periph.serial import AsyncSerialPeripheral
 from ..periph.timer import TimerPeripheral
 
@@ -13,25 +15,35 @@ __all__ = ["CPUSoC", "BIOSBuilder"]
 
 
 class CPUSoC(SoC):
-    cpu    = socproperty(CPU)
-    intc   = socproperty(InterruptController)
-    rom    = socproperty(SRAMPeripheral)
-    ram    = socproperty(SRAMPeripheral)
-    sdram  = socproperty(SDRAMPeripheral, weak=True)
-    uart   = socproperty(AsyncSerialPeripheral)
-    timer  = socproperty(TimerPeripheral)
+    cpu        = socproperty(CPU)
+    intc       = socproperty(InterruptController)
+    bootrom    = socproperty(SRAMPeripheral)
+    scratchpad = socproperty(SRAMPeripheral)
+    uart       = socproperty(AsyncSerialPeripheral)
+    timer      = socproperty(TimerPeripheral)
 
     # TODO: implement a CRG peripheral and expose clock frequencies through CSRs.
-    clk_freq = socproperty(int)
-
-    def build(self, name=None,
-              litedram_dir="build/litedram",
-              build_dir="build/soc", do_build=True,
-              do_init=False):
+    sync_clk_freq = socproperty(int)
+
+    @property
+    def constants(self):
+        return ConstantMapCollection(
+            CPU        = self.cpu.constant_map,
+            INTC       = self.intc.constant_map,
+            BOOTROM    = self.bootrom.constant_map,
+            SCRATCHPAD = self.scratchpad.constant_map,
+            UART       = self.uart.constant_map,
+            TIMER      = self.timer.constant_map,
+            SOC        = ConstantMap(
+                CLOCK_FREQ     = self.sync_clk_freq,
+                CSR_DATA_WIDTH = 32,
+            ),
+        )
+
+    def build(self, build_dir, name=None, do_build=True, do_init=False):
         """TODO
         """
-        plan = BIOSBuilder().prepare(self, build_dir, name,
-                                     litedram_dir=os.path.abspath(litedram_dir))
+        plan = BIOSBuilder().prepare(self, build_dir, name)
         if not do_build:
             return plan
 
@@ -39,61 +51,86 @@ class CPUSoC(SoC):
         if not do_init:
             return products
 
-        with products.extract("bios/bios.bin") as bios_filename:
+        with products.extract(f"{__name__}/bios/bios.bin") as bios_filename:
             with open(bios_filename, "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.bootrom.init = bios
+
+
+def kconfig_format(key, const, prefix="CONFIG_"):
+    if not isinstance(key, str) or not key:
+        raise ValueError("Key must be a non-empty string, not {!r}".format(key))
+    if isinstance(const, ConstantBool):
+        value = "y" if const.value else "n"
+    elif isinstance(const, ConstantAddr):
+        value = hex(const.value)
+    elif isinstance(const, ConstantInt):
+        value = const.value
+    else:
+        raise TypeError("Unsupported constant type, must be ConstantBool, ConstantAddr or "
+                        "ConstantInt, not {!r}"
+                        .format(const))
+    return "{}{}={}".format(prefix, key.upper(), value)
+
+
+def cpp_format(key, const, prefix=""):
+    if not isinstance(key, str) or not key:
+        raise ValueError("Key must be a non-empty string, not {!r}".format(key))
+    if isinstance(const, ConstantBool):
+        value = 1 if const.value else 0
+    elif isinstance(const, ConstantAddr):
+        value = "{:#x}UL".format(const.value)
+    elif isinstance(const, ConstantInt):
+        value = "{}{}".format(const.value, "U" if not const.signed else "")
+    else:
+        raise TypeError("Unsupported constant type, must be ConstantBool, ConstantAddr or "
+                        "ConstantInt, not {!r}"
+                        .format(const))
+    return "#define {}{} {}".format(prefix, key.upper(), value)
 
 
 class BIOSBuilder(ConfigBuilder):
     file_templates = {
         **ConfigBuilder.file_templates,
-        "{{name}}.config": r"""
+        "/".join([__name__, "{{name}}.config"]): r"""
             # {{autogenerated}}
-            CONFIG_CPU_{{soc.cpu.name.upper()}}=y
-            CONFIG_CPU_RESET_ADDR={{hex(soc.cpu.reset_addr)}}
-            CONFIG_CPU_BYTEORDER="{{soc.cpu.byteorder}}"
-            CONFIG_ARCH_{{soc.cpu.arch.upper()}}=y
-            {% if soc.cpu.muldiv == "soft" %}
-            CONFIG_{{soc.cpu.arch.upper()}}_MULDIV_SOFT=y
-            {% else %}
-            CONFIG_{{soc.cpu.arch.upper()}}_MULDIV_SOFT=n
-            {% endif %}
-            CONFIG_ROM_START={{hex(periph_addr(soc.rom))}}
-            CONFIG_ROM_SIZE={{hex(soc.rom.size)}}
-            CONFIG_RAM_START={{hex(periph_addr(soc.ram))}}
-            CONFIG_RAM_SIZE={{hex(soc.ram.size)}}
-            CONFIG_UART_START={{hex(periph_addr(soc.uart))}}
-            CONFIG_UART_IRQNO={{soc.intc.find_index(soc.uart.irq)}}
-            CONFIG_UART_RX_RINGBUF_SIZE_LOG2=7
-            CONFIG_UART_TX_RINGBUF_SIZE_LOG2=7
-            CONFIG_TIMER_START={{hex(periph_addr(soc.timer))}}
-            CONFIG_TIMER_IRQNO={{soc.intc.find_index(soc.timer.irq)}}
-            CONFIG_TIMER_CTR_WIDTH={{soc.timer.width}}
-            CONFIG_CLOCK_FREQ={{soc.clk_freq}}
 
-            {% if soc.sdram is not none %}
-            CONFIG_WITH_SDRAM=y
-            CONFIG_SDRAM_START={{hex(periph_addr(soc.sdram))}}
-            CONFIG_SDRAM_SIZE={{hex(soc.sdram.core.size)}}
-            {% else %}
-            CONFIG_WITH_SDRAM=n
-            {% endif %}
+            # Configuration constants
+            {% for key, value in constants.flatten(separator="_") %}
+            {{kconfig_format(key, value)}}
+            {% endfor %}
+
+            # Memory regions
+            {% for window, (start, stop, step) in soc.memory_map.windows() %}
+            {% set window_name = window.name.upper() %}
+            {{kconfig_format(window_name + "_BASE", ConstantAddr(start))}}
+            {{kconfig_format(window_name + "_LIMIT", ConstantAddr(stop))}}
+            {% endfor %}
         """,
-        "litex_config.h": r"""
+        "/".join([__name__, "litex_config.h"]): r"""
             // {{autogenerated}}
             #ifndef __LITEX_CONFIG_H_LAMBDASOC
             #define __LITEX_CONFIG_H_LAMBDASOC
 
-            #define LX_CONFIG_TIMER_START {{hex(periph_addr(soc.timer))}}
+            // Configuration constants
+            {% for key, value in constants.flatten(separator="_") %}
+            {{cpp_format(key, value, prefix="LX_CONFIG_")}}
+            {% endfor %}
+
+            // Memory regions
+            {% for window, (start, stop, step) in soc.memory_map.windows() %}
+            {% set window_name = window.name.upper() %}
+            {{cpp_format(window_name + "_BASE", ConstantAddr(start), prefix="LX_CONFIG_")}}
+            {{cpp_format(window_name + "_LIMIT", ConstantAddr(stop), prefix="LX_CONFIG_")}}
+            {% endfor %}
 
             {% if soc.sdram is not none %}
-            #define LX_CONFIG_SDRAM_START {{hex(periph_addr(soc.sdram))}}UL
-            #define LX_CONFIG_SDRAM_SIZE {{hex(soc.sdram.core.size)}}UL
-            #define LX_CONFIG_SDRAM_CACHE_SIZE {{soc.sdram._cache.size}}
-            #define LX_CONFIG_MEMTEST_DATA_SIZE 2*1024*1024
-            #define LX_CONFIG_MEMTEST_ADDR_SIZE 65536
+            #define LX_CONFIG_MAIN_RAM_BASE LX_CONFIG_SDRAM_BASE
+            #define LX_CONFIG_MAIN_RAM_SIZE LX_CONFIG_SDRAM_SIZE
+            {% else %}
+            #define LX_CONFIG_MAIN_RAM_BASE LX_CONFIG_SRAM_BASE
+            #define LX_CONFIG_MAIN_RAM_SIZE LX_CONFIG_SRAM_SIZE
             {% endif %}
 
             #endif
@@ -103,16 +140,24 @@ class BIOSBuilder(ConfigBuilder):
         *ConfigBuilder.command_templates,
         r"""
             {% if soc.sdram is not none %}
-            litedram_dir={{litedram_dir}}/{{soc.sdram.core.name}}
+            litedram_dir={{build_dir}}/{{litedram_pkg}}/{{soc.sdram.core.name}}
             {% endif %}
-            build={{build_dir}}
-            KCONFIG_CONFIG={{build_dir}}/{{name}}.config
+            build={{bios_dir}}
+            KCONFIG_CONFIG={{bios_dir}}/{{name}}.config
             make -C {{software_dir}}/bios 1>&2
         """,
     ]
 
-    def prepare(self, soc, build_dir, name, litedram_dir):
+    def prepare(self, soc, build_dir, name, **render_params):
         if not isinstance(soc, CPUSoC):
             raise TypeError("SoC must be an instance of CPUSoC, not {!r}"
                             .format(soc))
-        return super().prepare(soc, build_dir, name, litedram_dir=litedram_dir)
+
+        render_params.update({
+            "kconfig_format": kconfig_format,
+            "cpp_format": cpp_format,
+            "bios_dir": os.path.abspath(f"{build_dir}/{__name__}"),
+            "litedram_pkg": litedram.__name__,
+        })
+
+        return super().prepare(soc, build_dir, name, **render_params)
index 8c7a7328453f346bfdaa7f7bfc4b57654f40fa39..e897b9ae09ea05ecef6b47a6152b4dfa45cc860d 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -24,6 +24,7 @@ setup(
     license="BSD",
     setup_requires=["setuptools_scm"],
     install_requires=[
+        "jinja2>=3.0",
         "nmigen>=0.1,<0.5",
         "nmigen-soc",
         "nmigen-stdio",