Support self-recursive modules, bug659.

This commit is contained in:
Wilson Snyder 2017-11-18 17:42:35 -05:00
parent 21369bec95
commit a579e9273b
19 changed files with 229 additions and 31 deletions

View File

@ -4,6 +4,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 3.915 devel
*** Support self-recursive modules, bug659. [Sean Moore, et al]
**** Fix MacOS portability, bug1232. [Jeff Bush]
**** Detect MSB overflow when under VL_DEBUG, bug1238. [Junyi Xi]

View File

@ -1977,6 +1977,8 @@ private:
bool m_inLibrary:1; // From a library, no error if not used, never top level
bool m_dead:1; // LinkDot believes is dead; will remove in Dead visitors
bool m_internal:1; // Internally created
bool m_recursive:1; // Recursive module
bool m_recursiveClone:1; // If recursive, what module it clones, otherwise NULL
int m_level; // 1=top module, 2=cell off top module, ...
int m_varNum; // Incrementing variable number
int m_typeNum; // Incrementing implicit type number
@ -1984,7 +1986,8 @@ public:
AstNodeModule(FileLine* fl, const string& name)
: AstNode (fl)
,m_name(name), m_origName(name)
,m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false), m_internal(false)
,m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false)
,m_internal(false), m_recursive(false), m_recursiveClone(false)
,m_level(0), m_varNum(0), m_typeNum(0) { }
ASTNODE_BASE_FUNCS(NodeModule)
virtual void dump(ostream& str);
@ -2014,6 +2017,10 @@ public:
bool dead() const { return m_dead; }
void internal(bool flag) { m_internal = flag; }
bool internal() const { return m_internal; }
void recursive(bool flag) { m_recursive = flag; }
bool recursive() const { return m_recursive; }
void recursiveClone(bool flag) { m_recursiveClone = flag; }
bool recursiveClone() const { return m_recursiveClone; }
};
//######################################################################

View File

@ -779,6 +779,7 @@ void AstCCast::dump(ostream& str) {
}
void AstCell::dump(ostream& str) {
this->AstNode::dump(str);
if (recursive()) str<<" [RECURSIVE]";
if (modp()) { str<<" -> "; modp()->dump(str); }
else { str<<" ->UNLINKED:"<<modName(); }
}
@ -887,6 +888,8 @@ void AstNodeModule::dump(ostream& str) {
if (modPublic()) str<<" [P]";
if (inLibrary()) str<<" [LIB]";
if (dead()) str<<" [DEAD]";
if (recursiveClone()) str<<" [RECURSIVE-CLONE]";
else if (recursive()) str<<" [RECURSIVE]";
}
void AstPackageExport::dump(ostream& str) {
this->AstNode::dump(str);

View File

@ -1639,13 +1639,14 @@ private:
string m_modName; // Module the cell instances
AstNodeModule* m_modp; // [AfterLink] Pointer to module instanced
bool m_hasIfaceVar:1; // True if a Var has been created for this cell
bool m_recursive:1; // Self-recursive module
bool m_trace:1; // Trace this cell
public:
AstCell(FileLine* fl, const string& instName, const string& modName,
AstPin* pinsp, AstPin* paramsp, AstRange* rangep)
: AstNode(fl)
, m_name(instName), m_origName(instName), m_modName(modName)
, m_modp(NULL), m_hasIfaceVar(false), m_trace(true) {
, m_modp(NULL), m_hasIfaceVar(false), m_recursive(false), m_trace(true) {
addNOp1p(pinsp); addNOp2p(paramsp); setNOp3p(rangep); }
ASTNODE_NODE_FUNCS(Cell)
// No cloneRelink, we presume cloneee's want the same module linkages
@ -1670,6 +1671,8 @@ public:
void hasIfaceVar(bool flag) { m_hasIfaceVar = flag; }
void trace(bool flag) { m_trace=flag; }
bool isTrace() const { return m_trace; }
void recursive(bool flag) { m_recursive = flag; }
bool recursive() const { return m_recursive; }
};
class AstCellInline : public AstNode {

View File

@ -137,8 +137,10 @@ private:
// VISITORS
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
nodep->iterateChildren(*this);
checkAll(nodep);
if (!nodep->dead()) {
nodep->iterateChildren(*this);
checkAll(nodep);
}
m_modp = NULL;
}
virtual void visit(AstCFunc* nodep) {
@ -280,12 +282,14 @@ private:
AstNodeModule* nextmodp;
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; modp=nextmodp) {
nextmodp = modp->nextp()->castNodeModule();
if (modp->level()>2 && modp->user1()==0 && !modp->internal()) {
if (modp->dead() || (modp->level()>2 && modp->user1()==0 && !modp->internal())) {
// > 2 because L1 is the wrapper, L2 is the top user module
UINFO(4," Dead module "<<modp<<endl);
// And its children may now be killable too; correct counts
// Recurse, as cells may not be directly under the module but in a generate
DeadModVisitor visitor(modp);
if (!modp->dead()) { // If was dead didn't increment user1's
DeadModVisitor visitor(modp);
}
modp->unlinkFrBack()->deleteTree(); VL_DANGLING(modp);
retry = true;
}

View File

@ -181,6 +181,7 @@ public:
virtual string dotShape() const { return ""; }
virtual string dotStyle() const { return ""; }
virtual string dotName() const { return ""; }
virtual uint32_t rankAdder() const { return 1; }
virtual int sortCmp(const V3GraphVertex* rhsp) const {
// LHS goes first if of lower rank, or lower fanout
if (m_rank < rhsp->m_rank) return -1;

View File

@ -300,7 +300,7 @@ private:
vertexp->rank(currentRank);
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
if (followEdge(edgep)) {
vertexIterate(edgep->top(),currentRank+1);
vertexIterate(edgep->top(), currentRank + vertexp->rankAdder());
}
}
vertexp->user(2);

View File

@ -62,6 +62,8 @@ public:
virtual ~LinkCellsVertex() {}
AstNodeModule* modp() const { return m_modp; }
virtual string name() const { return modp()->name(); }
// Recursive modules get space for maximum recursion
virtual uint32_t rankAdder() const { return m_modp->recursiveClone() ? (1+v3Global.opt.moduleRecursionDepth()) : 1; }
};
class LibraryVertex : public V3GraphVertex {
@ -74,8 +76,9 @@ public:
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
if (LinkCellsVertex* vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
vvertexp->modp()->v3error("Recursive module (module instantiates itself): "
vvertexp->modp()->v3error("Unsupported: Recursive multiple modules (module instantiates something leading back to itself): "
<<vvertexp->modp()->prettyName());
vvertexp->modp()->v3error("Note self-recursion (module instantiating itself directly) is supported.");
V3Error::abortIfErrors();
} else { // Everything should match above, but...
v3fatalSrc("Recursive instantiations");
@ -90,10 +93,13 @@ private:
// NODE STATE
// Entire netlist:
// AstNodeModule::user1p() // V3GraphVertex* Vertex describing this module
// AstCell::user1() // bool Did it.
// AstNodeModule::user2p() // AstNodeModule* clone used for de-recursing
// AstCell::user1p() // ==V3NodeModule* if done, != if unprocessed
// AstCell::user2() // bool clone renaming completed
// Allocated across all readFiles in V3Global::readFiles:
// AstNode::user4p() // VSymEnt* Package and typedef symbol names
AstUser1InUse m_inuser1;
AstUser2InUse m_inuser2;
// STATE
V3InFilter* m_filterp; // Parser filter
@ -249,7 +255,7 @@ private:
// this move to post param, which would mean we do not auto-read modules
// and means we cannot compute module levels until later.
UINFO(4,"Link Bind: "<<nodep<<endl);
AstNodeModule* modp = resolveModule(nodep, nodep->name());
AstNodeModule* modp = resolveModule(nodep,nodep->name());
if (modp) {
AstNode* cellsp = nodep->cellsp()->unlinkFrBackWithNext();
// Module may have already linked, so need to pick up these new cells
@ -266,16 +272,58 @@ private:
virtual void visit(AstCell* nodep) {
// Cell: Resolve its filename. If necessary, parse it.
if (nodep->user1SetOnce()) return; // AstBind and AstNodeModule may call a cell twice
if (!nodep->modp()) {
// Execute only once. Complication is that cloning may result in user1 being set (for pre-clone)
// so check if user1() matches the m_mod, if 0 never did it, if !=, it is an unprocessed clone
bool cloned = (nodep->user1p() && nodep->user1p()!=m_modp);
if (nodep->user1p()==m_modp) return; // AstBind and AstNodeModule may call a cell twice
nodep->user1p(m_modp);
//
if (!nodep->modp() || cloned) {
UINFO(4,"Link Cell: "<<nodep<<endl);
// Use findIdFallback instead of findIdFlat; it doesn't matter for now
// but we might support modules-under-modules someday.
AstNodeModule* cellmodp = resolveModule(nodep, nodep->modName());
if (cellmodp) {
nodep->modp(cellmodp);
// Track module depths, so can sort list from parent down to children
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(cellmodp), 1, false);
if (cellmodp == m_modp
|| cellmodp->user2p() == m_modp) {
UINFO(1,"Self-recursive module "<<cellmodp<<endl);
cellmodp->recursive(true);
nodep->recursive(true);
if (!cellmodp->recursiveClone()) {
// In the non-Vrcm, which needs to point to Vrcm flavor
//
// Make a clone which this cell points to
// Later, the clone's cells will also point clone'd name
// This lets us link the XREFs between the (uncloned) children so
// they don't point to the same module which would
// break parameter resolution.
AstNodeModule* otherModp = cellmodp->user2p()->castNodeModule();
if (!otherModp) {
otherModp = cellmodp->cloneTree(false);
otherModp->name(otherModp->name()+"__Vrcm");
otherModp->user1p(NULL); // Need new vertex
otherModp->user2p(cellmodp);
otherModp->recursiveClone(true);
// user1 etc will retain its pre-clone value
cellmodp->user2p(otherModp);
v3Global.rootp()->addModulep(otherModp);
new V3GraphEdge(&m_graph, vertex(cellmodp), vertex(otherModp), 1, false);
}
cellmodp = otherModp;
nodep->modp(cellmodp);
}
else {
// In the Vrcm, which needs to point back to Vrcm flavor
// The cell already has the correct resolution (to Vrcm)
nodep->modp(cellmodp);
// We don't create a V3GraphEdge (as it would be circular)
}
}
else { // Non-recursive
// Track module depths, so can sort list from parent down to children
nodep->modp(cellmodp);
new V3GraphEdge(&m_graph, vertex(m_modp), vertex(cellmodp), 1, false);
}
}
}
// Remove AstCell(AstPin("",NULL)), it's a side effect of how we parse "()"
@ -310,6 +358,7 @@ private:
if (pinp->name()=="") pinp->name("__paramNumber"+cvtToStr(pinp->pinNum()));
}
if (nodep->modp()) {
nodep->modName(nodep->modp()->name());
// Note what pins exist
vl_unordered_set<string> ports; // Symbol table of all connected port names
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()->castPin()) {

View File

@ -87,6 +87,7 @@ private:
// NODE STATE
// Cleared on Netlist
// AstNodeModule::user1p() // VSymEnt*. Last symbol created for this node
// AstNodeModule::user2() // bool. Currently processing for recursion check
// ... Note maybe more than one, as can be multiple hierarchy places
// AstVarScope::user2p() // AstVarScope*. Base alias for AstInline of this signal
// AstVar::user2p() // AstFTask*. If a function variable, the task that links to the variable
@ -595,6 +596,7 @@ class LinkDotFindVisitor : public AstNVisitor {
AstBegin* m_beginp; // Current Begin/end block
AstNodeFTask* m_ftaskp; // Current function/task
bool m_inGenerate; // Inside a generate
bool m_inRecursion; // Inside a recursive module
int m_paramNum; // Parameter number, for position based connection
int m_beginNum; // Begin block number, 0=none seen
int m_modBeginNum; // Begin block number in module, 0=none seen
@ -682,7 +684,10 @@ class LinkDotFindVisitor : public AstNVisitor {
int oldParamNum = m_paramNum;
int oldBeginNum = m_beginNum;
int oldModBeginNum = m_modBeginNum;
if (doit) {
if (doit && nodep->user2()) {
nodep->v3error("Unsupported: Identically recursive module (module instantiates itself, without changing parameters): "
<<AstNode::prettyName(nodep->origName()));
} else if (doit) {
UINFO(2," Link Module: "<<nodep<<endl);
if (nodep->dead()) nodep->v3fatalSrc("Module in cell tree mislabeled as dead?");
VSymEnt* upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp();
@ -703,7 +708,9 @@ class LinkDotFindVisitor : public AstNVisitor {
m_modBeginNum = 0;
// m_modSymp/m_curSymp for non-packages set by AstCell above this module
// Iterate
nodep->user2(true);
nodep->iterateChildren(*this);
nodep->user2(false);
nodep->user4(true);
// Interfaces need another pass when signals are resolved
if (AstIface* ifacep = nodep->castIface()) {
@ -730,6 +737,7 @@ class LinkDotFindVisitor : public AstNVisitor {
virtual void visit(AstCell* nodep) {
UINFO(5," CELL under "<<m_scope<<" is "<<nodep<<endl);
// Process XREFs/etc inside pins
if (nodep->recursive() && m_inRecursion) return;
nodep->iterateChildren(*this);
// Recurse in, preserving state
string oldscope = m_scope;
@ -737,6 +745,7 @@ class LinkDotFindVisitor : public AstNVisitor {
VSymEnt* oldModSymp = m_modSymp;
VSymEnt* oldCurSymp = m_curSymp;
int oldParamNum = m_paramNum;
bool oldRecursion = m_inRecursion;
// Where do we add it?
VSymEnt* aboveSymp = m_curSymp;
string origname = AstNode::dedotName(nodep->name());
@ -755,6 +764,7 @@ class LinkDotFindVisitor : public AstNVisitor {
m_scope = m_scope+"."+nodep->name();
m_curSymp = m_modSymp = m_statep->insertCell(aboveSymp, m_modSymp, nodep, m_scope);
m_beginp = NULL;
m_inRecursion = nodep->recursive();
// We don't report NotFoundModule, as may be a unused module in a generate
if (nodep->modp()) nodep->modp()->accept(*this);
}
@ -763,6 +773,7 @@ class LinkDotFindVisitor : public AstNVisitor {
m_modSymp = oldModSymp;
m_curSymp = oldCurSymp;
m_paramNum = oldParamNum;
m_inRecursion = oldRecursion;
}
virtual void visit(AstCellInline* nodep) {
UINFO(5," CELLINLINE under "<<m_scope<<" is "<<nodep<<endl);
@ -1051,6 +1062,7 @@ public:
m_beginp = NULL;
m_ftaskp = NULL;
m_inGenerate = false;
m_inRecursion = false;
m_paramNum = 0;
m_beginNum = 0;
m_modBeginNum = 0;

View File

@ -1238,6 +1238,7 @@ V3Options::V3Options() {
m_dumpTree = 0;
m_ifDepth = 0;
m_inlineMult = 2000;
m_moduleRecursion = 100;
m_outputSplit = 0;
m_outputSplitCFuncs = 0;
m_outputSplitCTrace = 0;

View File

@ -109,6 +109,7 @@ class V3Options {
int m_dumpTree; // main switch: --dump-tree
int m_ifDepth; // main switch: --if-depth
int m_inlineMult; // main switch: --inline-mult
int m_moduleRecursion;// main switch: --module-recursion-depth
int m_outputSplit; // main switch: --output-split
int m_outputSplitCFuncs;// main switch: --output-split-cfuncs
int m_outputSplitCTrace;// main switch: --output-split-ctrace
@ -254,6 +255,7 @@ class V3Options {
int dumpTree() const { return m_dumpTree; }
int ifDepth() const { return m_ifDepth; }
int inlineMult() const { return m_inlineMult; }
int moduleRecursionDepth() const { return m_moduleRecursion; }
int outputSplit() const { return m_outputSplit; }
int outputSplitCFuncs() const { return m_outputSplitCFuncs; }
int outputSplitCTrace() const { return m_outputSplitCTrace; }

View File

@ -108,6 +108,8 @@ private:
typedef deque<AstCell*> CellList;
CellList m_cellps; // Cells left to process (in this module)
AstNodeModule* m_modp; // Current module being processed
string m_unlinkedTxt; // Text for AstUnlinkedRef
// METHODS
@ -218,6 +220,7 @@ private:
AstNodeModule* nodep = it->second;
m_todoModps.erase(it);
if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again
m_modp = nodep;
UINFO(4," MOD "<<nodep<<endl);
nodep->iterateChildren(*this);
// Note above iterate may add to m_todoModps
@ -233,6 +236,7 @@ private:
}
}
m_cellps.clear();
m_modp = NULL;
}
}
}
@ -245,6 +249,9 @@ private:
virtual void visit(AstNodeModule* nodep) {
if (nodep->dead()) {
UINFO(4," MOD-dead. "<<nodep<<endl); // Marked by LinkDot
} else if (nodep->recursiveClone()) {
UINFO(4," MOD-recursive-dead. "<<nodep<<endl); // Fake, made for recursive elimination
nodep->dead(true); // So Dead checks won't count references to it
} else if (nodep->level() <= 2 // Haven't added top yet, so level 2 is the top
|| nodep->castPackage()) { // Likewise haven't done wrapTopPackages yet
// Add request to END of modules left to process
@ -511,6 +518,7 @@ public:
// CONSTUCTORS
explicit ParamVisitor(AstNetlist* nodep) {
m_longId = 0;
m_modp = NULL;
//
nodep->accept(*this);
}
@ -539,6 +547,7 @@ void ParamVisitor::visitCell(AstCell* nodep) {
//if (debug()) nodep->dumpTree(cout,"-cel2:\t");
string longname = srcModp->name();
bool any_overrides = false;
if (nodep->recursive()) any_overrides = true; // Must always clone __Vrcm (recursive modules)
longname += "_";
if (debug()>8) nodep->paramsp()->dumpTreeAndNext(cout,"-cellparams:\t");
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=pinp->nextp()->castPin()) {
@ -679,7 +688,21 @@ void ParamVisitor::visitCell(AstCell* nodep) {
cellmodp = srcModp->cloneTree(false);
cellmodp->name(newname);
cellmodp->user5(false); // We need to re-recurse this module once changed
srcModp->addNextHere(cellmodp); // Keep tree sorted by cell occurrences
cellmodp->recursive(false);
cellmodp->recursiveClone(false);
nodep->recursive(false);
// Recursion may need level cleanups
if (cellmodp->level() <= m_modp->level()) cellmodp->level(m_modp->level()+1);
if ((cellmodp->level() - srcModp->level()) >= (v3Global.opt.moduleRecursionDepth() - 2)) {
nodep->v3error("Exceeded maximum --module-recursion-depth of "<<v3Global.opt.moduleRecursionDepth());
}
// Keep tree sorted by level
AstNodeModule* insertp = srcModp;
while (insertp->nextp()->castNodeModule()
&& insertp->nextp()->castNodeModule()->level() < cellmodp->level()) {
insertp = insertp->nextp()->castNodeModule();
}
insertp->addNextHere(cellmodp);
m_modNameMap.insert(make_pair(cellmodp->name(), ModInfo(cellmodp)));
iter = m_modNameMap.find(newname);
@ -742,6 +765,8 @@ void ParamVisitor::visitCell(AstCell* nodep) {
UINFO(8," Done with "<<cellmodp<<endl);
} // if any_overrides
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);

View File

@ -0,0 +1,18 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2004 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.
compile (
fails=>1,
expect=>
'.*%Error: t/t_inst_recurse2_bad.v:\d+: Unsupported: Identically recursive module \(module instantiates itself, without changing parameters\): looped
%Error: Exiting due to.*',
);
ok(1);
1;

View File

@ -0,0 +1,19 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2005 by Wilson Snyder.
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
looped looped ();
endmodule
module looped (/*AUTOARG*/);
looped looped ();
endmodule

View File

@ -8,11 +8,12 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
compile (
fails=>1,
expect=>
'.*%Error: t/t_inst_recurse_bad.v:\d+: Recursive module .module instantiates itself.: looped
fails=>1,
expect=>
'.*%Error: t/t_inst_recurse_bad.v:\d+: Unsupported: Recursive multiple modules \(module instantiates something leading back to itself\): looped
%Error: t/t_inst_recurse_bad.v:\d+: Note self-recursion \(module instantiating itself directly\) is supported.
%Error: Exiting due to.*',
);
);
ok(1);
1;

View File

@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug659");
compile (
);

View File

@ -25,7 +25,7 @@ module t (/*AUTOARG*/
pe (.out(valid), .outN(value[2:0]), .tripline(tripline));
// Aggregate outputs into a single result vector
wire [63:0] result = {59'h0, valid, value};
wire [63:0] result = {60'h0, valid, value};
// Test loop
always @ (posedge clk) begin
@ -77,9 +77,9 @@ module PriorityChoice (out, outN, tripline);
assign right = tripline[0];
always @(*) begin
out <= left || right ;
if(right) begin outN <= {1'b0}; end
else begin outN <= {1'b1}; end
out = left || right ;
if(right) begin outN = {1'b0}; end
else begin outN = {1'b1}; end
end
end else begin
PriorityChoice #(.OCODEWIDTH(OCODEWIDTH-1))
@ -98,11 +98,11 @@ module PriorityChoice (out, outN, tripline);
);
always @(*) begin
if(right) begin
out <= right;
outN <= {1'b0, rightN[OCODEWIDTH-2:0]};
out = right;
outN = {1'b0, rightN[OCODEWIDTH-2:0]};
end else begin
out <= left;
outN <= {1'b1, leftN[OCODEWIDTH-2:0]};
out = left;
outN = {1'b1, leftN[OCODEWIDTH-2:0]};
end
end
end

View File

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

View File

@ -0,0 +1,35 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Sean Moore.
module t (/*AUTOARG*/);
rec rec ();
endmodule
module rec;
parameter DEPTH = 1;
generate
if (DEPTH==1) begin
rec #(.DEPTH(DEPTH+1)) sub;
end
else if (DEPTH==2) begin
rec #(.DEPTH(DEPTH+1)) subb;
end
else if (DEPTH==3) begin
bottom #(.DEPTH(DEPTH+1)) bot;
end
endgenerate
endmodule
module bottom;
parameter DEPTH = 1;
initial begin
if (DEPTH!=4) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule