diff --git a/README.md b/README.md index 321fd65..724fb7c 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Fully open-hardware boards, the ECP5 and iCE40 ones are even usable with the ope | Fomu | Lattice iCE40 | iCE40-UP5K | 12MHz | USB | 128KB SPRAM | No | No | 16MB QSPI | No | | HADBadge | Lattice ECP5 | LFE5U-45F | 48MHz | IOs | 8-bit 32MB SDR | No | No | 16MB QSPI | No | | iCEBreaker | Lattice iCE40 | iCE40-UP5K | 24MHz | FTDI | 128KB SPRAM | No | No | 16MB QSPI | No | +| iCESugar | Lattice iCE40 | iCE40-UP5K | 24MHz | STM32| 128KB SPRAM | No | No | 8MB QSPI | No | | LogicBone | Lattice ECP5 | LFE5U-45F | 75MHz | FTDI | 16-bit 1GB DDR3 | No | 1Gbps RGMII | 16MB QSPI | Yes | | MarbleMini | Xilinx Artix7 | XC7A100T | 100MHz | FTDI | 16-bit 1GB DDR3 | No | 1Gbps RGMII | 16MB QSPI | No | | MiniSpartan6 | Xilinx Spartan6 | XC6SLX25 | 80MHz | FTDI | 16-bit 32MB SDR | No | No | 8MB QSPI | Yes | diff --git a/litex_boards/platforms/muselab_icesugar.py b/litex_boards/platforms/muselab_icesugar.py new file mode 100644 index 0000000..2285727 --- /dev/null +++ b/litex_boards/platforms/muselab_icesugar.py @@ -0,0 +1,101 @@ +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2021 Hans Baier +# SPDX-License-Identifier: BSD-2-Clause + +# iCESugar FPGA: +# https://www.aliexpress.com/item/4001201771358.html + +from litex.build.generic_platform import * +from litex.build.lattice import LatticePlatform +from litex.build.lattice.programmer import IceSugarProgrammer + +# IOs ---------------------------------------------------------------------------------------------- + +_io = [ + # Clk / Rst + ("clk12", 0, Pins("35"), IOStandard("LVCMOS33")), + + # Leds R / G / B + ("user_led_n", 0, Pins("40"), IOStandard("LVCMOS33")), + ("user_led_n", 1, Pins("39"), IOStandard("LVCMOS33")), + ("user_led_n", 2, Pins("41"), IOStandard("LVCMOS33")), + + # RGB led, active-low, alias for Leds + ("rgb_led", 0, + Subsignal("r", Pins("40")), + Subsignal("g", Pins("39")), + Subsignal("b", Pins("31")), + IOStandard("LVCMOS33"), + ), + + # Switches / jumper-attached to PMOD4 + ("user_sw", 0, Pins("18"), IOStandard("LVCMOS18")), + ("user_sw", 1, Pins("19"), IOStandard("LVCMOS18")), + ("user_sw", 2, Pins("20"), IOStandard("LVCMOS18")), + ("user_sw", 3, Pins("21"), IOStandard("LVCMOS18")), + + # Serial + ("serial", 0, + Subsignal("rx", Pins("4")), + Subsignal("tx", Pins("6"), Misc("PULLUP")), + IOStandard("LVCMOS33") + ), + + # SPIFlash + ("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")), + ), +] + +# Connectors --------------------------------------------------------------------------------------- + +_connectors = [ + # I chose the pin order such that it is the + # same as on the iCEBreaker so that its PMODs + # can be reused with this board + ("PMOD1", "10 6 3 48 9 4 2 47"), + ("PMOD2", "46 44 42 37 45 43 38 36"), + ("PMOD3", "34 31 27 25 32 28 26 23"), + + # numbering similar to the pmods: + # 0 is marked pin, parallel rows + # notice that all those pins are also connected to PMOD1 + ("J7", "48 - 3 47 - 2"), +] + +# PMODS -------------------------------------------------------------------------------------------- + +def led_pmod_io_v11(pmod, offset=0): + return [ + # LED PMOD: https://www.aliexpress.com/item/1005001504777342.html + # contrary to the supplied schematic, the two nibbles seem to be swapped on the board + ("user_led_n", offset + 0, Pins(f"{pmod}:4"), IOStandard("LVCMOS33")), + ("user_led_n", offset + 1, Pins(f"{pmod}:5"), IOStandard("LVCMOS33")), + ("user_led_n", offset + 2, Pins(f"{pmod}:6"), IOStandard("LVCMOS33")), + ("user_led_n", offset + 3, Pins(f"{pmod}:7"), IOStandard("LVCMOS33")), + ("user_led_n", offset + 4, Pins(f"{pmod}:0"), IOStandard("LVCMOS33")), + ("user_led_n", offset + 5, Pins(f"{pmod}:1"), IOStandard("LVCMOS33")), + ("user_led_n", offset + 6, Pins(f"{pmod}:2"), IOStandard("LVCMOS33")), + ("user_led_n", offset + 7, Pins(f"{pmod}:3"), IOStandard("LVCMOS33")), + ] + +# Platform ----------------------------------------------------------------------------------------- + +class Platform(LatticePlatform): + default_clk_name = "clk12" + default_clk_period = 1e9/12e6 + + def __init__(self, toolchain="icestorm"): + LatticePlatform.__init__(self, "ice40-up5k-sg48", _io, _connectors, toolchain=toolchain) + + def create_programmer(self): + return IceSugarProgrammer() + + def do_finalize(self, fragment): + LatticePlatform.do_finalize(self, fragment) + self.add_period_constraint(self.lookup_request("clk12", loose=True), 1e9/12e6) diff --git a/litex_boards/targets/muselab_icesugar.py b/litex_boards/targets/muselab_icesugar.py new file mode 100644 index 0000000..db541b6 --- /dev/null +++ b/litex_boards/targets/muselab_icesugar.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +# +# This file is part of LiteX-Boards. +# +# Copyright (c) 2021 Hans Baier +# SPDX-License-Identifier: BSD-2-Clause + +# iCESugar FPGA: +# https://www.aliexpress.com/item/4001201771358.html + +import os +import argparse + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex_boards.platforms import muselab_icesugar + +from litex.soc.cores.ram import Up5kSPRAM +from litex.soc.cores.clock import iCE40PLL +from litex.soc.integration.soc_core import * +from litex.soc.integration.soc import SoCRegion +from litex.soc.integration.builder import * +from litex.soc.cores.led import LedChaser + +kB = 1024 +mB = 1024*kB + +# CRG ---------------------------------------------------------------------------------------------- + +class _CRG(Module): + def __init__(self, platform, sys_clk_freq): + self.rst = Signal() + self.clock_domains.cd_sys = ClockDomain() + self.clock_domains.cd_por = ClockDomain(reset_less=True) + + # # # + + # Clk/Rst + clk12 = platform.request("clk12") + + # Power On Reset + por_count = Signal(16, reset=2**16-1) + por_done = Signal() + self.comb += self.cd_por.clk.eq(ClockSignal()) + self.comb += por_done.eq(por_count == 0) + self.sync.por += If(~por_done, por_count.eq(por_count - 1)) + + # PLL + self.submodules.pll = pll = iCE40PLL(primitive="SB_PLL40_PAD") + self.comb += pll.reset.eq(self.rst) + pll.register_clkin(clk12, 12e6) + pll.create_clkout(self.cd_sys, sys_clk_freq, with_reset=False) + self.specials += AsyncResetSynchronizer(self.cd_sys, ~por_done | ~pll.locked) + platform.add_period_constraint(self.cd_sys.clk, 1e9/sys_clk_freq) + +# BaseSoC ------------------------------------------------------------------------------------------ + +class BaseSoC(SoCCore): + mem_map = {**SoCCore.mem_map, **{"spiflash": 0x80000000}} + def __init__(self, bios_flash_offset, sys_clk_freq=int(24e6), with_video_terminal=False, **kwargs): + platform = muselab_icesugar.Platform() + + # Disable Integrated ROM/SRAM since too large for iCE40 and UP5K has specific SPRAM. + kwargs["integrated_sram_size"] = 0 + kwargs["integrated_rom_size"] = 0 + + # Set CPU variant / reset address + kwargs["cpu_variant"] = "lite" + kwargs["cpu_reset_address"] = self.mem_map["spiflash"] + bios_flash_offset + + # SoCCore ---------------------------------------------------------------------------------- + SoCCore.__init__(self, platform, sys_clk_freq, + ident = "LiteX SoC on Muselab iCESugar", + ident_version = True, + **kwargs) + + # CRG -------------------------------------------------------------------------------------- + self.submodules.crg = _CRG(platform, sys_clk_freq) + + # 128KB SPRAM (used as SRAM) --------------------------------------------------------------- + self.submodules.spram = Up5kSPRAM(size=64*kB) + self.bus.add_slave("sram", self.spram.bus, SoCRegion(size=64*kB)) + + # SPI Flash -------------------------------------------------------------------------------- + self.add_spi_flash(mode="1x", dummy_cycles=8) + + # Add ROM linker region -------------------------------------------------------------------- + self.bus.add_region("rom", SoCRegion( + origin = self.mem_map["spiflash"] + bios_flash_offset, + size = 32*kB, + linker = True) + ) + + # Leds ------------------------------------------------------------------------------------- + led_pads = platform.request_all("user_led_n") + self.submodules.leds = LedChaser( + pads = led_pads, + sys_clk_freq = sys_clk_freq) + +# Flash -------------------------------------------------------------------------------------------- + +def flash(bios_flash_offset): + from litex.build.lattice.programmer import IceSugarProgrammer + prog = IceSugarProgrammer() + prog.flash(bios_flash_offset, "build/muselab_icesugar/software/bios/bios.bin") + prog.flash(0x00000000, "build/muselab_icesugar/gateware/muselab_icesugar.bin") + +# Build -------------------------------------------------------------------------------------------- + +def main(): + parser = argparse.ArgumentParser(description="LiteX SoC on iCEBreaker") + parser.add_argument("--build", action="store_true", help="Build bitstream") + parser.add_argument("--load", action="store_true", help="Load bitstream") + parser.add_argument("--flash", action="store_true", help="Flash Bitstream") + parser.add_argument("--sys-clk-freq", default=24e6, help="System clock frequency (default: 24MHz)") + parser.add_argument("--bios-flash-offset", default=0x40000, help="BIOS offset in SPI Flash (default: 0x40000)") + builder_args(parser) + soc_core_args(parser) + args = parser.parse_args() + + soc = BaseSoC( + bios_flash_offset = args.bios_flash_offset, + sys_clk_freq = int(float(args.sys_clk_freq)), + **soc_core_argdict(args) + ) + builder = Builder(soc, **builder_argdict(args)) + builder.build(run=args.build) + + if args.load: + prog = soc.platform.create_programmer() + prog.load_bitstream(os.path.join(builder.gateware_dir, soc.build_name + ".bin")) + + if args.flash: + flash(args.bios_flash_offset) + +if __name__ == "__main__": + main() diff --git a/test/test_targets.py b/test/test_targets.py index 03b62ab..f95b3cd 100644 --- a/test/test_targets.py +++ b/test/test_targets.py @@ -101,6 +101,7 @@ class TestTargets(unittest.TestCase): platforms.append("fomu_pvt") platforms.append("tinyfpga_bx") platforms.append("icebreaker") + platforms.append("icesugar") # Lattice MachXO2 platforms.append("machxo3")