Support parameter arrays, bug683.

This commit is contained in:
Wilson Snyder 2014-04-01 23:16:16 -04:00
parent 091818483a
commit 28e35a64ea
11 changed files with 220 additions and 30 deletions

View File

@ -15,6 +15,8 @@ indicates the contributor was also the author of the fix; Thanks!
*** Add PINCONNECTEMPTY warning. [Holger Waechtler]
*** Support parameter arrays, bug683. [Jeremy Bennett]
**** Documentation fixes, bug723. [Glen Gibb]
**** Fix tracing of package variables and real arrays.

View File

@ -977,7 +977,6 @@ public:
void valuep(AstNode* nodep) { setOp3p(nodep); } // It's valuep, not constp, as may be more complicated than an AstConst
void addAttrsp(AstNode* nodep) { addNOp4p(nodep); }
AstNode* attrsp() const { return op4p()->castNode(); } // op4 = Attributes during early parse
bool hasSimpleInit() const { return (op3p() && !op3p()->castInitArray()); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
AstNodeDType* subDTypep() const { return dtypep() ? dtypep() : childDTypep(); }
void attrClockEn(bool flag) { m_attrClockEn = flag; }

View File

@ -106,7 +106,8 @@ private:
bool m_doShort; // Remove expressions that short circuit
bool m_doV; // Verilog, not C++ conversion
bool m_doGenerate; // Postpone width checking inside generate
AstNodeModule* m_modp; // Current module
AstNodeModule* m_modp; // Current module
AstArraySel* m_selp; // Current select
AstNode* m_scopep; // Current scope
AstAttrOf* m_attrp; // Current attribute
@ -1221,15 +1222,35 @@ private:
nodep->iterateChildren(*this);
m_attrp = oldAttr;
}
virtual void visit(AstArraySel* nodep, AstNUser*) {
nodep->bitp()->iterateAndNext(*this);
if (nodep->bitp()->castConst()
&& nodep->fromp()->castVarRef()
// Need to make sure it's an array object so don't mis-allow a constant (bug509.)
&& nodep->fromp()->castVarRef()->varp()
&& nodep->fromp()->castVarRef()->varp()->valuep()->castInitArray()) {
m_selp = nodep; // Ask visit(AstVarRef) to replace varref with const
}
nodep->fromp()->iterateAndNext(*this);
if (nodep->fromp()->castConst()) { // It did.
if (!m_selp) {
nodep->v3error("Illegal assignment of constant to unpacked array");
} else {
nodep->replaceWith(nodep->fromp()->unlinkFrBack());
}
}
m_selp = NULL;
}
virtual void visit(AstVarRef* nodep, AstNUser*) {
nodep->iterateChildren(*this);
if (!nodep->varp()) nodep->v3fatalSrc("Not linked");
bool did=false;
if (m_doV && nodep->varp()->hasSimpleInit() && !m_attrp) {
//if (debug()) nodep->varp()->valuep()->dumpTree(cout," visitvaref: ");
nodep->varp()->valuep()->iterateAndNext(*this);
if (operandConst(nodep->varp()->valuep())
&& !nodep->lvalue()
if (m_doV && nodep->varp()->valuep() && !m_attrp) {
//if (debug()) valuep->dumpTree(cout," visitvaref: ");
nodep->varp()->valuep()->iterateAndNext(*this); // May change nodep->varp()->valuep()
AstNode* valuep = nodep->varp()->valuep();
if (!nodep->lvalue()
&& ((!m_params // Can reduce constant wires into equations
&& m_doNConst
&& v3Global.opt.oConst()
@ -1237,11 +1258,23 @@ private:
&& nodep->varp()->isInput())
&& !nodep->varp()->isSigPublic())
|| nodep->varp()->isParam())) {
AstConst* constp = nodep->varp()->valuep()->castConst();
const V3Number& num = constp->num();
//UINFO(2,"constVisit "<<(void*)constp<<" "<<num<<endl);
replaceNum(nodep, num); nodep=NULL;
did=true;
if (operandConst(valuep)) {
const V3Number& num = valuep->castConst()->num();
//UINFO(2,"constVisit "<<(void*)valuep<<" "<<num<<endl);
replaceNum(nodep, num); nodep=NULL;
did=true;
}
else if (m_selp && valuep->castInitArray()) {
int bit = m_selp->bitConst();
AstNode* itemp = valuep->castInitArray()->initsp();
for (int n=0; n<bit && itemp; ++n, itemp=itemp->nextp()) {}
if (itemp->castConst()) {
const V3Number& num = itemp->castConst()->num();
//UINFO(2,"constVisit "<<(void*)valuep<<" "<<num<<endl);
replaceNum(nodep, num); nodep=NULL;
did=true;
}
}
}
}
if (!did && m_required) {
@ -1666,6 +1699,10 @@ private:
}
}
}
virtual void visit(AstInitArray* nodep, AstNUser*) {
// Constant if all children are constant
nodep->iterateChildren(*this);
}
// These are converted by V3Param. Don't constify as we don't want the from() VARREF to disappear, if any
// If output of a presel didn't get consted, chances are V3Param didn't visit properly
@ -1711,6 +1748,12 @@ private:
//-----
// Below lines are magic expressions processed by astgen
// TREE_SKIP_VISIT("AstNODETYPE") # Rename normal visit to visitGen and don't iterate
//-----
TREE_SKIP_VISIT("ArraySel");
//-----
// "AstNODETYPE { # bracket not paren
// $accessor_name, ...
// # ,, gets replaced with a , rather than &&
@ -2038,6 +2081,7 @@ public:
m_warn = false;
m_wremove = true; // Overridden in visitors
m_modp = NULL;
m_selp = NULL;
m_scopep = NULL;
m_attrp = NULL;
//

View File

@ -71,7 +71,7 @@ public:
string vfmt, char fmtLetter);
void emitVarDecl(AstVar* nodep, const string& prefixIfImp);
typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_STATIC, EVL_ALL} EisWhich;
typedef enum {EVL_IO, EVL_SIG, EVL_TEMP, EVL_PAR, EVL_ALL} EisWhich;
void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp);
void emitVarCtors();
bool emitSimpleOk(AstNodeMath* nodep);
@ -1309,7 +1309,9 @@ void EmitCImp::emitVarResets(AstNodeModule* modp) {
// Constructor deals with it
}
else if (varp->isParam()) {
if (!varp->hasSimpleInit()) nodep->v3fatalSrc("No init for a param?");
if (!varp->valuep()) nodep->v3fatalSrc("No init for a param?");
// If a simple CONST value we initialize it using an enum
// If an ARRAYINIT we initialize it using an initial block similar to a signal
//puts("// parameter "+varp->name()+" = "+varp->valuep()->name()+"\n");
}
else if (AstInitArray* initarp = varp->valuep()->castInitArray()) {
@ -1673,6 +1675,7 @@ void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& pref
case EVL_IO: doit = varp->isIO(); break;
case EVL_SIG: doit = (varp->isSignal() && !varp->isIO()); break;
case EVL_TEMP: doit = (varp->isTemp() && !varp->isIO()); break;
case EVL_PAR: doit = (varp->isParam() && !varp->valuep()->castConst()); break;
default: v3fatalSrc("Bad Case");
}
if (varp->isStatic() ? !isstatic : isstatic) doit=false;
@ -1828,6 +1831,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
puts("\n// PARAMETERS\n");
if (modp->isTop()) puts("// Parameters marked /*verilator public*/ for use by application code\n");
ofp()->putsPrivate(false); // public:
emitVarList(modp->stmtsp(), EVL_PAR, ""); // Only those that are non-CONST
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (AstVar* varp = nodep->castVar()) {
if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) {
@ -1837,7 +1841,7 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
if (varp->isWide()) { // Unsupported for output
puts("// enum WData "+varp->name()+" //wide");
} else if (!varp->valuep()->castConst()) { // Unsupported for output
puts("// enum IData "+varp->name()+" //not simple value");
//puts("// enum ..... "+varp->name()+" //not simple value, see variable above instead");
} else {
puts("enum ");
puts(varp->isQuad()?"_QData":"_IData");

View File

@ -230,8 +230,16 @@ private:
if (!nodep->user5SetOnce()) { // Process once
nodep->iterateChildren(*this);
if (nodep->isParam()) {
if (!nodep->hasSimpleInit()) { nodep->v3fatalSrc("Parameter without initial value"); }
if (!nodep->valuep()) { nodep->v3fatalSrc("Parameter without initial value"); }
V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init()
if (!nodep->valuep()->castConst()) { // Complex init, like an array
// Make a new INITIAL to set the value.
// This allows the normal array/struct handling code to properly initialize the parameter
nodep->addNext(new AstInitial(nodep->fileline(),
new AstAssign(nodep->fileline(),
new AstVarRef(nodep->fileline(), nodep, true),
nodep->valuep()->cloneTree(true))));
}
}
}
}

View File

@ -1277,6 +1277,13 @@ private:
patp->accept(*this,WidthVP(memp,BOTH).p());
// Convert to concat for now
AstNode* valuep = patp->lhssp()->unlinkFrBack();
if (valuep->castConst()) {
// Forming a AstConcat will cause problems with unsized (uncommitted sized) constants
if (AstNode* newp = WidthCommitVisitor::newIfConstCommitSize(valuep->castConst())) {
pushDeletep(valuep); valuep=NULL;
valuep = newp;
}
}
if (!newp) newp = valuep;
else {
AstConcat* concatp = new AstConcat(patp->fileline(), newp, valuep);
@ -1340,6 +1347,13 @@ private:
patp->accept(*this,WidthVP(patp->dtypep(),BOTH).p());
// Convert to InitArray or constify immediately
AstNode* valuep = patp->lhssp()->unlinkFrBack();
if (valuep->castConst()) {
// Forming a AstConcat will cause problems with unsized (uncommitted sized) constants
if (AstNode* newp = WidthCommitVisitor::newIfConstCommitSize(valuep->castConst())) {
pushDeletep(valuep); valuep=NULL;
valuep = newp;
}
}
if (arrayp->castUnpackArrayDType()) {
if (!newp) {
newp = new AstInitArray(nodep->fileline(), arrayp, valuep);

View File

@ -69,6 +69,22 @@ class WidthCommitVisitor : public AstNVisitor {
// AstVar::user1p -> bool, processed
AstUser1InUse m_inuser1;
public:
// METHODS
static AstConst* newIfConstCommitSize (AstConst* nodep) {
if ((nodep->dtypep()->width() != nodep->num().width())
|| !nodep->num().sized()) { // Need to force the number rrom unsized to sized
V3Number num (nodep->fileline(), nodep->dtypep()->width());
num.opAssign(nodep->num());
num.isSigned(nodep->isSigned());
AstConst* newp = new AstConst(nodep->fileline(), num);
newp->dtypeFrom(nodep);
return newp;
} else {
return NULL;
}
}
private:
// METHODS
void editDType(AstNode* nodep) {
@ -97,13 +113,7 @@ private:
virtual void visit(AstConst* nodep, AstNUser*) {
if (!nodep->dtypep()) nodep->v3fatalSrc("No dtype");
nodep->dtypep()->accept(*this); // Do datatype first
if ((nodep->dtypep()->width() != nodep->num().width())
|| !nodep->num().sized()) { // Need to force the number rrom unsized to sized
V3Number num (nodep->fileline(), nodep->dtypep()->width());
num.opAssign(nodep->num());
num.isSigned(nodep->isSigned());
AstConst* newp = new AstConst(nodep->fileline(), num);
newp->dtypeFrom(nodep);
if (AstConst* newp = newIfConstCommitSize(nodep)) {
nodep->replaceWith(newp);
AstNode* oldp = nodep; nodep = newp;
//if (debug()>4) oldp->dumpTree(cout," fixConstSize_old: ");

View File

@ -424,6 +424,11 @@ sub tree_line {
($typefunc->{uinfo} = $func) =~ s/[ \t\"\{\}]+/ /g;
push @{$self->{treeop}{$type}}, $typefunc;
}
elsif ($func =~ /TREE_SKIP_VISIT\s*\(\s* \"([^\"]*)\" \s*\)/sx) {
my $type = $1;
$self->{tree_skip_visit}{$type} = 1;
$::Classes{$type} or $self->error("Unknown node type: $type");
}
else {
$self->error("Unknown astgen op: $func");
}
@ -598,9 +603,12 @@ sub tree_base {
@out_for_type,
" }\n") if ($out_for_type[0]);
} elsif ($out_for_type[0]) { # Other types with something to print
my $skip = $self->{tree_skip_visit}{$type};
my $gen = $skip ? "Gen" : "";
$self->print(" // Generated by astgen\n",
" virtual void visit(Ast${type}* nodep, AstNUser*) {\n",
" nodep->iterateChildren(*this);\n",
" virtual void visit$gen(Ast${type}* nodep, AstNUser*) {\n",
($skip?"":
" nodep->iterateChildren(*this);\n"),
@out_for_type,
" }\n");
}

View File

@ -1892,10 +1892,6 @@ netId<strp>:
| idSVKwd { $$ = $1; $<fl>$=$<fl>1; }
;
sigId<varp>:
id { $$ = VARDONEA($<fl>1,*$1, NULL, NULL); }
;
sigAttrListE<nodep>:
/* empty */ { $$ = NULL; }
| sigAttrList { $$ = $1; }
@ -1958,7 +1954,8 @@ packed_dimension<rangep>: // ==IEEE: packed_dimension
param_assignment<varp>: // ==IEEE: param_assignment
// // IEEE: constant_param_expression
// // constant_param_expression: '$' is in expr
sigId sigAttrListE '=' expr { $$ = $1; $1->addAttrsp($2); $$->valuep($4); }
id/*new-parameter*/ variable_dimensionListE sigAttrListE '=' expr
/**/ { $$ = VARDONEA($<fl>1,*$1, $2, $3); $$->valuep($5); }
//UNSUP: exprOrDataType instead of expr
;

18
test_regress/t/t_param_array.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,86 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2013 by Jeremy Bennett.
module t (/*AUTOARG*/);
typedef enum int {
PADTYPE_DEFAULT = 32'd0,
PADTYPE_GPIO,
PADTYPE_VDD,
PADTYPE_GND
} t_padtype;
localparam int STR_PINID [0:15]
= '{
"DEF", "ERR", "ERR", "ERR", "ERR", "ERR", "ERR", "ERR",
"PA0", "PA1", "PA2", "PA3", "PA4", "PA5", "PA6", "PA7"
};
typedef struct packed {
t_padtype padtype;
int aux;
} t_pin_descriptor;
localparam t_pin_descriptor
PINOUT[ 1: 6]
= '{
'{default:0, padtype:PADTYPE_GPIO, aux:1},
'{default:0, padtype:PADTYPE_GPIO},
'{default:0, padtype:PADTYPE_GPIO},
'{default:0, padtype:PADTYPE_GPIO},
'{default:0, padtype:PADTYPE_VDD},
'{default:0, padtype:PADTYPE_GND}
};
localparam int PINOUT_SIZE = 6;
localparam int PINOUT_WA[1:PINOUT_SIZE][3]
= '{
'{0, PADTYPE_GPIO, 0},
'{1, PADTYPE_GPIO, 0},
'{2, PADTYPE_GPIO, 0},
'{5, PADTYPE_GPIO, 0},
'{6, PADTYPE_VDD, 0},
'{8, PADTYPE_GND , 0}
};
const int pinout_static_const[1:PINOUT_SIZE][3]
= '{
'{0, PADTYPE_GPIO, 0},
'{1, PADTYPE_GPIO, 0},
'{2, PADTYPE_GPIO, 0},
'{5, PADTYPE_GPIO, 0},
'{6, PADTYPE_VDD, 0},
'{8, PADTYPE_GND , 0}
};
// Make sure consants propagate
checkstr #(.PINID(STR_PINID[1]),
.EXP("ERR"))
substr1 ();
checkstr #(.PINID(STR_PINID[8]),
.EXP("PA0"))
substr8 ();
initial begin
$display("PINID1 %s", STR_PINID[1]);
$display("PINID8 %s", STR_PINID[8]);
if (STR_PINID[1] != "ERR") $stop;
if (STR_PINID[8] != "PA0") $stop;
if (pinout_static_const[0][0] != 0) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module checkstr;
parameter int PINID = " ";
parameter int EXP = " ";
initial begin
$display("PID %s EXP %s", PINID, EXP);
if (EXP != "ERR" && EXP != "PA0") $stop;
if (PINID != EXP) $stop;
end
endmodule