* ``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.
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.
"BG756": "caBGA756",
}
+ _trellis_required_tools = [
+ "yosys",
+ "nextpnr-ecp5",
+ "ecppack"
+ ]
_trellis_file_templates = {
**TemplatedPlatform.build_script_templates,
"{{name}}.il": r"""
{% 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 = [
# Diamond templates
+ _diamond_required_tools = [
+ "pnmainc",
+ "ddtcmd"
+ ]
_diamond_file_templates = {
**TemplatedPlatform.build_script_templates,
"build_{{name}}.sh": r"""
@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
class LatticeICE40Platform(TemplatedPlatform):
"""
+ IceStorm toolchain
+ ------------------
+
Required tools:
* ``yosys``
* ``nextpnr-ice40``
* ``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.
* ``{{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",
"iCE5LP1K": "",
}
- file_templates = {
+ _icestorm_required_tools = [
+ "yosys",
+ "nextpnr-ice40",
+ "icepack",
+ ]
+ _icestorm_file_templates = {
**TemplatedPlatform.build_script_templates,
"{{name}}.il": r"""
# {{autogenerated}}
{{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")}}
"""
]
+ # 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
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))