parent
c3fc0d9f0f
commit
9ea7abd1c7
|
|
@ -47,6 +47,7 @@ private:
|
|||
AstClocking* m_clockingp = nullptr; // Current clocking block
|
||||
// Reset each module:
|
||||
AstClocking* m_defaultClockingp = nullptr; // Default clocking for current module
|
||||
AstVar* m_defaultClkEvtVarp = nullptr; // Event var for default clocking (for ##0)
|
||||
AstDefaultDisable* m_defaultDisablep = nullptr; // Default disable for current module
|
||||
// Reset each assertion:
|
||||
AstSenItem* m_senip = nullptr; // Last sensitivity
|
||||
|
|
@ -376,9 +377,39 @@ private:
|
|||
nodep->v3error(
|
||||
"Delay value is not an elaboration-time constant (IEEE 1800-2023 16.7)");
|
||||
} else if (constp->isZero()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
VL_DO_DANGLING(pushDeletep(valuep), valuep);
|
||||
if (m_inSynchDrive) {
|
||||
// ##0 has no effect in synchronous drives (IEEE 1800-2023 14.11)
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
if (m_inPExpr) {
|
||||
// ##0 in sequence context = zero delay = same clock tick
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
// Procedural ##0: synchronize with default clocking event (IEEE 1800-2023 14.11)
|
||||
// If the clocking event has not yet occurred this timestep, wait for it;
|
||||
// otherwise continue without suspension.
|
||||
if (!m_defaultClockingp) {
|
||||
nodep->v3error("Usage of cycle delays requires default clocking"
|
||||
" (IEEE 1800-2023 14.11)");
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
}
|
||||
AstVar* const evtVarp = m_defaultClkEvtVarp;
|
||||
UASSERT_OBJ(evtVarp, nodep, "Default clocking event var not pre-created");
|
||||
AstCMethodHard* const isTriggeredp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, evtVarp, VAccess::READ}, VCMethod::EVENT_IS_TRIGGERED};
|
||||
isTriggeredp->dtypeSetBit();
|
||||
AstEventControl* const waitp = new AstEventControl{
|
||||
flp,
|
||||
new AstSenTree{flp, new AstSenItem{flp, VEdgeType::ET_EVENT,
|
||||
new AstVarRef{flp, evtVarp, VAccess::READ}}},
|
||||
nullptr};
|
||||
AstIf* const ifp = new AstIf{flp, new AstNot{flp, isTriggeredp}, waitp};
|
||||
nodep->replaceWith(ifp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
AstSenItem* sensesp = nullptr;
|
||||
|
|
@ -806,9 +837,11 @@ private:
|
|||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_defaultClockingp);
|
||||
VL_RESTORER(m_defaultClkEvtVarp);
|
||||
VL_RESTORER(m_defaultDisablep);
|
||||
VL_RESTORER(m_modp);
|
||||
m_defaultClockingp = nullptr;
|
||||
m_defaultClkEvtVarp = nullptr;
|
||||
nodep->foreach([&](AstClocking* const clockingp) {
|
||||
if (clockingp->isDefault()) {
|
||||
if (m_defaultClockingp) {
|
||||
|
|
@ -827,6 +860,11 @@ private:
|
|||
m_defaultDisablep = disablep;
|
||||
});
|
||||
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);
|
||||
}
|
||||
void visit(AstProperty* nodep) override {
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
%Error-UNSUPPORTED: t/t_clocking_unsup2.v:14:10: Unsupported: ##0 delays
|
||||
: ... note: In instance 't'
|
||||
14 | always ##0;
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -11,6 +11,6 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
test.lint()
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -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', '--timing'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
// 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\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic clk = 0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
logic [7:0] data_ref;
|
||||
logic [7:0] data_test;
|
||||
|
||||
default clocking cb @(posedge clk);
|
||||
output #0 data_ref;
|
||||
output #0 data_test;
|
||||
endclocking
|
||||
|
||||
// =========================================================
|
||||
// Branch 1: Procedural ##0 (IEEE 1800-2023 14.11)
|
||||
// - Before clocking event: suspend until event fires
|
||||
// - After clocking event: continue without suspension
|
||||
// =========================================================
|
||||
|
||||
// 1a: ##0 at time 0 -- event has not yet fired, must wait
|
||||
initial begin
|
||||
##0;
|
||||
`checkd(int'($time), 5)
|
||||
end
|
||||
|
||||
// 1b: ##0 after @(posedge clk) -- event already fired, no-op
|
||||
initial begin
|
||||
@(posedge clk);
|
||||
##0;
|
||||
`checkd(int'($time), 5)
|
||||
end
|
||||
|
||||
// 1c: Multiple consecutive ##0 all resolve at same time
|
||||
initial begin
|
||||
##0;
|
||||
##0;
|
||||
##0;
|
||||
`checkd(int'($time), 5)
|
||||
end
|
||||
|
||||
// 1d: ##0 followed by ##1 -- verify cycle counting still works
|
||||
initial begin
|
||||
##0;
|
||||
`checkd(int'($time), 5)
|
||||
##1;
|
||||
`checkd(int'($time), 15)
|
||||
end
|
||||
|
||||
// =========================================================
|
||||
// Branch 2: ##0 in synchronous drive (IEEE 1800-2023 14.11)
|
||||
// "shall have no effect, as if it were not present"
|
||||
// cb.data <= ##0 value behaves same as cb.data <= value
|
||||
// =========================================================
|
||||
|
||||
// Drive both at the same posedge: one with ##0, one without.
|
||||
// If ##0 is truly "no effect", both signals update in the same cycle.
|
||||
always begin
|
||||
@(posedge clk);
|
||||
cb.data_ref <= 8'hAB; // no ##0 -- baseline
|
||||
cb.data_test <= ##0 8'hAB; // with ##0 -- should behave identically
|
||||
@(posedge clk);
|
||||
`checkd(data_test, data_ref)
|
||||
`checkd(data_test, 8'hAB)
|
||||
wait(0);
|
||||
end
|
||||
|
||||
// =========================================================
|
||||
// Branch 3: ##0 in property/sequence context
|
||||
// a ##0 b = both a and b hold at the same clock tick
|
||||
//
|
||||
// =========================================================
|
||||
|
||||
logic p = 0, q = 0;
|
||||
int cycle = 0;
|
||||
int assert_pass_count = 0;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cycle <= cycle + 1;
|
||||
p <= (cycle == 3); // p=1 only at cycle 4
|
||||
q <= (cycle == 3); // q=1 only at cycle 4
|
||||
end
|
||||
|
||||
// Pass action block: only incremented on nonvacuous success
|
||||
assert property (@(posedge clk) p |-> (p ##0 q))
|
||||
assert_pass_count++;
|
||||
else
|
||||
$error("Branch 3: assertion (p |-> p ##0 q) failed");
|
||||
|
||||
// =========================================================
|
||||
// Completion
|
||||
// =========================================================
|
||||
|
||||
initial begin
|
||||
#200;
|
||||
if (assert_pass_count == 0) begin
|
||||
$write("%%Error: assertion (p |-> p ##0 q) never passed non-vacuously\n");
|
||||
`stop;
|
||||
end
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
%Error: t/t_clocking_zero_delay_bad.v:15:17: Cycle delays not allowed as intra-assignment delays (IEEE 1800-2023 14.11)
|
||||
: ... note: In instance 'has_clocking'
|
||||
15 | always out <= ##0 1;
|
||||
| ^~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_clocking_zero_delay_bad.v:20:11: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11)
|
||||
: ... note: In instance 'no_clocking'
|
||||
20 | initial ##0;
|
||||
| ^~
|
||||
%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,21 @@
|
|||
// 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
|
||||
|
||||
/* verilator lint_off MULTITOP */
|
||||
|
||||
// Bad: ##0 as intra-assignment delay (IEEE 1800-2023 14.11)
|
||||
module has_clocking;
|
||||
logic clk = 0;
|
||||
logic out;
|
||||
default clocking cb @(posedge clk);
|
||||
endclocking
|
||||
always out <= ##0 1;
|
||||
endmodule
|
||||
|
||||
// Bad: ##0 without default clocking (IEEE 1800-2023 14.11)
|
||||
module no_clocking;
|
||||
initial ##0;
|
||||
endmodule
|
||||
Loading…
Reference in New Issue