Support clocking event on a sequence declaration body (#7598) (#7793)

Fixes #7598.
This commit is contained in:
Yilou Wang 2026-06-17 23:57:18 +02:00 committed by GitHub
parent 26c85d4495
commit 3a4377d39e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 258 additions and 0 deletions

View File

@ -2162,6 +2162,43 @@ class AssertNfaVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(innerPropp), innerPropp);
}
// Hoist a leading clocking event (IEEE 1800-2023 16.7):
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
// |-> consequent and simple sequence cases. Returns the consequent/body
// BuildResult (invalid on parse/build failure).
@ -2337,6 +2374,10 @@ class AssertNfaVisitor final : public VNVisitor {
inlineAllSequenceRefs(assertp->propp());
if (AstPropSpec* const specp = VN_CAST(assertp->propp(), PropSpec)) {
if (hoistClockedSeq(specp)) return;
}
AstNode* const propp = assertp->propp();
if (!hasMultiCycleExpr(propp)) return;
if (isBareTopLevelUntil(propp)) return;

View File

@ -2225,6 +2225,23 @@ public:
bool sameNode(const AstNode* /*samep*/) 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(false); }
};
class AstSConsRep final : public AstNodeExpr {
// Consecutive repetition [*N], [*N:M], [+], [*] (IEEE 1800-2023 16.9.2)
// op1 := exprp -- the repeated expression

View File

@ -1142,6 +1142,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
puts(") ");
iterateConst(nodep->propp());
}
void visit(AstSClocked* nodep) override {
puts("@(");
iterateConst(nodep->sensesp());
puts(") ");
iterateConst(nodep->exprp());
puts("\n");
}
void visit(AstPropAlways* nodep) override {
puts(nodep->isStrong() ? "s_always" : "always");
if (!VN_IS(nodep->loBoundp(), Unbounded) || !VN_IS(nodep->hiBoundp(), Unbounded)) {

View File

@ -1837,6 +1837,22 @@ class WidthVisitor final : public VNVisitor {
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 {
assertAtExpr(nodep);
if (m_vup->prelim()) {

View File

@ -6673,6 +6673,12 @@ sequence_declarationBody<nodep>: // IEEE: part of sequence_declaration
| assertion_variable_declarationList sexpr ';' { $$ = addNextNull($1, $2); }
| 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

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')
test.compile(verilator_flags2=['--assert', '--timing'])
test.execute()
test.passes()

View File

@ -0,0 +1,53 @@
// 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 checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
module t (
input clk
);
int unsigned crc = 32'h1;
bit a, b;
int cyc = 0;
int fails_single = 0;
int fails_multi = 0;
// verilog_format: off // verible does not support clocking events inside sequence declarations
sequence s_single;
@(posedge clk) a
endsequence
sequence s_multi;
@(posedge clk) (a ##1 b);
endsequence
sequence s_unused;
@(posedge clk) b;
endsequence
// verilog_format: on
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; Questa: fails_single=17 fails_multi=17
final begin
`checkd(fails_single, 17);
`checkd(fails_multi, 17);
$write("*-* All Finished *-*\n");
end
endmodule

View File

@ -0,0 +1,30 @@
%Error-UNSUPPORTED: t/t_assert_seq_clocking_unsup.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_unsup.v:19:5: Unsupported: clocking event inside sequence expression
: ... note: In instance 't'
19 | @(posedge clk) b;
| ^
%Error-UNSUPPORTED: t/t_assert_seq_clocking_unsup.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_unsup.v:27:5: Unsupported: non-edge clocking event on a sequence; use an edge such as @(posedge clk)
: ... note: In instance 't'
27 | @clk a
| ^
%Error-UNSUPPORTED: t/t_assert_seq_clocking_unsup.v:32:3: Unsupported: Unclocked assertion
: ... note: In instance 't'
32 | assert property (s_nest ##1 a);
| ^~~~~~
%Error-UNSUPPORTED: t/t_assert_seq_clocking_unsup.v:33:3: Unsupported: Unclocked assertion
: ... note: In instance 't'
33 | assert property (s_level);
| ^~~~~~
%Error-UNSUPPORTED: t/t_assert_seq_clocking_unsup.v:34:3: Unsupported: Unclocked assertion
: ... note: In instance 't'
34 | assert property (s_level2);
| ^~~~~~
%Error: Exiting due to

View File

@ -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()

View File

@ -0,0 +1,36 @@
// 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,
input clk2
);
logic a, b;
// verilog_format: off
sequence s_multi;
@(posedge clk) a;
endsequence
sequence s_nest;
@(posedge clk) b;
endsequence
sequence s_level;
@clk a;
endsequence
sequence s_level2;
@clk a
endsequence
// verilog_format: on
assert property (@(posedge clk2) s_multi);
assert property (s_nest ##1 a);
assert property (s_level);
assert property (s_level2);
endmodule

View File

@ -364,6 +364,16 @@ module Vt_debug_emitv_t;
release sum;
end
end
sequence s_clocked;
@(posedge clk) in
endsequence
begin : assert_seq_clocked
assert property ( s_clocked()
) begin
end
else begin
end
end
property p;
@(posedge clk) ##1 sum[0]
endproperty

View File

@ -286,6 +286,14 @@ module t (/*AUTOARG*/
release sum;
end
// verilog_format: off // verible does not support clocking events inside sequence declarations
sequence s_clocked;
@(posedge clk) in
endsequence
// verilog_format: on
assert_seq_clocked: assert property (s_clocked);
property p;
@(posedge clk) ##1 sum[0]
endproperty