Support force/release with a variable reference (#5721) (#5810)

This commit is contained in:
Bartłomiej Chmiel 2025-03-04 16:12:02 +01:00 committed by GitHub
parent 27d3eb5b7b
commit a3b2c2af17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 648 additions and 103 deletions

View File

@ -451,17 +451,14 @@ disable
force, release
Verilator supports the procedural `force` (and corresponding `release`)
statement. However, the behavior of the `force` statement does not
entirely comply with IEEE 1800. According to the standard, when a
procedural statement of the form `force a = b;` is executed, the
simulation should behave as if, from that point forwards, a continuous
assignment `assign a = b;` has been added to override the drivers of `a`.
More specifically: the value of `a` should be updated whenever the value
of `b` changes, until a `release a;` statement is executed. Verilator
instead evaluates the current value of `b` when the `force` statement is
executed, and forces `a` to that value, without updating it until a new
`force` or `release` statement is encountered that applies to `a`. This
non-standard behavior is nevertheless consistent with some other
simulators.
entirely comply with IEEE 1800-2023:
1. Using forced variable as a value to another force statement is currently
not supported. The dependant force statement is forced by an initial
constant value.
2. Force/release with procedural continuous assignment is not supported.
Assignment is treated as a procedural one.
3. Expressions using multiple variable references or function calls on
forced right-hand side are not sensitive to dependency changes.
inside
Inside expressions may not include unpacked array traversal or $ as an

View File

@ -99,7 +99,7 @@ public:
}
// Get a pointer to the user data if exists, otherwise nullptr
T_Data* tryGet(const T_Node* nodep) { return getUserp(nodep); }
T_Data* tryGet(const T_Node* nodep) const { return getUserp(nodep); }
void clear() { m_allocated.clear(); }
};

View File

@ -19,22 +19,27 @@
// add 3 extra signals:
// - <name>__VforceRd: a net with same type as signal
// - <name>__VforceEn: a var with same type as signal, which is the bitwise force enable
// - <name>__VforceVl: a var with same type as signal, which is the forced value
// - <name>__VforceVal: a var with same type as signal, which is the forced value
// add an initial statement:
// initial <name>__VforceEn = 0;
// add a continuous assignment:
// assign <name>__VforceRd = <name>__VforceEn ? <name>__VforceVl : <name>;
// assign <name>__VforceRd = <name>__VforceEn ? <name>__VforceVal : <name>;
// replace all READ references to <name> with a read reference to <name>_VforceRd
//
// Replace each AstAssignForce with 3 assignments:
// - <lhs>__VforceEn = 1
// - <lhs>__VforceVl = <rhs>
// - <lhs>__VforceVal = <rhs>
// - <lhs>__VforceRd = <rhs>
//
// Replace each AstRelease with 1 or 2 assignments:
// - <lhs>__VforceEn = 0
// - <lhs>__VforceRd = <lhs> // iff lhs is a net
//
// After each WRITE of forced LHS
// reevaluate <lhs>__VforceRd to support immediate force/release
//
// After each WRITE of forced RHS
// reevaluate <lhs>__VforceVal to support VarRef rollback after release
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
@ -48,7 +53,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Convert force/release statements and signals marked 'forceable'
class ForceConvertVisitor final : public VNVisitor {
class ForceState final {
// TYPES
struct ForceComponentsVar final {
AstVar* const m_rdVarp; // New variable to replace read references with
@ -59,22 +64,24 @@ class ForceConvertVisitor final : public VNVisitor {
varp->dtypep()}}
, m_valVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceVal",
varp->dtypep()}}
, m_enVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn",
(isRangedDType(varp) ? varp->dtypep() : varp->findBitDType())}} {
, m_enVarp{new AstVar{
varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn",
(ForceState::isRangedDType(varp) ? varp->dtypep() : varp->findBitDType())}} {
m_rdVarp->addNext(m_enVarp);
m_rdVarp->addNext(m_valVarp);
varp->addNextHere(m_rdVarp);
}
};
public:
struct ForceComponentsVarScope final {
AstVarScope* const m_rdVscp; // New variable to replace read references with
AstVarScope* const m_enVscp; // Force enabled signal
AstVarScope* const m_valVscp; // Forced value
AstVarScope* const m_enVscp; // Force enabled signal
explicit ForceComponentsVarScope(AstVarScope* vscp, ForceComponentsVar& fcv)
: m_rdVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_rdVarp}}
, m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}}
, m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}} {
, m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}}
, m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}} {
m_rdVscp->addNext(m_enVscp);
m_rdVscp->addNext(m_valVscp);
vscp->addNextHere(m_rdVscp);
@ -89,64 +96,106 @@ class ForceConvertVisitor final : public VNVisitor {
AstAssign* const assignp = new AstAssign{flp, lhsp, rhsp};
AstActive* const activep = new AstActive{
flp, "force-init",
new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Initial{}}}};
new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Static{}}}};
activep->sensesStorep(activep->sensesp());
activep->addStmtsp(new AstInitial{flp, assignp});
vscp->scopep()->addBlocksp(activep);
}
{ // Add the combinational override
AstVarRef* const lhsp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE};
AstVarRef* const origp = new AstVarRef{flp, vscp, VAccess::READ};
origp->user2(1); // Don't replace this read ref with the read signal
AstNodeExpr* rhsp;
if (isRangedDType(vscp)) {
rhsp = new AstOr{
flp,
new AstAnd{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
new AstVarRef{flp, m_valVscp, VAccess::READ}},
new AstAnd{flp,
new AstNot{flp, new AstVarRef{flp, m_enVscp, VAccess::READ}},
origp}};
} else {
rhsp = new AstCond{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
new AstVarRef{flp, m_valVscp, VAccess::READ}, origp};
}
AstNodeExpr* const rhsp = forcedUpdate(vscp);
// Explicitly list dependencies for update.
// Note: rdVscp is also needed to retrigger assignment for the first time.
AstSenItem* const itemsp = new AstSenItem{
flp, VEdgeType::ET_CHANGED, new AstVarRef{flp, m_rdVscp, VAccess::READ}};
itemsp->addNext(new AstSenItem{flp, VEdgeType::ET_CHANGED,
new AstVarRef{flp, m_valVscp, VAccess::READ}});
itemsp->addNext(new AstSenItem{flp, VEdgeType::ET_CHANGED,
new AstVarRef{flp, m_enVscp, VAccess::READ}});
AstVarRef* const origp = new AstVarRef{flp, vscp, VAccess::READ};
ForceState::markNonReplaceable(origp);
itemsp->addNext(
new AstSenItem{flp, VEdgeType::ET_CHANGED, origp->cloneTree(false)});
AstActive* const activep
= new AstActive{flp, "force-comb",
new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Combo{}}}};
= new AstActive{flp, "force-update", new AstSenTree{flp, itemsp}};
activep->sensesStorep(activep->sensesp());
activep->addStmtsp(new AstAssignW{flp, lhsp, rhsp});
activep->addStmtsp(new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr,
new AstAssign{flp, lhsp, rhsp}});
vscp->scopep()->addBlocksp(activep);
}
}
AstNodeExpr* forcedUpdate(AstVarScope* const vscp) const {
FileLine* const flp = vscp->fileline();
AstVarRef* const origp = new AstVarRef{flp, vscp, VAccess::READ};
ForceState::markNonReplaceable(origp);
if (ForceState::isRangedDType(vscp)) {
return new AstOr{
flp,
new AstAnd{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
new AstVarRef{flp, m_valVscp, VAccess::READ}},
new AstAnd{flp, new AstNot{flp, new AstVarRef{flp, m_enVscp, VAccess::READ}},
origp}};
}
return new AstCond{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
new AstVarRef{flp, m_valVscp, VAccess::READ}, origp};
}
};
private:
// NODE STATE
// AstVar::user1p -> ForceComponentsVar* instance (via m_forceComponentsVar)
// AstVarScope::user1p -> ForceComponentsVarScope* instance (via m_forceComponentsVarScope)
// AstVarRef::user2 -> Flag indicating not to replace reference
// AstVarScope::user3 -> AstVarScope*, a `valVscp` force component for each VarScope of
// forced RHS
const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse;
const VNUser3InUse m_user3InUse;
AstUser1Allocator<AstVar, ForceComponentsVar> m_forceComponentsVar;
AstUser1Allocator<AstVarScope, ForceComponentsVarScope> m_forceComponentsVarScope;
// METHODS
public:
// CONSTRUCTORS
ForceState() = default;
VL_UNCOPYABLE(ForceState);
// STATIC METHODS
static bool isRangedDType(AstNode* nodep) {
// If ranged we need a multibit enable to support bit-by-bit part-select forces,
// otherwise forcing a real or other opaque dtype and need a single bit enable.
const AstBasicDType* const basicp = nodep->dtypep()->skipRefp()->basicp();
return basicp && basicp->isRanged();
}
static bool isNotReplaceable(const AstVarRef* const nodep) { return nodep->user2(); }
static void markNonReplaceable(AstVarRef* const nodep) { nodep->user2SetOnce(); }
static AstVarScope* getValVscp(AstVarRef* const refp) {
return VN_CAST(refp->varScopep()->user3p(), VarScope);
}
static void setValVscp(AstNodeVarRef* const refp, AstVarScope* const vscp) {
refp->varScopep()->user3p(vscp);
}
// METHODS
const ForceComponentsVarScope& getForceComponents(AstVarScope* vscp) {
AstVar* const varp = vscp->varp();
return m_forceComponentsVarScope(vscp, vscp, m_forceComponentsVar(varp, varp));
}
ForceComponentsVarScope* tryGetForceComponents(AstVarRef* nodep) const {
return m_forceComponentsVarScope.tryGet(nodep->varScopep());
}
};
class ForceConvertVisitor final : public VNVisitor {
// STATE
ForceState& m_state;
// STATIC METHODS
// Replace each AstNodeVarRef in the given 'nodep' that writes a variable by transforming the
// referenced AstVarScope with the given function.
void transformWritenVarScopes(AstNode* nodep, std::function<AstVarScope*(AstVarScope*)> f) {
static void transformWritenVarScopes(AstNode* nodep,
std::function<AstVarScope*(AstVarScope*)> f) {
UASSERT_OBJ(nodep->backp(), nodep, "Must have backp, otherwise will be lost if replaced");
nodep->foreach([&f](AstNodeVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
@ -159,7 +208,7 @@ class ForceConvertVisitor final : public VNVisitor {
});
}
// VISIT methods
// VISITORS
void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstAssignForce* nodep) override {
@ -172,24 +221,29 @@ class ForceConvertVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
// Set corresponding enable signals to ones
V3Number ones{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
V3Number ones{lhsp, ForceState::isRangedDType(lhsp) ? lhsp->width() : 1};
ones.setAllBits1();
AstAssign* const setEnp
= new AstAssign{flp, lhsp->cloneTreePure(false), new AstConst{rhsp->fileline(), ones}};
transformWritenVarScopes(setEnp->lhsp(), [this](AstVarScope* vscp) {
return getForceComponents(vscp).m_enVscp;
return m_state.getForceComponents(vscp).m_enVscp;
});
// Set corresponding value signals to the forced value
AstAssign* const setValp
= new AstAssign{flp, lhsp->cloneTreePure(false), rhsp->cloneTreePure(false)};
transformWritenVarScopes(setValp->lhsp(), [this](AstVarScope* vscp) {
return getForceComponents(vscp).m_valVscp;
transformWritenVarScopes(setValp->lhsp(), [this, rhsp](AstVarScope* vscp) {
AstVarScope* const valVscp = m_state.getForceComponents(vscp).m_valVscp;
// TODO support multiple VarRefs on RHS
if (AstVarRef* const refp = VN_CAST(rhsp, VarRef))
ForceState::setValVscp(refp, valVscp);
return valVscp;
});
// Set corresponding read signal directly as well, in case something in the same process
// reads it later
// Set corresponding read signal directly as well, in case something in the same
// process reads it later
AstAssign* const setRdp = new AstAssign{flp, lhsp->unlinkFrBack(), rhsp->unlinkFrBack()};
transformWritenVarScopes(setRdp->lhsp(), [this](AstVarScope* vscp) {
return getForceComponents(vscp).m_rdVscp;
return m_state.getForceComponents(vscp).m_rdVscp;
});
setEnp->addNext(setValp);
@ -206,12 +260,12 @@ class ForceConvertVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
// Set corresponding enable signals to zero
V3Number zero{lhsp, isRangedDType(lhsp) ? lhsp->width() : 1};
V3Number zero{lhsp, ForceState::isRangedDType(lhsp) ? lhsp->width() : 1};
zero.setAllBits0();
AstAssign* const resetEnp
= new AstAssign{flp, lhsp->cloneTreePure(false), new AstConst{lhsp->fileline(), zero}};
transformWritenVarScopes(resetEnp->lhsp(), [this](AstVarScope* vscp) {
return getForceComponents(vscp).m_enVscp;
return m_state.getForceComponents(vscp).m_enVscp;
});
// IEEE 1800-2023 10.6.2: If this is a net, and not a variable, then reset the read
// signal directly as well, in case something in the same process reads it later. Also, if
@ -226,8 +280,9 @@ class ForceConvertVisitor final : public VNVisitor {
resetRdp->lhsp()->foreach([this](AstNodeVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
AstVarScope* const vscp = refp->varScopep();
AstVarScope* const newVscp
= vscp->varp()->isContinuously() ? getForceComponents(vscp).m_rdVscp : vscp;
AstVarScope* const newVscp = vscp->varp()->isContinuously()
? m_state.getForceComponents(vscp).m_rdVscp
: vscp;
// Disable BLKANDNBLK for this reference
FileLine* const flp = new FileLine{refp->fileline()};
flp->warnOff(V3ErrorCode::BLKANDNBLK, true);
@ -239,28 +294,14 @@ class ForceConvertVisitor final : public VNVisitor {
resetRdp->rhsp()->foreach([this](AstNodeVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
AstVarScope* const vscp = refp->varScopep();
FileLine* const flp = new FileLine{refp->fileline()};
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), vscp, VAccess::READ};
newpRefp->user2(1); // Don't replace this read ref with the read signal
if (vscp->varp()->isContinuously()) {
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), vscp, VAccess::READ};
ForceState::markNonReplaceable(newpRefp);
refp->replaceWith(newpRefp);
} else if (isRangedDType(vscp)) {
refp->replaceWith(new AstOr{
flp,
new AstAnd{
flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp, VAccess::READ},
new AstVarRef{flp, getForceComponents(vscp).m_valVscp, VAccess::READ}},
new AstAnd{
flp,
new AstNot{flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp,
VAccess::READ}},
newpRefp}});
} else {
refp->replaceWith(new AstCond{
flp, new AstVarRef{flp, getForceComponents(vscp).m_enVscp, VAccess::READ},
new AstVarRef{flp, getForceComponents(vscp).m_valVscp, VAccess::READ},
newpRefp});
refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp));
}
VL_DO_DANGLING(refp->deleteTree(), refp);
});
@ -271,51 +312,108 @@ class ForceConvertVisitor final : public VNVisitor {
void visit(AstVarScope* nodep) override {
// If this signal is marked externally forceable, create the public force signals
if (nodep->varp()->isForceable()) {
const ForceComponentsVarScope& fc = getForceComponents(nodep);
const ForceState::ForceComponentsVarScope& fc = m_state.getForceComponents(nodep);
fc.m_enVscp->varp()->sigUserRWPublic(true);
fc.m_valVscp->varp()->sigUserRWPublic(true);
}
}
public:
// CONSTRUCTOR
explicit ForceConvertVisitor(AstNetlist* nodep) {
explicit ForceConvertVisitor(AstNetlist* nodep, ForceState& state)
: m_state{state} {
// Transform all force and release statements
iterateAndNextNull(nodep->modulesp());
// Replace references to forced signals
nodep->modulesp()->foreachAndNext([this](AstVarRef* nodep) {
if (ForceComponentsVarScope* const fcp
= m_forceComponentsVarScope.tryGet(nodep->varScopep())) {
switch (nodep->access()) {
case VAccess::READ:
// Read references replaced to read the new, possibly forced signal
if (!nodep->user2()) {
nodep->varp(fcp->m_rdVscp->varp());
nodep->varScopep(fcp->m_rdVscp);
}
break;
case VAccess::WRITE:
// Write references use the original signal
break;
default:
nodep->v3error(
"Unsupported: Signals used via read-write reference cannot be forced");
break;
}
}
});
}
public:
static void apply(AstNetlist* nodep) { ForceConvertVisitor{nodep}; }
};
class ForceReplaceVisitor final : public VNVisitor {
// STATE
const ForceState& m_state;
AstNodeStmt* m_stmtp = nullptr;
bool m_inLogic = false;
// METHODS
void iterateLogic(AstNode* logicp) {
VL_RESTORER(m_inLogic);
m_inLogic = true;
iterateChildren(logicp);
}
// VISITORS
void visit(AstNodeStmt* nodep) override {
VL_RESTORER(m_stmtp);
m_stmtp = nodep;
iterateChildren(nodep);
}
void visit(AstAlwaysPublic* nodep) override { iterateLogic(nodep); }
void visit(AstCFunc* nodep) override { iterateLogic(nodep); }
void visit(AstCoverToggle* nodep) override { iterateLogic(nodep); }
void visit(AstNodeProcedure* nodep) override { iterateLogic(nodep); }
void visit(AstSenItem* nodep) override { iterateLogic(nodep); }
void visit(AstVarRef* nodep) override {
if (ForceState::isNotReplaceable(nodep)) return;
switch (nodep->access()) {
case VAccess::READ: {
// Replace VarRef from forced LHS with rdVscp.
if (ForceState::ForceComponentsVarScope* const fcp
= m_state.tryGetForceComponents(nodep)) {
FileLine* const flp = nodep->fileline();
AstVarRef* const origp = new AstVarRef{flp, nodep->varScopep(), VAccess::READ};
ForceState::markNonReplaceable(origp);
nodep->varp(fcp->m_rdVscp->varp());
nodep->varScopep(fcp->m_rdVscp);
}
break;
}
case VAccess::WRITE: {
if (!m_inLogic) return;
// Emit rdVscp update after each write to any VarRef on forced LHS.
if (ForceState::ForceComponentsVarScope* const fcp
= m_state.tryGetForceComponents(nodep)) {
FileLine* const flp = nodep->fileline();
AstVarRef* const lhsp = new AstVarRef{flp, fcp->m_rdVscp, VAccess::WRITE};
AstNodeExpr* const rhsp = fcp->forcedUpdate(nodep->varScopep());
m_stmtp->addNextHere(new AstAssign{flp, lhsp, rhsp});
}
// Emit valVscp update after each write to any VarRef on forced RHS.
if (AstVarScope* const valVscp = ForceState::getValVscp(nodep)) {
FileLine* const flp = nodep->fileline();
AstVarRef* const valp = new AstVarRef{flp, valVscp, VAccess::WRITE};
AstVarRef* const rhsp = new AstVarRef{flp, nodep->varScopep(), VAccess::READ};
ForceState::markNonReplaceable(valp);
ForceState::markNonReplaceable(rhsp);
m_stmtp->addNextHere(new AstAssign{flp, valp, rhsp});
}
break;
}
default:
nodep->v3error("Unsupported: Signals used via read-write reference cannot be forced");
break;
}
}
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTOR
explicit ForceReplaceVisitor(AstNetlist* nodep, const ForceState& state)
: m_state{state} {
iterateChildren(nodep);
}
};
//######################################################################
//
void V3Force::forceAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
if (!v3Global.hasForceableSignals()) return;
ForceConvertVisitor::apply(nodep);
V3Global::dumpCheckGlobalTree("force", 0, dumpTreeEitherLevel() >= 3);
{
ForceState state;
{ ForceConvertVisitor{nodep, state}; }
{ ForceReplaceVisitor{nodep, state}; }
V3Global::dumpCheckGlobalTree("force", 0, dumpTreeEitherLevel() >= 3);
}
}

View File

@ -0,0 +1,4 @@
%Error: t/t_force_chained.v:27: got='h0 exp='h00000001
%Error: t/t_force_chained.v:33: got='h0 exp='h00000002
%Error: t/t_force_chained.v:40: got='h0 exp='h00000003
%Error: t/t_force_chained.v:46: got='h0 exp='h00000003

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--exe", "--main", "--timing"])
test.execute(expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,51 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); end while(0)
module t;
reg [1:0] a;
wire [1:0] b = 1;
bit [1:0] c;
initial begin
#1 a = 0;
force b = a;
force c = b;
`checkh(a, 0);
`checkh(b, 0);
`checkh(c, 0);
a = 1;
#1;
`checkh(a, 1);
`checkh(b, 1);
// TODO implement inter-dependency resolution between force statements
`checkh(c, 1);
a = 2;
#1;
`checkh(a, 2);
`checkh(b, 2);
`checkh(c, 2);
a = 3;
c = 3;
#1;
`checkh(a, 3);
`checkh(b, 3);
`checkh(c, 3);
release b;
release c;
`checkh(a, 3);
`checkh(b, 1);
`checkh(c, 3);
#1 $finish;
end
endmodule

View File

@ -0,0 +1,4 @@
%Error: t/t_force_func.v:26: got='h0 exp='h00000001
%Error: t/t_force_func.v:34: got='h0 exp='h00000002
%Error: t/t_force_func.v:39: got='h0 exp='h00000003
%Error: t/t_force_func.v:43: got='h0 exp='h00000003

18
test_regress/t/t_force_func.py Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--exe", "--main", "--timing"])
test.execute(expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,51 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); end while(0)
function bit [1:0] get_arg (bit [1:0] x);
return x;
endfunction
module t;
bit [1:0] a;
bit [1:0] b = 1;
initial begin
#1 a = 0;
force b = get_arg(a);
`checkh(a, 0);
`checkh(b, 0);
a = 1;
#1;
`checkh(a, 1);
`checkh(b, 1);
a = 2;
#1;
`checkh(a, 2);
// TODO
// IEEE 1800-2023 10.6
// Assignment shall be reevaluated while the assign or force is in effect.
`checkh(b, 2);
a = 3;
#1;
`checkh(a, 3);
`checkh(b, 3);
release b;
`checkh(a, 3);
`checkh(b, 3);
b = 2;
`checkh(b, 2);
#1 $finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--exe", "--main", "--timing"])
test.execute()
test.passes()

View File

@ -0,0 +1,33 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`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)
module t;
reg [2:0] a = 3'b000;
initial begin
a = 3'b001;
`checkh(a, 1);
force a = 3'b010;
`checkh(a, 2);
a = 3'b011;
`checkh(a, 2);
release a;
`checkh(a, 2);
a = 3'b100;
`checkh(a, 4);
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--exe", "--main", "--timing"])
test.execute()
test.passes()

View File

@ -0,0 +1,44 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); end while(0)
module t;
reg [1:0] a = 0;
reg [1:0] b = 2;
initial begin
force b = a;
`checkh(a, 0);
`checkh(b, 0);
end
initial begin
#1;
`checkh(b, 0);
a = 1;
#1;
`checkh(a, 1);
`checkh(b, 1);
a = 3;
#1;
`checkh(a, 3);
`checkh(b, 3);
release b;
`checkh(a, 3);
`checkh(b, 3);
b = 0;
#1;
`checkh(b, 0);
#1 $finish;
end
endmodule

View File

@ -0,0 +1,4 @@
0 d=0,e=0
10 d=1,e=1
20 d=1,e=0
%Error: t/t_force_release.v:36: got='h1 exp='h00000000

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--exe", "--main", "--timing"])
test.execute(expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,41 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); end while(0)
// Example from IEEE 1800-2023 10.6.2
module t;
logic a, b, c, d;
wire e;
and and1 (e, a, b, c);
initial begin
$monitor("%d d=%b,e=%b", $stime, d, e);
assign d = a & b & c;
a = 1;
b = 0;
c = 1;
#10;
force d = (a | b | c);
force e = (a | b | c);
`checkh(d, 1);
`checkh(e, 1);
#10;
release d;
release e;
// TODO support procedural continuous assignments.
//
// As per IEEE 1800-2023 10.6.2, value of `d` should be updated
// after release. However, Verilator treats `assign` inside an initial block
// as procedural assign thus value update is not properly restored.
#10;
`checkh(d, 0);
`checkh(e, 0);
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--exe", "--main", "--timing"])
test.execute()
test.passes()

View File

@ -0,0 +1,42 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`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)
module t;
reg [1:0] a;
wire [1:0] b = 1;
initial begin
#1 a = 0;
force b = a;
`checkh(a, 0);
`checkh(b, 0);
a = 1;
#1;
`checkh(a, 1);
`checkh(b, 1);
a = 2;
#1;
`checkh(a, 2);
`checkh(b, 2);
a = 3;
#1;
`checkh(a, 3);
`checkh(b, 3);
release b;
`checkh(a, 3);
`checkh(b, 1);
#1 $finish;
end
endmodule

View File

@ -0,0 +1,5 @@
%Error: t/t_force_rhs_ref_multiple.v:23: got='h0a exp='h00000009
%Error: t/t_force_rhs_ref_multiple.v:27: got='h0a exp='h00000007
%Error: t/t_force_rhs_ref_multiple.v:31: got='h0a exp='h00000004
%Error: t/t_force_rhs_ref_multiple.v:35: got='h0a exp='h00000000
%Error: t/t_force_rhs_ref_multiple.v:39: got='h0a exp='h00000000

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--exe", "--main", "--timing"])
test.execute(expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,45 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); end while(0)
module t;
logic [7:0] a = 1;
logic [7:0] b = 2;
logic [7:0] c = 3;
logic [7:0] d = 4;
logic [7:0] e = 5;
initial begin
force e = a + b + c + d;
`checkh(e, 10);
#1;
a = 0;
#1;
// TODO support multiple VarRefs on RHS
`checkh(e, 9);
b = 0;
#1;
`checkh(e, 7);
c = 0;
#1;
`checkh(e, 4);
d = 0;
#1;
`checkh(e, 0);
release e;
// Not driven, change value after procedural assignment.
`checkh(e, 0);
e = 5;
`checkh(e, 5);
$finish;
end
endmodule