Support genvar indexes into arrayed cells, bug517.

This commit is contained in:
Wilson Snyder 2015-10-22 20:13:49 -04:00
parent b16ea8b719
commit 040b1b06d5
19 changed files with 486 additions and 24 deletions

View File

@ -7,6 +7,8 @@ indicates the contributor was also the author of the fix; Thanks!
** Add --vpi flag, and fix VPI linkage, bug969. [Arthur Kahlich]
** Support genvar indexes into arrayed cells, bug517. [Todd Strader]
** Support $sformatf, bug977. [Johan Bjork]
**** Add VerilatedScopeNameMap for introspection, bug966. [Todd Strader]

View File

@ -1311,6 +1311,7 @@ public:
ASTNODE_NODE_FUNCS(VarXRef, VARXREF)
virtual void dump(ostream& str);
string dotted() const { return m_dotted; }
void dotted(const string& dotted) { m_dotted = dotted; }
string prettyDotted() const { return prettyName(dotted()); }
string inlinedDots() const { return m_inlinedDots; }
void inlinedDots(const string& flag) { m_inlinedDots = flag; }
@ -1570,6 +1571,56 @@ public:
virtual void name(const string& name) { m_name = name; }
};
class AstCellRef : public AstNode {
// As-of-yet unlinkable reference into a cell
private:
string m_name; // Cell name
public:
AstCellRef(FileLine* fl,
string name, AstNode* cellp, AstNode* exprp)
: AstNode(fl)
, m_name(name) {
addNOp1p(cellp); addNOp2p(exprp); }
ASTNODE_NODE_FUNCS(CellRef, CELLREF)
// ACCESSORS
virtual string name() const { return m_name; } // * = Array name
AstNode* cellp() const { return op1p(); } // op1 = Cell
AstNode* exprp() const { return op2p(); } // op2 = Expression
};
class AstCellArrayRef : public AstNode {
// As-of-yet unlinkable reference into an array of cells
private:
string m_name; // Array name
public:
AstCellArrayRef(FileLine* fl,
string name, AstNode* selectExprp)
: AstNode(fl)
, m_name(name) {
addNOp1p(selectExprp); }
ASTNODE_NODE_FUNCS(CellArrayRef, CELLARRAYREF)
// ACCESSORS
virtual string name() const { return m_name; } // * = Array name
AstNode* selp() const { return op1p(); } // op1 = Select expression
};
class AstUnlinkedVarXRef : public AstNode {
// As-of-yet unlinkable VarXRef
private:
string m_name; // Var name
public:
AstUnlinkedVarXRef(FileLine* fl,
AstVarXRef* vxrp, string name, AstNode* crp)
: AstNode(fl)
, m_name(name) {
addNOp1p(vxrp); addNOp2p(crp); }
ASTNODE_NODE_FUNCS(UnlinkedVarXRef, UNLINKEDVARXREF)
// ACCESSORS
virtual string name() const { return m_name; } // * = Var name
AstVarXRef* varxrefp() const { return op1p()->castVarXRef(); } // op1 = VarXRef
AstNode* cellrefp() const { return op2p(); } // op1 = CellArrayRef or CellRef
};
class AstBind : public AstNode {
// Parents: MODULE
// Children: CELL

View File

@ -1446,7 +1446,7 @@ private:
}
m_selp = NULL;
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
virtual void visit(AstNodeVarRef* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (!nodep->varp()) nodep->v3fatalSrc("Not linked");
bool did=false;

View File

@ -348,14 +348,18 @@ private:
// This is quite similar to how classes work; when unpacked classes are better supported
// may remap interfaces to be more like a class.
if (!nodep->hasIfaceVar()) {
string varName = nodep->name()+"__Viftop"; // V3LinkDot looks for this naming
AstIfaceRefDType* idtypep = new AstIfaceRefDType(nodep->fileline(), nodep->name(), nodep->modp()->name());
idtypep->cellp(nodep); // Only set when real parent cell known
idtypep->ifacep(NULL); // cellp overrides
AstVar* varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName, VFlagChildDType(), idtypep);
varp->isIfaceParent(true);
nodep->addNextHere(varp);
nodep->hasIfaceVar(true);
if (!nodep->rangep()) {
string varName = nodep->name() + "__Viftop"; // V3LinkDot looks for this naming
AstIfaceRefDType *idtypep = new AstIfaceRefDType(nodep->fileline(), nodep->name(),
nodep->modp()->name());
idtypep->cellp(nodep); // Only set when real parent cell known
idtypep->ifacep(NULL); // cellp overrides
AstVar *varp = new AstVar(nodep->fileline(), AstVarType::IFACEREF, varName,
VFlagChildDType(), idtypep);
varp->isIfaceParent(true);
nodep->addNextHere(varp);
nodep->hasIfaceVar(true);
}
}
}
if (nodep->modp()) {

View File

@ -1322,12 +1322,15 @@ private:
DotPosition m_dotPos; // Scope part of dotted resolution
VSymEnt* m_dotSymp; // SymEnt for dotted AstParse lookup
AstDot* m_dotp; // Current dot
bool m_unresolved; // Unresolved, needs help from V3Param
AstNode* m_unlinkedScope;// Unresolved scope, needs corresponding VarXRef
bool m_dotErr; // Error found in dotted resolution, ignore upwards
string m_dotText; // String of dotted names found in below parseref
DotStates() { init(NULL); }
~DotStates() {}
void init(VSymEnt* curSymp) {
m_dotPos = DP_NONE; m_dotSymp = curSymp; m_dotp = NULL; m_dotErr = false; m_dotText = "";
m_unresolved = false; m_unlinkedScope = NULL;
}
string ascii() const {
static const char* names[] = { "NONE","PACKAGE","SCOPE","FINAL","MEMBER" };
@ -1335,6 +1338,7 @@ private:
sstr<<"ds="<<names[m_dotPos];
sstr<<" dse"<<(void*)m_dotSymp;
sstr<<" txt="<<m_dotText;
sstr<<" unr="<<m_unresolved;
return sstr.str();
}
} m_ds; // State to preserve across recursions
@ -1505,6 +1509,9 @@ private:
nodep->lhsp()->iterateAndNext(*this);
//if (debug()>=9) nodep->dumpTree("-dot-lho: ");
}
if (m_ds.m_unresolved && (nodep->lhsp()->castCellRef() || nodep->lhsp()->castCellArrayRef())) {
m_ds.m_unlinkedScope = nodep->lhsp();
}
if (!m_ds.m_dotErr) { // Once something wrong, give up
if (start && m_ds.m_dotPos==DP_SCOPE) m_ds.m_dotPos = DP_FINAL; // Top 'final' dot RHS is final RHS, else it's a DOT(DOT(x,*here*),real-rhs) which we consider a RHS
nodep->rhsp()->iterateAndNext(*this);
@ -1523,6 +1530,10 @@ private:
pushDeletep(nodep); VL_DANGLING(nodep);
} else { // Dot midpoint
AstNode* newp = nodep->rhsp()->unlinkFrBack();
if (m_ds.m_unresolved) {
AstCellRef* crp = new AstCellRef(nodep->fileline(), nodep->name(), nodep->lhsp()->unlinkFrBack(), newp);
newp = crp;
}
nodep->replaceWith(newp);
pushDeletep(nodep); VL_DANGLING(nodep);
}
@ -1641,17 +1652,24 @@ private:
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
}
else if (allowVar) {
AstNodeVarRef* newp;
AstNode* newp;
if (m_ds.m_dotText != "") {
newp = new AstVarXRef(nodep->fileline(), nodep->name(), m_ds.m_dotText, false); // lvalue'ness computed later
newp->varp(varp);
AstVarXRef* refp = new AstVarXRef(nodep->fileline(), nodep->name(), m_ds.m_dotText, false); // lvalue'ness computed later
refp->varp(varp);
m_ds.m_dotText = "";
if (m_ds.m_unresolved && m_ds.m_unlinkedScope) {
newp = new AstUnlinkedVarXRef(nodep->fileline(), refp->castVarXRef(), refp->name(), m_ds.m_unlinkedScope->unlinkFrBack());
m_ds.m_unlinkedScope = NULL;
m_ds.m_unresolved = false;
} else {
newp = refp;
}
} else {
newp = new AstVarRef(nodep->fileline(), nodep->name(), false); // lvalue'ness computed later
newp->varp(varp);
newp->packagep(foundp->packagep());
UINFO(9," new "<<newp<<endl);
AstVarRef* refp = new AstVarRef(nodep->fileline(), varp, false); // lvalue'ness computed later
refp->packagep(foundp->packagep());
newp = refp;
}
UINFO(9," new "<<newp<<endl);
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
m_ds.m_dotPos = DP_MEMBER;
ok = true;
@ -1906,12 +1924,9 @@ private:
if (nodep->user3SetOnce()) return;
nodep->lhsp()->iterateAndNext(*this);
if (m_ds.m_dotPos == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
if (AstConst* constp = nodep->rhsp()->castConst()) {
string index = AstNode::encodeNumber(constp->toSInt());
m_ds.m_dotText += "__BRA__"+index+"__KET__";
} else {
nodep->v3error("Unsupported: Non-constant inside []'s in the cell part of a dotted reference");
}
UINFO(9," deferring until after a V3Param pass: "<<nodep<<endl);
m_ds.m_dotText += "__BRA__??__KET__";
m_ds.m_unresolved = true;
// And pass up m_ds.m_dotText
}
// Pass dot state down to fromp()
@ -1923,6 +1938,11 @@ private:
nodep->attrp()->iterateAndNext(*this);
}
m_ds = lastStates;
if (m_ds.m_unresolved && m_ds.m_dotPos == DP_SCOPE) {
AstNode* exprp = nodep->bitp()->unlinkFrBack();
AstCellArrayRef* newp = new AstCellArrayRef(nodep->fileline(), nodep->fromp()->name(), exprp);
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
}
}
virtual void visit(AstNodePreSel* nodep, AstNUser*) {
// Excludes simple AstSelBit, see above
@ -2018,6 +2038,18 @@ private:
checkNoDot(nodep);
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
}
virtual void visit(AstCellRef* nodep, AstNUser*) {
UINFO(5," AstCellRef: "<<nodep<<" "<<m_ds.ascii()<<endl);
nodep->iterateChildren(*this);
}
virtual void visit(AstCellArrayRef* nodep, AstNUser*) {
UINFO(5," AstCellArrayRef: "<<nodep<<" "<<m_ds.ascii()<<endl);
// Expression already iterated
}
virtual void visit(AstUnlinkedVarXRef* nodep, AstNUser*) {
UINFO(5," AstCellArrayRef: "<<nodep<<" "<<m_ds.ascii()<<endl);
// No need to iterate, if we have a UnlinkedVarXRef, we're already done
}
virtual void visit(AstNode* nodep, AstNUser*) {
// Default: Just iterate
checkNoDot(nodep);

View File

@ -211,6 +211,14 @@ private:
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstCellArrayRef* nodep, AstNUser*) {
bool last_setRefLvalue = m_setRefLvalue;
{ // selp is not an lvalue
m_setRefLvalue = false;
nodep->selp()->iterateAndNext(*this);
}
m_setRefLvalue = last_setRefLvalue;
}
virtual void visit(AstNodePreSel* nodep, AstNUser*) {
bool last_setRefLvalue = m_setRefLvalue;
{ // Only set lvalues on the from

View File

@ -204,6 +204,9 @@ private:
if (AstNodeVarRef* varrefp = basefromp->castNodeVarRef()) { // Maybe varxref - so need to clone
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
varrefp->cloneTree(false)));
} else if (AstUnlinkedVarXRef* uvxrp = basefromp->castUnlinkedVarXRef()) { // Maybe varxref - so need to clone
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
uvxrp->cloneTree(false)));
} else if (AstMemberSel* fromp = basefromp->castMemberSel()) {
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE,
fromp->cloneTree(false)));

View File

@ -108,6 +108,8 @@ private:
typedef deque<AstCell*> CellList;
CellList m_cellps; // Cells left to process (in this module)
string m_unlinkedTxt; // Text for AstUnlinkedVarXRef
// METHODS
static int debug() {
static int level = -1;
@ -251,6 +253,41 @@ private:
virtual void visit(AstVarXRef* nodep, AstNUser*) {
nodep->varp(NULL); // Needs relink, as may remove pointed-to var
}
virtual void visit(AstUnlinkedVarXRef* nodep, AstNUser*) {
m_unlinkedTxt.clear();
nodep->cellrefp()->iterate(*this);
nodep->varxrefp()->dotted(m_unlinkedTxt);
nodep->replaceWith(nodep->varxrefp()->unlinkFrBack());
pushDeletep(nodep); VL_DANGLING(nodep);
}
virtual void visit(AstCellArrayRef* nodep, AstNUser*) {
V3Const::constifyParamsEdit(nodep->selp());
if (AstConst* constp = nodep->selp()->castConst()) {
string index = AstNode::encodeNumber(constp->toSInt());
m_unlinkedTxt += nodep->name() + "__BRA__"+index+"__KET__";
} else {
nodep->v3error("Could not expand constant selection inside dotted reference: "<<nodep->selp()->prettyName());
return;
}
}
virtual void visit(AstCellRef* nodep, AstNUser*) {
// Children must be CellArrayRef, CellRef or ParseRef
if (nodep->cellp()->castCellArrayRef() || nodep->cellp()->castCellRef()) {
nodep->cellp()->iterate(*this);
} else if (nodep->cellp()->castParseRef()) {
m_unlinkedTxt += nodep->cellp()->name();
} else {
nodep->v3error("Could not elaborate dotted reference (LHS): "<<nodep->cellp()->prettyName());
}
m_unlinkedTxt += ".";
if (nodep->exprp()->castCellArrayRef() || nodep->exprp()->castCellRef()) {
nodep->exprp()->iterate(*this);
} else if (nodep->exprp()->castParseRef()) {
m_unlinkedTxt += nodep->exprp()->name();
} else {
nodep->v3error("Could not elaborate dotted reference (RHS): "<<nodep->exprp()->prettyName());
}
}
// Generate Statements
virtual void visit(AstGenerate* nodep, AstNUser*) {

18
test_regress/t/t_crazy_sel.pl Executable file
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,42 @@
// DESCRIPTION: Verilator: Dotted reference that uses another dotted reference
// as the select expression
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2015 by Todd Strader.
interface foo_intf;
logic a;
endinterface
function integer the_other_func (input integer val);
return val;
endfunction
module t (/*AUTOARG*/);
genvar the_genvar;
generate
for (the_genvar = 0; the_genvar < 4; the_genvar++) begin: foo_loop
foo foo_inst();
end
endgenerate
bar bar_inst();
logic x;
assign x = foo_loop[bar_inst.THE_LP].foo_inst.y;
//localparam N = 2;
//assign x = foo_loop[N].foo_inst.y;
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module foo();
logic y;
endmodule
module bar();
localparam THE_LP = 2;
endmodule

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, bug517");
# Compile time only test
compile (
);

18
test_regress/t/t_genfor_hier.pl Executable file
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,23 @@
// DESCRIPTION: Verilator: Demonstrate deferred linking across module
// bondaries
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2015 by Todd Strader.
module m1();
logic v1;
endmodule
module t (/*AUTOARG*/);
for (genvar the_genvar = 0; the_genvar < 4; the_genvar++) begin : m1_b
m1 m1_inst();
end
for (genvar the_other_genvar = 0; the_other_genvar < 4; the_other_genvar++) begin
always_comb m1_b[the_other_genvar].m1_inst.v1 = 1'b0;
end
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

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,79 @@
// DESCRIPTION: Verilator: Functionally demonstrate an array of interfaces
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2015 by Todd Strader.
interface foo_intf;
logic a;
modport source (
output a
);
modport sink (
input a
);
endinterface
function integer identity (input integer val);
return val;
endfunction
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
localparam N = 5;
logic [N-1:0] a_in;
logic [N-1:0] a_out;
logic [N-1:0] ack_out;
foo_intf foos [N-1:0] ();
// Deferred link dotting with genvars
generate
genvar i;
for (i = 0; i < N-4; i++) begin : someLoop
assign ack_out[i] = a_in[i];
assign foos[i].a = a_in[i];
assign a_out[i] = foos[i].a;
end
endgenerate
// Defferred link dotting with localparam
localparam THE_LP = N-3;
assign ack_out[THE_LP] = a_in[THE_LP];
assign foos[THE_LP].a = a_in[THE_LP];
assign a_out[THE_LP] = foos[THE_LP].a;
// Defferred link dotting with arithmetic expression
assign ack_out[N-2] = a_in[N-2];
assign foos[N-2].a = a_in[N-2];
assign a_out[N-2] = foos[N-2].a;
// Defferred link dotting with funcrefs
assign ack_out[identity(N-1)] = a_in[identity(N-1)];
assign foos[identity(N-1)].a = a_in[identity(N-1)];
assign a_out[identity(N-1)] = foos[identity(N-1)].a;
initial a_in = '0;
always @(posedge clk) begin
a_in <= a_in + { {N-1 {1'b0}}, 1'b1 };
if (ack_out != a_out) begin
$display("%%Error: Interface and non-interface paths do not match: 0b%b 0b%b",
ack_out, a_out);
$stop;
end
if (& a_in) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,21 @@
#!/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 (
fails=>1,
expect=>
'%Error: t/t_interface_array_bad.v:\d+: Expecting expression to be constant, but variable isn\'t const: bar
%Error: t/t_interface_array_bad.v:\d+: Could not expand constant selection inside dotted reference: bar
%Error: t/t_interface_array_bad.v:\d+: Can\'t find definition of \'a\' in dotted signal: .a
%Error: Known scopes under \'a\':.*
%Error: Exiting due to.*',
);
ok(1);
1;

View File

@ -0,0 +1,53 @@
// DESCRIPTION: Verilator: Demonstrate deferred linking error messages
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2015 by Todd Strader.
interface foo_intf;
logic a;
endinterface
function integer the_other_func (input integer val);
return val;
endfunction
module t (/*AUTOARG*/);
localparam N = 4;
foo_intf foos [N-1:0] ();
logic [ 7 : 0 ] bar;
// Non-constant dotted select is not allowed
assign foos[bar].a = 1'b1;
baz baz_inst ();
// Unsure how to produce V3Param AstCellRef visitor errors
//assign baz_inst.x = 1'b1;
//assign baz_inst.N = 1'b1;
//assign baz_inst.7 = 1'b1;
//assign baz_inst.qux_t = 1'b1;
//assign baz_inst.the_func = 1'b1;
//assign baz_inst.the_lp = 1'b1;
//assign bar.x = 1'b1;
//assign fake_inst.x = 1'b1;
//assign the_other_func.x = 1'b1;
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module baz;
typedef integer qux_t;
function integer the_func (input integer val);
return val;
endfunction
localparam the_lp = 5;
endmodule

View File

@ -0,0 +1,20 @@
#!/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.
$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug978");
compile (
);
execute (
check_finished=>1,
);
ok(1);
1;

View File

@ -0,0 +1,35 @@
// DESCRIPTION: Verilator: Connecting an interface array slice to a module's portmap
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2015 by Todd Strader.
interface foo_intf;
logic a;
endinterface
module foo_mod
(
foo_intf foo
);
endmodule
module t (/*AUTOARG*/);
localparam N = 4;
foo_intf foos [N-1:0] ();
//foo_intf foos ();
foo_mod
foo_mod
(
.foo (foos[2])
//.foo (foos)
);
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule