# Copyright edalize contributors
# Licensed under the 2-Clause BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-2-Clause
import logging
import re
from collections import OrderedDict
from edalize.edatool import Edatool
from edalize.utils import get_file_type
logger = logging.getLogger(__name__)
[docs]
class Spyglass(Edatool):
_description = """ Synopsys (formerly Atrenta) Spyglass Backend
Spyglass performs static source code analysis on HDL code and checks for common
coding errors or coding style violations.
Example snippet of a CAPI2 description file
::
spyglass:
methodology: "GuideWare/latest/block/rtl_handoff"
goals:
- lint/lint_rtl
spyglass_options:
# prevent error SYNTH_5273 on generic RAM descriptions
- handlememory yes
rule_parameters:
# Allow localparam to be used in case labels (e.g. in state machines)
- handle_static_caselabels yes
"""
tool_options = {
"members": {"methodology": "String"},
"lists": {
"goals": "String",
"spyglass_options": "String",
"rule_parameters": "String",
},
}
argtypes = ["vlogdefine", "vlogparam"]
tool_options_defaults = {
"methodology": "GuideWare/latest/block/rtl_handoff",
"goals": ["lint/lint_rtl"],
"spyglass_options": [],
"rule_parameters": [],
}
def _set_tool_options_defaults(self):
for key, default_value in self.tool_options_defaults.items():
if not key in self.tool_options:
logger.info(
"Set Spyglass tool option %s to default value %s"
% (key, str(default_value))
)
self.tool_options[key] = default_value
[docs]
def configure_main(self):
"""
Configuration is the first phase of the build.
This writes the project TCL files and Makefile. It first collects all
sources, IPs and constraints and then writes them to the TCL file along
with the build steps.
"""
self._set_tool_options_defaults()
(src_files, incdirs) = self._get_fileset_files(force_slash=True)
self.jinja_env.filters["src_file_filter"] = self.src_file_filter
has_systemVerilog = False
for src_file in src_files:
if src_file.file_type.startswith("systemVerilogSource"):
has_systemVerilog = True
break
# Spyglass expects all parameters in the form module.parameter
# Always prepend the toplevel module name to be consistent with all other
# backends, which do not require this syntax.
vlogparam_spyglass = OrderedDict(
(self.toplevel + "." + p, v) for (p, v) in self.vlogparam.items()
)
template_vars = {
"name": self.name,
"src_files": src_files,
"incdirs": incdirs,
"tool_options": self.tool_options,
"toplevel": self.toplevel,
"vlogparam": vlogparam_spyglass,
"vlogdefine": self.vlogdefine,
"has_systemVerilog": has_systemVerilog,
"sanitized_goals": [],
}
self.render_template(
"spyglass-project.prj.j2", self.name + ".prj", template_vars
)
# Create a single TCL file for each goal
goals = ["Design_Read"] + self.tool_options["goals"]
for goal in goals:
template_vars["goal"] = goal
sanitized_goal = re.sub(r"[^a-zA-Z0-9]", "_", goal).lower()
template_vars["sanitized_goals"].append(sanitized_goal)
self.render_template(
"spyglass-run-goal.tcl.j2",
"spyglass-run-%s.tcl" % sanitized_goal,
template_vars,
)
self.render_template("Makefile.j2", "Makefile", template_vars)
[docs]
def src_file_filter(self, f):
def _vhdl_source(f):
s = "read_file -type vhdl"
if f.logical_name:
s += " -library " + f.logical_name
return s
file_types = {
"verilogSource": "read_file -type verilog",
"systemVerilogSource": "read_file -type verilog",
"vhdlSource": _vhdl_source(f),
"tclSource": "source",
"waiver": "read_file -type waiver",
"awl": "read_file -type awl",
}
_file_type = get_file_type(f)
if _file_type in file_types:
return file_types[_file_type] + " " + f.name
elif _file_type == "user":
return ""
else:
_s = "{} has unknown file type '{}'"
logger.warning(_s.format(f.name, f.file_type))
return ""