Branch data Line data Source code
1 : : // -*- mode: C++; c-file-style: "cc-mode" -*-
2 : : //*************************************************************************
3 : : // DESCRIPTION: Verilator: Collect and print statistics
4 : : //
5 : : // Code available from: https://verilator.org
6 : : //
7 : : //*************************************************************************
8 : : //
9 : : // This program is free software; you can redistribute it and/or modify it
10 : : // under the terms of either the GNU Lesser General Public License Version 3
11 : : // or the Perl Artistic License Version 2.0.
12 : : // SPDX-FileCopyrightText: 2005-2026 Wilson Snyder
13 : : // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
14 : : //
15 : : //*************************************************************************
16 : :
17 : : #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
18 : :
19 : : #include "V3Assert.h"
20 : :
21 : : #include "V3AstUserAllocator.h"
22 : : #include "V3Stats.h"
23 : :
24 : : VL_DEFINE_DEBUG_FUNCTIONS;
25 : :
26 : : //######################################################################
27 : : // AssertDeFutureVisitor
28 : : // If any AstFuture, then move all non-future varrefs to be one cycle behind,
29 : : // see IEEE 1800-2023 16.9.4.
30 : :
31 : : class AssertDeFutureVisitor final : public VNVisitor {
32 : : // STATE - across all visitors
33 : : AstNodeModule* const m_modp; // Module future is underneath
34 : : const AstFuture* m_futurep; // First AstFuture found
35 : : const unsigned m_pastNum; // Prefix unique number for this module
36 : : std::map<AstVar*, AstVar*> m_delayedVars; // Old to delayed variable mapping
37 : : // STATE - for current visit position (use VL_RESTORER)
38 : : bool m_inFuture = false; // Inside a future
39 : : bool m_unsupported = false; // Printed unsupported
40 : :
41 : : // METHODS
42 : : void unsupported(AstNode* nodep) {
43 : : if (m_unsupported) return;
44 : : m_unsupported = true;
45 : : nodep->v3warn(E_UNSUPPORTED,
46 : : "Unsupported/illegal: Future value function used with expression with "
47 : : << nodep->prettyOperatorName());
48 : : }
49 : : // VISITORS
50 : : void visit(AstFuture* nodep) override {
51 : : VL_RESTORER(m_inFuture);
52 : : m_inFuture = true;
53 : : iterateChildren(nodep);
54 : : // Done with the future, this subexpression is current-time
55 : : nodep->replaceWith(nodep->exprp()->unlinkFrBack());
56 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
57 : : }
58 : : void visit(AstNodeVarRef* nodep) override {
59 : : if (nodep->user1SetOnce()) return;
60 : : if (m_inFuture || m_unsupported)
61 : : return; // Need user1 set above, don't process when Future is removed
62 : : if (nodep->access().isWriteOrRW()) {
63 : : unsupported(nodep);
64 : : return;
65 : : }
66 : : auto it = m_delayedVars.find(nodep->varp());
67 : : AstVar* outvarp;
68 : : if (it == m_delayedVars.end()) {
69 : : AstSenTree* const sentreep = m_futurep->sentreep();
70 : : AstAlways* const alwaysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS,
71 : : sentreep->cloneTree(false), nullptr};
72 : : m_modp->addStmtsp(alwaysp);
73 : : outvarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
74 : : "__Vnotfuture" + cvtToStr(m_pastNum) + "_" + nodep->name(),
75 : : nodep->dtypep()};
76 : : m_modp->addStmtsp(outvarp);
77 : : AstVarRef* varRefAWritep = new AstVarRef{nodep->fileline(), outvarp, VAccess::WRITE};
78 : : varRefAWritep->user1(true);
79 : : AstNodeVarRef* varRefAReadp = nodep->cloneTree(false);
80 : : varRefAReadp->user1(true);
81 : : AstNode* const assp = new AstAssignDly{nodep->fileline(), varRefAWritep, varRefAReadp};
82 : : alwaysp->addStmtsp(assp);
83 : : m_delayedVars.emplace(nodep->varp(), outvarp);
84 : : } else {
85 : : outvarp = it->second;
86 : : }
87 : : AstVarRef* newp = new AstVarRef{nodep->fileline(), outvarp, VAccess::READ};
88 : : newp->user1(true);
89 : : UINFO(9, "DeFuture " << nodep << " becomes " << newp);
90 : : nodep->replaceWith(newp);
91 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
92 : : }
93 : : void visit(AstNodeFTaskRef* nodep) override { unsupported(nodep); }
94 : : void visit(AstMethodCall* nodep) override { unsupported(nodep); }
95 : : void visit(AstNode* nodep) override {
96 : : if (!nodep->isPure()) unsupported(nodep);
97 : : iterateChildren(nodep);
98 : : }
99 : :
100 : : public:
101 : : // CONSTRUCTORS
102 : : explicit AssertDeFutureVisitor(AstNode* nodep, AstNodeModule* modp, unsigned pastNum)
103 : : : m_modp{modp}
104 : : , m_pastNum{pastNum} {
105 : : // See if any Future before we process
106 : : if (nodep->forall([&](const AstFuture* futurep) -> bool {
107 : : m_futurep = futurep;
108 : : return false;
109 : : }))
110 : : return;
111 : : // UINFOTREE(9, nodep, "", "defuture-in");
112 : : visit(nodep); // Nodep may get deleted
113 : : // UINFOTREE(9, nodep, "", "defuture-ou");
114 : : }
115 : : ~AssertDeFutureVisitor() = default;
116 : : };
117 : :
118 : : //######################################################################
119 : : // AssertVisitor
120 : :
121 : : class AssertVisitor final : public VNVisitor {
122 : : // CONSTANTS
123 : : static constexpr uint8_t ALL_ASSERT_TYPES
124 : : = std::numeric_limits<std::underlying_type<VAssertType::en>::type>::max();
125 : :
126 : : // NODE STATE/TYPES
127 : : // Cleared on netlist
128 : : // AstNode::user1() -> bool. True if processed
129 : : // AstAlways::user2p() -> std::vector<AstVar*>. Delayed variables via 'm_delayed'
130 : : // AstNodeVarRef::user2() -> bool. True if shouldn't be sampled
131 : : const VNUser1InUse m_user1InUse;
132 : : const VNUser2InUse m_user2InUse;
133 : : AstUser2Allocator<AstAlways, std::vector<AstVar*>> m_delayed;
134 : :
135 : : // STATE
136 : : AstNodeModule* m_modp = nullptr; // Last module
137 : : const AstNode* m_beginp = nullptr; // Last AstBegin/AstGenBlock
138 : : unsigned m_monitorNum = 0; // Global $monitor numbering (not per module)
139 : : AstVar* m_monitorNumVarp = nullptr; // $monitor number variable
140 : : AstVar* m_monitorOffVarp = nullptr; // $monitoroff variable
141 : : unsigned m_modPastNum = 0; // Module past numbering
142 : : unsigned m_modStrobeNum = 0; // Module $strobe numbering
143 : : AstNodeProcedure* m_procedurep = nullptr; // Current procedure
144 : : VDouble0 m_statCover; // Statistic tracking
145 : : VDouble0 m_statAsNotImm; // Statistic tracking
146 : : VDouble0 m_statAsImm; // Statistic tracking
147 : : VDouble0 m_statAsFull; // Statistic tracking
148 : : VDouble0 m_statPastVars; // Statistic tracking
149 : : bool m_inSampled = false; // True inside a sampled expression
150 : : bool m_inRestrict = false; // True inside restrict assertion
151 : : AstNode* m_passsp = nullptr; // Current pass statement
152 : : AstNode* m_failsp = nullptr; // Current fail statement
153 : : AstFinal* m_finalp = nullptr; // Current final block
154 : : // Map from (expression, senTree) to AstAlways that computes delayed values of the expression
155 : : std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
156 : : m_modExpr2Sen2DelayedAlwaysp;
157 : :
158 : : // METHODS
159 : : static AstNodeExpr* assertOnCond(FileLine* fl, VAssertType type,
160 : : VAssertDirectiveType directiveType) {
161 : : // cppcheck-suppress missingReturn
162 : : switch (directiveType) {
163 : : case VAssertDirectiveType::INTRINSIC: return new AstConst{fl, AstConst::BitTrue{}};
164 : : case VAssertDirectiveType::VIOLATION_CASE: {
165 : : if (v3Global.opt.assertCase()) {
166 : : return new AstCExpr{fl, AstCExpr::Pure{}, "vlSymsp->_vm_contextp__->assertOn()",
167 : : 1};
168 : : }
169 : : // If assertions are off, have constant propagation rip them out later
170 : : // This allows syntax errors and such to be detected normally.
171 : : return new AstConst{fl, AstConst::BitFalse{}};
172 : : }
173 : : case VAssertDirectiveType::ASSERT:
174 : : case VAssertDirectiveType::COVER:
175 : : case VAssertDirectiveType::ASSUME: {
176 : : if (v3Global.opt.assertOn()) {
177 : : return new AstCExpr{fl, AstCExpr::Pure{},
178 : : "vlSymsp->_vm_contextp__->assertOnGet("s + std::to_string(type)
179 : : + ", "s + std::to_string(directiveType) + ")"s,
180 : : 1};
181 : : }
182 : : return new AstConst{fl, AstConst::BitFalse{}};
183 : : }
184 : : case VAssertDirectiveType::INTERNAL:
185 : : case VAssertDirectiveType::VIOLATION_IF:
186 : : case VAssertDirectiveType::RESTRICT: {
187 : : if (v3Global.opt.assertOn()) {
188 : : return new AstCExpr{fl, AstCExpr::Pure{}, "vlSymsp->_vm_contextp__->assertOn()",
189 : : 1};
190 : : }
191 : : return new AstConst{fl, AstConst::BitFalse{}};
192 : : }
193 : : }
194 : : VL_UNREACHABLE;
195 : : }
196 : : string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message,
197 : : VDisplayType severity) {
198 : : if (severity == VDisplayType::DT_ERROR || severity == VDisplayType::DT_FATAL) {
199 : : return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
200 : : + cvtToStr(nodep->fileline()->lineno()) + ": Assertion failed in %m"
201 : : + ((message != "") ? ": " : "") + message + "\n");
202 : : }
203 : : return ("[%0t] "s + prefix + ": " + nodep->fileline()->filebasename() + ":"
204 : : + cvtToStr(nodep->fileline()->lineno()) + ": %m" + ((message != "") ? ": " : "")
205 : : + message + "\n");
206 : : }
207 : : static bool resolveAssertType(AstAssertCtl* nodep) {
208 : : if (!nodep->assertTypesp()) {
209 : : nodep->ctlAssertTypes(VAssertType{ALL_ASSERT_TYPES});
210 : : return true;
211 : : }
212 : : if (const AstConst* const assertTypesp = VN_CAST(nodep->assertTypesp(), Const)) {
213 : : nodep->ctlAssertTypes(VAssertType{assertTypesp->toSInt()});
214 : : return true;
215 : : }
216 : : return false;
217 : : }
218 : : static bool resolveControlType(AstAssertCtl* nodep) {
219 : : if (const AstConst* const constp = VN_CAST(nodep->controlTypep(), Const)) {
220 : : nodep->ctlType(constp->toSInt());
221 : : return true;
222 : : }
223 : : return false;
224 : : }
225 : : static bool resolveDirectiveType(AstAssertCtl* nodep) {
226 : : if (!nodep->directiveTypesp()) {
227 : : nodep->ctlDirectiveTypes(VAssertDirectiveType::ASSERT | VAssertDirectiveType::ASSUME
228 : : | VAssertDirectiveType::COVER);
229 : : return true;
230 : : }
231 : : if (const AstConst* const directiveTypesp = VN_CAST(nodep->directiveTypesp(), Const)) {
232 : : nodep->ctlDirectiveTypes(VAssertDirectiveType{directiveTypesp->toSInt()});
233 : : return true;
234 : : }
235 : : return false;
236 : : }
237 : : void replaceDisplay(AstDisplay* nodep, const string& prefix) {
238 : : nodep->fmtp()->text(
239 : : assertDisplayMessage(nodep, prefix, nodep->fmtp()->text(), nodep->displayType()));
240 : : nodep->displayType(VDisplayType::DT_WRITE);
241 : : // cppcheck-suppress nullPointer
242 : : AstNodeExpr* const timenewp = new AstTime{nodep->fileline(), m_modp->timeunit()};
243 : : if (AstNodeExpr* const timesp = nodep->fmtp()->exprsp()) {
244 : : timesp->unlinkFrBackWithNext();
245 : : timenewp->addNext(timesp);
246 : : }
247 : : nodep->fmtp()->addExprsp(timenewp);
248 : : if (!nodep->fmtp()->scopeNamep() && nodep->fmtp()->formatScopeTracking()) {
249 : : nodep->fmtp()->scopeNamep(new AstScopeName{nodep->fileline(), true});
250 : : }
251 : : }
252 : : AstSampled* newSampledExpr(AstNodeExpr* nodep) {
253 : : AstSampled* const sampledp = new AstSampled{nodep->fileline(), nodep};
254 : : sampledp->dtypeFrom(nodep);
255 : : return sampledp;
256 : : }
257 : : AstVarRef* newMonitorNumVarRefp(const AstNode* nodep, VAccess access) {
258 : : if (!m_monitorNumVarp) {
259 : : m_monitorNumVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorNum",
260 : : nodep->findUInt64DType()};
261 : : v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorNumVarp);
262 : : }
263 : : AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorNumVarp, access};
264 : : varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
265 : : return varrefp;
266 : : }
267 : : AstVarRef* newMonitorOffVarRefp(const AstNode* nodep, VAccess access) {
268 : : if (!m_monitorOffVarp) {
269 : : m_monitorOffVarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, "__VmonitorOff",
270 : : nodep->findBitDType()};
271 : : v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(m_monitorOffVarp);
272 : : }
273 : : AstVarRef* const varrefp = new AstVarRef{nodep->fileline(), m_monitorOffVarp, access};
274 : : varrefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
275 : : return varrefp;
276 : : }
277 : : static AstNodeStmt* newIfAssertOn(AstNode* bodyp, VAssertDirectiveType directiveType,
278 : : VAssertType type = VAssertType::INTERNAL) {
279 : : // Add a internal if to check assertions are on.
280 : : // Don't make this a AND term, as it's unlikely to need to test this.
281 : : FileLine* const fl = bodyp->fileline();
282 : :
283 : : AstNodeExpr* const condp = assertOnCond(fl, type, directiveType);
284 : : AstNodeIf* const newp = new AstIf{fl, condp, bodyp};
285 : : newp->isBoundsCheck(true); // To avoid LATCH warning
286 : : newp->user1(true); // Don't assert/cover this if
287 : : return newp;
288 : : }
289 : :
290 : : static AstIf* assertCond(const AstNodeCoverOrAssert* nodep, AstNodeExpr* propp,
291 : : AstNode* passsp, AstNode* failsp) {
292 : : AstIf* const ifp = new AstIf{nodep->fileline(), propp, passsp, failsp};
293 : : // It's more LIKELY that we'll take the nullptr if clause
294 : : // than the sim-killing else clause:
295 : : ifp->branchPred(VBranchPred::BP_LIKELY);
296 : : ifp->isBoundsCheck(true); // To avoid LATCH warning
297 : : return ifp;
298 : : }
299 : :
300 : : AstNode* assertBody(const AstNodeCoverOrAssert* nodep, AstNode* propp, AstNode* passsp,
301 : : AstNode* failsp) {
302 : : AstNode* bodyp = nullptr;
303 : : if (AstPExpr* const pexprp = VN_CAST(propp, PExpr)) {
304 : : AstFork* const forkp = new AstFork{nodep->fileline(), VJoinType::JOIN_NONE};
305 : : forkp->addForksp(pexprp->bodyp()->unlinkFrBack());
306 [ + + ]: 27 : if (AstNodeStmt* const finalp = pexprp->finalp()) {
307 [ + + ]: 7 : if (!m_finalp) {
308 : 3 : m_finalp = new AstFinal{m_modp->fileline(), finalp->unlinkFrBack()};
309 : 3 : m_modp->addStmtsp(m_finalp);
310 : : } else {
311 : 4 : m_finalp->addStmtsp(finalp->unlinkFrBack());
312 : : }
313 : : }
314 : : VL_DO_DANGLING2(pushDeletep(pexprp), pexprp, propp);
315 : : bodyp = forkp;
316 : : } else {
317 : : bodyp = assertCond(nodep, VN_AS(propp, NodeExpr), passsp, failsp);
318 : : }
319 : : return newIfAssertOn(bodyp, nodep->directive(), nodep->userType());
320 : : }
321 : :
322 : : AstNodeStmt* newFireAssertUnchecked(const AstNodeStmt* nodep, const string& message,
323 : : AstNodeExpr* exprsp = nullptr) {
324 : : // Like newFireAssert() but omits the asserts-on check
325 : : AstDisplay* const dispp
326 : : = new AstDisplay{nodep->fileline(), VDisplayType::DT_ERROR, message, nullptr, nullptr};
327 : : dispp->fmtp()->timeunit(m_modp->timeunit());
328 : : AstNodeStmt* const bodysp = dispp;
329 : : replaceDisplay(dispp, "%%Error"); // Convert to standard DISPLAY format
330 : : if (exprsp) dispp->fmtp()->exprsp()->addNext(exprsp);
331 : : if (v3Global.opt.stopFail()) bodysp->addNext(new AstStop{nodep->fileline(), false});
332 : : return bodysp;
333 : : }
334 : :
335 : : AstNodeStmt* newFireAssert(const AstNodeStmt* nodep, VAssertDirectiveType directiveType,
336 : : VAssertType assertType, const string& message,
337 : : AstNodeExpr* exprsp = nullptr) {
338 : : AstNodeStmt* bodysp = newFireAssertUnchecked(nodep, message, exprsp);
339 : : bodysp = newIfAssertOn(bodysp, directiveType, assertType);
340 : : return bodysp;
341 : : }
342 : :
343 : : AstVar* createDelayedVar(const std::string& name, AstAlways* alwaysp, AstNodeExpr* exprp) {
344 : : FileLine* const flp = exprp->fileline();
345 : : AstVar* const varp = new AstVar{flp, VVarType::MODULETEMP, name, exprp->dtypep()};
346 : : // TODO: this lifetime seems nonsene (can't have NBAs to automatics), but is as before
347 : : varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
348 : : m_modp->addStmtsp(varp);
349 : : ++m_statPastVars;
350 : : // Actually set the delayed value
351 : : AstNodeExpr* const lhsp = new AstVarRef{flp, varp, VAccess::WRITE};
352 : : AstAssignDly* const assignp = new AstAssignDly{flp, lhsp, exprp};
353 : : if (!alwaysp->stmtsp()) {
354 : : alwaysp->addStmtsp(assignp);
355 : : } else {
356 : : alwaysp->stmtsp()->addHereThisAsNext(assignp);
357 : : }
358 : : return varp;
359 : : }
360 : :
361 : : AstAlways* getDelayedAlways(AstNodeExpr* exprp, AstSenTree* senTreep) {
362 : : AstAlways*& alwayspr = m_modExpr2Sen2DelayedAlwaysp[*exprp][*senTreep];
363 : : if (!alwayspr) {
364 : : FileLine* const flp = exprp->fileline();
365 : : // Create the always block that computes the delayed values
366 : : alwayspr = new AstAlways{flp, VAlwaysKwd::ALWAYS, senTreep, nullptr};
367 : : m_modp->addStmtsp(alwayspr);
368 : : // Create the once-delayed variable
369 : : const std::string name = "_Vpast_" + cvtToStr(m_modPastNum++) + "_1";
370 : : AstVar* const varp = createDelayedVar(name, alwayspr, exprp);
371 : : // Add it to delayed variable vector
372 : : m_delayed(alwayspr).emplace_back(varp);
373 : : } else {
374 : : // Reusing exiting, not needed
375 : : VL_DO_DANGLING(pushDeletep(exprp), exprp);
376 : : VL_DO_DANGLING(pushDeletep(senTreep), senTreep);
377 : : }
378 : : return alwayspr;
379 : : }
380 : :
381 : : AstNodeExpr* getPastValue(AstNodeExpr* exprp, AstSenTree* senTreep, uint32_t ticks) {
382 : : UASSERT_OBJ(ticks > 0, exprp, "Delay must be > 0");
383 : : AstAlways* const alwaysp = getDelayedAlways(exprp, senTreep);
384 : : std::vector<AstVar*>& delayedr = m_delayed(alwaysp);
385 : : // Ensure the required delay exists
386 : : while (delayedr.size() < ticks) {
387 : : AstVar* const firstp = delayedr.front();
388 : : FileLine* const flp = firstp->fileline();
389 : : // Create once more delayed value
390 : : std::string name = firstp->name();
391 : : name.resize(name.size() - 1);
392 : : name += std::to_string(delayedr.size() + 1);
393 : : AstNodeExpr* const prevp = new AstVarRef{flp, delayedr.back(), VAccess::READ};
394 : : AstVar* const varp = createDelayedVar(name, alwaysp, prevp);
395 : : // Add it to delayed variable vector
396 : : delayedr.emplace_back(varp);
397 : : }
398 : : // Return a reference to the appropriately delayed variable
399 : : return new AstVarRef{exprp->fileline(), delayedr.at(ticks - 1), VAccess::READ};
400 : : }
401 : :
402 : : void visitAssertionIterate(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
403 : : if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
404 : :
405 : : { AssertDeFutureVisitor{nodep->propp(), m_modp, m_modPastNum++}; }
406 : :
407 : : iterateAndNextNull(nodep->sentreep());
408 : : if (AstAssert* const assertp = VN_CAST(nodep, Assert)) {
409 : : iterateAndNextNull(assertp->failsp());
410 : : } else if (AstAssertIntrinsic* const assertp = VN_CAST(nodep, AssertIntrinsic)) {
411 : : iterateAndNextNull(assertp->failsp());
412 : : } else if (AstCover* const coverp = VN_CAST(nodep, Cover)) {
413 : : iterateAndNextNull(coverp->coverincsp());
414 : : } else if (!VN_IS(nodep, Restrict)) {
415 : : nodep->v3fatalSrc("Unhandled assert type");
416 : : }
417 : : iterateAndNextNull(nodep->passsp());
418 : : AstSenTree* const sentreep = nodep->sentreep();
419 : : if (nodep->immediate()) {
420 : : UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensitivity");
421 : : } else {
422 : : UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensitivity");
423 : : if (m_procedurep) {
424 : : if (!nodep->senFromAlways()) {
425 : : // To support this need queue of asserts to activate
426 : : nodep->v3warn(E_UNSUPPORTED,
427 : : "Unsupported: Procedural concurrent assertion with"
428 : : " clocking event inside always (IEEE 1800-2023 16.14.6)");
429 : : }
430 : : // Change type to concurrent and relink after process
431 : : nodep->immediate(false);
432 : : static_cast<AstNode*>(m_procedurep)->addNext(nodep->unlinkFrBack());
433 : : return; // Later iterate will pick up
434 : : }
435 : : sentreep->unlinkFrBack();
436 : : }
437 : : //
438 : : const string& message = nodep->name();
439 : : AstNode* passsp = nodep->passsp();
440 : : if (passsp) passsp->unlinkFrBackWithNext();
441 : : if (failsp) failsp->unlinkFrBackWithNext();
442 : :
443 : : bool selfDestruct = false;
444 : : if (const AstCover* const snodep = VN_CAST(nodep, Cover)) {
445 : : ++m_statCover;
446 : : if (!v3Global.opt.coverageUser()) {
447 : : selfDestruct = true;
448 : : } else {
449 : : // V3Coverage assigned us a bucket to increment.
450 : : AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc);
451 : : UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion");
452 : : covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace
453 : : if (message != "") covincp->declp()->comment(message);
454 : : if (passsp) {
455 : : passsp = AstNode::addNext<AstNode, AstNode>(covincp, passsp);
456 : : } else {
457 : : passsp = covincp;
458 : : }
459 : : }
460 : : } else if (VN_IS(nodep, Assert) || VN_IS(nodep, AssertIntrinsic)) {
461 : : if (nodep->immediate()) {
462 : : ++m_statAsImm;
463 : : } else {
464 : : ++m_statAsNotImm;
465 : : }
466 : : if (!passsp && !failsp)
467 : : failsp = newFireAssertUnchecked(
468 : : nodep, VN_IS(nodep, AssertIntrinsic) ? "'$cast' failed." : "'assert' failed.");
469 : : } else {
470 : : nodep->v3fatalSrc("Unknown node type");
471 : : }
472 : :
473 : : VL_RESTORER(m_passsp);
474 : : VL_RESTORER(m_failsp);
475 : : m_passsp = passsp;
476 : : m_failsp = failsp;
477 : : iterate(nodep->propp());
478 : :
479 : : AstNode* propExprp;
480 : : AstNodeExpr* disablep = nullptr;
481 : : if (AstPropSpec* const specp = VN_CAST(nodep->propp(), PropSpec)) {
482 : : propExprp = specp->propp()->unlinkFrBack();
483 : : if (specp->disablep()) disablep = specp->disablep()->unlinkFrBack();
484 : : } else {
485 : : propExprp = nodep->propp()->unlinkFrBack();
486 : : }
487 : : AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp);
488 : : if (disablep) {
489 : : bodysp
490 : : = new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), disablep}, bodysp};
491 : : }
492 : : if (sentreep) {
493 : : bodysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, sentreep, bodysp};
494 : : }
495 : :
496 : : if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp);
497 : : if (failsp && !failsp->backp()) VL_DO_DANGLING(pushDeletep(failsp), failsp);
498 : :
499 : : // Install it
500 : : if (selfDestruct) {
501 : : // Delete it after making the tree. This way we can tell the user
502 : : // if it wasn't constructed nicely or has other errors without needing --coverage.
503 : : VL_DO_DANGLING(bodysp->deleteTree(), bodysp);
504 : : nodep->unlinkFrBack();
505 : : } else {
506 : : nodep->replaceWith(bodysp);
507 : : }
508 : : // Bye
509 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
510 : : }
511 : :
512 : : // VISITORS
513 : : void visit(AstIf* nodep) override {
514 : : if (nodep->user1SetOnce()) return;
515 : : if (nodep->uniquePragma() || nodep->unique0Pragma()) {
516 : : const AstNodeIf* ifp = nodep;
517 : : AstNodeExpr* propp = nullptr;
518 : : bool hasDefaultElse = false;
519 : : do {
520 : : // If this statement ends with 'else if', then nextIf will point to the
521 : : // nextIf statement. Otherwise it will be null.
522 : : const AstNodeIf* const nextifp = dynamic_cast<AstNodeIf*>(ifp->elsesp());
523 : : iterateAndNextNull(ifp->condp());
524 : :
525 : : // Recurse into the true case.
526 : : iterateAndNextNull(ifp->thensp());
527 : :
528 : : // If the last else is not an else if, recurse into that too.
529 : : if (ifp->elsesp() && !nextifp) { //
530 : : iterateAndNextNull(ifp->elsesp());
531 : : }
532 : :
533 : : // Build a bitmask of the true predicates
534 : : AstNodeExpr* const predp = ifp->condp()->cloneTreePure(false);
535 : : if (propp) {
536 : : propp = new AstConcat{nodep->fileline(), predp, propp};
537 : : } else {
538 : : propp = predp;
539 : : }
540 : :
541 : : // Record if this ends with an 'else' that does not have an if
542 : : if (ifp->elsesp() && !nextifp) hasDefaultElse = true;
543 : :
544 : : ifp = nextifp;
545 : : } while (ifp);
546 : :
547 : : AstIf* const newifp = nodep->cloneTree(false);
548 : : const bool allow_none = nodep->unique0Pragma();
549 : :
550 : : // Empty case means no property
551 : : if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
552 : :
553 : : // Note: if this ends with an 'else', then we don't need to validate that one of the
554 : : // predicates evaluates to true.
555 : : AstNodeExpr* const ohot
556 : : = ((allow_none || hasDefaultElse)
557 : : ? static_cast<AstNodeExpr*>(new AstOneHot0{nodep->fileline(), propp})
558 : : : static_cast<AstNodeExpr*>(new AstOneHot{nodep->fileline(), propp}));
559 : : const VAssertType assertType
560 : : = nodep->uniquePragma() ? VAssertType::UNIQUE : VAssertType::UNIQUE0;
561 : : AstIf* const checkifp
562 : : = new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot},
563 : : newFireAssert(nodep, VAssertDirectiveType::VIOLATION_IF, assertType,
564 : : "'unique if' statement violated"),
565 : : newifp};
566 : : checkifp->isBoundsCheck(true); // To avoid LATCH warning
567 : : checkifp->branchPred(VBranchPred::BP_UNLIKELY);
568 : : nodep->replaceWith(checkifp);
569 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
570 : : } else {
571 : : iterateChildren(nodep);
572 : : }
573 : : }
574 : :
575 : : //========== Case assertions
576 : : void visit(AstCase* nodep) override {
577 : : iterateChildren(nodep);
578 : : if (!nodep->user1SetOnce()) {
579 : : bool has_default = false;
580 : : for (AstCaseItem* itemp = nodep->itemsp(); itemp;
581 : : itemp = VN_AS(itemp->nextp(), CaseItem)) {
582 : : if (itemp->isDefault()) has_default = true;
583 : : }
584 : : const AstNodeDType* exprDtypep = nodep->exprp()->dtypep()->skipRefp();
585 : :
586 : : VAssertType assertType = VAssertType::INTERNAL;
587 : : if (nodep->priorityPragma()) {
588 : : assertType = VAssertType::PRIORITY;
589 : : } else if (nodep->uniquePragma()) {
590 : : assertType = VAssertType::UNIQUE;
591 : : } else if (nodep->unique0Pragma()) {
592 : : assertType = VAssertType::UNIQUE0;
593 : : }
594 : :
595 : : string valFmt;
596 : : if (exprDtypep->isIntegralOrPacked())
597 : : valFmt = " for '" + cvtToStr(exprDtypep->widthMin()) + "'h%X'";
598 : : if (nodep->fullPragma() || nodep->priorityPragma()) {
599 : : // Need to add a default if there isn't one already
600 : : ++m_statAsFull;
601 : : if (!has_default) {
602 : : nodep->addItemsp(new AstCaseItem{
603 : : nodep->fileline(), nullptr /*DEFAULT*/,
604 : : newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
605 : : nodep->pragmaString() + ", but non-match found" + valFmt,
606 : : valFmt.empty() ? nullptr
607 : : : nodep->exprp()->cloneTreePure(false))});
608 : : }
609 : : }
610 : : if (nodep->parallelPragma() || nodep->uniquePragma() || nodep->unique0Pragma()) {
611 : : // Need to check that one, and only one of the case items match at any moment
612 : : // If there's a default, we allow none to match, else exactly one must match
613 : : ++m_statAsFull;
614 : : if (!has_default && !nodep->itemsp()) {
615 : : // Not parallel, but harmlessly so.
616 : : } else {
617 : : AstNodeExpr* propp = nullptr;
618 : : for (AstCaseItem* itemp = nodep->itemsp(); itemp;
619 : : itemp = VN_AS(itemp->nextp(), CaseItem)) {
620 : : AstNodeExpr* itembitp = nullptr;
621 : : for (AstNodeExpr* icondp = itemp->condsp(); icondp;
622 : : icondp = VN_AS(icondp->nextp(), NodeExpr)) {
623 : : AstNodeExpr* onep;
624 : : if (AstInsideRange* const rcondp = VN_CAST(icondp, InsideRange)) {
625 : : onep = rcondp->newAndFromInside(
626 : : nodep->exprp()->cloneTreePure(true),
627 : : rcondp->lhsp()->cloneTreePure(true),
628 : : rcondp->rhsp()->cloneTreePure(true));
629 : : } else if (nodep->casex() || nodep->casez() || nodep->caseInside()) {
630 : : onep = AstEqWild::newTyped(itemp->fileline(),
631 : : nodep->exprp()->cloneTreePure(false),
632 : : icondp->cloneTreePure(false));
633 : : } else {
634 : : onep = AstEq::newTyped(icondp->fileline(),
635 : : nodep->exprp()->cloneTreePure(false),
636 : : icondp->cloneTreePure(false));
637 : : }
638 : : // OR together all conditions within the same case item
639 : : if (onep) {
640 : : if (itembitp) {
641 : : itembitp = new AstOr{icondp->fileline(), onep, itembitp};
642 : : } else {
643 : : itembitp = onep;
644 : : }
645 : : }
646 : : }
647 : : if (itembitp) {
648 : : if (propp) {
649 : : propp = new AstConcat{itemp->fileline(), itembitp, propp};
650 : : } else {
651 : : propp = itembitp;
652 : : }
653 : : }
654 : : }
655 : : // Empty case means no property
656 : : if (!propp) propp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
657 : : const bool allow_none = has_default || nodep->unique0Pragma();
658 : : // The following assertion looks as below.
659 : : // if (!$onehot(propp)) begin
660 : : // if (propp == '0) begin if (!allow_none) $error("none match"); end
661 : : // else $error("multiple match");
662 : : // end
663 : : AstNodeExpr* const ohot = new AstOneHot{nodep->fileline(), propp};
664 : : AstConst* const zero = new AstConst{
665 : : nodep->fileline(), AstConst::WidthedValue{}, propp->width(), 0};
666 : : AstIf* const ohotIfp
667 : : = new AstIf{nodep->fileline(), new AstLogNot{nodep->fileline(), ohot}};
668 : : AstIf* const zeroIfp = new AstIf{
669 : : nodep->fileline(),
670 : : new AstEq{nodep->fileline(), propp->cloneTreePure(false), zero}};
671 : : AstNodeExpr* const exprp = nodep->exprp();
672 : : const string pragmaStr = nodep->pragmaString();
673 : : if (!allow_none)
674 : : zeroIfp->addThensp(
675 : : newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
676 : : pragmaStr + ", but none matched" + valFmt,
677 : : valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
678 : : zeroIfp->addElsesp(
679 : : newFireAssert(nodep, VAssertDirectiveType::VIOLATION_CASE, assertType,
680 : : pragmaStr + ", but multiple matches found" + valFmt,
681 : : valFmt.empty() ? nullptr : exprp->cloneTreePure(false)));
682 : : ohotIfp->addThensp(zeroIfp);
683 : : ohotIfp->isBoundsCheck(true); // To avoid LATCH warning
684 : : ohotIfp->branchPred(VBranchPred::BP_UNLIKELY);
685 : : nodep->addNotParallelp(ohotIfp);
686 : : }
687 : : }
688 : : }
689 : : }
690 : :
691 : : void visit(AstFuture* nodep) override {
692 : : nodep->v3error("Future sampled value function called outside property or sequence "
693 : : "expression (IEEE 16.9.4)");
694 : : nodep->replaceWith(new AstConst{nodep->fileline(), 0});
695 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
696 : : }
697 : :
698 : : //========== Past
699 : : void visit(AstPast* nodep) override {
700 : : iterateChildren(nodep);
701 : : uint32_t ticks = 1;
702 : : if (nodep->ticksp()) {
703 : : UASSERT_OBJ(VN_IS(nodep->ticksp(), Const), nodep,
704 : : "Expected constant ticks, checked in V3Width");
705 : : ticks = VN_AS(nodep->ticksp(), Const)->toUInt();
706 : : }
707 : : UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
708 : : AstNodeExpr* const exprp = newSampledExpr(nodep->exprp()->unlinkFrBack());
709 : : AstNodeExpr* inp = getPastValue(exprp, nodep->sentreep()->unlinkFrBack(), ticks);
710 : : nodep->replaceWith(inp);
711 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
712 : : }
713 : :
714 : : //========== Move $sampled down to read-only variables
715 : : void visit(AstSampled* nodep) override {
716 : : if (nodep->user1()) return;
717 : : VL_RESTORER(m_inSampled);
718 : : {
719 : : m_inSampled = true;
720 : : iterateChildren(nodep);
721 : : }
722 : : if (nodep->exprp()) {
723 : : nodep->replaceWith(nodep->exprp()->unlinkFrBack());
724 : : } else {
725 : : nodep->unlinkFrBack();
726 : : }
727 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
728 : : }
729 : : void visit(AstNodeVarRef* nodep) override {
730 : : if (m_inSampled && !nodep->user2() && !nodep->varp()->isTemp()) {
731 : : if (!nodep->access().isReadOnly()) {
732 : : nodep->v3warn(E_UNSUPPORTED,
733 : : "Unsupported: Write to variable in sampled expression");
734 : : } else {
735 : : VNRelinker relinkHandle;
736 : : nodep->unlinkFrBack(&relinkHandle);
737 : : AstSampled* const newp = newSampledExpr(nodep);
738 : : relinkHandle.relink(newp);
739 : : newp->user1(1);
740 : : v3Global.setHasSampled();
741 : : }
742 : : }
743 : : }
744 : : // Don't sample sensitivities
745 : : void visit(AstSenItem* nodep) override {
746 : : VL_RESTORER(m_inSampled);
747 : : m_inSampled = false;
748 : : iterateChildren(nodep);
749 : : }
750 : : void visit(AstPExprClause* nodep) override {
751 : : AstNode* stmtsp = nullptr;
752 : : if (nodep->pass() && m_passsp) {
753 : : // Cover adds COVERINC by AstNode::addNext, thus need to clone next too.
754 : : stmtsp = m_passsp->cloneTree(true);
755 : : } else if (!nodep->pass() && m_failsp) {
756 : : stmtsp = m_failsp->cloneTree(true);
757 : : }
758 : : if (stmtsp) {
759 : : stmtsp->foreachAndNext([](AstNodeVarRef* const refp) {
760 : : // References inside action blocks shouldn't be implicitly sampled
761 : : // m_passsp/m_failsp have been already visited once and refs explicitly sampled
762 : : // are handled already
763 : : refp->user2(1);
764 : : });
765 : : nodep->replaceWith(stmtsp);
766 : : } else {
767 : : nodep->unlinkFrBack();
768 : : }
769 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
770 : : }
771 : : void visit(AstPExpr* nodep) override {
772 : : if (m_inRestrict) {
773 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
774 : : } else {
775 : : iterateChildren(nodep);
776 : : }
777 : : }
778 : :
779 : : //========== Statements
780 : : void visit(AstDisplay* nodep) override {
781 : : iterateChildren(nodep);
782 : : // Replace the special types with standard text
783 : : if (nodep->displayType() == VDisplayType::DT_INFO) {
784 : : replaceDisplay(nodep, "-Info");
785 : : } else if (nodep->displayType() == VDisplayType::DT_WARNING) {
786 : : replaceDisplay(nodep, "%%Warning");
787 : : } else if (nodep->displayType() == VDisplayType::DT_ERROR) {
788 : : replaceDisplay(nodep, "%%Error");
789 : : } else if (nodep->displayType() == VDisplayType::DT_FATAL) {
790 : : replaceDisplay(nodep, "%%Fatal");
791 : : } else if (nodep->displayType() == VDisplayType::DT_MONITOR) {
792 : : nodep->displayType(VDisplayType::DT_DISPLAY);
793 : : FileLine* const fl = nodep->fileline();
794 : :
795 : : AstSenItem* monSenItemsp = nullptr;
796 : : if (AstNode* const monExprsp = nodep->fmtp()->exprsp()) {
797 : : monExprsp->foreachAndNext([&](AstVarRef* varrefp) {
798 : : AstSenItem* const senItemp
799 : : = new AstSenItem{fl, VEdgeType::ET_CHANGED,
800 : : // Clone so get VarRef or VarXRef as needed
801 : : varrefp->cloneTree(false)};
802 : : if (!monSenItemsp) {
803 : : monSenItemsp = senItemp;
804 : : } else {
805 : : monSenItemsp->addNext(senItemp);
806 : : }
807 : : });
808 : : }
809 : :
810 : : AstSenTree* const monSenTree = new AstSenTree{fl, monSenItemsp};
811 : : const auto monNum = ++m_monitorNum;
812 : : // Where $monitor was we do "__VmonitorNum = N;"
813 : : AstAssign* const newsetp = new AstAssign{
814 : : fl, newMonitorNumVarRefp(nodep, VAccess::WRITE), new AstConst{fl, monNum}};
815 : : nodep->replaceWith(newsetp);
816 : : // Add "always_comb if (__VmonitorOn && __VmonitorNum==N) $display(...);"
817 : : AstNode* const stmtsp = nodep;
818 : : AstIf* const ifp = new AstIf{
819 : : fl,
820 : : new AstLogAnd{fl, new AstLogNot{fl, newMonitorOffVarRefp(nodep, VAccess::READ)},
821 : : new AstEq{fl, new AstConst{fl, monNum},
822 : : newMonitorNumVarRefp(nodep, VAccess::READ)}},
823 : : stmtsp};
824 : : ifp->isBoundsCheck(true); // To avoid LATCH warning
825 : : ifp->branchPred(VBranchPred::BP_UNLIKELY);
826 : : AstNode* const newp = new AstAlways{fl, VAlwaysKwd::ALWAYS, monSenTree, ifp};
827 : : m_modp->addStmtsp(newp);
828 : : } else if (nodep->displayType() == VDisplayType::DT_STROBE) {
829 : : nodep->displayType(VDisplayType::DT_DISPLAY);
830 : : // Need one-shot
831 : : FileLine* const fl = nodep->fileline();
832 : : AstVar* const varp
833 : : = new AstVar{fl, VVarType::MODULETEMP, "__Vstrobe" + cvtToStr(m_modStrobeNum++),
834 : : nodep->findBitDType()};
835 : : m_modp->addStmtsp(varp);
836 : : // Where $strobe was we do "__Vstrobe = '1;"
837 : : AstAssign* const newsetp = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
838 : : new AstConst{fl, AstConst::BitTrue{}}};
839 : : nodep->replaceWith(newsetp);
840 : : // Add "always_comb if (__Vstrobe) begin $display(...); __Vstrobe = '0; end"
841 : : AstNode* const stmtsp = nodep;
842 : : AstIf* const ifp = new AstIf{fl, new AstVarRef{fl, varp, VAccess::READ}, stmtsp};
843 : : ifp->isBoundsCheck(true); // To avoid LATCH warning
844 : : ifp->branchPred(VBranchPred::BP_UNLIKELY);
845 : : AstNode* const newp = new AstAlwaysPostponed{fl, ifp};
846 : : stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
847 : : new AstConst{fl, AstConst::BitFalse{}}});
848 : : m_modp->addStmtsp(newp);
849 : : }
850 : : }
851 : : void visit(AstMonitorOff* nodep) override {
852 : : AstAssign* const newp
853 : : = new AstAssign{nodep->fileline(), newMonitorOffVarRefp(nodep, VAccess::WRITE),
854 : : new AstConst{nodep->fileline(), AstConst::BitTrue{}, nodep->off()}};
855 : : nodep->replaceWith(newp);
856 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
857 : : }
858 : : void visit(AstAssert* nodep) override { //
859 : : visitAssertionIterate(nodep, nodep->failsp());
860 : : }
861 : : void visit(AstAssertCtl* nodep) override {
862 : : if (VN_IS(m_modp, Class) || VN_IS(m_modp, Iface)) {
863 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: assertcontrols in classes or interfaces");
864 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
865 : : return;
866 : : }
867 : :
868 : : iterateChildren(nodep);
869 : :
870 : : if (!resolveAssertType(nodep)) {
871 : : nodep->v3warn(E_UNSUPPORTED,
872 : : "Unsupported: non-constant assert assertion-type expression");
873 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
874 : : return;
875 : : }
876 : : if (nodep->ctlAssertTypes() != ALL_ASSERT_TYPES
877 : : && nodep->ctlAssertTypes().containsAny(VAssertType::EXPECT | VAssertType::UNIQUE
878 : : | VAssertType::UNIQUE0
879 : : | VAssertType::PRIORITY)) {
880 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: assert control assertion_type");
881 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
882 : : return;
883 : : }
884 : : if (!resolveControlType(nodep)) {
885 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: non-const assert control type expression");
886 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
887 : : return;
888 : : }
889 : : if (!resolveDirectiveType(nodep)) {
890 : : nodep->v3warn(E_UNSUPPORTED,
891 : : "Unsupported: non-const assert directive type expression");
892 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
893 : : return;
894 : : }
895 : :
896 : : FileLine* const fl = nodep->fileline();
897 : : switch (nodep->ctlType()) {
898 : : case VAssertCtlType::ON:
899 : : UINFO(9, "Generating assertctl for a module: " << m_modp);
900 : : nodep->replaceWith(
901 : : new AstCStmt{fl, "vlSymsp->_vm_contextp__->assertOnSet("s
902 : : + std::to_string(nodep->ctlAssertTypes()) + ", "s
903 : : + std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s});
904 : : break;
905 : : case VAssertCtlType::OFF:
906 : : case VAssertCtlType::KILL: {
907 : : UINFO(9, "Generating assertctl for a module: " << m_modp);
908 : : nodep->replaceWith(
909 : : new AstCStmt{fl, "vlSymsp->_vm_contextp__->assertOnClear("s
910 : : + std::to_string(nodep->ctlAssertTypes()) + " ,"s
911 : : + std::to_string(nodep->ctlDirectiveTypes()) + ");\n"s});
912 : : break;
913 : : }
914 : : case VAssertCtlType::LOCK:
915 : : case VAssertCtlType::UNLOCK:
916 : : case VAssertCtlType::PASS_ON:
917 : : case VAssertCtlType::PASS_OFF:
918 : : case VAssertCtlType::FAIL_ON:
919 : : case VAssertCtlType::FAIL_OFF:
920 : : case VAssertCtlType::NONVACUOUS_ON:
921 : : case VAssertCtlType::VACUOUS_OFF: {
922 : : nodep->unlinkFrBack();
923 : : nodep->v3warn(E_UNSUPPORTED, "Unsupported: $assertcontrol control_type '" << cvtToStr(
924 : : static_cast<int>(nodep->ctlType())) << "'");
925 : : break;
926 : : }
927 : : default: {
928 : : nodep->unlinkFrBack();
929 : : nodep->v3warn(EC_ERROR, "Bad $assertcontrol control_type '"
930 : : << cvtToStr(static_cast<int>(nodep->ctlType()))
931 : : << "' (IEEE 1800-2023 Table 20-5)");
932 : : }
933 : : }
934 : : VL_DO_DANGLING(pushDeletep(nodep), nodep);
935 : : }
936 : : void visit(AstAssertIntrinsic* nodep) override { //
937 : : visitAssertionIterate(nodep, nodep->failsp());
938 : : }
939 : : void visit(AstCover* nodep) override { //
940 : : visitAssertionIterate(nodep, nullptr);
941 : : }
942 : : void visit(AstRestrict* nodep) override {
943 : : VL_RESTORER(m_inRestrict);
944 : : m_inRestrict = true;
945 : : iterateChildren(nodep);
946 : : // IEEE says simulator ignores these
947 : : VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
948 : : }
949 : :
950 : : void visit(AstNodeModule* nodep) override {
951 : : VL_RESTORER(m_modp);
952 : : VL_RESTORER(m_modPastNum);
953 : : VL_RESTORER(m_modStrobeNum);
954 : : VL_RESTORER(m_modExpr2Sen2DelayedAlwaysp);
955 : : m_modp = nodep;
956 : : m_modPastNum = 0;
957 : : m_modStrobeNum = 0;
958 : : m_modExpr2Sen2DelayedAlwaysp.clear();
959 : : iterateChildren(nodep);
960 : : }
961 : : void visit(AstNodeProcedure* nodep) override {
962 : : VL_RESTORER(m_procedurep);
963 : : m_procedurep = nodep;
964 : : iterateChildren(nodep);
965 : : }
966 : : void visit(AstGenBlock* nodep) override {
967 : : // This code is needed rather than a visitor in V3Begin,
968 : : // because V3Assert is called before V3Begin
969 : : VL_RESTORER(m_beginp);
970 : : m_beginp = nodep;
971 : : iterateChildren(nodep);
972 : : }
973 : : void visit(AstBegin* nodep) override {
974 : : // This code is needed rather than a visitor in V3Begin,
975 : : // because V3Assert is called before V3Begin
976 : : VL_RESTORER(m_beginp);
977 : : m_beginp = nodep;
978 : : iterateChildren(nodep);
979 : : }
980 : :
981 : : void visit(AstNode* nodep) override { iterateChildren(nodep); }
982 : :
983 : : public:
984 : : // CONSTRUCTORS
985 : : explicit AssertVisitor(AstNetlist* nodep) { iterate(nodep); }
986 : : ~AssertVisitor() override {
987 : : V3Stats::addStat("Assertions, assert non-immediate statements", m_statAsNotImm);
988 : : V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm);
989 : : V3Stats::addStat("Assertions, cover statements", m_statCover);
990 : : V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
991 : : V3Stats::addStat("Assertions, $past variables", m_statPastVars);
992 : : }
993 : : };
994 : :
995 : : //######################################################################
996 : : // Top Assert class
997 : :
998 : : void V3Assert::assertAll(AstNetlist* nodep) {
999 : : UINFO(2, __FUNCTION__ << ":");
1000 : : { AssertVisitor{nodep}; } // Destruct before checking
1001 : : V3Global::dumpCheckGlobalTree("assert", 0, dumpTreeEitherLevel() >= 3);
1002 : : }
|