boards: add initial icebreaker platform/target from litex-boards.
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 9 Mar 2020 10:56:55 +0000 (11:56 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 9 Mar 2020 10:56:55 +0000 (11:56 +0100)
litex/boards/platforms/icebreaker.py [new file with mode: 0644]
litex/boards/targets/icebreaker.py [new file with mode: 0644]

diff --git a/litex/boards/platforms/icebreaker.py b/litex/boards/platforms/icebreaker.py
new file mode 100644 (file)
index 0000000..2cb41a4
--- /dev/null
@@ -0,0 +1,89 @@
+# This file is Copyright (c) 2020 Piotr Esden-Tempski <piotr@esden.net>
+# License: BSD
+
+# iCEBreaker FPGA:
+# - Crowd Supply campaign: https://www.crowdsupply.com/1bitsquared/icebreaker
+# - 1BitSquared Store: https://1bitsquared.com/products/icebreaker
+# - Design files: https://github.com/icebreaker/icebreaker
+
+from litex.build.generic_platform import *
+from litex.build.lattice import LatticePlatform
+from litex.build.lattice.programmer import IceStormProgrammer
+
+# IOs ----------------------------------------------------------------------------------------------
+
+_io = [
+    ("user_led_n",    0, Pins("11"), IOStandard("LVCMOS33")),
+    ("user_led_n",    1, Pins("37"), IOStandard("LVCMOS33")),
+    # Color-specific aliases
+    ("user_ledr_n",   0, Pins("11"), IOStandard("LVCMOS33")),
+    ("user_ledg_n",   0, Pins("37"), IOStandard("LVCMOS33")),
+    ("user_btn_n", 0, Pins("10"), IOStandard("LVCMOS33")),
+
+    ("serial", 0,
+        Subsignal("rx", Pins("6")),
+        Subsignal("tx", Pins("9"), Misc("PULLUP")),
+        IOStandard("LVCMOS33")
+    ),
+
+    ("spiflash", 0,
+        Subsignal("cs_n",      Pins("16"), IOStandard("LVCMOS33")),
+        Subsignal("clk",       Pins("15"), IOStandard("LVCMOS33")),
+        Subsignal("miso",        Pins("17"), IOStandard("LVCMOS33")),
+        Subsignal("mosi",        Pins("14"), IOStandard("LVCMOS33")),
+        Subsignal("wp",      Pins("12"), IOStandard("LVCMOS33")),
+        Subsignal("hold", Pins("13"), IOStandard("LVCMOS33")),
+    ),
+
+    ("spiflash4x", 0,
+        Subsignal("cs_n", Pins("16"), IOStandard("LVCMOS33")),
+        Subsignal("clk",  Pins("15"), IOStandard("LVCMOS33")),
+        Subsignal("dq",   Pins("14 17 12 13"), IOStandard("LVCMOS33")),
+    ),
+
+    ("clk12", 0, Pins("35"), IOStandard("LVCMOS33"))
+]
+
+# Connectors ---------------------------------------------------------------------------------------
+
+_connectors = [
+    ("PMOD1A", "4 2 47 45 3 48 46 44"),
+    ("PMOD1B", "43 38 34 31 42 36 32 28"),
+    ("PMOD2",  "27 25 21 19 26 23 20 18")
+]
+
+# The attached LED/button section can be either used standalone or as a PMOD.
+# Attach to platform using:
+# plat.add_extension(break_off_pmod)
+# pmod_btn = plat.request("user_btn")
+break_off_pmod = [
+     ("user_btn", 0, Pins("PMOD2:6"), IOStandard("LVCMOS33")),
+     ("user_btn", 1, Pins("PMOD2:3"), IOStandard("LVCMOS33")),
+     ("user_btn", 2, Pins("PMOD2:7"), IOStandard("LVCMOS33")),
+
+     ("user_led", 0, Pins("PMOD2:4"), IOStandard("LVCMOS33")),
+     ("user_led", 1, Pins("PMOD2:0"), IOStandard("LVCMOS33")),
+     ("user_led", 2, Pins("PMOD2:1"), IOStandard("LVCMOS33")),
+     ("user_led", 3, Pins("PMOD2:5"), IOStandard("LVCMOS33")),
+     ("user_led", 4, Pins("PMOD2:2"), IOStandard("LVCMOS33")),
+
+     # Color-specific aliases
+     ("user_ledr", 0, Pins("PMOD2:4"), IOStandard("LVCMOS33")),
+     ("user_ledg", 0, Pins("PMOD2:0"), IOStandard("LVCMOS33")),
+     ("user_ledg", 1, Pins("PMOD2:1"), IOStandard("LVCMOS33")),
+     ("user_ledg", 2, Pins("PMOD2:5"), IOStandard("LVCMOS33")),
+     ("user_ledg", 3, Pins("PMOD2:2"), IOStandard("LVCMOS33"))
+]
+
+# Platform -----------------------------------------------------------------------------------------
+
+class Platform(LatticePlatform):
+    default_clk_name = "clk12"
+    default_clk_period = 1e9 / 12e6
+
+    def __init__(self):
+        LatticePlatform.__init__(self, "ice40-up5k-sg48", _io, _connectors,
+                                 toolchain="icestorm")
+
+    def create_programmer(self):
+        return IceStormProgrammer()
diff --git a/litex/boards/targets/icebreaker.py b/litex/boards/targets/icebreaker.py
new file mode 100644 (file)
index 0000000..c2325d2
--- /dev/null
@@ -0,0 +1,186 @@
+#!/usr/bin/env python3
+
+# This file is Copyright (c) 2019 Sean Cross <sean@xobs.io>
+# This file is Copyright (c) 2018 David Shah <dave@ds0.me>
+# This file is Copyright (c) 2020 Piotr Esden-Tempski <piotr@esden.net>
+# License: BSD
+
+# This target was originally based on the Fomu target.
+
+import argparse
+
+from migen import *
+from migen.genlib.resetsync import AsyncResetSynchronizer
+
+from litex.soc.cores import up5kspram, spi_flash
+from litex.soc.integration.soc_core import SoCCore
+from litex.soc.integration.builder import Builder, builder_argdict, builder_args
+from litex.soc.integration.soc_core import soc_core_argdict, soc_core_args
+from litex.soc.integration.doc import AutoDoc
+
+from litex_boards.platforms.icebreaker import Platform
+
+from litex.soc.interconnect import wishbone
+from litex.soc.cores.uart import UARTWishboneBridge
+from litex.soc.cores.gpio import GPIOOut
+
+
+class JumpToAddressROM(wishbone.SRAM):
+    def __init__(self, size, addr):
+        data = [
+            0x00000537 | ((addr & 0xfffff000) << 0),   # lui   a0,%hi(addr)
+            0x00052503 | ((addr & 0x00000fff) << 20),  # lw    a0,%lo(addr)(a0)
+            0x000500e7,                                # jalr  a0
+        ]
+        wishbone.SRAM.__init__(self, size, read_only=True, init=data)
+
+# CRG ----------------------------------------------------------------------------------------------
+
+class _CRG(Module):
+    def __init__(self, platform):
+        self.clock_domains.cd_sys = ClockDomain()
+        self.clock_domains.cd_por = ClockDomain()
+
+        self.reset = Signal()
+
+        # # #
+
+        reset_delay = Signal(12, reset=4095)
+
+        # Clocks
+        clk12 = platform.request("clk12")
+        platform.add_period_constraint(clk12, 1e9/12e6)
+        self.comb += self.cd_sys.clk.eq(clk12)
+        self.comb += self.cd_por.clk.eq(clk12)
+        self.comb += self.cd_sys.rst.eq(reset_delay != 0)
+
+        # Power On Reset
+        self.sync.por += If(reset_delay != 0, reset_delay.eq(reset_delay - 1))
+        self.specials += AsyncResetSynchronizer(self.cd_por, self.reset)
+
+# BaseSoC ------------------------------------------------------------------------------------------
+
+class BaseSoC(SoCCore):
+    """A SoC on iCEBreaker, optionally with a softcore CPU"""
+
+    # Statically-define the memory map, to prevent it from shifting across various litex versions.
+    SoCCore.mem_map = {
+        "rom":              0x00000000,  # (default shadow @0x80000000)
+        "sram":             0x10000000,  # (default shadow @0xa0000000)
+        "spiflash":         0x20000000,  # (default shadow @0xa0000000)
+        "csr":              0xe0000000,  # (default shadow @0x60000000)
+        "vexriscv_debug":   0xf00f0000,
+    }
+
+    def __init__(self, debug=True, boot_vector=0x2001a000, **kwargs):
+        """Create a basic SoC for iCEBreaker.
+
+        Create a basic SoC for iCEBreaker.  The `sys` frequency will run at 12 MHz.
+
+        Returns:
+            Newly-constructed SoC
+        """
+        platform = Platform()
+
+        kwargs["cpu_variant"]       = "lite"
+        kwargs["cpu_reset_address"] = boot_vector
+        if debug:
+            kwargs["uart_name"]   = "crossover"
+            kwargs["cpu_variant"] = "lite+debug"
+
+        clk_freq = int(12e6)
+
+        # Force the SRAM size to 0, because we add our own SRAM with SPRAM
+        kwargs["integrated_sram_size"] = 0
+        kwargs["integrated_rom_size"]  = 0
+
+        SoCCore.__init__(self, platform, clk_freq, **kwargs)
+
+        # If there is a VexRiscv CPU, add a fake ROM that simply tells the CPU
+        # to jump to the given address.
+        if hasattr(self, "cpu") and self.cpu.name == "vexriscv":
+            self.add_memory_region("rom", 0, 16)
+            self.submodules.rom = JumpToAddressROM(16, boot_vector)
+
+        self.submodules.crg = _CRG(platform)
+
+        # UP5K has single port RAM, which is a dedicated 128 kilobyte block.
+        # Use this as CPU RAM.
+        spram_size = 128 * 1024
+        self.submodules.spram = up5kspram.Up5kSPRAM(size=spram_size)
+        self.register_mem("sram", self.mem_map["sram"], self.spram.bus, spram_size)
+
+        # The litex SPI module supports memory-mapped reads, as well as a bit-banged mode
+        # for doing writes.
+        spi_pads = platform.request("spiflash4x")
+        self.submodules.lxspi = spi_flash.SpiFlash(spi_pads, dummy=6, endianness="little")
+        self.register_mem("spiflash", self.mem_map["spiflash"], self.lxspi.bus, size=16 * 1024 * 1024)
+        self.add_csr("lxspi")
+
+        # In debug mode, add a UART bridge.  This takes over from the normal UART bridge,
+        # however you can use the "crossover" UART to communicate with this over the bridge.
+        if debug:
+            self.submodules.uart_bridge = UARTWishboneBridge(platform.request("serial"), clk_freq, baudrate=115200)
+            self.add_wb_master(self.uart_bridge.wishbone)
+            if hasattr(self, "cpu") and self.cpu.name == "vexriscv":
+                self.register_mem("vexriscv_debug", 0xf00f0000, self.cpu.debug_bus, 0x100)
+
+        self.submodules.leds = GPIOOut(Cat(
+            platform.request("user_ledr_n"),
+            platform.request("user_ledg_n")))
+        self.add_csr("leds")
+
+        # self.add_memory_region("rom", 0x2001a000, 16 * 1024 * 1024 - 0x1a000, type="cached+linker")
+        # self.add_memory_region("boot", 0, 16, type="cached+linker")
+        # self.mem_regions["rom"] = SoCMemRegion(0x2001a000, 16 * 1024 * 1024 - 0x1a000, "cached")
+        # self.mem_regions["boot"] = SoCMemRegion(0, 16, "cached")
+
+    def set_yosys_nextpnr_settings(self, nextpnr_seed=0, nextpnr_placer="heap"):
+        """Set Yosys/Nextpnr settings by overriding default LiteX's settings.
+        Args:
+            nextpnr_seed   (int): Seed to use in Nextpnr
+            nextpnr_placer (str): Placer to use in Nextpnr
+        """
+        assert hasattr(self.platform.toolchain, "yosys_template")
+        assert hasattr(self.platform.toolchain, "build_template")
+        self.platform.toolchain.yosys_template = [
+            "{read_files}",
+            "attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0",
+            # Use "-relut -dffe_min_ce_use 4" to the synth_ice40 command. The "-reult" adds an additional
+            # LUT pass to pack more stuff in, and the "-dffe_min_ce_use 4" flag prevents Yosys from
+            # generating a Clock Enable signal for a LUT that has fewer than 4 flip-flops. This increases
+            # density, and lets us use the FPGA more efficiently.
+            "synth_ice40 -json {build_name}.json -top {build_name} -relut -abc2 -dffe_min_ce_use 4 -relut",
+        ]
+        self.platform.toolchain.build_template = [
+            "yosys -q -l {build_name}.rpt {build_name}.ys",
+            "nextpnr-ice40 --json {build_name}.json --pcf {build_name}.pcf --asc {build_name}.txt"
+            + " --pre-pack {build_name}_pre_pack.py --{architecture} --package {package}"
+            + " --seed {}".format(nextpnr_seed)
+            + " --placer {}".format(nextpnr_placer),
+            # Disable final deep-sleep power down so firmware words are loaded onto softcore's address bus.
+            "icepack -s {build_name}.txt {build_name}.bin"
+        ]
+
+# Build --------------------------------------------------------------------------------------------
+
+def main():
+    parser = argparse.ArgumentParser(description="LiteX SoC on iCEBreaker")
+    parser.add_argument("--nextpnr-seed",   default=0, help="Seed to use in Nextpnr")
+    parser.add_argument("--nextpnr-placer", default="heap", choices=["sa", "heap"], help="Placer implementation to use in Nextpnr")
+    builder_args(parser)
+    soc_core_args(parser)
+    args = parser.parse_args()
+
+    soc = BaseSoC(debug=True, **soc_core_argdict(args))
+    soc.set_yosys_nextpnr_settings(nextpnr_seed=args.nextpnr_seed, nextpnr_placer=args.nextpnr_placer)
+
+    # Don't build software -- we don't include it since we just jump to SPI flash.
+    builder_kwargs = builder_argdict(args)
+    builder_kwargs["compile_software"] = False
+    builder = Builder(soc, **builder_kwargs)
+    builder.build()
+
+
+if __name__ == "__main__":
+    main()