diff --git a/litex/build/colognechip/__init__.py b/litex/build/colognechip/__init__.py new file mode 100644 index 000000000..4f7ade7e9 --- /dev/null +++ b/litex/build/colognechip/__init__.py @@ -0,0 +1 @@ +from litex.build.colognechip.platform import CologneChipPlatform diff --git a/litex/build/colognechip/colognechip.py b/litex/build/colognechip/colognechip.py new file mode 100644 index 000000000..2de8b7a6e --- /dev/null +++ b/litex/build/colognechip/colognechip.py @@ -0,0 +1,156 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2023 Gwenhael Goavec-Merou +# SPDX-License-Identifier: BSD-2-Clause + +import os +import sys +import math +import subprocess +from shutil import which, copyfile + +from migen.fhdl.structure import _Fragment + +from litex.build.generic_platform import * +from litex.build import tools +from litex.build.generic_toolchain import GenericToolchain +from litex.build.yosys_wrapper import YosysWrapper, yosys_args, yosys_argdict + + +# CologneChipToolchain ----------------------------------------------------------------------------- + +class CologneChipToolchain(GenericToolchain): + attr_translate = {} + supported_build_backend = ["litex", "edalize"] + + def __init__(self): + super().__init__() + self._yosys = None + self._yosys_cmds = [] + self._synth_opts = "-nomx8 " + + def finalize(self): + self._yosys = YosysWrapper( + platform = self.platform, + build_name = self._build_name, + target = "gatemate", + output_name = self._build_name+"_synth", + template = [], + yosys_opts = self._synth_opts, + yosys_cmds = self._yosys_cmds, + synth_format = "v", + ) + + # IO Constraints (.ccf) ------------------------------------------------------------------------ + + def _get_pin_direction(self, pinname): + pins = self.platform.constraint_manager.get_io_signals() + for pin in sorted(pins, key=lambda x: x.duid): + if (pinname.split("[")[0] == pin.name): + if pin.direction == "output": + return "Pin_out" + elif pin.direction == "input": + return "Pin_in" + else: + return "Pin_inout" + return "Unknown" + + def build_io_constraints(self): + ccf = [] + + flat_sc = [] + for name, pins, other, resource in self.named_sc: + if len(pins) > 1: + for i, p in enumerate(pins): + flat_sc.append((f"{name}[{i}]", p, other)) + else: + flat_sc.append((name, pins[0], other)) + + for name, pin, other in flat_sc: + pin_cst = "" + if pin != "X": + direction = self._get_pin_direction(name) + pin_cst = f"{direction} \"{name}\" Loc = \"{pin}\"" + + for c in other: + if isinstance(c, Misc): + pin_cst += f" | {c.misc}" + pin_cst += ";" + ccf.append(pin_cst) + + if self.named_pc: + ccf.extend(self.named_pc) + + tools.write_to_file(f"{self._build_name}.ccf", "\n".join(ccf)) + return (f"{self._build_name}.ccf", "CCF") + + # Project (.ys) -------------------------------------------------------------------------------- + + def build_project(self): + """ create project files (mainly Yosys ys file) + """ + self._yosys.build_script() + + # Script --------------------------------------------------------------------------------------- + + def build_script(self): + """ create build_xxx.yy by using Yosys and p_r instances. + Return + ====== + the script name (str) + """ + + if sys.platform in ("win32", "cygwin"): + script_ext = ".bat" + script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n" + fail_stmt = " || exit /b" + else: + script_ext = ".sh" + script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n" + fail_stmt = "" + fail_stmt += "\n" + + # yosys call + script_contents += self._yosys.get_yosys_call("script") + fail_stmt + # p_r call + script_contents += "p_r -ccf {build_name}.ccf -A 1 -i {build_name}_synth.v -o {build_name} -lib ccag\n".format( + build_name = self._build_name) + + script_file = "build_" + self._build_name + script_ext + tools.write_to_file(script_file, script_contents, force_unix=False) + + return script_file + + def run_script(self, script): + """ run build_xxx.yy script + Parameters + ========== + script: str + script name to use + """ + if sys.platform in ("win32", "cygwin"): + shell = ["cmd", "/c"] + else: + shell = ["bash"] + + if which("yosys") is None or which("p_r") is None: + msg = "Unable to find CologneChip toolchain, please:\n" + msg += "- Add Yosys/p_r toolchain to your $PATH." + raise OSError(msg) + + if subprocess.call(shell + [script]) != 0: + raise OSError("Error occured during Yosys/p_r's script execution.") + + + def add_period_constraint(self, platform, clk, period): + pass + +def colognechip_args(parser): + # TODO: yosys (default's yosys aren't supported + # TODO: p_r args + pass + +def colognechip_argdict(args): + # TODO: ditto + return {} diff --git a/litex/build/colognechip/common.py b/litex/build/colognechip/common.py new file mode 100644 index 000000000..54955c33a --- /dev/null +++ b/litex/build/colognechip/common.py @@ -0,0 +1,132 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2023 Gwenhael Goavec-Merou +# SPDX-License-Identifier: BSD-2-Clause + +from migen.fhdl.module import Module +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex.build.io import * + +# CologneChip AsyncResetSynchronizer --------------------------------------------------------------- + +class CologneChipAsyncResetSynchronizerImpl(Module): + def __init__(self, cd, async_reset): + rst1 = Signal() + self.specials += [ + Instance("CC_DFF", + p_CLK_INV = 0, + p_EN_INV = 0, + p_SR_INV = 0, + p_SR_VAL = 1, + i_D = 0, + i_CLK = cd.clk, + i_EN = 1, + i_SR = async_reset, + o_Q = rst1), + Instance("CC_DFF", + p_CLK_INV = 0, + p_EN_INV = 0, + p_SR_INV = 0, + p_SR_VAL = 1, + i_D = rst1, + i_CLK = cd.clk, + i_EN = 1, + i_SR = async_reset, + o_Q = cd.rst) + ] + +class CologneChipAsyncResetSynchronizer: + @staticmethod + def lower(dr): + return CologneChipAsyncResetSynchronizerImpl(dr.cd, dr.async_reset) + +# CologneChip DDR Input ---------------------------------------------------------------------------- + +class CologneChipDDRInputImpl(Module): + def __init__(self, i, o1, o2, clk): + self.specials += Instance("CC_IDDR", + i_CLK = clk, + i_D = i, + o_Q0 = o1, + o_Q1 = o2, + ) + +class CologneChipDDRInput: + @staticmethod + def lower(dr): + return CologneChipInputImpl(dr.i, dr.o1, dr.o2, dr.clk) + +# CologneChip DDR Output --------------------------------------------------------------------------- + +class CologneChipDDROutputImpl(Module): + def __init__(self, i1, i2, o, clk): + self.specials += Instance("CC_ODDR", + p_CLK_INV = 0, + i_CLK = clk, + i_DDR = ~clk, + i_D0 = i1, + i_D1 = i2, + o_Q = o, + ) + +class CologneChipDDROutput: + @staticmethod + def lower(dr): + return CologneChipDDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk) + +# CologneChip Differential Input ------------------------------------------------------------------- + +class CologneChipDifferentialInputImpl(Module): + def __init__(self, i_p, i_n, o): + self.specials += Instance("CC_LVDS_IBUF", + i_I_P = i_p, + i_I_N = i_n, + o_Y = o, + ) + +class CologneChipDifferentialInput: + @staticmethod + def lower(dr): + return CologneChipDifferentialInputImpl(dr.i_p, dr.i_n, dr.o) + +# CologneChip Differential Output ------------------------------------------------------------------ + +class CologneChipDifferentialOutputImpl(Module): + def __init__(self, i, o_p, o_n): + self.specials += Instance("CC_LVDS_OBUF", + i_A = i, + o_O_P = o_p, + o_O_N = o_n, + ) + +class CologneChipDifferentialOutput: + @staticmethod + def lower(dr): + return CologneChipDifferentialOutputImpl(dr.i, dr.o_p, dr.o_n) + +# CologneChip SDR Input ---------------------------------------------------------------------------- + +class CologneChipSDRInputImpl(Module): + def __init__(self, i, o): + self.specials += Instance("CC_IBUF", + i_I = i, + o_O = o, + ) + +class CologneChipSDRInput: + @staticmethod + def lower(dr): + return CologneChipSDRInput(dr.i, dr.o) + +# CologneChip Special Overrides -------------------------------------------------------------------- + +colognechip_special_overrides = { + AsyncResetSynchronizer: CologneChipAsyncResetSynchronizer, + DDRInput: CologneChipDDRInput, + DDROutput: CologneChipDDROutput, + DifferentialInput: CologneChipDifferentialInput, + DifferentialOutput: CologneChipDifferentialOutput, + SDRInput: CologneChipSDRInput, +} diff --git a/litex/build/colognechip/platform.py b/litex/build/colognechip/platform.py new file mode 100644 index 000000000..127c4e2fb --- /dev/null +++ b/litex/build/colognechip/platform.py @@ -0,0 +1,68 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2023 Gwenhael Goavec-Merou +# SPDX-License-Identifier: BSD-2-Clause + +import os + +from litex.build.generic_platform import GenericPlatform +from litex.build.colognechip import common, colognechip + +# CologneChipPlatform ------------------------------------------------------------------------------ + +class CologneChipPlatform(GenericPlatform): + bitstream_ext = "_00.cfg.bit" + + _supported_toolchains = ["colognechip"] + + def __init__(self, device, *args, toolchain="colognechip", devicename=None, **kwargs): + GenericPlatform.__init__(self, device, *args, **kwargs) + + self.toolchain = colognechip.CologneChipToolchain() + + def get_verilog(self, *args, special_overrides=dict(), **kwargs): + so = dict(common.colognechip_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) + + def add_period_constraint(self, clk, period): + if clk is None: return + self.toolchain.add_period_constraint(self, clk, period) + + @classmethod + def fill_args(cls, toolchain, parser): + """ + pass parser to the specific toolchain to + fill this with toolchain args + + Parameters + ========== + toolchain: str + toolchain name + parser: argparse.ArgumentParser + parser to be filled + """ + colognechip.colognechip_args(parser) + + @classmethod + def get_argdict(cls, toolchain, args): + """ + return a dict of args + + Parameters + ========== + toolchain: str + toolchain name + + Return + ====== + a dict of key/value for each args or an empty dict + """ + return colognechip.colognechip_argdict(args)