SVALint 2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- SVALint/__init__.py +0 -0
- SVALint/__main__.py +9 -0
- SVALint/af_lint_rule.py +58 -0
- SVALint/asfigo_linter.py +121 -0
- SVALint/rules/__init__.py +0 -0
- SVALint/rules/af_asrt_naming.py +45 -0
- SVALint/rules/af_asrt_no_label.py +30 -0
- SVALint/rules/af_assume_naming.py +39 -0
- SVALint/rules/af_cover_naming.py +39 -0
- SVALint/rules/af_func_cov_nolap.py +33 -0
- SVALint/rules/af_func_cov_olap.py +33 -0
- SVALint/rules/af_func_missing_fablk.py +36 -0
- SVALint/rules/af_func_no_ub_in_cnseq.py +43 -0
- SVALint/rules/af_missing_elbl_prop.py +34 -0
- SVALint/rules/af_missing_elbl_seq.py +34 -0
- SVALint/rules/af_no_aa_exists_sva.py +56 -0
- SVALint/rules/af_no_dollar_time.py +41 -0
- SVALint/rules/af_no_fmatch_oper.py +44 -0
- SVALint/rules/af_no_pop_back_sva.py +57 -0
- SVALint/rules/af_no_pop_front_sva.py +57 -0
- SVALint/rules/af_no_range_ant.py +58 -0
- SVALint/rules/af_no_timeliteral.py +37 -0
- SVALint/rules/af_no_within_oper.py +46 -0
- SVALint/rules/af_perf_missing_impl_oper.py +29 -0
- SVALint/rules/af_perf_no_large_del.py +33 -0
- SVALint/rules/af_perf_no_pass_ablk.py +37 -0
- SVALint/rules/af_perf_no_ub_range_ant.py +51 -0
- SVALint/rules/af_prop_naming.py +37 -0
- SVALint/rules/af_reuse_fa_one_liner.py +42 -0
- SVALint/rules/af_use_simple_cnseq.py +45 -0
- svalint-2.0.dist-info/METADATA +7 -0
- svalint-2.0.dist-info/RECORD +35 -0
- svalint-2.0.dist-info/WHEEL +5 -0
- svalint-2.0.dist-info/entry_points.txt +2 -0
- svalint-2.0.dist-info/top_level.txt +1 -0
SVALint/__init__.py
ADDED
|
File without changes
|
SVALint/__main__.py
ADDED
SVALint/af_lint_rule.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
import logging
|
|
7
|
+
from bin import verible_verilog_syntax
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
|
|
10
|
+
class AsFigoLintRule(ABC):
|
|
11
|
+
"""Base class for all linting rules."""
|
|
12
|
+
|
|
13
|
+
VeribleSyntax = verible_verilog_syntax
|
|
14
|
+
rule_count = 0 # Class variable to track rules executed
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def __init__(self, linter):
|
|
18
|
+
self.linter = linter # Store the linter instance
|
|
19
|
+
self.ruleID = 'SVALintDefaultRuleID'
|
|
20
|
+
'''
|
|
21
|
+
if not hasattr(self, "ruleID"): # Ensure ruleID exists in subclasses
|
|
22
|
+
raise ValueError(f"{self.__class__.__name__} must define a `ruleID` attribute!")
|
|
23
|
+
'''
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def get_rule_count(cls):
|
|
28
|
+
return cls.rule_count # Get the count of rules applied
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def apply(self, filePath: str, data: verible_verilog_syntax.SyntaxData):
|
|
32
|
+
"""Abstract method to apply the rule."""
|
|
33
|
+
raise NotImplementedError
|
|
34
|
+
|
|
35
|
+
def getClassName(self, classNode):
|
|
36
|
+
"""Extracts the class name from a class declaration."""
|
|
37
|
+
for header in classNode.iter_find_all({"tag": "kClassHeader"}):
|
|
38
|
+
for identifier in header.iter_find_all({"tag": "SymbolIdentifier"}):
|
|
39
|
+
return identifier.text
|
|
40
|
+
return "Unknown"
|
|
41
|
+
|
|
42
|
+
def getQualifiers(self, varNode):
|
|
43
|
+
"""Extracts variable qualifiers (e.g., local, protected, rand)."""
|
|
44
|
+
qualifiers = set()
|
|
45
|
+
for qualList in varNode.iter_find_all({"tag": "kQualifierList"}):
|
|
46
|
+
qualifiers.update(qualList.text.split()) # Extract words
|
|
47
|
+
return qualifiers
|
|
48
|
+
|
|
49
|
+
def run(self, filePath: str, data: verible_verilog_syntax.SyntaxData):
|
|
50
|
+
"""Wrapper method to automatically count and apply the rule."""
|
|
51
|
+
AsFigoLintRule.rule_count += 1 # Automatically increment rule count
|
|
52
|
+
message = (
|
|
53
|
+
f"Running lint ruleID: {self.ruleID} on file: {filePath}\n"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
self.linter.logInfo(self.ruleID, message)
|
|
57
|
+
|
|
58
|
+
self.apply(filePath, data) # Call the actual rule logic
|
SVALint/asfigo_linter.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import tomli
|
|
9
|
+
import os.path
|
|
10
|
+
import copy
|
|
11
|
+
import functools
|
|
12
|
+
from operator import countOf
|
|
13
|
+
import operator as op
|
|
14
|
+
import logging
|
|
15
|
+
import json
|
|
16
|
+
import string_utils.validation as str_val
|
|
17
|
+
from .af_lint_rule import AsFigoLintRule
|
|
18
|
+
|
|
19
|
+
class BaseLintLogger:
|
|
20
|
+
def __init__(self, prefix, configFile="config.toml", logLevel=logging.INFO, logFile="svalint_run.log"):
|
|
21
|
+
self.prefix = prefix
|
|
22
|
+
self.logger = logging.getLogger(f"{prefix}Logger")
|
|
23
|
+
self.logger.setLevel(logLevel)
|
|
24
|
+
|
|
25
|
+
formatter = logging.Formatter('%(message)s')
|
|
26
|
+
|
|
27
|
+
stream_handler = logging.StreamHandler()
|
|
28
|
+
stream_handler.setLevel(logLevel)
|
|
29
|
+
stream_handler.setFormatter(formatter)
|
|
30
|
+
|
|
31
|
+
file_handler = logging.FileHandler(logFile, mode='a')
|
|
32
|
+
file_handler.setLevel(logLevel)
|
|
33
|
+
file_handler.setFormatter(formatter)
|
|
34
|
+
|
|
35
|
+
if self.logger.hasHandlers():
|
|
36
|
+
self.logger.handlers.clear()
|
|
37
|
+
|
|
38
|
+
self.logger.addHandler(stream_handler)
|
|
39
|
+
self.logger.addHandler(file_handler)
|
|
40
|
+
|
|
41
|
+
self.rulesConfig = self.loadConfig(configFile)
|
|
42
|
+
self.infoCount = 0
|
|
43
|
+
self.warningCount = 0
|
|
44
|
+
self.errorCount = 0
|
|
45
|
+
self.errorList = []
|
|
46
|
+
self.warningList = []
|
|
47
|
+
|
|
48
|
+
def loadConfig(self, configFile):
|
|
49
|
+
try:
|
|
50
|
+
with open(configFile, "rb") as file:
|
|
51
|
+
config = tomli.load(file)
|
|
52
|
+
return config.get("rules", {})
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
self.logger.warning(f"{self.prefix}: Config file '{configFile}' not found. Using default settings.")
|
|
55
|
+
return {}
|
|
56
|
+
|
|
57
|
+
def ruleEnabled(self, ruleId):
|
|
58
|
+
return self.rulesConfig.get(ruleId, True)
|
|
59
|
+
|
|
60
|
+
def logInfo(self, ruleId, msg, severity="INFO"):
|
|
61
|
+
self.infoCount += 1
|
|
62
|
+
|
|
63
|
+
logMsg = f"{self.prefix}: INFO: {msg}"
|
|
64
|
+
self.logger.info(logMsg)
|
|
65
|
+
|
|
66
|
+
def logViolation(self, ruleId, msg, severity="ERROR"):
|
|
67
|
+
if self.ruleEnabled(ruleId):
|
|
68
|
+
wrapped_msg = msg
|
|
69
|
+
logMsg = f"{self.prefix}: Violation: [{ruleId}]:\n{msg}"
|
|
70
|
+
|
|
71
|
+
if severity == "ERROR":
|
|
72
|
+
self.errorCount += 1
|
|
73
|
+
self.errorList.append(ruleId) # Store error ID
|
|
74
|
+
self.logger.error(logMsg)
|
|
75
|
+
elif severity == "WARNING":
|
|
76
|
+
self.warningCount += 1
|
|
77
|
+
self.warningList.append(ruleId) # Store warning ID
|
|
78
|
+
self.logger.warning(logMsg)
|
|
79
|
+
else:
|
|
80
|
+
raise ValueError(f"Unsupported severity level: {severity}")
|
|
81
|
+
else:
|
|
82
|
+
self.logger.info(f"{self.prefix}: Rule [{ruleId}] is disabled and will not be logged.")
|
|
83
|
+
|
|
84
|
+
def logSummary(self):
|
|
85
|
+
self.logger.info("\n--------------------------------")
|
|
86
|
+
self.logger.info("AsFigo SVALint Report Summary")
|
|
87
|
+
self.logger.info(f"Total lint rules executed: {AsFigoLintRule.get_rule_count()}")
|
|
88
|
+
|
|
89
|
+
self.logger.info("--------------------------------")
|
|
90
|
+
# Report counts by severity
|
|
91
|
+
self.logger.info("** Report counts by severity")
|
|
92
|
+
self.logger.info(f"INFO : {self.infoCount}")
|
|
93
|
+
self.logger.info(f"WARNING : {self.warningCount}")
|
|
94
|
+
self.logger.info(f"ERROR : {self.errorCount}")
|
|
95
|
+
|
|
96
|
+
# Report counts by ID (show each unique rule ID and how many times it was logged)
|
|
97
|
+
self.logger.info("\n** Report counts by ID")
|
|
98
|
+
self._printRuleCounts(self.errorList, "ERROR")
|
|
99
|
+
self._printRuleCounts(self.warningList, "WARNING")
|
|
100
|
+
|
|
101
|
+
self.logger.info("--------------------------------\n")
|
|
102
|
+
|
|
103
|
+
def _printRuleCounts(self, ruleList, severity):
|
|
104
|
+
ruleDict = {}
|
|
105
|
+
for ruleId in ruleList:
|
|
106
|
+
ruleDict[ruleId] = ruleDict.get(ruleId, 0) + 1
|
|
107
|
+
|
|
108
|
+
for ruleId, count in sorted(ruleDict.items(), key=lambda x: -x[1]): # Sort by highest count
|
|
109
|
+
self.logger.info(f"[{ruleId}] {count}")
|
|
110
|
+
|
|
111
|
+
class AsFigoLinter(BaseLintLogger):
|
|
112
|
+
def __init__(self, configFile="config.toml", logLevel=logging.INFO):
|
|
113
|
+
super().__init__(prefix="AsFigo", configFile=configFile, logLevel=logLevel)
|
|
114
|
+
self.args = self.parseArguments()
|
|
115
|
+
self.testName = self.args.test
|
|
116
|
+
|
|
117
|
+
def parseArguments(self):
|
|
118
|
+
parser = argparse.ArgumentParser(description="AsFigoLinter Argument Parser")
|
|
119
|
+
parser.add_argument("-t", "--test", required=True, help="Input test name (file path)")
|
|
120
|
+
return parser.parse_args()
|
|
121
|
+
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# Author: Himank Gangwal, Sep 02, 2025
|
|
6
|
+
# ----------------------------------------------------
|
|
7
|
+
|
|
8
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
9
|
+
import logging
|
|
10
|
+
import anytree
|
|
11
|
+
|
|
12
|
+
class AssertNaming(AsFigoLintRule):
|
|
13
|
+
"""Checks if assert follows a naming convention - start with "a_" """
|
|
14
|
+
|
|
15
|
+
def __init__(self, linter):
|
|
16
|
+
self.linter = linter
|
|
17
|
+
self.ruleID = "ASSERT_NAMING"
|
|
18
|
+
|
|
19
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
20
|
+
for curNode in data.tree.iter_find_all({"tag": "kAssertionItem"}):
|
|
21
|
+
lvLabel = curNode.children[0].text
|
|
22
|
+
lvVerifDirType = curNode.children[1]
|
|
23
|
+
if (not 'kAssertPropertyStatement' in lvVerifDirType.tag):
|
|
24
|
+
continue
|
|
25
|
+
|
|
26
|
+
if not lvLabel:
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
if not lvLabel.startswith("a_"):
|
|
30
|
+
message = (
|
|
31
|
+
f"Debug: Found assert name without a_ prefix. "
|
|
32
|
+
f"Use a_ as assert prefix. This helps users to "
|
|
33
|
+
f"look for specific patterns in their log files. "
|
|
34
|
+
f"Found an Assertion as:\n"
|
|
35
|
+
f"{curNode.text}\n"
|
|
36
|
+
)
|
|
37
|
+
self.linter.logViolation(self.ruleID, message, "WARNING")
|
|
38
|
+
|
|
39
|
+
def getAssertPropertyName(self, assert_node):
|
|
40
|
+
"""Extracts the assert property name from an assert property statement."""
|
|
41
|
+
# Look for SymbolIdentifier nodes that represent the assert property name
|
|
42
|
+
for identifier in assert_node.iter_find_all({"tag": "SymbolIdentifier"}):
|
|
43
|
+
# The first SymbolIdentifier in an assert property is usually the name
|
|
44
|
+
return identifier.text
|
|
45
|
+
return "Unknown"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
|
|
7
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
8
|
+
import logging
|
|
9
|
+
import anytree
|
|
10
|
+
|
|
11
|
+
class MissingLabelChk(AsFigoLintRule):
|
|
12
|
+
"""Enhance debug-ability of assertions by using labels """
|
|
13
|
+
|
|
14
|
+
def __init__(self, linter):
|
|
15
|
+
self.linter = linter # Store the linter instance
|
|
16
|
+
self.ruleID = "ASSERT_MISSING_LABEL"
|
|
17
|
+
|
|
18
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
19
|
+
|
|
20
|
+
for curNode in data.tree.iter_find_all({"tag": "kAssertionItem"}):
|
|
21
|
+
lvSvaCode = curNode.text
|
|
22
|
+
lvAsrtLabel = curNode.children[0]
|
|
23
|
+
if (( ":" not in lvAsrtLabel.text)):
|
|
24
|
+
message = (
|
|
25
|
+
f"Error: Missing label for an assert statement.\n"
|
|
26
|
+
f"{lvSvaCode}\n"
|
|
27
|
+
)
|
|
28
|
+
self.linter.logViolation(self.ruleID, message)
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# Author: Himank Gangwal, Sep 02, 2025
|
|
6
|
+
# ----------------------------------------------------
|
|
7
|
+
|
|
8
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
9
|
+
import logging
|
|
10
|
+
import anytree
|
|
11
|
+
|
|
12
|
+
class AssumeNaming(AsFigoLintRule):
|
|
13
|
+
"""Checks if assume follows a naming convention - start with "m_" """
|
|
14
|
+
|
|
15
|
+
def __init__(self, linter):
|
|
16
|
+
self.linter = linter
|
|
17
|
+
self.ruleID = "ASSUME_NAMING"
|
|
18
|
+
|
|
19
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
20
|
+
for curNode in data.tree.iter_find_all({"tag": "kAssertionItem"}):
|
|
21
|
+
lvLabel = curNode.children[0].text
|
|
22
|
+
lvVerifDirType = curNode.children[1]
|
|
23
|
+
|
|
24
|
+
if (not 'kAssumePropertyStatement' in lvVerifDirType.tag):
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
if not lvLabel:
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
if not lvLabel.startswith("m_"):
|
|
31
|
+
message = (
|
|
32
|
+
f"Debug: Found assume name without m_ prefix. "
|
|
33
|
+
f"Use m_ as assume prefix. This helps users to "
|
|
34
|
+
f"look for specific patterns in their log files. "
|
|
35
|
+
f"Found an Assume as:\n"
|
|
36
|
+
f"{curNode.text}\n"
|
|
37
|
+
)
|
|
38
|
+
self.linter.logViolation(self.ruleID, message, "WARNING")
|
|
39
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# Author: Himank Gangwal, Sep 02, 2025
|
|
6
|
+
# ----------------------------------------------------
|
|
7
|
+
|
|
8
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
9
|
+
import logging
|
|
10
|
+
import anytree
|
|
11
|
+
|
|
12
|
+
class CoverNaming(AsFigoLintRule):
|
|
13
|
+
"""Checks if cover follows a naming convention - start with "c_" """
|
|
14
|
+
|
|
15
|
+
def __init__(self, linter):
|
|
16
|
+
self.linter = linter
|
|
17
|
+
self.ruleID = "COVER_NAMING"
|
|
18
|
+
|
|
19
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
20
|
+
for curNode in data.tree.iter_find_all({"tag": "kAssertionItem"}):
|
|
21
|
+
lvLabel = curNode.children[0].text
|
|
22
|
+
lvVerifDirType = curNode.children[1]
|
|
23
|
+
|
|
24
|
+
if (not 'kCoverPropertyStatement' in lvVerifDirType.tag):
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
if not lvLabel:
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
if not lvLabel.startswith("c_"):
|
|
31
|
+
message = (
|
|
32
|
+
f"Debug: Found cover name without c_ prefix. "
|
|
33
|
+
f"Use c_ as cover prefix. This helps users to "
|
|
34
|
+
f"look for specific patterns in their log files. "
|
|
35
|
+
f"Found a cover as:\n"
|
|
36
|
+
f"{curNode.text}\n"
|
|
37
|
+
)
|
|
38
|
+
self.linter.logViolation(self.ruleID, message, "WARNING")
|
|
39
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
7
|
+
import logging
|
|
8
|
+
import anytree
|
|
9
|
+
|
|
10
|
+
class FuncNOLAPInCoverProp(AsFigoLintRule):
|
|
11
|
+
"""Cover Property with non-overlapped implicaiton operator leads to false-positives """
|
|
12
|
+
|
|
13
|
+
def __init__(self, linter):
|
|
14
|
+
self.linter = linter # Store the linter instance
|
|
15
|
+
self.ruleID = "FUNC_NO_NON_OLAP_COVER"
|
|
16
|
+
|
|
17
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
18
|
+
|
|
19
|
+
for curNode in data.tree.iter_find_all({"tag": "kCoverPropertyStatement"}):
|
|
20
|
+
lvSvaCode = curNode.text
|
|
21
|
+
lvNonOlapImplG = curNode.iter_find_all({"tag": "|=>"})
|
|
22
|
+
if (len(list(lvNonOlapImplG)) > 0):
|
|
23
|
+
message = (
|
|
24
|
+
f"FUNC: Found a non-overlapped implication operator with "
|
|
25
|
+
f"cover property directive. This leads to vacuous/bogus "
|
|
26
|
+
f"functional/assertion/temporal coverage to be collected "
|
|
27
|
+
f"and reported. This can be a serious hole in the verification "
|
|
28
|
+
f"process as it gives a false-sense of security/coverage."
|
|
29
|
+
f"Use ##1 instead. Code snippet: \n"
|
|
30
|
+
f"{lvSvaCode}\n"
|
|
31
|
+
)
|
|
32
|
+
self.linter.logViolation(self.ruleID, message)
|
|
33
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
7
|
+
import logging
|
|
8
|
+
import anytree
|
|
9
|
+
|
|
10
|
+
class FuncOLAPInCoverProp(AsFigoLintRule):
|
|
11
|
+
"""Cover Property with overlapped implicaiton operator leads to false-positives """
|
|
12
|
+
|
|
13
|
+
def __init__(self, linter):
|
|
14
|
+
self.linter = linter # Store the linter instance
|
|
15
|
+
self.ruleID = "FUNC_NO_OLAP_COVER"
|
|
16
|
+
|
|
17
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
18
|
+
|
|
19
|
+
for curNode in data.tree.iter_find_all({"tag": "kCoverPropertyStatement"}):
|
|
20
|
+
lvSvaCode = curNode.text
|
|
21
|
+
lvNonOlapImplG = curNode.iter_find_all({"tag": "|->"})
|
|
22
|
+
if (len(list(lvNonOlapImplG)) > 0):
|
|
23
|
+
message = (
|
|
24
|
+
f"FUNC: Found a overlapped implication operator with "
|
|
25
|
+
f"cover property directive. This leads to vacuous/bogus "
|
|
26
|
+
f"functional/assertion/temporal coverage to be collected "
|
|
27
|
+
f"and reported. This can be a serious hole in the verification "
|
|
28
|
+
f"process as it gives a false-sense of security/coverage."
|
|
29
|
+
f"Use ##0 instead. Code snippet: \n"
|
|
30
|
+
f"{lvSvaCode}\n"
|
|
31
|
+
)
|
|
32
|
+
self.linter.logViolation(self.ruleID, message)
|
|
33
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
7
|
+
import logging
|
|
8
|
+
import anytree
|
|
9
|
+
|
|
10
|
+
class FuncMissingFABLK(AsFigoLintRule):
|
|
11
|
+
"""Action block related checks """
|
|
12
|
+
|
|
13
|
+
def __init__(self, linter):
|
|
14
|
+
self.linter = linter # Store the linter instance
|
|
15
|
+
self.ruleID = "FUNC_MISSING_FAIL_ABLK"
|
|
16
|
+
|
|
17
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
18
|
+
|
|
19
|
+
for curNode in data.tree.iter_find_all({"tag": "kAssertionItem"}):
|
|
20
|
+
lvSvaCode = curNode.text
|
|
21
|
+
|
|
22
|
+
lvAsrtIter = curNode.iter_find_all({"tag": "kAssertPropertyStatement"})
|
|
23
|
+
lvAsrtProp = next(lvAsrtIter, None)
|
|
24
|
+
if lvAsrtProp is None:
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
lvAsrtFailAblkNode = curNode.iter_find_all({"tag": "kElseClause"})
|
|
28
|
+
lvAsrtFailAblkNodeNxt = next(lvAsrtFailAblkNode, None)
|
|
29
|
+
if lvAsrtFailAblkNodeNxt is None:
|
|
30
|
+
message = (
|
|
31
|
+
f"FUNC: Missing fail action block in assert statement.\n"
|
|
32
|
+
f"Severly impacts verification completeness as errors may go undetected\n"
|
|
33
|
+
f"{lvSvaCode}\n"
|
|
34
|
+
)
|
|
35
|
+
self.linter.logViolation(self.ruleID, message)
|
|
36
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
|
|
7
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
8
|
+
import logging
|
|
9
|
+
import anytree
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NoUBRangeInConseqAsrt(AsFigoLintRule):
|
|
13
|
+
lvMsg = """
|
|
14
|
+
From book: https://payhip.com/b/7HvMk
|
|
15
|
+
|
|
16
|
+
by @hdlcohen
|
|
17
|
+
|
|
18
|
+
An assertion with an infinite range in its consequent can never FAIL, but can PASS.
|
|
19
|
+
|
|
20
|
+
$rose(req) |-> ##[0:$] ack);
|
|
21
|
+
Consider using a more deterministic delay
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, linter):
|
|
26
|
+
self.linter = linter
|
|
27
|
+
# Store the linter instance
|
|
28
|
+
self.ruleID = "FUNC_AVOID_$_RANGE_IN_CONSEQ_A"
|
|
29
|
+
|
|
30
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
31
|
+
|
|
32
|
+
for curNode in data.tree.iter_find_all({"tag": "kAssertionItem"}):
|
|
33
|
+
lvSvaCode = curNode.text
|
|
34
|
+
for curImplNode in curNode.iter_find_all({"tag": "|->"}):
|
|
35
|
+
lvImplCnseqNode = curImplNode.siblings[1]
|
|
36
|
+
lvRangeG = lvImplCnseqNode.iter_find_all({"tag": "kCycleDelayRange"})
|
|
37
|
+
for curDelRangeNode in lvRangeG:
|
|
38
|
+
lvCnseqUBRangeG = curDelRangeNode.iter_find_all({"tag": "$"})
|
|
39
|
+
lvCnseqUBRangeList = list(lvCnseqUBRangeG)
|
|
40
|
+
if len(lvCnseqUBRangeList) > 0:
|
|
41
|
+
message = f"{self.lvMsg}\n" f"{lvSvaCode}\n"
|
|
42
|
+
|
|
43
|
+
self.linter.logViolation(self.ruleID, message)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
|
|
7
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
8
|
+
import logging
|
|
9
|
+
import anytree
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MissingEndLblProp(AsFigoLintRule):
|
|
13
|
+
"""Checks for missing end-labels in property """
|
|
14
|
+
|
|
15
|
+
def __init__(self, linter):
|
|
16
|
+
self.linter = linter
|
|
17
|
+
# Store the linter instance
|
|
18
|
+
self.ruleID = "DBG_MISS_END_LBL_PROP"
|
|
19
|
+
|
|
20
|
+
def apply(
|
|
21
|
+
self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData
|
|
22
|
+
):
|
|
23
|
+
for curNode in data.tree.iter_find_all(
|
|
24
|
+
{"tag": "kPropertyDeclaration"}):
|
|
25
|
+
lvSvaCode = curNode.text
|
|
26
|
+
lvLastElem = curNode.descendants[-1]
|
|
27
|
+
if (not lvLastElem.text):
|
|
28
|
+
message = (
|
|
29
|
+
f"Debug: Found a property without endlabel. Use of endlabels "
|
|
30
|
+
f"greatly enhances debug and increases productivity\n"
|
|
31
|
+
f"{lvSvaCode}\n"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
self.linter.logViolation(self.ruleID, message)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
|
|
7
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
8
|
+
import logging
|
|
9
|
+
import anytree
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MissingEndLblSEQ(AsFigoLintRule):
|
|
13
|
+
"""Checks for missing end-labels in seqeunce """
|
|
14
|
+
|
|
15
|
+
def __init__(self, linter):
|
|
16
|
+
self.linter = linter
|
|
17
|
+
# Store the linter instance
|
|
18
|
+
self.ruleID = "DBG_MISS_END_LBL_SEQ"
|
|
19
|
+
|
|
20
|
+
def apply(
|
|
21
|
+
self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData
|
|
22
|
+
):
|
|
23
|
+
for curNode in data.tree.iter_find_all(
|
|
24
|
+
{"tag": "kSequenceDeclaration"}):
|
|
25
|
+
lvSvaCode = curNode.text
|
|
26
|
+
lvLastElem = curNode.descendants[-1]
|
|
27
|
+
if (not lvLastElem.text):
|
|
28
|
+
message = (
|
|
29
|
+
f"Debug: Found a SEQ without endlabel. Use of endlabels "
|
|
30
|
+
f"greatly enhances debug and increases productivity\n"
|
|
31
|
+
f"{lvSvaCode}\n"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
self.linter.logViolation(self.ruleID, message)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# Author: Himank Gangwal, Sep 02, 2025
|
|
6
|
+
# ----------------------------------------------------
|
|
7
|
+
|
|
8
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
9
|
+
import logging
|
|
10
|
+
import anytree
|
|
11
|
+
|
|
12
|
+
class AvoidAAExistsSVA(AsFigoLintRule):
|
|
13
|
+
"""Checks if Assoc array method exist() is in the SVA code """
|
|
14
|
+
|
|
15
|
+
lvMsg = """
|
|
16
|
+
Rule suggested by Ben Cohen:
|
|
17
|
+
|
|
18
|
+
IEEE LRM 1800 states:
|
|
19
|
+
|
|
20
|
+
16.6 Boolean expressions: “Elements of dynamic arrays, queues, and associative arrays that are sampled for assertion expression evaluation may get removed from the array or the array may get resized before the assertion expression is evaluated. These specific array elements sampled for assertion expression evaluation shall continue to exist within the scope of the assertion until the assertion expression evaluation completes.”
|
|
21
|
+
|
|
22
|
+
Many tools are slow to adopt this IEEE 1800 requirement. Several imposed restrictions on the use of associative arrays within assertions, and some prohibited them entirely, likely due to performance concerns.
|
|
23
|
+
|
|
24
|
+
So avoid usage of array.exists() method call in SVA
|
|
25
|
+
|
|
26
|
+
e.g.
|
|
27
|
+
((valid, v_err=mem_aarray.exists(addr))
|
|
28
|
+
"""
|
|
29
|
+
def __init__(self, linter):
|
|
30
|
+
self.linter = linter
|
|
31
|
+
self.ruleID = "NO_AA_EXISTS_SVA"
|
|
32
|
+
|
|
33
|
+
def apply(self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData):
|
|
34
|
+
for curNode in data.tree.iter_find_all({"tag": ["kPropertyDeclaration"]}):
|
|
35
|
+
for hierNode in curNode.iter_find_all({"tag": ["kHierarchyExtension"]}):
|
|
36
|
+
# check if the node contains "exists"
|
|
37
|
+
if self.containsExists(hierNode):
|
|
38
|
+
message = (
|
|
39
|
+
f"{self.lvMsg}"
|
|
40
|
+
f"COMPAT: Found .exists() in the code. \n"
|
|
41
|
+
f"{curNode.text}"
|
|
42
|
+
)
|
|
43
|
+
self.linter.logViolation(self.ruleID, message)
|
|
44
|
+
|
|
45
|
+
def containsExists(self, node):
|
|
46
|
+
"""Checks if a node or its children contain 'exists' usage."""
|
|
47
|
+
# Check the current node's text
|
|
48
|
+
if hasattr(node, 'text') and 'exists' in node.text:
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
# Check all SymbolIdentifier nodes in this hierarchy
|
|
52
|
+
for identifier in node.iter_find_all({"tag": "SymbolIdentifier"}):
|
|
53
|
+
if identifier.text == "exists":
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
return False
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# ----------------------------------------------------
|
|
2
|
+
# SPDX-FileCopyrightText: AsFigo Technologies, UK
|
|
3
|
+
# SPDX-FileCopyrightText: VerifWorks, India
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
# ----------------------------------------------------
|
|
6
|
+
|
|
7
|
+
from ..af_lint_rule import AsFigoLintRule
|
|
8
|
+
import logging
|
|
9
|
+
import anytree
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UseRealTimeVsTime(AsFigoLintRule):
|
|
13
|
+
"""Use $realtime than $time in SVA"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, linter):
|
|
16
|
+
self.linter = linter
|
|
17
|
+
# Store the linter instance
|
|
18
|
+
self.ruleID = "FUNC_AVOID_DOLLAR_TIME"
|
|
19
|
+
|
|
20
|
+
def apply(
|
|
21
|
+
self, filePath: str, data: AsFigoLintRule.VeribleSyntax.SyntaxData
|
|
22
|
+
):
|
|
23
|
+
for curNode in data.tree.iter_find_all(
|
|
24
|
+
{"tag": "kPropertyDeclaration"}
|
|
25
|
+
):
|
|
26
|
+
lvSvaCode = curNode.text
|
|
27
|
+
lvSysTFCall = curNode.iter_find_all({"tag": "kSystemTFCall"})
|
|
28
|
+
for curSysTFName in lvSysTFCall:
|
|
29
|
+
lvSysTFNameIter = curSysTFName.iter_find_all({"tag": "SystemTFIdentifier"})
|
|
30
|
+
lvCurSysTFName = next(lvSysTFNameIter)
|
|
31
|
+
if ('$time' in lvCurSysTFName.text):
|
|
32
|
+
message = (
|
|
33
|
+
f"FUNC: Found a call to System function $time "
|
|
34
|
+
f"inside a SVA property. "
|
|
35
|
+
f"Timing checks should be performed using realtime than "
|
|
36
|
+
f"$time. See: below discussion to appreciate the rationale: \n"
|
|
37
|
+
f"https://verificationacademy.com/forums/t/sva-for-delayed-state-transition-from-fault-id-to-wait-state-100ms-delay/51322 \n"
|
|
38
|
+
f"{lvSvaCode}\n"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
self.linter.logViolation(self.ruleID, message)
|