Support class parameters () ()

This commit is contained in:
Arkadiusz Kozdra 2022-08-28 16:24:55 +02:00 committed by GitHub
parent 2358ced061
commit 0a3a15a66e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 449 additions and 211 deletions

View File

@ -3235,8 +3235,7 @@ public:
AstNodeModule* classOrPackagep() const {
AstNode* foundp = m_classOrPackageNodep;
while (auto* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep();
while (auto* const anodep = VN_CAST(foundp, ClassRefDType))
foundp = anodep->classOrPackagep();
if (auto* const anodep = VN_CAST(foundp, ClassRefDType)) foundp = anodep->classp();
return VN_CAST(foundp, NodeModule);
}
AstPackage* packagep() const { return VN_CAST(classOrPackageNodep(), Package); }

View File

@ -86,7 +86,7 @@ static void makeToStringMiddle(AstClass* nodep) {
}
}
}
if (nodep->extendsp() && nodep->extendsp()->classp()->user1()) {
if (nodep->extendsp()) {
string stmt = "out += ";
if (!comma.empty()) stmt += "\", \"+ ";
// comma = ", "; // Nothing further so not needed

View File

@ -2379,6 +2379,7 @@ private:
iterateChildren(nodep);
}
}
virtual void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
virtual void visit(AstPin* nodep) override { iterateChildren(nodep); }
void replaceLogEq(AstLogEq* nodep) {

View File

@ -471,9 +471,6 @@ private:
}
}
// Accelerate the recursion
// Must do statements to support Generates, math though...
virtual void visit(AstNodeMath*) override {}
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
// METHODS

View File

@ -1988,6 +1988,16 @@ private:
}
}
bool isParamedClassRef(const AstNode* nodep) {
if (const auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
if (classRefp->paramsp()) return true;
const auto* classp = classRefp->classOrPackageNodep();
while (const auto* typedefp = VN_CAST(classp, Typedef)) classp = typedefp->subDTypep();
return VN_IS(classp, ClassRefDType) && VN_AS(classp, ClassRefDType)->paramsp();
}
return false;
}
// VISITs
virtual void visit(AstNetlist* nodep) override {
// Recurse..., backward as must do packages before using packages
@ -2172,11 +2182,17 @@ private:
// if (!start) { nodep->lhsp()->v3error("Package reference may not be embedded in
// dotted reference"); m_ds.m_dotErr=true; }
m_ds.m_dotPos = DP_PACKAGE;
iterateAndNextNull(nodep->lhsp());
} else {
m_ds.m_dotPos = DP_SCOPE;
iterateAndNextNull(nodep->lhsp());
// if (debug() >= 9) nodep->dumpTree("-dot-lho: ");
}
if (m_statep->forPrimary() && isParamedClassRef(nodep->lhsp())) {
// Dots of paramed classes will be linked after deparametrization
m_ds.m_dotPos = DP_NONE;
return;
}
if (m_ds.m_unresolved
&& (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) {
m_ds.m_unlinkedScopep = nodep->lhsp();
@ -2517,13 +2533,24 @@ private:
if (start) m_ds = lastStates;
}
virtual void visit(AstClassOrPackageRef* nodep) override {
UINFO(9, " linkClassOrPackageRef " << m_ds.ascii() << " n=" << nodep << endl);
if (m_ds.m_dotPos == DP_PACKAGE) {
// Already under dot, so this is {ClassOrPackage} Dot {ClassOrPackage}
// m_ds.m_dotText communicates the cell prefix between stages
m_ds.m_dotPos = DP_PACKAGE;
// Class: Recurse inside or cleanup not founds
// checkNoDot not appropriate, can be under a dot
AstNode::user5ClearTree();
UASSERT_OBJ(m_statep->forPrimary() || nodep->classOrPackagep(), nodep,
"ClassRef has unlinked class");
UASSERT_OBJ(m_statep->forPrimary() || !nodep->paramsp(), nodep,
"class reference parameter not removed by V3Param");
VL_RESTORER(m_ds);
VL_RESTORER(m_pinSymp);
{
// ClassRef's have pins, so track
if (nodep->classOrPackagep()) {
m_pinSymp = m_statep->getNodeSym(nodep->classOrPackagep());
}
m_ds.init(m_curSymp);
UINFO(4, "(Backto) Link ClassOrPackageRef: " << nodep << endl);
iterateChildren(nodep);
}
// TODO we don't iterate pins yet, as class parameters are not supported
}
virtual void visit(AstVarRef* nodep) override {

View File

@ -557,15 +557,16 @@ class ParamProcessor final {
if ((newmodp->level() - srcModp->level()) >= (v3Global.opt.moduleRecursionDepth() - 2)) {
cellp->v3error("Exceeded maximum --module-recursion-depth of "
<< v3Global.opt.moduleRecursionDepth());
return;
}
// Keep tree sorted by level. Note: Different parametrizations of the same recursive module
// end up with the same level, which we will need to fix up at the end, as we do not know
// up front how recursive modules are expanded, and a later expansion might re-use an
// earlier expansion (see t_recursive_module_bug_2).
AstNodeModule* insertp = srcModp;
while (insertp->nextp()
AstNode* insertp = srcModp;
while (VN_IS(insertp->nextp(), NodeModule)
&& VN_AS(insertp->nextp(), NodeModule)->level() <= newmodp->level()) {
insertp = VN_AS(insertp->nextp(), NodeModule);
insertp = insertp->nextp();
}
insertp->addNextHere(newmodp);
@ -699,9 +700,9 @@ class ParamProcessor final {
}
}
void cellInterfaceCleanup(AstCell* nodep, AstNodeModule* srcModp, string& longnamer,
void cellInterfaceCleanup(AstPin* pinsp, AstNodeModule* srcModp, string& longnamer,
bool& any_overridesr, IfaceRefRefs& ifaceRefRefs) {
for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
for (AstPin* pinp = pinsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
const AstVar* const modvarp = pinp->modVarp();
if (modvarp->isIfaceRef()) {
AstIfaceRefDType* portIrefp = VN_CAST(modvarp->subDTypep(), IfaceRefDType);
@ -761,8 +762,73 @@ class ParamProcessor final {
}
}
bool nodeDeparamCommon(AstNode* nodep, AstNodeModule*& srcModpr, AstPin* paramsp,
AstPin* pinsp, bool any_overrides) {
// Make sure constification worked
// Must be a separate loop, as constant conversion may have changed some pointers.
// if (debug()) nodep->dumpTree(cout, "-cel2: ");
string longname = srcModpr->name() + "_";
if (debug() > 8 && paramsp) paramsp->dumpTreeAndNext(cout, "-cellparams: ");
if (srcModpr->hierBlock()) {
longname = parameterizedHierBlockName(srcModpr, paramsp);
any_overrides = longname != srcModpr->name();
} else {
for (AstPin* pinp = paramsp; pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
cellPinCleanup(nodep, pinp, srcModpr, longname /*ref*/, any_overrides /*ref*/);
}
}
IfaceRefRefs ifaceRefRefs;
cellInterfaceCleanup(pinsp, srcModpr, longname /*ref*/, any_overrides /*ref*/,
ifaceRefRefs /*ref*/);
if (!any_overrides) {
UINFO(8, "Cell parameters all match original values, skipping expansion.\n");
} else if (AstNodeModule* const paramedModp
= m_hierBlocks.findByParams(srcModpr->name(), paramsp, m_modp)) {
paramedModp->dead(false);
// We need to relink the pins to the new module
relinkPinsByName(pinsp, paramedModp);
srcModpr = paramedModp;
} else {
const string newname
= srcModpr->hierBlock() ? longname : moduleCalcName(srcModpr, longname);
const ModInfo* const modInfop
= moduleFindOrClone(srcModpr, nodep, paramsp, newname, ifaceRefRefs);
// We need to relink the pins to the new module
relinkPinsByName(pinsp, modInfop->m_modp);
UINFO(8, " Done with " << modInfop->m_modp << endl);
srcModpr = modInfop->m_modp;
}
// Delete the parameters from the cell; they're not relevant any longer.
if (paramsp) paramsp->unlinkFrBackWithNext()->deleteTree();
return any_overrides;
}
void cellDeparam(AstCell* nodep, AstNodeModule*& srcModpr) {
// Must always clone __Vrcm (recursive modules)
if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nodep->pinsp(),
nodep->recursive())) {
nodep->modp(srcModpr);
nodep->modName(srcModpr->name());
}
nodep->recursive(false);
}
void classRefDeparam(AstClassOrPackageRef* nodep, AstNodeModule*& srcModpr) {
if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nullptr, false))
nodep->classOrPackagep(srcModpr);
}
void classRefDeparam(AstClassRefDType* nodep, AstNodeModule*& srcModpr) {
if (nodeDeparamCommon(nodep, srcModpr, nodep->paramsp(), nullptr, false))
nodep->classp(VN_AS(srcModpr, Class));
}
public:
void cellDeparam(AstCell* nodep, AstNodeModule* modp, const string& someInstanceName) {
void nodeDeparam(AstNode* nodep, AstNodeModule*& srcModpr, AstNodeModule* modp,
const string& someInstanceName) {
m_modp = modp;
// Cell: Check for parameters in the instantiation.
// We always run this, even if no parameters, as need to look for interfaces,
@ -772,57 +838,18 @@ public:
if (debug() >= 10) nodep->dumpTree(cout, "-cell: ");
// Evaluate all module constants
V3Const::constifyParamsEdit(nodep);
AstNodeModule* const srcModp = nodep->modp();
srcModp->someInstanceName(someInstanceName + "." + nodep->name());
srcModpr->someInstanceName(someInstanceName + "." + nodep->name());
// Make sure constification worked
// Must be a separate loop, as constant conversion may have changed some pointers.
// if (debug()) nodep->dumpTree(cout, "-cel2: ");
string longname = srcModp->name() + "_";
bool any_overrides = false;
// Must always clone __Vrcm (recursive modules)
if (nodep->recursive()) any_overrides = true;
if (debug() > 8 && nodep->paramsp())
nodep->paramsp()->dumpTreeAndNext(cout, "-cellparams: ");
if (srcModp->hierBlock()) {
longname = parameterizedHierBlockName(srcModp, nodep->paramsp());
any_overrides = longname != srcModp->name();
if (auto* cellp = VN_CAST(nodep, Cell)) {
cellDeparam(cellp, srcModpr);
} else if (auto* classRefp = VN_CAST(nodep, ClassRefDType)) {
classRefDeparam(classRefp, srcModpr);
} else if (auto* classRefp = VN_CAST(nodep, ClassOrPackageRef)) {
classRefDeparam(classRefp, srcModpr);
} else {
for (AstPin* pinp = nodep->paramsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) {
cellPinCleanup(nodep, pinp, srcModp, longname /*ref*/, any_overrides /*ref*/);
}
}
IfaceRefRefs ifaceRefRefs;
cellInterfaceCleanup(nodep, srcModp, longname /*ref*/, any_overrides /*ref*/,
ifaceRefRefs /*ref*/);
if (!any_overrides) {
UINFO(8, "Cell parameters all match original values, skipping expansion.\n");
} else if (AstNodeModule* const paramedModp
= m_hierBlocks.findByParams(srcModp->name(), nodep->paramsp(), m_modp)) {
nodep->modp(paramedModp);
nodep->modName(paramedModp->name());
paramedModp->dead(false);
// We need to relink the pins to the new module
relinkPinsByName(nodep->pinsp(), paramedModp);
} else {
const string newname
= srcModp->hierBlock() ? longname : moduleCalcName(srcModp, longname);
const ModInfo* const modInfop
= moduleFindOrClone(srcModp, nodep, nodep->paramsp(), newname, ifaceRefRefs);
// Have child use this module instead.
nodep->modp(modInfop->m_modp);
nodep->modName(newname);
// We need to relink the pins to the new module
relinkPinsByName(nodep->pinsp(), modInfop->m_modp);
UINFO(8, " Done with " << modInfop->m_modp << endl);
nodep->v3fatalSrc("Expected module parametrization");
}
nodep->recursive(false);
// Delete the parameters from the cell; they're not relevant any longer.
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
UINFO(8, " Done with " << nodep << endl);
// if (debug() >= 10)
// v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree"));
@ -854,7 +881,8 @@ class ParamVisitor final : public VNVisitor {
bool m_iterateModule = false; // Iterating module body
string m_generateHierName; // Generate portion of hierarchy name
string m_unlinkedTxt; // Text for AstUnlinkedRef
std::deque<AstCell*> m_cellps; // Cells left to process (in current module)
std::multimap<bool, AstNode*> m_cellps; // Cells left to process (in current module)
std::multimap<int, AstNodeModule*> m_workQueue; // Modules left to process
// Map from AstNodeModule to set of all AstNodeModules that instantiates it.
std::unordered_map<AstNodeModule*, std::unordered_set<AstNodeModule*>> m_parentps;
@ -887,32 +915,40 @@ class ParamVisitor final : public VNVisitor {
// Process interface cells, then non-interface cells, which may reference an interface
// cell.
for (bool doInterface : {true, false}) {
for (AstCell* const cellp : m_cellps) {
if (doInterface != VN_IS(cellp->modp(), Iface)) continue;
while (!m_cellps.empty()) {
const auto itm = m_cellps.cbegin();
AstNode* const cellp = itm->second;
m_cellps.erase(itm);
// Visit parameters in the instantiation.
iterateChildren(cellp);
// Update path
string someInstanceName(modp->someInstanceName());
if (const string* const genHierNamep = cellp->user5u().to<string*>()) {
someInstanceName += *genHierNamep;
cellp->user5p(nullptr);
VL_DO_DANGLING(delete genHierNamep, genHierNamep);
}
// Apply parameter specialization
m_processor.cellDeparam(cellp, modp, someInstanceName);
// Add the (now potentially specialized) child module to the work queue
workQueue.emplace(cellp->modp()->level(), cellp->modp());
// Add to the hierarchy registry
m_parentps[cellp->modp()].insert(modp);
AstNodeModule* srcModp = nullptr;
if (const auto* modCellp = VN_CAST(cellp, Cell)) {
srcModp = modCellp->modp();
} else if (const auto* classRefp = VN_CAST(cellp, ClassOrPackageRef)) {
srcModp = classRefp->classOrPackagep();
} else if (const auto* classRefp = VN_CAST(cellp, ClassRefDType)) {
srcModp = classRefp->classp();
} else {
cellp->v3fatalSrc("Expected module parametrization");
}
// Update path
string someInstanceName(modp->someInstanceName());
if (const string* const genHierNamep = cellp->user5u().to<string*>()) {
someInstanceName += *genHierNamep;
cellp->user5p(nullptr);
VL_DO_DANGLING(delete genHierNamep, genHierNamep);
}
// Apply parameter specialization
m_processor.nodeDeparam(cellp, srcModp /* ref */, modp, someInstanceName);
// Add the (now potentially specialized) child module to the work queue
workQueue.emplace(srcModp->level(), srcModp);
// Add to the hierarchy registry
m_parentps[srcModp].insert(modp);
}
m_cellps.clear();
if (workQueue.empty()) std::swap(workQueue, m_workQueue);
} while (!workQueue.empty());
m_iterateModule = false;
@ -930,25 +966,24 @@ class ParamVisitor final : public VNVisitor {
if (modp->level() <= maxParentLevel) modp->level(maxParentLevel + 1);
}
// A generic visitor for cells and class refs
void visitCellOrClassRef(AstNode* nodep, bool isIface) {
// Must do ifaces first, so push to list and do in proper order
string* const genHierNamep = new string{m_generateHierName};
nodep->user5p(genHierNamep);
// Visit parameters in the instantiation.
iterateChildren(nodep);
m_cellps.emplace(!isIface, nodep);
}
// VISITORS
virtual void visit(AstNodeModule* nodep) override {
if (nodep->recursiveClone()) nodep->dead(true); // Fake, made for recursive elimination
if (nodep->dead()) return; // Marked by LinkDot (and above)
// Warn on unsupported parametrised class
if (VN_IS(nodep, Class)) {
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (const AstVar* const varp = VN_CAST(stmtp, Var)) {
if (varp->isParam()) {
varp->v3warn(E_UNSUPPORTED, "Unsupported: class parameters");
}
}
}
}
if (m_iterateModule) { // Iterating body
UINFO(4, " MOD-under-MOD. " << nodep << endl);
iterateChildren(nodep);
m_workQueue.emplace(nodep->level(), nodep); // Delay until current module is done
return;
}
@ -961,19 +996,10 @@ class ParamVisitor final : public VNVisitor {
}
virtual void visit(AstCell* nodep) override {
// Must do ifaces first, so push to list and do in proper order
string* const genHierNamep = new string(m_generateHierName);
nodep->user5p(genHierNamep);
m_cellps.push_back(nodep);
}
virtual void visit(AstClassRefDType* nodep) override {
if (nodep->paramsp()) {
nodep->paramsp()->v3warn(E_UNSUPPORTED, "Unsupported: parameterized classes");
pushDeletep(nodep->paramsp()->unlinkFrBackWithNext());
}
iterateChildren(nodep);
visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface));
}
virtual void visit(AstClassRefDType* nodep) override { visitCellOrClassRef(nodep, false); }
virtual void visit(AstClassOrPackageRef* nodep) override { visitCellOrClassRef(nodep, false); }
// Make sure all parameters are constantified
virtual void visit(AstVar* nodep) override {

View File

@ -2438,6 +2438,17 @@ private:
// though causes problems with t_class_forward.v, so for now avoided
// userIterateChildren(nodep->classp(), nullptr);
}
virtual void visit(AstClassOrPackageRef* nodep) override {
if (nodep->didWidthAndSet()) return;
userIterateChildren(nodep, nullptr);
}
virtual void visit(AstDot* nodep) override {
// We can only reach this from constify called during V3Param (so before linkDotParam)
// ... #(Cls#(...)::...) ...
// ^^~~~ this is our DOT
nodep->v3warn(E_UNSUPPORTED, "dotted expressions in parameters\n"
<< nodep->warnMore() << "... Suggest use a typedef");
}
virtual void visit(AstClassExtends* nodep) override {
if (nodep->didWidthAndSet()) return;
if (VN_IS(nodep->childDTypep(), ClassRefDType)) {

View File

@ -1,30 +0,0 @@
%Error-UNSUPPORTED: t/t_class_param.v:40:11: Unsupported: parameterized classes
: ... In instance t
40 | Cls #(.PBASE(4)) c4;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_class_param.v:42:12: Unsupported: parameterized classes
: ... In instance t
42 | Wrap #(.P(16)) w16;
| ^
%Error-UNSUPPORTED: t/t_class_param.v:13:24: Unsupported: class parameters
: ... In instance t
13 | class Wrap #(parameter P = 13);
| ^
%Error-UNSUPPORTED: t/t_class_param.v:21:15: Unsupported: class parameters
: ... In instance t
21 | localparam PMINUS1 = P - 1;
| ^~~~~~~
%Error-UNSUPPORTED: t/t_class_param.v:20:17: Unsupported: parameterized classes
: ... In instance t
20 | Cls#(PMINUS1 + 1) c1;
| ^
%Error-UNSUPPORTED: t/t_class_param.v:24:23: Unsupported: class parameters
: ... In instance t
24 | class Cls #(parameter PBASE = 12);
| ^~~~~
%Error-UNSUPPORTED: t/t_class_param.v:35:14: Unsupported: parameterized classes
: ... In instance t
35 | typedef Cls#(8) Cls8_t;
| ^
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -21,6 +21,17 @@ class Wrap #(parameter P = 13);
localparam PMINUS1 = P - 1; // Checking works when last
endclass
class Wrap2 #(parameter P = 35);
function int get_p;
return c1.get_p();
endfunction
function new;
c1 = new;
endfunction
Wrap#(PMINUS1 + 1) c1;
localparam PMINUS1 = P - 1; // Checking works when last
endclass
class Cls #(parameter PBASE = 12);
bit [PBASE-1:0] member;
function bit [PBASE-1:0] get_member;
@ -40,11 +51,13 @@ module t (/*AUTOARG*/);
Cls #(.PBASE(4)) c4;
Cls8_t c8;
Wrap #(.P(16)) w16;
Wrap2 #(.P(32)) w32;
initial begin
c12 = new;
c4 = new;
c8 = new;
w16 = new;
w32 = new;
if (Cls#()::PBASE != 12) $stop;
if (Cls#(4)::PBASE != 4) $stop;
if (Cls8_t::PBASE != 8) $stop;
@ -65,6 +78,7 @@ module t (/*AUTOARG*/);
if (c4.get_p() != 4) $stop;
if (c8.get_p() != 8) $stop;
if (w16.get_p() != 16) $stop;
if (w32.get_p() != 32) $stop;
// verilator lint_off WIDTH
c12.member = 32'haaaaaaaa;

View File

@ -0,0 +1,5 @@
%Error: t/t_class_param_circ_bad.v:14:4: Exceeded maximum --module-recursion-depth of 100
: ... In instance t
14 | ClsA #(PARAM+1) a;
| ^~~~
%Error: Internal Error: ../V3Param.cpp:#: should find just-made module

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

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, 2022 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
typedef class ClsB;
class ClsA #(parameter PARAM = 12);
ClsB #(PARAM+1) b;
endclass
class ClsB #(parameter PARAM = 12);
ClsA #(PARAM+1) a;
endclass
module t (/*AUTOARG*/);
ClsA #(.PARAM(15)) c; // Bad param name
endmodule

View File

@ -0,0 +1,21 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,52 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
// Code your testbench here
// or browse Examples
class Base #(parameter PBASE = 12);
bit [PBASE-1:0] member;
function bit [PBASE-1:0] get_member;
return member;
endfunction
function int get_p;
return PBASE;
endfunction
endclass
class Cls #(parameter P = 13) extends Base #(P);
endclass
typedef Cls#(8) Cls8_t;
// See also t_class_param_mod.v
module t (/*AUTOARG*/);
Cls #(.P(4)) c4;
Cls8_t c8;
initial begin
c4 = new;
c8 = new;
if (c4.PBASE != 4) $stop;
if (c8.PBASE != 8) $stop;
if (c4.get_p() != 4) $stop;
if (c8.get_p() != 8) $stop;
// verilator lint_off WIDTH
c4.member = 32'haaaaaaaa;
c8.member = 32'haaaaaaaa;
// verilator lint_on WIDTH
if (c4.member != 4'ha) $stop;
if (c4.get_member() != 4'ha) $stop;
if (c8.member != 8'haa) $stop;
if (c8.get_member() != 8'haa) $stop;
$display("c4 = %s", $sformatf("%p", c4));
if ($sformatf("%p", c4) != "'{member:'ha}") $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -11,12 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -32,17 +32,30 @@ class Wrap #(parameter P = 13);
localparam PMINUS1 = P - 1; // Checking works when last
endclass
class Wrap2 #(parameter P = 35);
function int get_p;
return c1.get_p();
endfunction
function new;
c1 = new;
endfunction
Wrap#(PMINUS1 + 1) c1;
localparam PMINUS1 = P - 1; // Checking works when last
endclass
typedef Cls#(8) Cls8_t;
Cls c12;
Cls #(.PBASE(4)) c4;
Cls8_t c8;
Wrap #(.P(16)) w16;
Wrap2 #(.P(32)) w32;
initial begin
c12 = new;
c4 = new;
c8 = new;
w16 = new;
w32 = new;
if (Cls#()::PBASE != 12) $stop;
if (Cls#(4)::PBASE != 4) $stop;
if (Cls8_t::PBASE != 8) $stop;
@ -63,6 +76,7 @@ endclass
if (c4.get_p() != 4) $stop;
if (c8.get_p() != 8) $stop;
if (w16.get_p() != 16) $stop;
if (w32.get_p() != 32) $stop;
// verilator lint_off WIDTH
c12.member = 32'haaaaaaaa;

View File

@ -1,10 +1,9 @@
%Error-UNSUPPORTED: t/t_class_param_nconst_bad.v:12:11: Unsupported: parameterized classes
: ... In instance t
%Error: t/t_class_param_nconst_bad.v:12:17: Expecting expression to be constant, but can't convert a RAND to constant.
: ... In instance t
12 | Cls #(.PARAM($random)) c;
| ^~~~~~~
%Error: t/t_class_param_nconst_bad.v:12:11: Can't convert defparam value to constant: Param 'PARAM' of 'Cls'
: ... In instance t
12 | Cls #(.PARAM($random)) c;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_class_param_nconst_bad.v:7:23: Unsupported: class parameters
: ... In instance t
7 | class Cls #(parameter PARAM = 12);
| ^~~~~
%Error: Exiting due to

View File

@ -0,0 +1,10 @@
%Error-UNSUPPORTED: t/t_class_param_nested_bad.v:51:23: dotted expressions in parameters
: ... In instance t
: ... Suggest use a typedef
51 | Wrap2 #(Wrap#(19)::PBASE * 2) w38;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Internal Error: t/t_class_param_nested_bad.v:51:29: ../V3Width.cpp:#: Node has no type
: ... In instance t
51 | Wrap2 #(Wrap#(19)::PBASE * 2) w38;
| ^

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 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
scenarios(simulator => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,64 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Arkadiusz Kozdra.
// SPDX-License-Identifier: CC0-1.0
typedef class Cls;
class Wrap #(parameter P = 13);
function int get_p;
return c1.get_p();
endfunction
function new;
c1 = new;
endfunction
Cls#(PMINUS1 + 1) c1;
localparam PMINUS1 = P - 1; // Checking works when last
endclass
class Wrap2 #(parameter P = 35);
function int get_p;
return c1.get_p();
endfunction
function new;
c1 = new;
endfunction
Wrap#(PMINUS1 + 1) c1;
localparam PMINUS1 = P - 1; // Checking works when last
endclass
class Cls #(parameter PBASE = 12);
bit [PBASE-1:0] member;
function bit [PBASE-1:0] get_member;
return member;
endfunction
static function int get_p;
return PBASE;
endfunction
typedef enum { E_PBASE = PBASE } enum_t;
endclass
typedef Cls#(8) Cls8_t;
module t (/*AUTOARG*/);
Cls c12;
Cls #(.PBASE(4)) c4;
Cls8_t c8;
Wrap #(.P(16)) w16;
Wrap2 #(.P(32)) w32;
Wrap2 #(Wrap#(19)::PBASE * 2) w38;
initial begin
c12 = new;
c4 = new;
c8 = new;
w16 = new;
w32 = new;
w38 = new;
if (w38.get_p() != 38) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,30 +0,0 @@
%Error-UNSUPPORTED: t/t_class_param_pkg.v:43:16: Unsupported: parameterized classes
: ... In instance t
43 | Pkg::Cls #(.PBASE(4)) c4;
| ^~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_class_param_pkg.v:45:17: Unsupported: parameterized classes
: ... In instance t
45 | Pkg::Wrap #(.P(16)) w16;
| ^
%Error-UNSUPPORTED: t/t_class_param_pkg.v:14:27: Unsupported: class parameters
: ... In instance t
14 | class Wrap #(parameter P = 13);
| ^
%Error-UNSUPPORTED: t/t_class_param_pkg.v:22:18: Unsupported: class parameters
: ... In instance t
22 | localparam PMINUS1 = P - 1;
| ^~~~~~~
%Error-UNSUPPORTED: t/t_class_param_pkg.v:21:20: Unsupported: parameterized classes
: ... In instance t
21 | Cls#(PMINUS1 + 1) c1;
| ^
%Error-UNSUPPORTED: t/t_class_param_pkg.v:25:26: Unsupported: class parameters
: ... In instance t
25 | class Cls #(parameter PBASE = 12);
| ^~~~~
%Error-UNSUPPORTED: t/t_class_param_pkg.v:36:22: Unsupported: parameterized classes
: ... In instance t
36 | typedef Pkg::Cls#(8) Cls8_t;
| ^
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -22,6 +22,17 @@ package Pkg;
localparam PMINUS1 = P - 1; // Checking works when last
endclass
class Wrap2 #(parameter P = 35);
function int get_p;
return c1.get_p();
endfunction
function new;
c1 = new;
endfunction
Wrap#(PMINUS1 + 1) c1;
localparam PMINUS1 = P - 1; // Checking works when last
endclass
class Cls #(parameter PBASE = 12);
bit [PBASE-1:0] member;
function bit [PBASE-1:0] get_member;
@ -43,11 +54,13 @@ module t (/*AUTOARG*/);
Pkg::Cls #(.PBASE(4)) c4;
Pkg::Cls8_t c8;
Pkg::Wrap #(.P(16)) w16;
Pkg::Wrap2 #(.P(32)) w32;
initial begin
c12 = new;
c4 = new;
c8 = new;
w16 = new;
w32 = new;
if (Pkg::Cls#()::PBASE != 12) $stop;
if (Pkg::Cls#(4)::PBASE != 4) $stop;
if (Pkg::Cls8_t::PBASE != 8) $stop;
@ -68,6 +81,7 @@ module t (/*AUTOARG*/);
if (c4.get_p() != 4) $stop;
if (c8.get_p() != 8) $stop;
if (w16.get_p() != 16) $stop;
if (w32.get_p() != 32) $stop;
// verilator lint_off WIDTH
c12.member = 32'haaaaaaaa;

View File

@ -1,18 +0,0 @@
%Error-UNSUPPORTED: t/t_class_vparam.v:11:26: Unsupported: parameterized classes
: ... In instance t
11 | typedef paramed_class_t#(real, 1) paramed_class_double_t;
| ^~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_class_vparam.v:13:56: Unsupported: class parameters
: ... In instance t
13 | virtual class vclass #(type CTYPE_t = arg_class_t, int I = 0);
| ^
%Error-UNSUPPORTED: t/t_class_vparam.v:14:58: Unsupported: parameterized classes
: ... In instance t
14 | pure virtual function void funcname(paramed_class_t #(CTYPE_t) v);
| ^~~~~~~
%Error-UNSUPPORTED: t/t_class_vparam.v:17:46: Unsupported: class parameters
: ... In instance t
17 | class paramed_class_t #(type TYPE = int, int I = 0);
| ^
%Error: Exiting due to

View File

@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
scenarios(simulator => 1);
compile(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
execute(
check_finished => 1,
) if !$Self->{vlt_all};
);
ok(1);
1;

View File

@ -8,16 +8,18 @@
typedef class paramed_class_t;
typedef class arg_class_t;
typedef paramed_class_t#(real, 1) paramed_class_double_t;
typedef paramed_class_t#(logic[3:0], 1) paramed_class_logic4_t;
virtual class vclass #(type CTYPE_t = arg_class_t, int I = 0);
pure virtual function void funcname(paramed_class_t #(CTYPE_t) v);
endclass
class paramed_class_t #(type TYPE = int, int I = 0);
TYPE memb;
endclass
class arg_class_t;
int ifield;
endclass
module t (/*AUTOARG*/
@ -26,7 +28,15 @@ module t (/*AUTOARG*/
);
input clk;
always @ (posedge clk) begin
vclass vir;
paramed_class_t#(arg_class_t) argu;
initial begin
argu = new;
argu.memb = new;
argu.memb.ifield = 1234;
// vir.funcname(argu);
if (argu.memb.ifield != 1234) $stop;
$write("*-* All Finished *-*\n");
$finish;
end