Support enum.first/name and similar methods, bug848.

This commit is contained in:
Wilson Snyder 2014-11-28 20:34:23 -05:00
parent 93f1d7643d
commit c1593f856d
7 changed files with 321 additions and 73 deletions

View File

@ -11,6 +11,8 @@ indicates the contributor was also the author of the fix; Thanks!
** SystemPerl mode is deprecated and now untested.
*** Support enum.first/name and similar methods, bug848. [Jonathon Donaldson]
*** Add 'string' printing and comparisons, bug746, bug747, etc.
*** Inline C functions that are used only once, msg1525. [Jie Xu]

View File

@ -239,6 +239,13 @@ public:
//
DT_PUBLIC, // V3LinkParse moves to AstTypedef::attrPublic
//
ENUM_FIRST, // V3Width processes
ENUM_LAST, // V3Width processes
ENUM_NUM, // V3Width processes
ENUM_NEXT, // V3Width processes
ENUM_PREV, // V3Width processes
ENUM_NAME, // V3Width processes
//
MEMBER_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
//
VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes
@ -259,6 +266,7 @@ public:
"DIM_BITS", "DIM_DIMENSIONS", "DIM_HIGH", "DIM_INCREMENT", "DIM_LEFT",
"DIM_LOW", "DIM_RIGHT", "DIM_SIZE", "DIM_UNPK_DIMENSIONS",
"DT_PUBLIC",
"ENUM_FIRST", "ENUM_LAST", "ENUM_NUM", "ENUM_NEXT", "ENUM_PREV", "ENUM_NAME",
"MEMBER_BASE",
"VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC",
"VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD","VAR_PUBLIC_FLAT_RW",

View File

@ -881,6 +881,32 @@ public:
void fromp(AstNode* nodep) { setOp1p(nodep); }
};
class AstMethodSel : public AstNode {
// A reference to a member task (or function)
// We do not support generic member calls yet, so this is only enough to make built-in methods work
private:
string m_name; // Name of variable
public:
AstMethodSel(FileLine* fl, AstNode* fromp, VFlagChildDType, const string& name, AstNode* pinsp)
: AstNode(fl), m_name(name) {
setOp1p(fromp);
dtypep(NULL); // V3Width will resolve
addNOp2p(pinsp);
}
AstMethodSel(FileLine* fl, AstNode* fromp, const string& name, AstNode* pinsp)
:AstNode(fl), m_name(name) {
setOp1p(fromp);
addNOp2p(pinsp);
}
ASTNODE_NODE_FUNCS(MethodSel, METHODSEL)
virtual string name() const { return m_name; } // * = Var name
virtual void name(const string& name) { m_name = name; }
AstNode* fromp() const { return op1p()->castNode(); } // op1 = Extracting what (NULL=TBD during parsing)
void fromp(AstNode* nodep) { setOp1p(nodep); }
AstNode* pinsp() const { return op2p()->castNode(); } // op2 = Pin interconnection list
void addPinsp(AstNode* nodep) { addOp2p(nodep); }
};
class AstVar : public AstNode {
// A variable (in/out/wire/reg/param) inside a module
private:

View File

@ -1805,6 +1805,15 @@ private:
// EnumItemRef may be under a dot. Should already be resolved.
nodep->iterateChildren(*this);
}
virtual void visit(AstMethodSel* nodep, AstNUser*) {
// Created here so should already be resolved.
DotStates lastStates = m_ds;
{
m_ds.init(m_curSymp);
nodep->iterateChildren(*this);
}
m_ds = lastStates;
}
virtual void visit(AstVar* nodep, AstNUser*) {
checkNoDot(nodep);
nodep->iterateChildren(*this);
@ -1823,6 +1832,14 @@ private:
m_ds.m_dotp = NULL;
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
nodep->dotted(m_ds.m_dotText); // Maybe ""
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_MEMBER) {
// Found a Var, everything following is method call. {scope}.{var}.HERE {method} ( ARGS )
AstNode* varEtcp = m_ds.m_dotp->lhsp()->unlinkFrBack();
AstNode* argsp = NULL; if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext();
AstNode* newp = new AstMethodSel(nodep->fileline(), varEtcp, VFlagChildDType(), nodep->name(), argsp);
nodep->replaceWith(newp);
pushDeletep(nodep); nodep=NULL;
return;
} else {
checkNoDot(nodep);
}

View File

@ -410,12 +410,16 @@ private:
condp->deleteTree();
}
else if (!lvalue
&& !nodep->backp()->castArraySel()) { // Too complicated and slow if mid-multidimension
&& !nodep->backp()->castArraySel()) { // Too complicated and slow if mid-multidimension
// ARRAYSEL(...) -> COND(LT(bit<maxbit), ARRAYSEL(...), {width{1'bx}})
AstNRelinker replaceHandle;
nodep->unlinkFrBack(&replaceHandle);
V3Number xnum (nodep->fileline(), nodep->width());
xnum.setAllBitsX();
if (nodep->isString()) {
xnum = V3Number(V3Number::String(), nodep->fileline(), "");
} else {
xnum.setAllBitsX();
}
AstNode* newp = new AstCondBound (nodep->fileline(),
condp,
nodep,

View File

@ -1314,21 +1314,29 @@ private:
if (debug()>=9) nodep->dumpTree("-ms-in-");
nodep->iterateChildren(*this,WidthVP(SELF,BOTH).p());
// Find the fromp dtype - should be a class
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefp();
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
UINFO(9," from dt "<<fromDtp<<endl);
AstNodeClassDType* fromClassp = fromDtp->castNodeClassDType();
AstMemberDType* memberp = NULL; // NULL=error below
if (!fromClassp) {
nodep->v3error("Member selection of non-struct/union object '"
<<nodep->fromp()->prettyTypeName()<<"' which is a '"<<nodep->fromp()->dtypep()->prettyTypeName()<<"'");
}
else {
// No need to width-resolve the fromClassp, as it was done when we did the child
memberp = fromClassp->findMember(nodep->name());
if (AstNodeClassDType* adtypep = fromDtp->castNodeClassDType()) {
// No need to width-resolve the class, as it was done when we did the child
memberp = adtypep->findMember(nodep->name());
if (!memberp) {
nodep->v3error("Member '"<<nodep->prettyName()<<"' not found in structure");
}
}
else if (fromDtp->castEnumDType()) {
// Method call on enum without following parenthesis, e.g. "ENUM.next"
// Convert this into a method call, and let that visitor figure out what to do next
AstNode* newp = new AstMethodSel(nodep->fileline(), nodep->fromp()->unlinkFrBack(), nodep->name(), NULL);
nodep->replaceWith(newp);
pushDeletep(nodep); nodep=NULL;
newp->accept(*this,vup);
return;
}
else {
nodep->v3error("Member selection of non-struct/union object '"
<<nodep->fromp()->prettyTypeName()<<"' which is a '"<<nodep->fromp()->dtypep()->prettyTypeName()<<"'");
}
if (memberp) {
if (m_attrp) { // Looking for the base of the attribute
nodep->dtypep(memberp);
@ -1350,6 +1358,96 @@ private:
}
}
virtual void visit(AstMethodSel* nodep, AstNUser* vup) {
UINFO(5," METHODSEL "<<nodep<<endl);
if (debug()>=9) nodep->dumpTree("-ms-in-");
// Should check types the method requires, but at present we don't do much
nodep->fromp()->accept(*this,WidthVP(SELF,BOTH).p());
for (AstArg* argp = nodep->pinsp()->castArg(); argp; argp = argp->nextp()->castArg()) {
if (argp->exprp()) argp->exprp()->accept(*this,WidthVP(SELF,BOTH).p());
}
// Find the fromp dtype - should be a class
if (!nodep->fromp() || !nodep->fromp()->dtypep()) nodep->v3fatalSrc("Unsized expression");
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
UINFO(9," from dt "<<fromDtp<<endl);
if (AstEnumDType* adtypep = fromDtp->castEnumDType()) {
// Method call on enum without following parenthesis, e.g. "ENUM.next"
// Convert this into a method call, and let that visitor figure out what to do next
if (adtypep) {}
if (nodep->name() == "num"
|| nodep->name() == "first"
|| nodep->name() == "last") {
// Constant value
AstConst* newp = NULL;
if (nodep->pinsp()) nodep->v3error("Arguments passed to enum.num method, but it does not take arguments");
if (nodep->name() == "num") {
int items = 0;
for (AstNode* itemp = adtypep->itemsp(); itemp; itemp = itemp->nextp()) ++items;
newp = new AstConst(nodep->fileline(), AstConst::Signed32(), items);
} else if (nodep->name() == "first") {
AstEnumItem* itemp = adtypep->itemsp();
if (!itemp) newp = new AstConst(nodep->fileline(), AstConst::Signed32(), 0); // Spec doesn't say what to do
else newp = itemp->valuep()->cloneTree(false)->castConst(); // A const
} else if (nodep->name() == "last") {
AstEnumItem* itemp = adtypep->itemsp();
while (itemp && itemp->nextp()) itemp = itemp->nextp()->castEnumItem();
if (!itemp) newp = new AstConst(nodep->fileline(), AstConst::Signed32(), 0); // Spec doesn't say what to do
else newp = itemp->valuep()->cloneTree(false)->castConst(); // A const
}
if (!newp) nodep->v3fatalSrc("Enum method (perhaps enum item) not const");
newp->fileline(nodep->fileline()); // Use method's filename/line number to be clearer; may have warning disables
nodep->replaceWith(newp);
pushDeletep(nodep); nodep=NULL;
}
else if (nodep->name() == "name"
|| nodep->name() == "next"
|| nodep->name() == "prev") {
AstAttrType attrType;
if (nodep->name() == "name") attrType = AstAttrType::ENUM_NAME;
else if (nodep->name() == "next") attrType = AstAttrType::ENUM_NEXT;
else if (nodep->name() == "prev") attrType = AstAttrType::ENUM_PREV;
else nodep->v3fatalSrc("Bad case");
if (nodep->pinsp() && nodep->name() == "name") {
nodep->v3error("Arguments passed to enum.name method, but it does not take arguments");
} else if (nodep->pinsp() && !(nodep->pinsp()->castArg()->exprp()->castConst()
&& nodep->pinsp()->castArg()->exprp()->castConst()->toUInt()==1
&& !nodep->pinsp()->nextp())) {
nodep->v3error("Unsupported: Arguments passed to enum.next method");
}
// Need a runtime lookup table. Yuk.
// Ideally we would have a fast algorithm when a number is
// of small width and complete and so can use an array, and
// a map for when the value is many bits and sparse.
uint64_t max = 0;
{
AstEnumItem* itemp = adtypep->itemsp();
while (itemp && itemp->nextp()) {
itemp = itemp->nextp()->castEnumItem();
AstConst* vconstp = itemp->valuep()->castConst();
if (!vconstp) nodep->v3fatalSrc("Enum item without constified value");
if (vconstp->toUQuad() >= max) max = vconstp->toUQuad();
}
if (itemp->width() > 64 || max >= 1024) {
nodep->v3error("Unsupported; enum next/prev method on enum with > 10 bits");
return;
}
}
AstVar* varp = enumVarp(adtypep, attrType, max);
AstVarRef* varrefp = new AstVarRef(nodep->fileline(), varp, false);
varrefp->packagep(v3Global.rootp()->dollarUnitPkgAddp());
AstNode* newp = new AstArraySel(nodep->fileline(), varrefp, nodep->fromp()->unlinkFrBack());
nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL;
} else {
nodep->v3error("Unknown built-in enum method '"<<nodep->fromp()->prettyTypeName()<<"'");
}
}
else {
nodep->v3error("Unsupported: Member call on non-enum object '"
<<nodep->fromp()->prettyTypeName()<<"' which is a '"<<nodep->fromp()->dtypep()->prettyTypeName()<<"'");
}
}
virtual void visit(AstPattern* nodep, AstNUser* vup) {
if (nodep->didWidthAndSet()) return;
UINFO(9,"PATTERN "<<nodep<<endl);
@ -3208,6 +3306,78 @@ private:
m_tableMap.insert(make_pair(make_pair(nodep,attrType), varp));
return varp;
}
AstVar* enumVarp(AstEnumDType* nodep, AstAttrType attrType, uint32_t maxdim) {
// Return a variable table which has specified dimension properties for this variable
TableMap::iterator pos = m_tableMap.find(make_pair(nodep,attrType));
if (pos != m_tableMap.end()) {
return pos->second;
}
AstNodeDType* basep;
if (attrType == AstAttrType::ENUM_NAME) {
basep = nodep->findStringDType();
} else {
basep = nodep->findSigned32DType();
}
AstNodeArrayDType* vardtypep = new AstUnpackArrayDType(nodep->fileline(),
basep,
new AstRange(nodep->fileline(), maxdim, 0));
AstInitArray* initp = new AstInitArray (nodep->fileline(), vardtypep, NULL);
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
AstVar* varp = new AstVar (nodep->fileline(), AstVarType::MODULETEMP,
"__Venumtab_" + VString::downcase(attrType.ascii()) + cvtToStr(m_dtTables++),
vardtypep);
varp->isConst(true);
varp->isStatic(true);
varp->valuep(initp);
// Add to root, as don't know module we are in, and aids later structure sharing
v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp);
// Find valid values and populate
if (!nodep->itemsp()) nodep->v3fatalSrc("enum without items");
vector<AstNode*> values;
values.reserve(maxdim);
for (unsigned i=0; i<(maxdim+1); ++i) values[i] = NULL;
{
AstEnumItem* firstp = nodep->itemsp();
AstEnumItem* prevp = firstp; // Prev must start with last item
while (prevp->nextp()) prevp = prevp->nextp()->castEnumItem();
for (AstEnumItem* itemp = firstp; itemp;) {
AstEnumItem* nextp = itemp->nextp()->castEnumItem();
AstConst* vconstp = itemp->valuep()->castConst();
if (!vconstp) nodep->v3fatalSrc("Enum item without constified value");
uint32_t i = vconstp->toUInt();
if (attrType == AstAttrType::ENUM_NAME) {
values[i] = new AstConst(nodep->fileline(), AstConst::String(), itemp->name());
} else if (attrType == AstAttrType::ENUM_NEXT) {
values[i] = (nextp ? nextp : firstp)->valuep()->cloneTree(false); // A const
} else if (attrType == AstAttrType::ENUM_PREV) {
values[i] = prevp->valuep()->cloneTree(false); // A const
} else {
nodep->v3fatalSrc("Bad case");
}
prevp = itemp;
itemp = nextp;
}
}
// Fill in all unspecified values and add to table
for (unsigned i=0; i<(maxdim+1); ++i) {
AstNode* valp = values[i];
if (!valp) {
if (attrType == AstAttrType::ENUM_NAME) {
valp = new AstConst(nodep->fileline(), AstConst::String(), "");
} else if (attrType == AstAttrType::ENUM_NEXT
|| attrType == AstAttrType::ENUM_PREV) {
valp = new AstConst(nodep->fileline(), V3Number(nodep->fileline(), nodep->width(), 0));
} else {
nodep->v3fatalSrc("Bad case");
}
}
initp->addInitsp(valp);
}
varp->iterate(*this); // May have already done $unit so must do this var
m_tableMap.insert(make_pair(make_pair(nodep,attrType), varp));
return varp;
}
PatVecMap patVectorMap(AstPattern* nodep, const VNumRange& range) {
PatVecMap patmap;

View File

@ -1,48 +1,10 @@
// DESCRIPTION: Verilator: System Verilog test of enumerated type methods
// DESCRIPTION: Verilator: Verilog Test module
//
// This code instantiates a module that uses a localparam with an enumerated
// type.
//
// This file ONLY is placed into the Public Domain, for any use, without
// warranty.
// Contributed 2012 by M W Lund, Atmel Corporation and Jeremy Bennett, Embecosm.
// **** Pin Identifiers ****
typedef enum int
{
PINID_A0 = 32'd0, // MUST BE ZERO!
// - Standard Ports -
PINID_A1, PINID_A2, PINID_A3, PINID_A4, PINID_A5, PINID_A6, PINID_A7,
PINID_B0, PINID_B1, PINID_B2, PINID_B3, PINID_B4, PINID_B5, PINID_B6, PINID_B7,
PINID_C0, PINID_C1, PINID_C2, PINID_C3, PINID_C4, PINID_C5, PINID_C6, PINID_C7,
PINID_D0, PINID_D1, PINID_D2, PINID_D3, PINID_D4, PINID_D5, PINID_D6, PINID_D7,
PINID_E0, PINID_E1, PINID_E2, PINID_E3, PINID_E4, PINID_E5, PINID_E6, PINID_E7,
PINID_F0, PINID_F1, PINID_F2, PINID_F3, PINID_F4, PINID_F5, PINID_F6, PINID_F7,
PINID_G0, PINID_G1, PINID_G2, PINID_G3, PINID_G4, PINID_G5, PINID_G6, PINID_G7,
PINID_H0, PINID_H1, PINID_H2, PINID_H3, PINID_H4, PINID_H5, PINID_H6, PINID_H7,
// PINID_I0, PINID_I1, PINID_I2, PINID_I3, PINID_I4, PINID_I5, PINID_I6, PINID_I7,-> DO NOT USE!!!! I == 1
PINID_J0, PINID_J1, PINID_J2, PINID_J3, PINID_J4, PINID_J5, PINID_J6, PINID_J7,
PINID_K0, PINID_K1, PINID_K2, PINID_K3, PINID_K4, PINID_K5, PINID_K6, PINID_K7,
PINID_L0, PINID_L1, PINID_L2, PINID_L3, PINID_L4, PINID_L5, PINID_L6, PINID_L7,
PINID_M0, PINID_M1, PINID_M2, PINID_M3, PINID_M4, PINID_M5, PINID_M6, PINID_M7,
PINID_N0, PINID_N1, PINID_N2, PINID_N3, PINID_N4, PINID_N5, PINID_N6, PINID_N7,
// PINID_O0, PINID_O1, PINID_O2, PINID_O3, PINID_O4, PINID_O5, PINID_O6, PINID_O7,-> DO NOT USE!!!! O == 0
PINID_P0, PINID_P1, PINID_P2, PINID_P3, PINID_P4, PINID_P5, PINID_P6, PINID_P7,
PINID_Q0, PINID_Q1, PINID_Q2, PINID_Q3, PINID_Q4, PINID_Q5, PINID_Q6, PINID_Q7,
PINID_R0, PINID_R1, PINID_R2, PINID_R3, PINID_R4, PINID_R5, PINID_R6, PINID_R7,
// - AUX Port (Custom) -
PINID_X0, PINID_X1, PINID_X2, PINID_X3, PINID_X4, PINID_X5, PINID_X6, PINID_X7,
// - PDI Port -
PINID_D2W_DAT, PINID_D2W_CLK,
// - Power Pins -
PINID_VDD0, PINID_VDD1, PINID_VDD2, PINID_VDD3,
PINID_GND0, PINID_GND1, PINID_GND2, PINID_GND3,
// - Maximum number of pins -
PINID_MAX
} t_pinid;
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2014 by Wilson Snyder.
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
module t (/*AUTOARG*/
// Inputs
@ -50,26 +12,85 @@ module t (/*AUTOARG*/
);
input clk;
wire a = clk;
wire b = 1'b0;
reg c;
simple_test_1 simple_test_1_i (/*AUTOINST*/
// Outputs
.c (c),
// Inputs
.a (a),
.b (b));
typedef enum {
E01 = 1,
E03 = 3,
E04 = 4
} my_t;
// This is a compile time only test. Immediately finish
always @(posedge clk) begin
$write("*-* All Finished *-*\n");
$finish;
integer cyc=0;
my_t e;
int arrayfits [e.num]; // Check can use as constant
string all;
// Check constification
initial begin
e = E03;
`checkh(e.first, E01);
`checkh(e.last, E04);
`checkh(e.last(), E04);
`checkh(e.next, E04);
`checkh(e.next(), E04);
`checkh(e.next(1), E04);
//Unsup: `checkh(e.next(2), E01);
`checkh(e.prev, E01);
`checkh(e.prev(1), E01);
//Unsup: `checkh(e.prev(2), E04);
`checkh(e.num, 3);
`checks(e.name, "E03");
//
all = "";
for (my_t e = e.first; e != e.last; e = e.next) begin
all = {all, e.name};
end
e = e.last;
all = {all, e.name};
`checks(all, "E01E03E04");
end
// Check runtime
always @ (posedge clk) begin
cyc <= cyc + 1;
if (cyc==0) begin
// Setup
e <= E01;
end
else if (cyc==1) begin
`checks(e.name, "E01");
`checkh(e.next, E03);
`checkh(e.next(1), E03);
//Unsup: `checkh(e.next(2), E04);
`checkh(e.prev, E04);
`checkh(e.prev(1), E04);
//Unsup: `checkh(e.prev(2), E03);
e <= E03;
end
else if (cyc==2) begin
`checks(e.name, "E03");
`checkh(e.next, E04);
`checkh(e.next(1), E04);
//Unsup: `checkh(e.next(2), E01);
`checkh(e.prev, E01);
`checkh(e.prev(1), E01);
//Unsup: `checkh(e.prev(2), E04);
e <= E04;
end
else if (cyc==3) begin
`checks(e.name, "E04");
`checkh(e.next, E01);
`checkh(e.next(1), E01);
//Unsup: `checkh(e.next(2), E03);
`checkh(e.prev, E03);
`checkh(e.prev(1), E03);
//Unsup: `checkh(e.prev(2), E01);
e <= E01;
end
else if (cyc==99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
// **** Simple use of parameters for sizing an array ****
module simple_test_1 (input a, b, output c);
int myarray1 [PINID_MAX];
endmodule