From 4d96effec17cf4d09908716017f32a18fcdbb2a4 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sat, 21 Sep 2019 14:27:35 +0000 Subject: [PATCH] vendor.lattice_ice40: add iCECube support. This also makes some iCE40 and ECP5 overrides more consistent. --- nmigen/vendor/lattice_ecp5.py | 19 ++- nmigen/vendor/lattice_ice40.py | 221 ++++++++++++++++++++++++++++++--- 2 files changed, 221 insertions(+), 19 deletions(-) diff --git a/nmigen/vendor/lattice_ecp5.py b/nmigen/vendor/lattice_ecp5.py index f89ad63..fecd42e 100644 --- a/nmigen/vendor/lattice_ecp5.py +++ b/nmigen/vendor/lattice_ecp5.py @@ -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 diff --git a/nmigen/vendor/lattice_ice40.py b/nmigen/vendor/lattice_ice40.py index 26ec67b..cc6aa66 100644 --- a/nmigen/vendor/lattice_ice40.py +++ b/nmigen/vendor/lattice_ice40.py @@ -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)) -- 2.30.2