mirror of
https://github.com/verilator/verilator.git
synced 2025-04-16 01:26:54 +00:00
parent
24a0d2a0c9
commit
0c3ffa1841
1
Changes
1
Changes
@ -19,6 +19,7 @@ Verilator 4.217 devel
|
|||||||
|
|
||||||
**Minor:**
|
**Minor:**
|
||||||
|
|
||||||
|
* Support force/release (#2491) (#2593). [Shunyao CAD]
|
||||||
* Support lower dimension looping in foreach loops (#3172). [Ehab Ibrahim]
|
* Support lower dimension looping in foreach loops (#3172). [Ehab Ibrahim]
|
||||||
* Support up to 64 bit enums for .next/.prev/.name (#3244). [Alexander Grobman]
|
* Support up to 64 bit enums for .next/.prev/.name (#3244). [Alexander Grobman]
|
||||||
* Reduce .rodata footprint of trace initialization (#3250). [Geza Lore, Shunyao CAD]
|
* Reduce .rodata footprint of trace initialization (#3250). [Geza Lore, Shunyao CAD]
|
||||||
|
@ -197,6 +197,7 @@ RAW_OBJS = \
|
|||||||
V3Expand.o \
|
V3Expand.o \
|
||||||
V3File.o \
|
V3File.o \
|
||||||
V3FileLine.o \
|
V3FileLine.o \
|
||||||
|
V3Force.o \
|
||||||
V3Gate.o \
|
V3Gate.o \
|
||||||
V3GenClk.o \
|
V3GenClk.o \
|
||||||
V3Global.o \
|
V3Global.o \
|
||||||
|
@ -70,6 +70,29 @@ AstNode::AstNode(AstType t, FileLine* fl)
|
|||||||
editCountInc();
|
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 {
|
AstNode* AstNode::abovep() const {
|
||||||
// m_headtailp only valid at beginning or end of list
|
// m_headtailp only valid at beginning or end of list
|
||||||
// Avoid supporting at other locations as would require walking
|
// Avoid supporting at other locations as would require walking
|
||||||
|
11
src/V3Ast.h
11
src/V3Ast.h
@ -1511,6 +1511,14 @@ public:
|
|||||||
uint8_t brokenState() const { return m_brokenState; }
|
uint8_t brokenState() const { return m_brokenState; }
|
||||||
void brokenState(uint8_t value) { m_brokenState = value; }
|
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()
|
// Used by AstNode::broken()
|
||||||
bool brokeExists() const { return V3Broken::isLinkable(this); }
|
bool brokeExists() const { return V3Broken::isLinkable(this); }
|
||||||
bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); }
|
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
|
static void user5ClearTree() { AstUser5InUse::clear(); } // Clear userp()'s across the entire tree
|
||||||
// clang-format on
|
// 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; }
|
vluint64_t editCount() const { return m_editCount; }
|
||||||
void editCountInc() {
|
void editCountInc() {
|
||||||
m_editCount = ++s_editCntGbl; // Preincrement, so can "watch AstNode::s_editCntGbl=##"
|
m_editCount = ++s_editCntGbl; // Preincrement, so can "watch AstNode::s_editCntGbl=##"
|
||||||
|
@ -1988,6 +1988,7 @@ private:
|
|||||||
bool m_fileDescr : 1; // File descriptor
|
bool m_fileDescr : 1; // File descriptor
|
||||||
bool m_isRand : 1; // Random variable
|
bool m_isRand : 1; // Random variable
|
||||||
bool m_isConst : 1; // Table contains constant data
|
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_isStatic : 1; // Static C variable (for Verilog see instead isAutomatic)
|
||||||
bool m_isPulldown : 1; // Tri0
|
bool m_isPulldown : 1; // Tri0
|
||||||
bool m_isPullup : 1; // Tri1
|
bool m_isPullup : 1; // Tri1
|
||||||
@ -2026,6 +2027,7 @@ private:
|
|||||||
m_fileDescr = false;
|
m_fileDescr = false;
|
||||||
m_isRand = false;
|
m_isRand = false;
|
||||||
m_isConst = false;
|
m_isConst = false;
|
||||||
|
m_isContinuously = false;
|
||||||
m_isStatic = false;
|
m_isStatic = false;
|
||||||
m_isPulldown = false;
|
m_isPulldown = false;
|
||||||
m_isPullup = false;
|
m_isPullup = false;
|
||||||
@ -2180,6 +2182,7 @@ public:
|
|||||||
void primaryIO(bool flag) { m_primaryIO = flag; }
|
void primaryIO(bool flag) { m_primaryIO = flag; }
|
||||||
void isRand(bool flag) { m_isRand = flag; }
|
void isRand(bool flag) { m_isRand = flag; }
|
||||||
void isConst(bool flag) { m_isConst = flag; }
|
void isConst(bool flag) { m_isConst = flag; }
|
||||||
|
void isContinuously(bool flag) { m_isContinuously = flag; }
|
||||||
void isStatic(bool flag) { m_isStatic = flag; }
|
void isStatic(bool flag) { m_isStatic = flag; }
|
||||||
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
|
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
|
||||||
void funcLocal(bool flag) { m_funcLocal = flag; }
|
void funcLocal(bool flag) { m_funcLocal = flag; }
|
||||||
@ -2203,6 +2206,7 @@ public:
|
|||||||
virtual void tag(const string& text) override { m_tag = text; }
|
virtual void tag(const string& text) override { m_tag = text; }
|
||||||
virtual string tag() const override { return m_tag; }
|
virtual string tag() const override { return m_tag; }
|
||||||
bool isAnsi() const { return m_ansi; }
|
bool isAnsi() const { return m_ansi; }
|
||||||
|
bool isContinuously() const { return m_isContinuously; }
|
||||||
bool isDeclTyped() const { return m_declTyped; }
|
bool isDeclTyped() const { return m_declTyped; }
|
||||||
bool isInoutish() const { return m_direction.isInoutish(); }
|
bool isInoutish() const { return m_direction.isInoutish(); }
|
||||||
bool isNonOutput() const { return m_direction.isNonOutput(); }
|
bool isNonOutput() const { return m_direction.isNonOutput(); }
|
||||||
@ -2272,6 +2276,7 @@ public:
|
|||||||
if (fromp->attrClockEn()) attrClockEn(true);
|
if (fromp->attrClockEn()) attrClockEn(true);
|
||||||
if (fromp->attrFileDescr()) attrFileDescr(true);
|
if (fromp->attrFileDescr()) attrFileDescr(true);
|
||||||
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
|
if (fromp->attrIsolateAssign()) attrIsolateAssign(true);
|
||||||
|
if (fromp->isContinuously()) isContinuously(true);
|
||||||
}
|
}
|
||||||
bool gateMultiInputOptimizable() const {
|
bool gateMultiInputOptimizable() const {
|
||||||
// Ok to gate optimize; must return false if propagateAttrFrom would do anything
|
// 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; }
|
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 {
|
class AstAssignPre final : public AstNodeAssign {
|
||||||
// Like Assign, but predelayed assignment requiring special order handling
|
// Like Assign, but predelayed assignment requiring special order handling
|
||||||
public:
|
public:
|
||||||
|
@ -124,9 +124,15 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor {
|
|||||||
putqs(nodep, "*/\n");
|
putqs(nodep, "*/\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeAssign* nodep) override {
|
virtual void visit(AstNodeAssign* nodep) override {
|
||||||
iterateAndNextNull(nodep->lhsp());
|
if (VN_IS(nodep, AssignRelease)) {
|
||||||
putfs(nodep, " " + nodep->verilogKwd() + " ");
|
puts("release ");
|
||||||
iterateAndNextNull(nodep->rhsp());
|
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");
|
if (!m_suppressSemi) puts(";\n");
|
||||||
}
|
}
|
||||||
virtual void visit(AstAssignDly* nodep) override {
|
virtual void visit(AstAssignDly* nodep) override {
|
||||||
|
362
src/V3Force.cpp
Normal file
362
src/V3Force.cpp
Normal 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
34
src/V3Force.h
Normal 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
|
@ -103,6 +103,7 @@ class V3Global final {
|
|||||||
// Experimenting with always requiring heavy, see (#2701)
|
// Experimenting with always requiring heavy, see (#2701)
|
||||||
bool m_needTraceDumper = false; // Need __Vm_dumperp in symbols
|
bool m_needTraceDumper = false; // Need __Vm_dumperp in symbols
|
||||||
bool m_dpi = false; // Need __Dpi include files
|
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_useParallelBuild = false; // Use parallel build for model
|
||||||
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
|
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
|
||||||
|
|
||||||
@ -150,6 +151,8 @@ public:
|
|||||||
UASSERT(!m_hierPlanp, "call once");
|
UASSERT(!m_hierPlanp, "call once");
|
||||||
m_hierPlanp = plan;
|
m_hierPlanp = plan;
|
||||||
}
|
}
|
||||||
|
void useForce(bool flag) { m_useForce = flag; }
|
||||||
|
bool useForce() const { return m_useForce; }
|
||||||
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
|
void useParallelBuild(bool flag) { m_useParallelBuild = flag; }
|
||||||
bool useParallelBuild() const { return m_useParallelBuild; }
|
bool useParallelBuild() const { return m_useParallelBuild; }
|
||||||
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
|
void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; }
|
||||||
|
@ -35,6 +35,7 @@ private:
|
|||||||
// NODE STATE
|
// NODE STATE
|
||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
|
bool m_setContinuously = false; // Set that var has some continuous assignment
|
||||||
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments
|
||||||
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
||||||
|
|
||||||
@ -47,6 +48,9 @@ private:
|
|||||||
// VarRef: LValue its reference
|
// VarRef: LValue its reference
|
||||||
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
|
if (m_setRefLvalue != VAccess::NOCHANGE) nodep->access(m_setRefLvalue);
|
||||||
if (nodep->varp()) {
|
if (nodep->varp()) {
|
||||||
|
if (nodep->access().isWriteOrRW() && m_setContinuously) {
|
||||||
|
nodep->varp()->isContinuously(true);
|
||||||
|
}
|
||||||
if (nodep->access().isWriteOrRW() && !m_ftaskp && nodep->varp()->isReadOnly()) {
|
if (nodep->access().isWriteOrRW() && !m_ftaskp && nodep->varp()->isReadOnly()) {
|
||||||
nodep->v3warn(ASSIGNIN,
|
nodep->v3warn(ASSIGNIN,
|
||||||
"Assigning to input/const variable: " << nodep->prettyNameQ());
|
"Assigning to input/const variable: " << nodep->prettyNameQ());
|
||||||
@ -69,10 +73,13 @@ private:
|
|||||||
}
|
}
|
||||||
virtual void visit(AstNodeAssign* nodep) override {
|
virtual void visit(AstNodeAssign* nodep) override {
|
||||||
VL_RESTORER(m_setRefLvalue);
|
VL_RESTORER(m_setRefLvalue);
|
||||||
|
VL_RESTORER(m_setContinuously);
|
||||||
{
|
{
|
||||||
m_setRefLvalue = VAccess::WRITE;
|
m_setRefLvalue = VAccess::WRITE;
|
||||||
|
m_setContinuously = VN_IS(nodep, AssignW) || VN_IS(nodep, AssignAlias);
|
||||||
iterateAndNextNull(nodep->lhsp());
|
iterateAndNextNull(nodep->lhsp());
|
||||||
m_setRefLvalue = VAccess::NOCHANGE;
|
m_setRefLvalue = VAccess::NOCHANGE;
|
||||||
|
m_setContinuously = false;
|
||||||
iterateAndNextNull(nodep->rhsp());
|
iterateAndNextNull(nodep->rhsp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -753,7 +753,9 @@ private:
|
|||||||
virtual void visit(AstNodeAssign* nodep) override {
|
virtual void visit(AstNodeAssign* nodep) override {
|
||||||
if (jumpingOver(nodep)) return;
|
if (jumpingOver(nodep)) return;
|
||||||
if (!optimizable()) return; // Accelerate
|
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");
|
if (m_anyAssignComb) clearOptimizable(nodep, "Mix of dly/non-dly assigns");
|
||||||
m_anyAssignDly = true;
|
m_anyAssignDly = true;
|
||||||
m_inDlyAssign = true;
|
m_inDlyAssign = true;
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
#include "V3EmitXml.h"
|
#include "V3EmitXml.h"
|
||||||
#include "V3Expand.h"
|
#include "V3Expand.h"
|
||||||
#include "V3File.h"
|
#include "V3File.h"
|
||||||
|
#include "V3Force.h"
|
||||||
#include "V3Gate.h"
|
#include "V3Gate.h"
|
||||||
#include "V3GenClk.h"
|
#include "V3GenClk.h"
|
||||||
#include "V3Graph.h"
|
#include "V3Graph.h"
|
||||||
@ -301,6 +302,10 @@ static void process() {
|
|||||||
// After V3Task so task internal variables will get renamed
|
// After V3Task so task internal variables will get renamed
|
||||||
V3Name::nameAll(v3Global.rootp());
|
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
|
// Loop unrolling & convert FORs to WHILEs
|
||||||
V3Unroll::unrollAll(v3Global.rootp());
|
V3Unroll::unrollAll(v3Global.rootp());
|
||||||
|
|
||||||
|
@ -3090,9 +3090,9 @@ statement_item<nodep>: // IEEE: statement_item
|
|||||||
| yDEASSIGN variable_lvalue ';'
|
| yDEASSIGN variable_lvalue ';'
|
||||||
{ $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); }
|
{ $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); }
|
||||||
| yFORCE expr '=' expr ';'
|
| yFORCE expr '=' expr ';'
|
||||||
{ $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 force"); }
|
{ $$ = new AstAssignForce{$1, $2, $4}; }
|
||||||
| yRELEASE variable_lvalue ';'
|
| yRELEASE variable_lvalue ';'
|
||||||
{ $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 release"); }
|
{ $$ = new AstAssignRelease{$1, VFlagChildDType{}, $2}; }
|
||||||
//
|
//
|
||||||
// // IEEE: case_statement
|
// // IEEE: case_statement
|
||||||
| unique_priorityE caseStart caseAttrE case_itemListE yENDCASE { $$ = $2; if ($4) $2->addItemsp($4);
|
| unique_priorityE caseStart caseAttrE case_itemListE yENDCASE { $$ = $2; if ($4) $2->addItemsp($4);
|
||||||
|
@ -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
|
|
@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
|||||||
scenarios(simulator => 1);
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
compile(
|
compile(
|
||||||
fails => $Self->{vlt_all},
|
|
||||||
expect_filename => $Self->{golden_filename},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
check_finished => 1,
|
check_finished => 1,
|
||||||
) if !$Self->{vlt_all};
|
);
|
||||||
|
|
||||||
ok(1);
|
ok(1);
|
||||||
1;
|
1;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
%Error-UNSUPPORTED: t/t_force_mid.v:29:10: Unsupported: Verilog 1995 force
|
%Error-UNSUPPORTED: t/t_force_mid.v:18:17: Unsupported: Force/Release on primary input/output net 'tried'
|
||||||
29 | force tried = 4'b1010;
|
: ... 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
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
@ -10,14 +10,10 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
|||||||
|
|
||||||
scenarios(simulator => 1);
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
compile(
|
lint(
|
||||||
fails => $Self->{vlt_all},
|
fails => $Self->{vlt_all},
|
||||||
expect_filename => $Self->{golden_filename},
|
expect_filename => $Self->{golden_filename},
|
||||||
);
|
);
|
||||||
|
|
||||||
execute(
|
|
||||||
check_finished => 1,
|
|
||||||
) if !$Self->{vlt_all};
|
|
||||||
|
|
||||||
ok(1);
|
ok(1);
|
||||||
1;
|
1;
|
||||||
|
@ -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)
|
`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*/
|
module t(/*AUTOARG*/
|
||||||
// Inouts
|
// Outputs
|
||||||
tried,
|
tried,
|
||||||
// Inputs
|
// Inputs
|
||||||
clk
|
clk
|
||||||
);
|
);
|
||||||
|
|
||||||
input clk;
|
input clk;
|
||||||
inout tri [3:0] tried;
|
output [3:0] tried;
|
||||||
|
|
||||||
integer cyc = 0;
|
integer cyc = 0;
|
||||||
|
|
||||||
|
21
test_regress/t/t_force_multi.pl
Executable file
21
test_regress/t/t_force_multi.pl
Executable 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;
|
42
test_regress/t/t_force_multi.v
Normal file
42
test_regress/t/t_force_multi.v
Normal 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
|
@ -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
|
|
@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
|||||||
scenarios(simulator => 1);
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
compile(
|
compile(
|
||||||
fails => $Self->{vlt_all},
|
|
||||||
expect_filename => $Self->{golden_filename},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
check_finished => 1,
|
check_finished => 1,
|
||||||
) if !$Self->{vlt_all};
|
);
|
||||||
|
|
||||||
ok(1);
|
ok(1);
|
||||||
1;
|
1;
|
||||||
|
@ -15,19 +15,18 @@ module t(/*AUTOARG*/
|
|||||||
|
|
||||||
integer cyc = 0;
|
integer cyc = 0;
|
||||||
|
|
||||||
tri logic [7:0] subnet;
|
logic [7:0] subnet;
|
||||||
sub1 sub1(.*);
|
sub1 sub1(.*);
|
||||||
sub2 sub2(.*);
|
|
||||||
|
|
||||||
// Test loop
|
// Test loop
|
||||||
always @ (posedge clk) begin
|
always @ (posedge clk) begin
|
||||||
cyc <= cyc + 1;
|
cyc <= cyc + 1;
|
||||||
if (cyc == 10) begin
|
if (cyc == 10) begin
|
||||||
`checkh(subnet, 8'h11);
|
`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
|
end
|
||||||
else if (cyc == 11) begin
|
else if (cyc == 11) begin
|
||||||
`checkh(subnet, 8'h00);
|
`checkh(subnet, 8'h01);
|
||||||
force subnet = 8'h10; // sub1.subnet same as subnet
|
force subnet = 8'h10; // sub1.subnet same as subnet
|
||||||
end
|
end
|
||||||
else if (cyc == 12) begin
|
else if (cyc == 12) begin
|
||||||
@ -46,11 +45,6 @@ module t(/*AUTOARG*/
|
|||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module sub1(inout logic [7:0] subnet);
|
module sub1(output logic [7:0] subnet);
|
||||||
assign subnet = 8'hz1;
|
assign subnet = 8'h11;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
module sub2(inout logic [7:0] subnet);
|
|
||||||
assign subnet = 8'h1z;
|
|
||||||
endmodule
|
|
||||||
|
|
||||||
|
@ -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
|
|
@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
|||||||
scenarios(simulator => 1);
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
compile(
|
compile(
|
||||||
fails => $Self->{vlt_all},
|
|
||||||
expect_filename => $Self->{golden_filename},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
execute(
|
execute(
|
||||||
check_finished => 1,
|
check_finished => 1,
|
||||||
) if !$Self->{vlt_all};
|
);
|
||||||
|
|
||||||
ok(1);
|
ok(1);
|
||||||
1;
|
1;
|
||||||
|
@ -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;
|
27 | force bus = 4'bzz10;
|
||||||
| ^~~~~
|
| ^~~~~
|
||||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
@ -10,14 +10,10 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
|||||||
|
|
||||||
scenarios(simulator => 1);
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
compile(
|
lint(
|
||||||
fails => $Self->{vlt_all},
|
fails => $Self->{vlt_all},
|
||||||
expect_filename => $Self->{golden_filename},
|
expect_filename => $Self->{golden_filename},
|
||||||
);
|
);
|
||||||
|
|
||||||
execute(
|
|
||||||
check_finished => 1,
|
|
||||||
) if !$Self->{vlt_all};
|
|
||||||
|
|
||||||
ok(1);
|
ok(1);
|
||||||
1;
|
1;
|
||||||
|
Loading…
Reference in New Issue
Block a user