Merge alternate 'force'/'release' implementation

- Add more tests, including for tracing.
- Apply some cleaner, more generic abstractions in the implementation.
- Use clearer AstRelease which is not an assignment.
This commit is contained in:
Geza Lore 2021-12-17 17:56:33 +00:00
parent b4d8220cbb
commit 539c9d4c63
25 changed files with 1169 additions and 383 deletions

View File

@ -336,6 +336,24 @@ Verilator does not support SEREs yet. All assertion and coverage
statements must be simple expressions that complete in one cycle.
Force statement
---------------
Verilator supports the procedural `force` (and corresponding `release`)
statement. The behaviour of the `force` statement however does not entirely
comply with the IEEE 1800 SystemVerilog standard. 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 onwards, a continuous
assignment `assign a = b;` have been added to override the drivers of `a`.
More specifically: the value of `a` should be updated, whenever the value of
`b` changes, all the way until a `release a;` statement is executed.
Verilator instead evaluates the current value of `b` at the time 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 behaviour is nevertheless consistent with some other
simulators.
Encrypted Verilog
-----------------

View File

@ -71,29 +71,6 @@ AstNode::AstNode(VNType t, FileLine* fl)
editCountInc();
}
AstNode* AstNode::usernp(int n) const {
switch (n) {
case 1: return user1p();
case 2: return user2p();
case 3: return user3p();
case 4: return user4p();
case 5: return user5p();
}
v3fatalSrc("Bad Case");
return nullptr; // LCOV_EXCL_LINE
}
void AstNode::usernp(int n, void* userp) {
switch (n) {
case 1: user1p(userp); return;
case 2: user2p(userp); return;
case 3: user3p(userp); return;
case 4: user4p(userp); return;
case 5: user5p(userp); return;
}
v3fatalSrc("Bad Case");
VL_UNREACHABLE
}
AstNode* AstNode::abovep() const {
// m_headtailp only valid at beginning or end of list
// Avoid supporting at other locations as would require walking

View File

@ -1511,14 +1511,6 @@ public:
uint8_t brokenState() const { return m_brokenState; }
void brokenState(uint8_t value) { m_brokenState = value; }
void prefetch() const {
ASTNODE_PREFETCH(m_op1p);
ASTNODE_PREFETCH(m_op2p);
ASTNODE_PREFETCH(m_op3p);
ASTNODE_PREFETCH(m_op4p);
ASTNODE_PREFETCH(m_nextp);
}
// Used by AstNode::broken()
bool brokeExists() const { return V3Broken::isLinkable(this); }
bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); }
@ -1673,9 +1665,6 @@ public:
static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree
// clang-format on
AstNode* usernp(int n) const; // Return user1..userN based on provided n
void usernp(int n, void* userp); // Set user1..userN based on provided n
vluint64_t editCount() const { return m_editCount; }
void editCountInc() {
m_editCount = ++s_editCntGbl; // Preincrement, so can "watch AstNode::s_editCntGbl=##"

View File

@ -3559,11 +3559,10 @@ public:
};
class AstAssignForce final : public AstNodeAssign {
// Procedural 'force' statement
public:
AstAssignForce(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: ASTGEN_SUPER_AssignForce(fl, lhsp, rhsp) {
v3Global.useForce(true);
}
: ASTGEN_SUPER_AssignForce(fl, lhsp, rhsp) {}
ASTNODE_NODE_FUNCS(AssignForce)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
return new AstAssignForce{this->fileline(), lhsp, rhsp};
@ -3571,22 +3570,15 @@ public:
virtual bool brokeLhsMustBeLvalue() const override { return true; }
};
class AstAssignRelease final : public AstNodeAssign {
// Release is treated similar to an assign to `z
class AstRelease final : public AstNodeStmt {
// Procedural 'release' statement
public:
// Only for use in parser, as V3Width needs to resolve the '0 width.
AstAssignRelease(FileLine* fl, VFlagChildDType, AstNode* lhsp)
: ASTGEN_SUPER_AssignRelease(fl, lhsp, new AstConst{fl, AstConst::StringToParse{}, "'0"}) {
v3Global.useForce(true);
AstRelease(FileLine* fl, AstNode* lhsp)
: ASTGEN_SUPER_Release(fl) {
setOp1p(lhsp);
}
AstAssignRelease(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: ASTGEN_SUPER_AssignRelease(fl, lhsp, rhsp) {}
ASTNODE_NODE_FUNCS(AssignRelease)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
return new AstAssignRelease{this->fileline(), lhsp, rhsp};
}
virtual bool brokeLhsMustBeLvalue() const override { return true; }
AstNode* lhsp() const { return op2p(); }
ASTNODE_NODE_FUNCS(Release);
AstNode* lhsp() const { return op1p(); }
};
class AstAssignPre final : public AstNodeAssign {

View File

@ -24,6 +24,7 @@
#include "V3Ast.h"
#include <type_traits>
#include <utility>
#include <vector>
template <class T_Node, class T_Data, int T_UserN> class AstUserAllocatorBase VL_NOT_FINAL {
@ -92,11 +93,12 @@ protected:
VL_UNCOPYABLE(AstUserAllocatorBase);
public:
// Get a reference to the user data
T_Data& operator()(T_Node* nodep) {
// Get a reference to the user data. If does not exist, construct it with given arguments.
template <typename... Args> //
T_Data& operator()(T_Node* nodep, Args&&... args) {
T_Data* userp = getUserp(nodep);
if (!userp) {
userp = new T_Data;
userp = new T_Data{std::forward<Args>(args)...};
m_allocated.push_back(userp);
setUserp(nodep, userp);
}
@ -109,6 +111,9 @@ public:
UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode");
return *userp;
}
// Get a pointer to the user data if exists, otherwise nullptr
T_Data* tryGet(const T_Node* nodep) { return getUserp(nodep); }
};
// User pointer allocator classes. T_Node is the type of node the allocator should be applied to

View File

@ -220,6 +220,12 @@ private:
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
nodep, "Assignment LHS is not an lvalue");
}
virtual void visit(AstRelease* nodep) override {
processAndIterate(nodep);
UASSERT_OBJ(!(v3Global.assertDTypesResolved() && VN_IS(nodep->lhsp(), NodeVarRef)
&& !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()),
nodep, "Release LHS is not an lvalue");
}
virtual void visit(AstScope* nodep) override {
VL_RESTORER(m_inScope);
{

View File

@ -2768,6 +2768,18 @@ private:
varrefp->varp()->valuep(initvaluep);
}
}
virtual void visit(AstRelease* nodep) override {
if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) {
FileLine* const flp = nodep->fileline();
AstRelease* const newLp = new AstRelease{flp, concatp->lhsp()->unlinkFrBack()};
AstRelease* const newRp = new AstRelease{flp, concatp->rhsp()->unlinkFrBack()};
nodep->replaceWith(newLp);
newLp->addNextHere(newRp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
visit(newLp);
visit(newRp);
}
}
virtual void visit(AstNodeIf* nodep) override {
iterateChildren(nodep);

View File

@ -103,6 +103,8 @@ private:
VL_DEBUG_FUNC; // Declare debug()
void markVarUsage(AstNodeVarRef* nodep, bool blocking) {
// Ignore if warning is disabled on this reference (used by V3Force).
if (nodep->fileline()->warnIsOff(V3ErrorCode::BLKANDNBLK)) return;
if (blocking) nodep->user5(true);
AstVarScope* const vscp = nodep->varScopep();
// UINFO(4, " MVU " << blocking << " " << nodep << endl);

View File

@ -124,15 +124,10 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putqs(nodep, "*/\n");
}
virtual void visit(AstNodeAssign* nodep) override {
if (VN_IS(nodep, AssignRelease)) {
puts("release ");
iterateAndNextNull(nodep->lhsp());
} else {
if (VN_IS(nodep, AssignForce)) puts("force ");
iterateAndNextNull(nodep->lhsp());
putfs(nodep, " " + nodep->verilogKwd() + " ");
iterateAndNextNull(nodep->rhsp());
}
if (VN_IS(nodep, AssignForce)) puts("force ");
iterateAndNextNull(nodep->lhsp());
putfs(nodep, " " + nodep->verilogKwd() + " ");
iterateAndNextNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignDly* nodep) override {
@ -155,6 +150,11 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
iterateAndNextNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstRelease* nodep) override {
puts("release ");
iterateAndNextNull(nodep->lhsp());
if (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstBreak*) override {
putbs("break");
if (!m_suppressSemi) puts(";\n");

View File

@ -1,6 +1,6 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Make lookup forces
// DESCRIPTION: Verilator: Covert forceable signals, process force/release
//
// Code available from: https://verilator.org
//
@ -13,350 +13,288 @@
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
// FORCE TRANSFORMATIONS:
// Step 1 (ForceVisitor):
// For forced nets, make:
// __forceon - enable bitmask of what bits are forced
// __forcein - value being forced
// __preforce - value before force is applied
// V3Force's Transformations:
//
// Force sets the appropriate __forceon bits indicating a force is in
// effect using the value in __forcein. Release clears the
// appropriate __forceon bits.
// For each forceable net with name "<name>":
// 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
// add an initial statement:
// initial <name>__VforceEn = 0;
// add a continuous assignment:
// assign <name>__VforceRd = <name>__VforceEn ? <name>__VforceVl : <name>;
// replace all READ references to <name> with a read reference to <name>_VforceRd
//
// IEEE says that procedural assignments "hold" the forced value even
// after a release, so add an assignment to the original __preforce too.
// Replace each AstAssignForce with 3 assignments:
// - <lhs>__VforceEn = 1
// - <lhs>__VforceVl = <rhs>
// - <lhs>__VforceRd = <rhs>
//
// Tristates can't be forced, that would need a __forceen and makes a
// large mess, so just error out.
//
// Step 2 (ForceReplaceVisitor): (If any forces made)
//
// Replace any VarRef's to a forced signal to instead go to the
// reconsiled signal.
// Replace each AstRelease with 1 or 2 assignments:
// - <lhs>__VforceEn = 0
// - <lhs>__VforceRd = <lhs> // iff lhs is a net
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Global.h"
#include "V3Force.h"
#include "V3Simulate.h"
#include "V3Stats.h"
#include "V3Ast.h"
#include <cmath>
#include <vector>
#include "V3AstUserAllocator.h"
//######################################################################
// Force shared state
// Convert force/release statements and signals marked 'forceable'
class ForceBaseVisitor VL_NOT_FINAL : public VNVisitor {
class ForceConvertVisitor final : public VNVisitor {
// TYPES
public:
// Enum value must correspond to which user#p is used
enum class FVar : uint8_t { FORCEON = 2, FORCEIN = 3, PREFORCE = 4 };
struct ForceComponentsVar {
AstVar* const m_rdVarp; // New variable to replace read references with
AstVar* const m_enVarp; // Force enabled signal
AstVar* const m_valVarp; // Forced value
AstVar* const m_phVarp; // Placeholder variable for release (never read)
explicit ForceComponentsVar(AstVar* varp)
: m_rdVarp{new AstVar{varp->fileline(), VVarType::WIRE, varp->name() + "__VforceRd",
varp->dtypep()}}
, m_enVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn",
varp->dtypep()}}
, m_valVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceVal",
varp->dtypep()}}
, m_phVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforcePh",
varp->dtypep()}} {
m_rdVarp->addNext(m_enVarp);
m_rdVarp->addNext(m_valVarp);
m_rdVarp->addNext(m_phVarp);
varp->addNextHere(m_rdVarp);
if (varp->isPrimaryIO()) {
varp->v3warn(
E_UNSUPPORTED,
"Unsupported: Force/Release on primary input/output net "
<< varp->prettyNameQ() << "\n"
<< varp->warnMore()
<< "... Suggest assign it to/from a temporary net and force/release that");
}
}
};
struct ForceComponentsVarScope {
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_phVscp; // Placeholder variable for release (never read)
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_phVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_phVarp}} {
m_rdVscp->addNext(m_enVscp);
m_rdVscp->addNext(m_valVscp);
m_rdVscp->addNext(m_phVscp);
vscp->addNextHere(m_rdVscp);
FileLine* const flp = vscp->fileline();
{ // Add initialization of the enable signal
AstVarRef* const lhsp = new AstVarRef{flp, m_enVscp, VAccess::WRITE};
V3Number zero{m_enVscp, m_enVscp->width()};
zero.setAllBits0();
AstNodeMath* const rhsp = new AstConst{flp, zero};
AstAssign* const assignp = new AstAssign{flp, lhsp, rhsp};
AstActive* const activep = new AstActive{
flp, "force-init",
new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Initial{}}}};
activep->sensesStorep(activep->sensesp());
activep->addStmtsp(new AstInitial{flp, assignp});
vscp->scopep()->addActivep(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
AstOr* const 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}};
AstActive* const activep
= new AstActive{flp, "force-comb",
new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Combo{}}}};
activep->sensesStorep(activep->sensesp());
activep->addStmtsp(new AstAssignW{flp, lhsp, rhsp});
vscp->scopep()->addActivep(activep);
}
}
};
private:
// NODE STATE
// Ast*::user1 -> bool - processed
// AstVar::user1 -> bool - created here
// AstVarScope::user1 -> bool - created here
// AstVar::user2p -> AstVar* pointer to __forceon
// AstVarScope::user2p -> AstVarScope* pointer to __forceon
// AstVar::user3p -> AstVar* pointer to __forcein
// AstVarScope::user3p -> AstVarScope* pointer to __forcein
// AstVar::user4p -> AstVar* pointer to __preforce
// AstVarScope::user4p -> AstVarScope* pointer to __preforce
// Uses are in ForceVisitor
// AstVar::user1p -> ForceComponentsVar* instance (via m_forceComponentsVar)
// AstVarScope::user1p -> ForceComponentsVarScope* instance (via m_forceComponentsVarScope)
// AstVarRef::user2 -> Flag indicating not to replace reference
const VNUser1InUse m_user1InUse;
const VNUser2InUse m_user2InUse;
AstUser1Allocator<AstVar, ForceComponentsVar> m_forceComponentsVar;
AstUser1Allocator<AstVarScope, ForceComponentsVarScope> m_forceComponentsVarScope;
public:
VL_DEBUG_FUNC; // Declare debug()
// METHODS
const ForceComponentsVarScope& getForceComponents(AstVarScope* vscp) {
AstVar* const varp = vscp->varp();
return m_forceComponentsVarScope(vscp, vscp, m_forceComponentsVar(varp, varp));
}
static string fvarName(FVar fvar) {
switch (fvar) {
case FVar::FORCEON: return "__forceon";
case FVar::FORCEIN: return "__forcein";
case FVar::PREFORCE: return "__preforce";
}
v3fatalSrc("bad case");
return "";
// 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) {
UASSERT_OBJ(nodep->backp(), nodep, "Must have backp, otherwise will be lost if replaced");
nodep->foreach<AstNodeVarRef>([this, &f](AstNodeVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
// TODO: this is not strictly speaking safe for some complicated lvalues, eg.:
// 'force foo[a(cnt)] = 1;', where 'cnt' is an out parameter, but it will
// do for now...
refp->replaceWith(
new AstVarRef{refp->fileline(), f(refp->varScopep()), VAccess::WRITE});
pushDeletep(refp);
});
}
static AstVar* getForceVarNull(const AstVar* const nodep, FVar fvar) {
// E.g. trying to make a __perforce__FOO would be bad
UASSERT_OBJ(!nodep->user1(), nodep, "lookup on var that Force made itself");
return VN_AS(nodep->usernp(static_cast<int>(fvar)), Var);
// VISIT methods
void visit(AstNode* nodep) override { iterateChildren(nodep); }
void visit(AstAssignForce* nodep) override {
// The AstAssignForce node will be removed for sure
VNRelinker relinker;
nodep->unlinkFrBack(&relinker);
pushDeletep(nodep);
FileLine* const flp = nodep->fileline();
AstNode* const lhsp = nodep->lhsp(); // The LValue we are forcing
AstNode* const rhsp = nodep->rhsp(); // The value we are forcing it to
// Set corresponding enable signals to ones
V3Number ones{lhsp, lhsp->width()};
ones.setAllBits1();
AstAssign* const setEnp
= new AstAssign{flp, lhsp->cloneTree(false), new AstConst{rhsp->fileline(), ones}};
transformWritenVarScopes(setEnp->lhsp(), [this](AstVarScope* vscp) {
return getForceComponents(vscp).m_enVscp;
});
// Set corresponding value signals to the forced value
AstAssign* const setValp
= new AstAssign{flp, lhsp->cloneTree(false), rhsp->cloneTree(false)};
transformWritenVarScopes(setValp->lhsp(), [this](AstVarScope* vscp) {
return getForceComponents(vscp).m_valVscp;
});
// 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;
});
setEnp->addNext(setValp);
setEnp->addNext(setRdp);
relinker.relink(setEnp);
}
static AstVar* getForceVar(AstVar* const nodep, FVar fvar) {
AstVar* const foundp = getForceVarNull(nodep, fvar);
if (foundp) return foundp;
if (nodep->isPrimaryIO()) {
nodep->v3warn(
E_UNSUPPORTED,
"Unsupported: Force/Release on primary input/output net "
<< nodep->prettyNameQ() << "\n"
<< nodep->warnMore()
<< "... Suggest assign it to/from a temporary net and force/release that");
}
auto* const newp = new AstVar{nodep->fileline(), VVarType::MODULETEMP,
nodep->name() + fvarName(fvar), nodep};
newp->user1(true);
UINFO(9, "getForceVar for " << nodep << endl);
UINFO(9, "getForceVar new " << newp << endl);
nodep->addNextHere(newp);
nodep->usernp(static_cast<int>(fvar), newp);
return newp;
}
static AstVarScope* getForceVscNull(const AstVarScope* const nodep, FVar fvar) {
// E.g. trying to make a __perforce__FOO would be bad
UASSERT_OBJ(!nodep->user1(), nodep, "lookup on varscope that Force made itself");
return VN_AS(nodep->usernp(static_cast<int>(fvar)), VarScope);
}
static AstVarScope* getForceVsc(AstVarScope* const nodep, FVar fvar) {
AstVarScope* const foundp = getForceVscNull(nodep, fvar);
if (foundp) return foundp;
FileLine* const fl_nowarn = new FileLine{nodep->fileline()};
void visit(AstRelease* nodep) override {
// The AstRelease node will be removed for sure
VNRelinker relinker;
nodep->unlinkFrBack(&relinker);
pushDeletep(nodep);
FileLine* const flp = nodep->fileline();
AstNode* const lhsp = nodep->lhsp(); // The LValue we are releasing
// Set corresponding enable signals to zero
V3Number zero{lhsp, lhsp->width()};
zero.setAllBits0();
AstAssign* const resetEnp
= new AstAssign{flp, lhsp->cloneTree(false), new AstConst{lhsp->fileline(), zero}};
transformWritenVarScopes(resetEnp->lhsp(), [this](AstVarScope* vscp) {
return getForceComponents(vscp).m_enVscp;
});
// IEEE 1800-2017 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
// it is a variable, and not a net, set the original signal to the forced value, as it
// needs to retain the forced value until the next procedural update, which might happen on
// a later eval. Luckily we can do all this in a single assignment.
FileLine* const fl_nowarn = new FileLine{flp};
fl_nowarn->warnOff(V3ErrorCode::BLKANDNBLK, true);
auto* const newp
= new AstVarScope{fl_nowarn, nodep->scopep(), getForceVar(nodep->varp(), fvar)};
newp->user1(true);
UINFO(9, "getForceVsc for " << nodep << endl);
UINFO(9, "getForceVsc new " << newp << endl);
nodep->addNextHere(newp);
nodep->usernp(static_cast<int>(fvar), newp);
return newp;
}
static AstVarRef* makeVarRef(AstNodeVarRef* nodep, FVar fvar, VAccess access) {
return new AstVarRef{nodep->fileline(), getForceVsc(nodep->varScopep(), fvar), access};
}
static AstNode* makeForcingEquation(AstNodeVarRef* nodep) {
// Forcing: out = ((__forceon & __forcein) | (~__forceon & __preforce))
UINFO(9, "makeForcingEquation for " << nodep << endl);
FileLine* const fl = nodep->fileline();
AstNode* const orp = new AstOr{
fl,
new AstAnd{fl, makeVarRef(nodep, FVar::FORCEON, VAccess::READ),
makeVarRef(nodep, FVar::FORCEIN, VAccess::READ)},
new AstAnd{fl, new AstNot{fl, makeVarRef(nodep, FVar::FORCEON, VAccess::READ)},
makeVarRef(nodep, FVar::PREFORCE, VAccess::READ)}};
return orp;
}
};
AstAssign* const resetRdp
= new AstAssign{fl_nowarn, lhsp->cloneTree(false), lhsp->unlinkFrBack()};
// Replace write refs on the LHS
resetRdp->lhsp()->foreach<AstNodeVarRef>([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;
// Disable BLKANDNBLK for this reference
FileLine* const flp = new FileLine{refp->fileline()};
flp->warnOff(V3ErrorCode::BLKANDNBLK, true);
AstVarRef* const newpRefp = new AstVarRef{flp, newVscp, VAccess::WRITE};
refp->replaceWith(newpRefp);
pushDeletep(refp);
});
// Replace write refs on RHS
resetRdp->rhsp()->foreach<AstNodeVarRef>([this](AstNodeVarRef* refp) {
if (refp->access() != VAccess::WRITE) return;
AstVarScope* const vscp = refp->varScopep();
AstVarScope* const newVscp
= vscp->varp()->isContinuously() ? vscp : getForceComponents(vscp).m_valVscp;
AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), newVscp, VAccess::READ};
newpRefp->user2(1); // Don't replace this read ref with the read signal
refp->replaceWith(newpRefp);
pushDeletep(refp);
});
//######################################################################
// Recurse left-hand-side variables to do replaces underneath a force or release
resetEnp->addNext(resetRdp);
relinker.relink(resetEnp);
}
class ForceLhsVisitor final : public ForceBaseVisitor {
private:
// STATE
FVar const m_fvar; // Which variable to replace with
AstNodeVarRef* m_releaseVarRefp = nullptr; // Left hand side variable under release
// CONSTRUCTOR
explicit ForceConvertVisitor(AstNetlist* nodep) {
// Transform all force and release statements
iterateAndNextNull(nodep->modulesp());
virtual void visit(AstNodeVarRef* nodep) override {
if (nodep->user1()) return;
if (nodep->access().isWriteOrRW()) {
if (m_releaseVarRefp) {
nodep->v3error("Multiple variables forced in single statement: "
<< m_releaseVarRefp->prettyNameQ() << ", "
<< nodep->varScopep()->prettyNameQ());
return;
// Replace references to forced signals
nodep->modulesp()->foreachAndNext<AstVarRef>([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;
}
}
m_releaseVarRefp = nodep;
AstNode* const newp = makeVarRef(nodep, m_fvar, VAccess::WRITE);
newp->user1(true);
nodep->replaceWith(newp);
pushDeletep(nodep);
}
});
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ForceLhsVisitor(AstNode* nodep, FVar fvar)
: m_fvar(fvar) {
iterate(nodep);
}
virtual ~ForceLhsVisitor() override = default;
// METHODS
AstNodeVarRef* releaseVarRefp() const { return m_releaseVarRefp; }
static void apply(AstNetlist* nodep) { ForceConvertVisitor{nodep}; }
};
//######################################################################
// Force class functions
class ForceVisitor final : public ForceBaseVisitor {
private:
// NODE STATE
// See ForceBaseVisitor
const VNUser1InUse m_inuser1;
const VNUser2InUse m_inuser2;
const VNUser3InUse m_inuser3;
const VNUser4InUse m_inuser4;
// STATE
bool m_anyForce = false; // Any force, need reconciliation step
VDouble0 m_statForces; // stat tracking
std::deque<AstAssignForce*> m_forces; // Pointer to found forces
std::deque<AstAssignRelease*> m_releases; // Pointer to found releases
virtual void visit(AstAssignForce* nodep) override { m_forces.push_back(nodep); }
void visitEarlierForce(AstAssignForce* nodep) {
if (nodep->user1SetOnce()) return;
if (debug() >= 9) nodep->dumpTree(cout, "-force-i- ");
++m_statForces;
m_anyForce = true;
// For force/release, duplicate assignment to make
// AssignForce __forceon = '1
// AssignForce __forcein = {value}
// and clone LHS's node tree to handle appropriate extractions
{ // __forceon = '1
AstNodeAssign* const newp = nodep->cloneTree(false);
pushDeletep(newp->rhsp()->unlinkFrBack());
V3Number num{nodep, nodep->width()};
num.setAllBits1();
newp->rhsp(new AstConst{nodep->fileline(), num});
{ ForceLhsVisitor{newp->lhsp(), FVar::FORCEON}; }
newp->user1(true); // Don't process it again
nodep->addNextHere(newp);
if (debug() >= 9) newp->dumpTree(cout, "-force-fo- ");
}
{ // Edit to create assignment to have VarRef that refers to __forceon
{ ForceLhsVisitor{nodep->lhsp(), FVar::FORCEIN}; }
nodep->user1(true); // Don't process it again
if (debug() >= 9) nodep->dumpTree(cout, "-force-fi- ");
}
}
virtual void visit(AstAssignRelease* nodep) override { m_releases.push_back(nodep); }
void visitEarlierRelease(AstAssignRelease* nodep) {
if (nodep->user1SetOnce()) return;
if (debug() >= 9) nodep->dumpTree(cout, "-release-i- ");
// RHS is not relevant, so no iterate
// For release, edit assignment to make
// AssignRelease __forceon = `0
// we already have 0's on RHS, were made when AstNode created
// Create assignment to have VarRef that refers to __forceon
ForceLhsVisitor fvisitor{nodep->lhsp(), FVar::FORCEON};
// releaseVarRefp might be deleted when ForceLhsVisitor destructs, so
// keep ForceLhsVisitor in scope for now
AstNodeVarRef* const releaseVarRefp = fvisitor.releaseVarRefp();
UASSERT_OBJ(releaseVarRefp, nodep, "No LHS variable found under release");
if (!getForceVscNull(releaseVarRefp->varScopep(), FVar::FORCEIN)) {
UINFO(9, "Deleting release of variable that's never forced: " << nodep << endl);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
return;
}
if (!releaseVarRefp->varp()->isContinuously()) {
// Create assignment __out == _forceen so when release happens value sticks
// See IEEE - a strange historical language artifact
UINFO(9, "force var is procedural " << releaseVarRefp->varScopep() << endl);
FileLine* const fl_nowarn = new FileLine{nodep->fileline()};
fl_nowarn->warnOff(V3ErrorCode::BLKANDNBLK, true);
AstNodeAssign* const newp = new AstAssignRelease{
fl_nowarn, makeVarRef(releaseVarRefp, FVar::PREFORCE, VAccess::WRITE),
makeForcingEquation(releaseVarRefp)};
newp->user1(true); // Don't process it again
nodep->addHereThisAsNext(newp); // Must go before change forceon
if (debug() >= 9) newp->dumpTree(cout, "-release-rp- ");
}
if (debug() >= 9) nodep->dumpTree(cout, "-release-ro- ");
}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
// CONSTRUCTORS
explicit ForceVisitor(AstNetlist* nodep) {
iterate(nodep);
// Now that we know procedural markers in user5, do forces
for (auto* const nodep : m_forces) visitEarlierForce(nodep);
m_forces.clear(); // As dangling pointers now
// Do releases after all forces are processed, so we can just
// ignore any release with no corresponding force
for (auto* const nodep : m_releases) visitEarlierRelease(nodep);
m_releases.clear(); // As dangling pointers now
}
virtual ~ForceVisitor() override { //
V3Stats::addStat("Tristate, Forces", m_statForces);
}
// METHODS
bool anyForce() const { return m_anyForce; }
};
//######################################################################
// Force class functions
class ForceReplace final : public ForceBaseVisitor {
// This extra complete-netlist visit could be avoided by recording all
// AstVarRefs to every AstVar, but that's a lot of data structure
// building, faster to read-only iterate.
// As we only care about VarRef's we use direct recusion rather than a visitor
private:
void visitVarRef(AstNodeVarRef* nodep) {
if (nodep->varScopep()->user2p()) {
if (nodep->access().isRW()) {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: forced variable used in read-modify-write context");
} else if (nodep->access().isWriteOrRW()) {
UINFO(9, " changeRecurse-WR-replace " << nodep << endl);
AstNode* const newp = makeVarRef(nodep, FVar::PREFORCE, VAccess::WRITE);
newp->user1(true);
nodep->replaceWith(newp);
pushDeletep(nodep);
return;
} else if (nodep->access().isReadOrRW()) {
// We build forcing equation on each usage rather than making
// a variable otherwise we wouldn't know where between a statement
// that sets a preforce and uses a forced to insert the proposed
// assignment
UINFO(9, " changeRecurse-RD-replace " << nodep << endl);
AstNode* const newp = makeForcingEquation(nodep);
newp->user1(true);
nodep->replaceWith(newp);
pushDeletep(nodep);
return;
}
}
}
void changeRecurse(AstNode* nodep) {
// Recurse and replace any VarRef WRITEs to refer to the force equation
if (VL_LIKELY(!nodep->user1())) { // Else processed already
if (auto* const varrefp = VN_CAST(nodep, NodeVarRef)) {
visitVarRef(varrefp);
return; // Might have been edited -- and has no children so ok to exit
}
}
nodep->prefetch();
if (nodep->op1p()) changeRecurse(nodep->op1p());
if (nodep->op2p()) changeRecurse(nodep->op2p());
if (nodep->op3p()) changeRecurse(nodep->op3p());
if (nodep->op4p()) changeRecurse(nodep->op4p());
if (nodep->nextp()) changeRecurse(nodep->nextp());
}
virtual void visit(AstNode* nodep) override { v3error("Unused"); } // LCOV_EXCL_LINE
public:
// CONSTRUCTORS
explicit ForceReplace(AstNetlist* nodep) { changeRecurse(nodep); }
virtual ~ForceReplace() override = default;
};
//######################################################################
// Force class functions
//
void V3Force::forceAll(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ // Destruct before final check
ForceVisitor visitor{nodep};
if (visitor.anyForce()) {
V3Global::dumpCheckGlobalTree("force-mid", 0,
v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
ForceReplace{nodep};
}
}
if (!v3Global.hasForceableSignals()) return;
ForceConvertVisitor::apply(nodep);
V3Global::dumpCheckGlobalTree("force", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

View File

@ -103,7 +103,7 @@ class V3Global final {
// Experimenting with always requiring heavy, see (#2701)
bool m_needTraceDumper = false; // Need __Vm_dumperp in symbols
bool m_dpi = false; // Need __Dpi include files
bool m_useForce = false; // Need force/release processing
bool m_hasForceableSignals = false; // Need to apply V3Force pass
bool m_hasSCTextSections = false; // Has `systemc_* sections that need to be emitted
bool m_useParallelBuild = false; // Use parallel build for model
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
@ -147,6 +147,8 @@ public:
void needTraceDumper(bool flag) { m_needTraceDumper = flag; }
bool dpi() const { return m_dpi; }
void dpi(bool flag) { m_dpi = flag; }
bool hasForceableSignals() const { return m_hasForceableSignals; }
void setHasForceableSignals() { m_hasForceableSignals = true; }
bool hasSCTextSections() const { return m_hasSCTextSections; }
void setHasSCTextSections() { m_hasSCTextSections = true; }
V3HierBlockPlan* hierPlanp() const { return m_hierPlanp; }
@ -154,8 +156,6 @@ public:
UASSERT(!m_hierPlanp, "call once");
m_hierPlanp = plan;
}
void useForce(bool flag) { m_useForce = flag; }
bool useForce() const { return m_useForce; }
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
bool useParallelBuild() const { return m_useParallelBuild; }
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }

View File

@ -83,6 +83,15 @@ private:
iterateAndNextNull(nodep->rhsp());
}
}
virtual void visit(AstRelease* nodep) override {
VL_RESTORER(m_setRefLvalue);
VL_RESTORER(m_setContinuously);
{
m_setRefLvalue = VAccess::WRITE;
m_setContinuously = false;
iterateAndNextNull(nodep->lhsp());
}
}
virtual void visit(AstCastDynamic* nodep) override {
VL_RESTORER(m_setRefLvalue);
{

View File

@ -4025,6 +4025,12 @@ private:
}
}
virtual void visit(AstRelease* nodep) override {
userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p());
UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LValue be untyped?");
UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LValue be unsized?");
}
virtual void visit(AstSFormatF* nodep) override {
// Excludes NodeDisplay, see below
if (m_vup && !m_vup->prelim()) return; // Can be called as statement or function

View File

@ -301,10 +301,6 @@ static void process() {
// After V3Task so task internal variables will get renamed
V3Name::nameAll(v3Global.rootp());
// Process force/releases if there are any
// After flattening, before Life optimizations
if (v3Global.useForce()) V3Force::forceAll(v3Global.rootp());
// Loop unrolling & convert FORs to WHILEs
V3Unroll::unrollAll(v3Global.rootp());
@ -338,6 +334,10 @@ static void process() {
// Create tracing sample points, before we start eliminating signals
if (v3Global.opt.trace()) V3TraceDecl::traceDeclAll(v3Global.rootp());
// Convert forceable signals, process force/release statements.
// After V3TraceDecl so we don't trace additional signals inserted to implement forcing.
V3Force::forceAll(v3Global.rootp());
// Gate-based logic elimination; eliminate signals and push constant across cell boundaries
// Instant propagation makes lots-o-constant reduction possibilities.
if (v3Global.opt.oGate()) {

View File

@ -3088,10 +3088,10 @@ statement_item<nodep>: // IEEE: statement_item
//UNSUP: delay_or_event_controlE above
| yDEASSIGN variable_lvalue ';'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); }
| yFORCE expr '=' expr ';'
{ $$ = new AstAssignForce{$1, $2, $4}; }
| yFORCE variable_lvalue '=' expr ';'
{ $$ = new AstAssignForce{$1, $2, $4}; v3Global.setHasForceableSignals(); }
| yRELEASE variable_lvalue ';'
{ $$ = new AstAssignRelease{$1, VFlagChildDType{}, $2}; }
{ $$ = new AstRelease{$1, $2}; v3Global.setHasForceableSignals(); }
//
// // IEEE: case_statement
| unique_priorityE caseStart caseAttrE case_itemListE yENDCASE { $$ = $2; if ($4) $2->addItemsp($4);

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
compile();
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,130 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Geza Lore.
// 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 (/*AUTOARG*/
// Inputs
clk
);
input clk;
int cyc = 0;
always @(posedge clk) cyc <= cyc + 1;
wire net_1;
wire [7:0] net_8;
assign net_1 = ~cyc[0];
assign net_8 = ~cyc[1 +: 8];
always @ (posedge clk) begin
$display("%d pre : %x %x", cyc, net_8, net_1);
case (cyc)
4: begin
`checkh (net_1, 0);
`checkh (net_8, ~cyc[1 +: 8]);
end
5: begin
`checkh (net_1, 0);
`checkh (net_8, 8'h5f);
end
6: begin
`checkh (net_1, 1);
`checkh (net_8, 8'h5f);
end
7, 8: begin
`checkh (net_1, 1);
`checkh (net_8, 8'hf5);
end
9: begin
`checkh (net_1, ~cyc[0]);
`checkh (net_8, 8'hf5);
end
11, 12: begin
`checkh (net_1, 1);
`checkh (net_8, 8'h5a);
end
13, 14: begin
`checkh (net_1, 0);
`checkh (net_8, 8'ha5);
end
default: begin
`checkh ({net_8, net_1}, ~cyc[0 +: 9]);
end
endcase
`ifndef REVERSE
if (cyc == 3) force net_1 = 0;
if (cyc == 5) force net_1 = 1;
if (cyc == 8) release net_1;
if (cyc == 4) force net_8 = 8'h5f;
if (cyc == 6) force net_8 = 8'hf5;
if (cyc == 9) release net_8;
if (cyc == 10) force {net_1, net_8} = 9'b1_0101_1010;
if (cyc == 12) force {net_8, net_1} = 9'b1010_0101_0;
if (cyc == 14) release {net_1, net_8};
`else
if (cyc == 8) release net_1;
if (cyc == 5) force net_1 = 1;
if (cyc == 3) force net_1 = 0;
if (cyc == 9) release net_8;
if (cyc == 6) force net_8 = 8'hf5;
if (cyc == 4) force net_8 = 8'h5f;
if (cyc == 14) release {net_1, net_8};
if (cyc == 12) force {net_8, net_1} = 9'b1010_0101_0;
if (cyc == 10) force {net_1, net_8} = 9'b1_0101_1010;
`endif
$display("%d post: %x %x", cyc, net_8, net_1);
case (cyc)
3: begin
`checkh (net_1, 0);
`checkh (net_8, ~cyc[1 +: 8]);
end
4: begin
`checkh (net_1, 0);
`checkh (net_8, 8'h5f);
end
5: begin
`checkh (net_1, 1);
`checkh (net_8, 8'h5f);
end
6, 7: begin
`checkh (net_1, 1);
`checkh (net_8, 8'hf5);
end
8: begin
`checkh (net_1, ~cyc[0]);
`checkh (net_8, 8'hf5);
end
10, 11: begin
`checkh (net_1, 1);
`checkh (net_8, 8'h5a);
end
12, 13: begin
`checkh (net_1, 0);
`checkh (net_8, 8'ha5);
end
default: begin
`checkh ({net_8, net_1}, ~cyc[0 +: 9]);
end
endcase
if (cyc == 30) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,24 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
top_filename("t/t_force_release_net.v");
compile(
verilator_flags2 => ['+define+REVERSE']
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,213 @@
$version Generated by VerilatedVcd $end
$date Sun Dec 19 14:44:51 2021 $end
$timescale 1ps $end
$scope module top $end
$var wire 1 # clk $end
$scope module t $end
$var wire 1 # clk $end
$var wire 32 $ cyc [31:0] $end
$var wire 1 % net_1 $end
$var wire 8 & net_8 [7:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
0#
b00000000000000000000000000000000 $
1%
b11111111 &
#10
1#
b00000000000000000000000000000001 $
0%
#15
0#
#20
1#
b00000000000000000000000000000010 $
1%
b11111110 &
#25
0#
#30
1#
b00000000000000000000000000000011 $
0%
#35
0#
#40
1#
b00000000000000000000000000000100 $
b11111101 &
#45
0#
#50
1#
b00000000000000000000000000000101 $
b01011111 &
#55
0#
#60
1#
b00000000000000000000000000000110 $
1%
#65
0#
#70
1#
b00000000000000000000000000000111 $
b11110101 &
#75
0#
#80
1#
b00000000000000000000000000001000 $
#85
0#
#90
1#
b00000000000000000000000000001001 $
0%
#95
0#
#100
1#
b00000000000000000000000000001010 $
1%
b11111010 &
#105
0#
#110
1#
b00000000000000000000000000001011 $
b01011010 &
#115
0#
#120
1#
b00000000000000000000000000001100 $
#125
0#
#130
1#
b00000000000000000000000000001101 $
0%
b10100101 &
#135
0#
#140
1#
b00000000000000000000000000001110 $
#145
0#
#150
1#
b00000000000000000000000000001111 $
b11111000 &
#155
0#
#160
1#
b00000000000000000000000000010000 $
1%
b11110111 &
#165
0#
#170
1#
b00000000000000000000000000010001 $
0%
#175
0#
#180
1#
b00000000000000000000000000010010 $
1%
b11110110 &
#185
0#
#190
1#
b00000000000000000000000000010011 $
0%
#195
0#
#200
1#
b00000000000000000000000000010100 $
1%
b11110101 &
#205
0#
#210
1#
b00000000000000000000000000010101 $
0%
#215
0#
#220
1#
b00000000000000000000000000010110 $
1%
b11110100 &
#225
0#
#230
1#
b00000000000000000000000000010111 $
0%
#235
0#
#240
1#
b00000000000000000000000000011000 $
1%
b11110011 &
#245
0#
#250
1#
b00000000000000000000000000011001 $
0%
#255
0#
#260
1#
b00000000000000000000000000011010 $
1%
b11110010 &
#265
0#
#270
1#
b00000000000000000000000000011011 $
0%
#275
0#
#280
1#
b00000000000000000000000000011100 $
1%
b11110001 &
#285
0#
#290
1#
b00000000000000000000000000011101 $
0%
#295
0#
#300
1#
b00000000000000000000000000011110 $
1%
b11110000 &
#305
0#
#310
1#
b00000000000000000000000000011111 $
0%

View File

@ -0,0 +1,26 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
top_filename("t/t_force_release_net.v");
compile(
verilator_flags2 => ['--trace']
);
execute(
check_finished => 1,
);
vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename});
ok(1);
1;

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
compile();
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,138 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2021 by Geza Lore.
// 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 (/*AUTOARG*/
// Inputs
clk
);
input clk;
int cyc = 0;
always @(posedge clk) cyc <= cyc + 1;
reg var_1 = 0;
reg [7:0] var_8 = 0;
always @(posedge clk) begin
var_1 <= cyc[0];
var_8 <= cyc[1 +: 8];
end
always @ (posedge clk) begin
$display("%d pre : %x %x", cyc, var_8, var_1);
case (cyc)
0: begin
// Uninitialized
end
14: begin
`checkh (var_1, 1);
`checkh ({1'b0, var_8}, (cyc[0 +: 9] - 1) >> 1);
end
15: begin
`checkh (var_1, 1);
`checkh (var_8, 8'hf5);
end
16: begin
`checkh (var_1, 0);
`checkh (var_8, 8'hf5);
end
17, 18: begin
`checkh (var_1, 0);
`checkh (var_8, 8'h5f);
end
19: begin
`checkh (var_1, ~cyc[0]);
`checkh (var_8, 8'h5f);
end
21, 22: begin
`checkh (var_1, 1);
`checkh (var_8, 8'h5a);
end
23, 24: begin
`checkh (var_1, 0);
`checkh (var_8, 8'ha5);
end
default: begin
`checkh ({var_8, var_1}, cyc[0 +: 9] - 1);
end
endcase
`ifndef REVERSE
if (cyc == 13) force var_1 = 1;
if (cyc == 15) force var_1 = 0;
if (cyc == 18) release var_1;
if (cyc == 14) force var_8 = 8'hf5;
if (cyc == 16) force var_8 = 8'h5f;
if (cyc == 19) release var_8;
if (cyc == 20) force {var_1, var_8} = 9'b1_0101_1010;
if (cyc == 22) force {var_8, var_1} = 9'b1010_0101_0;
if (cyc == 24) release {var_1, var_8};
`else
if (cyc == 18) release var_1;
if (cyc == 15) force var_1 = 0;
if (cyc == 13) force var_1 = 1;
if (cyc == 19) release var_8;
if (cyc == 16) force var_8 = 8'h5f;
if (cyc == 14) force var_8 = 8'hf5;
if (cyc == 24) release {var_1, var_8};
if (cyc == 22) force {var_8, var_1} = 9'b1010_0101_0;
if (cyc == 20) force {var_1, var_8} = 9'b1_0101_1010;
`endif
$display("%d post: %x %x", cyc, var_8, var_1);
case (cyc)
0: begin
// Uninitialized
end
13: begin
`checkh (var_1, 1);
`checkh ({1'b0, var_8}, (cyc[0 +: 9] - 1) >> 1);
end
14: begin
`checkh (var_1, 1);
`checkh (var_8, 8'hf5);
end
15: begin
`checkh (var_1, 0);
`checkh (var_8, 8'hf5);
end
16, 17, 18: begin
`checkh (var_1, 0);
`checkh (var_8, 8'h5f);
end
19: begin
`checkh (var_1, ~cyc[0]);
`checkh (var_8, 8'h5f);
end
20, 21: begin
`checkh (var_1, 1);
`checkh (var_8, 8'h5a);
end
22, 23, 24: begin
`checkh (var_1, 0);
`checkh (var_8, 8'ha5);
end
default: begin
`checkh ({var_8, var_1}, cyc[0 +: 9] - 1);
end
endcase
if (cyc == 30) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,24 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
top_filename("t/t_force_release_var.v");
compile(
verilator_flags2 => ['+define+REVERSE']
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,211 @@
$version Generated by VerilatedVcd $end
$date Sun Dec 19 14:45:16 2021 $end
$timescale 1ps $end
$scope module top $end
$var wire 1 # clk $end
$scope module t $end
$var wire 1 # clk $end
$var wire 32 $ cyc [31:0] $end
$var wire 1 % var_1 $end
$var wire 8 & var_8 [7:0] $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
0#
b00000000000000000000000000000000 $
0%
b00000000 &
#10
1#
b00000000000000000000000000000001 $
#15
0#
#20
1#
b00000000000000000000000000000010 $
1%
#25
0#
#30
1#
b00000000000000000000000000000011 $
0%
b00000001 &
#35
0#
#40
1#
b00000000000000000000000000000100 $
1%
#45
0#
#50
1#
b00000000000000000000000000000101 $
0%
b00000010 &
#55
0#
#60
1#
b00000000000000000000000000000110 $
1%
#65
0#
#70
1#
b00000000000000000000000000000111 $
0%
b00000011 &
#75
0#
#80
1#
b00000000000000000000000000001000 $
1%
#85
0#
#90
1#
b00000000000000000000000000001001 $
0%
b00000100 &
#95
0#
#100
1#
b00000000000000000000000000001010 $
1%
#105
0#
#110
1#
b00000000000000000000000000001011 $
0%
b00000101 &
#115
0#
#120
1#
b00000000000000000000000000001100 $
1%
#125
0#
#130
1#
b00000000000000000000000000001101 $
0%
b00000110 &
#135
0#
#140
1#
b00000000000000000000000000001110 $
1%
#145
0#
#150
1#
b00000000000000000000000000001111 $
b11110101 &
#155
0#
#160
1#
b00000000000000000000000000010000 $
0%
#165
0#
#170
1#
b00000000000000000000000000010001 $
b01011111 &
#175
0#
#180
1#
b00000000000000000000000000010010 $
#185
0#
#190
1#
b00000000000000000000000000010011 $
#195
0#
#200
1#
b00000000000000000000000000010100 $
1%
b00001001 &
#205
0#
#210
1#
b00000000000000000000000000010101 $
b01011010 &
#215
0#
#220
1#
b00000000000000000000000000010110 $
#225
0#
#230
1#
b00000000000000000000000000010111 $
0%
b10100101 &
#235
0#
#240
1#
b00000000000000000000000000011000 $
#245
0#
#250
1#
b00000000000000000000000000011001 $
b00001100 &
#255
0#
#260
1#
b00000000000000000000000000011010 $
1%
#265
0#
#270
1#
b00000000000000000000000000011011 $
0%
b00001101 &
#275
0#
#280
1#
b00000000000000000000000000011100 $
1%
#285
0#
#290
1#
b00000000000000000000000000011101 $
0%
b00001110 &
#295
0#
#300
1#
b00000000000000000000000000011110 $
1%
#305
0#
#310
1#
b00000000000000000000000000011111 $
0%
b00001111 &

View File

@ -0,0 +1,26 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
top_filename("t/t_force_release_var.v");
compile(
verilator_flags2 => ['--trace']
);
execute(
check_finished => 1,
);
vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename});
ok(1);
1;