diff --git a/Changes b/Changes index a1bc78d6e..0a023db09 100644 --- a/Changes +++ b/Changes @@ -6,6 +6,8 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.885 devel +**** Fix enum values of 11-16 bits wide using .next/.prev, bug1062. [Brian Flachs] + **** Fix false warnings on non-power-2 enums using .next/.prev. diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 4f5ce4e5f..efb431e17 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -2981,20 +2981,37 @@ class AstInitArray : public AstNode { // Set a var to a large list of values // The values must be in sorted order, and not exceed the size of the var's array. // The first value on the initsp() list is for the lo() index of the array. + // If default is specified, the vector may be sparse, and not provide each value. // Parents: ASTVAR::init() // Children: CONSTs... + deque m_indices; // Which array index each entry in the list is for (if defaultp) public: - AstInitArray(FileLine* fl, AstNodeArrayDType* newDTypep, AstNode* initsp) + AstInitArray(FileLine* fl, AstNodeArrayDType* newDTypep, AstNode* defaultp) : AstNode(fl) { dtypep(newDTypep); - addNOp1p(initsp); + addNOp1p(defaultp); } ASTNODE_NODE_FUNCS(InitArray, INITARRAY) - AstNode* initsp() const { return op1p()->castNode(); } // op1 = Initial value expressions - void addInitsp(AstNode* newp) { addOp1p(newp); } + AstNode* defaultp() const { return op1p()->castNode(); } // op1 = Default if sparse + void defaultp(AstNode* newp) { setOp1p(newp); } + AstNode* initsp() const { return op2p()->castNode(); } // op2 = Initial value expressions + void addValuep(AstNode* newp) { addIndexValuep(m_indices.size(), newp); } + void addIndexValuep(uint32_t index, AstNode* newp) { + // Must insert in sorted order + if (!m_indices.empty()) UASSERT(index > m_indices.back(), "InitArray adding index <= previous index"); + m_indices.push_back(index); + addOp2p(newp); } + void addFrontValuep(AstNode* newp) { // Add to front of list, e.g. index 0. + // e.g. 0:100, 1:101 when addFront(200), get 0:200, 1:100, 2:101 + initsp()->addHereThisAsNext(newp); + m_indices.push_back(m_indices.size()); + } + int posIndex(int listPos) { + UASSERT (listPos < (int)m_indices.size(), "InitArray past end of indices list"); + return m_indices[listPos]; } virtual bool hasDType() const { return true; } virtual V3Hash sameHash() const { return V3Hash(); } - virtual bool same(AstNode* samep) const { return true; } + virtual bool same(AstNode* samep) const { return m_indices==samep->castInitArray()->m_indices; } }; class AstPragma : public AstNode { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index a6e4538ee..2292dd7f0 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1464,9 +1464,21 @@ private: did=true; } else if (m_selp && valuep->castInitArray()) { - int bit = m_selp->bitConst(); - AstNode* itemp = valuep->castInitArray()->initsp(); - for (int n=0; nnextp()) {} + AstInitArray* initarp = valuep->castInitArray(); + uint32_t bit = m_selp->bitConst(); + int pos = 0; + AstNode* itemp = initarp->initsp(); + for (; itemp; ++pos, itemp=itemp->nextp()) { + uint32_t index = initarp->posIndex(pos); + if (index == bit) break; + if (index > bit) { + if (initarp->defaultp()) { + itemp = initarp->defaultp(); + } else { + initarp->v3fatalSrc("Not enough values in array initalizement"); + } + } + } if (itemp->castConst()) { const V3Number& num = itemp->castConst()->num(); //UINFO(2,"constVisit "<<(void*)valuep<<" "<name()+" = "+varp->valuep()->name()+"\n"); } else if (AstInitArray* initarp = varp->valuep()->castInitArray()) { - AstConst* constsp = initarp->initsp()->castConst(); if (AstUnpackArrayDType* arrayp = varp->dtypeSkipRefp()->castUnpackArrayDType()) { - for (int i=0; ielementsConst(); i++) { - if (!constsp) initarp->v3fatalSrc("Not enough values in array initalizement"); - emitSetVarConstant(varp->name()+"["+cvtToStr(i)+"]", constsp); - constsp = constsp->nextp()->castConst(); + if (initarp->defaultp()) { + // MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block + puts("{ int __Vi=0;"); + puts(" for (; __Vi<"+cvtToStr(arrayp->elementsConst())); + puts("; ++__Vi) {\n"); + emitSetVarConstant(varp->name()+"[__Vi]", initarp->defaultp()->castConst()); + puts("}}\n"); + } + int pos = 0; + for (AstNode* itemp = initarp->initsp(); itemp; ++pos, itemp=itemp->nextp()) { + int index = initarp->posIndex(pos); + if (!initarp->defaultp() && index!=pos) initarp->v3fatalSrc("Not enough values in array initalizement"); + emitSetVarConstant(varp->name()+"["+cvtToStr(index)+"]", itemp->castConst()); } } else { varp->v3fatalSrc("InitArray under non-arrayed var"); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 8344a183d..1cfc8121f 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -448,9 +448,13 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { } virtual void visit(AstInitArray* nodep, AstNUser*) { putfs(nodep,"`{"); - for (AstNode* subp = nodep->initsp(); subp; subp=subp->nextp()) { - subp->accept(*this); - if (subp->nextp()) putbs(","); + int pos = 0; + for (AstNode* itemp = nodep->initsp(); itemp; ++pos, itemp=itemp->nextp()) { + int index = nodep->posIndex(pos); + puts(cvtToStr(index)); + puts(":"); + itemp->accept(*this); + if (itemp->nextp()) putbs(","); } puts("}"); } diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 6d698fdd3..2b76996c4 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -421,17 +421,19 @@ class SliceVisitor : public AstNVisitor { if (AstInitArray* initp = nodep->rhsp()->castInitArray()) { //if (debug()>=9) nodep->dumpTree(cout, "-InitArrayIn: "); AstNode* newp = NULL; - int index = 0; - while (AstNode* subp=initp->initsp()) { + for (int pos = 0; AstNode* itemp=initp->initsp(); ++pos) { + int index = initp->posIndex(pos); AstNode* lhsp = new AstArraySel(nodep->fileline(), nodep->lhsp()->cloneTree(false), - index++); - newp = AstNode::addNext(newp, nodep->cloneType(lhsp, subp->unlinkFrBack())); + index); + newp = AstNode::addNext(newp, nodep->cloneType(lhsp, itemp->unlinkFrBack())); + // Deleted from list of items without correcting posIndex, but that's ok as about + // to delete the entire InitArray } //if (debug()>=9) newp->dumpTreeAndNext(cout, "-InitArrayOut: "); nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); - return; // WIll iterate in a moment + return; // Will iterate in a moment } // Hasn't been searched for implicit slices yet findImplicit(nodep); diff --git a/src/V3Table.cpp b/src/V3Table.cpp index 624ed9ed7..f50abda12 100644 --- a/src/V3Table.cpp +++ b/src/V3Table.cpp @@ -328,7 +328,7 @@ private: setp = new AstConst (outnump->fileline(), *outnump); } // Note InitArray requires us to have the values in inValue order - m_tableVarps[outnum]->varp()->valuep()->castInitArray()->addInitsp(setp); + m_tableVarps[outnum]->varp()->valuep()->castInitArray()->addValuep(setp); outnum++; } @@ -336,7 +336,7 @@ private: if (inValue != inValueNextInitArray++) nodep->v3fatalSrc("InitArray requires us to have the values in inValue order"); AstNode* setp = new AstConst (nodep->fileline(), outputChgMask); - chgVscp->varp()->valuep()->castInitArray()->addInitsp(setp); + chgVscp->varp()->valuep()->castInitArray()->addValuep(setp); } } // each value } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index aefc35ab6..5640b642d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1504,13 +1504,13 @@ private: if (!vconstp) nodep->v3fatalSrc("Enum item without constified value"); if (vconstp->toUQuad() >= msbdim) msbdim = vconstp->toUQuad(); } - if (adtypep->itemsp()->width() > 64 || msbdim >= 1024) { + if (adtypep->itemsp()->width() > 64 || msbdim >= (1<<16)) { nodep->v3error("Unsupported; enum next/prev method on enum with > 10 bits"); return; } } int selwidth = V3Number::log2b(msbdim)+1; // Width to address a bit - AstVar* varp = enumVarp(adtypep, attrType, msbdim); + AstVar* varp = enumVarp(adtypep, attrType, (VL_ULL(1)<fileline(), varp, false); varrefp->packagep(v3Global.rootp()->dollarUnitPkgAddp()); AstNode* newp = new AstArraySel(nodep->fileline(), varrefp, @@ -1703,11 +1703,13 @@ private: } if (arrayp->castUnpackArrayDType()) { if (!newp) { - newp = new AstInitArray(nodep->fileline(), arrayp, valuep); + AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayp, NULL); + newap->addValuep(valuep); + newp = newap; } else { // We iterate hi()..lo() as that is what packed needs, // but INITARRAY needs lo() first - newp->castInitArray()->initsp()->addHereThisAsNext(valuep); + newp->castInitArray()->addFrontValuep(valuep); } } else { // Packed. Convert to concat for now. if (!newp) newp = valuep; @@ -3409,9 +3411,9 @@ private: // Add to root, as don't know module we are in, and aids later structure sharing v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp); // Element 0 is a non-index and has speced values - initp->addInitsp(dimensionValue(nodep, attrType, 0)); + initp->addValuep(dimensionValue(nodep, attrType, 0)); for (unsigned i=1; iaddInitsp(dimensionValue(nodep, attrType, i)); + initp->addValuep(dimensionValue(nodep, attrType, i)); } varp->iterate(*this); // May have already done $unit so must do this var m_tableMap.insert(make_pair(make_pair(nodep,attrType), varp)); @@ -3444,6 +3446,16 @@ private: // Add to root, as don't know module we are in, and aids later structure sharing v3Global.rootp()->dollarUnitPkgAddp()->addStmtp(varp); + // Default for all unspecified values + if (attrType == AstAttrType::ENUM_NAME) { + initp->defaultp(new AstConst(nodep->fileline(), AstConst::String(), "")); + } else if (attrType == AstAttrType::ENUM_NEXT + || attrType == AstAttrType::ENUM_PREV) { + initp->defaultp(new AstConst(nodep->fileline(), V3Number(nodep->fileline(), nodep->width(), 0))); + } else { + nodep->v3fatalSrc("Bad case"); + } + // Find valid values and populate if (!nodep->itemsp()) nodep->v3fatalSrc("enum without items"); vector values; @@ -3473,20 +3485,10 @@ private: itemp = nextp; } } - // Fill in all unspecified values and add to table + // Add all specified values to table for (unsigned i=0; i<(msbdim+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); + if (valp) initp->addIndexValuep(i, valp); } varp->iterate(*this); // May have already done $unit so must do this var m_tableMap.insert(make_pair(make_pair(nodep,attrType), varp)); diff --git a/test_regress/t/t_enum_large_methods.pl b/test_regress/t/t_enum_large_methods.pl new file mode 100755 index 000000000..f91289753 --- /dev/null +++ b/test_regress/t/t_enum_large_methods.pl @@ -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; diff --git a/test_regress/t/t_enum_large_methods.v b/test_regress/t/t_enum_large_methods.v new file mode 100644 index 000000000..95056c54e --- /dev/null +++ b/test_regress/t/t_enum_large_methods.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 + clk + ); + input clk; + + typedef enum { + E01 = 'h1, + ELARGE = 'hf00d + } my_t; + + integer cyc=0; + my_t e; + + string all; + + // 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, ELARGE); + e <= ELARGE; + end + else if (cyc==3) begin + `checks(e.name, "ELARGE"); + `checkh(e.next, E01); + `checkh(e.prev, E01); + e <= E01; + end + else if (cyc==20) begin + e <= 'h11; // Unknown + end + else if (cyc==20) begin + `checks(e.name, ""); // Unknown + end + else if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule