from litex.gen.genlib.resetsync import AsyncResetSynchronizer
-class LatticeAsyncResetSynchronizerImpl(Module):
+class DiamondAsyncResetSynchronizerImpl(Module):
def __init__(self, cd, async_reset):
rst1 = Signal()
self.specials += [
Instance("FD1S3BX", i_D=0, i_PD=async_reset,
- i_CK=cd.clk, o_Q=rst1),
+ i_CK=cd.clk, o_Q=rst1),
Instance("FD1S3BX", i_D=rst1, i_PD=async_reset,
- i_CK=cd.clk, o_Q=cd.rst)
+ i_CK=cd.clk, o_Q=cd.rst)
]
-class LatticeAsyncResetSynchronizer:
+class DiamondAsyncResetSynchronizer:
@staticmethod
def lower(dr):
- return LatticeAsyncResetSynchronizerImpl(dr.cd, dr.async_reset)
+ return DiamondAsyncResetSynchronizerImpl(dr.cd, dr.async_reset)
-class LatticeDDROutputImpl(Module):
+class DiamondDDROutputImpl(Module):
def __init__(self, i1, i2, o, clk):
self.specials += Instance("ODDRXD1",
- synthesis_directive="ODDRAPPS=\"SCLK_ALIGNED\"",
- i_SCLK=clk,
- i_DA=i1, i_DB=i2, o_Q=o,
- )
+ synthesis_directive="ODDRAPPS=\"SCLK_ALIGNED\"",
+ i_SCLK=clk,
+ i_DA=i1, i_DB=i2, o_Q=o)
-class LatticeDDROutput:
+class DiamondDDROutput:
@staticmethod
def lower(dr):
- return LatticeDDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk)
+ return DiamondDDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk)
-lattice_special_overrides = {
- AsyncResetSynchronizer: LatticeAsyncResetSynchronizer,
- DDROutput: LatticeDDROutput
+diamond_special_overrides = {
+ AsyncResetSynchronizer: DiamondAsyncResetSynchronizer,
+ DDROutput: DiamondDDROutput
+}
+
+
+class IcestormAsyncResetSynchronizerImpl(Module):
+ def __init__(self, cd, async_reset):
+ rst1 = Signal()
+ self.specials += [
+ Instance("SB_DFFS", i_D=0, i_S=async_reset,
+ i_C=cd.clk, o_Q=rst1),
+ Instance("SB_DFFS", i_D=rst1, i_S=async_reset,
+ i_C=cd.clk, o_Q=cd.rst)
+ ]
+
+
+class IcestormAsyncResetSynchronizer:
+ @staticmethod
+ def lower(dr):
+ return IcestormAsyncResetSynchronizerImpl(dr.cd, dr.async_reset)
+
+
+class IcestormDifferentialOutputImpl(Module):
+ def __init__(self, i, o_p, o_n):
+ self.specials += Instance("SB_IO",
+ p_PIN_TYPE=C(0b011000, 6),
+ p_IO_STANDARD="SB_LVCMOS",
+ io_PACKAGE_PIN=o_p,
+ i_D_OUT_0=i)
+
+ self.specials += Instance("SB_IO",
+ p_PIN_TYPE=C(0b011000, 6),
+ p_IO_STANDARD="SB_LVCMOS",
+ io_PACKAGE_PIN=o_n,
+ i_D_OUT_0=~i)
+
+
+class IcestormDifferentialOutput:
+ @staticmethod
+ def lower(dr):
+ return IcestormDifferentialOutputImpl(dr.i, dr.o_p, dr.o_n)
+
+icestorm_special_overrides = {
+ AsyncResetSynchronizer: IcestormAsyncResetSynchronizer,
+ DifferentialOutput: IcestormDifferentialOutput
}
from litex.build.generic_platform import *
from litex.build import tools
+from litex.build.lattice import common
def _format_constraint(c):
return r
-def _build_yosys(device, sources, vincpaths, build_name):
- ys_contents = ""
- incflags = ""
- for path in vincpaths:
- incflags += " -I" + path
- for filename, language, library in sources:
- ys_contents += "read_{}{} {}\n".format(language, incflags, filename)
-
- ys_contents += """synth_ice40 -top top -blif {build_name}.blif""".format(
- build_name=build_name)
-
- ys_name = build_name + ".ys"
- tools.write_to_file(ys_name, ys_contents)
-
-
-def _run_icestorm(build_name, source, yosys_opt, pnr_opt,
- icetime_opt, icepack_opt):
+def _run_icestorm(source, build_template, build_name, pnr_pkg_opts,
+ icetime_pkg_opts, icetime_constraint):
if sys.platform == "win32" or sys.platform == "cygwin":
source_cmd = "call "
script_ext = ".bat"
build_script_contents = "# Autogenerated by LiteX\nset -e\n"
fail_stmt = ""
- build_script_contents += """
-yosys {yosys_opt} -l {build_name}.rpt {build_name}.ys{fail_stmt}
-arachne-pnr {pnr_opt} -p {build_name}.pcf {build_name}.blif -o {build_name}.txt{fail_stmt}
-icetime {icetime_opt} -t -p {build_name}.pcf -r {build_name}.tim {build_name}.txt{fail_stmt}
-icepack {icepack_opt} {build_name}.txt {build_name}.bin{fail_stmt}
-"""
- build_script_contents = build_script_contents.format(
- build_name=build_name,
- yosys_opt=yosys_opt, pnr_opt=pnr_opt, icepack_opt=icepack_opt,
- icetime_opt=icetime_opt, fail_stmt=fail_stmt)
+ for s in build_template:
+ s_fail = s + "{fail_stmt}\n" # Required so Windows scripts fail early.
+ build_script_contents += s_fail.format(build_name=build_name,
+ pnr_pkg_opts=pnr_pkg_opts,
+ icetime_pkg_opts=icetime_pkg_opts,
+ icetime_constraint=icetime_constraint,
+ fail_stmt=fail_stmt)
+
build_script_file = "build_" + build_name + script_ext
tools.write_to_file(build_script_file, build_script_contents,
force_unix=False)
class LatticeIceStormToolchain:
+ attr_translate = {
+ "keep": ("keep", "true"),
+ "no_retiming": None, # yosys can do retiming via the (non-default)
+ # "-retime" option to "synth_ice40", but
+ # yosys does not check for an equivalent
+ # constraint to prevent retiming on signals.
+ "async_reg": None, # yosys has no equivalent, and arachne-pnr
+ # wouldn't take advantage of it anyway.
+
+ # While custom attributes are supported in yosys, neither
+ # arachne-pnr nor icetime currently can take advantage of them
+ # to add fine-grained timing constraints.
+ "mr_ff": None, # user-defined attribute
+ "mr_false_path": None, # user-defined attribute
+ "ars_ff1": None, # user-defined attribute
+ "ars_ff2": None, # user-defined attribute
+ "ars_false_path": None, # user-defined attribute
+
+ # ice40 does not have a shift register primitive.
+ "no_shreg_extract": None
+ }
+
+ special_overrides = common.icestorm_special_overrides
+
def __init__(self):
- self.yosys_opt = "-q"
- self.pnr_opt = "-q"
- self.icetime_opt = ""
- self.icepack_opt = ""
+ self.yosys_template = [
+ "{read_files}",
+ "attrmap -tocase keep -imap keep=\"true\" keep=1 -imap keep=\"false\" keep=0 -remove keep=0",
+ "synth_ice40 -top top -blif {build_name}.blif",
+ ]
+
+ self.build_template = [
+ "yosys -q -l {build_name}.rpt {build_name}.ys",
+ "arachne-pnr -q {pnr_pkg_opts} -p {build_name}.pcf {build_name}.blif -o {build_name}.txt",
+ "icetime {icetime_pkg_opts} -c {icetime_constraint} -t -p {build_name}.pcf -r {build_name}.tim {build_name}.txt",
+ "icepack {build_name}.txt {build_name}.bin"
+ ]
+
self.freq_constraints = dict()
- # platform.device should be of the form "ice40-{1k,8k}-{tq144, etc}""
+ # platform.device should be of the form "ice40-{lp384, hx1k, etc}-{tq144, etc}"
def build(self, platform, fragment, build_dir="build", build_name="top",
run=True):
os.makedirs(build_dir, exist_ok=True)
named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v"
v_output.write(v_file)
- sources = platform.sources | {(v_file, "verilog", "work")}
- _build_yosys(platform.device, sources, platform.verilog_include_paths,
- build_name)
+
+ ys_contents = "\n".join(_.format(build_name=build_name,
+ read_files=self.gen_read_files(platform, v_file))
+ for _ in self.yosys_template)
+
+ ys_name = build_name + ".ys"
+ tools.write_to_file(ys_name, ys_contents)
tools.write_to_file(build_name + ".pcf",
_build_pcf(named_sc, named_pc))
if run:
- (family, size, package) = self.parse_device_string(platform.device)
- pnr_opt = self.pnr_opt + " -d " + size + " -P " + package
- # TODO: PNR will probably eventually support LP devices.
- icetime_opt = self.icetime_opt + " -P " + package + \
- " -d " + "hx" + size + " -c " + \
- str(max(self.freq_constraints.values(), default=0.0))
- _run_icestorm(build_name, False, self.yosys_opt, pnr_opt,
- icetime_opt, self.icepack_opt)
+ (family, series_size, package) = self.parse_device_string(platform.device)
+ pnr_pkg_opts = "-d " + self.get_size_string(series_size) + \
+ " -P " + package
+ icetime_pkg_opts = "-P " + package + " -d " + series_size
+ icetime_constraint = str(max(self.freq_constraints.values(),
+ default=0.0))
+
+ _run_icestorm(False, self.build_template, build_name, pnr_pkg_opts,
+ icetime_pkg_opts, icetime_constraint)
os.chdir(cwd)
return v_output.ns
def parse_device_string(self, device_str):
- (family, size, package) = device_str.split("-")
+ # Arachne only understands packages based on the device size, but
+ # LP for a given size supports packages that HX for the same size
+ # doesn't and vice versa; we need to know the device series due to
+ # icetime.
+ valid_packages = {
+ "lp384": ["qn32", "cm36", "cm49"],
+ "lp1k": ["swg16tr", "cm36", "cm49", "cm81", "cb81", "qn84",
+ "cm121", "cb121"],
+ "hx1k": ["vq100", "cb132", "tq144"],
+ "lp8k": ["cm81", "cm81:4k", "cm121", "cm121:4k", "cm225",
+ "cm225:4k"],
+ "hx8k": ["cb132", "cb132:4k", "tq144:4k", "cm225", "ct256"],
+ }
+
+ (family, series_size, package) = device_str.split("-")
if family not in ["ice40"]:
raise ValueError("Unknown device family")
- if size not in ["1k", "8k"]:
- raise ValueError("Invalid device size")
- if package not in ["tq144", "ct256", "vq100"]:
+ if series_size not in ["lp384", "lp1k", "hx1k", "lp8k", "hx8k"]:
+ raise ValueError("Invalid device series/size")
+ if package not in valid_packages[series_size]:
raise ValueError("Invalid device package")
- return (family, size, package)
+ return (family, series_size, package)
+
+ def get_size_string(self, series_size_str):
+ return series_size_str[2:]
+
+ def gen_read_files(self, platform, main):
+ sources = platform.sources | {(main, "verilog", "work")}
+ incflags = ""
+ read_files = list()
+ for path in platform.verilog_include_paths:
+ incflags += " -I" + path
+ for filename, language, library in sources:
+ read_files.append("read_{}{} {}".format(language,
+ incflags,
+ filename))
+ return "\n".join(read_files)
# icetime can only handle a single global constraint. Pending more
# finely-tuned analysis features in arachne-pnr and IceStorm, save