amaranth/nmigen/vendor/intel.py
whitequark 892cff059b vendor.xilinx_{7series,ultrascale}: add (*keep*) on constrained clocks.
If the clock signal is not a top-level port and has aliases, it can
be optimized out, and then the constraint will no longer apply.
To prevent this, make sure the constrained signal is preferred over
any aliases by using the `keep` attribute.

Vivado does not parse attributes like (* keep = 32'd1 *) as valid
even though, AFAICT, they are equivalent to (* keep = 1 *) or simply
(* keep *) per IEEE 1364. To work around this, use the solution we
currently use for Quartus, which is `write_verilog -decimal`.

Fixes #373.
2020-05-20 04:58:03 +00:00

422 lines
16 KiB
Python

from abc import abstractproperty
from ..hdl import *
from ..build import *
__all__ = ["IntelPlatform"]
class IntelPlatform(TemplatedPlatform):
"""
Required tools:
* ``quartus_map``
* ``quartus_fit``
* ``quartus_asm``
* ``quartus_sta``
The environment is populated by running the script specified in the environment variable
``NMIGEN_ENV_Quartus``, if present.
Available overrides:
* ``nproc``: sets the number of cores used by all tools.
* ``quartus_map_opts``: adds extra options for ``quartus_map``.
* ``quartus_fit_opts``: adds extra options for ``quartus_fit``.
* ``quartus_asm_opts``: adds extra options for ``quartus_asm``.
* ``quartus_sta_opts``: adds extra options for ``quartus_sta``.
Build products:
* ``*.rpt``: toolchain reports.
* ``{{name}}.sof``: bitstream as SRAM object file.
* ``{{name}}.rbf``: bitstream as raw binary file.
"""
toolchain = "Quartus"
device = abstractproperty()
package = abstractproperty()
speed = abstractproperty()
suffix = ""
quartus_suppressed_warnings = [
10264, # All case item expressions in this case statement are onehot
10270, # Incomplete Verilog case statement has no default case item
10335, # Unrecognized synthesis attribute
10763, # Verilog case statement has overlapping case item expressions with non-constant or don't care bits
10935, # Verilog casex/casez overlaps with a previous casex/vasez item expression
12125, # Using design file which is not specified as a design file for the current project, but contains definitions used in project
18236, # Number of processors not specified in QSF
292013, # Feature is only available with a valid subscription license
]
required_tools = [
"quartus_map",
"quartus_fit",
"quartus_asm",
"quartus_sta",
]
file_templates = {
**TemplatedPlatform.build_script_templates,
"build_{{name}}.sh": r"""
# {{autogenerated}}
if [ -n "${{platform._toolchain_env_var}}" ]; then
QUARTUS_ROOTDIR=$(dirname $(dirname "${{platform._toolchain_env_var}}"))
# Quartus' qenv.sh does not work with `set -e`.
. "${{platform._toolchain_env_var}}"
fi
set -e{{verbose("x")}}
{{emit_commands("sh")}}
""",
# Quartus doesn't like constructs like (* keep = 32'd1 *), even though they mean the same
# thing as (* keep = 1 *); use -decimal to work around that.
"{{name}}.v": r"""
/* {{autogenerated}} */
{{emit_verilog(["-decimal"])}}
""",
"{{name}}.debug.v": r"""
/* {{autogenerated}} */
{{emit_debug_verilog(["-decimal"])}}
""",
"{{name}}.qsf": r"""
# {{autogenerated}}
{% if get_override("nproc") -%}
set_global_assignment -name NUM_PARALLEL_PROCESSORS {{get_override("nproc")}}
{% endif %}
{% for file in platform.iter_extra_files(".v") -%}
set_global_assignment -name VERILOG_FILE {{file|tcl_escape}}
{% endfor %}
{% for file in platform.iter_extra_files(".sv") -%}
set_global_assignment -name SYSTEMVERILOG_FILE {{file|tcl_escape}}
{% endfor %}
{% for file in platform.iter_extra_files(".vhd", ".vhdl") -%}
set_global_assignment -name VHDL_FILE {{file|tcl_escape}}
{% endfor %}
set_global_assignment -name VERILOG_FILE {{name}}.v
set_global_assignment -name TOP_LEVEL_ENTITY {{name}}
set_global_assignment -name DEVICE {{platform.device}}{{platform.package}}{{platform.speed}}{{platform.suffix}}
{% for port_name, pin_name, attrs in platform.iter_port_constraints_bits() -%}
set_location_assignment -to {{port_name|tcl_escape}} PIN_{{pin_name}}
{% for key, value in attrs.items() -%}
set_instance_assignment -to {{port_name|tcl_escape}} -name {{key}} {{value|tcl_escape}}
{% endfor %}
{% endfor %}
set_global_assignment -name GENERATE_RBF_FILE ON
""",
"{{name}}.sdc": r"""
{% for net_signal, port_signal, frequency in platform.iter_clock_constraints() -%}
{% if port_signal is not none -%}
create_clock -name {{port_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_ports {{port_signal.name|tcl_escape}}]
{% else -%}
create_clock -name {{net_signal.name|tcl_escape}} -period {{1000000000/frequency}} [get_nets {{net_signal|hierarchy("|")|tcl_escape}}]
{% endif %}
{% endfor %}
""",
"{{name}}.srf": r"""
{% for warning in platform.quartus_suppressed_warnings %}
{ "" "" "" "{{name}}.v" { } { } 0 {{warning}} "" 0 0 "Design Software" 0 -1 0 ""}
{% endfor %}
""",
}
command_templates = [
r"""
{{invoke_tool("quartus_map")}}
{{get_override("quartus_map_opts")|options}}
--rev={{name}} {{name}}
""",
r"""
{{invoke_tool("quartus_fit")}}
{{get_override("quartus_fit_opts")|options}}
--rev={{name}} {{name}}
""",
r"""
{{invoke_tool("quartus_asm")}}
{{get_override("quartus_asm_opts")|options}}
--rev={{name}} {{name}}
""",
r"""
{{invoke_tool("quartus_sta")}}
{{get_override("quartus_sta_opts")|options}}
--rev={{name}} {{name}}
""",
]
def add_clock_constraint(self, clock, frequency):
super().add_clock_constraint(clock, frequency)
clock.attrs["keep"] = 1
# The altiobuf_* and altddio_* primitives are explained in the following Intel documents:
# * https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altiobuf.pdf
# * https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/ug/ug_altddio.pdf
# See also errata mentioned in: https://www.intel.com/content/www/us/en/programmable/support/support-resources/knowledge-base/solutions/rd11192012_735.html.
@staticmethod
def _get_ireg(m, pin, invert):
def get_ineg(i):
if invert:
i_neg = Signal.like(i, name_suffix="_neg")
m.d.comb += i.eq(~i_neg)
return i_neg
else:
return i
if pin.xdr == 0:
return get_ineg(pin.i)
elif pin.xdr == 1:
i_sdr = Signal(pin.width, name="{}_i_sdr")
m.submodules += Instance("$dff",
p_CLK_POLARITY=1,
p_WIDTH=pin.width,
i_CLK=pin.i_clk,
i_D=i_sdr,
o_Q=get_ineg(pin.i),
)
return i_sdr
elif pin.xdr == 2:
i_ddr = Signal(pin.width, name="{}_i_ddr".format(pin.name))
m.submodules["{}_i_ddr".format(pin.name)] = Instance("altddio_in",
p_width=pin.width,
i_datain=i_ddr,
i_inclock=pin.i_clk,
o_dataout_h=get_ineg(pin.i0),
o_dataout_l=get_ineg(pin.i1),
)
return i_ddr
assert False
@staticmethod
def _get_oreg(m, pin, invert):
def get_oneg(o):
if invert:
o_neg = Signal.like(o, name_suffix="_neg")
m.d.comb += o_neg.eq(~o)
return o_neg
else:
return o
if pin.xdr == 0:
return get_oneg(pin.o)
elif pin.xdr == 1:
o_sdr = Signal(pin.width, name="{}_o_sdr".format(pin.name))
m.submodules += Instance("$dff",
p_CLK_POLARITY=1,
p_WIDTH=pin.width,
i_CLK=pin.o_clk,
i_D=get_oneg(pin.o),
o_Q=o_sdr,
)
return o_sdr
elif pin.xdr == 2:
o_ddr = Signal(pin.width, name="{}_o_ddr".format(pin.name))
m.submodules["{}_o_ddr".format(pin.name)] = Instance("altddio_out",
p_width=pin.width,
o_dataout=o_ddr,
i_outclock=pin.o_clk,
i_datain_h=get_oneg(pin.o0),
i_datain_l=get_oneg(pin.o1),
)
return o_ddr
assert False
@staticmethod
def _get_oereg(m, pin):
# altiobuf_ requires an output enable signal for each pin, but pin.oe is 1 bit wide.
if pin.xdr == 0:
return Repl(pin.oe, pin.width)
elif pin.xdr in (1, 2):
oe_reg = Signal(pin.width, name="{}_oe_reg".format(pin.name))
oe_reg.attrs["useioff"] = "1"
m.submodules += Instance("$dff",
p_CLK_POLARITY=1,
p_WIDTH=pin.width,
i_CLK=pin.o_clk,
i_D=pin.oe,
o_Q=oe_reg,
)
return oe_reg
assert False
def get_input(self, pin, port, attrs, invert):
self._check_feature("single-ended input", pin, attrs,
valid_xdrs=(0, 1, 2), valid_attrs=True)
if pin.xdr == 1:
port.attrs["useioff"] = 1
m = Module()
m.submodules[pin.name] = Instance("altiobuf_in",
p_enable_bus_hold="FALSE",
p_number_of_channels=pin.width,
p_use_differential_mode="FALSE",
i_datain=port,
o_dataout=self._get_ireg(m, pin, invert)
)
return m
def get_output(self, pin, port, attrs, invert):
self._check_feature("single-ended output", pin, attrs,
valid_xdrs=(0, 1, 2), valid_attrs=True)
if pin.xdr == 1:
port.attrs["useioff"] = 1
m = Module()
m.submodules[pin.name] = Instance("altiobuf_out",
p_enable_bus_hold="FALSE",
p_number_of_channels=pin.width,
p_use_differential_mode="FALSE",
p_use_oe="FALSE",
i_datain=self._get_oreg(m, pin, invert),
o_dataout=port,
)
return m
def get_tristate(self, pin, port, attrs, invert):
self._check_feature("single-ended tristate", pin, attrs,
valid_xdrs=(0, 1, 2), valid_attrs=True)
if pin.xdr == 1:
port.attrs["useioff"] = 1
m = Module()
m.submodules[pin.name] = Instance("altiobuf_out",
p_enable_bus_hold="FALSE",
p_number_of_channels=pin.width,
p_use_differential_mode="FALSE",
p_use_oe="TRUE",
i_datain=self._get_oreg(m, pin, invert),
o_dataout=port,
i_oe=self._get_oereg(m, pin)
)
return m
def get_input_output(self, pin, port, attrs, invert):
self._check_feature("single-ended input/output", pin, attrs,
valid_xdrs=(0, 1, 2), valid_attrs=True)
if pin.xdr == 1:
port.attrs["useioff"] = 1
m = Module()
m.submodules[pin.name] = Instance("altiobuf_bidir",
p_enable_bus_hold="FALSE",
p_number_of_channels=pin.width,
p_use_differential_mode="FALSE",
i_datain=self._get_oreg(m, pin, invert),
io_dataio=port,
o_dataout=self._get_ireg(m, pin, invert),
i_oe=self._get_oereg(m, pin),
)
return m
def get_diff_input(self, pin, p_port, n_port, attrs, invert):
self._check_feature("differential input", pin, attrs,
valid_xdrs=(0, 1, 2), valid_attrs=True)
if pin.xdr == 1:
p_port.attrs["useioff"] = 1
n_port.attrs["useioff"] = 1
m = Module()
m.submodules[pin.name] = Instance("altiobuf_in",
p_enable_bus_hold="FALSE",
p_number_of_channels=pin.width,
p_use_differential_mode="TRUE",
i_datain=p_port,
i_datain_b=n_port,
o_dataout=self._get_ireg(m, pin, invert)
)
return m
def get_diff_output(self, pin, p_port, n_port, attrs, invert):
self._check_feature("differential output", pin, attrs,
valid_xdrs=(0, 1, 2), valid_attrs=True)
if pin.xdr == 1:
p_port.attrs["useioff"] = 1
n_port.attrs["useioff"] = 1
m = Module()
m.submodules[pin.name] = Instance("altiobuf_out",
p_enable_bus_hold="FALSE",
p_number_of_channels=pin.width,
p_use_differential_mode="TRUE",
p_use_oe="FALSE",
i_datain=self._get_oreg(m, pin, invert),
o_dataout=p_port,
o_dataout_b=n_port,
)
return m
def get_diff_tristate(self, pin, p_port, n_port, attrs, invert):
self._check_feature("differential tristate", pin, attrs,
valid_xdrs=(0, 1, 2), valid_attrs=True)
if pin.xdr == 1:
p_port.attrs["useioff"] = 1
n_port.attrs["useioff"] = 1
m = Module()
m.submodules[pin.name] = Instance("altiobuf_out",
p_enable_bus_hold="FALSE",
p_number_of_channels=pin.width,
p_use_differential_mode="TRUE",
p_use_oe="TRUE",
i_datain=self._get_oreg(m, pin, invert),
o_dataout=p_port,
o_dataout_b=n_port,
i_oe=self._get_oereg(m, pin),
)
return m
def get_diff_input_output(self, pin, p_port, n_port, attrs, invert):
self._check_feature("differential input/output", pin, attrs,
valid_xdrs=(0, 1, 2), valid_attrs=True)
if pin.xdr == 1:
p_port.attrs["useioff"] = 1
n_port.attrs["useioff"] = 1
m = Module()
m.submodules[pin.name] = Instance("altiobuf_bidir",
p_enable_bus_hold="FALSE",
p_number_of_channels=pin.width,
p_use_differential_mode="TRUE",
i_datain=self._get_oreg(m, pin, invert),
io_dataio=p_port,
io_dataio_b=n_port,
o_dataout=self._get_ireg(m, pin, invert),
i_oe=self._get_oereg(m, pin),
)
return m
# The altera_std_synchronizer{,_bundle} megafunctions embed SDC constraints that mark false
# paths, so use them instead of our default implementation.
def get_ff_sync(self, ff_sync):
return Instance("altera_std_synchronizer_bundle",
p_width=len(ff_sync.i),
p_depth=ff_sync._stages,
i_clk=ClockSignal(ff_sync._o_domain),
i_reset_n=Const(1),
i_din=ff_sync.i,
o_dout=ff_sync.o,
)
def get_async_ff_sync(self, async_ff_sync):
m = Module()
sync_output = Signal()
if async_ff_sync._edge == "pos":
m.submodules += Instance("altera_std_synchronizer",
p_depth=async_ff_sync._stages,
i_clk=ClockSignal(async_ff_sync._domain),
i_reset_n=~async_ff_sync.i,
i_din=Const(1),
o_dout=sync_output,
)
else:
m.submodules += Instance("altera_std_synchronizer",
p_depth=async_ff_sync._stages,
i_clk=ClockSignal(async_ff_sync._domain),
i_reset_n=async_ff_sync.i,
i_din=Const(1),
o_dout=sync_output,
)
m.d.comb += async_ff_sync.o.eq(~sync_output)
return m