Import Icestorm backend improvements from Migen.
This commit is contained in:
parent
e7015e4191
commit
f096030fc8
|
@ -50,7 +50,7 @@ class Platform(LatticePlatform):
|
|||
default_clk_period = 83.333
|
||||
|
||||
def __init__(self):
|
||||
LatticePlatform.__init__(self, "ice40-1k-tq144", _io, _connectors,
|
||||
LatticePlatform.__init__(self, "ice40-hx1k-tq144", _io, _connectors,
|
||||
toolchain="icestorm")
|
||||
|
||||
def create_programmer(self):
|
||||
|
|
|
@ -4,7 +4,7 @@ from litex.gen.genlib.io import *
|
|||
from litex.gen.genlib.resetsync import AsyncResetSynchronizer
|
||||
|
||||
|
||||
class LatticeAsyncResetSynchronizerImpl(Module):
|
||||
class DiamondAsyncResetSynchronizerImpl(Module):
|
||||
def __init__(self, cd, async_reset):
|
||||
rst1 = Signal()
|
||||
self.specials += [
|
||||
|
@ -15,27 +15,69 @@ class LatticeAsyncResetSynchronizerImpl(Module):
|
|||
]
|
||||
|
||||
|
||||
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,
|
||||
)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import subprocess
|
|||
import shutil
|
||||
|
||||
from litex.gen.fhdl.structure import _Fragment
|
||||
from litex.gen.fhdl.verilog import DummyAttrTranslate
|
||||
|
||||
from litex.build.generic_platform import *
|
||||
from litex.build import tools
|
||||
|
@ -89,6 +90,10 @@ def _run_diamond(build_name, toolchain_path, ver=None):
|
|||
|
||||
|
||||
class LatticeDiamondToolchain:
|
||||
attr_translate = DummyAttrTranslate()
|
||||
|
||||
special_overrides = common.diamond_special_overrides
|
||||
|
||||
def build(self, platform, fragment, build_dir="build", build_name="top",
|
||||
toolchain_path="/opt/Diamond", run=True, **kwargs):
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
|
|
|
@ -9,6 +9,7 @@ from litex.gen.fhdl.structure import _Fragment
|
|||
|
||||
from litex.build.generic_platform import *
|
||||
from litex.build import tools
|
||||
from litex.build.lattice import common
|
||||
|
||||
|
||||
def _format_constraint(c):
|
||||
|
@ -32,23 +33,8 @@ def _build_pcf(named_sc, named_pc):
|
|||
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"
|
||||
|
@ -62,16 +48,14 @@ def _run_icestorm(build_name, source, yosys_opt, pnr_opt,
|
|||
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)
|
||||
|
@ -82,14 +66,47 @@ icepack {icepack_opt} {build_name}.txt {build_name}.bin{fail_stmt}
|
|||
|
||||
|
||||
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)
|
||||
|
@ -104,35 +121,69 @@ class LatticeIceStormToolchain:
|
|||
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
|
||||
|
|
|
@ -16,9 +16,12 @@ class LatticePlatform(GenericPlatform):
|
|||
raise ValueError("Unknown toolchain")
|
||||
|
||||
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
|
||||
so = dict(common.lattice_special_overrides)
|
||||
so = dict() # No common overrides between ECP and ice40.
|
||||
so.update(self.toolchain.special_overrides)
|
||||
so.update(special_overrides)
|
||||
return GenericPlatform.get_verilog(self, *args, special_overrides=so, **kwargs)
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue