parent
18dd44e970
commit
8c252952db
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue