Merge 42ff4772f7 into 5ead758f28
This commit is contained in:
commit
dfac5bf414
|
|
@ -266,10 +266,16 @@ The grammar of control commands is as follows:
|
||||||
|
|
||||||
.. option:: public [-module "<modulename>"] [-task/-function "<taskname>"] [-var "<signame>"]
|
.. option:: public [-module "<modulename>"] [-task/-function "<taskname>"] [-var "<signame>"]
|
||||||
|
|
||||||
|
.. option:: public_flat -path "<hier.path.signame>"
|
||||||
|
|
||||||
.. option:: public_flat [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"]
|
.. option:: public_flat [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"]
|
||||||
|
|
||||||
|
.. option:: public_flat_rd -path "<hier.path.signame>"
|
||||||
|
|
||||||
.. option:: public_flat_rd [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"]
|
.. option:: public_flat_rd [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"]
|
||||||
|
|
||||||
|
.. option:: public_flat_rw -path "<hier.path.signame>"
|
||||||
|
|
||||||
.. option:: public_flat_rw [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"] ["@(edge)"]
|
.. option:: public_flat_rw [-module "<modulename>"] [-task/-function "<taskname>"] [(-param | -port | -var) "<signame>"] ["@(edge)"]
|
||||||
|
|
||||||
Sets the specified signal to be public. Same as
|
Sets the specified signal to be public. Same as
|
||||||
|
|
@ -283,6 +289,23 @@ The grammar of control commands is as follows:
|
||||||
following ``<signame>`` can contain ``*`` and ``?`` wildcard
|
following ``<signame>`` can contain ``*`` and ``?`` wildcard
|
||||||
characters that match any substring or any single character respectively.
|
characters that match any substring or any single character respectively.
|
||||||
|
|
||||||
|
For ``public_flat``, ``public_flat_rd``, ``public_flat_rw``, and
|
||||||
|
``public``, ``-path`` may instead be given a fully qualified
|
||||||
|
hierarchical path (e.g. ``-path "top.i_sub.signame"``).
|
||||||
|
Verilator then walks the design hierarchy to find the named signal.
|
||||||
|
Wildcards are not supported in hierarchical paths. A ``\`` introducing a
|
||||||
|
Verilog escaped identifier must be written in the quoted string as ``\\``
|
||||||
|
and have trailing whitespace, for example
|
||||||
|
``-path "top.\\inst.with.dot .\\sig.with.dot "``.
|
||||||
|
|
||||||
|
When a hierarchical ``-path`` traverses an arrayed instance (e.g.
|
||||||
|
``sub i_sub [3:0] ();``), per-iteration selectivity is not currently
|
||||||
|
supported. Naming a single iteration such as
|
||||||
|
``-path "top.i_sub[2].signame"`` marks the signal as public in **all**
|
||||||
|
iterations of that cell array. Non-arrayed sibling instantiations
|
||||||
|
(e.g. ``sub i_a(); sub i_b();``) and generate-for iterations do support
|
||||||
|
per-instance selectivity.
|
||||||
|
|
||||||
.. option:: sc_biguint -module "<modulename>" -var "<signame>"
|
.. option:: sc_biguint -module "<modulename>" -var "<signame>"
|
||||||
|
|
||||||
Sets the input/output signal to be of ``sc_biguint<{width}>`` type.
|
Sets the input/output signal to be of ``sc_biguint<{width}>`` type.
|
||||||
|
|
|
||||||
|
|
@ -2146,6 +2146,7 @@ class AstVar final : public AstNode {
|
||||||
bool m_sigModPublic : 1; // User C code accesses this signal and module
|
bool m_sigModPublic : 1; // User C code accesses this signal and module
|
||||||
bool m_sigUserRdPublic : 1; // User C code accesses this signal, read only
|
bool m_sigUserRdPublic : 1; // User C code accesses this signal, read only
|
||||||
bool m_sigUserRWPublic : 1; // User C code accesses this signal, read-write
|
bool m_sigUserRWPublic : 1; // User C code accesses this signal, read-write
|
||||||
|
bool m_mayBecomePublic : 1; // Pre-V3Begin: candidate for hier-var-driven public flag
|
||||||
bool m_usedParam : 1; // Parameter is referenced (on link; later signals not setup)
|
bool m_usedParam : 1; // Parameter is referenced (on link; later signals not setup)
|
||||||
bool m_usedLoopIdx : 1; // Variable subject of for unrolling
|
bool m_usedLoopIdx : 1; // Variable subject of for unrolling
|
||||||
bool m_funcLocal : 1; // Local variable for a function
|
bool m_funcLocal : 1; // Local variable for a function
|
||||||
|
|
@ -2212,6 +2213,7 @@ class AstVar final : public AstNode {
|
||||||
m_sigModPublic = false;
|
m_sigModPublic = false;
|
||||||
m_sigUserRdPublic = false;
|
m_sigUserRdPublic = false;
|
||||||
m_sigUserRWPublic = false;
|
m_sigUserRWPublic = false;
|
||||||
|
m_mayBecomePublic = false;
|
||||||
m_funcLocal = false;
|
m_funcLocal = false;
|
||||||
m_funcLocalSticky = false;
|
m_funcLocalSticky = false;
|
||||||
m_funcReturn = false;
|
m_funcReturn = false;
|
||||||
|
|
@ -2388,6 +2390,8 @@ public:
|
||||||
m_sigUserRWPublic = flag;
|
m_sigUserRWPublic = flag;
|
||||||
if (flag) sigUserRdPublic(true);
|
if (flag) sigUserRdPublic(true);
|
||||||
}
|
}
|
||||||
|
bool mayBecomePublic() const { return m_mayBecomePublic; }
|
||||||
|
void mayBecomePublic(bool flag) { m_mayBecomePublic = flag; }
|
||||||
void sc(bool flag) { m_sc = flag; }
|
void sc(bool flag) { m_sc = flag; }
|
||||||
void scSensitive(bool flag) { m_scSensitive = flag; }
|
void scSensitive(bool flag) { m_scSensitive = flag; }
|
||||||
void primaryIO(bool flag) { m_primaryIO = flag; }
|
void primaryIO(bool flag) { m_primaryIO = flag; }
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,18 @@
|
||||||
|
|
||||||
#include "V3Control.h"
|
#include "V3Control.h"
|
||||||
|
|
||||||
|
#include "V3Hash.h"
|
||||||
#include "V3InstrCount.h"
|
#include "V3InstrCount.h"
|
||||||
#include "V3String.h"
|
#include "V3String.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
|
@ -791,11 +797,54 @@ public:
|
||||||
FileLine* flp() const { return m_flp; }
|
FileLine* flp() const { return m_flp; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Deferred application of a public attribute given by hierarchical -path
|
||||||
|
enum class V3ControlHierKind : uint8_t {
|
||||||
|
UNKNOWN, // Not yet classified
|
||||||
|
CELL,
|
||||||
|
BLOCK
|
||||||
|
};
|
||||||
|
struct V3ControlHierSegment final {
|
||||||
|
std::string m_name; // Identifier without trailing array index(es)
|
||||||
|
std::vector<int> m_indices; // Trailing array indices
|
||||||
|
V3ControlHierKind m_kind = V3ControlHierKind::UNKNOWN;
|
||||||
|
};
|
||||||
|
struct V3ControlHierVarEntry final {
|
||||||
|
FileLine* m_fl;
|
||||||
|
std::string m_path; // Full path
|
||||||
|
VAttrType m_attr;
|
||||||
|
std::string m_topName; // First segment: top module name
|
||||||
|
std::vector<V3ControlHierSegment> m_middle; // Intermediate cells/blocks
|
||||||
|
std::string m_leafName; // Final segment: variable name
|
||||||
|
V3ControlHierVarEntry(FileLine* fl, const std::string& path, VAttrType attr)
|
||||||
|
: m_fl{fl}
|
||||||
|
, m_path{path}
|
||||||
|
, m_attr{attr} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prefix tree over all -path entries
|
||||||
|
struct V3ControlHierTreeNode final {
|
||||||
|
V3ControlHierKind m_kind = V3ControlHierKind::UNKNOWN; // Kind of this node's segment
|
||||||
|
// Public'ed vars directly in this scope: (leaf var pretty name, attribute).
|
||||||
|
std::vector<std::pair<std::string, VAttrType>> m_vars;
|
||||||
|
// Child scopes keyed by pretty segment text.
|
||||||
|
std::map<std::string, std::unique_ptr<V3ControlHierTreeNode>> m_children;
|
||||||
|
|
||||||
|
V3ControlHierTreeNode* getOrAddChild(const std::string& key) {
|
||||||
|
std::unique_ptr<V3ControlHierTreeNode>& slot = m_children[key];
|
||||||
|
if (!slot) slot = std::make_unique<V3ControlHierTreeNode>();
|
||||||
|
return slot.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class V3ControlResolver final {
|
class V3ControlResolver final {
|
||||||
enum ProfileDataMode : uint8_t { NONE = 0, MTASK = 1, HIER_DPI = 2 };
|
enum ProfileDataMode : uint8_t { NONE = 0, MTASK = 1, HIER_DPI = 2 };
|
||||||
V3ControlModuleResolver m_modules; // Access to module names (with wildcards)
|
V3ControlModuleResolver m_modules; // Access to module names (with wildcards)
|
||||||
V3ControlFileResolver m_files; // Access to file names (with wildcards)
|
V3ControlFileResolver m_files; // Access to file names (with wildcards)
|
||||||
V3ControlScopeTraceResolver m_scopeTraces; // Regexp to trace enables
|
V3ControlScopeTraceResolver m_scopeTraces; // Regexp to trace enables
|
||||||
|
std::vector<V3ControlHierVarEntry> m_hierVarAttrs; // -path public attrs
|
||||||
|
V3ControlHierTreeNode m_hierVarTreeRoot; // Prefix tree over m_hierVarAttrs
|
||||||
|
std::unordered_map<std::string, V3ControlHierTreeNode*> m_hierVarCellPaths;
|
||||||
|
std::map<V3Hash, int> m_hierVarVariants; // Interned hier variant hashes
|
||||||
std::unordered_map<string, std::unordered_map<string, uint64_t>>
|
std::unordered_map<string, std::unordered_map<string, uint64_t>>
|
||||||
m_profileData; // Access to profile_data records
|
m_profileData; // Access to profile_data records
|
||||||
uint8_t m_mode = NONE;
|
uint8_t m_mode = NONE;
|
||||||
|
|
@ -813,6 +862,12 @@ public:
|
||||||
V3ControlModuleResolver& modules() { return m_modules; }
|
V3ControlModuleResolver& modules() { return m_modules; }
|
||||||
V3ControlFileResolver& files() { return m_files; }
|
V3ControlFileResolver& files() { return m_files; }
|
||||||
V3ControlScopeTraceResolver& scopeTraces() { return m_scopeTraces; }
|
V3ControlScopeTraceResolver& scopeTraces() { return m_scopeTraces; }
|
||||||
|
std::vector<V3ControlHierVarEntry>& hierVarAttrs() { return m_hierVarAttrs; }
|
||||||
|
V3ControlHierTreeNode& hierVarTreeRoot() { return m_hierVarTreeRoot; }
|
||||||
|
std::unordered_map<std::string, V3ControlHierTreeNode*>& hierVarCellPaths() {
|
||||||
|
return m_hierVarCellPaths;
|
||||||
|
}
|
||||||
|
std::map<V3Hash, int>& hierVarVariants() { return m_hierVarVariants; }
|
||||||
|
|
||||||
void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) {
|
void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) {
|
||||||
// Empty key for hierarchical DPI wrapper costs.
|
// Empty key for hierarchical DPI wrapper costs.
|
||||||
|
|
@ -1015,6 +1070,20 @@ void V3Control::addVarAttr(FileLine* fl, const string& module, const string& fta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For -path'ed vars
|
||||||
|
void V3Control::addHierVarAttr(FileLine* fl, const string& path, VAttrType attr) {
|
||||||
|
switch (attr) {
|
||||||
|
case VAttrType::VAR_PUBLIC:
|
||||||
|
case VAttrType::VAR_PUBLIC_FLAT:
|
||||||
|
case VAttrType::VAR_PUBLIC_FLAT_RD:
|
||||||
|
case VAttrType::VAR_PUBLIC_FLAT_RW:
|
||||||
|
V3ControlResolver::s().hierVarAttrs().emplace_back(fl, path, attr);
|
||||||
|
v3Global.dpi(true);
|
||||||
|
return;
|
||||||
|
default: fl->v3error("'"s + attr.ascii() + "' attribute does not support -path"); return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void V3Control::applyCase(AstCase* nodep) {
|
void V3Control::applyCase(AstCase* nodep) {
|
||||||
const string& filename = nodep->fileline()->filename();
|
const string& filename = nodep->fileline()->filename();
|
||||||
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);
|
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);
|
||||||
|
|
@ -1084,6 +1153,506 @@ void V3Control::applyVarAttr(const AstNodeModule* modulep, const AstNodeFTask* f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse one decimal array index from 'indexText' and append it to seg.m_indices.
|
||||||
|
static bool parseIndexDigits(const std::string& indexText, V3ControlHierSegment& seg) {
|
||||||
|
const size_t firstDigit = (!indexText.empty() && indexText[0] == '-') ? 1 : 0;
|
||||||
|
if (indexText.size() <= firstDigit) return false; // Empty, or just "-"
|
||||||
|
for (size_t i = firstDigit; i < indexText.size(); ++i) {
|
||||||
|
if (indexText[i] < '0' || indexText[i] > '9') return false;
|
||||||
|
}
|
||||||
|
errno = 0;
|
||||||
|
char* endp = nullptr;
|
||||||
|
const long value = std::strtol(indexText.c_str(), &endp, 10);
|
||||||
|
if (errno != 0 || *endp != '\0') return false; // Overflow of long, or trailing junk
|
||||||
|
if (value < std::numeric_limits<int>::min() || value > std::numeric_limits<int>::max())
|
||||||
|
return false; // Overflow of int
|
||||||
|
seg.m_indices.push_back(static_cast<int>(value));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class IndexRunResult : uint8_t {
|
||||||
|
OK, // Parsed a (possibly empty) run of "[N]" groups, pos past last ']'
|
||||||
|
UNTERMINATED, // A '[' has no matching ']'; pos left at the offending '['
|
||||||
|
BAD_DIGITS // A "[...]" body is not a valid index; pos left at the offending '['
|
||||||
|
};
|
||||||
|
|
||||||
|
static IndexRunResult parseIndexRun(const std::string& str, std::string::size_type& pos,
|
||||||
|
V3ControlHierSegment& seg) {
|
||||||
|
while (pos < str.size() && str[pos] == '[') {
|
||||||
|
const std::string::size_type closep = str.find(']', pos);
|
||||||
|
if (closep == std::string::npos) return IndexRunResult::UNTERMINATED;
|
||||||
|
if (!parseIndexDigits(str.substr(pos + 1, closep - pos - 1), seg)) {
|
||||||
|
return IndexRunResult::BAD_DIGITS;
|
||||||
|
}
|
||||||
|
pos = closep + 1;
|
||||||
|
}
|
||||||
|
return IndexRunResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseEscapedIndex(FileLine* fl, const std::string& path, std::string::size_type& pos,
|
||||||
|
V3ControlHierSegment& seg) {
|
||||||
|
const IndexRunResult result = parseIndexRun(path, pos, seg);
|
||||||
|
if (result == IndexRunResult::UNTERMINATED) {
|
||||||
|
fl->v3error("-path '" << path << "': unterminated '[' in escaped-name array index");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (result == IndexRunResult::BAD_DIGITS) {
|
||||||
|
const std::string::size_type closep = path.find(']', pos);
|
||||||
|
fl->v3error("-path '" << path << "': malformed array index '"
|
||||||
|
<< path.substr(pos, closep - pos + 1) << "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parsePlainSegment(FileLine* fl, const std::string& path, const std::string& raw,
|
||||||
|
V3ControlHierSegment& seg) {
|
||||||
|
const std::string::size_type firstOpen = raw.find('[');
|
||||||
|
if (firstOpen == std::string::npos) {
|
||||||
|
if (raw.find(']') != std::string::npos) {
|
||||||
|
fl->v3error("-path '" << path << "': malformed array index in segment '" << raw
|
||||||
|
<< "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
seg.m_name = raw;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
seg.m_name = raw.substr(0, firstOpen);
|
||||||
|
std::string::size_type pos = firstOpen;
|
||||||
|
const bool ok = firstOpen != 0 && parseIndexRun(raw, pos, seg) == IndexRunResult::OK;
|
||||||
|
if (!ok || pos != raw.size() || seg.m_name.find(']') != std::string::npos) {
|
||||||
|
fl->v3error("-path '" << path << "': malformed array index in segment '" << raw << "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<V3ControlHierSegment> splitHierPath(FileLine* fl, const std::string& path,
|
||||||
|
bool& errOut) {
|
||||||
|
static const std::string WHITESPACE = " \t\n\v\f\r";
|
||||||
|
errOut = false;
|
||||||
|
std::vector<V3ControlHierSegment> segs;
|
||||||
|
std::string::size_type i = 0;
|
||||||
|
const std::string::size_type len = path.size();
|
||||||
|
while (i < len) {
|
||||||
|
V3ControlHierSegment seg;
|
||||||
|
if (path[i] == '\\') {
|
||||||
|
++i; // skip '\'
|
||||||
|
const std::string::size_type start = i;
|
||||||
|
i = path.find_first_of(WHITESPACE, i);
|
||||||
|
if (i == std::string::npos) i = len;
|
||||||
|
seg.m_name = path.substr(start, i - start);
|
||||||
|
if (seg.m_name.empty()) {
|
||||||
|
fl->v3error("-path '" << path << "': empty escaped identifier");
|
||||||
|
errOut = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// Skip the terminating whitespace of the escaped identifier
|
||||||
|
i = path.find_first_not_of(WHITESPACE, i);
|
||||||
|
if (i == std::string::npos) i = len;
|
||||||
|
if (i < len && path[i] == '[') {
|
||||||
|
if (!parseEscapedIndex(fl, path, i, seg)) {
|
||||||
|
errOut = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const std::string::size_type start = i;
|
||||||
|
while (i < len && path[i] != '.') ++i;
|
||||||
|
const std::string raw = path.substr(start, i - start);
|
||||||
|
if (raw.empty()) {
|
||||||
|
fl->v3error("-path '" << path << "': empty identifier in path");
|
||||||
|
errOut = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (!parsePlainSegment(fl, path, raw, seg)) {
|
||||||
|
errOut = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
segs.push_back(seg);
|
||||||
|
if (i < len) {
|
||||||
|
if (path[i] != '.') {
|
||||||
|
fl->v3error("-path '" << path << "': expected '.' separator between identifiers");
|
||||||
|
errOut = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
++i; // skip '.'
|
||||||
|
if (i >= len) {
|
||||||
|
fl->v3error("-path '" << path << "': trailing '.' with no identifier");
|
||||||
|
errOut = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return segs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string segmentPrettyText(const V3ControlHierSegment& seg) {
|
||||||
|
std::string out = seg.m_name;
|
||||||
|
for (const int index : seg.m_indices) out += "[" + std::to_string(index) + "]";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void applyHierVarAttrToVar(AstVar* varp, VAttrType attr) {
|
||||||
|
switch (attr) {
|
||||||
|
case VAttrType::VAR_PUBLIC:
|
||||||
|
varp->sigUserRWPublic(true);
|
||||||
|
varp->sigModPublic(true);
|
||||||
|
break;
|
||||||
|
case VAttrType::VAR_PUBLIC_FLAT:
|
||||||
|
case VAttrType::VAR_PUBLIC_FLAT_RW: varp->sigUserRWPublic(true); break;
|
||||||
|
case VAttrType::VAR_PUBLIC_FLAT_RD: varp->sigUserRdPublic(true); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string scopeName(const AstNode* scopep) {
|
||||||
|
if (const AstNodeModule* m = VN_CAST(scopep, NodeModule)) return m->prettyDehashOrigOrName();
|
||||||
|
if (const AstBegin* b = VN_CAST(scopep, Begin)) return b->prettyDehashOrigOrName();
|
||||||
|
if (const AstGenBlock* g = VN_CAST(scopep, GenBlock)) return g->prettyDehashOrigOrName();
|
||||||
|
return scopep ? scopep->prettyName() : std::string{"<null>"};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void collectScopeMatches(AstNode* firstp, const std::string& name,
|
||||||
|
const std::string& stripped, std::vector<AstNode*>& out) {
|
||||||
|
for (AstNode* p = firstp; p; p = p->nextp()) {
|
||||||
|
if (AstCell* const cellp = VN_CAST(p, Cell)) {
|
||||||
|
if (cellp->prettyDehashOrigOrName() == name || cellp->origName() == name
|
||||||
|
|| cellp->name() == name) {
|
||||||
|
out.push_back(cellp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Currently touching one element in a cell array touches them all
|
||||||
|
if (cellp->rangep()
|
||||||
|
&& (cellp->prettyDehashOrigOrName() == stripped || cellp->origName() == stripped
|
||||||
|
|| cellp->name() == stripped))
|
||||||
|
out.push_back(cellp);
|
||||||
|
} else if (AstBegin* const beginp = VN_CAST(p, Begin)) {
|
||||||
|
if (beginp->prettyDehashOrigOrName() == name || beginp->name() == name
|
||||||
|
|| beginp->prettyDehashOrigOrName() == stripped || beginp->name() == stripped)
|
||||||
|
out.push_back(beginp);
|
||||||
|
} else if (AstGenBlock* const genp = VN_CAST(p, GenBlock)) {
|
||||||
|
if (genp->prettyDehashOrigOrName() == name || genp->name() == name
|
||||||
|
|| genp->prettyDehashOrigOrName() == stripped || genp->name() == stripped) {
|
||||||
|
out.push_back(genp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (genp->implied() || genp->unnamed())
|
||||||
|
collectScopeMatches(genp->itemsp(), name, stripped, out);
|
||||||
|
} else if (AstGenIf* const ifp = VN_CAST(p, GenIf)) {
|
||||||
|
collectScopeMatches(ifp->thensp(), name, stripped, out);
|
||||||
|
collectScopeMatches(ifp->elsesp(), name, stripped, out);
|
||||||
|
} else if (AstGenCase* const casep = VN_CAST(p, GenCase)) {
|
||||||
|
for (AstGenCaseItem* itemp = casep->itemsp(); itemp;
|
||||||
|
itemp = VN_AS(itemp->nextp(), GenCaseItem)) {
|
||||||
|
collectScopeMatches(itemp->itemsp(), name, stripped, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using ScopeChildrenCache = std::map<std::pair<const AstNode*, std::string>, std::vector<AstNode*>>;
|
||||||
|
|
||||||
|
static const std::vector<AstNode*>&
|
||||||
|
findScopeChildren(AstNode* scopep, const V3ControlHierSegment& seg, ScopeChildrenCache& cache) {
|
||||||
|
const std::string name = segmentPrettyText(seg);
|
||||||
|
const auto key = std::make_pair(static_cast<const AstNode*>(scopep), name);
|
||||||
|
const auto pair = cache.emplace(key, std::vector<AstNode*>{});
|
||||||
|
std::vector<AstNode*>& out = pair.first->second;
|
||||||
|
if (!pair.second) return out; // Cache hit
|
||||||
|
const std::string& stripped = seg.m_name;
|
||||||
|
if (const AstNodeModule* m = VN_CAST(scopep, NodeModule))
|
||||||
|
collectScopeMatches(m->stmtsp(), name, stripped, out);
|
||||||
|
else if (const AstBegin* b = VN_CAST(scopep, Begin))
|
||||||
|
collectScopeMatches(b->stmtsp(), name, stripped, out);
|
||||||
|
else if (const AstGenBlock* g = VN_CAST(scopep, GenBlock)) {
|
||||||
|
collectScopeMatches(g->itemsp(), name, stripped, out);
|
||||||
|
if (const AstGenFor* const forp = VN_CAST(g->genforp(), GenFor))
|
||||||
|
collectScopeMatches(forp->itemsp(), name, stripped, out);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AstVar* matchVarList(AstNode* firstp, const std::string& name) {
|
||||||
|
for (AstNode* p = firstp; p; p = p->nextp()) {
|
||||||
|
if (AstVar* const varp = VN_CAST(p, Var)) {
|
||||||
|
if (varp->prettyDehashOrigOrName() == name || varp->name() == name) return varp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AstVar* findVarInScope(AstNode* scopep, const std::string& name) {
|
||||||
|
if (const AstNodeModule* m = VN_CAST(scopep, NodeModule))
|
||||||
|
return matchVarList(m->stmtsp(), name);
|
||||||
|
if (const AstBegin* b = VN_CAST(scopep, Begin)) return matchVarList(b->stmtsp(), name);
|
||||||
|
if (const AstGenBlock* g = VN_CAST(scopep, GenBlock)) {
|
||||||
|
if (AstVar* const varp = matchVarList(g->itemsp(), name)) return varp;
|
||||||
|
if (const AstGenFor* const forp = VN_CAST(g->genforp(), GenFor))
|
||||||
|
return matchVarList(forp->itemsp(), name);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PathWalkResult final {
|
||||||
|
bool anyApplied = false;
|
||||||
|
size_t deepestIdx = 0;
|
||||||
|
AstNode* deepestScopep = nullptr;
|
||||||
|
};
|
||||||
|
static void walkAndApply(AstNode* scopep, const std::vector<V3ControlHierSegment>& segs,
|
||||||
|
size_t idx, V3ControlHierVarEntry& entry, PathWalkResult& result,
|
||||||
|
ScopeChildrenCache& cache) {
|
||||||
|
if (idx > result.deepestIdx) {
|
||||||
|
result.deepestIdx = idx;
|
||||||
|
result.deepestScopep = scopep;
|
||||||
|
}
|
||||||
|
if (idx == segs.size() - 1) {
|
||||||
|
AstVar* const varp = findVarInScope(scopep, segmentPrettyText(segs[idx]));
|
||||||
|
if (varp) {
|
||||||
|
varp->mayBecomePublic(true);
|
||||||
|
result.anyApplied = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const std::vector<AstNode*>& children = findScopeChildren(scopep, segs[idx], cache);
|
||||||
|
for (AstNode* const childp : children) {
|
||||||
|
AstNode* nextScopep = nullptr;
|
||||||
|
V3ControlHierKind kindSeen = V3ControlHierKind::UNKNOWN;
|
||||||
|
if (AstCell* const cellp = VN_CAST(childp, Cell)) {
|
||||||
|
if (!cellp->modp()) continue; // Unresolved
|
||||||
|
nextScopep = cellp->modp();
|
||||||
|
kindSeen = V3ControlHierKind::CELL;
|
||||||
|
} else {
|
||||||
|
nextScopep = childp;
|
||||||
|
kindSeen = V3ControlHierKind::BLOCK;
|
||||||
|
}
|
||||||
|
// idx-1 indexes into entry.m_middle (m_topName is segs[0]).
|
||||||
|
if (idx >= 1 && (idx - 1) < entry.m_middle.size()
|
||||||
|
&& entry.m_middle[idx - 1].m_kind == V3ControlHierKind::UNKNOWN) {
|
||||||
|
entry.m_middle[idx - 1].m_kind = kindSeen;
|
||||||
|
}
|
||||||
|
walkAndApply(nextScopep, segs, idx + 1, entry, result, cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void insertEntryIntoHierTree(const V3ControlHierVarEntry& entry) {
|
||||||
|
V3ControlHierTreeNode& root = V3ControlResolver::s().hierVarTreeRoot();
|
||||||
|
auto& cellPaths = V3ControlResolver::s().hierVarCellPaths();
|
||||||
|
// Top module is the first tree level
|
||||||
|
V3ControlHierTreeNode* nodep = root.getOrAddChild(entry.m_topName);
|
||||||
|
nodep->m_kind = V3ControlHierKind::CELL;
|
||||||
|
const bool registerCells
|
||||||
|
= !entry.m_middle.empty() && entry.m_middle.back().m_kind == V3ControlHierKind::CELL;
|
||||||
|
std::string instPath = entry.m_topName;
|
||||||
|
for (const V3ControlHierSegment& seg : entry.m_middle) {
|
||||||
|
const std::string key = segmentPrettyText(seg);
|
||||||
|
instPath += ".";
|
||||||
|
instPath += key;
|
||||||
|
nodep = nodep->getOrAddChild(key);
|
||||||
|
nodep->m_kind = seg.m_kind;
|
||||||
|
if (registerCells && seg.m_kind == V3ControlHierKind::CELL) cellPaths[instPath] = nodep;
|
||||||
|
}
|
||||||
|
nodep->m_vars.emplace_back(entry.m_leafName, entry.m_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark all possibly -path'ed vars as mayBecomePublic
|
||||||
|
void V3Control::applyHierVarAttrs(AstNetlist* netlistp) {
|
||||||
|
auto& entries = V3ControlResolver::s().hierVarAttrs();
|
||||||
|
if (entries.empty()) return;
|
||||||
|
// Reduce scanning for similar paths
|
||||||
|
ScopeChildrenCache scopeChildrenCache;
|
||||||
|
std::unordered_map<std::string, AstNodeModule*> topModCache;
|
||||||
|
for (V3ControlHierVarEntry& entry : entries) {
|
||||||
|
bool splitErr = false;
|
||||||
|
const std::vector<V3ControlHierSegment> segs
|
||||||
|
= splitHierPath(entry.m_fl, entry.m_path, splitErr);
|
||||||
|
if (splitErr) continue;
|
||||||
|
if (segs.size() < 2) {
|
||||||
|
entry.m_fl->v3error("-path '" << entry.m_path
|
||||||
|
<< "': must name at least a scope and a variable");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const std::string topName = segmentPrettyText(segs[0]);
|
||||||
|
entry.m_topName = topName;
|
||||||
|
entry.m_leafName = segmentPrettyText(segs.back());
|
||||||
|
entry.m_middle.assign(segs.begin() + 1, segs.end() - 1);
|
||||||
|
const auto topPair = topModCache.emplace(topName, nullptr);
|
||||||
|
AstNodeModule*& topModp = topPair.first->second;
|
||||||
|
if (topPair.second) { // Cache miss: scan the netlist once for this name
|
||||||
|
for (AstNodeModule* modp = netlistp->modulesp(); modp;
|
||||||
|
modp = VN_AS(modp->nextp(), NodeModule)) {
|
||||||
|
if (!modp->isTop()) continue;
|
||||||
|
if (modp->prettyDehashOrigOrName() == topName || modp->origName() == topName
|
||||||
|
|| modp->name() == topName) {
|
||||||
|
topModp = modp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!topModp) {
|
||||||
|
entry.m_fl->v3error("-path '" << entry.m_path << "': cannot find top scope '"
|
||||||
|
<< topName << "'");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Walk cells / begins / genblocks to var
|
||||||
|
PathWalkResult result;
|
||||||
|
walkAndApply(topModp, segs, 1, entry, result, scopeChildrenCache);
|
||||||
|
if (result.anyApplied) {
|
||||||
|
insertEntryIntoHierTree(entry);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// No path resolved - report at the deepest level reached for a useful error.
|
||||||
|
const size_t failIdx = result.deepestIdx;
|
||||||
|
AstNode* const failScopep = result.deepestScopep;
|
||||||
|
const std::string failName = segmentPrettyText(segs[failIdx]);
|
||||||
|
if (failIdx == segs.size() - 1) {
|
||||||
|
entry.m_fl->v3error("-path '" << entry.m_path << "': cannot find variable '"
|
||||||
|
<< failName << "' in scope '" << scopeName(failScopep)
|
||||||
|
<< "'");
|
||||||
|
} else {
|
||||||
|
entry.m_fl->v3error("-path '" << entry.m_path << "': cannot find scope '" << failName
|
||||||
|
<< "' in scope '" << scopeName(failScopep) << "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Note: entries are NOT cleared
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// V3Param time per-cell -path unique cloning
|
||||||
|
|
||||||
|
static V3Hash hashNodeMarkings(const V3ControlHierTreeNode* nodep) {
|
||||||
|
uint32_t varsXor = 0; // Order-independent accumulator over the leaf var set
|
||||||
|
for (const auto& var : nodep->m_vars) {
|
||||||
|
varsXor ^= (V3Hash{var.first} + V3Hash{static_cast<int32_t>(var.second)}).value();
|
||||||
|
}
|
||||||
|
V3Hash hash{varsXor};
|
||||||
|
for (const auto& child : nodep->m_children) {
|
||||||
|
hash += V3Hash{child.first} + V3Hash{static_cast<int32_t>(child.second->m_kind)}
|
||||||
|
+ hashNodeMarkings(child.second.get());
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variant ID if -path'ed, else -1
|
||||||
|
int V3Control::cellPathPublicVariant(const std::string& cellInstancePath) {
|
||||||
|
const auto& cellPaths = V3ControlResolver::s().hierVarCellPaths();
|
||||||
|
const auto it = cellPaths.find(cellInstancePath);
|
||||||
|
if (it == cellPaths.end()) return -1; // Not a -path'ed cell; no clone needed
|
||||||
|
auto& variants = V3ControlResolver::s().hierVarVariants();
|
||||||
|
const V3Hash hash = hashNodeMarkings(it->second);
|
||||||
|
const auto pair = variants.emplace(hash, static_cast<int>(variants.size()));
|
||||||
|
return pair.first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// Post-V3Begin, pre-V3Inline cell walk and public stamp
|
||||||
|
static std::string mangleHierSegment(const V3ControlHierSegment& seg) {
|
||||||
|
std::string out = seg.m_name;
|
||||||
|
for (const int index : seg.m_indices) {
|
||||||
|
out += "__BRA__" + AstNode::encodeNumber(index) + "__KET__";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join two mangled name parts with V3Begin's "__DOT__" separator.
|
||||||
|
static std::string mangledJoin(const std::string& a, const std::string& b) {
|
||||||
|
if (a.empty()) return b;
|
||||||
|
if (b.empty()) return a;
|
||||||
|
return a + "__DOT__" + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StageDCache final {
|
||||||
|
std::unordered_map<std::string, AstNodeModule*> m_topMods;
|
||||||
|
std::map<std::pair<const AstNodeModule*, std::string>, AstCell*> m_cells;
|
||||||
|
};
|
||||||
|
|
||||||
|
static AstNodeModule* findModuleByOrigName(AstNetlist* netlistp, const std::string& name,
|
||||||
|
StageDCache& cache) {
|
||||||
|
const auto pair = cache.m_topMods.emplace(name, nullptr);
|
||||||
|
AstNodeModule*& slot = pair.first->second;
|
||||||
|
if (!pair.second) return slot; // Cache hit
|
||||||
|
for (AstNodeModule* modp = netlistp->modulesp(); modp;
|
||||||
|
modp = VN_AS(modp->nextp(), NodeModule)) {
|
||||||
|
if (modp->prettyDehashOrigOrName() == name || modp->origName() == name
|
||||||
|
|| modp->name() == name) {
|
||||||
|
slot = modp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AstCell* findCellInModule(AstNodeModule* modp, const std::string& mangledName,
|
||||||
|
StageDCache& cache) {
|
||||||
|
const auto key = std::make_pair(static_cast<const AstNodeModule*>(modp), mangledName);
|
||||||
|
const auto pair = cache.m_cells.emplace(key, nullptr);
|
||||||
|
AstCell*& slot = pair.first->second;
|
||||||
|
if (!pair.second) return slot; // Cache hit
|
||||||
|
const std::string prettyLookup = AstNode::prettyName(mangledName);
|
||||||
|
for (AstNode* p = modp->stmtsp(); p; p = p->nextp()) {
|
||||||
|
AstCell* const cellp = VN_CAST(p, Cell);
|
||||||
|
if (!cellp) continue;
|
||||||
|
if (cellp->name() == mangledName || cellp->origName() == mangledName
|
||||||
|
|| cellp->prettyDehashOrigOrName() == mangledName
|
||||||
|
|| cellp->prettyDehashOrigOrName() == prettyLookup) {
|
||||||
|
slot = cellp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AstVar* findVarInModuleByMangledName(AstNodeModule* modp, const std::string& mangledName) {
|
||||||
|
const std::string prettyLookup = AstNode::prettyName(mangledName);
|
||||||
|
for (AstNode* p = modp->stmtsp(); p; p = p->nextp()) {
|
||||||
|
AstVar* const varp = VN_CAST(p, Var);
|
||||||
|
if (!varp) continue;
|
||||||
|
if (varp->name() == mangledName || varp->origName() == mangledName
|
||||||
|
|| varp->prettyDehashOrigOrName() == mangledName
|
||||||
|
|| varp->prettyDehashOrigOrName() == prettyLookup)
|
||||||
|
return varp;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void applyOneHierVarMarker(AstNetlist* netlistp, V3ControlHierVarEntry& entry,
|
||||||
|
StageDCache& cache) {
|
||||||
|
AstNodeModule* currentModp = findModuleByOrigName(netlistp, entry.m_topName, cache);
|
||||||
|
if (!currentModp) return; // Error already issued at V3LinkParse time.
|
||||||
|
std::string mangledPrefix; // Flattened BLOCK segments accumulate here.
|
||||||
|
for (const V3ControlHierSegment& seg : entry.m_middle) {
|
||||||
|
const std::string segMangled = mangleHierSegment(seg);
|
||||||
|
const std::string lookup = mangledJoin(mangledPrefix, segMangled);
|
||||||
|
if (seg.m_kind == V3ControlHierKind::CELL) {
|
||||||
|
AstCell* const cellp = findCellInModule(currentModp, lookup, cache);
|
||||||
|
if (!cellp || !cellp->modp()) return; // Pruned generate iteration, etc.
|
||||||
|
currentModp = cellp->modp();
|
||||||
|
mangledPrefix.clear();
|
||||||
|
} else {
|
||||||
|
mangledPrefix = lookup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const std::string leafLookup = mangledJoin(mangledPrefix, entry.m_leafName);
|
||||||
|
AstVar* const varp = findVarInModuleByMangledName(currentModp, leafLookup);
|
||||||
|
if (varp) applyHierVarAttrToVar(varp, entry.m_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finialize public flags
|
||||||
|
void V3Control::applyHierVarScopes(AstNetlist* netlistp) {
|
||||||
|
auto& entries = V3ControlResolver::s().hierVarAttrs();
|
||||||
|
if (entries.empty()) return;
|
||||||
|
|
||||||
|
StageDCache cache;
|
||||||
|
for (V3ControlHierVarEntry& entry : entries) applyOneHierVarMarker(netlistp, entry, cache);
|
||||||
|
|
||||||
|
netlistp->foreach([](AstVar* varp) {
|
||||||
|
if (varp->mayBecomePublic() && !varp->isSigPublic()) varp->mayBecomePublic(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
entries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
int V3Control::getHierWorkers(const string& model) {
|
int V3Control::getHierWorkers(const string& model) {
|
||||||
return V3ControlResolver::s().getHierWorkers(model);
|
return V3ControlResolver::s().getHierWorkers(model);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,11 @@ public:
|
||||||
VarSpecKind kind, const string& pattern, VAttrType type,
|
VarSpecKind kind, const string& pattern, VAttrType type,
|
||||||
AstSenTree* nodep);
|
AstSenTree* nodep);
|
||||||
|
|
||||||
|
static void addHierVarAttr(FileLine* fl, const string& path, VAttrType type);
|
||||||
|
static void applyHierVarAttrs(AstNetlist* netlistp);
|
||||||
|
static int cellPathPublicVariant(const std::string& cellInstancePath);
|
||||||
|
static void applyHierVarScopes(AstNetlist* netlistp);
|
||||||
|
|
||||||
static void applyCase(AstCase* nodep);
|
static void applyCase(AstCase* nodep);
|
||||||
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
|
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
|
||||||
static void applyCoverageBlock(AstNodeModule* modulep, AstGenBlock* nodep);
|
static void applyCoverageBlock(AstNodeModule* modulep, AstGenBlock* nodep);
|
||||||
|
|
|
||||||
|
|
@ -418,6 +418,7 @@ class DeadVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
bool mightElimVar(const AstVar* nodep) const {
|
bool mightElimVar(const AstVar* nodep) const {
|
||||||
if (nodep->isSigPublic()) return false; // Can't elim publics!
|
if (nodep->isSigPublic()) return false; // Can't elim publics!
|
||||||
|
if (nodep->mayBecomePublic()) return false; // .vlt -path'ed candidate
|
||||||
if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
|
if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
|
||||||
if (nodep->isTemp() && !nodep->isTrace()) return true;
|
if (nodep->isTemp() && !nodep->isTrace()) return true;
|
||||||
return m_elimUserVars; // Post-Trace can kill most anything
|
return m_elimUserVars; // Post-Trace can kill most anything
|
||||||
|
|
|
||||||
|
|
@ -1426,6 +1426,7 @@ public:
|
||||||
|
|
||||||
void V3LinkParse::linkParse(AstNetlist* rootp) {
|
void V3LinkParse::linkParse(AstNetlist* rootp) {
|
||||||
UINFO(4, __FUNCTION__ << ": ");
|
UINFO(4, __FUNCTION__ << ": ");
|
||||||
|
V3Control::applyHierVarAttrs(rootp);
|
||||||
{ LinkParseVisitor{rootp}; } // Destruct before checking
|
{ LinkParseVisitor{rootp}; } // Destruct before checking
|
||||||
V3Global::dumpCheckGlobalTree("linkparse", 0, dumpTreeEitherLevel() >= 6);
|
V3Global::dumpCheckGlobalTree("linkparse", 0, dumpTreeEitherLevel() >= 6);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@
|
||||||
|
|
||||||
#include "V3Case.h"
|
#include "V3Case.h"
|
||||||
#include "V3Const.h"
|
#include "V3Const.h"
|
||||||
|
#include "V3Control.h"
|
||||||
#include "V3EmitV.h"
|
#include "V3EmitV.h"
|
||||||
#include "V3Hasher.h"
|
#include "V3Hasher.h"
|
||||||
#include "V3LinkDotIfaceCapture.h"
|
#include "V3LinkDotIfaceCapture.h"
|
||||||
|
|
@ -1960,6 +1961,14 @@ class ParamProcessor final {
|
||||||
// Make sure constification worked
|
// Make sure constification worked
|
||||||
// Must be a separate loop, as constant conversion may have changed some pointers.
|
// Must be a separate loop, as constant conversion may have changed some pointers.
|
||||||
string longname = srcModp->name() + "_";
|
string longname = srcModp->name() + "_";
|
||||||
|
// Clone for .vlt -path'ed variants
|
||||||
|
if (VN_IS(nodep, Cell)) {
|
||||||
|
const int pubVariant = V3Control::cellPathPublicVariant(srcModp->someInstanceName());
|
||||||
|
if (pubVariant >= 0) {
|
||||||
|
any_overrides = true;
|
||||||
|
longname += "__Vhier" + cvtToStr(pubVariant);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (debug() >= 9 && paramsp) paramsp->dumpTreeAndNext(cout, "- cellparams: ");
|
if (debug() >= 9 && paramsp) paramsp->dumpTreeAndNext(cout, "- cellparams: ");
|
||||||
|
|
||||||
if (srcModp->hierBlock()) {
|
if (srcModp->hierBlock()) {
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,7 @@ struct SplitVarImpl VL_NOT_FINAL {
|
||||||
return reason;
|
return reason;
|
||||||
}
|
}
|
||||||
if (varp->isSigPublic()) return "it is public";
|
if (varp->isSigPublic()) return "it is public";
|
||||||
|
if (varp->mayBecomePublic()) return "it may become public from .vlt";
|
||||||
if (varp->isUsedLoopIdx()) return "it is used as a loop variable";
|
if (varp->isUsedLoopIdx()) return "it is used as a loop variable";
|
||||||
if (varp->isForceable()) return "it is forceable";
|
if (varp->isForceable()) return "it is forceable";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,10 @@ static void process() {
|
||||||
// No more AstGenBlocks after this
|
// No more AstGenBlocks after this
|
||||||
V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner
|
V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner
|
||||||
|
|
||||||
|
// Stamp public attributes from .vlt hier-var paths onto per-instance
|
||||||
|
// leaf vars
|
||||||
|
V3Control::applyHierVarScopes(v3Global.rootp());
|
||||||
|
|
||||||
// Expand inouts, stage 2
|
// Expand inouts, stage 2
|
||||||
// Also simplify pin connections to always be AssignWs in prep for V3Unknown
|
// Also simplify pin connections to always be AssignWs in prep for V3Unknown
|
||||||
V3Tristate::tristateAll(v3Global.rootp());
|
V3Tristate::tristateAll(v3Global.rootp());
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||||
-?"-module" { FL; return yVLT_D_MODULE; }
|
-?"-module" { FL; return yVLT_D_MODULE; }
|
||||||
-?"-mtask" { FL; return yVLT_D_MTASK; }
|
-?"-mtask" { FL; return yVLT_D_MTASK; }
|
||||||
-?"-param" { FL; return yVLT_D_PARAM; }
|
-?"-param" { FL; return yVLT_D_PARAM; }
|
||||||
|
-?"-path" { FL; return yVLT_D_PATH; }
|
||||||
-?"-port" { FL; return yVLT_D_PORT; }
|
-?"-port" { FL; return yVLT_D_PORT; }
|
||||||
-?"-rule" { FL; return yVLT_D_RULE; }
|
-?"-rule" { FL; return yVLT_D_RULE; }
|
||||||
-?"-q" { FL; return yVLT_D_Q; }
|
-?"-q" { FL; return yVLT_D_Q; }
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
||||||
%token<fl> yVLT_D_MODULE "--module"
|
%token<fl> yVLT_D_MODULE "--module"
|
||||||
%token<fl> yVLT_D_MTASK "--mtask"
|
%token<fl> yVLT_D_MTASK "--mtask"
|
||||||
%token<fl> yVLT_D_PARAM "--param"
|
%token<fl> yVLT_D_PARAM "--param"
|
||||||
|
%token<fl> yVLT_D_PATH "--path"
|
||||||
%token<fl> yVLT_D_PORT "--port"
|
%token<fl> yVLT_D_PORT "--port"
|
||||||
%token<fl> yVLT_D_RULE "--rule"
|
%token<fl> yVLT_D_RULE "--rule"
|
||||||
%token<fl> yVLT_D_Q "--q"
|
%token<fl> yVLT_D_Q "--q"
|
||||||
|
|
@ -8453,6 +8454,8 @@ vltItem:
|
||||||
}}
|
}}
|
||||||
| vltVarAttrFront vltDModuleE vltDFTaskE vltVarAttrSpecE attr_event_controlE
|
| vltVarAttrFront vltDModuleE vltDFTaskE vltVarAttrSpecE attr_event_controlE
|
||||||
{ V3Control::addVarAttr($<fl>1, *$2, *$3, GRAMMARP->m_vltVarSpecKind, *$4, $1, $5); }
|
{ V3Control::addVarAttr($<fl>1, *$2, *$3, GRAMMARP->m_vltVarSpecKind, *$4, $1, $5); }
|
||||||
|
| vltVarAttrFront vltDPath
|
||||||
|
{ V3Control::addHierVarAttr($<fl>1, *$2, $1); }
|
||||||
| vltVarAttrFrontDeprecated vltDModuleE vltDFTaskE vltVarAttrSpecE
|
| vltVarAttrFrontDeprecated vltDModuleE vltDFTaskE vltVarAttrSpecE
|
||||||
{ /* Historical, now has no effect */ }
|
{ /* Historical, now has no effect */ }
|
||||||
| vltInlineFront vltDModuleE vltDFTaskE
|
| vltInlineFront vltDModuleE vltDFTaskE
|
||||||
|
|
@ -8578,6 +8581,10 @@ vltDModuleE<strp>: // [--module <arg>]
|
||||||
{ $$ = $1; }
|
{ $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
vltDPath<strp>: // --path <arg>
|
||||||
|
yVLT_D_PATH str { $$ = $2; }
|
||||||
|
;
|
||||||
|
|
||||||
vltDScope<strp>: // --scope <arg>
|
vltDScope<strp>: // --scope <arg>
|
||||||
yVLT_D_SCOPE str { $$ = $2; }
|
yVLT_D_SCOPE str { $$ = $2; }
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: TOP
|
||||||
|
VAR 0x#: top_port_i
|
||||||
|
VAR 0x#: top_port_o
|
||||||
|
SCOPE 0x#: top
|
||||||
|
VAR 0x#: TOP_LOCALPARAM
|
||||||
|
VAR 0x#: TOP_PARAM
|
||||||
|
VAR 0x#: top_port_i
|
||||||
|
VAR 0x#: top_port_o
|
||||||
|
SCOPE 0x#: top.i_mid_0
|
||||||
|
VAR 0x#: MID_LOCALPARAM
|
||||||
|
VAR 0x#: MID_PARM
|
||||||
|
VAR 0x#: mid_tmp_b
|
||||||
|
SCOPE 0x#: top.i_mid_0.i_sub_0
|
||||||
|
VAR 0x#: f__Vstatic__SUB_F_LOCALPARAM
|
||||||
|
VAR 0x#: sub_port_i
|
||||||
|
VAR 0x#: sub_port_o
|
||||||
|
SCOPE 0x#: top.i_mid_0.i_sub_1
|
||||||
|
VAR 0x#: f__Vstatic__SUB_F_LOCALPARAM
|
||||||
|
VAR 0x#: sub_port_i
|
||||||
|
VAR 0x#: sub_port_o
|
||||||
|
SCOPE 0x#: top.i_mid_1
|
||||||
|
VAR 0x#: MID_LOCALPARAM
|
||||||
|
VAR 0x#: MID_PARM
|
||||||
|
VAR 0x#: mid_tmp_b
|
||||||
|
SCOPE 0x#: top.i_mid_1.i_sub_0
|
||||||
|
VAR 0x#: f__Vstatic__SUB_F_LOCALPARAM
|
||||||
|
VAR 0x#: sub_port_i
|
||||||
|
VAR 0x#: sub_port_o
|
||||||
|
SCOPE 0x#: top.i_mid_1.i_sub_1
|
||||||
|
VAR 0x#: f__Vstatic__SUB_F_LOCALPARAM
|
||||||
|
VAR 0x#: sub_port_i
|
||||||
|
VAR 0x#: sub_port_o
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
test.top_filename = "t/t_vlt_public_spec.v"
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
// Same effect as t_vlt_public_spec.vlt but using hierarchical -path
|
||||||
|
// (without -module) where applicable. Demonstrates fine-grained per-signal
|
||||||
|
// public control by fully qualified instance path.
|
||||||
|
|
||||||
|
public_flat_rd -module "top" -param "*"
|
||||||
|
public_flat_rw -module "top" -port "*"
|
||||||
|
|
||||||
|
public_flat_rd -module "mid" -param "*"
|
||||||
|
public_flat_rw -module "mid" -function "*" -port "top_f_port_o"
|
||||||
|
public_flat -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
public_flat -path "top.i_mid_1.mid_tmp_b"
|
||||||
|
|
||||||
|
public_flat -module "sub" -port "*"
|
||||||
|
public_flat_rd -module "sub" -function "*" -param "SUB_F_LOCALPARAM"
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
%Error: t/t_vlt_public_spec_hier_attr_bad.vlt:10:1: 'VAR_SC_BV' attribute does not support -path
|
||||||
|
10 | sc_bv -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
| ^~~~~
|
||||||
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
|
%Error: t/t_vlt_public_spec_hier_attr_bad.vlt:11:1: 'VAR_SC_BIGUINT' attribute does not support -path
|
||||||
|
11 | sc_biguint -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_attr_bad.vlt:12:1: 'VAR_SFORMAT' attribute does not support -path
|
||||||
|
12 | sformat -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
| ^~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_attr_bad.vlt:13:1: 'VAR_SPLIT_VAR' attribute does not support -path
|
||||||
|
13 | split_var -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
| ^~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_attr_bad.vlt:14:1: 'VAR_FORCEABLE' attribute does not support -path
|
||||||
|
14 | forceable -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
| ^~~~~~~~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
test.top_filename = "t/t_vlt_public_spec.v"
|
||||||
|
|
||||||
|
test.lint(fails=True,
|
||||||
|
expect_filename=test.golden_filename,
|
||||||
|
verilator_flags2=["--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
// Hierarchical -path is only supported with public-family attributes.
|
||||||
|
sc_bv -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
sc_biguint -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
sformat -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
split_var -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
forceable -path "top.i_mid_0.mid_tmp_b"
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:12:1: -path 'no_such_top.no_such_var': cannot find top scope 'no_such_top'
|
||||||
|
12 | public_flat -path "no_such_top.no_such_var"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:15:1: -path 'top.i_mid_bogus.mid_tmp_b': cannot find scope 'i_mid_bogus' in scope 'top'
|
||||||
|
15 | public_flat -path "top.i_mid_bogus.mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:18:1: -path 'top.i_mid_0.no_such_var': cannot find variable 'no_such_var' in scope 'mid'
|
||||||
|
18 | public_flat -path "top.i_mid_0.no_such_var"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:21:1: -path 'top.\ .mid_tmp_b': empty escaped identifier
|
||||||
|
21 | public_flat -path "top.\\ .mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:24:1: -path 'top..mid_tmp_b': empty identifier in path
|
||||||
|
24 | public_flat -path "top..mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:27:1: -path 'top.i_mid_0.': trailing '.' with no identifier
|
||||||
|
27 | public_flat -path "top.i_mid_0."
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:30:1: -path 'top.\i_mid_0 mid_tmp_b': expected '.' separator between identifiers
|
||||||
|
30 | public_flat -path "top.\\i_mid_0 mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:33:1: -path 'top.\foo] [2.mid_tmp_b': unterminated '[' in escaped-name array index
|
||||||
|
33 | public_flat -path "top.\\foo] [2.mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:36:1: -path 'top': must name at least a scope and a variable
|
||||||
|
36 | public_flat -path "top"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:39:1: -path 'top.i_mid_0[abc].mid_tmp_b': malformed array index in segment 'i_mid_0[abc]'
|
||||||
|
39 | public_flat -path "top.i_mid_0[abc].mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:42:1: -path 'top.i_mid_0[99999999999].mid_tmp_b': malformed array index in segment 'i_mid_0[99999999999]'
|
||||||
|
42 | public_flat -path "top.i_mid_0[99999999999].mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:45:1: -path 'top.\foo] [abc].mid_tmp_b': malformed array index '[abc]'
|
||||||
|
45 | public_flat -path "top.\\foo] [abc].mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:48:1: -path 'top.i_mid_0].mid_tmp_b': malformed array index in segment 'i_mid_0]'
|
||||||
|
48 | public_flat -path "top.i_mid_0].mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:51:1: -path 'top.[0].mid_tmp_b': malformed array index in segment '[0]'
|
||||||
|
51 | public_flat -path "top.[0].mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:54:1: -path 'top.i_mid_0[1][abc].mid_tmp_b': malformed array index in segment 'i_mid_0[1][abc]'
|
||||||
|
54 | public_flat -path "top.i_mid_0[1][abc].mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_bad.vlt:57:1: -path 'top.i_mid_0[1]x[0].mid_tmp_b': malformed array index in segment 'i_mid_0[1]x[0]'
|
||||||
|
57 | public_flat -path "top.i_mid_0[1]x[0].mid_tmp_b"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
test.top_filename = "t/t_vlt_public_spec.v"
|
||||||
|
|
||||||
|
test.lint(fails=True,
|
||||||
|
expect_filename=test.golden_filename,
|
||||||
|
verilator_flags2=["--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
// Each of the following lines exercises an error case for hierarchical -path.
|
||||||
|
|
||||||
|
// Unknown top module name
|
||||||
|
public_flat -path "no_such_top.no_such_var"
|
||||||
|
|
||||||
|
// Unknown cell name in the middle of the hierarchical path
|
||||||
|
public_flat -path "top.i_mid_bogus.mid_tmp_b"
|
||||||
|
|
||||||
|
// Unknown variable name at the leaf of the path
|
||||||
|
public_flat -path "top.i_mid_0.no_such_var"
|
||||||
|
|
||||||
|
// Empty escaped identifier: '\' immediately followed by whitespace
|
||||||
|
public_flat -path "top.\\ .mid_tmp_b"
|
||||||
|
|
||||||
|
// Empty identifier in path: two consecutive dots
|
||||||
|
public_flat -path "top..mid_tmp_b"
|
||||||
|
|
||||||
|
// Trailing '.' with no identifier
|
||||||
|
public_flat -path "top.i_mid_0."
|
||||||
|
|
||||||
|
// Missing '.' separator between escaped identifier and next segment
|
||||||
|
public_flat -path "top.\\i_mid_0 mid_tmp_b"
|
||||||
|
|
||||||
|
// Unterminated '[' in array index after escaped identifier
|
||||||
|
public_flat -path "top.\\foo] [2.mid_tmp_b"
|
||||||
|
|
||||||
|
// Single segment: must name at least a scope and a variable
|
||||||
|
public_flat -path "top"
|
||||||
|
|
||||||
|
// Non-numeric array index on a plain (non-escaped) segment
|
||||||
|
public_flat -path "top.i_mid_0[abc].mid_tmp_b"
|
||||||
|
|
||||||
|
// Array index that overflows int
|
||||||
|
public_flat -path "top.i_mid_0[99999999999].mid_tmp_b"
|
||||||
|
|
||||||
|
// Non-numeric folded array index after an escaped identifier
|
||||||
|
public_flat -path "top.\\foo] [abc].mid_tmp_b"
|
||||||
|
|
||||||
|
// Trailing ']' with no matching '[' in a non-escaped segment
|
||||||
|
public_flat -path "top.i_mid_0].mid_tmp_b"
|
||||||
|
|
||||||
|
// Stray '[' at position 0 of a non-escaped segment
|
||||||
|
public_flat -path "top.[0].mid_tmp_b"
|
||||||
|
|
||||||
|
// Multidimensional index with a non-numeric dimension
|
||||||
|
public_flat -path "top.i_mid_0[1][abc].mid_tmp_b"
|
||||||
|
|
||||||
|
// Text between two index groups in a multidim segment
|
||||||
|
public_flat -path "top.i_mid_0[1]x[0].mid_tmp_b"
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.i_sub[0]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
SCOPE 0x#: top.i_sub[1]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
|
// Demonstrate that per-instance selectivity is NOT yet implemented for
|
||||||
|
// arrayed cells.
|
||||||
|
|
||||||
|
module sub();
|
||||||
|
int leaf = 0;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top();
|
||||||
|
sub i_sub [1:0] ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
// Name a single iteration of a cell array. Per-instance selectivity is not
|
||||||
|
// yet supported for arrayed cells (see .v for the architectural reason),
|
||||||
|
// so this currently marks ALL iterations.
|
||||||
|
public_flat -path "top.i_sub[1].leaf"
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.\foo[abc]
|
||||||
|
VAR 0x#: \var.with.dot
|
||||||
|
SCOPE 0x#: top.\inst.with.dot
|
||||||
|
VAR 0x#: \var.with.dot
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
|
module sub_mod();
|
||||||
|
int \var.with.dot = 0;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top();
|
||||||
|
sub_mod \inst.with.dot ();
|
||||||
|
sub_mod \foo[abc] ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
public_flat -path "top.\\inst.with.dot .\\var.with.dot "
|
||||||
|
|
||||||
|
public_flat -path "top.\\foo[abc] .\\var.with.dot "
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.\foo] [0]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
SCOPE 0x#: top.\foo] [1]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
SCOPE 0x#: top.\foo] [2]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
SCOPE 0x#: top.\foo] [3]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
|
module sub();
|
||||||
|
int leaf = 0;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top();
|
||||||
|
sub \foo] [3:0] ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
public_flat -path "top.\\foo] [2].leaf"
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.gen_if
|
||||||
|
VAR 0x#: if_local
|
||||||
|
SCOPE 0x#: top.gen_if.i_sub
|
||||||
|
VAR 0x#: sub_tmp
|
||||||
|
SCOPE 0x#: top.gen_loop[0]
|
||||||
|
SCOPE 0x#: top.gen_loop[0].i_sub
|
||||||
|
VAR 0x#: sub_tmp
|
||||||
|
SCOPE 0x#: top.gen_loop[1]
|
||||||
|
VAR 0x#: loop_local
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
test.top_filename = "t/t_vlt_public_spec_hier_genblock.v"
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
|
module top (
|
||||||
|
input int top_port_i,
|
||||||
|
output int top_port_o
|
||||||
|
);
|
||||||
|
|
||||||
|
int top_tmp;
|
||||||
|
|
||||||
|
generate
|
||||||
|
for (genvar i = 0; i < 2; ++i) begin : gen_loop
|
||||||
|
int loop_local;
|
||||||
|
sub i_sub(top_port_i, loop_local);
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
generate
|
||||||
|
if (1) begin : gen_if
|
||||||
|
int if_local;
|
||||||
|
sub i_sub(top_port_i, if_local);
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
assign top_port_o = top_tmp;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
top_tmp = 0;
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module sub (
|
||||||
|
input int sub_port_i,
|
||||||
|
output int sub_port_o
|
||||||
|
);
|
||||||
|
int sub_tmp;
|
||||||
|
assign sub_tmp = sub_port_i + 1;
|
||||||
|
assign sub_port_o = sub_tmp;
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
// Var inside a cell inside a generate-for iteration:
|
||||||
|
public_flat -path "top.gen_loop[0].i_sub.sub_tmp"
|
||||||
|
|
||||||
|
// Var directly inside a generate-for iteration:
|
||||||
|
public_flat -path "top.gen_loop[1].loop_local"
|
||||||
|
|
||||||
|
// Var inside a cell inside a generate-if block:
|
||||||
|
public_flat -path "top.gen_if.i_sub.sub_tmp"
|
||||||
|
|
||||||
|
// Var directly inside a generate-if block:
|
||||||
|
public_flat -path "top.gen_if.if_local"
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
%Error: t/t_vlt_public_spec_hier_genblock_bad.vlt:13:1: -path 'top.no_such_gen.x': cannot find scope 'no_such_gen' in scope 'top'
|
||||||
|
13 | public_flat -path "top.no_such_gen.x"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
|
%Error: t/t_vlt_public_spec_hier_genblock_bad.vlt:16:1: -path 'top.gen_loop[0].no_such_var': cannot find variable 'no_such_var' in scope 'gen_loop'
|
||||||
|
16 | public_flat -path "top.gen_loop[0].no_such_var"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_genblock_bad.vlt:19:1: -path 'top.gen_if.no_such_var': cannot find variable 'no_such_var' in scope 'gen_if'
|
||||||
|
19 | public_flat -path "top.gen_if.no_such_var"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: t/t_vlt_public_spec_hier_genblock_bad.vlt:22:1: -path 'top.gen_loop[0].no_such_cell.sub_tmp': cannot find scope 'no_such_cell' in scope 'gen_loop'
|
||||||
|
22 | public_flat -path "top.gen_loop[0].no_such_cell.sub_tmp"
|
||||||
|
| ^~~~~~~~~~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
test.top_filename = "t/t_vlt_public_spec_hier_genblock.v"
|
||||||
|
|
||||||
|
test.lint(fails=True,
|
||||||
|
expect_filename=test.golden_filename,
|
||||||
|
verilator_flags2=["--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
// Each line below exercises an error case for hierarchical -path that
|
||||||
|
// descends through generate blocks.
|
||||||
|
|
||||||
|
// Unknown generate block name directly under the top module
|
||||||
|
public_flat -path "top.no_such_gen.x"
|
||||||
|
|
||||||
|
// Unknown variable inside a generate-for iteration
|
||||||
|
public_flat -path "top.gen_loop[0].no_such_var"
|
||||||
|
|
||||||
|
// Unknown variable inside a generate-if block
|
||||||
|
public_flat -path "top.gen_if.no_such_var"
|
||||||
|
|
||||||
|
// Unknown cell name inside a generate-for iteration
|
||||||
|
public_flat -path "top.gen_loop[0].no_such_cell.sub_tmp"
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.i_arr[0]
|
||||||
|
VAR 0x#: data
|
||||||
|
SCOPE 0x#: top.i_arr[1]
|
||||||
|
VAR 0x#: data
|
||||||
|
SCOPE 0x#: top.i_arr[2]
|
||||||
|
VAR 0x#: data
|
||||||
|
SCOPE 0x#: top.i_md[0][0]
|
||||||
|
VAR 0x#: data
|
||||||
|
SCOPE 0x#: top.i_md[0][1]
|
||||||
|
VAR 0x#: data
|
||||||
|
SCOPE 0x#: top.i_md[1][0]
|
||||||
|
VAR 0x#: data
|
||||||
|
SCOPE 0x#: top.i_md[1][1]
|
||||||
|
VAR 0x#: data
|
||||||
|
SCOPE 0x#: top.i_single
|
||||||
|
VAR 0x#: data
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
|
interface ifc();
|
||||||
|
logic [7:0] data;
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
module top();
|
||||||
|
ifc i_single ();
|
||||||
|
ifc i_arr [2:0] ();
|
||||||
|
ifc i_md [1:0][1:0] ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
// Single interface instance: per-instance selective.
|
||||||
|
public_flat -path "top.i_single.data"
|
||||||
|
|
||||||
|
// 1D interface array: marks the var in every iteration.
|
||||||
|
public_flat -path "top.i_arr[1].data"
|
||||||
|
|
||||||
|
// Multidimensional interface array: marks the var in every iteration.
|
||||||
|
public_flat -path "top.i_md[1][0].data"
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.i_sub[0][0]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
SCOPE 0x#: top.i_sub[0][1]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
SCOPE 0x#: top.i_sub[1][0]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
SCOPE 0x#: top.i_sub[1][1]
|
||||||
|
VAR 0x#: leaf
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
|
module sub();
|
||||||
|
int leaf = 0;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top();
|
||||||
|
sub i_sub [1:0][1:0] ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
public_flat -path "top.i_sub[1][0].leaf"
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.gen_loop[-1]
|
||||||
|
VAR 0x#: loop_local
|
||||||
|
SCOPE 0x#: top.i_sub[-1]
|
||||||
|
VAR 0x#: sub_leaf
|
||||||
|
SCOPE 0x#: top.i_sub[-2]
|
||||||
|
VAR 0x#: sub_leaf
|
||||||
|
SCOPE 0x#: top.i_sub[0]
|
||||||
|
VAR 0x#: sub_leaf
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
|
module sub();
|
||||||
|
int sub_leaf = 0;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top();
|
||||||
|
generate
|
||||||
|
for (genvar i = -2; i < 0; ++i) begin : gen_loop
|
||||||
|
int loop_local;
|
||||||
|
end
|
||||||
|
endgenerate
|
||||||
|
|
||||||
|
sub i_sub [0:-2] ();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
public_flat -path "top.gen_loop[-1].loop_local"
|
||||||
|
|
||||||
|
public_flat -path "top.i_sub[-1].sub_leaf"
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module sub #(
|
||||||
|
parameter int SUB_PARAM = 7
|
||||||
|
) ();
|
||||||
|
|
||||||
|
localparam int SUB_LOCALPARAM = 3;
|
||||||
|
int by_pragma /*verilator public_flat_rd*/;
|
||||||
|
int by_vlt;
|
||||||
|
int not_public;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
by_pragma = SUB_PARAM;
|
||||||
|
by_vlt = SUB_LOCALPARAM;
|
||||||
|
not_public = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top();
|
||||||
|
sub i_sub();
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.i_sub
|
||||||
|
VAR 0x#: SUB_LOCALPARAM
|
||||||
|
VAR 0x#: SUB_PARAM
|
||||||
|
VAR 0x#: by_vlt
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
test.top_filename = "t/t_vlt_public_spec_hier_pub.v"
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=[
|
||||||
|
"--binary", "--vpi", "--public-ignore", "--public-params", test.name + ".vlt"
|
||||||
|
])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
public_flat -path "top.i_sub.by_vlt"
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
scopesDump:
|
||||||
|
SCOPE 0x#: top
|
||||||
|
SCOPE 0x#: top.sub_a
|
||||||
|
VAR 0x#: some_var
|
||||||
|
SCOPE 0x#: top.sub_b
|
||||||
|
VAR 0x#: some_var
|
||||||
|
SCOPE 0x#: top.sub_c
|
||||||
|
VAR 0x#: some_var
|
||||||
|
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('vlt')
|
||||||
|
|
||||||
|
test.compile(verilator_flags2=["--binary", "--vpi", test.name + ".vlt"])
|
||||||
|
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
|
||||||
|
module sub();
|
||||||
|
int some_var = 0;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module top();
|
||||||
|
sub sub_a (); // tagged public_flat
|
||||||
|
sub sub_b (); // tagged public_flat (same marking as sub_a -> shares clone)
|
||||||
|
sub sub_c (); // tagged public_flat_rd (different marking -> own clone)
|
||||||
|
sub sub_d (); // not tagged
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$c("Verilated::scopesDump();");
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`verilator_config
|
||||||
|
|
||||||
|
// sub_a and sub_b: identical marking -> share one deparam clone.
|
||||||
|
public_flat -path "top.sub_a.some_var"
|
||||||
|
public_flat -path "top.sub_b.some_var"
|
||||||
|
|
||||||
|
// sub_c: different attribute -> distinct clone, still per-instance public.
|
||||||
|
public_flat_rd -path "top.sub_c.some_var"
|
||||||
|
|
||||||
|
// sub_d is left untagged: its some_var must NOT be public.
|
||||||
Loading…
Reference in New Issue