Support force/release (#2491) (#2593).

This commit is contained in:
Wilson Snyder 2022-01-01 12:24:19 -05:00
parent 24a0d2a0c9
commit 0c3ffa1841
27 changed files with 579 additions and 87 deletions

View File

@ -19,6 +19,7 @@ Verilator 4.217 devel
**Minor:**
* Support force/release (#2491) (#2593). [Shunyao CAD]
* Support lower dimension looping in foreach loops (#3172). [Ehab Ibrahim]
* Support up to 64 bit enums for .next/.prev/.name (#3244). [Alexander Grobman]
* Reduce .rodata footprint of trace initialization (#3250). [Geza Lore, Shunyao CAD]

View File

@ -197,6 +197,7 @@ RAW_OBJS = \
V3Expand.o \
V3File.o \
V3FileLine.o \
V3Force.o \
V3Gate.o \
V3GenClk.o \
V3Global.o \

View File

@ -70,6 +70,29 @@ AstNode::AstNode(AstType 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,6 +1511,14 @@ 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); }
@ -1665,6 +1673,9 @@ public:
static void user5ClearTree() { AstUser5InUse::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

@ -1988,6 +1988,7 @@ private:
bool m_fileDescr : 1; // File descriptor
bool m_isRand : 1; // Random variable
bool m_isConst : 1; // Table contains constant data
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
bool m_isStatic : 1; // Static C variable (for Verilog see instead isAutomatic)
bool m_isPulldown : 1; // Tri0
bool m_isPullup : 1; // Tri1
@ -2026,6 +2027,7 @@ private:
m_fileDescr = false;
m_isRand = false;
m_isConst = false;
m_isContinuously = false;
m_isStatic = false;
m_isPulldown = false;
m_isPullup = false;
@ -2180,6 +2182,7 @@ public:
void primaryIO(bool flag) { m_primaryIO = flag; }
void isRand(bool flag) { m_isRand = flag; }
void isConst(bool flag) { m_isConst = flag; }
void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
void funcLocal(bool flag) { m_funcLocal = flag; }
@ -2203,6 +2206,7 @@ public:
virtual void tag(const string& text) override { m_tag = text; }
virtual string tag() const override { return m_tag; }
bool isAnsi() const { return m_ansi; }
bool isContinuously() const { return m_isContinuously; }
bool isDeclTyped() const { return m_declTyped; }
bool isInoutish() const { return m_direction.isInoutish(); }
bool isNonOutput() const { return m_direction.isNonOutput(); }
@ -2272,6 +2276,7 @@ public:
if (fromp->attrClockEn()) attrClockEn(true);
if (fromp->attrFileDescr()) attrFileDescr(true);
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
if (fromp->isContinuously()) isContinuously(true);
}
bool gateMultiInputOptimizable() const {
// Ok to gate optimize; must return false if propagateAttrFrom would do anything
@ -3537,6 +3542,37 @@ public:
uint32_t direction() const { return (uint32_t)m_direction; }
};
class AstAssignForce final : public AstNodeAssign {
public:
AstAssignForce(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: ASTGEN_SUPER_AssignForce(fl, lhsp, rhsp) {
v3Global.useForce(true);
}
ASTNODE_NODE_FUNCS(AssignForce)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
return new AstAssignForce{this->fileline(), lhsp, rhsp};
}
virtual bool brokeLhsMustBeLvalue() const override { return true; }
};
class AstAssignRelease final : public AstNodeAssign {
// Release is treated similar to an assign to `z
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);
}
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(); }
};
class AstAssignPre final : public AstNodeAssign {
// Like Assign, but predelayed assignment requiring special order handling
public:

View File

@ -124,9 +124,15 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
putqs(nodep, "*/\n");
}
virtual void visit(AstNodeAssign* nodep) override {
iterateAndNextNull(nodep->lhsp());
putfs(nodep, " " + nodep->verilogKwd() + " ");
iterateAndNextNull(nodep->rhsp());
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 (!m_suppressSemi) puts(";\n");
}
virtual void visit(AstAssignDly* nodep) override {

362
src/V3Force.cpp Normal file
View File

@ -0,0 +1,362 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Make lookup forces
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-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
//
//*************************************************************************
// 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
//
// Force sets the appropriate __forceon bits indicating a force is in
// effect using the value in __forcein. Release clears the
// appropriate __forceon bits.
//
// IEEE says that procedural assignments "hold" the forced value even
// after a release, so add an assignment to the original __preforce too.
//
// 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.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3Force.h"
#include "V3Simulate.h"
#include "V3Stats.h"
#include "V3Ast.h"
#include <cmath>
#include <vector>
//######################################################################
// Force shared state
class ForceBaseVisitor VL_NOT_FINAL : public AstNVisitor {
// TYPES
public:
// Enum value must correspond to which user#p is used
enum class FVar : uint8_t { FORCEON = 2, FORCEIN = 3, PREFORCE = 4 };
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
public:
VL_DEBUG_FUNC; // Declare debug()
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 "";
}
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);
}
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(), AstVarType::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()};
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;
}
};
//######################################################################
// Recurse left-hand-side variables to do replaces underneath a force or release
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
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;
}
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; }
};
//######################################################################
// Force class functions
class ForceVisitor final : public ForceBaseVisitor {
private:
// NODE STATE
// See ForceBaseVisitor
const AstUser1InUse m_inuser1;
const AstUser2InUse m_inuser2;
const AstUser3InUse m_inuser3;
const AstUser4InUse 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};
}
}
V3Global::dumpCheckGlobalTree("force", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
}

34
src/V3Force.h Normal file
View File

@ -0,0 +1,34 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Process force/release
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-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
//
//*************************************************************************
#ifndef VERILATOR_V3FORCE_H_
#define VERILATOR_V3FORCE_H_
#include "config_build.h"
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
//============================================================================
class V3Force final {
public:
static void forceAll(AstNetlist* nodep);
};
#endif // Guard

View File

@ -103,6 +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_useParallelBuild = false; // Use parallel build for model
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
@ -150,6 +151,8 @@ 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

@ -35,6 +35,7 @@ private:
// NODE STATE
// STATE
bool m_setContinuously = false; // Set that var has some continuous assignment
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
@ -47,6 +48,9 @@ private:
// VarRef: LValue its reference
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
if (nodep->varp()) {
if (nodep->access().isWriteOrRW() && m_setContinuously) {
nodep->varp()->isContinuously(true);
}
if (nodep->access().isWriteOrRW() && !m_ftaskp && nodep->varp()->isReadOnly()) {
nodep->v3warn(ASSIGNIN,
"Assigning to input/const variable: " << nodep->prettyNameQ());
@ -69,10 +73,13 @@ private:
}
virtual void visit(AstNodeAssign* nodep) override {
VL_RESTORER(m_setRefLvalue);
VL_RESTORER(m_setContinuously);
{
m_setRefLvalue = VAccess::WRITE;
m_setContinuously = VN_IS(nodep, AssignW) || VN_IS(nodep, AssignAlias);
iterateAndNextNull(nodep->lhsp());
m_setRefLvalue = VAccess::NOCHANGE;
m_setContinuously = false;
iterateAndNextNull(nodep->rhsp());
}
}

View File

@ -753,7 +753,9 @@ private:
virtual void visit(AstNodeAssign* nodep) override {
if (jumpingOver(nodep)) return;
if (!optimizable()) return; // Accelerate
if (VN_IS(nodep, AssignDly)) {
if (VN_IS(nodep, AssignForce)) {
clearOptimizable(nodep, "Force");
} else if (VN_IS(nodep, AssignDly)) {
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non-dly assigns");
m_anyAssignDly = true;
m_inDlyAssign = true;

View File

@ -51,6 +51,7 @@
#include "V3EmitXml.h"
#include "V3Expand.h"
#include "V3File.h"
#include "V3Force.h"
#include "V3Gate.h"
#include "V3GenClk.h"
#include "V3Graph.h"
@ -301,6 +302,10 @@ 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());

View File

@ -3090,9 +3090,9 @@ statement_item<nodep>: // IEEE: statement_item
| yDEASSIGN variable_lvalue ';'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); }
| yFORCE expr '=' expr ';'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 force"); }
{ $$ = new AstAssignForce{$1, $2, $4}; }
| yRELEASE variable_lvalue ';'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 release"); }
{ $$ = new AstAssignRelease{$1, VFlagChildDType{}, $2}; }
//
// // IEEE: case_statement
| unique_priorityE caseStart caseAttrE case_itemListE yENDCASE { $$ = $2; if ($4) $2->addItemsp($4);

View File

@ -1,26 +0,0 @@
%Error-UNSUPPORTED: t/t_force.v:25:7: Unsupported: Verilog 1995 force
25 | force bus[1:0] = 2'b10;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_force.v:29:7: Unsupported: Verilog 1995 release
29 | release bus;
| ^~~~~~~
%Error-UNSUPPORTED: t/t_force.v:43:10: Unsupported: Verilog 1995 force
43 | force never_driven = 32'h888;
| ^~~~~
%Error-UNSUPPORTED: t/t_force.v:56:10: Unsupported: Verilog 1995 release
56 | release never_forced;
| ^~~~~~~
%Error-UNSUPPORTED: t/t_force.v:65:10: Unsupported: Verilog 1995 force
65 | force bus = 4'b0111;
| ^~~~~
%Error-UNSUPPORTED: t/t_force.v:69:10: Unsupported: Verilog 1995 force
69 | force bus = 4'b1111;
| ^~~~~
%Error-UNSUPPORTED: t/t_force.v:73:10: Unsupported: Verilog 1995 release
73 | release bus;
| ^~~~~~~
%Error-UNSUPPORTED: t/t_force.v:85:10: Unsupported: Verilog 1995 release
85 | release bus[0];
| ^~~~~~~
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -1,5 +1,6 @@
%Error-UNSUPPORTED: t/t_force_mid.v:29:10: Unsupported: Verilog 1995 force
29 | force tried = 4'b1010;
| ^~~~~
%Error-UNSUPPORTED: t/t_force_mid.v:18:17: Unsupported: Force/Release on primary input/output net 'tried'
: ... Suggest assign it to/from a temporary net and force/release that
18 | output [3:0] tried;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -10,14 +10,10 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
ok(1);
1;

View File

@ -8,13 +8,14 @@
`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*/
// Inouts
// Outputs
tried,
// Inputs
clk
);
input clk;
inout tri [3:0] tried;
output [3:0] tried;
integer cyc = 0;

21
test_regress/t/t_force_multi.pl Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2021 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,42 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2021 by Wilson Snyder.
// 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;
integer cyc = 0;
logic [3:0] busa;
logic [3:0] busb;
// Test loop
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc == 0) begin
busa <= 4'b0101;
busb <= 4'b0111;
end
else if (cyc == 1) begin
force {busa, busb} = 8'b1111_1101;
end
else if (cyc == 2) begin
`checkh(busa, 4'b1111);
`checkh(busb, 4'b1101);
end
//
else if (cyc == 99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -1,11 +0,0 @@
%Error-UNSUPPORTED: t/t_force_subnet.v:27:10: Unsupported: Verilog 1995 force
27 | force sub1.subnet = 8'h00;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_force_subnet.v:31:10: Unsupported: Verilog 1995 force
31 | force subnet = 8'h10;
| ^~~~~
%Error-UNSUPPORTED: t/t_force_subnet.v:35:10: Unsupported: Verilog 1995 release
35 | release subnet;
| ^~~~~~~
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -15,19 +15,18 @@ module t(/*AUTOARG*/
integer cyc = 0;
tri logic [7:0] subnet;
logic [7:0] subnet;
sub1 sub1(.*);
sub2 sub2(.*);
// Test loop
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc == 10) begin
`checkh(subnet, 8'h11);
force sub1.subnet = 8'h00; // sub1.subnet same as subnet
force sub1.subnet = 8'h01; // sub1.subnet same as subnet
end
else if (cyc == 11) begin
`checkh(subnet, 8'h00);
`checkh(subnet, 8'h01);
force subnet = 8'h10; // sub1.subnet same as subnet
end
else if (cyc == 12) begin
@ -46,11 +45,6 @@ module t(/*AUTOARG*/
endmodule
module sub1(inout logic [7:0] subnet);
assign subnet = 8'hz1;
module sub1(output logic [7:0] subnet);
assign subnet = 8'h11;
endmodule
module sub2(inout logic [7:0] subnet);
assign subnet = 8'h1z;
endmodule

View File

@ -1,8 +0,0 @@
%Error-UNSUPPORTED: t/t_force_subvar.v:26:10: Unsupported: Verilog 1995 force
26 | force sub.subvar = 32'hffff;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_force_subvar.v:36:10: Unsupported: Verilog 1995 release
36 | release sub.subvar;
| ^~~~~~~
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -1,4 +1,5 @@
%Error-UNSUPPORTED: t/t_force_tri.v:27:10: Unsupported: Verilog 1995 force
%Error-UNSUPPORTED: t/t_force_tri.v:27:10: Unsupported tristate construct: ASSIGNFORCE
: ... In instance t
27 | force bus = 4'bzz10;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest

View File

@ -10,14 +10,10 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
ok(1);
1;