Compare commits
34 Commits
20140650e5
...
f259a50031
Author | SHA1 | Date |
---|---|---|
![]() |
f259a50031 | |
![]() |
3aa7d7dfbc | |
![]() |
abd509ce53 | |
![]() |
1bf24c7eb4 | |
![]() |
1f0357ba93 | |
![]() |
db6b17fdb4 | |
![]() |
f3e109d8c5 | |
![]() |
371ac07c6f | |
![]() |
caf3522364 | |
![]() |
03e0d49d99 | |
![]() |
e2e5d9eaf1 | |
![]() |
2f199f20cf | |
![]() |
cefe1845df | |
![]() |
1044398f95 | |
![]() |
77180c4020 | |
![]() |
2fc12d951e | |
![]() |
0982260d3b | |
![]() |
58b867c39c | |
![]() |
4e8a8a0398 | |
![]() |
4dc6a31276 | |
![]() |
ce77bac99a | |
![]() |
31c279a7b3 | |
![]() |
9ad0de1efd | |
![]() |
d89df33fcd | |
![]() |
d1462f3120 | |
![]() |
9fc7143fce | |
![]() |
597b973f7b | |
![]() |
a84c5d2010 | |
![]() |
dbfbc657e1 | |
![]() |
8b3a6ba542 | |
![]() |
8ba7cec15b | |
![]() |
f037ac50b4 | |
![]() |
1f0e767b61 | |
![]() |
5777ab75c7 |
|
@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15)
|
|||
cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime
|
||||
project(
|
||||
Verilator
|
||||
VERSION 5.037
|
||||
VERSION 5.039
|
||||
HOMEPAGE_URL https://verilator.org
|
||||
LANGUAGES CXX
|
||||
)
|
||||
|
|
26
Changes
26
Changes
|
@ -8,7 +8,29 @@ The changes in each Verilator version are described below. The
|
|||
contributors that suggested or implemented a given issue are shown in []. Thanks!
|
||||
|
||||
|
||||
Verilator 5.037 devel
|
||||
Verilator 5.039 devel
|
||||
==========================
|
||||
|
||||
**Other:**
|
||||
|
||||
* Add ENUMITEMWIDTH error, and apply to X-extended and ranged values.
|
||||
* Add NOEFFECT warning, replacing previous `foreach` error.
|
||||
* Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang]
|
||||
* Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.]
|
||||
* Support randomize() on class member selects (#6161). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Support multiple variables on RHS of a `force` assignment (#6163). [Artur Bieniek, Antmicro Ltd.]]
|
||||
* Change control file `public_flat_*` and other signal attributes to support __ in names (#6140).
|
||||
* Fix constructor parameters in inheritance hierarchies (#6036) (#6070). [Petr Nohavica]
|
||||
* Fix cmake `-Wno` compiler flag testing (#6145). [Martin Stadler]
|
||||
* Fix class extends dotted error (#6162). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix genvar error with `-O0` (#6165). [Max Wipfli]
|
||||
* Fix uninitialized thread PGO counters (#6167). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix additional UNOPTFLAT combinational cycles automatically in DFG (#6168) (#6173). [Geza Lore]
|
||||
* Fix omitting error when assigning to an input (#6169). [Artur Bieniek, Antmicro Ltd.]]
|
||||
* Fix param-dependent class typedef linking (#6171). [Igor Zaworski, Antmicro Ltd.]
|
||||
|
||||
|
||||
Verilator 5.038 2025-07-08
|
||||
==========================
|
||||
|
||||
**Important:**
|
||||
|
@ -47,6 +69,7 @@ Verilator 5.037 devel
|
|||
* Optimize DFG variable elimination (#6091). [Geza Lore]
|
||||
* Optimize DFG PUSH_SEL_THROUGH_CONCAT pattern (#6092). [Geza Lore]
|
||||
* Optimize DFG before V3Gate (#6141). [Geza Lore]
|
||||
* Optimize DFG peephole patterns (#6149). [Geza Lore]
|
||||
* Optimize constification within Expand and Subst stages (#6111). [Geza Lore]
|
||||
* Fix --x-initial and --x-assign random stability (#2662) (#5958) (#6018) (#6025) (#6075). [Todd Strader]
|
||||
* Fix trace hierarchical-name runtime errors (#5668) (#6076). [Paul Swirhun]
|
||||
|
@ -95,6 +118,7 @@ Verilator 5.037 devel
|
|||
* Fix wide non-blocking assignment mis-optimization (#6150) (#6152) (#6155). [Todd Strader]
|
||||
* Fix interface array connections with non-zero low declaration index.
|
||||
* Fix developer build error on MacOS/Flex2.6.4 (#6153). [Paul Swirhun]
|
||||
* Fix crash with --dumpi-V3LinkDot without --debug (#6159). [Igor Zaworski, Antmicro Ltd.]
|
||||
|
||||
|
||||
Verilator 5.036 2025-04-27
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
# Then 'make maintainer-dist'
|
||||
#AC_INIT([Verilator],[#.### YYYY-MM-DD])
|
||||
#AC_INIT([Verilator],[#.### devel])
|
||||
AC_INIT([Verilator],[5.037 devel],
|
||||
AC_INIT([Verilator],[5.039 devel],
|
||||
[https://verilator.org],
|
||||
[verilator],[https://verilator.org])
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Anthony Donlon
|
|||
Anthony Moore
|
||||
Arkadiusz Kozdra
|
||||
Arthur Rosa
|
||||
Artur Bieniek
|
||||
Aylon Chaim Porat
|
||||
Bartłomiej Chmiel
|
||||
Brian Li
|
||||
|
@ -83,6 +84,7 @@ Huang Rui
|
|||
Huanghuang Zhou
|
||||
HungMingWu
|
||||
HyungKi Jeong
|
||||
Igor Zaworski
|
||||
Ilya Barkov
|
||||
Iru Cai
|
||||
Ivan Vnučec
|
||||
|
|
|
@ -606,6 +606,10 @@ Summary:
|
|||
optimizer. Alias for :vlopt:`-fno-dfg-pre-inline`,
|
||||
:vlopt:`-fno-dfg-post-inline` and :vlopt:`-fno-dfg-scoped`.
|
||||
|
||||
.. option:: -fno-dfg-break-cycles
|
||||
|
||||
Rarely needed. Disable breaking combinational cycles during DFG.
|
||||
|
||||
.. option:: -fno-dfg-peephole
|
||||
|
||||
Rarely needed. Disable the DFG peephole optimizer.
|
||||
|
|
|
@ -709,6 +709,28 @@ List Of Warnings
|
|||
missing."
|
||||
|
||||
|
||||
.. option:: ENUMITEMWIDTH
|
||||
|
||||
An error that an enum item value is being assigned from a value which
|
||||
would be truncated (similar to :option:`WIDTHTRUNC`), or from a sized
|
||||
literal constant with a different bit width (similar to
|
||||
:option:`WIDTHTRUNC` or :option:`WIDTHEXPAND`). IEEE requires this
|
||||
error, but it may be disabled.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. code-block:: sv
|
||||
:linenos:
|
||||
:emphasize-lines: 2
|
||||
|
||||
typedef enum [3:0] {
|
||||
WRONG_WIDTH = 33'h3 //<--- Warning
|
||||
} enum_t;
|
||||
|
||||
To repair, correct the size of the item's value directly, or use a cast,
|
||||
so the resulting width matches the enum's width.
|
||||
|
||||
|
||||
.. option:: ENUMVALUE
|
||||
|
||||
An error that an enum data type value is being assigned from another data
|
||||
|
@ -1286,6 +1308,21 @@ List Of Warnings
|
|||
simulate correctly.
|
||||
|
||||
|
||||
.. option:: NOEFFECT
|
||||
|
||||
Warns that the statement will have no effect and is roughly equivalent
|
||||
to not being present. This is only issued when it is "non-obvious",
|
||||
e.g. a :code:`if (0)` will not result in this warning.
|
||||
|
||||
Faulty example:
|
||||
|
||||
.. code-block:: sv
|
||||
|
||||
foreach (array[]) begin ... end //<--- Warning
|
||||
|
||||
For a fix, remove the statement.
|
||||
|
||||
|
||||
.. option:: NOLATCH
|
||||
|
||||
.. TODO better example
|
||||
|
|
|
@ -247,6 +247,7 @@ Lesik
|
|||
Libenzi
|
||||
Licker
|
||||
Liland
|
||||
LinkDot
|
||||
Liu
|
||||
Liwei
|
||||
Lockhart
|
||||
|
@ -480,6 +481,7 @@ Wfuture
|
|||
Whatson
|
||||
Wildman
|
||||
Wim
|
||||
Wipfli
|
||||
Wmisleading
|
||||
Wno
|
||||
Wojciech
|
||||
|
|
|
@ -218,7 +218,7 @@ class VlPgoProfiler final {
|
|||
};
|
||||
|
||||
// Counters are stored packed, all together to reduce cache effects
|
||||
std::array<uint64_t, N_Entries> m_counters; // Time spent on this record
|
||||
std::array<uint64_t, N_Entries> m_counters{}; // Time spent on this record
|
||||
std::vector<Record> m_records; // Record information
|
||||
|
||||
public:
|
||||
|
|
|
@ -175,6 +175,14 @@ package std;
|
|||
`endif
|
||||
endtask
|
||||
|
||||
static task killQueue(ref process processQueue[$]);
|
||||
`ifdef VERILATOR_TIMING
|
||||
while (processQueue.size() > 0) begin
|
||||
processQueue.pop_back().kill();
|
||||
end
|
||||
`endif
|
||||
endtask
|
||||
|
||||
// Two process references are equal if the different classes' containing
|
||||
// m_process are equal. Can't yet use <=> as the base class template
|
||||
// comparisons doesn't define <=> as they don't yet require --timing and C++20.
|
||||
|
|
|
@ -227,6 +227,7 @@ set(COMMON_SOURCES
|
|||
V3Descope.cpp
|
||||
V3Dfg.cpp
|
||||
V3DfgAstToDfg.cpp
|
||||
V3DfgBreakCycles.cpp
|
||||
V3DfgCache.cpp
|
||||
V3DfgDecomposition.cpp
|
||||
V3DfgDfgToAst.cpp
|
||||
|
|
|
@ -240,6 +240,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3Descope.o \
|
||||
V3Dfg.o \
|
||||
V3DfgAstToDfg.o \
|
||||
V3DfgBreakCycles.o \
|
||||
V3DfgCache.o \
|
||||
V3DfgDecomposition.o \
|
||||
V3DfgDfgToAst.o \
|
||||
|
|
|
@ -2222,6 +2222,9 @@ public:
|
|||
// ACCESSORS
|
||||
virtual string name() const VL_MT_STABLE { return ""; }
|
||||
virtual string origName() const { return ""; }
|
||||
string prettyOrigOrName() const {
|
||||
return prettyName(origName().empty() ? name() : origName());
|
||||
}
|
||||
virtual void name(const string& name) {
|
||||
this->v3fatalSrc("name() called on object without name() method");
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ protected:
|
|||
|
||||
public:
|
||||
ASTGEN_MEMBERS_AstNodeBlock;
|
||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Block name
|
||||
|
@ -1973,7 +1974,7 @@ class AstVar final : public AstNode {
|
|||
bool m_isConst : 1; // Table contains constant data
|
||||
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
|
||||
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
|
||||
bool m_isStatic : 1; // Static C variable (for Verilog see instead isAutomatic)
|
||||
bool m_isStatic : 1; // Static C variable (for Verilog see instead lifetime())
|
||||
bool m_isPulldown : 1; // Tri0
|
||||
bool m_isPullup : 1; // Tri1
|
||||
bool m_isIfaceParent : 1; // dtype is reference to interface present in this module
|
||||
|
@ -3096,14 +3097,18 @@ public:
|
|||
bool isCycleDelay() const { return m_isCycle; }
|
||||
};
|
||||
class AstDisable final : public AstNodeStmt {
|
||||
string m_name; // Name of block
|
||||
// @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot
|
||||
// @astgen ptr := m_targetp : Optional[AstNode] // Task or block after V3LinkDot
|
||||
public:
|
||||
AstDisable(FileLine* fl, const string& name)
|
||||
: ASTGEN_SUPER_Disable(fl)
|
||||
, m_name{name} {}
|
||||
AstDisable(FileLine* fl, AstNodeExpr* targetRefp)
|
||||
: ASTGEN_SUPER_Disable(fl) {
|
||||
this->targetRefp(targetRefp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstDisable;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Block name
|
||||
void name(const string& flag) override { m_name = flag; }
|
||||
const char* broken() const override;
|
||||
void dump(std::ostream& str) const override;
|
||||
void targetp(AstNode* nodep) { m_targetp = nodep; }
|
||||
AstNode* targetp() const { return m_targetp; }
|
||||
bool isBrancher() const override {
|
||||
return true; // SPECIAL: We don't process code after breaks
|
||||
}
|
||||
|
|
|
@ -2604,6 +2604,8 @@ void AstVar::dump(std::ostream& str) const {
|
|||
if (isPulldown()) str << " [PULLDOWN]";
|
||||
if (isUsedClock()) str << " [CLK]";
|
||||
if (isSigPublic()) str << " [P]";
|
||||
if (isSigUserRdPublic()) str << " [PRD]";
|
||||
if (isSigUserRWPublic()) str << " [PWR]";
|
||||
if (isInternal()) str << " [INTERNAL]";
|
||||
if (isLatched()) str << " [LATCHED]";
|
||||
if (isUsedLoopIdx()) str << " [LOOP]";
|
||||
|
@ -3177,6 +3179,20 @@ void AstDelay::dumpJson(std::ostream& str) const {
|
|||
dumpJsonBoolFunc(str, isCycleDelay);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
|
||||
const char* AstDisable::broken() const {
|
||||
BROKEN_RTN((m_targetp && targetRefp()) || ((!m_targetp && !targetRefp())));
|
||||
return nullptr;
|
||||
}
|
||||
void AstDisable::dump(std::ostream& str) const {
|
||||
this->AstNodeStmt::dump(str);
|
||||
str << " -> ";
|
||||
if (targetp()) {
|
||||
targetp()->dump(str);
|
||||
} else {
|
||||
str << "UNLINKED";
|
||||
}
|
||||
}
|
||||
const char* AstAnd::widthMismatch() const VL_MT_STABLE {
|
||||
BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin());
|
||||
BROKEN_RTN(lhsp()->widthMin() != widthMin());
|
||||
|
|
|
@ -530,7 +530,11 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
|
|||
}
|
||||
// The parser validates we don't have "foreach (array[,,,])"
|
||||
AstNode* const bodyp = nodep->stmtsp();
|
||||
UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable");
|
||||
if (!newp) {
|
||||
nodep->v3warn(NOEFFECT, "foreach with no loop variable has no effect");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return nullptr;
|
||||
}
|
||||
if (bodyp) {
|
||||
bodyPointp->replaceWith(bodyp->unlinkFrBackWithNext());
|
||||
} else {
|
||||
|
|
|
@ -57,28 +57,18 @@ class ClassVisitor final : public VNVisitor {
|
|||
|
||||
// METHODS
|
||||
|
||||
bool recurseImplements(AstClass* nodep, bool setit) {
|
||||
// Returns true to set useVirtualPublic().
|
||||
// If there's an implements of an interface class then we have
|
||||
// multiple classes that point to same object, that need same
|
||||
// VlClass (the diamond problem). C++ will require we use 'virtual
|
||||
// public' for VlClass. So, we need the interface class, and all
|
||||
// classes above, and any below using any implements to use
|
||||
// 'virtual public' via useVirtualPublic().
|
||||
if (nodep->useVirtualPublic()) return true; // Short-circuit
|
||||
if (nodep->isInterfaceClass()) setit = true;
|
||||
void recurseImplements(AstClass* nodep) {
|
||||
// In SystemVerilog, we have two inheritance chains:
|
||||
// - extends of concrete clasess: mapped to non-virtual C++ inheritance
|
||||
// as there is only single ancestor allowed
|
||||
// - implements of concrete classes / extends of interface classes: mapped
|
||||
// to virtual inheritance to allow diamond patterns with multiple ancestors
|
||||
if (nodep->useVirtualPublic()) return; // Short-circuit to exit diamond cycles
|
||||
if (nodep->isInterfaceClass()) { nodep->useVirtualPublic(true); }
|
||||
for (const AstClassExtends* extp = nodep->extendsp(); extp;
|
||||
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
||||
if (recurseImplements(extp->classp(), setit)) setit = true;
|
||||
recurseImplements(extp->classp());
|
||||
}
|
||||
if (setit) {
|
||||
nodep->useVirtualPublic(true);
|
||||
for (const AstClassExtends* extp = nodep->extendsp(); extp;
|
||||
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
||||
(void)recurseImplements(extp->classp(), true);
|
||||
}
|
||||
}
|
||||
return setit;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
@ -89,7 +79,7 @@ class ClassVisitor final : public VNVisitor {
|
|||
nodep->name(m_prefix + nodep->name());
|
||||
nodep->unlinkFrBack();
|
||||
v3Global.rootp()->addModulesp(nodep);
|
||||
(void)recurseImplements(nodep, false);
|
||||
recurseImplements(nodep);
|
||||
// Make containing package
|
||||
// Note origName is the same as the class origName so errors look correct
|
||||
AstClassPackage* const packagep
|
||||
|
|
|
@ -276,7 +276,7 @@ public:
|
|||
const VPragmaType pragma = VPragmaType::COVERAGE_BLOCK_OFF;
|
||||
if (!nodep->unnamed()) {
|
||||
for (const string& i : m_coverageOffBlocks) {
|
||||
if (VString::wildmatch(nodep->name(), i)) {
|
||||
if (VString::wildmatch(nodep->prettyOrigOrName(), i)) {
|
||||
nodep->addStmtsp(new AstPragma{nodep->fileline(), pragma});
|
||||
}
|
||||
}
|
||||
|
@ -724,49 +724,50 @@ void V3Control::addVarAttr(FileLine* fl, const string& module, const string& fta
|
|||
|
||||
void V3Control::applyCase(AstCase* nodep) {
|
||||
const string& filename = nodep->fileline()->filename();
|
||||
V3ControlFile* filep = V3ControlResolver::s().files().resolve(filename);
|
||||
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);
|
||||
if (filep) filep->applyCase(nodep);
|
||||
}
|
||||
|
||||
void V3Control::applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep) {
|
||||
const string& filename = nodep->fileline()->filename();
|
||||
V3ControlFile* filep = V3ControlResolver::s().files().resolve(filename);
|
||||
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);
|
||||
if (filep) filep->applyBlock(nodep);
|
||||
const string& modname = modulep->name();
|
||||
V3ControlModule* modp = V3ControlResolver::s().modules().resolve(modname);
|
||||
const string& modname = modulep->prettyOrigOrName();
|
||||
V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname);
|
||||
if (modp) modp->applyBlock(nodep);
|
||||
}
|
||||
|
||||
void V3Control::applyIgnores(FileLine* filelinep) {
|
||||
const string& filename = filelinep->filename();
|
||||
V3ControlFile* filep = V3ControlResolver::s().files().resolve(filename);
|
||||
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);
|
||||
if (filep) filep->applyIgnores(filelinep);
|
||||
}
|
||||
|
||||
void V3Control::applyModule(AstNodeModule* modulep) {
|
||||
const string& modname = modulep->origName();
|
||||
V3ControlModule* modp = V3ControlResolver::s().modules().resolve(modname);
|
||||
const string& modname = modulep->prettyOrigOrName();
|
||||
V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname);
|
||||
if (modp) modp->apply(modulep);
|
||||
}
|
||||
|
||||
void V3Control::applyFTask(AstNodeModule* modulep, AstNodeFTask* ftaskp) {
|
||||
const string& modname = modulep->name();
|
||||
V3ControlModule* modp = V3ControlResolver::s().modules().resolve(modname);
|
||||
const string& modname = modulep->prettyOrigOrName();
|
||||
V3ControlModule* const modp = V3ControlResolver::s().modules().resolve(modname);
|
||||
if (!modp) return;
|
||||
const V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->name());
|
||||
const V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->prettyOrigOrName());
|
||||
if (ftp) ftp->apply(ftaskp);
|
||||
}
|
||||
|
||||
void V3Control::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp) {
|
||||
V3ControlVar* vp;
|
||||
V3ControlModule* modp = V3ControlResolver::s().modules().resolve(modulep->name());
|
||||
V3ControlModule* const modp
|
||||
= V3ControlResolver::s().modules().resolve(modulep->prettyOrigOrName());
|
||||
if (!modp) return;
|
||||
if (ftaskp) {
|
||||
V3ControlFTask* ftp = modp->ftasks().resolve(ftaskp->name());
|
||||
V3ControlFTask* const ftp = modp->ftasks().resolve(ftaskp->prettyOrigOrName());
|
||||
if (!ftp) return;
|
||||
vp = ftp->vars().resolve(varp->name());
|
||||
vp = ftp->vars().resolve(varp->prettyOrigOrName());
|
||||
} else {
|
||||
vp = modp->vars().resolve(varp->name());
|
||||
vp = modp->vars().resolve(varp->prettyOrigOrName());
|
||||
}
|
||||
if (vp) vp->apply(varp);
|
||||
}
|
||||
|
@ -797,7 +798,7 @@ bool V3Control::containsMTaskProfileData() {
|
|||
}
|
||||
|
||||
bool V3Control::waive(FileLine* filelinep, V3ErrorCode code, const string& message) {
|
||||
V3ControlFile* filep = V3ControlResolver::s().files().resolve(filelinep->filename());
|
||||
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filelinep->filename());
|
||||
if (!filep) return false;
|
||||
return filep->waive(code, message);
|
||||
}
|
||||
|
|
242
src/V3Dfg.cpp
242
src/V3Dfg.cpp
|
@ -34,6 +34,147 @@ DfgGraph::~DfgGraph() {
|
|||
forEachVertex([](DfgVertex& vtxp) { delete &vtxp; });
|
||||
}
|
||||
|
||||
std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
||||
const bool scoped = !modulep();
|
||||
|
||||
DfgGraph* const clonep = new DfgGraph{modulep(), name()};
|
||||
|
||||
// Map from original vertex to clone
|
||||
std::unordered_map<const DfgVertex*, DfgVertex*> vtxp2clonep(size() * 2);
|
||||
|
||||
// Clone constVertices
|
||||
for (const DfgConst& vtx : m_constVertices) {
|
||||
DfgConst* const cp = new DfgConst{*clonep, vtx.fileline(), vtx.num()};
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
}
|
||||
// Clone variable vertices
|
||||
for (const DfgVertexVar& vtx : m_varVertices) {
|
||||
const DfgVertexVar* const vp = vtx.as<DfgVertexVar>();
|
||||
DfgVertexVar* cp = nullptr;
|
||||
|
||||
switch (vtx.type()) {
|
||||
case VDfgType::atVarArray: {
|
||||
if (scoped) {
|
||||
cp = new DfgVarArray{*clonep, vp->varScopep()};
|
||||
} else {
|
||||
cp = new DfgVarArray{*clonep, vp->varp()};
|
||||
}
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}
|
||||
case VDfgType::atVarPacked: {
|
||||
if (scoped) {
|
||||
cp = new DfgVarPacked{*clonep, vp->varScopep()};
|
||||
} else {
|
||||
cp = new DfgVarPacked{*clonep, vp->varp()};
|
||||
}
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
vtx.v3fatalSrc("Unhandled variable vertex type: " + vtx.typeName());
|
||||
VL_UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (vp->hasDfgRefs()) cp->setHasDfgRefs();
|
||||
if (vp->hasModRefs()) cp->setHasModRefs();
|
||||
if (vp->hasExtRefs()) cp->setHasExtRefs();
|
||||
}
|
||||
// Clone operation vertices
|
||||
for (const DfgVertex& vtx : m_opVertices) {
|
||||
switch (vtx.type()) {
|
||||
#include "V3Dfg__gen_clone_cases.h" // From ./astgen
|
||||
case VDfgType::atSel: {
|
||||
DfgSel* const cp = new DfgSel{*clonep, vtx.fileline(), vtx.dtypep()};
|
||||
cp->lsb(vtx.as<DfgSel>()->lsb());
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}
|
||||
case VDfgType::atMux: {
|
||||
DfgMux* const cp = new DfgMux{*clonep, vtx.fileline(), vtx.dtypep()};
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}
|
||||
case VDfgType::atSpliceArray: {
|
||||
DfgSpliceArray* const cp = new DfgSpliceArray{*clonep, vtx.fileline(), vtx.dtypep()};
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}
|
||||
case VDfgType::atSplicePacked: {
|
||||
DfgSplicePacked* const cp = new DfgSplicePacked{*clonep, vtx.fileline(), vtx.dtypep()};
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
vtx.v3fatalSrc("Unhandled operation vertex type: " + vtx.typeName());
|
||||
VL_UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
UASSERT(size() == clonep->size(), "Size of clone should be the same");
|
||||
|
||||
// Constants have no inputs
|
||||
// Hook up inputs of cloned variables
|
||||
for (const DfgVertexVar& vtx : m_varVertices) {
|
||||
// All variable vertices are unary
|
||||
if (DfgVertex* const srcp = vtx.srcp()) {
|
||||
vtxp2clonep.at(&vtx)->as<DfgVertexVar>()->srcp(vtxp2clonep.at(srcp));
|
||||
}
|
||||
}
|
||||
// Hook up inputs of cloned operation vertices
|
||||
for (const DfgVertex& vtx : m_opVertices) {
|
||||
if (vtx.is<DfgVertexVariadic>()) {
|
||||
switch (vtx.type()) {
|
||||
case VDfgType::atSpliceArray: {
|
||||
const DfgSpliceArray* const vp = vtx.as<DfgSpliceArray>();
|
||||
DfgSpliceArray* const cp = vtxp2clonep.at(vp)->as<DfgSpliceArray>();
|
||||
vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
||||
if (DfgVertex* const srcp = edge.sourcep()) {
|
||||
cp->addDriver(vp->driverFileLine(i), //
|
||||
vp->driverIndex(i), //
|
||||
vtxp2clonep.at(srcp));
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case VDfgType::atSplicePacked: {
|
||||
const DfgSplicePacked* const vp = vtx.as<DfgSplicePacked>();
|
||||
DfgSplicePacked* const cp = vtxp2clonep.at(vp)->as<DfgSplicePacked>();
|
||||
vp->forEachSourceEdge([&](const DfgEdge& edge, size_t i) {
|
||||
if (DfgVertex* const srcp = edge.sourcep()) {
|
||||
cp->addDriver(vp->driverFileLine(i), //
|
||||
vp->driverLsb(i), //
|
||||
vtxp2clonep.at(srcp));
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
vtx.v3fatalSrc("Unhandled DfgVertexVariadic sub type: " + vtx.typeName());
|
||||
VL_UNREACHABLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DfgVertex* const cp = vtxp2clonep.at(&vtx);
|
||||
const auto oSourceEdges = vtx.sourceEdges();
|
||||
auto cSourceEdges = cp->sourceEdges();
|
||||
UASSERT_OBJ(oSourceEdges.second == cSourceEdges.second, &vtx,
|
||||
"Mismatched source count");
|
||||
for (size_t i = 0; i < oSourceEdges.second; ++i) {
|
||||
if (DfgVertex* const srcp = oSourceEdges.first[i].sourcep()) {
|
||||
cSourceEdges.first[i].relinkSource(vtxp2clonep.at(srcp));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<DfgGraph>{clonep};
|
||||
}
|
||||
|
||||
void DfgGraph::addGraph(DfgGraph& other) {
|
||||
m_size += other.m_size;
|
||||
other.m_size = 0;
|
||||
|
@ -188,6 +329,25 @@ static void dumpDotVertex(std::ostream& os, const DfgVertex& vtx) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (vtx.is<DfgVertexSplice>()) {
|
||||
os << toDotId(vtx);
|
||||
os << " [label=\"" << vtx.typeName() << "\n";
|
||||
if (const DfgSpliceArray* const sp = vtx.cast<DfgSpliceArray>()) {
|
||||
const int elements = VN_AS(sp->dtypep(), UnpackArrayDType)->elementsConst();
|
||||
os << "_[" << elements << "]";
|
||||
} else {
|
||||
os << "W" << vtx.width();
|
||||
}
|
||||
os << " / F" << vtx.fanout() << '"';
|
||||
if (vtx.hasMultipleSinks()) {
|
||||
os << ", shape=doubleoctagon";
|
||||
} else {
|
||||
os << ", shape=octagon";
|
||||
}
|
||||
os << "]\n";
|
||||
return;
|
||||
}
|
||||
|
||||
os << toDotId(vtx);
|
||||
os << " [label=\"" << vtx.typeName() << "\nW" << vtx.width() << " / F" << vtx.fanout() << '"';
|
||||
if (vtx.hasMultipleSinks()) {
|
||||
|
@ -211,7 +371,7 @@ static void dumpDotVertexAndSourceEdges(std::ostream& os, const DfgVertex& vtx)
|
|||
vtx.forEachSourceEdge([&](const DfgEdge& edge, size_t idx) { //
|
||||
if (edge.sourcep()) {
|
||||
string headLabel;
|
||||
if (vtx.arity() > 1 || vtx.is<DfgVertexVar>()) headLabel = vtx.srcName(idx);
|
||||
if (vtx.arity() > 1 || vtx.is<DfgVertexSplice>()) headLabel = vtx.srcName(idx);
|
||||
dumpDotEdge(os, edge, headLabel);
|
||||
}
|
||||
});
|
||||
|
@ -396,24 +556,34 @@ bool DfgVertex::selfEquals(const DfgVertex& that) const { return true; }
|
|||
V3Hash DfgVertex::selfHash() const { return V3Hash{}; }
|
||||
|
||||
bool DfgVertex::equals(const DfgVertex& that, EqualsCache& cache) const {
|
||||
// If same vertex, then equal
|
||||
if (this == &that) return true;
|
||||
|
||||
// If different type, then not equal
|
||||
if (this->type() != that.type()) return false;
|
||||
|
||||
// If different data type, then not equal
|
||||
if (this->dtypep() != that.dtypep()) return false;
|
||||
|
||||
// If different number of inputs, then not equal
|
||||
auto thisPair = this->sourceEdges();
|
||||
const DfgEdge* const thisSrcEdgesp = thisPair.first;
|
||||
const size_t thisArity = thisPair.second;
|
||||
auto thatPair = that.sourceEdges();
|
||||
const DfgEdge* const thatSrcEdgesp = thatPair.first;
|
||||
const size_t thatArity = thatPair.second;
|
||||
if (thisArity != thatArity) return false;
|
||||
|
||||
// Check vertex specifics
|
||||
if (!this->selfEquals(that)) return false;
|
||||
|
||||
// Check sources
|
||||
const auto key = (this < &that) ? EqualsCache::key_type{this, &that} //
|
||||
: EqualsCache::key_type{&that, this};
|
||||
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
|
||||
uint8_t& result = cache[key];
|
||||
if (!result) {
|
||||
result = 2; // Assume equals
|
||||
auto thisPair = this->sourceEdges();
|
||||
const DfgEdge* const thisSrcEdgesp = thisPair.first;
|
||||
const size_t thisArity = thisPair.second;
|
||||
auto thatPair = that.sourceEdges();
|
||||
const DfgEdge* const thatSrcEdgesp = thatPair.first;
|
||||
const size_t thatArity = thatPair.second;
|
||||
UASSERT_OBJ(thisArity == thatArity, this, "Same type vertices must have same arity!");
|
||||
for (size_t i = 0; i < thisArity; ++i) {
|
||||
const DfgVertex* const thisSrcVtxp = thisSrcEdgesp[i].m_sourcep;
|
||||
const DfgVertex* const thatSrcVtxp = thatSrcEdgesp[i].m_sourcep;
|
||||
|
@ -436,7 +606,12 @@ V3Hash DfgVertex::hash() {
|
|||
// variables, which we rely on.
|
||||
if (!is<DfgVertexVar>()) {
|
||||
hash += m_type;
|
||||
hash += width(); // Currently all non-variable vertices are packed, so this is safe
|
||||
if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep(), UnpackArrayDType)) {
|
||||
hash += adtypep->elementsConst();
|
||||
// TODO: maybe include sub-dtype, but not hugely important at the moment
|
||||
} else {
|
||||
hash += width();
|
||||
}
|
||||
const auto pair = sourceEdges();
|
||||
const DfgEdge* const edgesp = pair.first;
|
||||
const size_t arity = pair.second;
|
||||
|
@ -454,19 +629,16 @@ uint32_t DfgVertex::fanout() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
DfgVarPacked* DfgVertex::getResultVar() {
|
||||
UASSERT_OBJ(!this->is<DfgVarArray>(), this, "Arrays are not supported by " << __FUNCTION__);
|
||||
|
||||
DfgVertexVar* DfgVertex::getResultVar() {
|
||||
// It's easy if the vertex is already a variable ...
|
||||
if (DfgVarPacked* const varp = this->cast<DfgVarPacked>()) return varp;
|
||||
if (DfgVertexVar* const varp = this->cast<DfgVertexVar>()) return varp;
|
||||
|
||||
// Inspect existing variables fully written by this vertex, and choose one
|
||||
DfgVarPacked* resp = nullptr;
|
||||
// Inspect existing variables written by this vertex, and choose one
|
||||
DfgVertexVar* resp = nullptr;
|
||||
// cppcheck-has-bug-suppress constParameter
|
||||
this->forEachSink([&resp](DfgVertex& sink) {
|
||||
DfgVarPacked* const varp = sink.cast<DfgVarPacked>();
|
||||
DfgVertexVar* const varp = sink.cast<DfgVertexVar>();
|
||||
if (!varp) return;
|
||||
if (!varp->isDrivenFullyByDfg()) return;
|
||||
// Ignore SystemC variables, they cannot participate in expressions or
|
||||
// be assigned rvalue expressions.
|
||||
if (varp->varp()->isSc()) return;
|
||||
|
@ -569,6 +741,42 @@ bool DfgSel::selfEquals(const DfgVertex& that) const { return lsb() == that.as<D
|
|||
|
||||
V3Hash DfgSel::selfHash() const { return V3Hash{lsb()}; }
|
||||
|
||||
// DfgSpliceArray ----------
|
||||
|
||||
bool DfgSpliceArray::selfEquals(const DfgVertex& that) const {
|
||||
const DfgSpliceArray* const thatp = that.as<DfgSpliceArray>();
|
||||
const size_t arity = this->arity();
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (driverIndex(i) != thatp->driverIndex(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
V3Hash DfgSpliceArray::selfHash() const {
|
||||
V3Hash hash;
|
||||
const size_t arity = this->arity();
|
||||
for (size_t i = 0; i < arity; ++i) hash += driverIndex(i);
|
||||
return hash;
|
||||
}
|
||||
|
||||
// DfgSplicePacked ----------
|
||||
|
||||
bool DfgSplicePacked::selfEquals(const DfgVertex& that) const {
|
||||
const DfgSplicePacked* const thatp = that.as<DfgSplicePacked>();
|
||||
const size_t arity = this->arity();
|
||||
for (size_t i = 0; i < arity; ++i) {
|
||||
if (driverLsb(i) != thatp->driverLsb(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
V3Hash DfgSplicePacked::selfHash() const {
|
||||
V3Hash hash;
|
||||
const size_t arity = this->arity();
|
||||
for (size_t i = 0; i < arity; ++i) hash += driverLsb(i);
|
||||
return hash;
|
||||
}
|
||||
|
||||
// DfgVertexVar ----------
|
||||
|
||||
bool DfgVertexVar::selfEquals(const DfgVertex& that) const {
|
||||
|
|
19
src/V3Dfg.h
19
src/V3Dfg.h
|
@ -238,8 +238,7 @@ public:
|
|||
|
||||
// Width of result
|
||||
uint32_t width() const {
|
||||
// This is a hot enough function that this is an expensive check, so in debug build only.
|
||||
UDEBUGONLY(UASSERT_OBJ(VN_IS(dtypep(), BasicDType), this, "non-packed has no 'width()'"););
|
||||
UASSERT_OBJ(VN_IS(dtypep(), BasicDType), this, "non-packed has no 'width()'");
|
||||
return dtypep()->width();
|
||||
}
|
||||
|
||||
|
@ -283,7 +282,7 @@ public:
|
|||
|
||||
// Return a canonical variable vertex that holds the value of this vertex,
|
||||
// or nullptr if no such variable exists in the graph. This is O(fanout).
|
||||
DfgVarPacked* getResultVar() VL_MT_DISABLED;
|
||||
DfgVertexVar* getResultVar() VL_MT_DISABLED;
|
||||
|
||||
// Cache type for 'scopep' below
|
||||
using ScopeCache = std::unordered_map<const DfgVertex*, AstScope*>;
|
||||
|
@ -572,7 +571,7 @@ class DfgVertexVariadic VL_NOT_FINAL : public DfgVertex {
|
|||
|
||||
protected:
|
||||
DfgVertexVariadic(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep,
|
||||
uint32_t initialCapacity = 1)
|
||||
uint32_t initialCapacity)
|
||||
: DfgVertex{dfg, type, flp, dtypep}
|
||||
, m_srcsp{allocSources(initialCapacity)}
|
||||
, m_srcCap{initialCapacity} {}
|
||||
|
@ -706,6 +705,9 @@ public:
|
|||
// 'const' variant of 'forEachVertex'. No mutation allowed.
|
||||
inline void forEachVertex(std::function<void(const DfgVertex&)> f) const;
|
||||
|
||||
// Return an identical, independent copy of this graph. Vertex and edge order might differ.
|
||||
std::unique_ptr<DfgGraph> clone() const VL_MT_DISABLED;
|
||||
|
||||
// Add contents of other graph to this graph. Leaves other graph empty.
|
||||
void addGraph(DfgGraph& other) VL_MT_DISABLED;
|
||||
|
||||
|
@ -911,15 +913,14 @@ bool DfgVertex::isOnes() const {
|
|||
// Inline method definitions - for DfgVertexVar
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity)
|
||||
: DfgVertexVariadic{dfg, type, varp->fileline(), dtypeFor(varp), initialCapacity}
|
||||
DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp)
|
||||
: DfgVertexUnary{dfg, type, varp->fileline(), dtypeFor(varp)}
|
||||
, m_varp{varp}
|
||||
, m_varScopep{nullptr} {
|
||||
UASSERT_OBJ(dfg.modulep(), varp, "Un-scoped DfgVertexVar created in scoped DfgGraph");
|
||||
}
|
||||
DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp,
|
||||
uint32_t initialCapacity)
|
||||
: DfgVertexVariadic{dfg, type, vscp->fileline(), dtypeFor(vscp), initialCapacity}
|
||||
DfgVertexVar::DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp)
|
||||
: DfgVertexUnary{dfg, type, vscp->fileline(), dtypeFor(vscp)}
|
||||
, m_varp{vscp->varp()}
|
||||
, m_varScopep{vscp} {
|
||||
UASSERT_OBJ(!dfg.modulep(), vscp, "Scoped DfgVertexVar created in un-scoped DfgGraph");
|
||||
|
|
|
@ -183,49 +183,37 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
return m_foundUnhandled;
|
||||
}
|
||||
|
||||
// Build DfgEdge representing the LValue assignment. Returns false if unsuccessful.
|
||||
bool convertAssignment(FileLine* flp, AstNode* nodep, DfgVertex* vtxp) {
|
||||
DfgVertexSplice* convertLValue(AstNode* nodep) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
|
||||
if (AstVarRef* const vrefp = VN_CAST(nodep, VarRef)) {
|
||||
m_foundUnhandled = false;
|
||||
visit(vrefp);
|
||||
// cppcheck-has-bug-suppress knownConditionTrueFalse
|
||||
if (m_foundUnhandled) return false;
|
||||
getVertex(vrefp)->template as<DfgVarPacked>()->addDriver(flp, 0, vtxp);
|
||||
return true;
|
||||
}
|
||||
if (AstSel* const selp = VN_CAST(nodep, Sel)) {
|
||||
AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef);
|
||||
const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const);
|
||||
if (!vrefp || !lsbp) {
|
||||
++m_ctx.m_nonRepLhs;
|
||||
return false;
|
||||
if (m_foundUnhandled) return nullptr;
|
||||
DfgVertexVar* const vtxp = getVertex(vrefp)->template as<DfgVertexVar>();
|
||||
// Ensure driving splice vertex exists
|
||||
if (!vtxp->srcp()) {
|
||||
if (VN_IS(vtxp->dtypep(), UnpackArrayDType)) {
|
||||
vtxp->srcp(new DfgSpliceArray{*m_dfgp, flp, vtxp->dtypep()});
|
||||
} else {
|
||||
vtxp->srcp(new DfgSplicePacked{*m_dfgp, flp, vtxp->dtypep()});
|
||||
}
|
||||
}
|
||||
m_foundUnhandled = false;
|
||||
visit(vrefp);
|
||||
// cppcheck-has-bug-suppress knownConditionTrueFalse
|
||||
if (m_foundUnhandled) return false;
|
||||
getVertex(vrefp)->template as<DfgVarPacked>()->addDriver(flp, lsbp->toUInt(), vtxp);
|
||||
return true;
|
||||
}
|
||||
if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) {
|
||||
AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef);
|
||||
const AstConst* const idxp = VN_CAST(selp->bitp(), Const);
|
||||
if (!vrefp || !idxp) {
|
||||
++m_ctx.m_nonRepLhs;
|
||||
return false;
|
||||
}
|
||||
m_foundUnhandled = false;
|
||||
visit(vrefp);
|
||||
// cppcheck-has-bug-suppress knownConditionTrueFalse
|
||||
if (m_foundUnhandled) return false;
|
||||
getVertex(vrefp)->template as<DfgVarArray>()->addDriver(flp, idxp->toUInt(), vtxp);
|
||||
return true;
|
||||
return vtxp->srcp()->as<DfgVertexSplice>();
|
||||
}
|
||||
|
||||
++m_ctx.m_nonRepLhs;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Build DfgEdge representing the LValue assignment. Returns false if unsuccessful.
|
||||
bool convertAssignment(FileLine* flp, AstNode* nodep, DfgVertex* vtxp) {
|
||||
// Concatenation on the LHS. Select parts of the driving 'vtxp' then convert each part
|
||||
if (AstConcat* const concatp = VN_CAST(nodep, Concat)) {
|
||||
AstNode* const lhsp = concatp->lhsp();
|
||||
AstNode* const rhsp = concatp->rhsp();
|
||||
|
||||
{
|
||||
{ // Convet LHS of concat
|
||||
FileLine* const lFlp = lhsp->fileline();
|
||||
DfgSel* const lVtxp = new DfgSel{*m_dfgp, lFlp, DfgVertex::dtypeFor(lhsp)};
|
||||
lVtxp->fromp(vtxp);
|
||||
|
@ -233,7 +221,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
if (!convertAssignment(flp, lhsp, lVtxp)) return false;
|
||||
}
|
||||
|
||||
{
|
||||
{ // Convert RHS of concat
|
||||
FileLine* const rFlp = rhsp->fileline();
|
||||
DfgSel* const rVtxp = new DfgSel{*m_dfgp, rFlp, DfgVertex::dtypeFor(rhsp)};
|
||||
rVtxp->fromp(vtxp);
|
||||
|
@ -241,7 +229,37 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
return convertAssignment(flp, rhsp, rVtxp);
|
||||
}
|
||||
}
|
||||
++m_ctx.m_nonRepLhs;
|
||||
|
||||
if (AstSel* const selp = VN_CAST(nodep, Sel)) {
|
||||
AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef);
|
||||
const AstConst* const lsbp = VN_CAST(selp->lsbp(), Const);
|
||||
if (!vrefp || !lsbp) {
|
||||
++m_ctx.m_nonRepLhs;
|
||||
return false;
|
||||
}
|
||||
if (DfgVertexSplice* const splicep = convertLValue(vrefp)) {
|
||||
splicep->template as<DfgSplicePacked>()->addDriver(flp, lsbp->toUInt(), vtxp);
|
||||
return true;
|
||||
}
|
||||
} else if (AstArraySel* const selp = VN_CAST(nodep, ArraySel)) {
|
||||
AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef);
|
||||
const AstConst* const idxp = VN_CAST(selp->bitp(), Const);
|
||||
if (!vrefp || !idxp) {
|
||||
++m_ctx.m_nonRepLhs;
|
||||
return false;
|
||||
}
|
||||
if (DfgVertexSplice* const splicep = convertLValue(vrefp)) {
|
||||
splicep->template as<DfgSpliceArray>()->addDriver(flp, idxp->toUInt(), vtxp);
|
||||
return true;
|
||||
}
|
||||
} else if (VN_IS(nodep, VarRef)) {
|
||||
if (DfgVertexSplice* const splicep = convertLValue(nodep)) {
|
||||
splicep->template as<DfgSplicePacked>()->addDriver(flp, 0, vtxp);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
++m_ctx.m_nonRepLhs;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -315,18 +333,23 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
for (DfgVarPacked* const varp : m_varPackedps) {
|
||||
// Delete variables with no sinks nor sources (this can happen due to reverting
|
||||
// uncommitted vertices, which does not remove variables)
|
||||
if (!varp->hasSinks() && varp->arity() == 0) {
|
||||
if (!varp->hasSinks() && !varp->srcp()) {
|
||||
VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Nothing to do for un-driven (input) variables
|
||||
if (!varp->srcp()) continue;
|
||||
|
||||
DfgSplicePacked* const splicep = varp->srcp()->as<DfgSplicePacked>();
|
||||
|
||||
// Gather (and unlink) all drivers
|
||||
std::vector<Driver> drivers;
|
||||
drivers.reserve(varp->arity());
|
||||
varp->forEachSourceEdge([this, varp, &drivers](DfgEdge& edge, size_t idx) {
|
||||
drivers.reserve(splicep->arity());
|
||||
splicep->forEachSourceEdge([this, splicep, &drivers](DfgEdge& edge, size_t idx) {
|
||||
DfgVertex* const driverp = edge.sourcep();
|
||||
UASSERT(driverp, "Should not have created undriven sources");
|
||||
addDriver(varp->driverFileLine(idx), varp->driverLsb(idx), driverp, drivers);
|
||||
addDriver(splicep->driverFileLine(idx), splicep->driverLsb(idx), driverp, drivers);
|
||||
edge.unlinkSource();
|
||||
});
|
||||
|
||||
|
@ -424,10 +447,10 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
// Reinsert drivers in order
|
||||
varp->resetSources();
|
||||
splicep->resetSources();
|
||||
for (const Driver& driver : drivers) {
|
||||
if (!driver.m_vtxp) break; // Stop at end of compacted list
|
||||
varp->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp);
|
||||
splicep->addDriver(driver.m_fileline, driver.m_lsb, driver.m_vtxp);
|
||||
}
|
||||
|
||||
// Prune vertices potentially unused due to resolving multiple drivers.
|
||||
|
@ -442,6 +465,15 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
vtxp->forEachSource([&](DfgVertex& src) { prune.emplace(&src); });
|
||||
vtxp->unlinkDelete(*m_dfgp);
|
||||
}
|
||||
|
||||
// If the whole variable is driven, remove the splice node
|
||||
if (splicep->arity() == 1 //
|
||||
&& splicep->driverLsb(0) == 0 //
|
||||
&& splicep->source(0)->width() == varp->width()) {
|
||||
varp->srcp(splicep->source(0));
|
||||
varp->driverFileLine(splicep->driverFileLine(0));
|
||||
splicep->unlinkDelete(*m_dfgp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -450,7 +482,7 @@ class AstToDfgVisitor final : public VNVisitor {
|
|||
for (DfgVarArray* const varp : m_varArrayps) {
|
||||
// Delete variables with no sinks nor sources (this can happen due to reverting
|
||||
// uncommitted vertices, which does not remove variables)
|
||||
if (!varp->hasSinks() && varp->arity() == 0) {
|
||||
if (!varp->hasSinks() && !varp->srcp()) {
|
||||
VL_DO_DANGLING(varp->unlinkDelete(*m_dfgp), varp);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -341,32 +341,20 @@ class ExtractCyclicComponents final {
|
|||
return *clonep;
|
||||
}
|
||||
|
||||
// Fix up non-variable sources of a DfgVertexVar that are in a different component,
|
||||
// using the provided 'relink' callback
|
||||
template <typename T_Vertex>
|
||||
void fixSources(T_Vertex& vtx, std::function<void(T_Vertex&, DfgVertex&, size_t)> relink) {
|
||||
static_assert(std::is_base_of<DfgVertexVar, T_Vertex>::value,
|
||||
"'Vertex' must be a 'DfgVertexVar'");
|
||||
// Fix edges that cross components
|
||||
void fixEdges(DfgVertexVar& vtx) {
|
||||
const size_t component = state(vtx).component;
|
||||
vtx.forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
|
||||
DfgVertex& source = *edge.sourcep();
|
||||
// DfgVertexVar sources are fixed up by `fixSinks` on those sources
|
||||
if (source.is<DfgVertexVar>()) return;
|
||||
const size_t sourceComponent = state(source).component;
|
||||
// Same component is OK
|
||||
if (sourceComponent == component) return;
|
||||
// Unlink the source edge (source is reconnected by 'relink'
|
||||
edge.unlinkSource();
|
||||
// Apply the fixup
|
||||
// cppcheck-has-bug-suppress constVariable
|
||||
DfgVertexVar& clone = getClone(vtx, sourceComponent);
|
||||
relink(*(clone.as<T_Vertex>()), source, idx);
|
||||
});
|
||||
}
|
||||
|
||||
// Fix up sinks of given variable vertex that are in a different component
|
||||
void fixSinks(DfgVertexVar& vtx) {
|
||||
const size_t component = state(vtx).component;
|
||||
// All variable vertices have at most a single source, and only variable
|
||||
// vertices can have multiple sinks, therefore the source must be either:
|
||||
// - in the same component as the variable vertex
|
||||
// - be a variable vertex itself, which might be in a different component
|
||||
// The later case will be fixed up when handling the source variable
|
||||
DfgVertex* const srcp = vtx.srcp();
|
||||
UASSERT_OBJ(!srcp || srcp->is<DfgVertexVar>() || state(*srcp).component == component, &vtx,
|
||||
"Driver of DfgVertexVar must be in the same component");
|
||||
|
||||
// Fix up sinks in a differetn component
|
||||
vtx.forEachSinkEdge([&](DfgEdge& edge) {
|
||||
const size_t sinkComponent = state(*edge.sinkp()).component;
|
||||
// Same component is OK
|
||||
|
@ -376,49 +364,6 @@ class ExtractCyclicComponents final {
|
|||
});
|
||||
}
|
||||
|
||||
// Fix edges that cross components
|
||||
void fixEdges(DfgVertexVar& vtx) {
|
||||
if (DfgVarPacked* const vvtxp = vtx.cast<DfgVarPacked>()) {
|
||||
fixSources<DfgVarPacked>(
|
||||
*vvtxp, [&](DfgVarPacked& clone, DfgVertex& driver, size_t driverIdx) {
|
||||
clone.addDriver(vvtxp->driverFileLine(driverIdx), //
|
||||
vvtxp->driverLsb(driverIdx), &driver);
|
||||
});
|
||||
fixSinks(*vvtxp);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DfgVarArray* const vvtxp = vtx.cast<DfgVarArray>()) {
|
||||
fixSources<DfgVarArray>( //
|
||||
*vvtxp, [&](DfgVarArray& clone, DfgVertex& driver, size_t driverIdx) {
|
||||
clone.addDriver(vvtxp->driverFileLine(driverIdx), //
|
||||
vvtxp->driverIndex(driverIdx), &driver);
|
||||
});
|
||||
fixSinks(*vvtxp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void packSources(DfgGraph& dfg) {
|
||||
// Remove undriven variable sources
|
||||
for (DfgVertexVar* const vtxp : dfg.varVertices().unlinkable()) {
|
||||
if (DfgVarPacked* const varp = vtxp->cast<DfgVarPacked>()) {
|
||||
varp->packSources();
|
||||
if (!varp->hasSinks() && varp->arity() == 0) {
|
||||
VL_DO_DANGLING(varp->unlinkDelete(dfg), varp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (DfgVarArray* const varp = vtxp->cast<DfgVarArray>()) {
|
||||
varp->packSources();
|
||||
if (!varp->hasSinks() && varp->arity() == 0) {
|
||||
VL_DO_DANGLING(varp->unlinkDelete(dfg), varp);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Vertex>
|
||||
void moveVertices(DfgVertex::List<Vertex>& list) {
|
||||
for (DfgVertex* const vtxp : list.unlinkable()) {
|
||||
|
@ -446,11 +391,6 @@ class ExtractCyclicComponents final {
|
|||
UASSERT_OBJ(component == state(snk).component, &vtx,
|
||||
"Edge crossing components without variable involvement");
|
||||
});
|
||||
if (const DfgVertexVar* const vtxp = vtx.cast<DfgVertexVar>()) {
|
||||
vtxp->forEachSourceEdge([](const DfgEdge& edge, size_t) {
|
||||
UASSERT_OBJ(edge.sourcep(), edge.sinkp(), "Missing source on variable vertex");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -489,11 +429,6 @@ class ExtractCyclicComponents final {
|
|||
if (&vtx == lastp) break;
|
||||
}
|
||||
|
||||
// Pack sources of variables to remove the now undriven inputs
|
||||
// (cloning might have unlinked some of the inputs),
|
||||
packSources(m_dfg);
|
||||
for (const auto& dfgp : m_components) packSources(*dfgp);
|
||||
|
||||
// Check results for consistency
|
||||
if (VL_UNLIKELY(m_doExpensiveChecks)) {
|
||||
checkEdges(m_dfg);
|
||||
|
|
|
@ -201,45 +201,52 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
++m_ctx.m_resultEquations;
|
||||
}
|
||||
|
||||
void convertVarDriver(const DfgVarPacked* dfgVarp) {
|
||||
if (dfgVarp->isDrivenFullyByDfg()) {
|
||||
// Whole variable is driven. Render driver and assign directly to whole variable.
|
||||
FileLine* const flp = dfgVarp->driverFileLine(0);
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->source(0));
|
||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
||||
} else {
|
||||
void convertPackedDriver(const DfgVarPacked* dfgVarp) {
|
||||
if (DfgSplicePacked* const splicep = dfgVarp->srcp()->cast<DfgSplicePacked>()) {
|
||||
// Variable is driven partially. Render each driver as a separate assignment.
|
||||
dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
||||
splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
||||
UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources");
|
||||
// Render the rhs expression
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep());
|
||||
// Create select LValue
|
||||
FileLine* const flp = dfgVarp->driverFileLine(idx);
|
||||
FileLine* const flp = splicep->driverFileLine(idx);
|
||||
AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
||||
AstConst* const lsbp = new AstConst{flp, dfgVarp->driverLsb(idx)};
|
||||
AstConst* const lsbp = new AstConst{flp, splicep->driverLsb(idx)};
|
||||
const int width = static_cast<int>(edge.sourcep()->width());
|
||||
AstSel* const lhsp = new AstSel{flp, refp, lsbp, width};
|
||||
// Add assignment of the value to the selected bits
|
||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Whole variable is driven. Render driver and assign directly to whole variable.
|
||||
FileLine* const flp
|
||||
= dfgVarp->driverFileLine() ? dfgVarp->driverFileLine() : dfgVarp->fileline();
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(dfgVarp->srcp());
|
||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
||||
}
|
||||
|
||||
void convertArrayDiver(const DfgVarArray* dfgVarp) {
|
||||
// Variable is driven partially. Assign from parts of the canonical var.
|
||||
dfgVarp->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
||||
UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources");
|
||||
// Render the rhs expression
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep());
|
||||
// Create select LValue
|
||||
FileLine* const flp = dfgVarp->driverFileLine(idx);
|
||||
AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
||||
AstConst* const idxp = new AstConst{flp, dfgVarp->driverIndex(idx)};
|
||||
AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp};
|
||||
// Add assignment of the value to the selected bits
|
||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
||||
});
|
||||
if (DfgSpliceArray* const splicep = dfgVarp->srcp()->cast<DfgSpliceArray>()) {
|
||||
// Variable is driven partially. Assign from parts of the canonical var.
|
||||
splicep->forEachSourceEdge([&](const DfgEdge& edge, size_t idx) {
|
||||
UASSERT_OBJ(edge.sourcep(), dfgVarp, "Should have removed undriven sources");
|
||||
// Render the rhs expression
|
||||
AstNodeExpr* const rhsp = convertDfgVertexToAstNodeExpr(edge.sourcep());
|
||||
// Create select LValue
|
||||
FileLine* const flp = splicep->driverFileLine(idx);
|
||||
AstVarRef* const refp = new AstVarRef{flp, getNode(dfgVarp), VAccess::WRITE};
|
||||
AstConst* const idxp = new AstConst{flp, splicep->driverIndex(idx)};
|
||||
AstArraySel* const lhsp = new AstArraySel{flp, refp, idxp};
|
||||
// Add assignment of the value to the selected bits
|
||||
addResultEquation(dfgVarp, flp, lhsp, rhsp);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
UASSERT_OBJ(false, dfgVarp, "Should not have wholly driven arrays in Dfg");
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
|
@ -285,11 +292,11 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
// The graph must have been regularized, so we only need to render assignments
|
||||
for (DfgVertexVar& vtx : dfg.varVertices()) {
|
||||
// If there is no driver (this vertex is an input to the graph), then nothing to do.
|
||||
if (!vtx.isDrivenByDfg()) continue;
|
||||
if (!vtx.srcp()) continue;
|
||||
|
||||
// Render packed variable assignments
|
||||
if (const DfgVarPacked* const dfgVarp = vtx.cast<DfgVarPacked>()) {
|
||||
convertVarDriver(dfgVarp);
|
||||
convertPackedDriver(dfgVarp);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -240,16 +240,34 @@ static void process(DfgGraph& dfg, V3DfgOptimizationContext& ctx) {
|
|||
// Extract the cyclic sub-graphs. We do this because a lot of the optimizations assume a
|
||||
// DAG, and large, mostly acyclic graphs could not be optimized due to the presence of
|
||||
// small cycles.
|
||||
const std::vector<std::unique_ptr<DfgGraph>>& cyclicComponents
|
||||
std::vector<std::unique_ptr<DfgGraph>> cyclicComponents
|
||||
= dfg.extractCyclicComponents("cyclic");
|
||||
|
||||
// Split the remaining acyclic DFG into [weakly] connected components
|
||||
const std::vector<std::unique_ptr<DfgGraph>>& acyclicComponents
|
||||
= dfg.splitIntoComponents("acyclic");
|
||||
std::vector<std::unique_ptr<DfgGraph>> acyclicComponents = dfg.splitIntoComponents("acyclic");
|
||||
|
||||
// Quick sanity check
|
||||
UASSERT_OBJ(dfg.size() == 0, dfg.modulep(), "DfgGraph should have become empty");
|
||||
|
||||
// Attempt to convert cyclic components into acyclic ones
|
||||
if (v3Global.opt.fDfgBreakCyckes()) {
|
||||
for (auto it = cyclicComponents.begin(); it != cyclicComponents.end();) {
|
||||
auto result = V3DfgPasses::breakCycles(**it, ctx);
|
||||
if (!result.first) {
|
||||
// No improvement, moving on.
|
||||
++it;
|
||||
} else if (!result.second) {
|
||||
// Improved, but still cyclic. Replace the original cyclic component.
|
||||
*it = std::move(result.first);
|
||||
++it;
|
||||
} else {
|
||||
// Result became acyclic. Move to acyclicComponents, delete original.
|
||||
acyclicComponents.emplace_back(std::move(result.first));
|
||||
it = cyclicComponents.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For each acyclic component
|
||||
for (auto& component : acyclicComponents) {
|
||||
if (dumpDfgLevel() >= 7) component->dumpDotFilePrefixed(ctx.prefix() + "source");
|
||||
|
|
|
@ -30,6 +30,16 @@ V3DfgBinToOneHotContext::~V3DfgBinToOneHotContext() {
|
|||
m_decodersCreated);
|
||||
}
|
||||
|
||||
V3DfgBreakCyclesContext::~V3DfgBreakCyclesContext() {
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, made acyclic", m_nFixed);
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, improved", m_nImproved);
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, left unchanged",
|
||||
m_nUnchanged);
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, trivial", m_nTrivial);
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " BreakCycles, changes applied",
|
||||
m_nImprovements);
|
||||
}
|
||||
|
||||
V3DfgCseContext::~V3DfgCseContext() {
|
||||
V3Stats::addStat("Optimizations, DFG " + m_label + " CSE, expressions eliminated",
|
||||
m_eliminated);
|
||||
|
@ -162,7 +172,7 @@ void V3DfgPasses::inlineVars(DfgGraph& dfg) {
|
|||
// Don't inline SystemC variables, as SystemC types are not interchangeable with
|
||||
// internal types, and hence the variables are not interchangeable either.
|
||||
if (varp->hasSinks() && varp->isDrivenFullyByDfg() && !varp->varp()->isSc()) {
|
||||
DfgVertex* const driverp = varp->source(0);
|
||||
DfgVertex* const driverp = varp->srcp();
|
||||
|
||||
// We must keep the original driver in certain cases, when swapping them would
|
||||
// not be functionally or technically (implementation reasons) equivalent:
|
||||
|
@ -371,12 +381,14 @@ void V3DfgPasses::binToOneHot(DfgGraph& dfg, V3DfgBinToOneHotContext& ctx) {
|
|||
// The index variable
|
||||
DfgVarPacked* const idxVtxp = [&]() {
|
||||
// If there is an existing result variable, use that, otherwise create a new variable
|
||||
DfgVarPacked* varp = srcp->getResultVar();
|
||||
if (!varp) {
|
||||
DfgVarPacked* varp = nullptr;
|
||||
if (DfgVertexVar* const vp = srcp->getResultVar()) {
|
||||
varp = vp->as<DfgVarPacked>();
|
||||
} else {
|
||||
const std::string name = dfg.makeUniqueName("BinToOneHot_Idx", nTables);
|
||||
varp = dfg.makeNewVar(flp, name, idxDTypep, nullptr)->as<DfgVarPacked>();
|
||||
varp->varp()->isInternal(true);
|
||||
varp->addDriver(flp, 0, srcp);
|
||||
varp->srcp(srcp);
|
||||
}
|
||||
varp->setHasModRefs();
|
||||
return varp;
|
||||
|
@ -544,9 +556,9 @@ void V3DfgPasses::eliminateVars(DfgGraph& dfg, V3DfgEliminateVarsContext& ctx) {
|
|||
if (!varp->hasModRefs()) {
|
||||
// If it is only referenced in this DFG, it can be removed
|
||||
++ctx.m_varsRemoved;
|
||||
varp->replaceWith(varp->source(0));
|
||||
varp->replaceWith(varp->srcp());
|
||||
varp->nodep()->unlinkFrBack()->deleteTree();
|
||||
} else if (DfgVarPacked* const driverp = varp->source(0)->cast<DfgVarPacked>()) {
|
||||
} else if (DfgVarPacked* const driverp = varp->srcp()->cast<DfgVarPacked>()) {
|
||||
// If it's driven from another variable, it can be replaced by that. However, we do not
|
||||
// want to propagate SystemC variables into the design.
|
||||
if (driverp->varp()->isSc()) continue;
|
||||
|
|
|
@ -40,6 +40,20 @@ public:
|
|||
~V3DfgBinToOneHotContext() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class V3DfgBreakCyclesContext final {
|
||||
const std::string m_label; // Label to apply to stats
|
||||
|
||||
public:
|
||||
VDouble0 m_nFixed; // Number of graphs that became acyclic
|
||||
VDouble0 m_nImproved; // Number of graphs that were imporoved but still cyclic
|
||||
VDouble0 m_nUnchanged; // Number of graphs that were left unchanged
|
||||
VDouble0 m_nTrivial; // Number of graphs that were not changed
|
||||
VDouble0 m_nImprovements; // Number of changes made to graphs
|
||||
explicit V3DfgBreakCyclesContext(const std::string& label)
|
||||
: m_label{label} {}
|
||||
~V3DfgBreakCyclesContext() VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
class V3DfgCseContext final {
|
||||
const std::string m_label; // Label to apply to stats
|
||||
|
||||
|
@ -93,6 +107,7 @@ public:
|
|||
VDouble0 m_resultEquations; // Number of result combinational equations
|
||||
|
||||
V3DfgBinToOneHotContext m_binToOneHotContext{m_label};
|
||||
V3DfgBreakCyclesContext m_breakCyclesContext{m_label};
|
||||
V3DfgCseContext m_cseContext0{m_label + " 1st"};
|
||||
V3DfgCseContext m_cseContext1{m_label + " 2nd"};
|
||||
V3DfgPeepholeContext m_peepholeContext{m_label};
|
||||
|
@ -120,6 +135,16 @@ DfgGraph* astToDfg(AstModule&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
|||
// Same as above, but for the entire netlist, after V3Scope
|
||||
DfgGraph* astToDfg(AstNetlist&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
|
||||
// Attempt to make the given cyclic graph into an acyclic, or "less cyclic"
|
||||
// equivalent. If the returned pointer is null, then no improvement was
|
||||
// possible on the input graph. Otherwise the returned graph is an improvement
|
||||
// on the input graph, with at least some cycles eliminated. The returned
|
||||
// graph is always independent of the original. If an imporoved graph is
|
||||
// returned, then the returned 'bool' flag indicated if the returned graph is
|
||||
// acyclic (flag 'true'), or still cyclic (flag 'false').
|
||||
std::pair<std::unique_ptr<DfgGraph>, bool> breakCycles(const DfgGraph&,
|
||||
V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
|
||||
// Optimize the given DfgGraph
|
||||
void optimize(DfgGraph&, V3DfgOptimizationContext&) VL_MT_DISABLED;
|
||||
|
||||
|
|
|
@ -1202,11 +1202,15 @@ class V3DfgPeephole final : public DfgVisitor {
|
|||
void visit(DfgArraySel* vtxp) override {
|
||||
if (DfgConst* const idxp = vtxp->bitp()->cast<DfgConst>()) {
|
||||
if (DfgVarArray* const varp = vtxp->fromp()->cast<DfgVarArray>()) {
|
||||
const size_t idx = idxp->toSizeT();
|
||||
if (DfgVertex* const driverp = varp->driverAt(idx)) {
|
||||
APPLYING(INLINE_ARRAYSEL) {
|
||||
replace(vtxp, driverp);
|
||||
return;
|
||||
if (varp->srcp()) {
|
||||
if (DfgSpliceArray* const splicep = varp->srcp()->cast<DfgSpliceArray>()) {
|
||||
const size_t idx = idxp->toSizeT();
|
||||
if (DfgVertex* const driverp = splicep->driverAt(idx)) {
|
||||
APPLYING(INLINE_ARRAYSEL) {
|
||||
replace(vtxp, driverp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,12 +45,23 @@ class DfgRegularize final {
|
|||
// Ensure intermediate values used multiple times are written to variables
|
||||
for (DfgVertex& vtx : m_dfg.opVertices()) {
|
||||
const bool needsIntermediateVariable = [&]() {
|
||||
// Splice vertices represent partial assignments, so they need a variable
|
||||
// iff and only if they have a non-variable sink.
|
||||
if (vtx.is<DfgVertexSplice>()) {
|
||||
const bool hasNonVarSink
|
||||
= vtx.findSink<DfgVertex>([](const DfgVertex& snk) { //
|
||||
return !snk.is<DfgVertexVar>();
|
||||
});
|
||||
return hasNonVarSink;
|
||||
}
|
||||
// Anything that drives an SC variable needs an intermediate,
|
||||
// as we can only assign simple variables to SC variables at runtime.
|
||||
const bool hasScSink = vtx.findSink<DfgVertexVar>([](const DfgVertexVar& var) { //
|
||||
return var.varp()->isSc();
|
||||
});
|
||||
if (hasScSink) return true;
|
||||
// // Splice vertices always need a variable as they represent partial updates
|
||||
// if (vtx.is<DfgVertexSplice>()) return true;
|
||||
// Operations without multiple sinks need no variables
|
||||
if (!vtx.hasMultipleSinks()) return false;
|
||||
// Array selects need no variables, they are just memory references
|
||||
|
@ -63,26 +74,21 @@ class DfgRegularize final {
|
|||
|
||||
// This is an op that requires a result variable. Ensure it is
|
||||
// assigned to one, and redirect other sinks read that variable.
|
||||
if (DfgVarPacked* const varp = vtx.getResultVar()) {
|
||||
// A variable already exists
|
||||
UASSERT_OBJ(varp->arity() == 1, varp, "Result variable with multiple drivers");
|
||||
FileLine* const flp = varp->driverFileLine(0);
|
||||
varp->sourceEdge(0)->unlinkSource();
|
||||
varp->resetSources();
|
||||
if (DfgVertexVar* const varp = vtx.getResultVar()) {
|
||||
varp->sourceEdge<0>()->unlinkSource();
|
||||
vtx.replaceWith(varp);
|
||||
varp->addDriver(flp, 0, &vtx);
|
||||
varp->srcp(&vtx);
|
||||
} else {
|
||||
// Need to create an intermediate variable
|
||||
const std::string name = m_dfg.makeUniqueName("Regularize", m_nTmps);
|
||||
FileLine* const flp = vtx.fileline();
|
||||
AstScope* const scopep = scoped ? vtx.scopep(scopeCache) : nullptr;
|
||||
DfgVarPacked* const newp
|
||||
= m_dfg.makeNewVar(flp, name, vtx.dtypep(), scopep)->as<DfgVarPacked>();
|
||||
DfgVertexVar* const newp = m_dfg.makeNewVar(flp, name, vtx.dtypep(), scopep);
|
||||
++m_nTmps;
|
||||
++m_ctx.m_temporariesIntroduced;
|
||||
// Replace vertex with the variable and add back driver
|
||||
vtx.replaceWith(newp);
|
||||
newp->addDriver(vtx.fileline(), 0, &vtx);
|
||||
newp->srcp(&vtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,22 +38,24 @@
|
|||
|
||||
// === Abstract base node types (DfgVertex*) ===================================
|
||||
|
||||
class DfgVertexVar VL_NOT_FINAL : public DfgVertexVariadic {
|
||||
class DfgVertexVar VL_NOT_FINAL : public DfgVertexUnary {
|
||||
AstVar* const m_varp; // The AstVar associated with this vertex (not owned by this vertex)
|
||||
AstVarScope* const m_varScopep; // The AstVarScope associated with this vertex (not owned)
|
||||
bool m_hasDfgRefs = false; // This AstVar is referenced in a different DFG of the module
|
||||
bool m_hasModRefs = false; // This AstVar is referenced outside the DFG, but in the module
|
||||
bool m_hasExtRefs = false; // This AstVar is referenced from outside the module
|
||||
// Location of driver of this variable. Only used for converting back to Ast. Might be nullptr.
|
||||
FileLine* m_driverFileLine = nullptr;
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const final VL_MT_DISABLED;
|
||||
V3Hash selfHash() const final VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp, uint32_t initialCapacity);
|
||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp, uint32_t initialCapacity);
|
||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVar* varp);
|
||||
inline DfgVertexVar(DfgGraph& dfg, VDfgType type, AstVarScope* vscp);
|
||||
ASTGEN_MEMBERS_DfgVertexVar;
|
||||
|
||||
bool isDrivenByDfg() const { return arity() > 0; }
|
||||
const std::string srcName(size_t) const override { return ""; }
|
||||
|
||||
AstVar* varp() const { return m_varp; }
|
||||
AstVarScope* varScopep() const { return m_varScopep; }
|
||||
|
@ -68,6 +70,13 @@ public:
|
|||
void setHasExtRefs() { m_hasExtRefs = true; }
|
||||
bool hasNonLocalRefs() const { return hasDfgRefs() || hasModRefs() || hasExtRefs(); }
|
||||
|
||||
FileLine* driverFileLine() const { return m_driverFileLine; }
|
||||
void driverFileLine(FileLine* flp) { m_driverFileLine = flp; }
|
||||
|
||||
bool isDrivenFullyByDfg() const {
|
||||
return srcp() && !srcp()->is<DfgVertexSplice>() && !varp()->isForced();
|
||||
}
|
||||
|
||||
// Variable cannot be removed, even if redundant in the DfgGraph (might be used externally)
|
||||
bool keep() const {
|
||||
// Keep if referenced outside this module
|
||||
|
@ -82,6 +91,12 @@ public:
|
|||
return false;
|
||||
}
|
||||
};
|
||||
class DfgVertexSplice VL_NOT_FINAL : public DfgVertexVariadic {
|
||||
public:
|
||||
DfgVertexSplice(DfgGraph& dfg, VDfgType type, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexVariadic{dfg, type, flp, dtypep, 1u} {}
|
||||
ASTGEN_MEMBERS_DfgVertexSplice;
|
||||
};
|
||||
|
||||
// === Concrete node types =====================================================
|
||||
|
||||
|
@ -184,14 +199,49 @@ class DfgVarArray final : public DfgVertexVar {
|
|||
|
||||
public:
|
||||
DfgVarArray(DfgGraph& dfg, AstVar* varp)
|
||||
: DfgVertexVar{dfg, dfgType(), varp, 4u} {
|
||||
: DfgVertexVar{dfg, dfgType(), varp} {
|
||||
UASSERT_OBJ(VN_IS(dtypep(), UnpackArrayDType), varp, "Non array DfgVarArray");
|
||||
}
|
||||
DfgVarArray(DfgGraph& dfg, AstVarScope* vscp)
|
||||
: DfgVertexVar{dfg, dfgType(), vscp, 4u} {
|
||||
: DfgVertexVar{dfg, dfgType(), vscp} {
|
||||
UASSERT_OBJ(VN_IS(dtypep(), UnpackArrayDType), vscp, "Non array DfgVarArray");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgVarArray;
|
||||
};
|
||||
class DfgVarPacked final : public DfgVertexVar {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
||||
public:
|
||||
DfgVarPacked(DfgGraph& dfg, AstVar* varp)
|
||||
: DfgVertexVar{dfg, dfgType(), varp} {
|
||||
UASSERT_OBJ(!VN_IS(dtypep(), UnpackArrayDType), varp, "Array DfgVarPacked");
|
||||
}
|
||||
DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp)
|
||||
: DfgVertexVar{dfg, dfgType(), vscp} {
|
||||
UASSERT_OBJ(!VN_IS(dtypep(), UnpackArrayDType), vscp, "Array DfgVarPacked");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgVarPacked;
|
||||
};
|
||||
|
||||
// === DfgVertexSplice ===
|
||||
class DfgSpliceArray final : public DfgVertexSplice {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
||||
using DriverData = std::pair<FileLine*, uint32_t>;
|
||||
|
||||
std::vector<DriverData> m_driverData; // Additional data associated with each driver
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
|
||||
V3Hash selfHash() const override VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
DfgSpliceArray(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexSplice{dfg, dfgType(), flp, dtypep} {
|
||||
UASSERT_OBJ(VN_IS(dtypep, UnpackArrayDType), flp, "Non array DfgSpliceArray");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgSpliceArray;
|
||||
|
||||
void addDriver(FileLine* flp, uint32_t index, DfgVertex* vtxp) {
|
||||
m_driverData.emplace_back(flp, index);
|
||||
|
@ -203,27 +253,6 @@ public:
|
|||
DfgVertexVariadic::resetSources();
|
||||
}
|
||||
|
||||
// Remove undriven sources
|
||||
void packSources() {
|
||||
// Grab and reset the driver data
|
||||
std::vector<DriverData> driverData;
|
||||
driverData.swap(m_driverData);
|
||||
|
||||
// Grab and unlink the sources
|
||||
std::vector<DfgVertex*> sources{arity()};
|
||||
forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
|
||||
sources[idx] = edge.sourcep();
|
||||
edge.unlinkSource();
|
||||
});
|
||||
DfgVertexVariadic::resetSources();
|
||||
|
||||
// Add back the driven sources
|
||||
for (size_t i = 0; i < sources.size(); ++i) {
|
||||
if (!sources[i]) continue;
|
||||
addDriver(driverData[i].first, driverData[i].second, sources[i]);
|
||||
}
|
||||
}
|
||||
|
||||
FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; }
|
||||
uint32_t driverIndex(size_t idx) const { return m_driverData[idx].second; }
|
||||
|
||||
|
@ -234,26 +263,27 @@ public:
|
|||
return edgep ? edgep->sourcep() : nullptr;
|
||||
}
|
||||
|
||||
const string srcName(size_t idx) const override { return cvtToStr(driverIndex(idx)); }
|
||||
const std::string srcName(size_t idx) const override {
|
||||
return std::to_string(driverIndex(idx));
|
||||
}
|
||||
};
|
||||
class DfgVarPacked final : public DfgVertexVar {
|
||||
class DfgSplicePacked final : public DfgVertexSplice {
|
||||
friend class DfgVertex;
|
||||
friend class DfgVisitor;
|
||||
|
||||
using DriverData = std::pair<FileLine*, uint32_t>;
|
||||
|
||||
std::vector<DriverData> m_driverData; // Additional data associate with each driver
|
||||
std::vector<DriverData> m_driverData; // Additional data associated with each driver
|
||||
|
||||
bool selfEquals(const DfgVertex& that) const override VL_MT_DISABLED;
|
||||
V3Hash selfHash() const override VL_MT_DISABLED;
|
||||
|
||||
public:
|
||||
DfgVarPacked(DfgGraph& dfg, AstVar* varp)
|
||||
: DfgVertexVar{dfg, dfgType(), varp, 1u} {}
|
||||
DfgVarPacked(DfgGraph& dfg, AstVarScope* vscp)
|
||||
: DfgVertexVar{dfg, dfgType(), vscp, 1u} {}
|
||||
ASTGEN_MEMBERS_DfgVarPacked;
|
||||
|
||||
bool isDrivenFullyByDfg() const {
|
||||
return arity() == 1 && source(0)->dtypep() == dtypep() && !varp()->isForced();
|
||||
DfgSplicePacked(DfgGraph& dfg, FileLine* flp, AstNodeDType* dtypep)
|
||||
: DfgVertexSplice{dfg, dfgType(), flp, dtypep} {
|
||||
UASSERT_OBJ(!VN_IS(dtypep, UnpackArrayDType), flp, "Array DfgSplicePacked");
|
||||
}
|
||||
ASTGEN_MEMBERS_DfgSplicePacked;
|
||||
|
||||
void addDriver(FileLine* flp, uint32_t lsb, DfgVertex* vtxp) {
|
||||
m_driverData.emplace_back(flp, lsb);
|
||||
|
@ -265,33 +295,10 @@ public:
|
|||
DfgVertexVariadic::resetSources();
|
||||
}
|
||||
|
||||
// Remove undriven sources
|
||||
void packSources() {
|
||||
// Grab and reset the driver data
|
||||
std::vector<DriverData> driverData;
|
||||
driverData.swap(m_driverData);
|
||||
|
||||
// Grab and unlink the sources
|
||||
std::vector<DfgVertex*> sources{arity()};
|
||||
forEachSourceEdge([&](DfgEdge& edge, size_t idx) {
|
||||
sources[idx] = edge.sourcep();
|
||||
edge.unlinkSource();
|
||||
});
|
||||
DfgVertexVariadic::resetSources();
|
||||
|
||||
// Add back the driven sources
|
||||
for (size_t i = 0; i < sources.size(); ++i) {
|
||||
if (!sources[i]) continue;
|
||||
addDriver(driverData[i].first, driverData[i].second, sources[i]);
|
||||
}
|
||||
}
|
||||
|
||||
FileLine* driverFileLine(size_t idx) const { return m_driverData[idx].first; }
|
||||
uint32_t driverLsb(size_t idx) const { return m_driverData[idx].second; }
|
||||
|
||||
const string srcName(size_t idx) const override {
|
||||
return isDrivenFullyByDfg() ? "" : cvtToStr(driverLsb(idx));
|
||||
}
|
||||
const std::string srcName(size_t idx) const override { return std::to_string(driverLsb(idx)); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -181,7 +181,7 @@ void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeMo
|
|||
|
||||
void EmitCBaseVisitorConst::emitVarDecl(const AstVar* nodep, bool asRef) {
|
||||
const AstBasicDType* const basicp = nodep->basicp();
|
||||
bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType);
|
||||
const bool refNeedParens = VN_IS(nodep->dtypeSkipRefp(), UnpackArrayDType);
|
||||
|
||||
const auto emitDeclArrayBrackets = [this](const AstVar* nodep) -> void {
|
||||
// This isn't very robust and may need cleanup for other data types
|
||||
|
|
|
@ -261,33 +261,57 @@ public:
|
|||
if (const AstCNew* const cnewp = getSuperNewCallRecursep(nodep->nextp())) return cnewp;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void putConstructorSubinit(const AstClass* classp, AstCFunc* cfuncp, bool top,
|
||||
std::set<AstClass*>& doneClassesr) {
|
||||
void putConstructorSubinit(const AstClass* classp, AstCFunc* cfuncp) {
|
||||
// Virtual bases in depth-first left-to-right order
|
||||
std::vector<AstClass*> virtualBases;
|
||||
std::unordered_set<AstClass*> doneClasses;
|
||||
collectVirtualBasesRecursep(classp, virtualBases);
|
||||
for (AstClass* vbase : virtualBases) {
|
||||
if (doneClasses.count(vbase)) continue;
|
||||
puts(doneClasses.empty() ? "" : "\n , ");
|
||||
doneClasses.emplace(vbase);
|
||||
puts(prefixNameProtect(vbase));
|
||||
if (constructorNeedsProcess(vbase)) {
|
||||
puts("(vlProcess, vlSymsp)");
|
||||
} else {
|
||||
puts("(vlSymsp)");
|
||||
}
|
||||
}
|
||||
const AstCNew* const superNewCallp = getSuperNewCallRecursep(cfuncp->stmtsp());
|
||||
// Direct non-virtual bases in declaration order
|
||||
for (const AstClassExtends* extp = classp->extendsp(); extp;
|
||||
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
||||
if (extp->classp()->useVirtualPublic()) {
|
||||
// It's a c++ virtual class (diamond relation)
|
||||
// Must get the subclasses initialized first
|
||||
putConstructorSubinit(extp->classp(), cfuncp, false, doneClassesr);
|
||||
}
|
||||
// Diamond pattern with same base class twice?
|
||||
if (doneClassesr.find(extp->classp()) != doneClassesr.end()) continue;
|
||||
puts(doneClassesr.empty() ? "" : "\n , ");
|
||||
doneClassesr.emplace(extp->classp());
|
||||
if (extp->classp()->useVirtualPublic()) continue;
|
||||
if (doneClasses.count(extp->classp())) continue;
|
||||
puts(doneClasses.empty() ? "" : "\n , ");
|
||||
doneClasses.emplace(extp->classp());
|
||||
puts(prefixNameProtect(extp->classp()));
|
||||
if (constructorNeedsProcess(extp->classp())) {
|
||||
puts("(vlProcess, vlSymsp");
|
||||
} else {
|
||||
puts("(vlSymsp");
|
||||
}
|
||||
if (top) {
|
||||
const AstCNew* const superNewCallp = getSuperNewCallRecursep(cfuncp->stmtsp());
|
||||
UASSERT_OBJ(superNewCallp, cfuncp, "super.new call not found");
|
||||
// Handle super.new() args for the concrete parent
|
||||
if (!extp->classp()->isInterfaceClass() && superNewCallp) {
|
||||
putCommaIterateNext(superNewCallp->argsp(), true);
|
||||
}
|
||||
puts(")");
|
||||
top = false;
|
||||
}
|
||||
}
|
||||
void collectVirtualBasesRecursep(const AstClass* classp,
|
||||
std::vector<AstClass*>& virtualBases) {
|
||||
std::set<const AstClass*> visited;
|
||||
collectVirtualBasesRecursep(classp, virtualBases /*ref*/, visited /*ref*/);
|
||||
}
|
||||
void collectVirtualBasesRecursep(const AstClass* classp, std::vector<AstClass*>& virtualBases,
|
||||
std::set<const AstClass*>& visited) {
|
||||
if (visited.count(classp)) return;
|
||||
visited.emplace(classp);
|
||||
for (const AstClassExtends* extp = classp->extendsp(); extp;
|
||||
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
||||
// Depth-first: recurse into this base first
|
||||
collectVirtualBasesRecursep(extp->classp(), virtualBases, visited);
|
||||
if (extp->classp()->useVirtualPublic()) { virtualBases.push_back(extp->classp()); }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,8 +338,7 @@ public:
|
|||
const AstClass* const classp = VN_CAST(nodep->scopep()->modp(), Class);
|
||||
if (nodep->isConstructor() && classp && classp->extendsp()) {
|
||||
puts("\n : ");
|
||||
std::set<AstClass*> doneClasses;
|
||||
putConstructorSubinit(classp, nodep, true, doneClasses /*ref*/);
|
||||
putConstructorSubinit(classp, nodep);
|
||||
}
|
||||
}
|
||||
puts(" {\n");
|
||||
|
|
|
@ -109,7 +109,8 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
// Emit variables in consecutive anon and non-anon batches
|
||||
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
||||
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
||||
if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) {
|
||||
if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()
|
||||
|| varp->isGenVar()) {
|
||||
const bool anon = isAnonOk(varp);
|
||||
if (anon != lastAnon) emitCurrentList();
|
||||
lastAnon = anon;
|
||||
|
@ -563,16 +564,20 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
if (!VN_IS(modp, Class)) puts("alignas(VL_CACHE_LINE_BYTES) ");
|
||||
puts(prefixNameProtect(modp));
|
||||
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
||||
const string virtpub = classp->useVirtualPublic() ? "virtual public " : "public ";
|
||||
puts(" : " + virtpub);
|
||||
puts(" : ");
|
||||
if (classp->extendsp()) {
|
||||
bool needComma = false;
|
||||
for (const AstClassExtends* extp = classp->extendsp(); extp;
|
||||
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
||||
if (needComma) puts(", ");
|
||||
// Use virtual only for interfaces for class inheritance
|
||||
// (extends)
|
||||
puts(extp->classp()->useVirtualPublic() ? "virtual public " : "public ");
|
||||
putns(extp, prefixNameProtect(extp->classp()));
|
||||
if (extp->nextp()) puts(", " + virtpub);
|
||||
needComma = true;
|
||||
}
|
||||
} else {
|
||||
puts("VlClass");
|
||||
puts("public virtual VlClass");
|
||||
}
|
||||
} else {
|
||||
puts(" final : public VerilatedModule");
|
||||
|
|
|
@ -743,13 +743,13 @@ void EmitCSyms::emitSymImp() {
|
|||
if (!optSystemC()) {
|
||||
puts("\nvoid " + symClassName() + "::_traceDump() {\n");
|
||||
// Caller checked for __Vm_dumperp non-nullptr
|
||||
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
|
||||
puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n");
|
||||
puts("__Vm_dumperp->dump(VL_TIME_Q());\n");
|
||||
puts("}\n");
|
||||
}
|
||||
|
||||
puts("\nvoid " + symClassName() + "::_traceDumpOpen() {\n");
|
||||
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
|
||||
puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n");
|
||||
puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n");
|
||||
puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n");
|
||||
puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n");
|
||||
|
@ -760,7 +760,7 @@ void EmitCSyms::emitSymImp() {
|
|||
puts("}\n");
|
||||
|
||||
puts("\nvoid " + symClassName() + "::_traceDumpClose() {\n");
|
||||
puts("const VerilatedLockGuard lock(__Vm_dumperMutex);\n");
|
||||
puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n");
|
||||
puts("__Vm_dumping = false;\n");
|
||||
puts("VL_DO_CLEAR(delete __Vm_dumperp, __Vm_dumperp = nullptr);\n");
|
||||
puts("}\n");
|
||||
|
|
|
@ -922,6 +922,20 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
|||
m_sensesp = nodep->sensesp();
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
}
|
||||
void visit(AstDelay* nodep) override {
|
||||
puts(""); // this is for proper alignment
|
||||
puts("#");
|
||||
iterateConst(nodep->lhsp());
|
||||
puts(";\n");
|
||||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
}
|
||||
void visit(AstCAwait* nodep) override {
|
||||
AstCMethodHard* methodp = VN_CAST(nodep->exprp(), CMethodHard);
|
||||
UASSERT_OBJ(methodp, nodep, "AstCAwait expression must be an AstCMethodHard");
|
||||
puts(""); // this is for proper alignment
|
||||
puts("#");
|
||||
iterateConst(methodp->pinsp());
|
||||
}
|
||||
void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); }
|
||||
void visit(AstNodeText*) override {}
|
||||
void visit(AstVarScope*) override {}
|
||||
|
|
|
@ -99,6 +99,7 @@ public:
|
|||
DEPRECATED, // Feature will be deprecated
|
||||
ENCAPSULATED, // Error: local/protected violation
|
||||
ENDLABEL, // End lable name mismatch
|
||||
ENUMITEMWIDTH, // Error: enum item width mismatch
|
||||
ENUMVALUE, // Error: enum type needs explicit cast
|
||||
EOFNEWLINE, // End-of-file missing newline
|
||||
GENCLK, // Generated Clock. Historical, never issued.
|
||||
|
@ -124,6 +125,7 @@ public:
|
|||
MULTIDRIVEN, // Driven from multiple blocks
|
||||
MULTITOP, // Multiple top level modules
|
||||
NEWERSTD, // Newer language standard required
|
||||
NOEFFECT, // Statement has no effect
|
||||
NOLATCH, // No latch detected in always_latch block
|
||||
NONSTD, // Non-standard feature present in other sims
|
||||
NULLPORT, // Null port detected in module definition
|
||||
|
@ -189,40 +191,33 @@ public:
|
|||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||
constexpr operator en() const VL_MT_SAFE { return m_e; }
|
||||
const char* ascii() const VL_MT_SAFE {
|
||||
// clang-format off
|
||||
static const char* const names[] = {
|
||||
// Leading spaces indicate it can't be disabled.
|
||||
" MIN", " INFO", " FATAL", " FATALMANY", " FATALSRC", " ERROR", " FIRST_NAMED",
|
||||
// Boolean
|
||||
" I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING", " I_TRACING", " I_UNUSED",
|
||||
" I_CELLDEFINE", " I_COVERAGE", " I_DEF_NETTYPE_WIRE", " I_LINT", " I_TIMING",
|
||||
" I_TRACING", " I_UNUSED",
|
||||
// Errors
|
||||
"LIFETIME", "NEEDTIMINGOPT", "NOTIMING", "PORTSHORT", "TASKNSVAR", "UNSUPPORTED",
|
||||
// Warnings
|
||||
" EC_FIRST_WARN",
|
||||
"ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", "BADVLTPRAGMA",
|
||||
"BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE",
|
||||
"CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA",
|
||||
"CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN",
|
||||
"DECLFILENAME", "DEFOVERRIDE", "DEFPARAM", "DEPRECATED",
|
||||
"ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK",
|
||||
"GENUNNAMED", "HIERBLOCK",
|
||||
"IFDEPTH", "IGNOREDRETURN",
|
||||
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE",
|
||||
"LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING",
|
||||
"MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE",
|
||||
"PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY",
|
||||
"SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR",
|
||||
"STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
|
||||
"TICKCOUNT", "TIMESCALEMOD",
|
||||
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
|
||||
"UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP" ,"UNUSEDPARAM", "UNUSEDSIGNAL",
|
||||
"USERERROR", "USERFATAL", "USERINFO", "USERWARN",
|
||||
"VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL",
|
||||
" MAX"
|
||||
};
|
||||
// clang-format on
|
||||
" EC_FIRST_WARN", "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA",
|
||||
"BADVLTPRAGMA", "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", "CASEINCOMPLETE",
|
||||
"CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", "CMPCONST",
|
||||
"COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", "DECLFILENAME",
|
||||
"DEFOVERRIDE", "DEFPARAM", "DEPRECATED", "ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH",
|
||||
"ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED", "HIERBLOCK", "IFDEPTH",
|
||||
"IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE",
|
||||
"INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "LATCH", "LITENDIAN",
|
||||
"MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", "MULTIDRIVEN", "MULTITOP",
|
||||
"NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NULLPORT", "PINCONNECTEMPTY",
|
||||
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT",
|
||||
"PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "RANDC", "REALCVT", "REDEFMACRO",
|
||||
"RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPLITVAR", "STATICVAR",
|
||||
"STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN",
|
||||
"UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSEDGENVAR",
|
||||
"UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO",
|
||||
"USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND",
|
||||
"WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
// Warnings that default to off
|
||||
|
@ -249,9 +244,9 @@ public:
|
|||
bool pretendError() const VL_MT_SAFE {
|
||||
return (m_e == ASSIGNIN || m_e == BADSTDPRAGMA || m_e == BADVLTPRAGMA || m_e == BLKANDNBLK
|
||||
|| m_e == BLKLOOPINIT || m_e == CONTASSREG || m_e == ENCAPSULATED
|
||||
|| m_e == ENDLABEL || m_e == ENUMVALUE || m_e == IMPURE || m_e == MODMISSING
|
||||
|| m_e == PINNOTFOUND || m_e == PKGNODECL || m_e == PROCASSWIRE
|
||||
|| m_e == ZEROREPL // Says IEEE
|
||||
|| m_e == ENDLABEL || m_e == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == IMPURE
|
||||
|| m_e == MODMISSING || m_e == PINNOTFOUND || m_e == PKGNODECL
|
||||
|| m_e == PROCASSWIRE || m_e == ZEROREPL // Says IEEE
|
||||
);
|
||||
}
|
||||
// Warnings to mention manual
|
||||
|
|
|
@ -369,6 +369,10 @@ public:
|
|||
&& m_lastLineno == rhs.m_lastLineno && m_lastColumn == rhs.m_lastColumn
|
||||
&& m_filenameno == rhs.m_filenameno && m_msgEnIdx == rhs.m_msgEnIdx);
|
||||
}
|
||||
bool equalFirstLineCol(const FileLine& rhs) const {
|
||||
return (m_filenameno == rhs.m_filenameno && m_firstLineno == rhs.m_firstLineno
|
||||
&& m_firstColumn == rhs.m_firstColumn);
|
||||
}
|
||||
// Returns -1 if (*this) should come before rhs after sorted. 1 for the opposite case. 0 for
|
||||
// equivalent.
|
||||
int operatorCompare(const FileLine& rhs) const {
|
||||
|
|
|
@ -150,9 +150,11 @@ private:
|
|||
// AstVarRef::user2 -> Flag indicating not to replace reference
|
||||
// AstVarScope::user3 -> AstVarScope*, a `valVscp` force component for each VarScope of
|
||||
// forced RHS
|
||||
// AstVarScope::user4p -> AstNodeExpr*, the RHS expression
|
||||
const VNUser1InUse m_user1InUse;
|
||||
const VNUser2InUse m_user2InUse;
|
||||
const VNUser3InUse m_user3InUse;
|
||||
const VNUser4InUse m_user4InUse;
|
||||
AstUser1Allocator<AstVar, ForceComponentsVar> m_forceComponentsVar;
|
||||
AstUser1Allocator<AstVarScope, ForceComponentsVarScope> m_forceComponentsVarScope;
|
||||
|
||||
|
@ -185,6 +187,12 @@ public:
|
|||
ForceComponentsVarScope* tryGetForceComponents(AstVarRef* nodep) const {
|
||||
return m_forceComponentsVarScope.tryGet(nodep->varScopep());
|
||||
}
|
||||
void setValVscpRhsExpr(AstVarScope* valVscp, AstNodeExpr* rhsExpr) {
|
||||
valVscp->user4p(rhsExpr);
|
||||
}
|
||||
AstNodeExpr* getValVscpRhsExpr(AstVarScope* valVscp) const {
|
||||
return VN_CAST(valVscp->user4p(), NodeExpr);
|
||||
}
|
||||
};
|
||||
|
||||
class ForceConvertVisitor final : public VNVisitor {
|
||||
|
@ -233,9 +241,8 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
= new AstAssign{flp, lhsp->cloneTreePure(false), rhsp->cloneTreePure(false)};
|
||||
transformWritenVarScopes(setValp->lhsp(), [this, rhsp](AstVarScope* vscp) {
|
||||
AstVarScope* const valVscp = m_state.getForceComponents(vscp).m_valVscp;
|
||||
// TODO support multiple VarRefs on RHS
|
||||
if (AstVarRef* const refp = VN_CAST(rhsp, VarRef))
|
||||
ForceState::setValVscp(refp, valVscp);
|
||||
m_state.setValVscpRhsExpr(valVscp, rhsp->cloneTreePure(false));
|
||||
rhsp->foreach([valVscp](AstVarRef* refp) { ForceState::setValVscp(refp, valVscp); });
|
||||
return valVscp;
|
||||
});
|
||||
|
||||
|
@ -267,11 +274,14 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
transformWritenVarScopes(resetEnp->lhsp(), [this](AstVarScope* vscp) {
|
||||
return m_state.getForceComponents(vscp).m_enVscp;
|
||||
});
|
||||
// IEEE 1800-2023 10.6.2: If this is a net, and not a variable, then reset the read
|
||||
// signal directly as well, in case something in the same process reads it later. Also, if
|
||||
// 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.
|
||||
|
||||
// IEEE 1800-2023 10.6.2: When released, then if the variable is not driven by a continuous
|
||||
// assignment and does not currently have an active procedural continuous assignment, the
|
||||
// variable shall not immediately change value and shall maintain its current value until
|
||||
// the next procedural assignment to the variable is executed. Releasing a variable that is
|
||||
// driven by a continuous assignment or currently has an active assign procedural
|
||||
// continuous assignment shall reestablish that assignment and schedule a reevaluation in
|
||||
// the continuous assignment's scheduling region.
|
||||
AstAssign* const resetRdp
|
||||
= new AstAssign{flp, lhsp->cloneTreePure(false), lhsp->unlinkFrBack()};
|
||||
// Replace write refs on the LHS
|
||||
|
@ -376,10 +386,12 @@ class ForceReplaceVisitor final : public VNVisitor {
|
|||
if (AstVarScope* const valVscp = ForceState::getValVscp(nodep)) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstVarRef* const valp = new AstVarRef{flp, valVscp, VAccess::WRITE};
|
||||
AstVarRef* const rhsp = new AstVarRef{flp, nodep->varScopep(), VAccess::READ};
|
||||
AstNodeExpr* rhsp = m_state.getValVscpRhsExpr(valVscp);
|
||||
UASSERT_OBJ(rhsp, flp, "RHS of force/release must be an AstNodeExpr");
|
||||
rhsp = rhsp->cloneTreePure(false);
|
||||
|
||||
ForceState::markNonReplaceable(valp);
|
||||
ForceState::markNonReplaceable(rhsp);
|
||||
rhsp->foreach([](AstVarRef* refp) { ForceState::markNonReplaceable(refp); });
|
||||
|
||||
m_stmtp->addNextHere(new AstAssign{flp, valp, rhsp});
|
||||
}
|
||||
|
|
|
@ -2365,6 +2365,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
bool m_unresolvedClass; // Unresolved class reference, needs help from V3Param
|
||||
bool m_genBlk; // Contains gen block reference
|
||||
AstNode* m_unlinkedScopep; // Unresolved scope, needs corresponding VarXRef
|
||||
AstDisable* m_disablep; // Disable statement under which the reference is
|
||||
bool m_dotErr; // Error found in dotted resolution, ignore upwards
|
||||
string m_dotText; // String of dotted names found in below parseref
|
||||
DotStates() { init(nullptr); }
|
||||
|
@ -2380,6 +2381,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_unresolvedClass = false;
|
||||
m_genBlk = false;
|
||||
m_unlinkedScopep = nullptr;
|
||||
m_disablep = nullptr;
|
||||
}
|
||||
string ascii() const {
|
||||
static const char* const names[]
|
||||
|
@ -2646,6 +2648,46 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_ds.init(m_curSymp);
|
||||
iterateNull(nodep);
|
||||
}
|
||||
static const AstNodeDType* getElemDTypep(const AstNodeDType* dtypep) {
|
||||
dtypep = dtypep->skipRefp();
|
||||
while (true) {
|
||||
if (const AstBracketArrayDType* const adtypep = VN_CAST(dtypep, BracketArrayDType)) {
|
||||
dtypep = adtypep->childDTypep()->skipRefp();
|
||||
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
||||
dtypep = adtypep->childDTypep()->skipRefp();
|
||||
} else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
dtypep = adtypep->childDTypep()->skipRefp();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dtypep;
|
||||
}
|
||||
static const AstNodeDType* getExprDTypep(const AstNodeExpr* selp) {
|
||||
while (const AstNodePreSel* const sp = VN_CAST(selp, NodePreSel)) selp = sp->fromp();
|
||||
if (const AstMemberSel* const sp = VN_CAST(selp, MemberSel)) {
|
||||
if (const AstNodeDType* dtypep = getExprDTypep(sp->fromp())) {
|
||||
if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
|
||||
const AstClass* const classp = classRefp->classp();
|
||||
const bool found = classp->existsMember(
|
||||
[&dtypep, name = selp->name()](const AstClass*, const AstVar* nodep) {
|
||||
dtypep = nodep->childDTypep();
|
||||
return nodep->name() == name;
|
||||
});
|
||||
if (found) return getElemDTypep(dtypep);
|
||||
selp->v3error("Class " << classRefp->prettyNameQ()
|
||||
<< " does not contain field " << selp->prettyNameQ());
|
||||
} else {
|
||||
selp->v3fatalSrc("Member selection on expression of type "
|
||||
<< dtypep->prettyDTypeNameQ()
|
||||
<< ", which is not a class type");
|
||||
}
|
||||
}
|
||||
} else if (const AstNodeVarRef* const varRefp = VN_CAST(selp, NodeVarRef)) {
|
||||
return getElemDTypep(varRefp->varp()->childDTypep());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define LINKDOT_VISIT_START() \
|
||||
VL_RESTORER(m_indent); \
|
||||
|
@ -2762,7 +2804,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
if (nodep->name() == "__paramNumber1" && m_cellp
|
||||
&& VN_IS(m_cellp->modp(), Primitive)) {
|
||||
// Primitive parameter is really a delay we can just ignore
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
return;
|
||||
} else {
|
||||
const string suggest
|
||||
|
@ -3084,7 +3126,11 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
bool allowVar = false;
|
||||
bool allowFTask = false;
|
||||
bool staticAccess = false;
|
||||
if (m_ds.m_dotPos == DP_PACKAGE) {
|
||||
if (m_ds.m_disablep) {
|
||||
allowScope = true;
|
||||
allowFTask = true;
|
||||
expectWhat = "block/task";
|
||||
} else if (m_ds.m_dotPos == DP_PACKAGE) {
|
||||
// {package-or-class}::{a}
|
||||
AstNodeModule* classOrPackagep = nullptr;
|
||||
expectWhat = "scope/variable/func";
|
||||
|
@ -3185,7 +3231,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
if (!foundp) {
|
||||
} else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), Begin)
|
||||
} else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), NodeBlock)
|
||||
|| VN_IS(foundp->nodep(), Netlist) // for $root
|
||||
|| VN_IS(foundp->nodep(), Module)) { // if top
|
||||
if (allowScope) {
|
||||
|
@ -3193,8 +3239,20 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_ds.m_dotText = VString::dot(m_ds.m_dotText, ".", nodep->name());
|
||||
m_ds.m_dotSymp = foundp;
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
if (m_ds.m_disablep && VN_IS(foundp->nodep(), NodeBlock)) {
|
||||
// Possibly it is not the final link. If we are under dot and not in its
|
||||
// last component, `targetp()` field will be overwritten by next components
|
||||
m_ds.m_disablep->targetp(foundp->nodep());
|
||||
}
|
||||
if (const AstBegin* const beginp = VN_CAST(foundp->nodep(), Begin)) {
|
||||
if (beginp->generate()) m_ds.m_genBlk = true;
|
||||
if (beginp->generate()) {
|
||||
m_ds.m_genBlk = true;
|
||||
if (m_ds.m_disablep) {
|
||||
m_ds.m_disablep->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: Generate block referenced by disable");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Upper AstDot visitor will handle it from here
|
||||
} else if (VN_IS(foundp->nodep(), Cell) && allowVar) {
|
||||
|
@ -3231,7 +3289,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
taskrefp = new AstFuncRef{nodep->fileline(), nodep->name(), nullptr};
|
||||
}
|
||||
nodep->replaceWith(taskrefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
m_ds = lastStates;
|
||||
return;
|
||||
} else if (AstVar* const varp = foundToVarp(foundp, nodep, VAccess::READ)) {
|
||||
|
@ -3438,7 +3496,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_ds.m_dotPos = DP_MEMBER;
|
||||
} else {
|
||||
// Cells/interfaces can't be implicit
|
||||
const bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText == "" && !foundp);
|
||||
const bool checkImplicit
|
||||
= (!m_ds.m_dotp && m_ds.m_dotText == "" && !m_ds.m_disablep && !foundp);
|
||||
const bool err
|
||||
= !(checkImplicit && m_statep->implicitOk(m_modp, nodep->name()));
|
||||
if (err) {
|
||||
|
@ -3493,9 +3552,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
UINFO(9, indent() << m_ds.ascii());
|
||||
VL_RESTORER(m_usedPins);
|
||||
m_usedPins.clear();
|
||||
UASSERT_OBJ(m_statep->forPrimary() || VN_IS(nodep->classOrPackageNodep(), ParamTypeDType)
|
||||
|| nodep->classOrPackageSkipp(),
|
||||
nodep, "ClassRef has unlinked class");
|
||||
UASSERT_OBJ(m_statep->forPrimary() || !nodep->paramsp(), nodep,
|
||||
"class reference parameter not removed by V3Param");
|
||||
{
|
||||
|
@ -3506,6 +3562,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
m_statep->resolveClassOrPackage(m_ds.m_dotSymp, nodep, m_ds.m_dotPos != DP_PACKAGE,
|
||||
false, ":: reference");
|
||||
}
|
||||
UASSERT_OBJ(m_statep->forPrimary()
|
||||
|| VN_IS(nodep->classOrPackageNodep(), ParamTypeDType)
|
||||
|| nodep->classOrPackageSkipp(),
|
||||
nodep, "ClassRef has unlinked class");
|
||||
|
||||
// ClassRef's have pins, so track
|
||||
if (nodep->classOrPackageSkipp()) {
|
||||
|
@ -3665,7 +3725,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
AstVarRef* const newrefp
|
||||
= new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()};
|
||||
nodep->replaceWith(newrefp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3693,7 +3753,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
AstVarRef* const newvscp
|
||||
= new AstVarRef{nodep->fileline(), vscp, nodep->access()};
|
||||
nodep->replaceWith(newvscp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
UINFO(9, indent() << "new " << newvscp); // Also prints taskp
|
||||
}
|
||||
}
|
||||
|
@ -3727,17 +3787,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
if (!fromDtp) {
|
||||
if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) {
|
||||
fromDtp = varRefp->varp()->subDTypep();
|
||||
} else if (const AstNodeSel* const selp = VN_CAST(nodep->fromp(), NodeSel)) {
|
||||
if (const AstNodeVarRef* const varRefp
|
||||
= VN_CAST(selp->fromp(), NodeVarRef)) {
|
||||
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
|
||||
}
|
||||
} else if (const AstNodePreSel* const selp
|
||||
= VN_CAST(nodep->fromp(), NodePreSel)) {
|
||||
if (const AstNodeVarRef* const varRefp
|
||||
= VN_CAST(selp->fromp(), NodeVarRef)) {
|
||||
fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep();
|
||||
}
|
||||
} else {
|
||||
fromDtp = getExprDTypep(nodep->fromp());
|
||||
}
|
||||
if (!fromDtp) {
|
||||
if (VN_IS(nodep->pinsp(), With)) {
|
||||
|
@ -3845,7 +3896,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
// HERE function() . method_called_on_function_return_value()
|
||||
m_ds.m_dotPos = DP_MEMBER;
|
||||
m_ds.m_dotText = "";
|
||||
} else {
|
||||
} else if (!m_ds.m_disablep) {
|
||||
// visit(AstDisable*) setup the dot handling
|
||||
checkNoDot(nodep);
|
||||
}
|
||||
if (nodep->classOrPackagep() && nodep->taskp()) {
|
||||
|
@ -3983,7 +4035,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
AstNode* addp = pinp;
|
||||
if (AstArg* const argp = VN_CAST(pinp, Arg)) {
|
||||
addp = argp->exprp()->unlinkFrBack();
|
||||
VL_DO_DANGLING(pinp->deleteTree(), pinp);
|
||||
VL_DO_DANGLING(pushDeletep(pinp), pinp);
|
||||
}
|
||||
outp = AstNode::addNext(outp, addp);
|
||||
}
|
||||
|
@ -3991,7 +4043,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
newp->dtypep(nodep->dtypep());
|
||||
}
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} else {
|
||||
VSpellCheck speller;
|
||||
|
@ -4213,6 +4265,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
if (nodep->classOrNullp()) return;
|
||||
LINKDOT_VISIT_START();
|
||||
if (m_statep->forPrimary()) {
|
||||
if (nodep->childDTypep()) return;
|
||||
AstNode* cprp = nodep->classOrPkgsp();
|
||||
VSymEnt* lookSymp = m_curSymp;
|
||||
if (AstDot* const dotp = VN_CAST(cprp, Dot)) {
|
||||
|
@ -4419,6 +4472,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
nodep->classOrPackagep(cpackagerefp->classOrPackageSkipp());
|
||||
if (!VN_IS(nodep->classOrPackagep(), Class)
|
||||
&& !VN_IS(nodep->classOrPackagep(), Package)) {
|
||||
if (m_statep->forPrimary()) {
|
||||
// It may be a type that comes from parameter class that is not
|
||||
// instantioned yet
|
||||
iterate(cpackagep);
|
||||
return;
|
||||
}
|
||||
// Likely impossible, as error thrown earlier
|
||||
cpackagerefp->v3error( // LCOV_EXCL_LINE
|
||||
"'::' expected to reference a class/package but referenced '"
|
||||
|
@ -4431,7 +4490,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
cpackagep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Multiple '::' package/class reference");
|
||||
}
|
||||
VL_DO_DANGLING(cpackagep->unlinkFrBack()->deleteTree(), cpackagep);
|
||||
VL_DO_DANGLING(pushDeletep(cpackagep->unlinkFrBack()), cpackagep);
|
||||
}
|
||||
if (m_ds.m_dotp && (m_ds.m_dotPos == DP_PACKAGE || m_ds.m_dotPos == DP_SCOPE)) {
|
||||
UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(),
|
||||
|
@ -4476,7 +4535,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
= new AstClassRefDType{nodep->fileline(), defp, paramsp};
|
||||
newp->classOrPackagep(foundp->classOrPackagep());
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} else if (m_insideClassExtParam) {
|
||||
return;
|
||||
|
@ -4508,25 +4567,49 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
taskp->dpiExport(true);
|
||||
if (nodep->cname() != "") taskp->cname(nodep->cname());
|
||||
}
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstDisable* nodep) override {
|
||||
LINKDOT_VISIT_START();
|
||||
checkNoDot(nodep);
|
||||
VL_RESTORER(m_ds);
|
||||
m_ds.init(m_curSymp);
|
||||
m_ds.m_dotPos = DP_FIRST;
|
||||
m_ds.m_disablep = nodep;
|
||||
iterateChildren(nodep);
|
||||
if (nodep->targetRefp()) {
|
||||
if (AstTaskRef* const taskRefp = VN_CAST(nodep->targetRefp(), TaskRef)) {
|
||||
nodep->targetp(taskRefp->taskp());
|
||||
} else if (!VN_IS(nodep->targetRefp(), ParseRef)) {
|
||||
// If it is a ParseRef, either it couldn't be linked or it is linked to a block
|
||||
nodep->v3warn(E_UNSUPPORTED, "Node of type "
|
||||
<< nodep->targetRefp()->prettyTypeName()
|
||||
<< " referenced by disable");
|
||||
pushDeletep(nodep->unlinkFrBack());
|
||||
}
|
||||
if (nodep->targetp()) {
|
||||
// If the target is already linked, there is no need to store reference as child
|
||||
VL_DO_DANGLING(nodep->targetRefp()->unlinkFrBack()->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstPackageImport* nodep) override {
|
||||
// No longer needed
|
||||
LINKDOT_VISIT_START();
|
||||
checkNoDot(nodep);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstPackageExport* nodep) override {
|
||||
// No longer needed
|
||||
LINKDOT_VISIT_START();
|
||||
checkNoDot(nodep);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstPackageExportStarStar* nodep) override {
|
||||
// No longer needed
|
||||
LINKDOT_VISIT_START();
|
||||
checkNoDot(nodep);
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstCellRef* nodep) override {
|
||||
LINKDOT_VISIT_START();
|
||||
|
@ -4648,7 +4731,7 @@ void V3LinkDot::linkDotGuts(AstNetlist* rootp, VLinkDotStep step) {
|
|||
state.computeIfaceVarSyms();
|
||||
state.computeScopeAliases();
|
||||
state.dumpSelf();
|
||||
{ LinkDotResolveVisitor{rootp, &state}; }
|
||||
LinkDotResolveVisitor visitor{rootp, &state};
|
||||
state.dumpSelf();
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "V3AstUserAllocator.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -60,6 +61,8 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
int m_modRepeatNum = 0; // Repeat counter
|
||||
VOptionBool m_unrollFull; // Pragma full, disable, or default unrolling
|
||||
std::vector<AstNodeBlock*> m_blockStack; // All begin blocks above current node
|
||||
V3UniqueNames m_queueNames{
|
||||
"__VprocessQueue"}; // Names for queues needed for 'disable' handling
|
||||
|
||||
// METHODS
|
||||
AstJumpLabel* findAddLabel(AstNode* nodep, bool endOfIter) {
|
||||
|
@ -159,6 +162,79 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
if (AstNode* const refp = nodep->op4p()) addPrefixToBlocksRecurse(prefix, refp);
|
||||
if (AstNode* const refp = nodep->nextp()) addPrefixToBlocksRecurse(prefix, refp);
|
||||
}
|
||||
static AstNode* getMemberp(const AstNodeModule* const nodep, const std::string& name) {
|
||||
for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp->name() == name) return itemp;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
bool existsBlockAbove(const std::string& name) const {
|
||||
for (const AstNodeBlock* const stackp : vlstd::reverse_view(m_blockStack)) {
|
||||
if (stackp->name() == name) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static AstStmtExpr* getQueuePushProcessSelfp(AstVarRef* const queueRefp) {
|
||||
// Constructs queue.push_back(std::process::self()) statement
|
||||
FileLine* const fl = queueRefp->fileline();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
AstFunc* const selfMethodp = VN_AS(getMemberp(processClassp, "self"), Func);
|
||||
AstFuncRef* const processSelfp = new AstFuncRef{fl, selfMethodp, nullptr};
|
||||
processSelfp->classOrPackagep(processClassp);
|
||||
return new AstStmtExpr{
|
||||
fl, new AstMethodCall{fl, queueRefp, "push_back", new AstArg{fl, "", processSelfp}}};
|
||||
}
|
||||
void handleDisableOnFork(AstDisable* const nodep, const std::vector<AstBegin*>& forks) {
|
||||
// The support is limited only to disabling a fork from outside that fork.
|
||||
// It utilizes the process::kill()` method. For each `disable` a queue of processes is
|
||||
// declared. At the beginning of each fork that can be disabled, its process handle is
|
||||
// pushed to the queue. `disable` statement is replaced with calling `kill()` method on
|
||||
// each element of the queue.
|
||||
FileLine* const fl = nodep->fileline();
|
||||
const std::string targetName = nodep->targetp()->name();
|
||||
if (existsBlockAbove(targetName)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from within same fork");
|
||||
}
|
||||
if (m_ftaskp) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function");
|
||||
}
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
// Declare queue of processes (as a global variable for simplicity)
|
||||
AstVar* const processQueuep = new AstVar{
|
||||
fl, VVarType::VAR, m_queueNames.get(targetName), VFlagChildDType{},
|
||||
new AstQueueDType{fl, VFlagChildDType{},
|
||||
new AstClassRefDType{fl, processClassp, nullptr}, nullptr}};
|
||||
processQueuep->lifetime(VLifetime::STATIC);
|
||||
topPkgp->addStmtsp(processQueuep);
|
||||
|
||||
AstVarRef* const queueWriteRefp
|
||||
= new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE};
|
||||
AstStmtExpr* pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp);
|
||||
|
||||
for (AstBegin* const beginp : forks) {
|
||||
if (pushCurrentProcessp->backp()) {
|
||||
pushCurrentProcessp = pushCurrentProcessp->cloneTree(false);
|
||||
}
|
||||
if (beginp->stmtsp()) {
|
||||
// There is no need to add it to empty block
|
||||
beginp->stmtsp()->addHereThisAsNext(pushCurrentProcessp);
|
||||
}
|
||||
}
|
||||
AstVarRef* const queueRefp = new AstVarRef{fl, topPkgp, processQueuep, VAccess::READWRITE};
|
||||
AstTaskRef* const killQueueCall
|
||||
= new AstTaskRef{fl, VN_AS(getMemberp(processClassp, "killQueue"), Task),
|
||||
new AstArg{fl, "", queueRefp}};
|
||||
killQueueCall->classOrPackagep(processClassp);
|
||||
nodep->addNextHere(new AstStmtExpr{fl, killQueueCall});
|
||||
}
|
||||
static bool directlyUnderFork(const AstNode* const nodep) {
|
||||
if (nodep->backp()->nextp() == nodep) return directlyUnderFork(nodep->backp());
|
||||
if (VN_IS(nodep->backp(), Fork)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
|
@ -337,33 +413,52 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstDisable* nodep) override {
|
||||
UINFO(8, " DISABLE " << nodep);
|
||||
iterateChildren(nodep);
|
||||
AstNodeBlock* blockp = nullptr;
|
||||
for (AstNodeBlock* const stackp : vlstd::reverse_view(m_blockStack)) {
|
||||
UINFO(9, " UNDERBLK " << stackp);
|
||||
if (stackp->name() == nodep->name()) {
|
||||
blockp = stackp;
|
||||
break;
|
||||
AstNode* const targetp = nodep->targetp();
|
||||
FileLine* const fl = nodep->fileline();
|
||||
UASSERT_OBJ(targetp, nodep, "Unlinked disable statement");
|
||||
if (VN_IS(targetp, Task)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling task by name");
|
||||
} else if (AstFork* const forkp = VN_CAST(targetp, Fork)) {
|
||||
std::vector<AstBegin*> forks;
|
||||
for (AstNode* forkItemp = forkp->stmtsp(); forkItemp; forkItemp = forkItemp->nextp()) {
|
||||
// Further handling of disable stmt requires all forks to be begin blocks
|
||||
AstBegin* beginp = VN_CAST(forkItemp, Begin);
|
||||
if (!beginp) {
|
||||
beginp = new AstBegin{fl, "", nullptr};
|
||||
forkItemp->replaceWith(beginp);
|
||||
beginp->addStmtsp(forkItemp);
|
||||
// In order to continue the iteration
|
||||
forkItemp = beginp;
|
||||
}
|
||||
forks.push_back(beginp);
|
||||
}
|
||||
}
|
||||
// if (debug() >= 9) { UINFO(0, "\n"); blockp->dumpTree("- labeli: "); }
|
||||
if (!blockp) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"disable isn't underneath a begin with name: " << nodep->prettyNameQ());
|
||||
} else if (AstBegin* const beginp = VN_CAST(blockp, Begin)) {
|
||||
if (beginp->user3()) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling block that contains a fork");
|
||||
handleDisableOnFork(nodep, forks);
|
||||
} else if (AstBegin* const beginp = VN_CAST(targetp, Begin)) {
|
||||
if (directlyUnderFork(beginp)) {
|
||||
std::vector<AstBegin*> forks{beginp};
|
||||
handleDisableOnFork(nodep, forks);
|
||||
} else {
|
||||
// Jump to the end of the named block
|
||||
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp});
|
||||
const std::string targetName = beginp->name();
|
||||
if (existsBlockAbove(targetName)) {
|
||||
if (beginp->user3()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: disabling block that contains a fork");
|
||||
} else {
|
||||
// Jump to the end of the named block
|
||||
AstJumpLabel* const labelp = findAddLabel(beginp, false);
|
||||
nodep->addNextHere(new AstJumpGo{nodep->fileline(), labelp});
|
||||
}
|
||||
} else {
|
||||
nodep->v3warn(E_UNSUPPORTED, "disable isn't underneath a begin with name: '"
|
||||
<< targetName << "'");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork by name");
|
||||
nodep->v3fatalSrc("Disable linked with node of unhandled type "
|
||||
<< targetp->prettyTypeName());
|
||||
}
|
||||
nodep->unlinkFrBack();
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
// if (debug() >= 9) { UINFO(0, "\n"); beginp->dumpTree("- labelo: "); }
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (m_loopInc && nodep->varp()) nodep->varp()->usedLoopIdx(true);
|
||||
|
|
|
@ -267,16 +267,17 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
const int left = nodep->rangep()->leftConst();
|
||||
const int right = nodep->rangep()->rightConst();
|
||||
const int increment = (left > right) ? -1 : 1;
|
||||
int offset_from_init = 0;
|
||||
uint32_t offset_from_init = 0;
|
||||
AstEnumItem* addp = nullptr;
|
||||
FileLine* const flp = nodep->fileline();
|
||||
for (int i = left; i != (right + increment); i += increment, offset_from_init++) {
|
||||
for (int i = left; i != (right + increment); i += increment, ++offset_from_init) {
|
||||
const string name = nodep->name() + cvtToStr(i);
|
||||
AstNodeExpr* valuep = nullptr;
|
||||
if (nodep->valuep()) {
|
||||
// V3Width looks for Adds with same fileline as the EnumItem
|
||||
valuep
|
||||
= new AstAdd{flp, nodep->valuep()->cloneTree(true),
|
||||
new AstConst(flp, AstConst::Unsized32{}, offset_from_init)};
|
||||
new AstConst{flp, AstConst::Unsized32{}, offset_from_init}};
|
||||
}
|
||||
addp = AstNode::addNext(addp, new AstEnumItem{flp, name, nullptr, valuep});
|
||||
}
|
||||
|
@ -577,7 +578,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
} else if (VN_IS(bracketp, SelLoopVars)) {
|
||||
// Ok
|
||||
} else {
|
||||
nodep->v3error("Syntax error; foreach missing bracketed loop variable"
|
||||
nodep->v3error("Foreach missing bracketed loop variable is no-operation"
|
||||
" (IEEE 1800-2023 12.7.3)");
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
|
|
|
@ -1188,7 +1188,7 @@ uint32_t V3Number::countOnes() const {
|
|||
|
||||
uint32_t V3Number::mostSetBitP1() const {
|
||||
for (int bit = width() - 1; bit >= 0; bit--) {
|
||||
if (bitIs1(bit)) return bit + 1;
|
||||
if (!bitIs0(bit)) return bit + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1336,6 +1336,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
|||
m_fDfgPostInline = flag;
|
||||
m_fDfgScoped = flag;
|
||||
});
|
||||
DECL_OPTION("-fdfg-break-cycles", FOnOff, &m_fDfgBreakCycles);
|
||||
DECL_OPTION("-fdfg-peephole", FOnOff, &m_fDfgPeephole);
|
||||
DECL_OPTION("-fdfg-peephole-", CbPartialMatch, [this](const char* optp) { //
|
||||
m_fDfgPeepholeDisabled.erase(optp);
|
||||
|
|
|
@ -420,6 +420,7 @@ private:
|
|||
bool m_fConstBitOpTree; // main switch: -fno-const-bit-op-tree constant bit op tree
|
||||
bool m_fConstEager = true; // main switch: -fno-const-eagerly run V3Const during passes
|
||||
bool m_fDedupe; // main switch: -fno-dedupe: logic deduplication
|
||||
bool m_fDfgBreakCycles = true; // main switch: -fno-dfg-break-cycles
|
||||
bool m_fDfgPeephole = true; // main switch: -fno-dfg-peephole
|
||||
bool m_fDfgPreInline; // main switch: -fno-dfg-pre-inline and -fno-dfg
|
||||
bool m_fDfgPostInline; // main switch: -fno-dfg-post-inline and -fno-dfg
|
||||
|
@ -735,6 +736,7 @@ public:
|
|||
bool fConstBitOpTree() const { return m_fConstBitOpTree; }
|
||||
bool fConstEager() const { return m_fConstEager; }
|
||||
bool fDedupe() const { return m_fDedupe; }
|
||||
bool fDfgBreakCyckes() const { return m_fDfgBreakCycles; }
|
||||
bool fDfgPeephole() const { return m_fDfgPeephole; }
|
||||
bool fDfgPreInline() const { return m_fDfgPreInline; }
|
||||
bool fDfgPostInline() const { return m_fDfgPostInline; }
|
||||
|
|
|
@ -1116,7 +1116,7 @@ class ParamVisitor final : public VNVisitor {
|
|||
} else {
|
||||
cellp->v3fatalSrc("Expected module parameterization");
|
||||
}
|
||||
UASSERT_OBJ(srcModp, cellp, "Unlinked class ref");
|
||||
if (!srcModp) continue;
|
||||
|
||||
// Update path
|
||||
string someInstanceName = modp->someInstanceName();
|
||||
|
|
|
@ -293,12 +293,12 @@ public:
|
|||
void dumpInputsFile() VL_MT_DISABLED;
|
||||
void dumpTokensAhead(int line) VL_MT_DISABLED;
|
||||
static void candidatePli(VSpellCheck* spellerp) VL_MT_DISABLED;
|
||||
void importIfInStd(FileLine* fileline, const string& id);
|
||||
|
||||
private:
|
||||
void preprocDumps(std::ostream& os);
|
||||
void lexFile(const string& modname) VL_MT_DISABLED;
|
||||
void yylexReadTok() VL_MT_DISABLED;
|
||||
void importIfInStd(FileLine* fileline, const string& id);
|
||||
void tokenPull() VL_MT_DISABLED;
|
||||
void tokenPipeline() VL_MT_DISABLED; // Internal; called from tokenToBison
|
||||
int tokenPipelineId(int token) VL_MT_DISABLED;
|
||||
|
|
|
@ -1189,6 +1189,18 @@ VirtIfaceTriggers::makeIfaceToSensMap(AstNetlist* const netlistp, size_t vifTrig
|
|||
return ifaceToSensMap;
|
||||
}
|
||||
|
||||
VirtIfaceTriggers::IfaceMemberSensMap
|
||||
VirtIfaceTriggers::makeMemberToSensMap(AstNetlist* const netlistp, size_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const {
|
||||
IfaceMemberSensMap memberToSensMap;
|
||||
for (const auto& p : m_memberTriggers) {
|
||||
memberToSensMap.emplace(
|
||||
std::make_pair(p.first, createTriggerSenTree(netlistp, trigVscp, vifTriggerIndex)));
|
||||
++vifTriggerIndex;
|
||||
}
|
||||
return memberToSensMap;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// Top level entry-point to scheduling
|
||||
|
||||
|
|
|
@ -153,17 +153,55 @@ public:
|
|||
};
|
||||
|
||||
class VirtIfaceTriggers final {
|
||||
// Represents a specific member in a virtual interface
|
||||
struct IfaceMember final {
|
||||
const AstIface* m_ifacep; // Interface type
|
||||
const AstVar* m_memberVarp; // pointer to member field
|
||||
|
||||
IfaceMember(const AstIface* ifacep, const AstVar* memberVarp)
|
||||
: m_ifacep(ifacep)
|
||||
, m_memberVarp(memberVarp) {}
|
||||
|
||||
bool operator<(const IfaceMember& other) const {
|
||||
if (m_ifacep != other.m_ifacep) return m_ifacep < other.m_ifacep;
|
||||
return m_memberVarp < other.m_memberVarp;
|
||||
}
|
||||
};
|
||||
|
||||
using IfaceMemberTrigger = std::pair<IfaceMember, AstVarScope*>;
|
||||
using IfaceMemberTriggerVec = std::vector<IfaceMemberTrigger>;
|
||||
using IfaceMemberSensMap = std::map<IfaceMember, AstSenTree*>;
|
||||
|
||||
using IfaceTrigger = std::pair<const AstIface*, AstVarScope*>;
|
||||
using IfaceTriggerVec = std::vector<IfaceTrigger>;
|
||||
using IfaceSensMap = std::map<const AstIface*, AstSenTree*>;
|
||||
IfaceTriggerVec m_triggers;
|
||||
|
||||
IfaceMemberTriggerVec m_memberTriggers;
|
||||
IfaceTriggerVec m_ifaceTriggers;
|
||||
|
||||
public:
|
||||
void emplace_back(IfaceTrigger&& p) { m_triggers.emplace_back(std::move(p)); }
|
||||
IfaceTriggerVec::const_iterator begin() const { return m_triggers.begin(); }
|
||||
IfaceTriggerVec::const_iterator end() const { return m_triggers.end(); }
|
||||
void addMemberTrigger(const AstIface* ifacep, const AstVar* memberVarp,
|
||||
AstVarScope* triggerVscp) {
|
||||
m_memberTriggers.emplace_back(IfaceMember(ifacep, memberVarp), triggerVscp);
|
||||
}
|
||||
|
||||
AstVarScope* findMemberTrigger(const AstIface* ifacep, const AstVar* memberVarp) const {
|
||||
IfaceMember target{ifacep, memberVarp};
|
||||
for (const auto& pair : m_memberTriggers) {
|
||||
if (!(pair.first < target) && !(target < pair.first)) return pair.second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IfaceMemberSensMap makeMemberToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const;
|
||||
|
||||
void emplace_back(IfaceTrigger&& p) { m_ifaceTriggers.emplace_back(std::move(p)); }
|
||||
IfaceTriggerVec::const_iterator begin() const { return m_ifaceTriggers.begin(); }
|
||||
IfaceTriggerVec::const_iterator end() const { return m_ifaceTriggers.end(); }
|
||||
IfaceSensMap makeIfaceToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const;
|
||||
|
||||
VL_UNCOPYABLE(VirtIfaceTriggers);
|
||||
VirtIfaceTriggers() = default;
|
||||
VirtIfaceTriggers(VirtIfaceTriggers&&) = default;
|
||||
|
|
|
@ -49,12 +49,15 @@ private:
|
|||
|
||||
// TYPES
|
||||
using OnWriteToVirtIface = std::function<void(AstVarRef*, AstIface*)>;
|
||||
using OnWriteToVirtIfaceMember
|
||||
= std::function<void(AstVarRef*, AstIface*, const std::string&)>;
|
||||
|
||||
// STATE
|
||||
AstNetlist* const m_netlistp; // Root node
|
||||
AstAssign* m_trigAssignp = nullptr; // Previous/current trigger assignment
|
||||
AstIface* m_trigAssignIfacep = nullptr; // Interface type whose trigger is assigned
|
||||
// by m_trigAssignp
|
||||
AstVar* m_trigAssignMemberVarp; // Member pointer whose trigger is assigned
|
||||
V3UniqueNames m_vifTriggerNames{"__VvifTrigger"}; // Unique names for virt iface
|
||||
// triggers
|
||||
VirtIfaceTriggers m_triggers; // Interfaces and corresponding trigger vars
|
||||
|
@ -73,6 +76,25 @@ private:
|
|||
}
|
||||
});
|
||||
}
|
||||
// For each write across a virtual interface boundary (member-level tracking)
|
||||
static void foreachWrittenVirtIfaceMember(
|
||||
AstNode* const nodep, const std::function<void(AstVarRef*, AstIface*, AstVar*)>& onWrite) {
|
||||
nodep->foreach([&](AstVarRef* const refp) {
|
||||
if (refp->access().isReadOnly()) return;
|
||||
if (AstIfaceRefDType* const dtypep = VN_CAST(refp->varp()->dtypep(), IfaceRefDType)) {
|
||||
if (dtypep->isVirtual()) {
|
||||
if (AstMemberSel* const memberSelp = VN_CAST(refp->firstAbovep(), MemberSel)) {
|
||||
// Extract the member varp from the MemberSel node
|
||||
AstVar* memberVarp = memberSelp->varp();
|
||||
onWrite(refp, dtypep->ifacep(), memberVarp);
|
||||
}
|
||||
}
|
||||
} else if (AstIface* const ifacep = refp->varp()->sensIfacep()) {
|
||||
AstVar* memberVarp = refp->varp();
|
||||
onWrite(refp, ifacep, memberVarp);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Returns true if there is a write across a virtual interface boundary
|
||||
static bool writesToVirtIface(const AstNode* const nodep) {
|
||||
return nodep->exists([](const AstVarRef* const refp) {
|
||||
|
@ -103,12 +125,31 @@ private:
|
|||
return new AstVarRef{flp, VN_AS(ifacep->user1p(), VarScope), VAccess::WRITE};
|
||||
}
|
||||
|
||||
// Create trigger reference for a specific interface member
|
||||
AstVarRef* createVirtIfaceMemberTriggerRefp(FileLine* const flp, AstIface* ifacep,
|
||||
const AstVar* memberVarp) {
|
||||
// Check if we already have a trigger for this specific member
|
||||
AstVarScope* existingTrigger = m_triggers.findMemberTrigger(ifacep, memberVarp);
|
||||
if (!existingTrigger) {
|
||||
AstScope* const scopeTopp = m_netlistp->topScopep()->scopep();
|
||||
// Create a unique name for this member trigger
|
||||
const std::string triggerName
|
||||
= m_vifTriggerNames.get(ifacep) + "_Vtrigm_" + memberVarp->name();
|
||||
AstVarScope* const vscp = scopeTopp->createTemp(triggerName, 1);
|
||||
m_triggers.addMemberTrigger(ifacep, memberVarp, vscp);
|
||||
existingTrigger = vscp;
|
||||
}
|
||||
return new AstVarRef{flp, existingTrigger, VAccess::WRITE};
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
m_trigAssignp = nullptr;
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
m_trigAssignIfacep = nullptr;
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
|
@ -116,6 +157,8 @@ private:
|
|||
m_trigAssignp = nullptr;
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
m_trigAssignIfacep = nullptr;
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstAssignW* nodep) override {
|
||||
|
@ -140,11 +183,13 @@ private:
|
|||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
}
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
|
@ -152,6 +197,7 @@ private:
|
|||
// branch
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
|
@ -161,18 +207,21 @@ private:
|
|||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
// Clear the trigger assignment, as there could have been timing controls in the loop
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
VL_RESTORER(m_trigAssignMemberVarp);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
|
@ -180,29 +229,48 @@ private:
|
|||
// block
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstNodeStmt* nodep) override {
|
||||
if (v3Global.usesTiming()
|
||||
&& nodep->exists([](AstNode* nodep) { return nodep->isTimingControl(); })) {
|
||||
m_trigAssignp = nullptr; // Could be after a delay - need new trigger assignment
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
// No restorer, as following statements should not reuse the old assignment
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
foreachWrittenVirtIface(nodep, [&](AstVarRef*, AstIface* ifacep) {
|
||||
if (ifacep != m_trigAssignIfacep) {
|
||||
// Write to different interface type than before - need new trigger assignment
|
||||
// No restorer, as following statements should not reuse the old assignment
|
||||
|
||||
foreachWrittenVirtIfaceMember(nodep, [&](AstVarRef*, AstIface* ifacep,
|
||||
AstVar* memberVarp) {
|
||||
if (ifacep != m_trigAssignIfacep || memberVarp != m_trigAssignMemberVarp) {
|
||||
// Write to different interface member than before - need new trigger assignment
|
||||
m_trigAssignIfacep = ifacep;
|
||||
m_trigAssignMemberVarp = memberVarp;
|
||||
m_trigAssignp = nullptr;
|
||||
}
|
||||
if (!m_trigAssignp) {
|
||||
m_trigAssignp = new AstAssign{flp, createVirtIfaceTriggerRefp(flp, ifacep),
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
m_trigAssignp
|
||||
= new AstAssign{flp, createVirtIfaceMemberTriggerRefp(flp, ifacep, memberVarp),
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
nodep->addNextHere(m_trigAssignp);
|
||||
}
|
||||
});
|
||||
// Fallback to whole-interface tracking if no member-specific assignments found
|
||||
if (!m_trigAssignp) {
|
||||
foreachWrittenVirtIface(nodep, [&](AstVarRef*, AstIface* ifacep) {
|
||||
if (ifacep != m_trigAssignIfacep) {
|
||||
m_trigAssignIfacep = ifacep;
|
||||
m_trigAssignMemberVarp = nullptr;
|
||||
m_trigAssignp = nullptr;
|
||||
}
|
||||
if (!m_trigAssignp) {
|
||||
m_trigAssignp = new AstAssign{flp, createVirtIfaceTriggerRefp(flp, ifacep),
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
nodep->addNextHere(m_trigAssignp);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
void visit(AstNodeExpr*) override {} // Accelerate
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
|
|
@ -2461,11 +2461,11 @@ class WidthVisitor final : public VNVisitor {
|
|||
// if (debug() >= 9) nodep->dumpTree("- VRout: ");
|
||||
if (nodep->access().isWriteOrRW() && nodep->varp()->direction() == VDirection::CONSTREF) {
|
||||
nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ());
|
||||
} else if (!nodep->varp()->isForced() && nodep->access().isWriteOrRW()
|
||||
&& nodep->varp()->isInput() && !nodep->varp()->isFuncLocal()
|
||||
&& nodep->varp()->isReadOnly() && (!m_ftaskp || !m_ftaskp->isConstructor())
|
||||
&& !VN_IS(m_procedurep, InitialAutomatic)
|
||||
&& !VN_IS(m_procedurep, InitialStatic)) {
|
||||
} else if (nodep->access().isWriteOrRW() && nodep->varp()->isInput()
|
||||
&& !nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()
|
||||
&& (!m_ftaskp || !m_ftaskp->isConstructor())
|
||||
&& !VN_IS(m_procedurep, InitialAutomatic) && !VN_IS(m_procedurep, InitialStatic)
|
||||
&& !VN_IS(nodep->abovep(), AssignForce) && !VN_IS(nodep->abovep(), Release)) {
|
||||
nodep->v3warn(ASSIGNIN, "Assigning to input/const variable: " << nodep->prettyNameQ());
|
||||
} else if (nodep->access().isWriteOrRW() && nodep->varp()->isConst() && !m_paramsOnly
|
||||
&& (!m_ftaskp || !m_ftaskp->isConstructor())
|
||||
|
@ -2553,9 +2553,16 @@ class WidthVisitor final : public VNVisitor {
|
|||
// Default type is int, but common to assign narrower values, so minwidth from value
|
||||
userIterateAndNext(nodep->valuep(), WidthVP{CONTEXT_DET, PRELIM}.p());
|
||||
bool warnOn = true;
|
||||
if (const AstConst* const constp = VN_CAST(nodep->valuep(), Const)) {
|
||||
AstNodeExpr* valuep = nodep->valuep();
|
||||
if (const AstAdd* const anodep = VN_CAST(valuep, Add)) {
|
||||
// If constructed by V3LinkParse due to "enumitem[N_REPEATS] value"
|
||||
if (anodep->fileline()->equalFirstLineCol(*(nodep->fileline())))
|
||||
valuep = anodep->lhsp();
|
||||
}
|
||||
if (const AstConst* const constp = VN_CAST(valuep, Const)) {
|
||||
if (static_cast<int>(constp->num().mostSetBitP1()) > nodep->width()) {
|
||||
constp->v3error("Enum value exceeds width of enum type (IEEE 1800-2023 6.19)");
|
||||
constp->v3warn(ENUMITEMWIDTH,
|
||||
"Enum value exceeds width of enum type (IEEE 1800-2023 6.19)");
|
||||
warnOn = false; // Prevent normal WIDTHTRUNC
|
||||
}
|
||||
}
|
||||
|
@ -7668,11 +7675,11 @@ class WidthVisitor final : public VNVisitor {
|
|||
// SIGNED/DOUBLE METHODS
|
||||
|
||||
AstNodeExpr* checkCvtUS(AstNodeExpr* nodep) {
|
||||
if (nodep && nodep->isDouble()) {
|
||||
nodep->v3error("Expected integral (non-" << nodep->dtypep()->prettyDTypeNameQ()
|
||||
<< ") input to "
|
||||
<< nodep->backp()->prettyTypeName());
|
||||
nodep = spliceCvtS(nodep, true, 32);
|
||||
if (nodep && nodep->dtypep()->skipRefp()->isDouble()) {
|
||||
nodep->v3warn(REALCVT,
|
||||
"Implicit conversion of real to integer; expected integral input to "
|
||||
<< nodep->backp()->prettyTypeName());
|
||||
nodep = spliceCvtS(nodep, false, 32);
|
||||
}
|
||||
return nodep;
|
||||
}
|
||||
|
|
24
src/astgen
24
src/astgen
|
@ -1219,6 +1219,29 @@ def write_dfg_auto_classes(filename):
|
|||
fh.write("\n")
|
||||
|
||||
|
||||
def write_dfg_clone_cases(filename):
|
||||
with open_file(filename) as fh:
|
||||
|
||||
def emitBlock(pattern, **fmt):
|
||||
fh.write(textwrap.dedent(pattern).format(**fmt))
|
||||
|
||||
for node in DfgVertexList:
|
||||
# Only generate code for automatically derived leaf nodes
|
||||
if (node.file is not None) or not node.isLeaf:
|
||||
continue
|
||||
|
||||
emitBlock('''\
|
||||
case VDfgType::at{t}: {{
|
||||
Dfg{t}* const cp = new Dfg{t}{{*clonep, vtx.fileline(), vtx.dtypep()}};
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}}
|
||||
''',
|
||||
t=node.name,
|
||||
s=node.superClass.name)
|
||||
fh.write("\n")
|
||||
|
||||
|
||||
def write_dfg_ast_to_dfg(filename):
|
||||
with open_file(filename) as fh:
|
||||
for node in DfgVertexList:
|
||||
|
@ -1408,6 +1431,7 @@ if Args.classes:
|
|||
write_type_tests("Dfg", DfgVertexList)
|
||||
write_dfg_macros("V3Dfg__gen_macros.h")
|
||||
write_dfg_auto_classes("V3Dfg__gen_auto_classes.h")
|
||||
write_dfg_clone_cases("V3Dfg__gen_clone_cases.h")
|
||||
write_dfg_ast_to_dfg("V3Dfg__gen_ast_to_dfg.h")
|
||||
write_dfg_dfg_to_ast("V3Dfg__gen_dfg_to_ast.h")
|
||||
|
||||
|
|
|
@ -2226,10 +2226,11 @@ data_typeNoRef<nodeDTypep>: // ==IEEE: data_type, excluding class_ty
|
|||
new AstDefImplicitDType{$1->fileline(),
|
||||
"__typeimpsu" + cvtToStr(GRAMMARP->s_typeImpNum++),
|
||||
VFlagChildDType{}, $1}, $2, true); }
|
||||
| enumDecl
|
||||
{ $$ = new AstDefImplicitDType{$1->fileline(),
|
||||
"__typeimpenum" + cvtToStr(GRAMMARP->s_typeImpNum++),
|
||||
VFlagChildDType{}, $1}; }
|
||||
| enumDecl packed_dimensionListE
|
||||
{ $$ = GRAMMARP->createArray(
|
||||
new AstDefImplicitDType{$1->fileline(),
|
||||
"__typeimpenum" + cvtToStr(GRAMMARP->s_typeImpNum++),
|
||||
VFlagChildDType{}, $1}, $2, true); }
|
||||
| ySTRING
|
||||
{ $$ = new AstBasicDType{$1, VBasicDTypeKwd::STRING}; }
|
||||
| yCHANDLE
|
||||
|
@ -3753,10 +3754,10 @@ statement_item<nodep>: // IEEE: statement_item
|
|||
//
|
||||
// // IEEE: disable_statement
|
||||
| yDISABLE yFORK ';' { $$ = new AstDisableFork{$1}; }
|
||||
| yDISABLE idAny/*UNSUP: hierarchical_identifier-task_or_block*/ ';'
|
||||
{ $$ = new AstDisable{$1, *$2}; }
|
||||
| yDISABLE idAny '.' idDottedSel ';'
|
||||
{ $$ = nullptr; BBUNSUP($4, "Unsupported: disable with '.'"); }
|
||||
| yDISABLE idDottedSel ';'
|
||||
{ $$ = new AstDisable{$1, $2};
|
||||
PARSEP->importIfInStd($1, "process");
|
||||
}
|
||||
// // IEEE: event_trigger
|
||||
| yP_MINUSGT expr ';'
|
||||
{ $$ = new AstFireEvent{$1, $2, false}; }
|
||||
|
@ -4180,6 +4181,7 @@ loop_variables<nodep>: // IEEE: loop_variables
|
|||
parseRefBase { $$ = $1; }
|
||||
| loop_variables ',' parseRefBase { $$ = $1->addNext($3); }
|
||||
| ',' parseRefBase { $$ = new AstEmpty{$1}; $$->addNext($2); }
|
||||
| ',' { $$ = new AstEmpty{$1}; }
|
||||
;
|
||||
|
||||
//************************************************
|
||||
|
@ -6118,6 +6120,8 @@ idArrayedForeach<nodeExprp>: // IEEE: id + select (under foreach expression)
|
|||
| idArrayed '[' expr yP_MINUSCOLON constExpr ']' { $$ = new AstSelMinus{$2, $1, $3, $5}; }
|
||||
// // IEEE: loop_variables (under foreach expression)
|
||||
// // To avoid conflicts we allow expr as first element, must post-check
|
||||
| idArrayed '[' ']'
|
||||
{ $$ = new AstSelLoopVars{$2, $1, new AstEmpty{$3}}; }
|
||||
| idArrayed '[' expr ',' loop_variables ']'
|
||||
{ $$ = new AstSelLoopVars{$2, $1, addNextNull(static_cast<AstNode*>($3), $5)}; }
|
||||
| idArrayed '[' ',' loop_variables ']'
|
||||
|
@ -6821,21 +6825,38 @@ covergroup_declaration<nodep>: // ==IEEE: covergroup_declaration
|
|||
| covergroup_declarationFront '(' tf_port_listE ')'
|
||||
/*cont*/ coverage_eventE ';' coverage_spec_or_optionListE
|
||||
/*cont*/ yENDGROUP endLabelE
|
||||
{ $$ = $1;
|
||||
{ AstFunc* const newp = new AstFunc{$<fl>1, "new", nullptr, nullptr};
|
||||
newp->classMethod(true);
|
||||
newp->isConstructor(true);
|
||||
newp->dtypep($1->dtypep());
|
||||
newp->addStmtsp($3);
|
||||
$1->addMembersp(newp);
|
||||
$$ = $1;
|
||||
GRAMMARP->endLabel($<fl>9, $1, $9); }
|
||||
// // IEEE 1800-2023 added:
|
||||
| covergroup_declarationFront yEXTENDS idAny/*covergroup_identifier*/
|
||||
/*cont*/ ';' coverage_spec_or_optionListE
|
||||
/*cont*/ yENDGROUP endLabelE
|
||||
{ $$ = $1;
|
||||
GRAMMARP->endLabel($<fl>7, $1, $7); }
|
||||
;
|
||||
|
||||
covergroup_extendsE<fl>: // IEEE: Part of covergroup_declaration
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| yEXTENDS { $$ = $1; }
|
||||
;
|
||||
|
||||
covergroup_declarationFront<classp>: // IEEE: part of covergroup_declaration
|
||||
yCOVERGROUP idAny
|
||||
{ $$ = new AstClass{$<fl>2, *$2, PARSEP->libname()};
|
||||
yCOVERGROUP covergroup_extendsE idAny
|
||||
{
|
||||
$$ = new AstClass{$<fl>3, *$3, PARSEP->libname()};
|
||||
|
||||
AstFunc* const sample = new AstFunc{$<fl>1, "sample", nullptr, nullptr};
|
||||
sample->classMethod(true);
|
||||
sample->dtypep(sample->findVoidDType());
|
||||
$$->addMembersp(sample);
|
||||
|
||||
AstFunc* const getCoverage = new AstFunc{$<fl>1, "get_coverage", nullptr, nullptr};
|
||||
getCoverage->classMethod(true);
|
||||
getCoverage->dtypep(getCoverage->findVoidDType());
|
||||
$$->addMembersp(getCoverage);
|
||||
|
||||
BBCOVERIGN($<fl>1, "Ignoring unsupported: covergroup"); }
|
||||
;
|
||||
;
|
||||
|
||||
cgexpr<nodeExprp>: // IEEE-2012: covergroup_expression, before that just expression
|
||||
expr { $$ = $1; }
|
||||
|
@ -7005,7 +7026,7 @@ cross_itemList<nodep>: // IEEE: part of list_of_cross_items
|
|||
;
|
||||
|
||||
cross_item<nodep>: // ==IEEE: cross_item
|
||||
idAny/*cover_point_identifier or variable_identifier*/ { $$ = nullptr; /*UNSUP*/ }
|
||||
idDotted/*cover_point_identifier or variable_identifier*/ { $1->deleteTree(); $$ = nullptr; /*UNSUP*/ }
|
||||
;
|
||||
|
||||
cross_body<nodep>: // ==IEEE: cross_body
|
||||
|
@ -7551,7 +7572,12 @@ class_item<nodep>: // ==IEEE: class_item
|
|||
| class_declaration { $$ = $1; }
|
||||
| timeunits_declaration { $$ = $1; }
|
||||
| covergroup_declaration
|
||||
{ $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup within class"); }
|
||||
{
|
||||
const string cgName = $1->name();
|
||||
$1->name("__vlAnonCG_" + cgName);
|
||||
AstVar* const newp = new AstVar{$<fl>1, VVarType::VAR, cgName, VFlagChildDType{}, new AstRefDType($<fl>1, $1->name())};
|
||||
$$ = addNextNull($1, newp);
|
||||
}
|
||||
// // local_parameter_declaration under parameter_declaration
|
||||
| parameter_declaration ';' { $$ = $1; }
|
||||
| ';' { $$ = nullptr; }
|
||||
|
@ -8089,8 +8115,10 @@ vltDModule<strp>: // --module <arg>
|
|||
;
|
||||
|
||||
vltDModuleE<strp>: // [--module <arg>]
|
||||
/* empty */ { static string unit = "__024unit"; $$ = &unit; }
|
||||
| vltDModule { $$ = $1; }
|
||||
/* empty */
|
||||
{ static string unit = "$unit"; $$ = &unit; } // .vlt uses prettyName
|
||||
| vltDModule
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
vltDScope<strp>: // --scope <arg>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,88 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Petr Nohavica
|
||||
// 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);
|
||||
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
|
||||
interface class IBottomMid;
|
||||
pure virtual function void moo(int i);
|
||||
endclass
|
||||
|
||||
interface class IBottom;
|
||||
pure virtual function bit foo();
|
||||
endclass
|
||||
|
||||
interface class IMid extends IBottomMid;
|
||||
pure virtual function string bar();
|
||||
endclass
|
||||
|
||||
class bottom_class implements IBottom;
|
||||
string name;
|
||||
|
||||
function new(string name);
|
||||
this.name = name;
|
||||
endfunction
|
||||
|
||||
virtual function bit foo();
|
||||
$display("%s", name);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class middle_class extends bottom_class implements IMid, IBottom;
|
||||
function new(string name);
|
||||
super.new($sformatf("middle %0s", name));
|
||||
endfunction
|
||||
|
||||
virtual function bit foo();
|
||||
$display("%s", name);
|
||||
return 0;
|
||||
endfunction
|
||||
|
||||
virtual function void moo(int i);
|
||||
$display("moo: %d", i);
|
||||
endfunction
|
||||
|
||||
virtual function string bar();
|
||||
return name;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class top_class extends middle_class;
|
||||
int i;
|
||||
function new(string name, int i);
|
||||
super.new($sformatf("%0s %0d", name, i));
|
||||
this.i = i;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class sky_class extends top_class;
|
||||
function new(string name);
|
||||
super.new(name, 42);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
|
||||
module t;
|
||||
|
||||
initial begin
|
||||
sky_class s = new("ahoj");
|
||||
bottom_class b = s;
|
||||
top_class t = s;
|
||||
IMid im;
|
||||
|
||||
`checks( b.name, "middle ahoj 42" );
|
||||
`checks( s.name, "middle ahoj 42" );
|
||||
`checks( t.name, "middle ahoj 42" );
|
||||
`checkh( t.i, 42);
|
||||
`checks(s.bar(), "middle ahoj 42");
|
||||
im = s;
|
||||
im.moo(42);
|
||||
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,26 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Class1 #(type T);
|
||||
static function int get_p();
|
||||
return 7;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Class2 #(type T) extends Class1 #(T);
|
||||
static function int get_p2;
|
||||
return T::get_p();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
typedef Class2#(Class1#(int)) Class;
|
||||
if (Class::get_p2() != Class1#(int)::get_p()) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,26 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Class1 #(type T);
|
||||
static function int get();
|
||||
return T::Helper::getter();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Class2;
|
||||
typedef Class2 Helper;
|
||||
static function int getter();
|
||||
return 13;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
if (Class1#(Class2)::get() != 13) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,19 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t;
|
||||
covergroup cgArgs(int var1, int var2=42);
|
||||
|
||||
endgroup
|
||||
|
||||
cgArgs cov1 = new(69, 77);
|
||||
cgArgs cov2 = new(69);
|
||||
function x();
|
||||
cov1.sample();
|
||||
cov2.get_coverage();
|
||||
endfunction;
|
||||
endmodule
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,39 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t;
|
||||
class base;
|
||||
enum {red, green, blue} color;
|
||||
covergroup g1 (bit [3:0] a) with function sample(bit b);
|
||||
option.weight = 10;
|
||||
option.per_instance = 1;
|
||||
coverpoint a;
|
||||
coverpoint b;
|
||||
c: coverpoint color;
|
||||
endgroup
|
||||
function new();
|
||||
g1 = new(0);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class derived extends base;
|
||||
bit d;
|
||||
covergroup extends g1;
|
||||
option.weight = 1; // overrides the weight from base g1
|
||||
// uses per_instance = 1 from base g1
|
||||
c: coverpoint color // overrides the c coverpoint in base g1
|
||||
{
|
||||
ignore_bins ignore = {blue};
|
||||
}
|
||||
coverpoint d; // adds new coverpoint
|
||||
cross a, d; // crosses new coverpoint with inherited one
|
||||
endgroup :g1
|
||||
function new();
|
||||
super.new();
|
||||
endfunction
|
||||
endclass
|
||||
endmodule
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,39 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t;
|
||||
class base;
|
||||
function new();
|
||||
g1 = new(0);
|
||||
endfunction
|
||||
enum {red, green, blue} color;
|
||||
covergroup g1 (bit [3:0] a) with function sample(bit b);
|
||||
option.weight = 10;
|
||||
option.per_instance = 1;
|
||||
coverpoint a;
|
||||
coverpoint b;
|
||||
c: coverpoint color;
|
||||
endgroup
|
||||
endclass
|
||||
|
||||
class derived extends base;
|
||||
bit d;
|
||||
function new();
|
||||
super.new();
|
||||
endfunction
|
||||
covergroup extends g1;
|
||||
option.weight = 1; // overrides the weight from base g1
|
||||
// uses per_instance = 1 from base g1
|
||||
c: coverpoint color // overrides the c coverpoint in base g1
|
||||
{
|
||||
ignore_bins ignore = {blue};
|
||||
}
|
||||
coverpoint d; // adds new coverpoint
|
||||
cross a, d; // crosses new coverpoint with inherited one
|
||||
endgroup :g1
|
||||
endclass
|
||||
endmodule
|
|
@ -0,0 +1,5 @@
|
|||
%Error: t/t_covergroup_func_override_bad.v:10:5: syntax error, unexpected function
|
||||
10 | function sample();
|
||||
| ^~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
if test.vlt_all:
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
else:
|
||||
test.compile(nc_flags2=["-coverage", "functional"])
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,17 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t();
|
||||
covergroup cg;
|
||||
function sample();
|
||||
|
||||
endfunction
|
||||
function get_coverage();
|
||||
|
||||
endfunction
|
||||
endgroup
|
||||
endmodule
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,18 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
class myClass;
|
||||
covergroup embeddedCg;
|
||||
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
embeddedCg = new();
|
||||
embeddedCg.sample();
|
||||
embeddedCg.get_coverage();
|
||||
endfunction
|
||||
endclass
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,30 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
class myClass;
|
||||
covergroup embeddedCg;
|
||||
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
embeddedCg = new();
|
||||
embeddedCg.sample();
|
||||
embeddedCg.get_coverage();
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class secondClass;
|
||||
covergroup embeddedCg;
|
||||
|
||||
endgroup
|
||||
|
||||
function new();
|
||||
embeddedCg = new();
|
||||
embeddedCg.sample();
|
||||
embeddedCg.get_coverage();
|
||||
endfunction
|
||||
endclass
|
|
@ -0,0 +1,8 @@
|
|||
%Error: t/t_covergroup_in_class_duplicate_bad.v:13:16: Duplicate declaration of CLASS '__vlAnonCG_embeddedCg': '__vlAnonCG_embeddedCg'
|
||||
13 | covergroup embeddedCg;
|
||||
| ^~~~~~~~~~
|
||||
t/t_covergroup_in_class_duplicate_bad.v:9:16: ... Location of original declaration
|
||||
9 | covergroup embeddedCg;
|
||||
| ^~~~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,16 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
class myClass;
|
||||
covergroup embeddedCg;
|
||||
|
||||
endgroup
|
||||
|
||||
covergroup embeddedCg;
|
||||
|
||||
endgroup
|
||||
endclass
|
|
@ -0,0 +1,5 @@
|
|||
%Error: t/t_covergroup_new_override_bad.v:10:5: syntax error, unexpected function
|
||||
10 | function new();
|
||||
| ^~~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Exiting due to
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
if test.vlt_all:
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
else:
|
||||
test.compile(nc_flags2=["-coverage", "functional"])
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,14 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
/* verilator lint_off COVERIGN */
|
||||
module t();
|
||||
covergroup cg;
|
||||
function new();
|
||||
|
||||
endfunction
|
||||
endgroup
|
||||
endmodule
|
|
@ -385,28 +385,21 @@
|
|||
136 | cross a, b {
|
||||
| ^~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:154:4: Ignoring unsupported: covergroup
|
||||
154 | covergroup cg_more extends cg_empty;
|
||||
154 | covergroup cgArgs(int cg_lim);
|
||||
| ^~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:157:4: Ignoring unsupported: covergroup
|
||||
157 | covergroup cg_args(int cg_lim);
|
||||
| ^~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:164:7: Ignoring unsupported: covergroup
|
||||
164 | covergroup cov1 @m_z;
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:161:7: Ignoring unsupported: covergroup
|
||||
161 | covergroup cov1 @m_z;
|
||||
| ^~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:164:23: Ignoring unsupported: coverage clocking event
|
||||
164 | covergroup cov1 @m_z;
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:161:23: Ignoring unsupported: coverage clocking event
|
||||
161 | covergroup cov1 @m_z;
|
||||
| ^
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:165:10: Ignoring unsupported: coverpoint
|
||||
165 | coverpoint m_x;
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:162:10: Ignoring unsupported: coverpoint
|
||||
162 | coverpoint m_x;
|
||||
| ^~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:166:10: Ignoring unsupported: coverpoint
|
||||
166 | coverpoint m_y;
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:163:10: Ignoring unsupported: coverpoint
|
||||
163 | coverpoint m_y;
|
||||
| ^~~~~~~~~~
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:164:18: Ignoring unsupported: covergroup within class
|
||||
164 | covergroup cov1 @m_z;
|
||||
| ^~~~
|
||||
%Error: t/t_covergroup_unsup.v:169:23: Can't find definition of variable: 'cov1'
|
||||
169 | function new(); cov1 = new; endfunction
|
||||
| ^~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Warning-COVERIGN: t/t_covergroup_unsup.v:171:7: Ignoring unsupported: covergroup
|
||||
171 | covergroup extends cg_empty;
|
||||
| ^~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
|
@ -151,10 +151,7 @@ module t (/*AUTOARG*/
|
|||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_more extends cg_empty;
|
||||
endgroup
|
||||
|
||||
covergroup cg_args(int cg_lim);
|
||||
covergroup cgArgs(int cg_lim);
|
||||
endgroup
|
||||
|
||||
class CgCls;
|
||||
|
@ -170,10 +167,15 @@ module t (/*AUTOARG*/
|
|||
`endif
|
||||
endclass
|
||||
|
||||
class CgEmb;
|
||||
covergroup extends cg_empty;
|
||||
endgroup
|
||||
endclass
|
||||
|
||||
always @(posedge clk) begin
|
||||
cg_more cov1 = new;
|
||||
cg_empty cov1 = new;
|
||||
`ifndef T_COVERGROUP_UNSUP_IGN
|
||||
cg_args cov2 = new(2);
|
||||
cgArgs cov2 = new(2);
|
||||
`endif
|
||||
if (cyc == 10) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
|
|
|
@ -74,6 +74,8 @@ module Vt_debug_emitv_t;
|
|||
if (the_ifaces[2].ifsig) begin
|
||||
$write("");
|
||||
end
|
||||
#64'h1;
|
||||
$write("After #1 delay");
|
||||
end
|
||||
end
|
||||
bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15];
|
||||
|
|
|
@ -14,7 +14,7 @@ test.scenarios("vlt")
|
|||
test.lint(
|
||||
# We also have dump-tree turned on, so hit a lot of AstNode*::dump() functions
|
||||
# Likewise XML
|
||||
v_flags=["--lint-only --dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv"])
|
||||
v_flags=["--lint-only --dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv --timing"])
|
||||
|
||||
output_vs = test.glob_some(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v")
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ module t (/*AUTOARG*/
|
|||
if (|downto_32[55+:3]) $write("");
|
||||
if (|downto_32[60-:7]) $write("");
|
||||
if (the_ifaces[2].ifsig) $write("");
|
||||
#1 $write("After #1 delay");
|
||||
end
|
||||
|
||||
bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15];
|
||||
|
|
|
@ -15,6 +15,6 @@ test.top_filename = "t/t_debug_emitv.v"
|
|||
test.lint(
|
||||
# We also have dump-tree turned on, so hit a lot of AstNode*::dump() functions
|
||||
# Likewise XML
|
||||
v_flags=["--lint-only --dumpi-tree 9 --dump-tree-addrids"])
|
||||
v_flags=["--lint-only --dumpi-tree 9 --dump-tree-addrids --timing"])
|
||||
|
||||
test.passes()
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// DESCRIPTION: Verilator: DFG optimizer equivalence testing
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
#include <verilated.h>
|
||||
#include <verilated_cov.h>
|
||||
|
||||
#include <Vopt.h>
|
||||
#include <Vref.h>
|
||||
#include <iostream>
|
||||
|
||||
void rngUpdate(uint64_t& x) {
|
||||
x ^= x << 13;
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
// Create contexts
|
||||
VerilatedContext ctx;
|
||||
|
||||
// Create models
|
||||
Vref ref{&ctx};
|
||||
Vopt opt{&ctx};
|
||||
|
||||
uint64_t rand_a = 0x5aef0c8dd70a4497;
|
||||
uint64_t rand_b = 0xf0c0a8dd75ae4497;
|
||||
uint64_t srand_a = 0x00fa8dcc7ae4957;
|
||||
uint64_t srand_b = 0x0fa8dc7ae3c9574;
|
||||
|
||||
for (size_t n = 0; n < 200000; ++n) {
|
||||
// Update rngs
|
||||
rngUpdate(rand_a);
|
||||
rngUpdate(rand_b);
|
||||
rngUpdate(srand_a);
|
||||
rngUpdate(srand_b);
|
||||
|
||||
// Assign inputs
|
||||
ref.rand_a = opt.rand_a = rand_a;
|
||||
ref.rand_b = opt.rand_b = rand_b;
|
||||
ref.srand_a = opt.srand_a = srand_a;
|
||||
ref.srand_b = opt.srand_b = srand_b;
|
||||
|
||||
// Evaluate both models
|
||||
ref.eval();
|
||||
opt.eval();
|
||||
|
||||
// Check equivalence
|
||||
#include "checks.h"
|
||||
|
||||
// increment time
|
||||
ctx.timeInc(1);
|
||||
}
|
||||
|
||||
std::cout << "*-* All Finished *-*\n";
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt_all')
|
||||
test.sim_time = 2000000
|
||||
|
||||
root = ".."
|
||||
|
||||
if not os.path.exists(root + "/.git"):
|
||||
test.skip("Not in a git repository")
|
||||
|
||||
# Read expected source lines hit
|
||||
expectedLines = set()
|
||||
|
||||
with open(root + "/src/V3DfgBreakCycles.cpp", 'r', encoding="utf8") as fd:
|
||||
for lineno, line in enumerate(fd, 1):
|
||||
line = line.split("//")[0]
|
||||
if re.match(r'^[^#]*SET_RESULT', line):
|
||||
expectedLines.add(lineno)
|
||||
if re.match(r'^[^#]*MASK', line):
|
||||
expectedLines.add(lineno)
|
||||
|
||||
if not expectedLines:
|
||||
test.error("Failed to read expected source line numbers")
|
||||
|
||||
# Generate the equivalence checks and declaration boilerplate
|
||||
rdFile = test.top_filename
|
||||
plistFile = test.obj_dir + "/portlist.vh"
|
||||
pdeclFile = test.obj_dir + "/portdecl.vh"
|
||||
checkFile = test.obj_dir + "/checks.h"
|
||||
nExpectedCycles = 0
|
||||
with open(rdFile, 'r', encoding="utf8") as rdFh, \
|
||||
open(plistFile, 'w', encoding="utf8") as plistFh, \
|
||||
open(pdeclFile, 'w', encoding="utf8") as pdeclFh, \
|
||||
open(checkFile, 'w', encoding="utf8") as checkFh:
|
||||
for line in rdFh:
|
||||
line = line.split("//")[0]
|
||||
m = re.search(r'`signal\((\w+),', line)
|
||||
if not m:
|
||||
continue
|
||||
nExpectedCycles += 1
|
||||
sig = m.group(1)
|
||||
plistFh.write(sig + ",\n")
|
||||
pdeclFh.write("output " + sig + ";\n")
|
||||
checkFh.write("if (ref." + sig + " != opt." + sig + ") {\n")
|
||||
checkFh.write(" std::cout << \"Mismatched " + sig + "\" << std::endl;\n")
|
||||
checkFh.write(" std::cout << \"Ref: 0x\" << std::hex << (ref." + sig +
|
||||
" + 0) << std::endl;\n")
|
||||
checkFh.write(" std::cout << \"Opt: 0x\" << std::hex << (opt." + sig +
|
||||
" + 0) << std::endl;\n")
|
||||
checkFh.write(" std::exit(1);\n")
|
||||
checkFh.write("}\n")
|
||||
|
||||
# Compile un-optimized
|
||||
test.compile(verilator_flags2=[
|
||||
"--stats",
|
||||
"--build",
|
||||
"-fno-dfg-break-cycles",
|
||||
"-fno-dfg-post-inline",
|
||||
"-fno-dfg-scoped",
|
||||
"+incdir+" + test.obj_dir,
|
||||
"-Mdir", test.obj_dir + "/obj_ref",
|
||||
"--prefix", "Vref",
|
||||
"-Wno-UNOPTFLAT"
|
||||
]) # yapf:disable
|
||||
|
||||
# Check we got the expected number of circular logic warnings
|
||||
test.file_grep(test.obj_dir + "/obj_ref/Vref__stats.txt",
|
||||
r'Warnings, Suppressed UNOPTFLAT\s+(\d+)', nExpectedCycles)
|
||||
|
||||
# Compile optimized - also builds executable
|
||||
test.compile(verilator_flags2=[
|
||||
"--stats",
|
||||
"--build",
|
||||
"-fno-dfg-post-inline",
|
||||
"-fno-dfg-scoped",
|
||||
"--exe",
|
||||
"+incdir+" + test.obj_dir,
|
||||
"-Mdir", test.obj_dir + "/obj_opt",
|
||||
"--prefix", "Vopt",
|
||||
"-Werror-UNOPTFLAT",
|
||||
"--dumpi-V3DfgBreakCycles", "9", # To fill code coverage
|
||||
"-CFLAGS \"-I .. -I ../obj_ref\"",
|
||||
"../obj_ref/Vref__ALL.a",
|
||||
"../../t/" + test.name + ".cpp"
|
||||
]) # yapf:disable
|
||||
|
||||
# Check all source lines hit
|
||||
coveredLines = set()
|
||||
|
||||
|
||||
def readCovered(fileName):
|
||||
with open(fileName, 'r', encoding="utf8") as fd:
|
||||
for line in fd:
|
||||
coveredLines.add(int(line.strip()))
|
||||
|
||||
|
||||
readCovered(test.obj_dir + "/obj_opt/Vopt__V3DfgBreakCycles-TraceDriver-line-coverage.txt")
|
||||
readCovered(test.obj_dir + "/obj_opt/Vopt__V3DfgBreakCycles-IndependentBits-line-coverage.txt")
|
||||
|
||||
if coveredLines != expectedLines:
|
||||
for n in sorted(expectedLines - coveredLines):
|
||||
test.error_keep_going(f"V3DfgBreakCycles.cpp line {n} not covered")
|
||||
for n in sorted(coveredLines - expectedLines):
|
||||
test.error_keep_going(f"V3DfgBreakCycles.cpp line {n} covered but not expected")
|
||||
|
||||
# Execute test to check equivalence
|
||||
test.execute(executable=test.obj_dir + "/obj_opt/Vopt")
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,114 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Geza Lore.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`define signal(name, width) wire [width-1:0] name;
|
||||
|
||||
module t (
|
||||
`include "portlist.vh" // Boilerplate generated by t_dfg_break_cycles.py
|
||||
rand_a, rand_b, srand_a, srand_b
|
||||
);
|
||||
|
||||
`include "portdecl.vh" // Boilerplate generated by t_dfg_break_cycles.py
|
||||
|
||||
input rand_a;
|
||||
input rand_b;
|
||||
input srand_a;
|
||||
input srand_b;
|
||||
wire logic [63:0] rand_a;
|
||||
wire logic [63:0] rand_b;
|
||||
wire logic signed [63:0] srand_a;
|
||||
wire logic signed [63:0] srand_b;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Interesting user code to cover
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
`signal(GRAY_SEL, 3);
|
||||
assign GRAY_SEL = rand_a[2:0] ^ 3'(GRAY_SEL[2:1]);
|
||||
|
||||
`signal(GRAY_SHIFT, 3);
|
||||
assign GRAY_SHIFT = rand_a[2:0] ^ (GRAY_SHIFT >> 1);
|
||||
|
||||
`signal(GRAY_REV_SEL, 3);
|
||||
assign GRAY_REV_SEL = rand_a[2:0] ^ {GRAY_REV_SEL[1:0], 1'b0};
|
||||
|
||||
`signal(GRAY_REV_SHIFT, 3);
|
||||
assign GRAY_REV_SHIFT = rand_a[2:0] ^ (GRAY_REV_SHIFT << 1);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Fill coverage
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
`signal(CONCAT_RHS, 2);
|
||||
assign CONCAT_RHS[0] = rand_a[0];
|
||||
assign CONCAT_RHS[1] = CONCAT_RHS[0];
|
||||
|
||||
`signal(CONCAT_LHS, 2);
|
||||
assign CONCAT_LHS[0] = CONCAT_LHS[1];
|
||||
assign CONCAT_LHS[1] = rand_a[1];
|
||||
|
||||
`signal(CONCAT_MID, 3);
|
||||
assign CONCAT_MID[0] = |CONCAT_MID[2:1];
|
||||
assign CONCAT_MID[2:1] = {rand_a[2], ~rand_a[2]};
|
||||
|
||||
`signal(SEL, 3);
|
||||
assign SEL[0] = rand_a[4];
|
||||
assign SEL[1] = SEL[0];
|
||||
assign SEL[2] = SEL[1];
|
||||
|
||||
`signal(EXTEND, 8);
|
||||
assign EXTEND[0] = rand_a[3];
|
||||
assign EXTEND[3:1] = 3'(EXTEND[0]);
|
||||
assign EXTEND[4] = EXTEND[1];
|
||||
assign EXTEND[6:5] = EXTEND[2:1];
|
||||
assign EXTEND[7] = EXTEND[3];
|
||||
|
||||
`signal(NOT, 3);
|
||||
assign NOT = ~(rand_a[2:0] ^ 3'(NOT[2:1]));
|
||||
|
||||
`signal(AND, 3);
|
||||
assign AND = rand_a[2:0] & 3'(AND[2:1]);
|
||||
|
||||
`signal(OR, 3);
|
||||
assign OR = rand_a[2:0] | 3'(OR[2:1]);
|
||||
|
||||
`signal(SHIFTR, 14);
|
||||
assign SHIFTR = {
|
||||
SHIFTR[6:5], // 13:12
|
||||
SHIFTR[7:6], // 11:10
|
||||
SHIFTR[5:4], // 9:8
|
||||
SHIFTR[3:0] >> 2, // 7:4
|
||||
rand_a[3:0] // 3:0
|
||||
};
|
||||
|
||||
`signal(SHIFTR_VARIABLE, 2);
|
||||
assign SHIFTR_VARIABLE = rand_a[1:0] ^ ({1'b0, SHIFTR_VARIABLE[1]} >> rand_b[0]);
|
||||
|
||||
`signal(SHIFTL, 14);
|
||||
assign SHIFTL = {
|
||||
SHIFTL[6:5], // 13:12
|
||||
SHIFTL[7:6], // 11:10
|
||||
SHIFTL[5:4], // 9:8
|
||||
SHIFTL[3:0] << 2, // 7:4
|
||||
rand_a[3:0] // 3:0
|
||||
};
|
||||
|
||||
`signal(SHIFTL_VARIABLE, 2);
|
||||
assign SHIFTL_VARIABLE = rand_a[1:0] ^ ({SHIFTL_VARIABLE[0], 1'b0} << rand_b[0]);
|
||||
|
||||
`signal(VAR_A, 2);
|
||||
wire logic [1:0] VAR_B;
|
||||
assign VAR_A = {rand_a[0], VAR_B[0]};
|
||||
assign VAR_B = (VAR_A >> 1) ^ 2'(VAR_B[1]);
|
||||
|
||||
`signal(REPLICATE, 4);
|
||||
assign REPLICATE = rand_a[3:0] ^ ({2{REPLICATE[3:2]}} >> 2);
|
||||
|
||||
`signal(PARTIAL, 4);
|
||||
assign PARTIAL[0] = rand_a[0];
|
||||
// PARTIAL[1] intentionally unconnected
|
||||
assign PARTIAL[3:2] = rand_a[3:2] ^ {PARTIAL[2], PARTIAL[0]};
|
||||
endmodule
|
|
@ -0,0 +1,9 @@
|
|||
%Warning-UNOPTFLAT: t/t_dfg_true_cycle_bad.v:10:23: Signal unoptimizable: Circular combinational logic: 'o'
|
||||
10 | output wire [9:0] o
|
||||
| ^
|
||||
... For warning description see https://verilator.org/warn/UNOPTFLAT?v=latest
|
||||
... Use "/* verilator lint_off UNOPTFLAT */" and lint_on around source to disable this message.
|
||||
t/t_dfg_true_cycle_bad.v:10:23: Example path: o
|
||||
t/t_dfg_true_cycle_bad.v:12:22: Example path: ASSIGNW
|
||||
t/t_dfg_true_cycle_bad.v:10:23: Example path: o
|
||||
%Error: Exiting due to
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,16 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Geza Lore.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module t(
|
||||
output wire [9:0] o
|
||||
);
|
||||
assign o[1:0] = o[9:8];
|
||||
assign o[3:2] = {o[0], o[1]};
|
||||
assign o[7:4] = 4'(o[3:2]);
|
||||
assign o[9:8] = o[5:4];
|
||||
endmodule
|
|
@ -1,4 +1,4 @@
|
|||
%Error-UNSUPPORTED: t/t_disable.v:11:10: Unsupported: disabling fork by name
|
||||
%Error-UNSUPPORTED: t/t_disable.v:11:10: Unsupported: disabling fork from within same fork
|
||||
11 | disable foo;
|
||||
| ^~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
%Error: t/t_disable_bad.v:9:15: Can't find definition of block/task: 'abcd'
|
||||
9 | disable abcd;
|
||||
| ^~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: Internal Error: t/t_disable_bad.v:9:7: ../V3LinkJump.cpp:#: Unlinked disable statement
|
||||
9 | disable abcd;
|
||||
| ^~~~~~~
|
||||
... This fatal error may be caused by the earlier error(s); resolve those first.
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,11 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
initial begin
|
||||
disable abcd;
|
||||
end
|
||||
endmodule: t
|
|
@ -0,0 +1,5 @@
|
|||
%Error-UNSUPPORTED: t/t_disable_empty.v:12:7: disable isn't underneath a begin with name: 'block'
|
||||
12 | disable block;
|
||||
| ^~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,17 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t(/*AUTOARG*/);
|
||||
|
||||
initial begin
|
||||
if (0) begin : block
|
||||
end
|
||||
disable block;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(timing_loop=True, verilator_flags2=["--timing"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,28 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
initial begin
|
||||
begin : blk
|
||||
int x = 0;
|
||||
fork : fork_blk
|
||||
begin
|
||||
end
|
||||
begin
|
||||
x = 1;
|
||||
#2;
|
||||
x = 2;
|
||||
end
|
||||
join_none
|
||||
#1;
|
||||
disable fork_blk;
|
||||
#2;
|
||||
if (x != 1) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
|
@ -0,0 +1,5 @@
|
|||
%Error-UNSUPPORTED: t/t_disable_func_bad.v:19:13: Node of type FUNCREF 'increment_x' referenced by disable
|
||||
19 | #1 disable increment_x;
|
||||
| ^~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(verilator_flags2=['--lint-only --timing'],
|
||||
fails=True,
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,23 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
int x = 0;
|
||||
|
||||
function int increment_x;
|
||||
x++;
|
||||
return x;
|
||||
endfunction
|
||||
|
||||
module t(/*AUTOARG*/);
|
||||
|
||||
initial begin
|
||||
fork
|
||||
increment_x();
|
||||
#1 disable increment_x;
|
||||
join
|
||||
end
|
||||
|
||||
endmodule
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue