Support NBAs in initial blocks (#7754)
This commit is contained in:
parent
047d6e03d9
commit
e269b914b2
|
|
@ -0,0 +1,7 @@
|
|||
.. comment: generated by t_finaldly_bad
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
bit foo;
|
||||
final foo <= 1; // <--- Error
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.. comment: generated by t_finaldly_bad
|
||||
.. code-block::
|
||||
|
||||
%Error-FINALDLY: example.v:1:13 Non-blocking assignment '<=' in final block
|
||||
9 | final foo <= 1;
|
||||
| ^~
|
||||
|
|
@ -883,6 +883,22 @@ List Of Warnings
|
|||
with a newline."
|
||||
|
||||
|
||||
.. option:: FINALDLY
|
||||
|
||||
Error issued when a non-blocking assignment `<=` is used in a
|
||||
`final` block.
|
||||
|
||||
This error can be disabled. If disabled, the assignment will be
|
||||
executed as a `=` blocking assignment.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. include:: ../../docs/gen/ex_FINALDLY_faulty.rst
|
||||
|
||||
Results in:
|
||||
|
||||
.. include:: ../../docs/gen/ex_FINALDLY_msg.rst
|
||||
|
||||
.. option:: FSMMULTI
|
||||
|
||||
Warns that the same always block contains multiple enum-typed case
|
||||
|
|
@ -1179,7 +1195,7 @@ List Of Warnings
|
|||
|
||||
.. option:: INITIALDLY
|
||||
|
||||
.. TODO better example
|
||||
Historical, never issued since version 5.050.
|
||||
|
||||
Warns that the code has a delayed assignment inside of an ``initial`` or
|
||||
``final`` block. If this message is suppressed, Verilator will convert
|
||||
|
|
|
|||
|
|
@ -351,7 +351,7 @@ public:
|
|||
|
||||
class ActiveDlyVisitor final : public VNVisitor {
|
||||
public:
|
||||
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL, CT_SUSPENDABLE };
|
||||
enum CheckType : uint8_t { CT_COMB, CT_FINAL };
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
|
|
@ -359,15 +359,9 @@ private:
|
|||
|
||||
// VISITORS
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
// Non-blocking assignments are OK in sequential processes
|
||||
if (m_check == CT_SEQ || m_check == CT_SUSPENDABLE) return;
|
||||
|
||||
// Issue appropriate warning
|
||||
if (m_check == CT_INITIAL) {
|
||||
nodep->v3warn(INITIALDLY,
|
||||
"Non-blocking assignment '<=' in initial/final block\n"
|
||||
<< nodep->warnMore()
|
||||
<< "... This will be executed as a blocking assignment '='!");
|
||||
if (m_check == CT_FINAL) {
|
||||
nodep->v3warn(FINALDLY, "Non-blocking assignment '<=' in final block");
|
||||
} else {
|
||||
nodep->v3warn(COMBDLY,
|
||||
"Non-blocking assignment '<=' in combinational logic process\n"
|
||||
|
|
@ -465,11 +459,7 @@ class ActiveVisitor final : public VNVisitor {
|
|||
wantactivep->addStmtsp(nodep);
|
||||
|
||||
// Warn and convert any delayed assignments
|
||||
{
|
||||
ActiveDlyVisitor{nodep, !m_clockedProcess ? ActiveDlyVisitor::CT_COMB
|
||||
: oldsentreep ? ActiveDlyVisitor::CT_SEQ
|
||||
: ActiveDlyVisitor::CT_SUSPENDABLE};
|
||||
}
|
||||
if (!m_clockedProcess) ActiveDlyVisitor{nodep, ActiveDlyVisitor::CT_COMB};
|
||||
|
||||
// Delete sensitivity list
|
||||
if (oldsentreep) VL_DO_DANGLING(oldsentreep->deleteTree(), oldsentreep);
|
||||
|
|
@ -509,17 +499,11 @@ class ActiveVisitor final : public VNVisitor {
|
|||
|
||||
void visit(AstInitialStatic* nodep) override { moveUnderSpecial<AstSenItem::Static>(nodep); }
|
||||
void visit(AstInitial* nodep) override {
|
||||
const bool timedInitial
|
||||
= v3Global.opt.timing().isSetTrue() && nodep->exists([](const AstNode* const subp) {
|
||||
return VN_IS(subp, Delay) || VN_IS(subp, EventControl);
|
||||
});
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, timedInitial ? ActiveDlyVisitor::CT_SUSPENDABLE
|
||||
: ActiveDlyVisitor::CT_INITIAL};
|
||||
visitSenItems(nodep);
|
||||
moveUnderSpecial<AstSenItem::Initial>(nodep);
|
||||
}
|
||||
void visit(AstFinal* nodep) override {
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
||||
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_FINAL};
|
||||
moveUnderSpecial<AstSenItem::Final>(nodep);
|
||||
}
|
||||
void visit(AstCoverToggle* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
||||
|
|
|
|||
|
|
@ -1428,6 +1428,7 @@ public:
|
|||
ET_EVENT, // VlEventBase::isFired
|
||||
// Involving an expression
|
||||
ET_TRUE,
|
||||
ET_INITIAL_NBA, // Event that is fired initially and never again
|
||||
//
|
||||
ET_COMBO, // Sensitive to all combo inputs to this block
|
||||
ET_COMBO_STAR, // Sensitive to all combo inputs to this block (from .*)
|
||||
|
|
@ -1446,6 +1447,7 @@ public:
|
|||
true, // ET_NEGEDGE
|
||||
true, // ET_EVENT
|
||||
true, // ET_TRUE
|
||||
true, // ET_INITIAL_NBA
|
||||
|
||||
false, // ET_COMBO
|
||||
false, // ET_COMBO_STAR
|
||||
|
|
@ -1469,14 +1471,14 @@ public:
|
|||
}
|
||||
const char* ascii() const {
|
||||
static const char* const names[]
|
||||
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "COMBO",
|
||||
"COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
= {"CHANGED", "BOTH", "POS", "NEG", "EVENT", "TRUE", "ET_INITIAL_NBA",
|
||||
"COMBO", "COMBO_STAR", "HYBRID", "STATIC", "INITIAL", "FINAL", "NEVER"};
|
||||
return names[m_e];
|
||||
}
|
||||
const char* verilogKwd() const {
|
||||
static const char* const names[]
|
||||
= {"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "*",
|
||||
"*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
static const char* const names[] = {
|
||||
"[changed]", "edge", "posedge", "negedge", "[event]", "[true]", "[initial_nba]",
|
||||
"*", "*", "[hybrid]", "[static]", "[initial]", "[final]", "[never]"};
|
||||
return names[m_e];
|
||||
}
|
||||
// Return true iff this and the other have mutually exclusive transitions
|
||||
|
|
|
|||
|
|
@ -1753,6 +1753,7 @@ public:
|
|||
class Combo {}; // for constructor type-overload selection
|
||||
class Static {}; // for constructor type-overload selection
|
||||
class Initial {}; // for constructor type-overload selection
|
||||
class InitialNBA {}; // for constructor type-overload selection
|
||||
class Final {}; // for constructor type-overload selection
|
||||
class Never {}; // for constructor type-overload selection
|
||||
AstSenItem(FileLine* fl, VEdgeType edgeType, AstNodeExpr* senp, AstNodeExpr* condp = nullptr)
|
||||
|
|
@ -1770,6 +1771,9 @@ public:
|
|||
AstSenItem(FileLine* fl, Initial)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_INITIAL} {}
|
||||
AstSenItem(FileLine* fl, InitialNBA)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_INITIAL_NBA} {}
|
||||
AstSenItem(FileLine* fl, Final)
|
||||
: ASTGEN_SUPER_SenItem(fl)
|
||||
, m_edgeType{VEdgeType::ET_FINAL} {}
|
||||
|
|
|
|||
|
|
@ -275,6 +275,7 @@ class DelayedVisitor final : public VNVisitor {
|
|||
bool m_inSuspendableOrFork = false; // True in suspendable processes and forks
|
||||
bool m_ignoreBlkAndNBlk = false; // Suppress delayed assignment BLKANDNBLK
|
||||
bool m_inNonCombLogic = false; // We are in non-combinational logic
|
||||
bool m_needsInitialTrigger = false; // Whether a NodeProcedure needs a initial trigger
|
||||
AstVarRef* m_currNbaLhsRefp = nullptr; // Current NBA LHS variable reference
|
||||
|
||||
// STATE - during NBA conversion (after visit)
|
||||
|
|
@ -291,6 +292,7 @@ class DelayedVisitor final : public VNVisitor {
|
|||
VDouble0 m_nSchemeValueQueuesWhole; // Number of variables using Scheme::ValueQueueWhole
|
||||
VDouble0 m_nSchemeValueQueuesPartial; // Number of variables using Scheme::ValueQueuePartial
|
||||
VDouble0 m_nSharedSetFlags; // "Set" flags actually shared by Scheme::FlagShared variables
|
||||
VDouble0 m_nInitialNBA; // Number of procedural blocks with initial NBA
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -999,6 +1001,12 @@ class DelayedVisitor final : public VNVisitor {
|
|||
m_writeRefs(nodep->varScopep()).emplace_back(nodep, nonBlocking, m_inNonCombLogic);
|
||||
}
|
||||
|
||||
template <typename Procedure_T>
|
||||
static bool isProcedureWithSentreep(const AstNodeProcedure* const nodep) {
|
||||
const Procedure_T* const procedurep = AstNode::cast<Procedure_T>(nodep);
|
||||
return procedurep && procedurep->sentreep();
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNetlist* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -1112,21 +1120,36 @@ class DelayedVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_needsInitialTrigger);
|
||||
const size_t firstNBAAddedIndex = m_nbas.size();
|
||||
{
|
||||
VL_RESTORER(m_inSuspendableOrFork);
|
||||
VL_RESTORER(m_procp);
|
||||
VL_RESTORER(m_ignoreBlkAndNBlk);
|
||||
VL_RESTORER(m_inNonCombLogic);
|
||||
m_inSuspendableOrFork = nodep->isSuspendable();
|
||||
// When we are dealing with initial block we need to
|
||||
// treat it as suspendable when we meet a NBA
|
||||
m_inSuspendableOrFork = nodep->isSuspendable() || VN_IS(nodep, Initial);
|
||||
m_procp = nodep;
|
||||
if (m_inSuspendableOrFork) {
|
||||
if (nodep->isSuspendable()) {
|
||||
m_ignoreBlkAndNBlk = false;
|
||||
m_inNonCombLogic = true;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
if (m_timingDomains.empty()) return;
|
||||
auto containsClocled = [](const AstSenItem* itemp) {
|
||||
while (itemp) {
|
||||
if (itemp->edgeType().clockedStmt()) return true;
|
||||
itemp = VN_AS(itemp->nextp(), SenItem);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const bool addInitialTrigger = m_needsInitialTrigger
|
||||
&& !(isProcedureWithSentreep<AstAlways>(nodep)
|
||||
|| isProcedureWithSentreep<AstAlwaysObserved>(nodep)
|
||||
|| isProcedureWithSentreep<AstAlwaysReactive>(nodep))
|
||||
&& !containsClocled(m_activep->sentreep()->sensesp());
|
||||
if (m_timingDomains.empty() && !addInitialTrigger) return;
|
||||
|
||||
// There were some timing domains involved in the process. Add all of them as sensitivities
|
||||
// of all NBA targets in this process. Note this is a bit of a sledgehammer, we should only
|
||||
|
|
@ -1135,6 +1158,11 @@ class DelayedVisitor final : public VNVisitor {
|
|||
|
||||
// First gather all senItems
|
||||
AstSenItem* senItemp = nullptr;
|
||||
if (addInitialTrigger) {
|
||||
senItemp = new AstSenItem{nodep->fileline(), AstSenItem::InitialNBA{}};
|
||||
++m_nInitialNBA;
|
||||
}
|
||||
|
||||
for (const AstSenTree* const domainp : m_timingDomains) {
|
||||
if (domainp->sensesp())
|
||||
senItemp = AstNode::addNext(senItemp, domainp->sensesp()->cloneTree(true));
|
||||
|
|
@ -1218,6 +1246,8 @@ class DelayedVisitor final : public VNVisitor {
|
|||
UASSERT_OBJ(m_inSuspendableOrFork || m_activep->hasClocked(), nodep,
|
||||
"<= assignment in non-clocked block, should have been converted in V3Active");
|
||||
|
||||
m_needsInitialTrigger |= m_timingDomains.empty();
|
||||
|
||||
// Record scope of this NBA
|
||||
nodep->user2p(m_scopep);
|
||||
|
||||
|
|
@ -1327,6 +1357,7 @@ public:
|
|||
V3Stats::addStat("NBA, variables using ValueQueuePartial scheme",
|
||||
m_nSchemeValueQueuesPartial);
|
||||
V3Stats::addStat("Optimizations, NBA flags shared", m_nSharedSetFlags);
|
||||
V3Stats::addStat("Procedures needing initial NBA trigger", m_nInitialNBA);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ public:
|
|||
ENUMITEMWIDTH, // Error: enum item width mismatch
|
||||
ENUMVALUE, // Error: enum type needs explicit cast
|
||||
EOFNEWLINE, // End-of-file missing newline
|
||||
FINALDLY, // Final delayed statement
|
||||
FSMMULTI, // Multiple FSM candidates in one always block
|
||||
FUNCTIMECTL, // Functions cannot have timing/delay/wait
|
||||
FUTURE, // Feature is under development and not yet supported
|
||||
|
|
@ -227,8 +228,8 @@ public:
|
|||
"BSSPACE", "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST",
|
||||
"CDCRSTLOGIC", "CLKDATA", "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN",
|
||||
"CONTASSREG", "COVERIGN", "DECLFILENAME", "DEFOVERRIDE", "DEFPARAM", "DEPRECATED",
|
||||
"ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", "ENUMVALUE", "EOFNEWLINE", "FSMMULTI",
|
||||
"FUNCTIMECTL", "FUTURE", "GENCLK", "GENUNNAMED", "HIERBLOCK", "HIERPARAM",
|
||||
"ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", "ENUMVALUE", "EOFNEWLINE", "FINALDLY",
|
||||
"FSMMULTI", "FUNCTIMECTL", "FUTURE", "GENCLK", "GENUNNAMED", "HIERBLOCK", "HIERPARAM",
|
||||
"IEEEMAYDEPRECATE", "IFDEPTH", "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT",
|
||||
"IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY",
|
||||
"INSECURE", "INSIDETRUE", "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP",
|
||||
|
|
@ -269,11 +270,11 @@ public:
|
|||
bool pretendError() const VL_MT_SAFE {
|
||||
return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BADVLTPRAGMA || m_e == BLKANDNBLK
|
||||
|| m_e == BLKLOOPINIT || m_e == CONTASSREG || m_e == ENCAPSULATED
|
||||
|| m_e == ENDLABEL || m_e == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == HIERPARAM
|
||||
|| m_e == FUNCTIMECTL || m_e == IMPURE || m_e == MODMISSING || m_e == NOTREDOP
|
||||
|| m_e == PARAMNODEFAULT || m_e == PINNOTFOUND || m_e == PKGNODECL
|
||||
|| m_e == PROCASSWIRE || m_e == PROTOTYPEMIS || m_e == SUPERNFIRST
|
||||
|| m_e == ZEROREPL);
|
||||
|| m_e == ENDLABEL || m_e == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == FINALDLY
|
||||
|| m_e == HIERPARAM || m_e == FUNCTIMECTL || m_e == IMPURE || m_e == MODMISSING
|
||||
|| m_e == NOTREDOP || m_e == PARAMNODEFAULT || m_e == PINNOTFOUND
|
||||
|| m_e == PKGNODECL || m_e == PROCASSWIRE || m_e == PROTOTYPEMIS
|
||||
|| m_e == SUPERNFIRST || m_e == ZEROREPL);
|
||||
}
|
||||
// Warnings to mention manual
|
||||
bool mentionManual() const VL_MT_SAFE {
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ private:
|
|||
// Check if expression contains a class member access that could be null
|
||||
// (e.g., accessing an event through a class reference that may not be initialized)
|
||||
static bool hasClassMemberAccess(const AstNode* const exprp) {
|
||||
return exprp->exists([](const AstNode* const nodep) {
|
||||
return exprp && exprp->exists([](const AstNode* const nodep) {
|
||||
if (const AstMemberSel* const mselp = VN_CAST(nodep, MemberSel)) {
|
||||
// Check if the base expression is a class reference
|
||||
return mselp->fromp()->dtypep()
|
||||
|
|
@ -294,6 +294,8 @@ private:
|
|||
}
|
||||
case VEdgeType::ET_TRUE: //
|
||||
return {currp(), false};
|
||||
case VEdgeType::ET_INITIAL_NBA: //
|
||||
return {new AstConst{flp, AstConst::BitFalse{}}, true};
|
||||
default: // LCOV_EXCL_START
|
||||
senItemp->v3fatalSrc("Unknown edge type");
|
||||
return {nullptr, false};
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
//See also: https://github.com/twosigma/verilator_support
|
||||
// verilog_format: off
|
||||
// verilator lint_off COMBDLY,INITIALDLY,LATCH,MULTIDRIVEN,UNSIGNED,WIDTH
|
||||
// verilator lint_off COMBDLY,LATCH,MULTIDRIVEN,UNSIGNED,WIDTH
|
||||
|
||||
// BEGINNING OF MODULE
|
||||
`timescale 1 ps / 1 ps
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
%Error-FINALDLY: t/t_finaldly_bad.v:9:13: Non-blocking assignment '<=' in final block
|
||||
9 | final foo <= 1;
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/FINALDLY?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -4,14 +4,21 @@
|
|||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2024 Wilson Snyder
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
test.top_filename = "t/t_initial_dlyass.v"
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
test.compile(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.extract(in_filename=test.top_filename,
|
||||
out_filename=test.root + "/docs/gen/ex_FINALDLY_faulty.rst",
|
||||
lines="8-9")
|
||||
|
||||
test.extract(in_filename=test.golden_filename,
|
||||
out_filename=test.root + "/docs/gen/ex_FINALDLY_msg.rst",
|
||||
lines="1-3")
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
bit foo;
|
||||
final foo <= 1; // <--- Error
|
||||
endmodule
|
||||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary -Wno-INITIALDLY"])
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['-Wno-INITIALDLY'])
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
%Warning-INITIALDLY: t/t_initial_dlyass.v:17:7: Non-blocking assignment '<=' in initial/final block
|
||||
: ... This will be executed as a blocking assignment '='!
|
||||
17 | a <= 22;
|
||||
| ^~
|
||||
... For warning description see https://verilator.org/warn/INITIALDLY?v=latest
|
||||
... Use "/* verilator lint_off INITIALDLY */" and lint_on around source to disable this message.
|
||||
%Warning-INITIALDLY: t/t_initial_dlyass.v:18:7: Non-blocking assignment '<=' in initial/final block
|
||||
: ... This will be executed as a blocking assignment '='!
|
||||
18 | b <= 33;
|
||||
| ^~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -40,6 +40,7 @@ module t;
|
|||
// verilator lint_off ENUMITEMWIDTH
|
||||
// verilator lint_off ENUMVALUE
|
||||
// verilator lint_off EOFNEWLINE
|
||||
// verilator lint_off FINALDLY
|
||||
// verilator lint_off FUNCTIMECTL
|
||||
// verilator lint_off GENCLK
|
||||
// verilator lint_off GENUNNAMED
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.top_filename = "t/t_procedure_nba.v"
|
||||
|
||||
test.compile(verilator_flags2=["--binary", "--stats", "-DPROCEDURE=always"])
|
||||
|
||||
test.file_grep(test.stats, r'Scheduling, \'act\' extra triggers\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Scheduling, \'act\' pre triggers\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Scheduling, \'act\' sense triggers\s+(\d+)', 3)
|
||||
test.file_grep(test.stats, r'Procedures needing initial NBA trigger\s+(\d+)', 100)
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of either the GNU Lesser General Public License Version 3
|
||||
# or the Perl Artistic License Version 2.0.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.top_filename = "t/t_procedure_nba.v"
|
||||
|
||||
test.compile(verilator_flags2=["--binary", "--stats", "-DPROCEDURE=initial"])
|
||||
|
||||
test.file_grep(test.stats, r'Scheduling, \'act\' extra triggers\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Scheduling, \'act\' pre triggers\s+(\d+)', 0)
|
||||
test.file_grep(test.stats, r'Scheduling, \'act\' sense triggers\s+(\d+)', 3)
|
||||
test.file_grep(test.stats, r'Procedures needing initial NBA trigger\s+(\d+)', 100)
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
logic clk;
|
||||
initial clk = 0;
|
||||
always #5 clk = ~clk;
|
||||
bit [99:0][2:0] foo;
|
||||
bit bar;
|
||||
|
||||
always @(posedge clk) begin
|
||||
bar <= ~bar;
|
||||
#1;
|
||||
end
|
||||
|
||||
genvar i;
|
||||
for (i = 0; i < 100; i=i+1)
|
||||
`PROCEDURE begin
|
||||
foo[i] <= 3;
|
||||
if (foo[i] !== 0) $stop;
|
||||
@(posedge clk);
|
||||
if (foo[i] !== 3) $stop;
|
||||
foo[i] = 2;
|
||||
if (foo[i] !== 2) $stop;
|
||||
#1;
|
||||
if (foo[i] !== 2) $stop;
|
||||
foo[i] = 0;
|
||||
end
|
||||
|
||||
initial #100 begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue