diff --git a/litex/build/quicklogic/__init__.py b/litex/build/quicklogic/__init__.py new file mode 100644 index 000000000..6a0ad479c --- /dev/null +++ b/litex/build/quicklogic/__init__.py @@ -0,0 +1 @@ +from litex.build.quicklogic.platform import QuickLogicPlatform diff --git a/litex/build/quicklogic/common.py b/litex/build/quicklogic/common.py new file mode 100644 index 000000000..17d749490 --- /dev/null +++ b/litex/build/quicklogic/common.py @@ -0,0 +1,9 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2021 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +# QuickLogic Special Overrides --------------------------------------------------------------------- + +quicklogic_special_overrides = {} diff --git a/litex/build/quicklogic/platform.py b/litex/build/quicklogic/platform.py new file mode 100644 index 000000000..44c854f6a --- /dev/null +++ b/litex/build/quicklogic/platform.py @@ -0,0 +1,33 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2021 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +import os + +from litex.build.generic_platform import GenericPlatform +from litex.build.quicklogic import common, symbiflow + +# QuickLogicPlatform ------------------------------------------------------------------------------- + +class QuickLogicPlatform(GenericPlatform): + bitstream_ext = ".bit" + + def __init__(self, device, *args, toolchain="symbiflow", **kwargs): + GenericPlatform.__init__(self, device, *args, **kwargs) + if toolchain == "symbiflow": + self.toolchain = symbiflow.SymbiflowToolchain() + else: + raise ValueError("Unknown toolchain") + + def get_verilog(self, *args, special_overrides=dict(), **kwargs): + so = dict(common.quicklogic_special_overrides) + so.update(special_overrides) + return GenericPlatform.get_verilog(self, *args, + special_overrides = so, + attr_translate = self.toolchain.attr_translate, + **kwargs) + + def build(self, *args, **kwargs): + return self.toolchain.build(self, *args, **kwargs) diff --git a/litex/build/quicklogic/symbiflow.py b/litex/build/quicklogic/symbiflow.py new file mode 100644 index 000000000..afee2abcb --- /dev/null +++ b/litex/build/quicklogic/symbiflow.py @@ -0,0 +1,116 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2021 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +import os +import sys +import subprocess +from shutil import which + +from migen.fhdl.structure import _Fragment + +from litex.build.generic_platform import * +from litex.build import tools +from litex.build.quicklogic import common + + +# IO Constraints (.pcf) ---------------------------------------------------------------------------- + +def _format_io_pcf(signame, pin, others): + r = f"set_io {signame} {Pins(pin).identifiers[0]}\n" + return r + +def _build_io_pcf(named_sc, named_pc, build_name): + pcf = "" + for sig, pins, others, resname in named_sc: + if len(pins) > 1: + for i, p in enumerate(pins): + pcf += _format_io_pcf(sig + "(" + str(i) + ")", p, others) + else: + pcf += _format_io_pcf(sig, pins[0], others) + tools.write_to_file(build_name + ".pcf", pcf) + +# Build Makefile ----------------------------------------------------------------------------------- + +def _build_makefile(platform, sources, build_dir, build_name): + makefile = [] + + # Define Paths. + makefile.append("mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))") + makefile.append("current_dir := $(patsubst %/,%,$(dir $(mkfile_path)))") + + # Create Project. + # FIXME: Only use top file for now and ignore .init files. + makefile.append("all:") + makefile.append("\tql_symbiflow -compile -d {device} -P {part} -v {verilog} -t {top} -p {pcf}".format( + device = platform.device, + part = {"ql-eos-s3": "pd64"}.get(platform.device), + verilog = f"{build_name}.v", + top = build_name, + pcf = f"{build_name}.pcf" + )) + + # Generate Makefile. + tools.write_to_file("Makefile", "\n".join(makefile)) + +def _run_make(): + make_cmd = ["make", "-j1"] + + if which("ql_symbiflow") is None: + msg = "Unable to find QuickLogic Symbiflow toolchain, please:\n" + msg += "- Add QuickLogic Symbiflow toolchain to your $PATH." + raise OSError(msg) + + if subprocess.call(make_cmd) != 0: + raise OSError("Error occured during QuickLogic Symbiflow's script execution.") + + +# SymbiflowToolchain ------------------------------------------------------------------------------- + +class SymbiflowToolchain: + attr_translate = {} + + special_overrides = common.quicklogic_special_overrides + + def __init__(self): + self.clocks = dict() + self.false_paths = set() + + def build(self, platform, fragment, + build_dir = "build", + build_name = "top", + run = False, + **kwargs): + + # Create build directory. + os.makedirs(build_dir, exist_ok=True) + cwd = os.getcwd() + os.chdir(build_dir) + + # Finalize design. + if not isinstance(fragment, _Fragment): + fragment = fragment.get_fragment() + platform.finalize(fragment) + + # Generate verilog. + v_output = platform.get_verilog(fragment, name=build_name, **kwargs) + named_sc, named_pc = platform.resolve_signals(v_output.ns) + top_file = build_name + ".v" + v_output.write(top_file) + platform.add_source(top_file) + + # Generate .pcf IO constraints file. + _build_io_pcf(named_sc, named_pc, build_name) + + # Generate Makefie. + _build_makefile(platform, platform.sources, build_dir, build_name) + + # Run. + if run: + _run_make() + + os.chdir(cwd) + + return v_output.ns