From 37869e38b8e64d9bdd7c58cf8ad7209be0b2ffb0 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 9 Mar 2020 11:56:55 +0100 Subject: [PATCH] boards: add initial icebreaker platform/target from litex-boards. --- litex/boards/platforms/icebreaker.py | 89 +++++++++++++ litex/boards/targets/icebreaker.py | 186 +++++++++++++++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 litex/boards/platforms/icebreaker.py create mode 100644 litex/boards/targets/icebreaker.py diff --git a/litex/boards/platforms/icebreaker.py b/litex/boards/platforms/icebreaker.py new file mode 100644 index 000000000..2cb41a4c8 --- /dev/null +++ b/litex/boards/platforms/icebreaker.py @@ -0,0 +1,89 @@ +# This file is Copyright (c) 2020 Piotr Esden-Tempski +# 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 index 000000000..c2325d2a2 --- /dev/null +++ b/litex/boards/targets/icebreaker.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 + +# This file is Copyright (c) 2019 Sean Cross +# This file is Copyright (c) 2018 David Shah +# This file is Copyright (c) 2020 Piotr Esden-Tempski +# 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()