vendor._lattice: merge ECP5 and MachXO[23] into one platform.

This commit is contained in:
Wanda 2024-04-18 16:44:41 +02:00 committed by Catherine
parent a7a7d32099
commit 09045a2724
9 changed files with 164 additions and 328 deletions

View file

@ -11,6 +11,7 @@ __all__ = [
"LatticeICE40Platform",
"LatticeMachXO2Platform",
"LatticeMachXO3LPlatform",
"LatticePlatform",
"QuicklogicPlatform",
"SiliconBluePlatform",
"XilinxPlatform",
@ -28,12 +29,10 @@ def __getattr__(name):
if name == "GowinPlatform":
from ._gowin import GowinPlatform
return GowinPlatform
if name == "LatticeECP5Platform":
from ._lattice_ecp5 import LatticeECP5Platform
return LatticeECP5Platform
if name in ("LatticeMachXO2Platform", "LatticeMachXO3LPlatform"):
from ._lattice_machxo_2_3l import LatticeMachXO2Or3LPlatform
return LatticeMachXO2Or3LPlatform
if name in ("LatticePlatform", "LatticeECP5Platform", "LatticeMachXO2Platform",
"LatticeMachXO3LPlatform"):
from ._lattice import LatticePlatform
return LatticePlatform
if name == "QuicklogicPlatform":
from ._quicklogic import QuicklogicPlatform
return QuicklogicPlatform

View file

@ -123,7 +123,7 @@ class FFBuffer(io.FFBuffer):
return m
class DDRBuffer(io.DDRBuffer):
class DDRBufferECP5(io.DDRBuffer):
def elaborate(self, platform):
m = Module()
@ -164,9 +164,50 @@ class DDRBuffer(io.DDRBuffer):
return m
class LatticeECP5Platform(TemplatedPlatform):
class DDRBufferMachXO2(io.DDRBuffer):
def elaborate(self, platform):
m = Module()
m.submodules.buf = buf = InnerBuffer(self.direction, self.port)
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
if self.direction is not io.Direction.Output:
i0_inv = Signal(len(self.port))
i1_inv = Signal(len(self.port))
for bit in range(len(self.port)):
m.submodules[f"i_ddr{bit}"] = Instance("IDDRXE",
i_SCLK=ClockSignal(self.i_domain),
i_RST=Const(0),
i_D=buf.i[bit],
o_Q0=i0_inv[bit],
o_Q1=i1_inv[bit],
)
m.d.comb += self.i[0].eq(i0_inv ^ inv_mask)
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
if self.direction is not io.Direction.Input:
o0_inv = Signal(len(self.port))
o1_inv = Signal(len(self.port))
m.d.comb += [
o0_inv.eq(self.o[0] ^ inv_mask),
o1_inv.eq(self.o[1] ^ inv_mask),
]
for bit in range(len(self.port)):
m.submodules[f"o_ddr{bit}"] = Instance("ODDRXE",
i_SCLK=ClockSignal(self.o_domain),
i_RST=Const(0),
i_D0=o0_inv[bit],
i_D1=o1_inv[bit],
o_Q=buf.o[bit],
)
_make_oereg(m, self.o_domain, ~self.oe, buf.t)
return m
class LatticePlatform(TemplatedPlatform):
"""
.. rubric:: Trellis toolchain
.. rubric:: Trellis toolchain (ECP5 only)
Required tools:
* ``yosys``
@ -195,7 +236,7 @@ class LatticeECP5Platform(TemplatedPlatform):
* ``{{name}}.bit``: binary bitstream.
* ``{{name}}.svf``: JTAG programming vector.
.. rubric:: Diamond toolchain
.. rubric:: Diamond toolchain (ECP5, MachXO2, MachXO3)
Required tools:
* ``pnmainc``
@ -217,8 +258,11 @@ class LatticeECP5Platform(TemplatedPlatform):
Build products:
* ``{{name}}_impl/{{name}}_impl.htm``: consolidated log.
* ``{{name}}.jed``: JEDEC fuse file (MachXO2, MachXO3 only).
* ``{{name}}.bit``: binary bitstream.
* ``{{name}}.svf``: JTAG programming vector.
* ``{{name}}.svf``: JTAG programming vector (ECP5 only).
* ``{{name}}_flash.svf``: JTAG programming vector for FLASH programming (MachXO2, MachXO3 only).
* ``{{name}}_sram.svf``: JTAG programming vector for SRAM programming (MachXO2, MachXO3 only).
"""
toolchain = None # selected when creating platform
@ -378,6 +422,9 @@ class LatticeECP5Platform(TemplatedPlatform):
prj_run Map -impl impl
prj_run PAR -impl impl
prj_run Export -impl impl -task Bitgen
{% if family == "machxo2" -%}
prj_run Export -impl impl -task Jedecgen
{% endif %}
{{get_override("script_after_export")|default("# (script_after_export placeholder)")}}
""",
"{{name}}.lpf": r"""
@ -405,7 +452,7 @@ class LatticeECP5Platform(TemplatedPlatform):
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
}
_diamond_command_templates = [
_diamond_command_templates_ecp5 = [
# These don't have any usable command-line option overrides.
r"""
{{invoke_tool("pnmainc")}}
@ -422,12 +469,54 @@ class LatticeECP5Platform(TemplatedPlatform):
-if {{name}}_impl/{{name}}_impl.bit -of {{name}}.svf
""",
]
_diamond_command_templates_machxo2 = [
# These don't have any usable command-line option overrides.
r"""
{{invoke_tool("pnmainc")}}
{{name}}.tcl
""",
r"""
{{invoke_tool("ddtcmd")}}
-oft -bit
-if {{name}}_impl/{{name}}_impl.bit -of {{name}}.bit
""",
r"""
{{invoke_tool("ddtcmd")}}
-oft -jed
-dev {{platform.device}}-{{platform.speed}}{{platform.package}}{{platform.grade}}
-if {{name}}_impl/{{name}}_impl.jed -of {{name}}.jed
""",
r"""
{{invoke_tool("ddtcmd")}}
-oft -svfsingle -revd -op "FLASH Erase,Program,Verify"
-if {{name}}_impl/{{name}}_impl.jed -of {{name}}_flash.svf
""",
r"""
{{invoke_tool("ddtcmd")}}
-oft -svfsingle -revd -op "SRAM Fast Program"
-if {{name}}_impl/{{name}}_impl.bit -of {{name}}_sram.svf
""",
]
# Common logic
def __init__(self, *, toolchain="Trellis"):
def __init__(self, *, toolchain=None):
super().__init__()
device = self.device.lower()
if device.startswith(("lfe5", "lae5")):
self.family = "ecp5"
elif device.startswith(("lcmxo2-", "lcmxo3l", "lcmxo3d", "lamxo2-", "lamxo3l", "lamxo3d", "lfmnx-")):
self.family = "machxo2"
else:
raise ValueError(f"Device '{self.device}' is not recognized")
if toolchain is None:
if self.family == "ecp5":
toolchain = "Trellis"
else:
toolchain = "Diamond"
assert toolchain in ("Trellis", "Diamond")
self.toolchain = toolchain
@ -452,23 +541,46 @@ class LatticeECP5Platform(TemplatedPlatform):
if self.toolchain == "Trellis":
return self._trellis_command_templates
if self.toolchain == "Diamond":
return self._diamond_command_templates
if self.family == "ecp5":
return self._diamond_command_templates_ecp5
if self.family == "machxo2":
return self._diamond_command_templates_machxo2
assert False
# These numbers were extracted from
# "MachXO2 sysCLOCK PLL Design and Usage Guide"
_supported_osch_freqs = [
2.08, 2.15, 2.22, 2.29, 2.38, 2.46, 2.56, 2.66, 2.77, 2.89,
3.02, 3.17, 3.33, 3.50, 3.69, 3.91, 4.16, 4.29, 4.43, 4.59,
4.75, 4.93, 5.12, 5.32, 5.54, 5.78, 6.05, 6.33, 6.65, 7.00,
7.39, 7.82, 8.31, 8.58, 8.87, 9.17, 9.50, 9.85, 10.23, 10.64,
11.08, 11.57, 12.09, 12.67, 13.30, 14.00, 14.78, 15.65, 15.65, 16.63,
17.73, 19.00, 20.46, 22.17, 24.18, 26.60, 29.56, 33.25, 38.00, 44.33,
53.20, 66.50, 88.67, 133.00
]
@property
def default_clk_constraint(self):
if self.default_clk == "OSCG":
# Internal high-speed oscillator on ECP5 devices.
return Clock(310e6 / self.oscg_div)
if self.default_clk == "OSCH":
# Internal high-speed oscillator on MachXO2/MachXO3L devices.
# It can have a range of frequencies.
assert self.osch_frequency in self._supported_osch_freqs
return Clock(int(self.osch_frequency * 1e6))
# Otherwise, use the defined Clock resource.
return super().default_clk_constraint
def create_missing_domain(self, name):
# Lattice ECP5 devices have two global set/reset signals: PUR, which is driven at startup
# Lattice devices have two global set/reset signals: PUR, which is driven at startup
# by the configuration logic and unconditionally resets every storage element, and GSR,
# which is driven by user logic and each storage element may be configured as affected or
# unaffected by GSR. PUR is purely asynchronous, so even though it is a low-skew global
# network, its deassertion may violate a setup/hold constraint with relation to a user
# clock. To avoid this, a GSR/SGSR instance should be driven synchronized to user clock.
if name == "sync" and self.default_clk is not None:
using_osch = False
m = Module()
if self.default_clk == "OSCG":
if not hasattr(self, "oscg_div"):
@ -480,6 +592,14 @@ class LatticeECP5Platform(TemplatedPlatform):
.format(self.oscg_div))
clk_i = Signal()
m.submodules += Instance("OSCG", p_DIV=self.oscg_div, o_OSC=clk_i)
elif self.default_clk == "OSCH":
osch_freq = self.osch_frequency
if osch_freq not in self._supported_osch_freqs:
raise ValueError("Frequency {!r} is not valid for OSCH clock. Valid frequencies are {!r}"
.format(osch_freq, self._supported_osch_freqs))
osch_freq_param = f"{float(osch_freq):.2f}"
clk_i = Signal()
m.submodules += [ Instance("OSCH", p_NOM_FREQ=osch_freq_param, i_STDBY=Const(0), o_OSC=clk_i, o_SEDSTDBY=Signal()) ]
else:
clk_i = self.request(self.default_clk).i
if self.default_rst is not None:
@ -489,7 +609,7 @@ class LatticeECP5Platform(TemplatedPlatform):
gsr0 = Signal()
gsr1 = Signal()
# There is no end-of-startup signal on ECP5, but PUR is released after IOB enable, so
# There is no end-of-startup signal on Lattice, but PUR is released after IOB enable, so
# a simple reset synchronizer (with PUR as the asynchronous reset) does the job.
m.submodules += [
Instance("FD1S3AX", p_GSR="DISABLED", i_CK=clk_i, i_D=~rst_i, o_Q=gsr0),
@ -512,7 +632,12 @@ class LatticeECP5Platform(TemplatedPlatform):
elif isinstance(buffer, io.FFBuffer):
result = FFBuffer(buffer.direction, buffer.port)
elif isinstance(buffer, io.DDRBuffer):
result = DDRBuffer(buffer.direction, buffer.port)
if self.family == "ecp5":
result = DDRBufferECP5(buffer.direction, buffer.port)
elif self.family == "machxo2":
result = DDRBufferMachXO2(buffer.direction, buffer.port)
else:
raise NotImplementedError # :nocov:
else:
raise TypeError(f"Unsupported buffer type {buffer!r}") # :nocov:
if buffer.direction is not io.Direction.Output:
@ -522,5 +647,5 @@ class LatticeECP5Platform(TemplatedPlatform):
result.oe = buffer.oe
return result
# CDC primitives are not currently specialized for ECP5.
# CDC primitives are not currently specialized for Lattice.
# While Diamond supports false path constraints; nextpnr-ecp5 does not.

View file

@ -1,275 +0,0 @@
from abc import abstractmethod
from ..hdl import *
from ..lib import io, wiring
from ..build import *
# MachXO[23] I/O buffers are identical to ECP5 except for DDR.
from ._lattice_ecp5 import InnerBuffer, IOBuffer, FFBuffer, _make_oereg
class DDRBuffer(io.DDRBuffer):
def elaborate(self, platform):
m = Module()
m.submodules.buf = buf = InnerBuffer(self.direction, self.port)
inv_mask = sum(inv << bit for bit, inv in enumerate(self.port.invert))
if self.direction is not io.Direction.Output:
i0_inv = Signal(len(self.port))
i1_inv = Signal(len(self.port))
for bit in range(len(self.port)):
m.submodules[f"i_ddr{bit}"] = Instance("IDDRXE",
i_SCLK=ClockSignal(self.i_domain),
i_RST=Const(0),
i_D=buf.i[bit],
o_Q0=i0_inv[bit],
o_Q1=i1_inv[bit],
)
m.d.comb += self.i[0].eq(i0_inv ^ inv_mask)
m.d.comb += self.i[1].eq(i1_inv ^ inv_mask)
if self.direction is not io.Direction.Input:
o0_inv = Signal(len(self.port))
o1_inv = Signal(len(self.port))
m.d.comb += [
o0_inv.eq(self.o[0] ^ inv_mask),
o1_inv.eq(self.o[1] ^ inv_mask),
]
for bit in range(len(self.port)):
m.submodules[f"o_ddr{bit}"] = Instance("ODDRXE",
i_SCLK=ClockSignal(self.o_domain),
i_RST=Const(0),
i_D0=o0_inv[bit],
i_D1=o1_inv[bit],
o_Q=buf.o[bit],
)
_make_oereg(m, self.o_domain, ~self.oe, buf.t)
return m
# MachXO2 and MachXO3L primitives are the same. Handle both using
# one class and expose user-aliases for convenience.
class LatticeMachXO2Or3LPlatform(TemplatedPlatform):
"""
Required tools:
* ``pnmainc``
* ``ddtcmd``
The environment is populated by running the script specified in the environment variable
``AMARANTH_ENV_DIAMOND``, if present. On Linux, diamond_env as provided by Diamond
itself is a good candidate. On Windows, the following script (named ``diamond_env.bat``,
for instance) is known to work::
@echo off
set PATH=C:\\lscc\\diamond\\%DIAMOND_VERSION%\\bin\\nt64;%PATH%
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 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.
* ``{{name}}.jed``: JEDEC fuse file.
* ``{{name}}.bit``: binary bitstream.
* ``{{name}}.svf``: JTAG programming vector for FLASH programming.
* ``{{name}}_flash.svf``: JTAG programming vector for FLASH programming.
* ``{{name}}_sram.svf``: JTAG programming vector for SRAM programming.
"""
toolchain = "Diamond"
device = property(abstractmethod(lambda: None))
package = property(abstractmethod(lambda: None))
speed = property(abstractmethod(lambda: None))
grade = "C" # [C]ommercial, [I]ndustrial
required_tools = [
"pnmainc",
"ddtcmd"
]
file_templates = {
**TemplatedPlatform.build_script_templates,
"build_{{name}}.sh": r"""
# {{autogenerated}}
set -e{{verbose("x")}}
if [ -z "$BASH" ] ; then exec /bin/bash "$0" "$@"; fi
if [ -n "${{platform._toolchain_env_var}}" ]; then
bindir=$(dirname "${{platform._toolchain_env_var}}")
. "${{platform._toolchain_env_var}}"
fi
{{emit_commands("sh")}}
""",
"{{name}}.v": r"""
/* {{autogenerated}} */
{{emit_verilog()}}
""",
"{{name}}.debug.v": r"""
/* {{autogenerated}} */
{{emit_debug_verilog()}}
""",
"{{name}}.tcl": r"""
prj_project new -name {{name}} -impl impl -impl_dir {{name}}_impl \
-dev {{platform.device}}-{{platform.speed}}{{platform.package}}{{platform.grade}} \
-lpf {{name}}.lpf \
-synthesis synplify
{% for file in platform.iter_files(".v", ".sv", ".vhd", ".vhdl") -%}
prj_src add {{file|tcl_quote}}
{% endfor %}
prj_src add {{name}}.v
prj_impl option top {{name}}
prj_src add {{name}}.sdc
{{get_override("script_project")|default("# (script_project placeholder)")}}
prj_project save
prj_run Synthesis -impl impl
prj_run Translate -impl impl
prj_run Map -impl impl
prj_run PAR -impl impl
prj_run Export -impl impl -task Bitgen
prj_run Export -impl impl -task Jedecgen
{{get_override("script_after_export")|default("# (script_after_export placeholder)")}}
""",
"{{name}}.lpf": r"""
# {{autogenerated}}
BLOCK ASYNCPATHS;
BLOCK RESETPATHS;
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
LOCATE COMP "{{port_name}}" SITE "{{pin_name}}";
{% if attrs -%}
IOBUF PORT "{{port_name}}"
{%- for key, value in attrs.items() %} {{key}}={{value}}{% endfor %};
{% endif %}
{% endfor %}
{{get_override("add_preferences")|default("# (add_preferences placeholder)")}}
""",
"{{name}}.sdc": r"""
set_hierarchy_separator {/}
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -name {{port_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_quote}}]
{% else -%}
create_clock -name {{net_signal.name|tcl_quote}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("/")|tcl_quote}}]
{% endif %}
{% endfor %}
{{get_override("add_constraints")|default("# (add_constraints placeholder)")}}
""",
}
command_templates = [
# These don't have any usable command-line option overrides.
r"""
{{invoke_tool("pnmainc")}}
{{name}}.tcl
""",
r"""
{{invoke_tool("ddtcmd")}}
-oft -bit
-if {{name}}_impl/{{name}}_impl.bit -of {{name}}.bit
""",
r"""
{{invoke_tool("ddtcmd")}}
-oft -jed
-dev {{platform.device}}-{{platform.speed}}{{platform.package}}{{platform.grade}}
-if {{name}}_impl/{{name}}_impl.jed -of {{name}}.jed
""",
r"""
{{invoke_tool("ddtcmd")}}
-oft -svfsingle -revd -op "FLASH Erase,Program,Verify"
-if {{name}}_impl/{{name}}_impl.jed -of {{name}}_flash.svf
""",
r"""
{{invoke_tool("ddtcmd")}}
-oft -svfsingle -revd -op "SRAM Fast Program"
-if {{name}}_impl/{{name}}_impl.bit -of {{name}}_sram.svf
""",
]
# These numbers were extracted from
# "MachXO2 sysCLOCK PLL Design and Usage Guide"
_supported_osch_freqs = [
2.08, 2.15, 2.22, 2.29, 2.38, 2.46, 2.56, 2.66, 2.77, 2.89,
3.02, 3.17, 3.33, 3.50, 3.69, 3.91, 4.16, 4.29, 4.43, 4.59,
4.75, 4.93, 5.12, 5.32, 5.54, 5.78, 6.05, 6.33, 6.65, 7.00,
7.39, 7.82, 8.31, 8.58, 8.87, 9.17, 9.50, 9.85, 10.23, 10.64,
11.08, 11.57, 12.09, 12.67, 13.30, 14.00, 14.78, 15.65, 15.65, 16.63,
17.73, 19.00, 20.46, 22.17, 24.18, 26.60, 29.56, 33.25, 38.00, 44.33,
53.20, 66.50, 88.67, 133.00
]
@property
def default_clk_constraint(self):
# Internal high-speed oscillator on MachXO2/MachXO3L devices.
# It can have a range of frequencies.
if self.default_clk == "OSCH":
assert self.osch_frequency in self._supported_osch_freqs
return Clock(int(self.osch_frequency * 1e6))
# Otherwise, use the defined Clock resource.
return super().default_clk_constraint
def create_missing_domain(self, name):
# Lattice MachXO2/MachXO3L devices have two global set/reset signals: PUR, which is driven at
# startup by the configuration logic and unconditionally resets every storage element,
# and GSR, which is driven by user logic and each storage element may be configured as
# affected or unaffected by GSR. PUR is purely asynchronous, so even though it is
# a low-skew global network, its deassertion may violate a setup/hold constraint with
# relation to a user clock. To avoid this, a GSR/SGSR instance should be driven
# synchronized to user clock.
if name == "sync" and self.default_clk is not None:
using_osch = False
if self.default_clk == "OSCH":
using_osch = True
clk_i = Signal()
else:
clk_i = self.request(self.default_clk).i
if self.default_rst is not None:
rst_i = self.request(self.default_rst).i
else:
rst_i = Const(0)
gsr0 = Signal()
gsr1 = Signal()
m = Module()
# There is no end-of-startup signal on MachXO2/MachXO3L, but PUR is released after IOB
# enable, so a simple reset synchronizer (with PUR as the asynchronous reset) does the job.
m.submodules += [
Instance("FD1S3AX", p_GSR="DISABLED", i_CK=clk_i, i_D=~rst_i, o_Q=gsr0),
Instance("FD1S3AX", p_GSR="DISABLED", i_CK=clk_i, i_D=gsr0, o_Q=gsr1),
# Although we already synchronize the reset input to user clock, SGSR has dedicated
# clock routing to the center of the FPGA; use that just in case it turns out to be
# more reliable. (None of this is documented.)
Instance("SGSR", i_CLK=clk_i, i_GSR=gsr1),
]
if using_osch:
osch_freq = self.osch_frequency
if osch_freq not in self._supported_osch_freqs:
raise ValueError("Frequency {!r} is not valid for OSCH clock. Valid frequencies are {!r}"
.format(osch_freq, self._supported_osch_freqs))
osch_freq_param = f"{float(osch_freq):.2f}"
m.submodules += [ Instance("OSCH", p_NOM_FREQ=osch_freq_param, i_STDBY=Const(0), o_OSC=clk_i, o_SEDSTDBY=Signal()) ]
# GSR implicitly connects to every appropriate storage element. As such, the sync
# domain is reset-less; domains driven by other clocks would need to have dedicated
# reset circuitry or otherwise meet setup/hold constraints on their own.
m.domains += ClockDomain("sync", reset_less=True)
m.d.comb += ClockSignal("sync").eq(clk_i)
return m
def get_io_buffer(self, buffer):
if isinstance(buffer, io.Buffer):
result = IOBuffer(buffer.direction, buffer.port)
elif isinstance(buffer, io.FFBuffer):
result = FFBuffer(buffer.direction, buffer.port)
elif isinstance(buffer, io.DDRBuffer):
result = DDRBuffer(buffer.direction, buffer.port)
else:
raise TypeError(f"Unsupported buffer type {buffer!r}") # :nocov:
if buffer.direction is not io.Direction.Output:
result.i = buffer.i
if buffer.direction is not io.Direction.Input:
result.o = buffer.o
result.oe = buffer.oe
return result
# CDC primitives are not currently specialized for MachXO2/MachXO3L.

View file

@ -12,8 +12,7 @@ Platform integration
platform/altera
platform/gowin
platform/lattice-ecp5
platform/lattice-ice40
platform/lattice-machxo-2-3l
platform/lattice
platform/quicklogic
platform/siliconblue
platform/xilinx

View file

@ -1,10 +0,0 @@
Lattice ECP5
############
.. currentmodule:: amaranth.vendor
The :class:`LatticeECP5Platform` class provides a base platform to support Lattice ECP5 devices.
The Trellis and Diamond toolchains are supported.
.. autoclass:: LatticeECP5Platform

View file

@ -1,10 +0,0 @@
Lattice iCE40
#############
.. currentmodule:: amaranth.vendor
The :class:`LatticeICE40Platform` class provides a base platform to support Lattice iCE40 devices.
The IceStorm and iCECube2 toolchains are supported.
.. autoclass:: LatticeICE40Platform

View file

@ -1,12 +0,0 @@
Lattice MachXO2 and MachXO3L
############################
.. currentmodule:: amaranth.vendor
The :class:`LatticeMachXO2Platform` and :class:`LatticeMachXO3LPlatform` classes provide base platforms to support Lattice MachXO2 and MachXO3L devices.
The Diamond toolchain is supported.
.. autoclass:: amaranth.vendor._lattice_machxo_2_3l.LatticeMachXO2Or3LPlatform
.. autoclass:: LatticeMachXO2Platform
.. autoclass:: LatticeMachXO3LPlatform

10
docs/platform/lattice.rst Normal file
View file

@ -0,0 +1,10 @@
Lattice
#######
.. currentmodule:: amaranth.vendor
The :class:`LatticePlatform` class provides a base platform to support Lattice toolchains (not including iCE40 devices, which are supported by :class:`SiliconBluePlatform`). Currently supported devices include ECP5, MachXO2, and MachXO3L.
The Trellis and Diamond toolchains are supported.
.. autoclass:: LatticePlatform

View file

@ -0,0 +1,10 @@
SiliconBlue
###########
.. currentmodule:: amaranth.vendor
The :class:`SiliconBluePlatform` class provides a base platform to support Lattice (earlier SiliconBlue) iCE40 devices.
The IceStorm and iCECube2 toolchains are supported.
.. autoclass:: SiliconBluePlatform