This commit is contained in:
parent
327fc4ffbe
commit
bb1bfabab3
|
|
@ -1467,6 +1467,8 @@ public:
|
||||||
class AssertNfaVisitor final : public VNVisitor {
|
class AssertNfaVisitor final : public VNVisitor {
|
||||||
// STATE
|
// STATE
|
||||||
AstNodeModule* m_modp = nullptr; // Current module being processed
|
AstNodeModule* m_modp = nullptr; // Current module being processed
|
||||||
|
AstClocking* m_defaultClockingp = nullptr; // Default clocking
|
||||||
|
AstDefaultDisable* m_defaultDisablep = nullptr; // Default disable iff
|
||||||
SvaNfaLowering* m_loweringp = nullptr; // NFA-to-hardware lowering engine
|
SvaNfaLowering* m_loweringp = nullptr; // NFA-to-hardware lowering engine
|
||||||
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable names
|
V3UniqueNames m_propVarNames{"__Vpropvar"}; // Property-local variable names
|
||||||
V3UniqueNames m_disableCntNames{"__VnfaDis"}; // Disable-iff counter names
|
V3UniqueNames m_disableCntNames{"__VnfaDis"}; // Disable-iff counter names
|
||||||
|
|
@ -1815,6 +1817,13 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||||
bool senTreeOwned = false; // True if we created senTreep locally
|
bool senTreeOwned = false; // True if we created senTreep locally
|
||||||
AstPropSpec* const propSpecp = VN_CAST(assertp->propp(), PropSpec);
|
AstPropSpec* const propSpecp = VN_CAST(assertp->propp(), PropSpec);
|
||||||
UASSERT_OBJ(propSpecp, assertp, "Concurrent assertion must have PropSpec");
|
UASSERT_OBJ(propSpecp, assertp, "Concurrent assertion must have PropSpec");
|
||||||
|
// Inherit module defaults (IEEE 14.12, 16.15) when assertion has none.
|
||||||
|
if (!propSpecp->sensesp() && m_defaultClockingp) {
|
||||||
|
propSpecp->sensesp(m_defaultClockingp->sensesp()->cloneTree(true));
|
||||||
|
}
|
||||||
|
if (!propSpecp->disablep() && m_defaultDisablep) {
|
||||||
|
propSpecp->disablep(m_defaultDisablep->condp()->cloneTreePure(true));
|
||||||
|
}
|
||||||
if (!senTreep && propSpecp->sensesp()) {
|
if (!senTreep && propSpecp->sensesp()) {
|
||||||
senTreep
|
senTreep
|
||||||
= new AstSenTree{propSpecp->fileline(), propSpecp->sensesp()->cloneTree(true)};
|
= new AstSenTree{propSpecp->fileline(), propSpecp->sensesp()->cloneTree(true)};
|
||||||
|
|
@ -1886,11 +1895,22 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
VL_RESTORER(m_loweringp);
|
VL_RESTORER(m_loweringp);
|
||||||
|
VL_RESTORER(m_defaultClockingp);
|
||||||
|
VL_RESTORER(m_defaultDisablep);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
|
m_defaultClockingp = nullptr;
|
||||||
|
m_defaultDisablep = nullptr;
|
||||||
SvaNfaLowering lowering{nodep};
|
SvaNfaLowering lowering{nodep};
|
||||||
m_loweringp = &lowering;
|
m_loweringp = &lowering;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
void visit(AstClocking* nodep) override {
|
||||||
|
if (nodep->isDefault() && !m_defaultClockingp) m_defaultClockingp = nodep;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
void visit(AstDefaultDisable* nodep) override {
|
||||||
|
if (!m_defaultDisablep) m_defaultDisablep = nodep;
|
||||||
|
}
|
||||||
void visit(AstAssert* nodep) override { processAssertion(nodep); }
|
void visit(AstAssert* nodep) override { processAssertion(nodep); }
|
||||||
void visit(AstCover* nodep) override { processAssertion(nodep); }
|
void visit(AstCover* nodep) override { processAssertion(nodep); }
|
||||||
void visit(AstRestrict* nodep) override {
|
void visit(AstRestrict* nodep) override {
|
||||||
|
|
|
||||||
|
|
@ -240,6 +240,15 @@ private:
|
||||||
VL_RESTORER(m_clockingp);
|
VL_RESTORER(m_clockingp);
|
||||||
m_clockingp = nodep;
|
m_clockingp = nodep;
|
||||||
UINFO(8, " CLOCKING" << nodep);
|
UINFO(8, " CLOCKING" << nodep);
|
||||||
|
if (nodep->isDefault()) {
|
||||||
|
if (m_defaultClockingp) {
|
||||||
|
nodep->v3error("Only one default clocking block allowed per module"
|
||||||
|
" (IEEE 1800-2023 14.12)");
|
||||||
|
} else {
|
||||||
|
m_defaultClockingp = nodep;
|
||||||
|
m_defaultClkEvtVarp = nodep->ensureEventp();
|
||||||
|
}
|
||||||
|
}
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
if (nodep->eventp()) nodep->addNextHere(nodep->eventp()->unlinkFrBack());
|
if (nodep->eventp()) nodep->addNextHere(nodep->eventp()->unlinkFrBack());
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
|
|
@ -1182,7 +1191,12 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(AstDefaultDisable* nodep) override {
|
void visit(AstDefaultDisable* nodep) override {
|
||||||
// Done with these
|
if (m_defaultDisablep) {
|
||||||
|
nodep->v3error("Only one 'default disable iff' allowed per module"
|
||||||
|
" (IEEE 1800-2023 16.15)");
|
||||||
|
} else {
|
||||||
|
m_defaultDisablep = nodep;
|
||||||
|
}
|
||||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||||
}
|
}
|
||||||
void visit(AstInferredDisable* nodep) override {
|
void visit(AstInferredDisable* nodep) override {
|
||||||
|
|
@ -1293,29 +1307,8 @@ private:
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
m_defaultClockingp = nullptr;
|
m_defaultClockingp = nullptr;
|
||||||
m_defaultClkEvtVarp = nullptr;
|
m_defaultClkEvtVarp = nullptr;
|
||||||
nodep->foreach([&](AstClocking* const clockingp) {
|
|
||||||
if (clockingp->isDefault()) {
|
|
||||||
if (m_defaultClockingp) {
|
|
||||||
clockingp->v3error("Only one default clocking block allowed per module"
|
|
||||||
" (IEEE 1800-2023 14.12)");
|
|
||||||
}
|
|
||||||
m_defaultClockingp = clockingp;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
m_defaultDisablep = nullptr;
|
m_defaultDisablep = nullptr;
|
||||||
nodep->foreach([&](AstDefaultDisable* const disablep) {
|
|
||||||
if (m_defaultDisablep) {
|
|
||||||
disablep->v3error("Only one 'default disable iff' allowed per module"
|
|
||||||
" (IEEE 1800-2023 16.15)");
|
|
||||||
}
|
|
||||||
m_defaultDisablep = disablep;
|
|
||||||
});
|
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
// Pre-create and cache the clocking event var before iterating children.
|
|
||||||
// visit(AstClocking) will unlink the event from the clocking node and place it
|
|
||||||
// in the module tree, then delete the clocking. After that, ensureEventp() would
|
|
||||||
// create an orphaned var. Caching here avoids this.
|
|
||||||
m_defaultClkEvtVarp = m_defaultClockingp ? m_defaultClockingp->ensureEventp() : nullptr;
|
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstProperty* nodep) override {
|
void visit(AstProperty* nodep) override {
|
||||||
|
|
|
||||||
|
|
@ -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=['--assert', '--timing'])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
// 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);
|
||||||
|
`define checkd(gotv, expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||||
|
// verilog_format: on
|
||||||
|
|
||||||
|
// Issue #7472 reproducer (default clocking + |=> + throughout).
|
||||||
|
module wsn (
|
||||||
|
input clk,
|
||||||
|
input a, b, c, d
|
||||||
|
);
|
||||||
|
default clocking @(posedge clk); endclocking
|
||||||
|
int fail = 0;
|
||||||
|
assert property (
|
||||||
|
a |=> b throughout (c ##1 d)
|
||||||
|
) else fail <= fail + 1;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// Explicit @(edge) must override default clocking.
|
||||||
|
module clk_override (
|
||||||
|
input clk_default,
|
||||||
|
input clk_explicit,
|
||||||
|
input a, b, c, d
|
||||||
|
);
|
||||||
|
default clocking @(posedge clk_default); endclocking
|
||||||
|
int default_fail = 0;
|
||||||
|
int explicit_fail = 0;
|
||||||
|
assert property (a |=> b throughout (c ##1 d))
|
||||||
|
else default_fail <= default_fail + 1;
|
||||||
|
assert property (@(posedge clk_explicit) a |=> b throughout (c ##1 d))
|
||||||
|
else explicit_fail <= explicit_fail + 1;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// Explicit `disable iff` must override default disable.
|
||||||
|
module dgate (
|
||||||
|
input clk,
|
||||||
|
input rst,
|
||||||
|
input a, b, c, d
|
||||||
|
);
|
||||||
|
default clocking @(posedge clk); endclocking
|
||||||
|
default disable iff (rst);
|
||||||
|
int default_dis_fail = 0;
|
||||||
|
int explicit_dis_fail = 0;
|
||||||
|
assert property (a |=> b throughout (c ##1 d))
|
||||||
|
else default_dis_fail <= default_dis_fail + 1;
|
||||||
|
assert property (disable iff (1'b0) a |=> b throughout (c ##1 d))
|
||||||
|
else explicit_dis_fail <= explicit_dis_fail + 1;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// No defaults, explicit clock -- fix must not alter this path.
|
||||||
|
module nodef (
|
||||||
|
input clk,
|
||||||
|
input a, b, c, d
|
||||||
|
);
|
||||||
|
int fail = 0;
|
||||||
|
assert property (@(posedge clk) a |=> b throughout (c ##1 d))
|
||||||
|
else fail <= fail + 1;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t (
|
||||||
|
input clk
|
||||||
|
);
|
||||||
|
integer cyc = 0;
|
||||||
|
reg [63:0] crc = '0;
|
||||||
|
|
||||||
|
wire a = crc[0];
|
||||||
|
wire b = crc[4];
|
||||||
|
wire c = crc[8];
|
||||||
|
wire d = crc[12];
|
||||||
|
|
||||||
|
wire clk_alt = ~clk;
|
||||||
|
wire rst = (cyc < 10);
|
||||||
|
|
||||||
|
default clocking @(posedge clk); endclocking
|
||||||
|
default disable iff (cyc < 10);
|
||||||
|
|
||||||
|
int count_fail1 = 0;
|
||||||
|
int count_fail2 = 0;
|
||||||
|
int count_fail3 = 0;
|
||||||
|
int count_fail4 = 0;
|
||||||
|
|
||||||
|
// Issue #7472 exact shape.
|
||||||
|
assert property (a |=> b throughout (c ##1 d))
|
||||||
|
else count_fail1 <= count_fail1 + 1;
|
||||||
|
assert property (a |-> b throughout (c ##1 d))
|
||||||
|
else count_fail2 <= count_fail2 + 1;
|
||||||
|
assert property (a |=> c ##1 d)
|
||||||
|
else count_fail3 <= count_fail3 + 1;
|
||||||
|
assert property (a |=> b throughout (c throughout (c ##1 d)))
|
||||||
|
else count_fail4 <= count_fail4 + 1;
|
||||||
|
|
||||||
|
cover property (a throughout (c ##[1:2] d));
|
||||||
|
cover property (a |=> b throughout (c ##1 d));
|
||||||
|
|
||||||
|
// Generate-scope: module foreach must reach inside generate blocks.
|
||||||
|
generate
|
||||||
|
if (1) begin : g
|
||||||
|
int gen_fail = 0;
|
||||||
|
assert property (a |=> b throughout (c ##1 d))
|
||||||
|
else gen_fail <= gen_fail + 1;
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
wsn u_wsn (.clk(clk), .a(a), .b(b), .c(c), .d(d));
|
||||||
|
clk_override u_override (.clk_default(clk), .clk_explicit(clk_alt),
|
||||||
|
.a(a), .b(b), .c(c), .d(d));
|
||||||
|
dgate u_dgate (.clk(clk), .rst(rst), .a(a), .b(b), .c(c), .d(d));
|
||||||
|
nodef u_nodef (.clk(clk), .a(a), .b(b), .c(c), .d(d));
|
||||||
|
|
||||||
|
always_ff @(posedge clk) begin
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};
|
||||||
|
if (cyc == 0) begin
|
||||||
|
crc <= 64'h5aef0c8d_d70a4497;
|
||||||
|
end else if (cyc == 99) begin
|
||||||
|
`checkh(crc, 64'hc77bb9b3784ea091);
|
||||||
|
`checkd(count_fail1, 35);
|
||||||
|
`checkd(count_fail2, 36);
|
||||||
|
`checkd(count_fail3, 29);
|
||||||
|
`checkd(count_fail4, 35);
|
||||||
|
`checkd(u_wsn.fail, 39);
|
||||||
|
`checkd(u_override.default_fail, 39);
|
||||||
|
`checkd(u_override.explicit_fail, 39);
|
||||||
|
`checkd(u_dgate.default_dis_fail, 35);
|
||||||
|
`checkd(u_dgate.explicit_dis_fail, 39);
|
||||||
|
`checkd(u_nodef.fail, 39);
|
||||||
|
`checkd(g.gen_fail, 35);
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue