Support clocking event on a sequence declaration body
This commit is contained in:
parent
d84af81a11
commit
3113af1f31
|
|
@ -2014,6 +2014,45 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||||
VL_DO_DANGLING(pushDeletep(innerPropp), innerPropp);
|
VL_DO_DANGLING(pushDeletep(innerPropp), innerPropp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hoist a leading clocking event (IEEE 1800-2023 16.7:
|
||||||
|
// sequence_expr ::= clocking_event sequence_expr) from the sequence body onto
|
||||||
|
// the enclosing assertion clock. Returns true if E_UNSUPPORTED was emitted.
|
||||||
|
bool hoistClockedSeq(AstPropSpec* specp) {
|
||||||
|
while (AstSClocked* const clockedp = VN_CAST(specp->propp(), SClocked)) {
|
||||||
|
if (specp->sensesp()) {
|
||||||
|
clockedp->v3warn(E_UNSUPPORTED, "Unsupported: multiclocked sequence or property");
|
||||||
|
replaceBodyOnBuildError(specp->fileline(), specp, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const AstSenItem* sp = clockedp->sensesp(); sp;
|
||||||
|
sp = VN_CAST(sp->nextp(), SenItem)) {
|
||||||
|
if (!sp->edgeType().anEdge()) {
|
||||||
|
clockedp->v3warn(E_UNSUPPORTED,
|
||||||
|
"Unsupported: non-edge clocking event on a sequence; "
|
||||||
|
"use an edge such as @(posedge clk)");
|
||||||
|
replaceBodyOnBuildError(specp->fileline(), specp, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
specp->sensesp(clockedp->sensesp()->unlinkFrBackWithNext());
|
||||||
|
AstNodeExpr* const bodyp = clockedp->exprp()->unlinkFrBack();
|
||||||
|
clockedp->replaceWith(bodyp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(clockedp), clockedp);
|
||||||
|
}
|
||||||
|
// A clocking event anywhere else in the sequence is not supported.
|
||||||
|
const AstSClocked* nestedp = nullptr;
|
||||||
|
specp->propp()->foreach([&](const AstSClocked* p) {
|
||||||
|
if (!nestedp) nestedp = p;
|
||||||
|
});
|
||||||
|
if (nestedp) {
|
||||||
|
nestedp->v3warn(E_UNSUPPORTED,
|
||||||
|
"Unsupported: clocking event inside sequence expression");
|
||||||
|
replaceBodyOnBuildError(specp->fileline(), specp, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Build the NFA graph for a property body, handling both the antecedent
|
// Build the NFA graph for a property body, handling both the antecedent
|
||||||
// |-> consequent and simple sequence cases. Returns the consequent/body
|
// |-> consequent and simple sequence cases. Returns the consequent/body
|
||||||
// BuildResult (invalid on parse/build failure).
|
// BuildResult (invalid on parse/build failure).
|
||||||
|
|
@ -2189,6 +2228,10 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||||
|
|
||||||
inlineAllSequenceRefs(assertp->propp());
|
inlineAllSequenceRefs(assertp->propp());
|
||||||
|
|
||||||
|
if (AstPropSpec* const specp = VN_CAST(assertp->propp(), PropSpec)) {
|
||||||
|
if (hoistClockedSeq(specp)) return;
|
||||||
|
}
|
||||||
|
|
||||||
AstNode* const propp = assertp->propp();
|
AstNode* const propp = assertp->propp();
|
||||||
if (!hasMultiCycleExpr(propp)) return;
|
if (!hasMultiCycleExpr(propp)) return;
|
||||||
if (isBareTopLevelUntil(propp)) return;
|
if (isBareTopLevelUntil(propp)) return;
|
||||||
|
|
|
||||||
|
|
@ -2225,6 +2225,25 @@ public:
|
||||||
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
bool sameNode(const AstNode* /*samep*/) const override { return true; }
|
||||||
bool isSystemFunc() const override { return true; }
|
bool isSystemFunc() const override { return true; }
|
||||||
};
|
};
|
||||||
|
class AstSClocked final : public AstNodeExpr {
|
||||||
|
// Sequence expression with an explicit leading clocking event
|
||||||
|
// IEEE 1800-2023 16.7: sequence_expr ::= clocking_event sequence_expr
|
||||||
|
// The clocking event is hoisted to the enclosing assertion clock by V3AssertNfa.
|
||||||
|
// @astgen op1 := sensesp : AstSenItem
|
||||||
|
// @astgen op2 := exprp : AstNodeExpr
|
||||||
|
public:
|
||||||
|
AstSClocked(FileLine* fl, AstSenItem* sensesp, AstNodeExpr* exprp)
|
||||||
|
: ASTGEN_SUPER_SClocked(fl) {
|
||||||
|
this->sensesp(sensesp);
|
||||||
|
this->exprp(exprp);
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstSClocked;
|
||||||
|
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||||
|
int instrCount() const override { return widthInstrs(); }
|
||||||
|
bool isMultiCycleSva() const override { return false; }
|
||||||
|
};
|
||||||
class AstSConsRep final : public AstNodeExpr {
|
class AstSConsRep final : public AstNodeExpr {
|
||||||
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
|
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
|
||||||
// op1 := exprp -- the repeated expression
|
// op1 := exprp -- the repeated expression
|
||||||
|
|
|
||||||
|
|
@ -1838,6 +1838,22 @@ class WidthVisitor final : public VNVisitor {
|
||||||
nodep->dtypeSetBit();
|
nodep->dtypeSetBit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstSClocked* nodep) override {
|
||||||
|
VL_RESTORER(m_underSExpr);
|
||||||
|
m_underSExpr = true;
|
||||||
|
m_hasSExpr = true;
|
||||||
|
assertAtExpr(nodep);
|
||||||
|
if (m_vup->prelim()) {
|
||||||
|
// Width each clock expression directly; the senitem chain is hoisted to
|
||||||
|
// the assertion clock by V3AssertNfa, where it is fully processed.
|
||||||
|
for (AstSenItem* senp = nodep->sensesp(); senp;
|
||||||
|
senp = VN_CAST(senp->nextp(), SenItem)) {
|
||||||
|
userIterateAndNext(senp->sensp(), WidthVP{SELF, BOTH}.p());
|
||||||
|
}
|
||||||
|
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
|
||||||
|
nodep->dtypeSetBit();
|
||||||
|
}
|
||||||
|
}
|
||||||
void visit(AstURandomRange* nodep) override {
|
void visit(AstURandomRange* nodep) override {
|
||||||
assertAtExpr(nodep);
|
assertAtExpr(nodep);
|
||||||
if (m_vup->prelim()) {
|
if (m_vup->prelim()) {
|
||||||
|
|
|
||||||
|
|
@ -6653,6 +6653,12 @@ sequence_declarationBody<nodep>: // IEEE: part of sequence_declaration
|
||||||
| assertion_variable_declarationList sexpr ';' { $$ = addNextNull($1, $2); }
|
| assertion_variable_declarationList sexpr ';' { $$ = addNextNull($1, $2); }
|
||||||
| sexpr { $$ = $1; }
|
| sexpr { $$ = $1; }
|
||||||
| sexpr ';' { $$ = $1; }
|
| sexpr ';' { $$ = $1; }
|
||||||
|
// // IEEE: clocking_event sequence_expr (16.7)
|
||||||
|
// // A leading clocking event on a named sequence body.
|
||||||
|
| '@' '(' event_expression ')' sexpr { $$ = new AstSClocked{$1, $3, $5}; }
|
||||||
|
| '@' '(' event_expression ')' sexpr ';' { $$ = new AstSClocked{$1, $3, $5}; }
|
||||||
|
| '@' senitemVar sexpr { $$ = new AstSClocked{$1, $2, $3}; }
|
||||||
|
| '@' senitemVar sexpr ';' { $$ = new AstSClocked{$1, $2, $3}; }
|
||||||
;
|
;
|
||||||
|
|
||||||
property_spec<propSpecp>: // IEEE: property_spec
|
property_spec<propSpecp>: // IEEE: property_spec
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=['--assert', '--timing'])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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
|
||||||
|
module t (
|
||||||
|
input clk
|
||||||
|
);
|
||||||
|
int unsigned crc = 32'h1;
|
||||||
|
bit a, b;
|
||||||
|
int cyc = 0;
|
||||||
|
int fails_single = 0;
|
||||||
|
int fails_multi = 0;
|
||||||
|
|
||||||
|
// Single-cycle clocked sequence body (IEEE 1800-2023 16.7).
|
||||||
|
sequence s_single;
|
||||||
|
@(posedge clk) a;
|
||||||
|
endsequence
|
||||||
|
|
||||||
|
// Multi-cycle clocked sequence body.
|
||||||
|
sequence s_multi;
|
||||||
|
@(posedge clk) (a ##1 b);
|
||||||
|
endsequence
|
||||||
|
|
||||||
|
// Declared but never referenced; must not reach codegen.
|
||||||
|
sequence s_unused;
|
||||||
|
@(posedge clk) b;
|
||||||
|
endsequence
|
||||||
|
|
||||||
|
ap_single: assert property (s_single) else fails_single = fails_single + 1;
|
||||||
|
ap_multi: assert property (s_multi) else fails_multi = fails_multi + 1;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
crc <= {crc[30:0], crc[31] ^ crc[21] ^ crc[1] ^ crc[0]};
|
||||||
|
a <= crc[0];
|
||||||
|
b <= crc[1];
|
||||||
|
if (cyc == 40) $finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Counts read in final (Postponed) to avoid same-timestep races.
|
||||||
|
// Concrete Verilator counts; cross-checked equal in Questa 2022.3
|
||||||
|
// on the self-clocked equivalent (same CRC stimulus).
|
||||||
|
// Questa: fails_single=17 fails_multi=17
|
||||||
|
final begin
|
||||||
|
if (fails_single == 17 && fails_multi == 17) begin
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
end else begin
|
||||||
|
$write("FAILED fails_single=%0d fails_multi=%0d\n", fails_single, fails_multi);
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
%Error-UNSUPPORTED: t/t_assert_seq_clocking_bad.v:15:5: Unsupported: multiclocked sequence or property
|
||||||
|
: ... note: In instance 't'
|
||||||
|
15 | @(posedge clk) a;
|
||||||
|
| ^
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
%Error-UNSUPPORTED: t/t_assert_seq_clocking_bad.v:19:5: Unsupported: clocking event inside sequence expression
|
||||||
|
: ... note: In instance 't'
|
||||||
|
19 | @(posedge clk) b;
|
||||||
|
| ^
|
||||||
|
%Error-UNSUPPORTED: t/t_assert_seq_clocking_bad.v:23:5: Unsupported: non-edge clocking event on a sequence; use an edge such as @(posedge clk)
|
||||||
|
: ... note: In instance 't'
|
||||||
|
23 | @clk a;
|
||||||
|
| ^
|
||||||
|
%Error-UNSUPPORTED: t/t_assert_seq_clocking_bad.v:30:3: Unsupported: Unclocked assertion
|
||||||
|
: ... note: In instance 't'
|
||||||
|
30 | assert property (s_nest ##1 a);
|
||||||
|
| ^~~~~~
|
||||||
|
%Error-UNSUPPORTED: t/t_assert_seq_clocking_bad.v:33:3: Unsupported: Unclocked assertion
|
||||||
|
: ... note: In instance 't'
|
||||||
|
33 | assert property (s_level);
|
||||||
|
| ^~~~~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/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.lint(fails=True, expect_filename=test.golden_filename)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
// 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
|
||||||
|
module t (
|
||||||
|
input clk,
|
||||||
|
input clk2
|
||||||
|
);
|
||||||
|
logic a, b;
|
||||||
|
|
||||||
|
sequence s_multi;
|
||||||
|
@(posedge clk) a;
|
||||||
|
endsequence
|
||||||
|
|
||||||
|
sequence s_nest;
|
||||||
|
@(posedge clk) b;
|
||||||
|
endsequence
|
||||||
|
|
||||||
|
sequence s_level;
|
||||||
|
@clk a;
|
||||||
|
endsequence
|
||||||
|
|
||||||
|
// Multiclocked: explicit assertion clock differs from the sequence clock.
|
||||||
|
assert property (@(posedge clk2) s_multi);
|
||||||
|
|
||||||
|
// Clocking event nested inside a larger sequence expression.
|
||||||
|
assert property (s_nest ##1 a);
|
||||||
|
|
||||||
|
// Non-edge clocking event on a sequence.
|
||||||
|
assert property (s_level);
|
||||||
|
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue