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:
parent
b4d8220cbb
commit
539c9d4c63
|
|
@ -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
|
||||
-----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
11
src/V3Ast.h
11
src/V3Ast.h
|
|
@ -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=##"
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
558
src/V3Force.cpp
558
src/V3Force.cpp
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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%
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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 &
|
||||
|
|
@ -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;
|
||||
Loading…
Reference in New Issue