Merge pull request #30 from cr1901/icestorm
Add Icestorm backend and iCEStick
This commit is contained in:
commit
d95d561737
|
@ -0,0 +1,57 @@
|
||||||
|
from litex.build.generic_platform import *
|
||||||
|
from litex.build.lattice import LatticePlatform
|
||||||
|
from litex.build.lattice.programmer import IceStormProgrammer
|
||||||
|
|
||||||
|
|
||||||
|
_io = [
|
||||||
|
("user_led", 0, Pins("99"), IOStandard("LVCMOS33")),
|
||||||
|
("user_led", 1, Pins("98"), IOStandard("LVCMOS33")),
|
||||||
|
("user_led", 2, Pins("97"), IOStandard("LVCMOS33")),
|
||||||
|
("user_led", 3, Pins("96"), IOStandard("LVCMOS33")),
|
||||||
|
("user_led", 4, Pins("95"), IOStandard("LVCMOS33")),
|
||||||
|
|
||||||
|
("serial", 0,
|
||||||
|
Subsignal("rx", Pins("9")),
|
||||||
|
Subsignal("tx", Pins("8"), Misc("PULLUP")),
|
||||||
|
Subsignal("rts", Pins("7"), Misc("PULLUP")),
|
||||||
|
Subsignal("cts", Pins("4"), Misc("PULLUP")),
|
||||||
|
Subsignal("dtr", Pins("3"), Misc("PULLUP")),
|
||||||
|
Subsignal("dsr", Pins("2"), Misc("PULLUP")),
|
||||||
|
Subsignal("dcd", Pins("1"), Misc("PULLUP")),
|
||||||
|
IOStandard("LVTTL"),
|
||||||
|
),
|
||||||
|
|
||||||
|
("irda", 0,
|
||||||
|
Subsignal("rx", Pins("106")),
|
||||||
|
Subsignal("tx", Pins("105")),
|
||||||
|
Subsignal("sd", Pins("107")),
|
||||||
|
IOStandard("LVCMOS33")
|
||||||
|
),
|
||||||
|
|
||||||
|
("spiflash", 0,
|
||||||
|
Subsignal("cs_n", Pins("71"), IOStandard("LVCMOS33")),
|
||||||
|
Subsignal("clk", Pins("70"), IOStandard("LVCMOS33")),
|
||||||
|
Subsignal("mosi", Pins("67"), IOStandard("LVCMOS33")),
|
||||||
|
Subsignal("miso", Pins("68"), IOStandard("LVCMOS33"))
|
||||||
|
),
|
||||||
|
|
||||||
|
("clk12", 0, Pins("21"), IOStandard("LVCMOS33"))
|
||||||
|
]
|
||||||
|
|
||||||
|
_connectors = [
|
||||||
|
("GPIO0", "44 45 47 48 56 60 61 62"),
|
||||||
|
("GPIO1", "119 118 117 116 115 114 113 112"),
|
||||||
|
("PMOD", "78 79 80 81 87 88 90 91")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Platform(LatticePlatform):
|
||||||
|
default_clk_name = "clk12"
|
||||||
|
default_clk_period = 83.333
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
LatticePlatform.__init__(self, "ice40-1k-tq144", _io, _connectors,
|
||||||
|
toolchain="icestorm")
|
||||||
|
|
||||||
|
def create_programmer(self):
|
||||||
|
return IceStormProgrammer()
|
|
@ -0,0 +1,148 @@
|
||||||
|
# This file is Copyright (c) 2017 William D. Jones <thor0505@comcast.net>
|
||||||
|
# License: BSD
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from litex.gen.fhdl.structure import _Fragment
|
||||||
|
|
||||||
|
from litex.build.generic_platform import *
|
||||||
|
from litex.build import tools
|
||||||
|
|
||||||
|
|
||||||
|
def _format_constraint(c):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _format_pcf(signame, pin, others, resname):
|
||||||
|
return "set_io " + signame + " " + pin + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def _build_pcf(named_sc, named_pc):
|
||||||
|
r = ""
|
||||||
|
for sig, pins, others, resname in named_sc:
|
||||||
|
if len(pins) > 1:
|
||||||
|
for i, p in enumerate(pins):
|
||||||
|
r += _format_pcf(sig + "[" + str(i) + "]", p, others, resname)
|
||||||
|
else:
|
||||||
|
r += _format_pcf(sig, pins[0], others, resname)
|
||||||
|
if named_pc:
|
||||||
|
r += "\n" + "\n\n".join(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):
|
||||||
|
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||||
|
source_cmd = "call "
|
||||||
|
script_ext = ".bat"
|
||||||
|
shell = ["cmd", "/c"]
|
||||||
|
build_script_contents = "@echo off\nrem Autogenerated by LiteX\n"
|
||||||
|
fail_stmt = " || exit /b"
|
||||||
|
else:
|
||||||
|
source_cmd = "source "
|
||||||
|
script_ext = ".sh"
|
||||||
|
shell = ["bash"]
|
||||||
|
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)
|
||||||
|
build_script_file = "build_" + build_name + script_ext
|
||||||
|
tools.write_to_file(build_script_file, build_script_contents,
|
||||||
|
force_unix=False)
|
||||||
|
command = shell + [build_script_file]
|
||||||
|
r = subprocess.call(command)
|
||||||
|
if r != 0:
|
||||||
|
raise OSError("Subprocess failed")
|
||||||
|
|
||||||
|
|
||||||
|
class LatticeIceStormToolchain:
|
||||||
|
def __init__(self):
|
||||||
|
self.yosys_opt = "-q"
|
||||||
|
self.pnr_opt = "-q"
|
||||||
|
self.icetime_opt = ""
|
||||||
|
self.icepack_opt = ""
|
||||||
|
self.freq_constraints = dict()
|
||||||
|
|
||||||
|
# platform.device should be of the form "ice40-{1k,8k}-{tq144, etc}""
|
||||||
|
def build(self, platform, fragment, build_dir="build", build_name="top",
|
||||||
|
run=True):
|
||||||
|
os.makedirs(build_dir, exist_ok=True)
|
||||||
|
cwd = os.getcwd()
|
||||||
|
os.chdir(build_dir)
|
||||||
|
|
||||||
|
if not isinstance(fragment, _Fragment):
|
||||||
|
fragment = fragment.get_fragment()
|
||||||
|
platform.finalize(fragment)
|
||||||
|
|
||||||
|
v_output = platform.get_verilog(fragment)
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
os.chdir(cwd)
|
||||||
|
|
||||||
|
return v_output.ns
|
||||||
|
|
||||||
|
def parse_device_string(self, device_str):
|
||||||
|
(family, 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"]:
|
||||||
|
raise ValueError("Invalid device package")
|
||||||
|
return (family, size, package)
|
||||||
|
|
||||||
|
# icetime can only handle a single global constraint. Pending more
|
||||||
|
# finely-tuned analysis features in arachne-pnr and IceStorm, save
|
||||||
|
# all the constraints in a dictionary and test against the fastest clk.
|
||||||
|
# Though imprecise, if the global design satisfies the fastest clock,
|
||||||
|
# we can be sure all other constraints are satisfied.
|
||||||
|
def add_period_constraint(self, platform, clk, period):
|
||||||
|
new_freq = 1000.0/period
|
||||||
|
|
||||||
|
if clk not in self.freq_constraints.keys():
|
||||||
|
self.freq_constraints[clk] = new_freq
|
||||||
|
else:
|
||||||
|
raise ConstraintError("Period constraint already added to signal.")
|
|
@ -1,5 +1,5 @@
|
||||||
from litex.build.generic_platform import GenericPlatform
|
from litex.build.generic_platform import GenericPlatform
|
||||||
from litex.build.lattice import common, diamond
|
from litex.build.lattice import common, diamond, icestorm
|
||||||
|
|
||||||
|
|
||||||
class LatticePlatform(GenericPlatform):
|
class LatticePlatform(GenericPlatform):
|
||||||
|
@ -9,6 +9,9 @@ class LatticePlatform(GenericPlatform):
|
||||||
GenericPlatform.__init__(self, *args, **kwargs)
|
GenericPlatform.__init__(self, *args, **kwargs)
|
||||||
if toolchain == "diamond":
|
if toolchain == "diamond":
|
||||||
self.toolchain = diamond.LatticeDiamondToolchain()
|
self.toolchain = diamond.LatticeDiamondToolchain()
|
||||||
|
elif toolchain == "icestorm":
|
||||||
|
self.bitstream_ext = ".bin"
|
||||||
|
self.toolchain = icestorm.LatticeIceStormToolchain()
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown toolchain")
|
raise ValueError("Unknown toolchain")
|
||||||
|
|
||||||
|
|
|
@ -20,3 +20,24 @@ class LatticeProgrammer(GenericProgrammer):
|
||||||
else:
|
else:
|
||||||
pgrcmr = 'pgrcmr'
|
pgrcmr = 'pgrcmr'
|
||||||
subprocess.call([pgrcmd, "-infile", xcf_file])
|
subprocess.call([pgrcmd, "-infile", xcf_file])
|
||||||
|
|
||||||
|
|
||||||
|
class IceStormProgrammer(GenericProgrammer):
|
||||||
|
needs_bitreverse = False
|
||||||
|
|
||||||
|
def flash(self, address, bitstream_file):
|
||||||
|
subprocess.call(["iceprog", "-o", str(address), bitstream_file])
|
||||||
|
|
||||||
|
def load_bitstream(self, bitstream_file):
|
||||||
|
subprocess.call(["iceprog", "-S", bitstream_file])
|
||||||
|
|
||||||
|
|
||||||
|
class IceBurnProgrammer(GenericProgrammer):
|
||||||
|
def __init__(self, iceburn_path):
|
||||||
|
GenericProgrammer.__init__(self)
|
||||||
|
self.iceburn = iceburn_path
|
||||||
|
|
||||||
|
needs_bitreverse = False
|
||||||
|
|
||||||
|
def load_bitstream(self, bitstream_file):
|
||||||
|
subprocess.call([self.iceburn, "-evw", bitstream_file])
|
||||||
|
|
Loading…
Reference in New Issue