Add `+verilator+assert+lock` to ignore RTL assert control statements (#8086)
Co-authored-by: Sumanth Kadiyala <sumanth@hudson-trading.com>
This commit is contained in:
parent
0ebb92da26
commit
44fe96b2da
|
|
@ -636,6 +636,7 @@ description of these arguments.
|
||||||
|
|
||||||
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
|
=for VL_SPHINX_EXTRACT "_build/gen/args_verilated.rst"
|
||||||
|
|
||||||
|
+verilator+assert+lock Lock assertion status changes at startup
|
||||||
+verilator+coverage+file+<filename> Set coverage output filename
|
+verilator+coverage+file+<filename> Set coverage output filename
|
||||||
+verilator+debug Enable debugging
|
+verilator+debug Enable debugging
|
||||||
+verilator+debugi+<value> Enable debugging at a level
|
+verilator+debugi+<value> Enable debugging at a level
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,13 @@ Summary:
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
|
.. option:: +verilator+assert+lock
|
||||||
|
|
||||||
|
Only allow command line options to disable / enable assertions.
|
||||||
|
Disables RTL from changing assertion handling via ``$asserton``,
|
||||||
|
``$assertoff``, and ``$assertcontrol``. Also prevents ``VerilatedContext*``
|
||||||
|
assertion control functions from updating assertion handling.
|
||||||
|
|
||||||
.. option:: +verilator+coverage+file+<filename>
|
.. option:: +verilator+coverage+file+<filename>
|
||||||
|
|
||||||
When a model was Verilated using :vlopt:`--coverage`, sets the filename
|
When a model was Verilated using :vlopt:`--coverage`, sets the filename
|
||||||
|
|
|
||||||
|
|
@ -3092,6 +3092,7 @@ VerilatedContext::Serialized::Serialized() {
|
||||||
|
|
||||||
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
|
||||||
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
|
||||||
|
if (assertCtlsLocked()) return;
|
||||||
// Set all assert and directive types when true, clear otherwise.
|
// Set all assert and directive types when true, clear otherwise.
|
||||||
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
|
||||||
}
|
}
|
||||||
|
|
@ -3110,16 +3111,26 @@ uint32_t VerilatedContext::assertOnMask(VerilatedAssertType_t types,
|
||||||
}
|
}
|
||||||
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
|
if (assertCtlsLocked()) return;
|
||||||
m_s.m_assertOn |= assertOnMask(types, directives);
|
m_s.m_assertOn |= assertOnMask(types, directives);
|
||||||
}
|
}
|
||||||
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
|
if (assertCtlsLocked()) return;
|
||||||
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
m_s.m_assertOn &= ~assertOnMask(types, directives);
|
||||||
}
|
}
|
||||||
|
bool VerilatedContext::assertCtlsLocked() const VL_MT_SAFE {
|
||||||
|
return m_ns.m_assertCtlsLocked;
|
||||||
|
}
|
||||||
|
void VerilatedContext::assertCtlsLocked(bool flag) VL_MT_SAFE {
|
||||||
|
m_ns.m_assertCtlsLocked = flag;
|
||||||
|
}
|
||||||
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
void VerilatedContext::assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
|
||||||
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
|
// IEEE 1800-2023 Table 20-5 control_type. Lock freezes the On/Off state of the
|
||||||
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
|
// selected bits until Unlock; On/Off/Kill leave locked bits unchanged.
|
||||||
|
// +verilator+assert+lock freezes everything, including Lock/Unlock itself.
|
||||||
|
if (assertCtlsLocked()) return;
|
||||||
const uint32_t mask = assertOnMask(types, directives);
|
const uint32_t mask = assertOnMask(types, directives);
|
||||||
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
const uint32_t lockedMask = mask & ~m_s.m_assertLock;
|
||||||
switch (controlType) {
|
switch (controlType) {
|
||||||
|
|
@ -3570,7 +3581,9 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||||
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
|
if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
|
||||||
std::string str;
|
std::string str;
|
||||||
uint64_t u64;
|
uint64_t u64;
|
||||||
if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
if (arg == "+verilator+assert+lock") {
|
||||||
|
assertCtlsLocked(true);
|
||||||
|
} else if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
|
||||||
coverageFilename(str);
|
coverageFilename(str);
|
||||||
} else if (arg == "+verilator+debug") {
|
} else if (arg == "+verilator+debug") {
|
||||||
Verilated::debug(4);
|
Verilated::debug(4);
|
||||||
|
|
@ -3589,7 +3602,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
||||||
logFilename(str);
|
logFilename(str);
|
||||||
logOutputToFile(false /* append */);
|
logOutputToFile(false /* append */);
|
||||||
} else if (arg == "+verilator+noassert") {
|
} else if (arg == "+verilator+noassert") {
|
||||||
assertOn(false);
|
// Set directly on to avoid conflicts with +verilator+assert+lock
|
||||||
|
m_s.m_assertOn = 0;
|
||||||
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) {
|
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) {
|
||||||
profExecStart(u64);
|
profExecStart(u64);
|
||||||
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
|
} else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
|
||||||
|
|
|
||||||
|
|
@ -450,6 +450,7 @@ protected:
|
||||||
// A worker queues $finish before the main thread callback can set m_gotFinish.
|
// A worker queues $finish before the main thread callback can set m_gotFinish.
|
||||||
std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks
|
std::atomic<uint32_t> m_finishPending{0}; // Number of queued $finish callbacks
|
||||||
std::atomic<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback
|
std::atomic<uint64_t> m_finishPendingTime{TIME_UNSET}; // Time of the first callback
|
||||||
|
std::atomic<bool> m_assertCtlsLocked{false}; // When true, all assertion-control updates are ignored
|
||||||
int m_stopReserved = 0; // Posted $stop requests not yet executed
|
int m_stopReserved = 0; // Posted $stop requests not yet executed
|
||||||
bool m_executingFinal = false; // Running generated final() code
|
bool m_executingFinal = false; // Running generated final() code
|
||||||
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
uint64_t m_profExecStart = 1; // +prof+exec+start time
|
||||||
|
|
@ -531,6 +532,12 @@ public:
|
||||||
/// Clear enabled status for given assertion types
|
/// Clear enabled status for given assertion types
|
||||||
void assertOnClear(VerilatedAssertType_t types,
|
void assertOnClear(VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||||
|
/// Return if assertion-control updates are locked. When locked, RTL assert
|
||||||
|
// control statements ($asserton/$assertoff/$assertcontrol) are ignored, as
|
||||||
|
// are updates from the C++ API.
|
||||||
|
bool assertCtlsLocked() const VL_MT_SAFE;
|
||||||
|
/// Lock/unlock assertion-control updates.
|
||||||
|
void assertCtlsLocked(bool flag) VL_MT_SAFE;
|
||||||
/// Apply assertion control for given control, assertion, and directive types
|
/// Apply assertion control for given control, assertion, and directive types
|
||||||
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
void assertCtl(uint32_t controlType, VerilatedAssertType_t types,
|
||||||
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
|
||||||
|
|
|
||||||
|
|
@ -138,8 +138,34 @@ void verilatedTest() {
|
||||||
contextp->assertCtl(FAIL_ON, TYPE, DIRECTIVE);
|
contextp->assertCtl(FAIL_ON, TYPE, DIRECTIVE);
|
||||||
TEST_CHECK_NZ(contextp->assertCtlGet(Query::ASSERT_CTL_FAIL_ON, TYPE, DIRECTIVE));
|
TEST_CHECK_NZ(contextp->assertCtlGet(Query::ASSERT_CTL_FAIL_ON, TYPE, DIRECTIVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void verilatedLockedTest() {
|
||||||
|
// With +verilator+assert+lock, every assertion-control update is a no-op
|
||||||
|
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||||
|
|
||||||
|
// Clear some bits to test assertOnSet
|
||||||
|
contextp->assertOnClear(2, 3);
|
||||||
|
const char* argsp[] = {"+verilator+assert+lock"};
|
||||||
|
contextp->commandArgsAdd(1, argsp);
|
||||||
|
TEST_CHECK_EQ(contextp->assertCtlsLocked(), true);
|
||||||
|
|
||||||
|
// Validate each assert control API call is a no-op
|
||||||
|
contextp->assertOn(false);
|
||||||
|
TEST_CHECK_NZ(contextp->assertOn());
|
||||||
|
|
||||||
|
contextp->assertOnSet(2, 3);
|
||||||
|
TEST_CHECK_Z(contextp->assertOnGet(2, 3));
|
||||||
|
|
||||||
|
contextp->assertOnClear(1, 1);
|
||||||
|
TEST_CHECK_NZ(contextp->assertOnGet(1, 1));
|
||||||
|
|
||||||
|
contextp->assertCtl(4, 4, 1);
|
||||||
|
TEST_CHECK_NZ(contextp->assertOnGet(4, 1));
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
verilatedTest();
|
verilatedTest();
|
||||||
|
verilatedLockedTest();
|
||||||
if (errors) return 10;
|
if (errors) return 10;
|
||||||
|
|
||||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||||
|
|
|
||||||
|
|
@ -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=["--binary --assert"])
|
||||||
|
|
||||||
|
test.execute(all_run_flags=["+verilator+assert+lock"])
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
// Run with +verilator+assert+lock, which freezes the enabled assertions, so
|
||||||
|
// every assertion control below is ignored and all directives keep firing.
|
||||||
|
|
||||||
|
// verilog_format: off
|
||||||
|
`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
|
||||||
|
|
||||||
|
`ifdef T_ASSERT_CTL_LOCK_ARG_NOASSERT
|
||||||
|
`define ctl_on_off 3 // On, ignored as the assertions stay disabled
|
||||||
|
`define expected 0
|
||||||
|
`else
|
||||||
|
`define ctl_on_off 4 // Off, ignored as the assertions stay enabled
|
||||||
|
`define expected 10
|
||||||
|
`endif
|
||||||
|
|
||||||
|
module t;
|
||||||
|
logic clk = 0;
|
||||||
|
logic fals = 1'b0;
|
||||||
|
|
||||||
|
int imm_fails = 0;
|
||||||
|
int conc_fails = 0;
|
||||||
|
int assume_fails = 0;
|
||||||
|
int ctl;
|
||||||
|
|
||||||
|
always #5 clk = ~clk;
|
||||||
|
|
||||||
|
// posedge clk at t = 5, 15, 25, 35, 45, 55, 65, 75, 85, 95
|
||||||
|
always @(posedge clk) imm_assert : assert (fals) else imm_fails = imm_fails + 1;
|
||||||
|
|
||||||
|
conc_assert : assert property (@(posedge clk) fals) else conc_fails = conc_fails + 1;
|
||||||
|
|
||||||
|
conc_assume : assume property (@(posedge clk) fals) else assume_fails = assume_fails + 1;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
#6; // t=6
|
||||||
|
$assertcontrol(`ctl_on_off); // On or Off, whichever would flip all types
|
||||||
|
#10; // t=16
|
||||||
|
$assertcontrol(`ctl_on_off, 2); // Same, but only for SIMPLE_IMMEDIATE
|
||||||
|
#10; // t=26
|
||||||
|
$assertcontrol(1); // Lock
|
||||||
|
$assertcontrol(2); // Unlock
|
||||||
|
#10; // t=36
|
||||||
|
$assertcontrol(5); // Kill
|
||||||
|
#10; // t=46
|
||||||
|
$assertpassoff;
|
||||||
|
$assertfailoff;
|
||||||
|
#10; // t=56
|
||||||
|
ctl = `ctl_on_off;
|
||||||
|
$assertcontrol(ctl); // Same again, via a non-constant control_type
|
||||||
|
#10; // t=66
|
||||||
|
$assertkill;
|
||||||
|
#34; // t=100
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Every directive kept the enabled state it started with, as no control
|
||||||
|
// above took effect
|
||||||
|
final begin
|
||||||
|
`checkd(imm_fails, `expected);
|
||||||
|
`checkd(conc_fails, `expected);
|
||||||
|
`checkd(assume_fails, `expected);
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/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.top_filename = "t_assert_ctl_lock_arg.v"
|
||||||
|
|
||||||
|
test.compile(
|
||||||
|
verilator_flags2=["--binary --assert +define+T_ASSERT_CTL_LOCK_ARG_NOASSERT"])
|
||||||
|
|
||||||
|
test.execute(all_run_flags=["+verilator+noassert", "+verilator+assert+lock"])
|
||||||
|
|
||||||
|
# Same result with the arguments in the opposite order
|
||||||
|
test.execute(all_run_flags=["+verilator+assert+lock", "+verilator+noassert"])
|
||||||
|
|
||||||
|
test.passes()
|
||||||
Loading…
Reference in New Issue