Skip to content

Commit ae20ff3

Browse files
author
Yan Churkin
committed
[slang] Introduce clock resolution support (SystemVerilog LRM 16.13/16.16 sections).
1 parent 73b79be commit ae20ff3

File tree

12 files changed

+2405
-4
lines changed

12 files changed

+2405
-4
lines changed

Diff for: docs/command-line-ref.dox

+5
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,11 @@ Specifies a default time scale to use for design elements that don't explicitly
289289
provide one. If this option is not set, there is no default and an error will be
290290
issued if not all elements have a time scale specified. Example: `--timescale=1ns/1ns`
291291

292+
`--disable-clock-resolution`
293+
294+
Disables clock resolution checks from SystemVerilog LRM 16.13 section which
295+
are performed by default.
296+
292297
`-G <name>=<value>`
293298

294299
Override all parameters with the given name in top-level modules to the provided value.

Diff for: include/slang/ast/ClockResolution.h

+295
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
//------------------------------------------------------------------------------
2+
// ClockResolution.h
3+
// Clock resolution AST visitors and helpers
4+
//
5+
// SPDX-FileCopyrightText: ISP RAS
6+
// SPDX-License-Identifier: MIT
7+
//------------------------------------------------------------------------------
8+
#pragma once
9+
10+
#include "slang/ast/ASTVisitor.h"
11+
#include "slang/ast/symbols/CompilationUnitSymbols.h"
12+
#include "slang/diagnostics/CompilationDiags.h"
13+
14+
namespace slang::ast::clk_res {
15+
16+
using namespace syntax;
17+
18+
struct ClockingEvent {
19+
using EventList = SmallVector<const Symbol*>;
20+
21+
enum ClockingEventKind { None, SeqProp, Default, Always } kind;
22+
bool initialize(ClockingEventKind eventKind, const SignalEventControl* sEC,
23+
Compilation& compilation);
24+
bool operator==(const ClockingEvent&) const;
25+
26+
/// For diagnostics
27+
SourceRange sourceRange;
28+
/// For simple event expression with just edge wrapped signals
29+
EventList events;
30+
EdgeKind edge;
31+
/// For storing complex event expression (with `iff` also).
32+
/// Storing AST as a string is necessary in order to determine if there are any identical events
33+
/// by comparing strings.
34+
std::string strEvent;
35+
};
36+
37+
/// Visitor checks the properties and sequences at `clocking` blocks to determine it's signal event
38+
/// list
39+
class ClockingBlockVisitor : public ASTVisitor<ClockingBlockVisitor, true, true> {
40+
public:
41+
ClockingBlockVisitor(Compilation& compilation, const SignalEventControl* clkBlkSEC,
42+
const SourceLocation sLoc) :
43+
compilation(compilation), clkBlkSEC(clkBlkSEC), sLoc(sLoc) {}
44+
45+
void handle(const AssertionInstanceExpression&);
46+
void handle(const SignalEventControl&);
47+
48+
private:
49+
Compilation& compilation;
50+
/// Clocking block signal event control expression
51+
const SignalEventControl* clkBlkSEC;
52+
/// Stack for storing current assertion instance context to get it's scope later
53+
SmallVector<const AssertionInstanceExpression*> aInstP;
54+
/// For diagnostics
55+
const SourceLocation sLoc;
56+
};
57+
58+
/// Endpoint of analysis which is represented by possible leaf nodes.
59+
/// It emits an error if any leaf was reached with no determined clock event but adds it
60+
/// into the context if not.
61+
#define REPORT_INFERRED(EXPR) \
62+
void handle(const EXPR&) { \
63+
if (clkEvents.empty()) { \
64+
if (inferred.empty()) \
65+
compilation.getRoot().addDiag(diag::NoClockResolved, sourceRange); \
66+
else \
67+
addUniqueCE(inferred.back()); \
68+
} \
69+
}
70+
71+
/// Sequence verify visitor
72+
class SequenceVisitor : public ASTVisitor<SequenceVisitor, true, true> {
73+
public:
74+
SequenceVisitor(Compilation& compilation, SourceRange sourceRange,
75+
SmallVector<ClockingEvent>& inferred, SmallVector<ClockingEvent>& clkEvents,
76+
bool ignoreSubSeq) :
77+
compilation(compilation), sourceRange(sourceRange), inferred(inferred),
78+
clkEvents(clkEvents), ignoreSubSeq(ignoreSubSeq) {}
79+
80+
SmallVector<ClockingEvent> merge(const SmallVector<ClockingEvent>& first,
81+
const SmallVector<ClockingEvent>& second);
82+
/// Check the clocking event presense in visitor clocking event lists and add into it if not
83+
void addUniqueCE(const ClockingEvent&);
84+
REPORT_INFERRED(ValueExpressionBase)
85+
REPORT_INFERRED(CallExpression)
86+
REPORT_INFERRED(IntegerLiteral)
87+
REPORT_INFERRED(RealLiteral)
88+
REPORT_INFERRED(TimeLiteral)
89+
REPORT_INFERRED(UnbasedUnsizedIntegerLiteral)
90+
REPORT_INFERRED(NullLiteral)
91+
REPORT_INFERRED(UnboundedLiteral)
92+
REPORT_INFERRED(StringLiteral)
93+
void handle(const BinaryAssertionExpr&);
94+
void handle(const SequenceConcatExpr&);
95+
void handle(const ClockingAssertionExpr&);
96+
97+
private:
98+
Compilation& compilation;
99+
/// For diagnostics
100+
SourceRange sourceRange;
101+
/// Sequence inferred clock events
102+
SmallVector<ClockingEvent>& inferred;
103+
/// List of all current context clocking events
104+
SmallVector<ClockingEvent>& clkEvents;
105+
/// Store binary and concat operators left and right part clocking event lists
106+
/// to compare it later
107+
SmallVector<ClockingEvent> binClkEvents;
108+
/// Flag to ignore checking nested nodes in the case of assumptions or restrictions being made.
109+
bool ignoreSubSeq;
110+
};
111+
112+
/// Concurrent assertion visitor
113+
class ConcurrentAssertionVisitor : public ASTVisitor<ConcurrentAssertionVisitor, true, true> {
114+
public:
115+
ConcurrentAssertionVisitor(Compilation& compilation, SourceRange sourceRange,
116+
SmallVector<ClockingEvent>& inferred, bool explicitClocking) :
117+
compilation(compilation), sourceRange(sourceRange), inferred(inferred) {
118+
// Below is a implementation of the VCS check which emits an error for such type of sequence
119+
// property
120+
// `sequence seqX; @(negedge clk) a ##1 @(posedge clk_n)b; endsequence`
121+
// `always @(posedge_clk) assert property (seqX);`
122+
if (!explicitClocking && inferred.size() && inferred.back().kind == ClockingEvent::Always) {
123+
clkEvents.push_back(inferred.back());
124+
}
125+
}
126+
127+
void addUniqueCE(const ClockingEvent&);
128+
REPORT_INFERRED(ValueExpressionBase)
129+
REPORT_INFERRED(CallExpression)
130+
REPORT_INFERRED(IntegerLiteral)
131+
REPORT_INFERRED(RealLiteral)
132+
REPORT_INFERRED(TimeLiteral)
133+
REPORT_INFERRED(UnbasedUnsizedIntegerLiteral)
134+
REPORT_INFERRED(NullLiteral)
135+
REPORT_INFERRED(UnboundedLiteral)
136+
REPORT_INFERRED(StringLiteral)
137+
void handle(const AbortAssertionExpr&);
138+
void handle(const ConditionalAssertionExpr&);
139+
void handle(const CaseAssertionExpr&);
140+
void handle(const SequenceConcatExpr&);
141+
void handle(const ClockingAssertionExpr&);
142+
void handle(const UnaryAssertionExpr&);
143+
void handle(const BinaryAssertionExpr&);
144+
void handle(const AssertionInstanceExpression&);
145+
146+
/// List of current context clocking events
147+
SmallVector<ClockingEvent> clkEvents;
148+
149+
private:
150+
Compilation& compilation;
151+
/// For diagnostics
152+
SourceRange sourceRange;
153+
/// Sequence/property inferred clock events
154+
SmallVector<ClockingEvent>& inferred;
155+
/// Flag to ignore checking nested nodes in the case of assumptions or restrictions being made.
156+
bool ignoreSubSeq = false;
157+
};
158+
159+
/// Visitor to collect all possible inferred clocks at `SignalEventControl` expression
160+
class AlwaysTimingVisitor : public ASTVisitor<AlwaysTimingVisitor, true, true> {
161+
public:
162+
AlwaysTimingVisitor(Compilation& compilation, SmallVector<ClockingEvent>& inferred,
163+
ClockingEvent& checkerInferredClock) :
164+
compilation(compilation), inferred(inferred), checkerInferredClock(checkerInferredClock) {}
165+
166+
void handle(const SignalEventControl&);
167+
168+
private:
169+
Compilation& compilation;
170+
/// List of inferred clocks
171+
SmallVector<ClockingEvent>& inferred;
172+
/// Previously stored checker instance inferred clock
173+
ClockingEvent& checkerInferredClock;
174+
};
175+
176+
/// Visitor to erase from a list of inferred clocking events that signals which is used outside
177+
/// assertions (that isn't a real clock due to SystemVerilog LRM).
178+
/// It also checks that delay are not present before assertion.
179+
class AlwaysTimingCheckUseVisitor : public ASTVisitor<AlwaysTimingCheckUseVisitor, true, true> {
180+
public:
181+
AlwaysTimingCheckUseVisitor(Compilation& compilation, SmallVector<ClockingEvent>& inferred) :
182+
compilation(compilation), inferred(inferred) {
183+
delayBeforeAssert = nullptr;
184+
}
185+
186+
void handle(const ConcurrentAssertionStatement&) {
187+
if (delayBeforeAssert) {
188+
compilation.getRoot().addDiag(diag::AssertAfterTimingControl,
189+
delayBeforeAssert->sourceRange);
190+
delayBeforeAssert = nullptr;
191+
}
192+
// No `defaultVisit` for ignoring subsidaries
193+
}
194+
195+
void handle(const ImmediateAssertionStatement&) {
196+
// IEEE Std 1800-2017 16.14.6.c.2
197+
// Ignore subsidaries
198+
}
199+
200+
void handle(const ValueExpressionBase& vEB) {
201+
for (auto& iter : inferred) {
202+
auto& iterEvList = iter.events;
203+
if (iter.kind == ClockingEvent::Always &&
204+
(std::find(iterEvList.begin(), iterEvList.end(), &vEB.symbol) != iterEvList.end()))
205+
iterEvList.clear();
206+
}
207+
208+
inferred.erase(std::remove_if(inferred.begin(), inferred.end(),
209+
[](const auto& cE) { return cE.events.empty(); }),
210+
inferred.end());
211+
}
212+
213+
void handle(const TimingControl& tC) {
214+
if ((tC.as_if<DelayControl>() || tC.as_if<SignalEventControl>()) && !delayBeforeAssert)
215+
delayBeforeAssert = &tC;
216+
}
217+
218+
private:
219+
Compilation& compilation;
220+
/// List of `always` block inferred clocks
221+
SmallVector<ClockingEvent>& inferred;
222+
// For delay storing which is present before assertion
223+
const TimingControl* delayBeforeAssert;
224+
};
225+
226+
/// Instance body helper visitor - visiting should start at InstanceBody symbol
227+
class InstanceBodyVisitor : public ASTVisitor<InstanceBodyVisitor, true, true> {
228+
public:
229+
InstanceBodyVisitor(Compilation& compilation, ClockingEvent& ce, bool skipCheckerBody = true) :
230+
compilation(compilation), skipCheckerBody(skipCheckerBody) {
231+
if (ce.events.size() || ce.strEvent != "")
232+
inferred.push_back(ce);
233+
}
234+
235+
void processPropSeq(const Symbol&) const;
236+
237+
/// If skipCheckerBody is set to false, then we skip the current checker instance body to
238+
/// process it later by using the instances array at the ProceduralCheckerStatement handler
239+
/// below. In reality, a checker instance can be located, for example, inside an always block,
240+
/// but according to the AST, it may appear outside, preventing a correct understanding of the
241+
/// context of inferred clocks. Therefore, the processing of checker instances can be delayed.
242+
void handle(const CheckerInstanceBodySymbol& sym) {
243+
if (!skipCheckerBody)
244+
visitDefault(sym);
245+
}
246+
void handle(const ProceduralCheckerStatement&);
247+
void handle(const ProceduralBlockSymbol&);
248+
void handle(const PropertySymbol&);
249+
void handle(const SequenceSymbol&);
250+
void handle(const ConcurrentAssertionStatement&);
251+
void handle(const ClockingBlockSymbol&);
252+
253+
/// Skip instance body members because the outermost top-level `ClockResolutionVisitor`
254+
/// already handles it.
255+
void handle(const InstanceBodySymbol&) {}
256+
257+
private:
258+
Compilation& compilation;
259+
/// For emitting clocking block nested sequences
260+
const SignalEventControl* clkBlkSEC{};
261+
/// List for storing found inferred clocks
262+
SmallVector<ClockingEvent> inferred;
263+
/// For storing checker instance context inferred clock
264+
ClockingEvent checkerInferredClock;
265+
/// Checker instance body skipping control
266+
bool skipCheckerBody = true;
267+
};
268+
269+
/// Visitor helper for checking nested invalid nodes to ignore their parents later
270+
class MarkInvalidVisitor : public ASTVisitor<MarkInvalidVisitor, true, true> {
271+
public:
272+
void handle(const ClockingAssertionExpr& e) {
273+
if (e.clocking.bad())
274+
invalid = true;
275+
visitDefault(e);
276+
}
277+
278+
void handle(const SimpleAssertionExpr& e) {
279+
if (e.expr.bad())
280+
invalid = true;
281+
visitDefault(e);
282+
}
283+
284+
bool invalid = false;
285+
};
286+
287+
class ClockResolutionVisitor : public ASTVisitor<ClockResolutionVisitor, false, false> {
288+
public:
289+
ClockResolutionVisitor(Compilation& compilation) : compilation(compilation) {}
290+
void handle(const InstanceBodySymbol&);
291+
292+
private:
293+
Compilation& compilation;
294+
};
295+
} // namespace slang::ast::clk_res

Diff for: include/slang/ast/Compilation.h

+18
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,24 @@ struct SLANG_EXPORT CompilationOptions {
204204
/// A list of library names, in the order in which they should be searched
205205
/// when binding cells to instances.
206206
std::vector<std::string> defaultLiblist;
207+
208+
/// Disable clock resolution checking
209+
bool disableClockResolution = false;
210+
211+
// Do not check a multiclocked property with contextually inferred leading clocking event in an
212+
// always block in terms of vcs compatibility.
213+
// This is only "true" if the compat option have "vcs" value.
214+
// This was done to support this case (from 16.16 section):
215+
// ```
216+
// always @(negedge clk)
217+
// a3: assert property ($fell(c) |=> q2); // VCS doesn't emits any error on it
218+
// // illegal: multiclocked property with contextually
219+
// // inferred leading clocking event
220+
// end
221+
// ```
222+
// Despite the fact that the LRM says that for the right-hand property of the implication and
223+
// followed by property the is ignored the entire property is multi-clock.
224+
bool ignoreRightOfBOp = false;
207225
};
208226

209227
/// Information about how a bind directive applies to some definition

Diff for: include/slang/driver/Driver.h

+3
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ class SLANG_EXPORT Driver {
221221

222222
/// @}
223223

224+
/// Disable clock resolution checking
225+
std::optional<bool> disableClockResolution;
226+
224227
/// Returns true if the lintMode option is provided.
225228
bool lintMode() const;
226229
} options;

Diff for: scripts/diagnostics.txt

+10
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,15 @@ error UnknownLibrary "unknown library '{}'"
10981098
error NestedConfigMultipleTops "non-top config '{}' has more than one top cell specified"
10991099
error ConfigParamsIgnored "parameter overrides provided by config rule are ignored because the target instance is using a different nested config '{}'"
11001100
error MultipleTopDupName "more than one top module specified with the name '{}'"
1101+
error NoClockResolved "no clock could be resolved for concurrent property"
1102+
error ClockinBlockClockEvent "explicit clocking event in clocking block"
1103+
error DiffClockInClockinBlock "the property/sequence has clocking event not identical to that of the clocking block"
1104+
error NotUniqueLeadingClock "single semantic leading clock expected for top-level sequence/property expression"
1105+
error MaxPropBadClkEven "no default, inferred, or explicit leading clocking event and maximal property is not an instance"
1106+
error AssertAfterTimingControl "procedural assertion (assert, assume or cover property) is not allowed after delay or event control statement"
1107+
error SingleClockBinSeqExpected "clocking events of binary '{}' sequence/property operands doesn't agree"
1108+
error SingleClockDelaySeqExpected "clocking events of delay concatenation operands doesn't agree"
1109+
error MultiClkConcatAdmitsEmpty "differently clocked sequence shall not admit any empty match"
11011110
warning unused-def UnusedDefinition "{} definition is unused"
11021111
warning unused-net UnusedNet "unused net '{}'"
11031112
warning unused-implicit-net UnusedImplicitNet "implicit net '{}' is unused elsewhere"
@@ -1119,6 +1128,7 @@ warning unused-import UnusedImport "unused import '{}'"
11191128
warning unused-wildcard-import UnusedWildcardImport "unused wildcard import"
11201129
warning missing-top NoTopModules "no top-level modules found in design"
11211130
warning duplicate-defparam DuplicateDefparam "parameter already has a defparam override applied; ignoring this one"
1131+
note FoundClocks "found clocks:"
11221132

11231133
subsystem Meta
11241134
error TooManyErrors "too many errors emitted, stopping now [--error-limit=]"

Diff for: source/ast/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ target_sources(
4747
ASTContext.cpp
4848
ASTSerializer.cpp
4949
Bitstream.cpp
50+
ClockResolution.cpp
5051
Compilation.cpp
5152
Constraints.cpp
5253
EvalContext.cpp

0 commit comments

Comments
 (0)