vendor.lattice_ice40: add iCECube support.

This also makes some iCE40 and ECP5 overrides more consistent.
This commit is contained in:
whitequark 2019-09-21 14:27:35 +00:00
parent 8050cfaa98
commit 3d62dac1cb
2 changed files with 221 additions and 19 deletions

View file

@ -29,6 +29,7 @@ class LatticeECP5Platform(TemplatedPlatform):
* ``yosys_opts``: adds extra options for ``yosys``.
* ``nextpnr_opts``: adds extra options for ``nextpnr-ecp5``.
* ``ecppack_opts``: adds extra options for ``ecppack``.
* ``add_preferences``: inserts commands at the end of the LPF file.
Build products:
* ``{{name}}.rpt``: Yosys log.
@ -51,8 +52,8 @@ class LatticeECP5Platform(TemplatedPlatform):
Available overrides:
* ``script_project``: inserts commands before ``prj_project save`` in Tcl script.
* ``script_after_export``: inserts commands after ``prj_run Export`` in Tcl script.
* ``add_preferences``: inserts commands in LPF file.
* ``add_constraints``: inserts commands in XDC file.
* ``add_preferences``: inserts commands at the end of the LPF file.
* ``add_constraints``: inserts commands at the end of the XDC file.
Build products:
* ``{{name}}_impl/{{name}}_impl.htm``: consolidated log.
@ -91,6 +92,11 @@ class LatticeECP5Platform(TemplatedPlatform):
"BG756": "caBGA756",
}
_trellis_required_tools = [
"yosys",
"nextpnr-ecp5",
"ecppack"
]
_trellis_file_templates = {
**TemplatedPlatform.build_script_templates,
"{{name}}.il": r"""
@ -123,6 +129,7 @@ class LatticeECP5Platform(TemplatedPlatform):
{% for signal, frequency in platform.iter_clock_constraints() -%}
FREQUENCY NET "{{signal|hierarchy(".")}}" {{frequency}} HZ;
{% endfor %}
{{get_override("add_preferences")|default("# (add_preferences placeholder)")}}
"""
}
_trellis_command_templates = [
@ -157,6 +164,10 @@ class LatticeECP5Platform(TemplatedPlatform):
# Diamond templates
_diamond_required_tools = [
"pnmainc",
"ddtcmd"
]
_diamond_file_templates = {
**TemplatedPlatform.build_script_templates,
"build_{{name}}.sh": r"""
@ -243,9 +254,9 @@ class LatticeECP5Platform(TemplatedPlatform):
@property
def required_tools(self):
if self.toolchain == "Trellis":
return ["yosys", "nextpnr-ecp5", "ecppack"]
return self._trellis_required_tools
if self.toolchain == "Diamond":
return ["pnmainc", "ddtcmd"]
return self._diamond_required_tools
assert False
@property

View file

@ -9,6 +9,9 @@ __all__ = ["LatticeICE40Platform"]
class LatticeICE40Platform(TemplatedPlatform):
"""
IceStorm toolchain
------------------
Required tools:
* ``yosys``
* ``nextpnr-ice40``
@ -25,6 +28,8 @@ class LatticeICE40Platform(TemplatedPlatform):
* ``script_after_synth``: inserts commands after ``synth_ice40`` in Yosys script.
* ``yosys_opts``: adds extra options for ``yosys``.
* ``nextpnr_opts``: adds extra options for ``nextpnr-ice40``.
* ``add_pre_pack``: inserts commands at the end in pre-pack Python script.
* ``add_constraints``: inserts commands at the end in the PCF file.
Build products:
* ``{{name}}.rpt``: Yosys log.
@ -32,18 +37,42 @@ class LatticeICE40Platform(TemplatedPlatform):
* ``{{name}}.tim``: nextpnr log.
* ``{{name}}.asc``: ASCII bitstream.
* ``{{name}}.bin``: binary bitstream.
iCECube2 toolchain
------------------
This toolchain comes in two variants: ``LSE-iCECube2`` and ``Synplify-iCECube2``.
Required tools:
* iCECube2 toolchain
* ``tclsh``
The environment is populated by setting the necessary environment variables based on
``NMIGEN_ENV_iCECube2``, which must point to the root of the iCECube2 installation, and
is required.
Available overrides:
* ``verbose``: enables logging of informational messages to standard error.
* ``lse_opts``: adds options for LSE.
* ``script_after_add``: inserts commands after ``add_file`` in Synplify Tcl script.
* ``script_after_options``: inserts commands after ``set_option`` in Synplify Tcl script.
* ``add_constraints``: inserts commands in SDC file.
* ``script_after_flow``: inserts commands after ``run_sbt_backend_auto`` in SBT
Tcl script.
Build products:
* ``{{name}}_lse.log`` (LSE) or ``{{name}}_design/{{name}}.htm`` (Synplify): synthesis log.
* ``sbt/outputs/router/{{name}}_timing.rpt``: timing report.
* ``{{name}}.edf``: EDIF netlist.
* ``{{name}}.bin``: binary bitstream.
"""
toolchain = "IceStorm"
toolchain = None # selected when creating platform
device = abstractproperty()
package = abstractproperty()
required_tools = [
"yosys",
"nextpnr-ice40",
"icepack",
]
# IceStorm templates
_nextpnr_device_options = {
"iCE40LP384": "--lp384",
@ -67,7 +96,12 @@ class LatticeICE40Platform(TemplatedPlatform):
"iCE5LP1K": "",
}
file_templates = {
_icestorm_required_tools = [
"yosys",
"nextpnr-ice40",
"icepack",
]
_icestorm_file_templates = {
**TemplatedPlatform.build_script_templates,
"{{name}}.il": r"""
# {{autogenerated}}
@ -87,21 +121,23 @@ class LatticeICE40Platform(TemplatedPlatform):
{{get_override("script_after_synth")|default("# (script_after_synth placeholder)")}}
write_json {{name}}.json
""",
"{{name}}.pcf": r"""
# {{autogenerated}}
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
set_io {{port_name}} {{pin_name}}
{% endfor %}
""",
"{{name}}_pre_pack.py": r"""
# {{autogenerated}}
{% for signal, frequency in platform.iter_clock_constraints() -%}
{# Clock in MHz #}
ctx.addClock("{{signal|hierarchy(".")}}", {{frequency/1000000}})
{% endfor%}
{{get_override("add_pre_pack")|default("# (add_pre_pack placeholder)")}}
""",
"{{name}}.pcf": r"""
# {{autogenerated}}
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
set_io {{port_name}} {{pin_name}}
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
}
command_templates = [
_icestorm_command_templates = [
r"""
{{get_tool("yosys")}}
{{quiet("-q")}}
@ -130,6 +166,161 @@ class LatticeICE40Platform(TemplatedPlatform):
"""
]
# iCECube2 templates
_icecube2_required_tools = [
"tclsh",
]
_icecube2_file_templates = {
**TemplatedPlatform.build_script_templates,
"build_{{name}}.sh": r"""
# {{autogenerated}}
set -e{{verbose("x")}}
if [ -n "${{platform._toolchain_env_var}}" ]; then
# LSE environment
export LD_LIBRARY_PATH=${{platform._toolchain_env_var}}/LSE/bin/lin64:$LD_LIBRARY_PATH
export PATH=${{platform._toolchain_env_var}}/LSE/bin/lin64:$PATH
export FOUNDRY=${{platform._toolchain_env_var}}/LSE
# Synplify environment
export LD_LIBRARY_PATH=${{platform._toolchain_env_var}}/sbt_backend/bin/linux/opt/synpwrap:$LD_LIBRARY_PATH
export PATH=${{platform._toolchain_env_var}}/sbt_backend/bin/linux/opt/synpwrap:$PATH
export SYNPLIFY_PATH=${{platform._toolchain_env_var}}/synpbase
# Common environment
export SBT_DIR=${{platform._toolchain_env_var}}/sbt_backend
else
echo "Variable ${{platform._toolchain_env_var}} must be set" >&2; exit 1
fi
{{emit_commands("sh")}}
""",
"{{name}}.v": r"""
/* {{autogenerated}} */
{{emit_design("verilog")}}
""",
"{{name}}_lse.prj": r"""
# {{autogenerated}}
-a SBT{{platform.family}}
-d {{platform.device}}
-t {{platform.package}}
{{get_override("lse_opts")|options|default("# (lse_opts placeholder)")}}
{% for file in platform.iter_extra_files(".v") -%}
-ver {{file}}
{% endfor %}
-ver {{name}}.v
-sdc {{name}}.sdc
-top {{name}}
-output_edif {{name}}.edf
-logfile {{name}}_lse.log
""",
"{{name}}_syn.prj": r"""
# {{autogenerated}}
{% for file in platform.iter_extra_files(".v", ".sv", ".vhd", ".vhdl") -%}
add_file -verilog {{file}}
{% endfor %}
add_file -verilog {{name}}.v
add_file -constraint {{name}}.sdc
{{get_override("script_after_add")|default("# (script_after_add placeholder)")}}
impl -add {{name}}_design -type fpga
set_option -technology SBT{{platform.family}}
set_option -part {{platform.device}}
set_option -package {{platform.package}}
{{get_override("script_after_options")|default("# (script_after_options placeholder)")}}
project -result_format edif
project -result_file {{name}}.edf
impl -active {{name}}_design
project -run compile
project -run map
project -run fpga_mapper
file copy -force -- {{name}}_design/{{name}}.edf {{name}}.edf
""",
"{{name}}.sdc": r"""
# {{autogenerated}}
{% for signal, frequency in platform.iter_clock_constraints() -%}
create_clock -name {{signal.name}} -period {{1000000000/frequency}} [get_nets {{signal|hierarchy("/")}}]
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
"{{name}}.tcl": r"""
# {{autogenerated}}
set device {{platform.device}}-{{platform.package}}
set top_module {{name}}
set proj_dir .
set output_dir .
set edif_file {{name}}
set tool_options ":edifparser -y {{name}}.pcf"
set sbt_root $::env(SBT_DIR)
append sbt_tcl $sbt_root "/tcl/sbt_backend_synpl.tcl"
source $sbt_tcl
run_sbt_backend_auto $device $top_module $proj_dir $output_dir $tool_options $edif_file
{{get_override("script_after_file")|default("# (script_after_file placeholder)")}}
file copy -force -- sbt/outputs/bitmap/{{name}}_bitmap.bin {{name}}.bin
exit
""",
"{{name}}.pcf": r"""
# {{autogenerated}}
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
set_io {{port_name}} {{pin_name}}
{% endfor %}
""",
}
_lse_icecube2_command_templates = [
r"""synthesis -f {{name}}_lse.prj""",
r"""tclsh {{name}}.tcl""",
]
_synplify_icecube2_command_templates = [
r"""synpwrap -prj {{name}}_syn.prj -log {{name}}_syn.log""",
r"""tclsh {{name}}.tcl""",
]
# Common logic
def __init__(self, *, toolchain="IceStorm"):
super().__init__()
assert toolchain in ("IceStorm", "LSE-iCECube2", "Synplify-iCECube2")
self.toolchain = toolchain
@property
def family(self):
if self.device.startswith("iCE40"):
return "iCE40"
if self.device.startswith("iCE5"):
return "iCE5"
assert False
@property
def _toolchain_env_var(self):
if self.toolchain == "IceStorm":
return f"NMIGEN_ENV_{self.toolchain}"
if self.toolchain in ("LSE-iCECube2", "Synplify-iCECube2"):
return f"NMIGEN_ENV_iCECube2"
assert False
@property
def required_tools(self):
if self.toolchain == "IceStorm":
return self._icestorm_required_tools
if self.toolchain in ("LSE-iCECube2", "Synplify-iCECube2"):
return self._icecube2_required_tools
assert False
@property
def file_templates(self):
if self.toolchain == "IceStorm":
return self._icestorm_file_templates
if self.toolchain in ("LSE-iCECube2", "Synplify-iCECube2"):
return self._icecube2_file_templates
assert False
@property
def command_templates(self):
if self.toolchain == "IceStorm":
return self._icestorm_command_templates
if self.toolchain == "LSE-iCECube2":
return self._lse_icecube2_command_templates
if self.toolchain == "Synplify-iCECube2":
return self._synplify_icecube2_command_templates
assert False
def create_missing_domain(self, name):
# For unknown reasons (no errata was ever published, and no documentation mentions this
# issue), iCE40 BRAMs read as zeroes for ~3 us after configuration and release of internal
@ -287,7 +478,7 @@ class LatticeICE40Platform(TemplatedPlatform):
o_type = 0b0100 # PIN_OUTPUT_DDR
elif pin.xdr == 2:
o_type = 0b1100 # PIN_OUTPUT_DDR_ENABLE_REGISTERED
io_args.append(("p", "PIN_TYPE", (o_type << 2) | i_type))
io_args.append(("p", "PIN_TYPE", C((o_type << 2) | i_type, 6)))
if hasattr(pin, "i_clk"):
io_args.append(("i", "INPUT_CLK", pin.i_clk))