Merge upstream/master and resolve conflicts

This commit is contained in:
Yilou Wang 2026-04-08 13:58:28 +02:00
commit db1dd74404
32 changed files with 626 additions and 55 deletions

View File

@ -1855,8 +1855,11 @@ List Of Warnings
and #(1,2,3) AND (out, a, b);
Warns that rising, falling, and turn-off delays are currently unsupported.
The first (rising) delay is used for all cases.
Warns that the third (turn-off) delay is currently unsupported and is
ignored. Rising and falling delays are supported.
In versions before 5.048, warned that rising, falling, and turn-off delays were
unsupported. The first (rising) delay was used for all cases.
.. option:: SELRANGE

View File

@ -709,9 +709,8 @@ class AssertVisitor final : public VNVisitor {
}
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void visit(AstVarRef* nodep) override {
iterateChildren(nodep);
if (m_inSampled && !(nodep->varp() && nodep->varp()->noSample())) {
void visit(AstNodeVarRef* nodep) override {
if (m_inSampled && !nodep->varp()->noSample()) {
if (!nodep->access().isReadOnly()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: Write to variable in sampled expression");

View File

@ -746,6 +746,7 @@ private:
AstSenTree* const senTreep = newSenTree(nodep);
AstAlways* const alwaysp = new AstAlways{flp, VAlwaysKwd::ALWAYS, senTreep, ifp};
cntVarp->addNextHere(alwaysp);
// Match: cnt >= N-1 (previous cycles via NBA) && expr (current cycle)
AstNodeExpr* const cntCheckp = new AstGte{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
new AstConst{flp, static_cast<uint32_t>(n - 1)}};
cntCheckp->dtypeSetBit();
@ -852,6 +853,7 @@ private:
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, name + "__counter",
exprp->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
cntVarp->noSample(true);
AstBegin* const beginp = new AstBegin{flp, name + "__block", cntVarp, true};
beginp->addStmtsp(

View File

@ -364,8 +364,9 @@ static std::vector<SeqStep> extractTimeline(AstNodeExpr* nodep) {
if (AstConst* const constp = VN_CAST(dlyp->lhsp(), Const)) {
cycles = constp->toSInt();
} else {
dlyp->lhsp()->v3warn(E_UNSUPPORTED,
"Unsupported: non-constant cycle delay in sequence and/or");
dlyp->lhsp()->v3warn(
E_UNSUPPORTED,
"Unsupported: non-constant cycle delay in sequence and/or/intersect");
}
}
// The expression after the delay
@ -386,6 +387,18 @@ static std::vector<SeqStep> extractTimeline(AstNodeExpr* nodep) {
return timeline;
}
// True if any AstSExpr in the subtree has a range delay (##[m:n]).
// Uses forall(): predicate returns false when a range delay is found,
// so !forall(...) means "at least one range delay exists".
static bool subtreeHasRangeDelay(const AstNode* nodep) {
return !nodep->forall([](const AstSExpr* sexprp) {
if (const AstDelay* const dlyp = VN_CAST(sexprp->delayp(), Delay)) {
if (dlyp->isRangeDelay()) return false;
}
return true;
});
}
// Lower sequence and/or to AST
class AssertPropLowerVisitor final : public VNVisitor {
// STATE
@ -624,6 +637,47 @@ class AssertPropLowerVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
// Lower a multi-cycle sequence 'intersect' (IEEE 1800-2023 16.9.6).
// intersect = and + equal-length constraint: both operands must match with the same duration.
// When total delays are equal constants, this is identical to 'and'; otherwise constant-false.
void lowerSeqIntersect(AstNodeBiop* nodep) {
const std::vector<SeqStep> lhsTimeline = extractTimeline(nodep->lhsp());
const std::vector<SeqStep> rhsTimeline = extractTimeline(nodep->rhsp());
int lhsTotal = 0;
for (const auto& step : lhsTimeline) lhsTotal += step.delayCycles;
int rhsTotal = 0;
for (const auto& step : rhsTimeline) rhsTotal += step.delayCycles;
if (lhsTotal != rhsTotal) {
// Lengths differ: per IEEE 16.9.6, the match set is empty -- constant-false.
// Warn the user; mismatched lengths are almost always a mistake.
// Skip when either operand had a range delay: RangeDelayExpander already
// replaced it with an FSM expression (containsSExpr returns false) and
// issued UNSUPPORTED, so no second diagnostic is needed.
if (containsSExpr(nodep->lhsp()) && containsSExpr(nodep->rhsp())) {
if (lhsTotal > rhsTotal) {
nodep->v3warn(WIDTHEXPAND, "Intersect sequence length mismatch"
" (left "
<< lhsTotal << " cycles, right " << rhsTotal
<< " cycles) -- intersection is always empty");
} else {
nodep->v3warn(WIDTHTRUNC, "Intersect sequence length mismatch"
" (left "
<< lhsTotal << " cycles, right " << rhsTotal
<< " cycles) -- intersection is always empty");
}
}
FileLine* const flp = nodep->fileline();
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
bodyp->addStmtsp(new AstPExprClause{flp, false});
AstPExpr* const pexprp = new AstPExpr{flp, bodyp, nodep->dtypep()};
nodep->replaceWith(pexprp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
}
// Same length: length restriction is trivially satisfied, lower as 'and'.
lowerSeqAnd(nodep);
}
void visit(AstSAnd* nodep) override {
iterateChildren(nodep);
if (containsSExpr(nodep->lhsp()) || containsSExpr(nodep->rhsp())) {
@ -637,6 +691,19 @@ class AssertPropLowerVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void visit(AstSIntersect* nodep) override {
iterateChildren(nodep);
if (containsSExpr(nodep->lhsp()) || containsSExpr(nodep->rhsp())) {
lowerSeqIntersect(nodep);
} else {
// Pure boolean operands: length is always 0, lower to LogAnd
AstLogAnd* const newp = new AstLogAnd{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack()};
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void visit(AstSOr* nodep) override {
iterateChildren(nodep);
if (containsSExpr(nodep->lhsp()) || containsSExpr(nodep->rhsp())) {
@ -1327,6 +1394,15 @@ class RangeDelayExpander final : public VNVisitor {
}
}
void visit(AstSIntersect* nodep) override {
// intersect with a range-delay operand cannot be lowered: the length-pairing
// logic requires knowing each operand's concrete length, which is dynamic.
if (subtreeHasRangeDelay(nodep->lhsp()) || subtreeHasRangeDelay(nodep->rhsp())) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: intersect with ranged cycle-delay operand");
}
iterateChildren(nodep);
}
void visit(AstSThroughout* nodep) override {
// Reject throughout with range-delay sequences before FSM expansion
// would silently lose per-tick enforcement (IEEE 1800-2023 16.9.9)

View File

@ -3699,6 +3699,30 @@ public:
bool sizeMattersRhs() const override { return false; }
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
};
class AstSIntersect final : public AstNodeBiop {
// Sequence 'intersect' (IEEE 1800-2023 16.9.6): both operands match with equal length.
// AND with a length restriction. For boolean operands, lowered to AstLogAnd.
public:
AstSIntersect(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
: ASTGEN_SUPER_SIntersect(fl, lhsp, rhsp) {
dtypeSetBit();
}
ASTGEN_MEMBERS_AstSIntersect;
// LCOV_EXCL_START // Lowered before these are ever called
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
out.opLogAnd(lhs, rhs);
}
string emitVerilog() override { return "%k(%l %fintersect %r)"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { return true; }
bool cleanLhs() const override { return true; }
bool cleanRhs() const override { return true; }
bool sizeMattersLhs() const override { return false; }
bool sizeMattersRhs() const override { return false; }
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
// LCOV_EXCL_STOP
};
class AstSOr final : public AstNodeBiop {
// Sequence 'or' (IEEE 1800-2023 16.9.7): at least one operand sequence must match.
// Operates on match sets, not values. For boolean operands, lowered to AstLogOr.

View File

@ -543,11 +543,10 @@ class AstDelay final : public AstNodeStmt {
// Delay statement
// @astgen op1 := lhsp : AstNodeExpr // Delay value (or min for range)
// @astgen op2 := stmtsp : List[AstNode] // Statements under delay
// @astgen op3 := rhsp : Optional[AstNodeExpr] // Max delay value (range delay only)
// @astgen op3 := rhsp : Optional[AstNodeExpr] // Max bound for cycle range or fall delay
// @astgen op4 := throughoutp : Optional[AstNodeExpr] // Throughout condition (IEEE 16.9.9)
VTimescale m_timeunit; // Delay's time unit
const bool m_isCycle; // True if it is a cycle delay
public:
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle)
: ASTGEN_SUPER_Delay(fl)
@ -562,8 +561,10 @@ public:
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
VTimescale timeunit() const { return m_timeunit; }
bool isCycleDelay() const { return m_isCycle; }
bool isRangeDelay() const { return rhsp() != nullptr; }
bool isUnbounded() const { return rhsp() && VN_IS(rhsp(), Unbounded); }
bool isRangeDelay() const { return m_isCycle && rhsp() != nullptr; }
bool isUnbounded() const { return isRangeDelay() && VN_IS(rhsp(), Unbounded); }
void fallDelay(AstNodeExpr* const fallDelayp) { rhsp(fallDelayp); }
AstNodeExpr* fallDelay() const { return m_isCycle ? nullptr : rhsp(); }
};
class AstDisable final : public AstNodeStmt {
// @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot

View File

@ -781,11 +781,13 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const {
}
case VDfgType::SAnd:
case VDfgType::SIntersect:
case VDfgType::SOr:
case VDfgType::SThroughout: {
UASSERT_OBJ(false, this,
"SAnd/SOr/SThroughout should be removed before DFG"); // LCOV_EXCL_LINE
return; // LCOV_EXCL_LINE
// LCOV_EXCL_START // Lowered before DFG
UASSERT_OBJ(false, this, "SAnd/SIntersect/SOr/SThroughout should be removed before DFG");
return;
// LCOV_EXCL_STOP
}
case VDfgType::LogAnd:

View File

@ -126,6 +126,7 @@ class V3DfgCse final {
case VDfgType::StreamL:
case VDfgType::StreamR:
case VDfgType::SAnd:
case VDfgType::SIntersect:
case VDfgType::SOr:
case VDfgType::SThroughout:
case VDfgType::Sub:
@ -251,6 +252,7 @@ class V3DfgCse final {
case VDfgType::ShiftRS:
case VDfgType::StreamL:
case VDfgType::SAnd:
case VDfgType::SIntersect:
case VDfgType::SOr:
case VDfgType::SThroughout:
case VDfgType::StreamR:

View File

@ -1127,7 +1127,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
}
VL_RESTORER(m_prefixed);
m_prefixed = false;
iterateConst(nodep->lhsp());
if (AstNodeExpr* const fallDelayp = nodep->fallDelay()) {
puts("(");
iterateConst(nodep->lhsp());
puts(", ");
iterateConst(fallDelayp);
puts(")");
} else {
iterateConst(nodep->lhsp());
}
if (!m_suppressSemi) {
puts(";\n");
} else {

View File

@ -1235,30 +1235,29 @@ class ParamProcessor final {
}
}
// Check if exprp is a ClassRefDType whose class is the default-parameter clone
// of origp's template class. This catches the case where an explicit type parameter
// like Holder#(W#(int)) resolves to the same specialization as the implicit default
// Holder#(), because W#(int) deparameterizes to W_ (the all-default clone of W).
// Uses the user4p link set at line ~1658 when defaultsResolved is true.
static bool classTypeMatchesDefaultClone(const AstNodeDType* exprp,
const AstNodeDType* origp) {
// Check if exprp's class matches origp's class after deparameterization.
// Handles both the simple case (user4p link from defaultsResolved) and the
// nested case where the default's inner class has non-default sub-parameters
// (e.g., uvm_sequence#(uvm_reg_item) where uvm_reg_item != default uvm_sequence_item).
bool classTypeMatchesDefaultClone(const AstNodeDType* exprp, const AstNodeDType* origp) {
exprp = exprp->skipRefp();
origp = origp->skipRefp();
const auto* const exprClassRefp = VN_CAST(exprp, ClassRefDType);
const auto* const origClassRefp = VN_CAST(origp, ClassRefDType);
UINFO(9, "classTypeMatchesDefaultClone: exprClassRef="
<< exprClassRefp << " origClassRef=" << origClassRefp);
if (!exprClassRefp || !origClassRefp) return false;
// Fast path: check user4p link (set when template was deparameterized with defaults)
const AstNodeModule* const defaultClonep
= VN_CAST(origClassRefp->classp()->user4p(), Class);
const bool result = defaultClonep && defaultClonep == exprClassRefp->classp();
UINFO(9, " origClass=" << origClassRefp->classp()->prettyNameQ()
<< " origClassp=" << cvtToHex(origClassRefp->classp())
<< " user4p=" << (defaultClonep ? cvtToHex(defaultClonep) : "null")
<< " exprClass=" << exprClassRefp->classp()->prettyNameQ()
<< " exprClassp=" << cvtToHex(exprClassRefp->classp())
<< " result=" << result);
return result;
if (defaultClonep && defaultClonep == exprClassRefp->classp()) return true;
// Slow path: deparameterize the default type and compare the result.
if (!origClassRefp->classp()->hasGParam()) return false;
// const_cast safe: cloneTree doesn't modify the source
AstClassRefDType* const origClonep = static_cast<AstClassRefDType*>(
const_cast<AstClassRefDType*>(origClassRefp)->cloneTree(false));
AstNodeModule* const resolvedModp = classRefDeparam(origClonep, origClassRefp->classp());
const bool match = resolvedModp && VN_CAST(resolvedModp, Class) == exprClassRefp->classp();
VL_DO_DANGLING(origClonep->deleteTree(), origClonep);
return match;
}
static bool paramConstsEqualAtMaxWidth(AstConst* exprp, AstConst* origp) {

View File

@ -542,6 +542,20 @@ class TimingControlVisitor final : public VNVisitor {
= timeunit.powerOfTen() - m_netlistp->timeprecision().powerOfTen();
return std::pow(10.0, scalePowerOfTen);
}
static bool staticallyNonZeroDelay(const AstNodeExpr* valuep) {
if (const AstConst* const constp = VN_CAST(valuep, Const)) return !constp->isZero();
if (const AstCond* const condp = VN_CAST(valuep, Cond)) {
return staticallyNonZeroDelay(condp->thenp())
&& staticallyNonZeroDelay(condp->elsep());
}
if (const AstMul* const mulp = VN_CAST(valuep, Mul)) {
const AstConst* const lhsConstp = VN_CAST(mulp->lhsp(), Const);
const AstConst* const rhsConstp = VN_CAST(mulp->rhsp(), Const);
if (lhsConstp && !lhsConstp->isZero()) return staticallyNonZeroDelay(mulp->rhsp());
if (rhsConstp && !rhsConstp->isZero()) return staticallyNonZeroDelay(mulp->lhsp());
}
return false;
}
// Creates the global delay scheduler variable
AstVarScope* getCreateDelayScheduler() {
if (m_delaySchedp) return m_delaySchedp;
@ -1022,6 +1036,9 @@ class TimingControlVisitor final : public VNVisitor {
m_hasStaticZeroDelay = true;
// Don't warn on variable delays, as no point
m_unknownDelayFlps.clear();
} else if (staticallyNonZeroDelay(valuep)) {
// Delay is dynamic, but every statically known outcome is non-zero.
// So we don't need #0 delay support and there should be no warning.
} else if (!VN_IS(valuep, Const)) {
// Delay is not known at compiile time. Conservatively schedule for #0 support,
// but warn if no static #0 delays used as performance might be improved
@ -1250,6 +1267,15 @@ class TimingControlVisitor final : public VNVisitor {
AstNodeExpr* const lhs1p = nodep->lhsp()->unlinkFrBack();
AstNodeExpr* const rhs1p = nodep->rhsp()->unlinkFrBack();
AstNode* const controlp = nodep->timingControlp()->unlinkFrBack();
if (AstDelay* const delayp = VN_CAST(controlp, Delay)) {
if (AstNodeExpr* fallDelayp = delayp->fallDelay()) {
fallDelayp = fallDelayp->unlinkFrBack();
// Use fall only for an all-zero value, rise otherwise.
delayp->lhsp(
new AstCond{flp, new AstEq{flp, rhs1p->cloneTree(false), new AstConst{flp, 0}},
fallDelayp, delayp->lhsp()->unlinkFrBack()});
}
}
AstAssign* const assignp = new AstAssign{nodep->fileline(), lhs1p, rhs1p, controlp};
// Put the assignment in a fork..join_none.
AstFork* const forkp = new AstFork{flp, VJoinType::JOIN_NONE};

View File

@ -294,8 +294,9 @@ class WidthVisitor final : public VNVisitor {
// Widths: 1 bit out, lhs 1 bit, rhs 1 bit; Real: converts via compare with 0
void visit(AstLogAnd* nodep) override { visit_log_and_or(nodep); }
void visit(AstLogOr* nodep) override { visit_log_and_or(nodep); }
// Sequence and/or (IEEE 1800-2023 16.9.2), same width rules as LogAnd/LogOr
// Sequence and/or/intersect (IEEE 1800-2023 16.9.2), same width rules as LogAnd/LogOr
void visit(AstSAnd* nodep) override { visit_log_and_or(nodep); }
void visit(AstSIntersect* nodep) override { visit_log_and_or(nodep); }
void visit(AstSOr* nodep) override { visit_log_and_or(nodep); }
void visit(AstLogEq* nodep) override {
// Conversion from real not in IEEE, but a fallout
@ -731,6 +732,11 @@ class WidthVisitor final : public VNVisitor {
iterateCheckBool(nodep, "default disable iff condition", nodep->condp(), BOTH);
}
void visit(AstDelay* nodep) override {
if (AstNodeExpr* const fallDelayp = nodep->fallDelay()) {
iterateCheckDelay(nodep, "delay", nodep->lhsp(), BOTH);
iterateCheckDelay(nodep, "delay", fallDelayp, BOTH);
return;
}
if (VN_IS(m_procedurep, Final)) {
nodep->v3error("Delays are not legal in final blocks (IEEE 1800-2023 9.2.3)");
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);

View File

@ -38,8 +38,7 @@
{ BBUNSUP((fl), "Unsupported: Verilog 1995 gate primitive: " << (tok)); }
#define RISEFALLDLYUNSUP(nodep) \
if (nodep->fileline()->timingOn() && v3Global.opt.timing().isSetTrue()) { \
nodep->v3warn(RISEFALLDLY, \
"Unsupported: rising/falling/turn-off delays. Using the first delay"); \
nodep->v3warn(RISEFALLDLY, "Unsupported: turn-off delays. Ignoring the third delay"); \
}
#define MINTYPMAXDLYUNSUP(nodep) \
if (nodep->fileline()->timingOn() && v3Global.opt.timing().isSetTrue()) { \
@ -3043,9 +3042,10 @@ delay_control<delayp>: //== IEEE: delay_control
| '#' '(' minTypMax ')'
{ $$ = new AstDelay{$<fl>1, $3, false}; }
| '#' '(' minTypMax ',' minTypMax ')'
{ $$ = new AstDelay{$<fl>1, $3, false}; RISEFALLDLYUNSUP($3); DEL($5); }
{ $$ = new AstDelay{$<fl>1, $3, false};
$$->fallDelay($5); }
| '#' '(' minTypMax ',' minTypMax ',' minTypMax ')'
{ $$ = new AstDelay{$<fl>1, $5, false}; RISEFALLDLYUNSUP($5); DEL($3); DEL($7); }
{ $$ = new AstDelay{$<fl>1, $3, false}; $$->fallDelay($5); RISEFALLDLYUNSUP($7); DEL($7); }
;
delay_value<nodeExprp>: // ==IEEE:delay_value
@ -6860,7 +6860,7 @@ sexpr<nodeExprp>: // ==IEEE: sequence_expr (The name sexpr is important as reg
{ $$ = new AstSOr{$2, $1, $3}; }
// // Intersect always has an sexpr rhs
| ~p~sexpr yINTERSECT sexpr
{ $$ = $1; BBUNSUP($2, "Unsupported: intersect (in sequence expression)"); DEL($3); }
{ $$ = new AstSIntersect{$2, $1, $3}; }
//
| yFIRST_MATCH '(' sexpr ')'
{ $$ = $3; BBUNSUP($1, "Unsupported: first_match (in sequence expression)"); }
@ -6894,7 +6894,7 @@ cycle_delay_range<delayp>: // IEEE: ==cycle_delay_range
// // the sv-ac committee has been asked to clarify (Mantis 1901)
| yP_POUNDPOUND '[' constExpr ':' constExpr ']'
{ $$ = new AstDelay{$1, $3, true};
$$->rhsp($5); }
$$->rhsp($5); }
| yP_POUNDPOUND yP_BRASTAR ']'
{ $$ = new AstDelay{$1, new AstConst{$1, 0}, true};
$$->rhsp(new AstUnbounded{$1}); }

View File

@ -22,7 +22,7 @@ module t (
// verilator lint_off IMPLICIT
not #(0.108) NT0 (nt0, a[0]);
and #1 AN0 (an0, a[0], b[0]);
nand #(2,3) ND0 (nd0, a[0], b[0], b[1]);
nand #(2,3,4) ND0 (nd0, a[0], b[0], b[1]);
or OR0 (or0, a[0], b[0]);
nor NR0 (nr0, a[0], b[0], b[2]);
xor (xo0, a[0], b[0]);

View File

@ -1,6 +1,6 @@
%Warning-RISEFALLDLY: t/t_gate_basic.v:25:11: Unsupported: rising/falling/turn-off delays. Using the first delay
25 | nand #(2,3) ND0 (nd0, a[0], b[0], b[1]);
| ^
%Warning-RISEFALLDLY: t/t_gate_basic.v:25:15: Unsupported: turn-off delays. Ignoring the third delay
25 | nand #(2,3,4) ND0 (nd0, a[0], b[0], b[1]);
| ^
... For warning description see https://verilator.org/warn/RISEFALLDLY?v=latest
... Use "/* verilator lint_off RISEFALLDLY */" and lint_on around source to disable this message.
%Warning-SPECIFYIGN: t/t_gate_basic.v:49:25: Ignoring unsupported: specify block construct

View File

@ -1,6 +1,6 @@
%Warning-RISEFALLDLY: t/t_gate_basic.v:25:11: Unsupported: rising/falling/turn-off delays. Using the first delay
25 | nand #(2,3) ND0 (nd0, a[0], b[0], b[1]);
| ^
%Warning-RISEFALLDLY: t/t_gate_basic.v:25:15: Unsupported: turn-off delays. Ignoring the third delay
25 | nand #(2,3,4) ND0 (nd0, a[0], b[0], b[1]);
| ^
... For warning description see https://verilator.org/warn/RISEFALLDLY?v=latest
... Use "/* verilator lint_off RISEFALLDLY */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -9,11 +9,12 @@
import vltest_bootstrap
test.scenarios('linter')
test.scenarios('vlt')
test.top_filename = "t/t_gate_basic.v"
test.lint(verilator_flags2=["-Wall", "-Wno-DECLFILENAME -Wno-SPECIFYIGN -Wno-UNUSED"],
fails=True,
expect_filename=test.golden_filename)
test.compile(
verilator_flags2=["--timing", "-Wall", "-Wno-DECLFILENAME -Wno-SPECIFYIGN -Wno-UNUSED"],
fails=True,
expect_filename=test.golden_filename)
test.passes()

View File

@ -24,7 +24,7 @@ module t (
int fails_b = 0;
// First launch at cyc==2 should be canceled by reset pulse in the middle.
assert property (@(posedge clk) disable iff (rst) (cyc == 2) |-> ##2 done)
assert property (@(posedge clk) disable iff (rst) (t.cyc == 2) |-> ##2 done)
else fails_a++;
// Second launch at cyc==8 has no reset pulse in flight and should fail once.

View File

@ -0,0 +1,18 @@
#!/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.compile(timing_loop=True, verilator_flags2=["--timing"])
test.execute()
test.passes()

View File

@ -0,0 +1,104 @@
// DESCRIPTION: Verilator: Rise/fall delays on continuous assigns and gates
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__, `__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0)
`define check_scalar(exp) do begin `checkh(out_assign, exp); `checkh(out_buf, exp); `checkh(out_net, exp); end while(0)
module t;
logic in = 0;
logic [3:0] in_vec = 4'h0;
wire out_assign;
wire out_buf;
wire #(5,3) out_net;
wire [3:0] out_vec_assign;
assign #(5,3) out_assign = in;
buf #(5,3) u_buf (out_buf, in);
assign out_net = in;
assign #(5,3) out_vec_assign = in_vec;
initial begin
#4;
`check_scalar(1'b0);
`checkh(out_vec_assign, 4'h0);
// Rise canceled by a fall before the rise delay expires.
in = 1'b1;
#2;
`check_scalar(1'b0);
in = 1'b0;
#4;
`check_scalar(1'b0);
// A committed rise.
in = 1'b1;
#4;
`check_scalar(1'b0);
#1;
`check_scalar(1'b1);
// Fall canceled by a new rise before the fall delay expires.
in = 1'b0;
#2;
`check_scalar(1'b1);
in = 1'b1;
#4;
`check_scalar(1'b1);
#1;
`check_scalar(1'b1);
// A committed fall.
in = 1'b0;
#2;
`check_scalar(1'b1);
#1;
`check_scalar(1'b0);
// Whole-value vector rise canceled by a fall back to zero.
in_vec = 4'h3;
#2;
`checkh(out_vec_assign, 4'h0);
in_vec = 4'h0;
#4;
`checkh(out_vec_assign, 4'h0);
// Zero to nonzero uses the rise delay.
in_vec = 4'h3;
#4;
`checkh(out_vec_assign, 4'h0);
#1;
`checkh(out_vec_assign, 4'h3);
// Nonzero to nonzero still uses the rise delay on the whole value.
in_vec = 4'h5;
#4;
`checkh(out_vec_assign, 4'h3);
#1;
`checkh(out_vec_assign, 4'h5);
// A pending fall back to zero is canceled by a new nonzero value.
in_vec = 4'h0;
#2;
`checkh(out_vec_assign, 4'h5);
in_vec = 4'h6;
#4;
`checkh(out_vec_assign, 4'h5);
#1;
`checkh(out_vec_assign, 4'h6);
// Nonzero to zero uses the fall delay.
in_vec = 4'h0;
#2;
`checkh(out_vec_assign, 4'h6);
#1;
`checkh(out_vec_assign, 4'h0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/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.compile(verilator_flags2=['--assert --timing'])
test.execute()
test.passes()

View File

@ -0,0 +1,82 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv, expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t (
input clk
);
integer cyc = 0;
reg [63:0] crc = '0;
reg [63:0] sum = '0;
// Derive test signals from CRC
wire a = crc[0];
wire b = crc[1];
wire c = crc[2];
wire d = crc[3];
wire [63:0] result = {60'h0, d, c, b, a};
always_ff @(posedge clk) begin
`ifdef TEST_VERBOSE
$write("[%0t] cyc==%0d crc=%x a=%b b=%b c=%b d=%b\n",
$time, cyc, crc, a, b, c, d);
`endif
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};
sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]};
if (cyc == 0) begin
crc <= 64'h5aef0c8d_d70a4497;
sum <= '0;
end else if (cyc < 10) begin
sum <= '0;
end else if (cyc == 99) begin
`checkh(crc, 64'hc77bb9b3784ea091);
`checkh(sum, 64'hdb7bc8bfe61f987e);
$write("*-* All Finished *-*\n");
$finish;
end
end
// =========================================================================
// Boolean intersect (length-0): equivalent to boolean AND (IEEE 16.9.6)
// =========================================================================
// Boolean intersect: when a & b, intersect succeeds (equivalent to AND)
assert property (@(posedge clk) disable iff (cyc < 2)
(a & b) |-> (a intersect b));
// Boolean intersect with constant true -- reduces to just 'a'
assert property (@(posedge clk) disable iff (cyc < 2)
a |-> (a intersect 1'b1));
// =========================================================================
// Multi-cycle sequence intersect (IEEE 1800-2023 16.9.6)
// Same-length sequences: intersect succeeds when both arms complete
// =========================================================================
// Both arms have length 1; 1'b1 guarantees completion on both sides
assert property (@(posedge clk)
(a & b) |-> (a ##1 1'b1) intersect (b ##1 1'b1));
// Both arms have length 2
assert property (@(posedge clk)
(a & b) |-> (a ##2 1'b1) intersect (b ##2 1'b1));
// Different internal structure, same total length (2 cycles each)
assert property (@(posedge clk)
(a & b) |-> (a ##1 1'b1 ##1 1'b1) intersect (b ##2 1'b1));
// Standalone constant intersect (always passes)
assert property (@(posedge clk)
(1'b1 ##1 1'b1) intersect (1'b1 ##1 1'b1));
endmodule

View File

@ -0,0 +1,13 @@
%Warning-WIDTHTRUNC: t/t_sequence_intersect_len_warn.v:16:17: Intersect sequence length mismatch (left 1 cycles, right 3 cycles) -- intersection is always empty
: ... note: In instance 't'
16 | (a ##1 b) intersect (c ##3 d));
| ^~~~~~~~~
... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest
... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message.
%Warning-WIDTHEXPAND: t/t_sequence_intersect_len_warn.v:20:17: Intersect sequence length mismatch (left 3 cycles, right 1 cycles) -- intersection is always empty
: ... note: In instance 't'
20 | (a ##3 b) intersect (c ##1 d));
| ^~~~~~~~~
... For warning description see https://verilator.org/warn/WIDTHEXPAND?v=latest
... Use "/* verilator lint_off WIDTHEXPAND */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/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('vlt_all')
test.compile(verilator_flags2=['--assert', '--timing', '--lint-only'],
fails=True,
expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,22 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
// verilog_lint: off
// verilog_format: on
module t (input clk);
logic a, b, c, d;
// LHS length 2, RHS length 4 -- WIDTHTRUNC (left < right)
assert property (@(posedge clk)
(a ##1 b) intersect (c ##3 d));
// LHS length 4, RHS length 2 -- WIDTHEXPAND (left > right)
assert property (@(posedge clk)
(a ##3 b) intersect (c ##1 d));
endmodule

View File

@ -0,0 +1,6 @@
%Error-UNSUPPORTED: t/t_sequence_intersect_range_unsup.v:12:21: Unsupported: intersect with ranged cycle-delay operand
: ... note: In instance 't'
12 | (a ##[1:5] b) intersect (c ##2 d));
| ^~~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/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('linter')
test.compile(verilator_flags2=['--assert --timing'],
fails=True,
expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,14 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
module t (input clk);
logic a, b, c, d;
// Range delay in intersect operand is unsupported
assert property (@(posedge clk)
(a ##[1:5] b) intersect (c ##2 d));
endmodule

View File

@ -1,4 +1,4 @@
%Error-UNSUPPORTED: t/t_sequence_nonconst_delay_unsup.v:14:40: Unsupported: non-constant cycle delay in sequence and/or
%Error-UNSUPPORTED: t/t_sequence_nonconst_delay_unsup.v:14:40: Unsupported: non-constant cycle delay in sequence and/or/intersect
: ... note: In instance 't'
14 | assert property (@(posedge clk) (a ##delay b) and (c ##1 d));
| ^~~~~

View File

@ -2,9 +2,6 @@
34 | a within(b);
| ^~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:50:7: Unsupported: intersect (in sequence expression)
50 | a intersect b;
| ^~~~~~~~~
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:95:7: Unsupported: [= boolean abbrev expression
95 | a [= 1];
| ^~

View File

@ -0,0 +1,18 @@
#!/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.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,94 @@
// DESCRIPTION: Verilator: Verify that nested class type parameters with
// explicit-equivalent defaults resolve to the same specialization.
//
// When uvm_reg_sequence#(type BASE = uvm_sequence#(uvm_reg_item)) is
// extended with the explicit equivalent default, both must produce the
// same specialization so that $cast succeeds between them.
//
// 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
virtual class uvm_object;
endclass
class uvm_factory;
uvm_object m_type_names[string];
function uvm_object create_object_by_name(string name);
uvm_object wrapper;
if (m_type_names.exists(name)) begin
wrapper = m_type_names[name];
end
else begin
$display("%%Error: object not found '%s'", name);
$stop;
end
return wrapper;
endfunction
function void register(uvm_object obj, string name);
m_type_names[name] = obj;
endfunction
endclass
uvm_factory factory;
class uvm_sequence_item extends uvm_object;
endclass
class uvm_reg_item extends uvm_sequence_item;
endclass
virtual class uvm_sequence #(
type REQ = uvm_sequence_item,
type RSP = REQ
) extends uvm_object;
endclass
class uvm_reg_sequence #(
type BASE = uvm_sequence#(uvm_reg_item)
) extends BASE;
function new;
factory.register(this, "uvm_reg_sequence");
endfunction
endclass
class uvm_reg_hw_reset_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));
function new;
factory.register(this, "uvm_reg_hw_reset_seq");
endfunction
endclass
module t;
initial begin
uvm_reg_hw_reset_seq rsq;
uvm_reg_sequence seq;
uvm_object obj;
int cst;
string seq_name;
factory = new;
rsq = new;
seq_name = "uvm_reg_hw_reset_seq";
obj = factory.create_object_by_name(seq_name);
if (obj == null) $stop;
cst = $cast(seq, obj);
/* verilator lint_off WIDTHTRUNC */
if (!cst || seq == null) begin
$display("%%Error: cast failed");
$stop;
end
/* verilator lint_on WIDTHTRUNC */
if (seq != rsq) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule