Support `config` instance clauses (#5891 partial) (#6745)

This commit is contained in:
Dan Ruelas-Petrisko 2025-12-06 06:11:20 -08:00 committed by GitHub
parent 18dd44e970
commit 8c252952db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 242 additions and 133 deletions

View File

@ -73,6 +73,17 @@ public:
string name() const override VL_MT_STABLE { return "*LIBRARY*"; }
};
class CellEdge final : public V3GraphEdge {
VL_RTTI_IMPL(CellEdge, V3GraphEdge)
AstCell* const m_cellp;
public:
CellEdge(V3Graph* graphp, V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cutable, AstCell* cellp)
: V3GraphEdge{graphp, fromp, top, weight, cutable}, m_cellp{cellp} {}
AstCell* cellp() const { return m_cellp; }
string name() const override VL_MT_STABLE { return cellp() ? cvtToHex(cellp()) + ' ' + cellp()->name() : ""; }
};
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp, V3EdgeFuncP edgeFuncp) {
if (const LinkCellsVertex* const vvertexp = vertexp->cast<LinkCellsVertex>()) {
vvertexp->modp()->v3warn(E_UNSUPPORTED,
@ -94,7 +105,7 @@ void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp, V3EdgeFuncP edgeFunc
// State to pass between config parsing and cell linking visitors.
struct LinkCellsState final {
// Set of possible top module names from command line and configs
std::unordered_set<std::string> m_topModuleNames;
std::vector<std::pair<std::string, std::string>> m_designs;
// Default library lists to search
std::vector<std::string> m_liblistDefault;
// Library lists for specific cells
@ -102,56 +113,84 @@ struct LinkCellsState final {
// Use list for specific cells (libname, cellname)
std::unordered_map<std::string, std::vector<std::pair<std::string, std::string>>>
m_uselistCell;
// Library lists for specific insts
std::unordered_map<std::string, std::vector<std::string>> m_liblistInst;
// Use list for specific insts (libname, cellname)
std::unordered_map<std::string, std::vector<std::pair<std::string, std::string>>>
m_uselistInst;
};
class LinkConfigsVisitor final : public VNVisitor {
// STATE
LinkCellsState& m_state; // Context for linking cells
bool m_isTop = false; // Whether we're in the top-level config
bool m_isDefault = false; // Whether we're currently in a default clause
string m_cell; // Current cell being processed
string m_hierInst; // Current hierarchical instance being processed
AstDot* m_dotp = nullptr; // Current dot being processed
// VISITORS
void visit(AstConfig* nodep) override {
const string fullTopName = v3Global.opt.work() + '.' + v3Global.opt.topModule();
const bool topMatch = (fullTopName == nodep->name());
if (topMatch) {
m_state.m_topModuleNames.erase(fullTopName);
for (AstConfigCell* cellp = nodep->designp(); cellp;
cellp = VN_AS(cellp->nextp(), ConfigCell)) {
m_state.m_topModuleNames.insert(cellp->name());
}
}
// We don't do iterateChildren here because we want to skip designp
iterateAndNextNull(nodep->itemsp());
VL_RESTORER(m_isTop);
const auto& fullName = std::pair<std::string, std::string>{nodep->libname(), nodep->configname()};
m_isTop = std::find(m_state.m_designs.begin(), m_state.m_designs.end(), fullName) != m_state.m_designs.end();
m_state.m_designs.erase(
std::remove(m_state.m_designs.begin(), m_state.m_designs.end(), fullName),
m_state.m_designs.end());
iterateChildren(nodep);
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
}
void visit(AstConfigCell* nodep) override {
if (m_isTop) m_state.m_designs.emplace_back(nodep->libname(), nodep->cellname());
}
void visit(AstConfigRule* nodep) override {
if (!nodep->cellp()) {
for (AstNode* usep = nodep->usep(); usep; usep = usep->nextp()) {
m_state.m_liblistDefault.push_back(usep->name());
}
VL_RESTORER(m_isDefault);
m_isDefault = true;
iterateAndNextNull(nodep->usep());
} else if (nodep->isCell()) {
string cellName = nodep->cellp()->name();
if (VN_IS(nodep->usep(), ParseRef)) {
m_state.m_liblistCell[cellName] = std::vector<std::string>{};
for (AstParseRef* usep = VN_AS(nodep->usep(), ParseRef); usep;
usep = VN_AS(usep->nextp(), ParseRef)) {
m_state.m_liblistCell[cellName].push_back(usep->name());
}
} else {
m_state.m_uselistCell[cellName]
= std::vector<std::pair<std::string, std::string>>{};
for (AstConfigUse* usep = VN_AS(nodep->usep(), ConfigUse); usep;
usep = VN_AS(usep->nextp(), ConfigUse)) {
m_state.m_uselistCell[cellName].push_back(
std::pair<std::string, std::string>{usep->libname(), usep->cellname()});
}
}
VL_RESTORER(m_cell);
m_cell = nodep->cellp()->name();
iterateAndNextNull(nodep->usep());
} else {
if (VN_IS(nodep->usep(), ParseRef)) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: config inst liblist rule");
} else {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: config inst use rule");
VL_RESTORER(m_hierInst);
{
VL_RESTORER(m_dotp);
m_dotp = VN_AS(nodep->cellp(), Dot);
iterateAndNextNull(nodep->cellp());
}
iterateAndNextNull(nodep->usep());
}
}
void visit(AstDot* nodep) override {
iterateChildren(nodep);
}
void visit(AstParseRef* nodep) override {
if (m_isDefault) {
m_state.m_liblistDefault.emplace_back(nodep->name());
} else if (!m_cell.empty()) {
m_state.m_liblistCell[m_cell].emplace_back(nodep->name());
} else if (m_dotp) {
m_hierInst += m_hierInst.empty() ? nodep->name() : '.' + nodep->name();
} else if (!m_hierInst.empty()) {
m_state.m_liblistInst[m_hierInst].emplace_back(nodep->name());
}
}
void visit(AstConfigUse* nodep) override {
if (nodep->isConfig()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: hierarchical config rule");
} else if (!m_cell.empty()) {
m_state.m_uselistCell[m_cell].emplace_back(nodep->libname(), nodep->cellname());
} else if (m_dotp) {
m_hierInst += m_hierInst.empty() ? nodep->name() : '.' + nodep->name();
} else if (!m_hierInst.empty()) {
m_state.m_uselistInst[m_hierInst].emplace_back(
nodep->libname(), nodep->cellname());
}
}
@ -162,9 +201,8 @@ public:
LinkConfigsVisitor(AstNetlist* nodep, LinkCellsState& state)
: m_state{state} {
// Initialize top module from command line option
if (!m_state.m_topModuleNames.size() && !v3Global.opt.topModule().empty()) {
const string fullTopName = v3Global.opt.work() + '.' + v3Global.opt.topModule();
m_state.m_topModuleNames.insert(fullTopName);
if (!m_state.m_designs.size() && !v3Global.opt.topModule().empty()) {
m_state.m_designs.emplace_back(v3Global.opt.work(), v3Global.opt.topModule());
}
iterate(nodep);
}
@ -206,6 +244,10 @@ class LinkCellsVisitor final : public VNVisitor {
const V3GraphEdge* const edgep = new V3GraphEdge{&m_graph, fromp, top, weight, cuttable};
UINFO(9, " newEdge " << edgep << " " << fromp->name() << " -> " << top->name());
}
void cellEdge(V3GraphVertex* fromp, V3GraphVertex* top, int weight, bool cuttable, AstCell* cellp) {
const V3GraphEdge* const edgep = new CellEdge{&m_graph, fromp, top, weight, cuttable, cellp};
UINFO(9, " cellEdge " << edgep << " " << fromp->name() << " -> " << top->name());
}
void insertModInLib(const string& name, const string& libname, AstNodeModule* nodep) {
// Be able to find the module under it's library using the name it was given
VSymEnt* libSymp = m_mods.rootp()->findIdFlat(libname);
@ -292,10 +334,78 @@ class LinkCellsVisitor final : public VNVisitor {
}
}
AstCell* findCellByHier(AstNetlist* nodep, const std::string& hierPath) {
std::stringstream ss(hierPath);
std::string top;
bool topFound = false;
std::getline(ss, top, '.');
for (auto const& pair : m_state.m_designs) {
if (top == pair.second) {
topFound = true;
break;
}
}
if (!topFound) {
nodep->v3error("Can't find top-level module for instance path: '" << hierPath << "'");
V3Error::abortIfErrors();
return nullptr;
}
const V3GraphVertex* vtx = m_topVertexp;
const CellEdge* finalEdgep = nullptr;
std::string seg;
while (std::getline(ss, seg, '.')) {
finalEdgep = nullptr;
for (const V3GraphEdge& edge : vtx->outEdges()) {
if (const CellEdge* const cedgep = edge.cast<CellEdge>()) {
if (cedgep->cellp()->name() == seg) {
vtx = cedgep->top();
finalEdgep = cedgep;
break;
}
}
}
if (!finalEdgep) return nullptr;
}
return finalEdgep->cellp();
}
// VISITORS
void visit(AstNetlist* nodep) override {
readModNames();
iterateChildren(nodep);
// Replace instance module pointers based on config
AstNodeModule* modp = nullptr;
AstCell* cellp = nullptr;
// Search liblists for each instance
for (auto const& pair : m_state.m_liblistInst) {
cellp = findCellByHier(nodep, pair.first);
if (!cellp) continue;
for (auto const& libname : pair.second) {
modp = findModuleLibSym(cellp->modName(), libname);
if (modp) {
cellp->modp(modp);
break;
}
}
}
// Search uselists for each instance
for (auto const& pair : m_state.m_uselistInst) {
cellp = findCellByHier(nodep, pair.first);
for (auto const& u : pair.second) {
modp = findModuleLibSym(u.second, u.first);
if (modp) {
cellp->modp(modp);
break;
}
}
}
// Find levels in graph
m_graph.removeRedundantEdgesMax(&V3GraphEdge::followAlwaysTrue);
if (dumpGraphLevel()) m_graph.dumpDotFilePrefixed("linkcells");
@ -348,8 +458,9 @@ class LinkCellsVisitor final : public VNVisitor {
if (VN_IS(nodep, Iface) || VN_IS(nodep, Package)) {
nodep->inLibrary(true); // Interfaces can't be at top, unless asked
}
const string fullName = nodep->libname() + "." + nodep->name();
const bool topMatch = (m_state.m_topModuleNames.count(fullName) > 0);
auto const& fullName = std::pair<std::string, std::string>(nodep->libname(), nodep->name());
const bool topMatch = std::find(m_state.m_designs.begin(), m_state.m_designs.end(), fullName) != m_state.m_designs.end();
if (topMatch) {
m_topVertexp = vertex(nodep);
UINFO(2, "Link --top-module: " << nodep);
@ -507,7 +618,7 @@ class LinkCellsVisitor final : public VNVisitor {
} else { // Non-recursive
// Track module depths, so can sort list from parent down to children
nodep->modp(cellmodp);
newEdge(vertex(m_modp), vertex(cellmodp), 1, false);
cellEdge(vertex(m_modp), vertex(cellmodp), 1, false, nodep);
}
}
}

View File

@ -1,11 +1,14 @@
%Error-UNSUPPORTED: t/t_config_hier.v:36:3: Unsupported: config inst use rule
%Error-UNSUPPORTED: t/t_config_hier.v:36:18: Unsupported: hierarchical config rule
36 | instance t.u_1 use work.cfg2 :config;
| ^~~~~~~~
| ^~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_config_hier.v:41:3: Unsupported: config inst use rule
%Error-UNSUPPORTED: t/t_config_hier.v:41:18: Unsupported: hierarchical config rule
41 | instance t.u_1 use cfg2 :config;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_config_hier.v:62:3: Unsupported: config inst use rule
62 | instance u_bb use work.c2_bb;
| ^~~~~~~~
| ^~~
%Error-UNSUPPORTED: t/t_config_hier.v:46:16: Unsupported: hierarchical config rule
46 | cell work.m1 use work.cfg2 :config;
| ^~~
%Error-UNSUPPORTED: t/t_config_hier.v:51:11: Unsupported: hierarchical config rule
51 | cell m1 use cfg2 :config;
| ^~~
%Error: Exiting due to

View File

@ -59,5 +59,5 @@ endconfig
// Base usage
config cfg2;
design c2_b;
instance u_bb use work.c2_bb;
instance c2_b.u_bb use work.c2_bb;
endconfig

View File

@ -0,0 +1,5 @@
*-* All Finished *-*
liba:m1 %m=t.u_1 %l=liba.m1
liba:m3 %m=t.u_2.u_23 %l=liba.m3
libb:m2 %m=t.u_2 %l=libb.m2
libb:m3 %m=t.u_1.u_13 %l=libb.m3

24
test_regress/t/t_config_inst.py Executable file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=[
'--binary', '--top cfg1', '--work liba', 't/t_config_work__liba.v', '--work libb',
't/t_config_work__libb.v'
])
test.execute()
# Sort so that 'initial' scheduling order is not relevant
test.files_identical_sorted(test.run_log_filename, test.golden_filename, is_logfile=True)
test.passes()

View File

@ -0,0 +1,19 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t;
// Test config allows selecting two different libraries for these instances
m1 u_1();
m2 u_2();
final $write("*-* All Finished *-*\n");
endmodule
config cfg1;
design t;
// Use libb's version of m3 for m1 and liba's version of m3 for m2
instance t.u_1.u_13 liblist libb;
instance t.u_2.u_23 use liba.m3;
endconfig

View File

@ -0,0 +1,3 @@
%Error: Can't find top-level module for instance path: 't2.u_2.u_23'
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -9,8 +9,11 @@
import vltest_bootstrap
test.scenarios('vlt')
test.scenarios('simulator')
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
test.lint(verilator_flags2=[
'--binary', '--top cfg1', '--work liba', 't/t_config_work__liba.v', '--work libb',
't/t_config_work__libb.v'
], fails=test.vlt_all, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,21 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t;
// Test config allows selecting two different libraries for these instances
m1 u_1();
m2 u_2();
final $write("*-* All Finished *-*\n");
endmodule
config cfg1;
design t;
// Use libb's version of m3 for m1 and liba's version of m3 for m2
instance t.u_1.u_13 liblist libb;
// t2 not found, causes error
instance t2.u_2.u_23 use liba.m3;
endconfig

View File

@ -15,13 +15,8 @@ test.compile(verilator_flags2=[
"--binary", "--top cfg", "--work liba t/t_config_rules_sub.v",
"--work libb t/t_config_rules_sub.v", "--work libc t/t_config_rules_sub.v",
"--work libd t/t_config_rules_sub.v"
],
fails=True,
expect_filename=test.golden_filename)
])
#test.execute()
# Sort so that 'initial' scheduling order is not relevant
#test.files_identical_sorted(test.run_log_filename, test.golden_filename, is_logfile=True)
test.execute()
test.passes()

View File

@ -1,26 +0,0 @@
%Error-UNSUPPORTED: t/t_config_unsup.v:32:3: Unsupported: config inst use rule
32 | instance t.m20 liblist;
| ^~~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_config_unsup.v:33:3: Unsupported: config inst liblist rule
33 | instance t.m21 liblist libc;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_config_unsup.v:34:3: Unsupported: config inst liblist rule
34 | instance t.m22 liblist libc libd;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_config_unsup.v:35:3: Unsupported: config inst liblist rule
35 | instance t.m23 liblist libc libd;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_config_unsup.v:36:3: Unsupported: config inst liblist rule
36 | instance t.m24 liblist libc libd;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_config_unsup.v:39:3: Unsupported: config inst use rule
39 | instance t.m30 use cell_identifier;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_config_unsup.v:40:3: Unsupported: config inst use rule
40 | instance t.m31 use lib_id.cell_id;
| ^~~~~~~~
%Error-UNSUPPORTED: t/t_config_unsup.v:41:3: Unsupported: config inst use rule
41 | instance t.m32 use #();
| ^~~~~~~~
%Error: Exiting due to

View File

@ -1,49 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t;
m10 u_10();
m20 u_20();
m21 u_21();
m22 u_22();
m23 u_23();
m24 u_24();
m30 u_30();
m31 u_31();
m32 u_32();
m40 u_40();
m41 u_41();
m42 u_42();
m43 u_43();
final $write("*-* All Finished *-*\n");
endmodule
config cfg;
design t;
// Test uses m10
default liblist; // Ignored
default liblist liba libb;
// Test uses m20-29
instance t.m20 liblist; // Use parent's cell library
instance t.m21 liblist libc;
instance t.m22 liblist libc libd; // m22 in libc
instance t.m23 liblist libc libd; // m23 in libd
instance t.m24 liblist libc libd; // m24 in default (libb)
// Test uses m30-39
instance t.m30 use cell_identifier;
instance t.m31 use lib_id.cell_id;
instance t.m32 use #();
// Test uses m40-49
cell m40 liblist libc libd;
cell work.m41 liblist libc libd;
cell m42 use m42alt;
cell work.m43 use work.m43alt;
endconfig