vendor.lattice_ice40: add iCECube support.
This also makes some iCE40 and ECP5 overrides more consistent.
This commit is contained in:
parent
8050cfaa98
commit
3d62dac1cb
19
nmigen/vendor/lattice_ecp5.py
vendored
19
nmigen/vendor/lattice_ecp5.py
vendored
|
@ -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
|
||||
|
|
221
nmigen/vendor/lattice_ice40.py
vendored
221
nmigen/vendor/lattice_ice40.py
vendored
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue