Support parameter arrays, bug683.
This commit is contained in:
parent
091818483a
commit
28e35a64ea
2
Changes
2
Changes
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
//
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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: ");
|
||||
|
|
|
|||
12
src/astgen
12
src/astgen
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue