
Verilog has an edge case where an `always @*` process, which is used to describe a combinatorial function procedurally, may not execute at time zero because none of the signals in its implicit sensitivity list change, i.e. when the process doesn't read any signals. This causes the wires driven by the process to stay undefined. The workaround to this problem (assuming SystemVerilog `always_comb` is not available) is to introduce a dummy signal that changes only at time zero and is optimized out during synthesis. nMigen has had its own workaround, `$verilog_initial_trigger`, for a while. However, `proc_prune`, while increasing readability, pulls references to this signal out of the process. Because of this, a similar workaround was implemented in Yosys' `write_verilog` itself. This commit ensures we use our workaround on versions of Yosys without the updated `write_verilog`, and Yosys' workaround on later versions. Fixes #418.
54 lines
2.2 KiB
Python
54 lines
2.2 KiB
Python
from .._toolchain.yosys import *
|
|
from . import rtlil
|
|
|
|
|
|
__all__ = ["YosysError", "convert", "convert_fragment"]
|
|
|
|
|
|
def _convert_rtlil_text(rtlil_text, *, strip_internal_attrs=False, write_verilog_opts=()):
|
|
# this version requirement needs to be synchronized with the one in setup.py!
|
|
yosys = find_yosys(lambda ver: ver >= (0, 9))
|
|
yosys_version = yosys.version()
|
|
|
|
script = []
|
|
script.append("read_ilang <<rtlil\n{}\nrtlil".format(rtlil_text))
|
|
|
|
if yosys_version >= (0, 9, 3468):
|
|
# Yosys >=0.9+3468 (since commit f3d7e9a1) emits Verilog without a possible sim/synth
|
|
# mismatch, making $verilog_initial_trigger unnecessary.
|
|
script.append("delete w:$verilog_initial_trigger")
|
|
script.append("proc_prune")
|
|
script.append("proc_init")
|
|
script.append("proc_arst")
|
|
script.append("proc_dff")
|
|
script.append("proc_clean")
|
|
script.append("memory_collect")
|
|
|
|
if strip_internal_attrs:
|
|
attr_map = []
|
|
attr_map.append("-remove generator")
|
|
attr_map.append("-remove top")
|
|
attr_map.append("-remove src")
|
|
attr_map.append("-remove nmigen.hierarchy")
|
|
attr_map.append("-remove nmigen.decoding")
|
|
script.append("attrmap {}".format(" ".join(attr_map)))
|
|
script.append("attrmap -modattr {}".format(" ".join(attr_map)))
|
|
|
|
script.append("write_verilog -norename {}".format(" ".join(write_verilog_opts)))
|
|
|
|
return yosys.run(["-q", "-"], "\n".join(script),
|
|
# At the moment, Yosys always shows a warning indicating that not all processes can be
|
|
# translated to Verilog. We carefully emit only the processes that *can* be translated, and
|
|
# squash this warning. Once Yosys' write_verilog pass is fixed, we should remove this.
|
|
ignore_warnings=True)
|
|
|
|
|
|
def convert_fragment(*args, strip_internal_attrs=False, **kwargs):
|
|
rtlil_text, name_map = rtlil.convert_fragment(*args, **kwargs)
|
|
return _convert_rtlil_text(rtlil_text, strip_internal_attrs=strip_internal_attrs), name_map
|
|
|
|
|
|
def convert(*args, strip_internal_attrs=False, **kwargs):
|
|
rtlil_text = rtlil.convert(*args, **kwargs)
|
|
return _convert_rtlil_text(rtlil_text, strip_internal_attrs=strip_internal_attrs)
|