Fully qualified publics in verilator control files
This commit is contained in:
parent
6b3e2ce971
commit
63ec401644
|
|
@ -266,10 +266,13 @@ The grammar of control commands is as follows:
|
|||
|
||||
.. 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_rd -path "<hier.path.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)"]
|
||||
|
||||
Sets the specified signal to be public. Same as
|
||||
|
|
@ -283,6 +286,23 @@ The grammar of control commands is as follows:
|
|||
following ``<signame>`` can contain ``*`` and ``?`` wildcard
|
||||
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>"
|
||||
|
||||
Sets the input/output signal to be of ``sc_biguint<{width}>`` type.
|
||||
|
|
|
|||
|
|
@ -2138,6 +2138,7 @@ class AstVar final : public AstNode {
|
|||
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_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_usedLoopIdx : 1; // Variable subject of for unrolling
|
||||
bool m_funcLocal : 1; // Local variable for a function
|
||||
|
|
@ -2204,6 +2205,7 @@ class AstVar final : public AstNode {
|
|||
m_sigModPublic = false;
|
||||
m_sigUserRdPublic = false;
|
||||
m_sigUserRWPublic = false;
|
||||
m_mayBecomePublic = false;
|
||||
m_funcLocal = false;
|
||||
m_funcLocalSticky = false;
|
||||
m_funcReturn = false;
|
||||
|
|
@ -2380,6 +2382,8 @@ public:
|
|||
m_sigUserRWPublic = flag;
|
||||
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 scSensitive(bool flag) { m_scSensitive = flag; }
|
||||
void primaryIO(bool flag) { m_primaryIO = flag; }
|
||||
|
|
|
|||
|
|
@ -21,9 +21,12 @@
|
|||
#include "V3InstrCount.h"
|
||||
#include "V3String.h"
|
||||
|
||||
#include <charconv>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -791,11 +794,53 @@ public:
|
|||
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 {
|
||||
enum ProfileDataMode : uint8_t { NONE = 0, MTASK = 1, HIER_DPI = 2 };
|
||||
V3ControlModuleResolver m_modules; // Access to module names (with wildcards)
|
||||
V3ControlFileResolver m_files; // Access to file names (with wildcards)
|
||||
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::unordered_map<string, std::unordered_map<string, uint64_t>>
|
||||
m_profileData; // Access to profile_data records
|
||||
uint8_t m_mode = NONE;
|
||||
|
|
@ -813,6 +858,11 @@ public:
|
|||
V3ControlModuleResolver& modules() { return m_modules; }
|
||||
V3ControlFileResolver& files() { return m_files; }
|
||||
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;
|
||||
}
|
||||
|
||||
void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) {
|
||||
// Empty key for hierarchical DPI wrapper costs.
|
||||
|
|
@ -1015,6 +1065,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) {
|
||||
const string& filename = nodep->fileline()->filename();
|
||||
V3ControlFile* const filep = V3ControlResolver::s().files().resolve(filename);
|
||||
|
|
@ -1084,6 +1148,516 @@ 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) {
|
||||
int index = 0;
|
||||
const char* const first = indexText.data();
|
||||
const char* const last = first + indexText.size();
|
||||
const auto [ptr, ec] = std::from_chars(first, last, index);
|
||||
if (ec != std::errc{} || ptr != last) return false;
|
||||
seg.m_indices.push_back(index);
|
||||
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 void appendNodeSignature(const V3ControlHierTreeNode* nodep, std::string& out) {
|
||||
// Leaf vars: "<name>=<attr>;" sorted by (name, attr).
|
||||
std::vector<std::pair<std::string, VAttrType>> vars = nodep->m_vars;
|
||||
std::sort(vars.begin(), vars.end(), [](const auto& a, const auto& b) {
|
||||
if (a.first != b.first) return a.first < b.first;
|
||||
return a.second.ascii() < b.second.ascii();
|
||||
});
|
||||
// NOCOMMIT -- is this too verbose?
|
||||
out += "v{";
|
||||
for (const auto& var : vars) {
|
||||
out += var.first;
|
||||
out += "=";
|
||||
out += var.second.ascii();
|
||||
out += ";";
|
||||
}
|
||||
out += "}";
|
||||
// Child scopes: "<key>(<kind>:<subsig>)" in sorted key order (std::map).
|
||||
out += "c{";
|
||||
for (const auto& child : nodep->m_children) {
|
||||
out += child.first;
|
||||
out += ":";
|
||||
out += std::to_string(static_cast<int>(child.second->m_kind));
|
||||
out += "(";
|
||||
appendNodeSignature(child.second.get(), out);
|
||||
out += ")";
|
||||
}
|
||||
out += "}";
|
||||
}
|
||||
|
||||
// -path'ed public marking as a string for V3Param uniquification and cloning
|
||||
std::string V3Control::cellPathPublicSignature(const std::string& cellInstancePath) {
|
||||
const auto& cellPaths = V3ControlResolver::s().hierVarCellPaths();
|
||||
const auto it = cellPaths.find(cellInstancePath);
|
||||
if (it == cellPaths.end()) return "";
|
||||
std::string sig;
|
||||
appendNodeSignature(it->second, sig);
|
||||
return sig;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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) {
|
||||
return V3ControlResolver::s().getHierWorkers(model);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,11 @@ public:
|
|||
VarSpecKind kind, const string& pattern, VAttrType type,
|
||||
AstSenTree* nodep);
|
||||
|
||||
static void addHierVarAttr(FileLine* fl, const string& path, VAttrType type);
|
||||
static void applyHierVarAttrs(AstNetlist* netlistp);
|
||||
static std::string cellPathPublicSignature(const std::string& cellInstancePath);
|
||||
static void applyHierVarScopes(AstNetlist* netlistp);
|
||||
|
||||
static void applyCase(AstCase* nodep);
|
||||
static void applyCoverageBlock(AstNodeModule* modulep, AstBegin* nodep);
|
||||
static void applyCoverageBlock(AstNodeModule* modulep, AstGenBlock* nodep);
|
||||
|
|
|
|||
|
|
@ -418,6 +418,7 @@ class DeadVisitor final : public VNVisitor {
|
|||
}
|
||||
bool mightElimVar(const AstVar* nodep) const {
|
||||
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->isTemp() && !nodep->isTrace()) return true;
|
||||
return m_elimUserVars; // Post-Trace can kill most anything
|
||||
|
|
|
|||
|
|
@ -1427,6 +1427,7 @@ public:
|
|||
|
||||
void V3LinkParse::linkParse(AstNetlist* rootp) {
|
||||
UINFO(4, __FUNCTION__ << ": ");
|
||||
V3Control::applyHierVarAttrs(rootp);
|
||||
{ LinkParseVisitor{rootp}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("linkparse", 0, dumpTreeEitherLevel() >= 6);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
#include "V3Param.h"
|
||||
|
||||
#include "V3Case.h"
|
||||
#include "V3Control.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3Hasher.h"
|
||||
|
|
@ -1960,6 +1961,14 @@ class ParamProcessor final {
|
|||
// Make sure constification worked
|
||||
// Must be a separate loop, as constant conversion may have changed some pointers.
|
||||
string longname = srcModp->name() + "_";
|
||||
// Clone for .vlt -path'ed variants
|
||||
if (VN_IS(nodep, Cell)) {
|
||||
const string pubSig = V3Control::cellPathPublicSignature(srcModp->someInstanceName());
|
||||
if (!pubSig.empty()) {
|
||||
any_overrides = true;
|
||||
longname += "__Vhier" + V3Hash{pubSig}.toString();
|
||||
}
|
||||
}
|
||||
if (debug() >= 9 && paramsp) paramsp->dumpTreeAndNext(cout, "- cellparams: ");
|
||||
|
||||
if (srcModp->hierBlock()) {
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ struct SplitVarImpl VL_NOT_FINAL {
|
|||
return reason;
|
||||
}
|
||||
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->isForceable()) return "it is forceable";
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -301,6 +301,10 @@ static void process() {
|
|||
// No more AstGenBlocks after this
|
||||
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
|
||||
// Also simplify pin connections to always be AssignWs in prep for V3Unknown
|
||||
V3Tristate::tristateAll(v3Global.rootp());
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
|||
-?"-module" { FL; return yVLT_D_MODULE; }
|
||||
-?"-mtask" { FL; return yVLT_D_MTASK; }
|
||||
-?"-param" { FL; return yVLT_D_PARAM; }
|
||||
-?"-path" { FL; return yVLT_D_PATH; }
|
||||
-?"-port" { FL; return yVLT_D_PORT; }
|
||||
-?"-rule" { FL; return yVLT_D_RULE; }
|
||||
-?"-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_MTASK "--mtask"
|
||||
%token<fl> yVLT_D_PARAM "--param"
|
||||
%token<fl> yVLT_D_PATH "--path"
|
||||
%token<fl> yVLT_D_PORT "--port"
|
||||
%token<fl> yVLT_D_RULE "--rule"
|
||||
%token<fl> yVLT_D_Q "--q"
|
||||
|
|
@ -8444,6 +8445,8 @@ vltItem:
|
|||
}}
|
||||
| vltVarAttrFront vltDModuleE vltDFTaskE vltVarAttrSpecE attr_event_controlE
|
||||
{ V3Control::addVarAttr($<fl>1, *$2, *$3, GRAMMARP->m_vltVarSpecKind, *$4, $1, $5); }
|
||||
| vltVarAttrFront vltDPath
|
||||
{ V3Control::addHierVarAttr($<fl>1, *$2, $1); }
|
||||
| vltVarAttrFrontDeprecated vltDModuleE vltDFTaskE vltVarAttrSpecE
|
||||
{ /* Historical, now has no effect */ }
|
||||
| vltInlineFront vltDModuleE vltDFTaskE
|
||||
|
|
@ -8569,6 +8572,10 @@ vltDModuleE<strp>: // [--module <arg>]
|
|||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
vltDPath<strp>: // --path <arg>
|
||||
yVLT_D_PATH str { $$ = $2; }
|
||||
;
|
||||
|
||||
vltDScope<strp>: // --scope <arg>
|
||||
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,20 @@
|
|||
%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_ISOLATE_ASSIGNMENTS' attribute does not support -path
|
||||
14 | isolate_assignments -path "top.i_mid_0.mid_tmp_b"
|
||||
| ^~~~~~~~~~~~~~~~~~~
|
||||
%Error: t/t_vlt_public_spec_hier_attr_bad.vlt:15:1: 'VAR_FORCEABLE' attribute does not support -path
|
||||
15 | forceable -path "top.i_mid_0.mid_tmp_b"
|
||||
| ^~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/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,15 @@
|
|||
// 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"
|
||||
isolate_assignments -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,18 @@
|
|||
#!/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,18 @@
|
|||
#!/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,24 @@
|
|||
#!/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