
This function was added in commit 20553b14
in the wrong place, with
the wrong name, and without tests. Fix all that.
445 lines
16 KiB
Python
445 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")}}
|
|
""",
|
|
"{{name}}.v": r"""
|
|
/* {{autogenerated}} */
|
|
{{emit_verilog()}}
|
|
""",
|
|
"{{name}}.debug.v": r"""
|
|
/* {{autogenerated}} */
|
|
{{emit_debug_verilog()}}
|
|
""",
|
|
"{{name}}.qsf": r"""
|
|
# {{autogenerated}}
|
|
{% if get_override("nproc") -%}
|
|
set_global_assignment -name NUM_PARALLEL_PROCESSORS {{get_override("nproc")}}
|
|
{% endif %}
|
|
|
|
{% for file in platform.iter_files(".v") -%}
|
|
set_global_assignment -name VERILOG_FILE {{file|tcl_quote}}
|
|
{% endfor %}
|
|
{% for file in platform.iter_files(".sv") -%}
|
|
set_global_assignment -name SYSTEMVERILOG_FILE {{file|tcl_quote}}
|
|
{% endfor %}
|
|
{% for file in platform.iter_files(".vhd", ".vhdl") -%}
|
|
set_global_assignment -name VHDL_FILE {{file|tcl_quote}}
|
|
{% 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_quote}} PIN_{{pin_name}}
|
|
{% for key, value in attrs.items() -%}
|
|
set_instance_assignment -to {{port_name|tcl_quote}} -name {{key}} {{value|tcl_quote}}
|
|
{% 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_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 %}
|
|
""",
|
|
"{{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"] = "true"
|
|
|
|
@property
|
|
def default_clk_constraint(self):
|
|
# Internal high-speed oscillator on Cyclone V devices.
|
|
# It is specified to not be faster than 100MHz, but the actual
|
|
# frequency seems to vary a lot between devices. Measurements
|
|
# of 78 to 84 MHz have been observed.
|
|
if self.default_clk == "cyclonev_oscillator":
|
|
assert self.device.startswith("5C")
|
|
return Clock(100e6)
|
|
# Otherwise, use the defined Clock resource.
|
|
return super().default_clk_constraint
|
|
|
|
def create_missing_domain(self, name):
|
|
if name == "sync" and self.default_clk == "cyclonev_oscillator":
|
|
# Use the internal high-speed oscillator for Cyclone V devices
|
|
assert self.device.startswith("5C")
|
|
m = Module()
|
|
m.domains += ClockDomain("sync")
|
|
m.submodules += Instance("cyclonev_oscillator",
|
|
i_oscena=Const(1),
|
|
o_clkout=ClockSignal("sync"))
|
|
return m
|
|
else:
|
|
return super().create_missing_domain(name)
|
|
|
|
# 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.io,
|
|
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.io,
|
|
)
|
|
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.io,
|
|
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.io,
|
|
o_dataout=self._get_ireg(m, pin, invert),
|
|
i_oe=self._get_oereg(m, pin),
|
|
)
|
|
return m
|
|
|
|
def get_diff_input(self, pin, port, attrs, invert):
|
|
self._check_feature("differential input", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
if pin.xdr == 1:
|
|
port.p.attrs["useioff"] = 1
|
|
port.n.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=port.p,
|
|
i_datain_b=port.n,
|
|
o_dataout=self._get_ireg(m, pin, invert)
|
|
)
|
|
return m
|
|
|
|
def get_diff_output(self, pin, port, attrs, invert):
|
|
self._check_feature("differential output", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
if pin.xdr == 1:
|
|
port.p.attrs["useioff"] = 1
|
|
port.n.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=port.p,
|
|
o_dataout_b=port.n,
|
|
)
|
|
return m
|
|
|
|
def get_diff_tristate(self, pin, port, attrs, invert):
|
|
self._check_feature("differential tristate", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
if pin.xdr == 1:
|
|
port.p.attrs["useioff"] = 1
|
|
port.n.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=port.p,
|
|
o_dataout_b=port.n,
|
|
i_oe=self._get_oereg(m, pin),
|
|
)
|
|
return m
|
|
|
|
def get_diff_input_output(self, pin, port, attrs, invert):
|
|
self._check_feature("differential input/output", pin, attrs,
|
|
valid_xdrs=(0, 1, 2), valid_attrs=True)
|
|
if pin.xdr == 1:
|
|
port.p.attrs["useioff"] = 1
|
|
port.n.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=port.p,
|
|
io_dataio_b=port.n,
|
|
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._o_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._o_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
|