forked from github/verilator
Fix conditional slices and add related optimizations.
This commit is contained in:
parent
b532a47e4a
commit
75aab4e9d2
2
Changes
2
Changes
@ -23,6 +23,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
**** Fix unnecessary Vdly variables, bug1224 partial. [John Coiner]
|
||||
|
||||
**** Fix conditional slices and add related optimizations.
|
||||
|
||||
|
||||
* Verilator 3.912 2017-09-23
|
||||
|
||||
|
@ -763,10 +763,6 @@ void AstAlways::dump(ostream& str) {
|
||||
if (keyword() != VAlwaysKwd::ALWAYS) str<<" ["<<keyword().ascii()<<"]";
|
||||
}
|
||||
|
||||
void AstArraySel::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" [start:"<<start()<<"] [length:"<<length()<<"]";
|
||||
}
|
||||
void AstAttrOf::dump(ostream& str) {
|
||||
this->AstNode::dump(str);
|
||||
str<<" ["<<attrType().ascii()<<"]";
|
||||
|
@ -766,8 +766,6 @@ class AstArraySel : public AstNodeSel {
|
||||
// Parents: math|stmt
|
||||
// Children: varref|arraysel, math
|
||||
private:
|
||||
unsigned m_start;
|
||||
unsigned m_length;
|
||||
void init(AstNode* fromp) {
|
||||
if (fromp && fromp->dtypep()->skipRefp()->castNodeArrayDType()) {
|
||||
// Strip off array to find what array references
|
||||
@ -776,11 +774,11 @@ private:
|
||||
}
|
||||
public:
|
||||
AstArraySel(FileLine* fl, AstNode* fromp, AstNode* bitp)
|
||||
:AstNodeSel(fl, fromp, bitp), m_start(0), m_length(1) {
|
||||
:AstNodeSel(fl, fromp, bitp) {
|
||||
init(fromp);
|
||||
}
|
||||
AstArraySel(FileLine* fl, AstNode* fromp, int bit)
|
||||
:AstNodeSel(fl, fromp, new AstConst(fl,bit)), m_start(0), m_length(1) {
|
||||
:AstNodeSel(fl, fromp, new AstConst(fl,bit)) {
|
||||
init(fromp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ArraySel)
|
||||
@ -797,13 +795,8 @@ public:
|
||||
virtual V3Hash sameHash() const { return V3Hash(); }
|
||||
virtual bool same(AstNode* samep) const { return true; }
|
||||
virtual int instrCount() const { return widthInstrs(); }
|
||||
unsigned length() const { return m_length; }
|
||||
void length(unsigned length) { m_length = length; }
|
||||
void start(unsigned start) { m_start = start; }
|
||||
unsigned start() const { return m_start; }
|
||||
// Special operators
|
||||
static AstNode* baseFromp(AstNode* nodep); ///< What is the base variable (or const) this dereferences?
|
||||
virtual void dump(ostream& str);
|
||||
};
|
||||
|
||||
class AstWordSel : public AstNodeSel {
|
||||
|
459
src/V3Slice.cpp
459
src/V3Slice.cpp
@ -45,153 +45,6 @@
|
||||
#include "V3Global.h"
|
||||
#include "V3Slice.h"
|
||||
#include "V3Ast.h"
|
||||
#include <vector>
|
||||
|
||||
class SliceCloneVisitor : public AstNVisitor {
|
||||
// NODE STATE
|
||||
// Inputs:
|
||||
// AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to
|
||||
// AstNodeAssign::user2() -> int. The number of clones needed for this assign
|
||||
// AstArraySel::user3() -> bool. Error detected
|
||||
|
||||
// STATE
|
||||
vector<vector<unsigned> > m_selBits; // Indexes of the ArraySel we are expanding
|
||||
int m_vecIdx; // Current vector index
|
||||
unsigned m_depth; // Number of ArraySel's from the VarRef
|
||||
AstVarRef* m_refp; // VarRef under this ArraySel
|
||||
|
||||
// METHODS
|
||||
static int debug() {
|
||||
static int level = -1;
|
||||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||||
return level;
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstArraySel* nodep) {
|
||||
if (!nodep->backp()->castArraySel()) {
|
||||
// This is the top of an ArraySel, setup for iteration
|
||||
m_refp = nodep->user1p()->castVarRef();
|
||||
m_vecIdx += 1;
|
||||
if (m_vecIdx == (int)m_selBits.size()) {
|
||||
m_selBits.push_back(vector<unsigned>());
|
||||
AstVar* varp = m_refp->varp();
|
||||
pair<uint32_t,uint32_t> arrDim = varp->dtypep()->dimensions(false);
|
||||
uint32_t dimensions = arrDim.second;
|
||||
// for 3-dimensions we want m_selBits[m_vecIdx]=[0,0,0]
|
||||
for (uint32_t i = 0; i < dimensions; ++i) {
|
||||
m_selBits[m_vecIdx].push_back(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
if (nodep->fromp()->castVarRef()) {
|
||||
m_depth = 0;
|
||||
} else {
|
||||
++m_depth;
|
||||
}
|
||||
// Check if m_selBits has overflowed
|
||||
if (m_selBits[m_vecIdx][m_depth] >= nodep->length()) {
|
||||
m_selBits[m_vecIdx][m_depth] = 0;
|
||||
if (m_depth + 1 < m_selBits[m_vecIdx].size())
|
||||
m_selBits[m_vecIdx][m_depth+1] += 1;
|
||||
}
|
||||
// Reassign the bitp()
|
||||
if (nodep->length() > 1) {
|
||||
if (AstConst* bitp = nodep->bitp()->castConst()) {
|
||||
AstUnpackArrayDType* adtypep = nodep->fromp()->dtypep()->skipRefp()->castUnpackArrayDType();
|
||||
if (!adtypep) nodep->v3fatalSrc("slice select tried to expand an array without an ArrayDType");
|
||||
unsigned idx = nodep->start() + m_selBits[m_vecIdx][m_depth] - adtypep->lsb();
|
||||
if (adtypep->rangep()->littleEndian()) { // Little must iterate backwards
|
||||
idx = adtypep->rangep()->elementsConst() - 1 - idx;
|
||||
}
|
||||
AstNode* constp = new AstConst(bitp->fileline(), V3Number(bitp->fileline(), bitp->castConst()->num().width(), idx));
|
||||
bitp->replaceWith(constp);
|
||||
} else {
|
||||
nodep->v3error("Unsupported: Only constants supported in slices");
|
||||
}
|
||||
}
|
||||
if (!nodep->backp()->castArraySel()) {
|
||||
// Top ArraySel, increment m_selBits
|
||||
m_selBits[m_vecIdx][0] += 1;
|
||||
}
|
||||
nodep->length(1);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
if (nodep->user2() < 2) return; // Don't need clones
|
||||
m_selBits.clear();
|
||||
UINFO(4, "Cloning "<<nodep->user2()<<" times: "<<nodep<<endl);
|
||||
for (int i = 0; i < nodep->user2(); ++i) {
|
||||
// Clone the node and iterate over the clone
|
||||
m_vecIdx = -1;
|
||||
AstNodeAssign* clonep = nodep->cloneTree(false)->castNodeAssign();
|
||||
clonep->iterateChildren(*this);
|
||||
nodep->addNextHere(clonep);
|
||||
}
|
||||
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
|
||||
// Not all Uniop nodes should be cloned down to a single bit
|
||||
void cloneUniop(AstNodeUniop* nodep) {
|
||||
if (nodep->user2() < 2) return; // Don't need clones
|
||||
m_selBits.clear();
|
||||
UINFO(4, "Cloning "<<nodep->user2()<<" times: "<<nodep<<endl);
|
||||
|
||||
AstNode* lhsp = NULL;
|
||||
for (int i = 0; i < nodep->user2(); ++i) {
|
||||
// Clone the node and iterate over the clone
|
||||
m_vecIdx = -1;
|
||||
AstNodeUniop* clonep = nodep->cloneTree(false)->castNodeUniop();
|
||||
clonep->iterateChildren(*this);
|
||||
if (!lhsp) lhsp = clonep;
|
||||
else {
|
||||
switch (nodep->type()) {
|
||||
case AstType::atRedOr:
|
||||
lhsp = new AstOr(nodep->fileline(), lhsp, clonep);
|
||||
break;
|
||||
case AstType::atRedAnd:
|
||||
lhsp = new AstAnd(nodep->fileline(), lhsp, clonep);
|
||||
break;
|
||||
case AstType::atRedXor:
|
||||
lhsp = new AstXor(nodep->fileline(), lhsp, clonep);
|
||||
break;
|
||||
case AstType::atRedXnor:
|
||||
lhsp = new AstXnor(nodep->fileline(), lhsp, clonep);
|
||||
break;
|
||||
default:
|
||||
nodep->v3fatalSrc("Unsupported: Unary operation on multiple packed dimensions");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
nodep->replaceWith(lhsp);
|
||||
nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
}
|
||||
virtual void visit(AstRedOr* nodep) {
|
||||
cloneUniop(nodep);
|
||||
}
|
||||
virtual void visit(AstRedAnd* nodep) {
|
||||
cloneUniop(nodep);
|
||||
}
|
||||
virtual void visit(AstRedXor* nodep) {
|
||||
cloneUniop(nodep);
|
||||
}
|
||||
virtual void visit(AstRedXnor* nodep) {
|
||||
cloneUniop(nodep);
|
||||
}
|
||||
|
||||
virtual void visit(AstNode* nodep) {
|
||||
// Default: Just iterate
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
public:
|
||||
// CONSTUCTORS
|
||||
explicit SliceCloneVisitor(AstNode* nodep) {
|
||||
nodep->accept(*this);
|
||||
}
|
||||
virtual ~SliceCloneVisitor() {}
|
||||
};
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
@ -201,18 +54,10 @@ class SliceVisitor : public AstNVisitor {
|
||||
// AstNodeAssign::user1() -> bool. True if find is complete
|
||||
// AstUniop::user1() -> bool. True if find is complete
|
||||
// AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to
|
||||
// AstNode::user2() -> int. The number of clones needed for this node
|
||||
AstUser1InUse m_inuser1;
|
||||
AstUser2InUse m_inuser2;
|
||||
AstUser3InUse m_inuser3;
|
||||
|
||||
// TYPEDEFS
|
||||
typedef pair<uint32_t, uint32_t> ArrayDimensions; // Array Dimensions (packed, unpacked)
|
||||
|
||||
// STATE
|
||||
AstNode* m_assignp; // Assignment we are under
|
||||
AstNodeVarRef* m_lhsVarRefp; // Var on the LHS
|
||||
bool m_extend; // We have found an extend node
|
||||
bool m_assignError; // True if the current assign already has an error
|
||||
|
||||
// METHODS
|
||||
@ -222,265 +67,103 @@ class SliceVisitor : public AstNVisitor {
|
||||
return level;
|
||||
}
|
||||
|
||||
unsigned explicitDimensions(AstArraySel* nodep) {
|
||||
// Find out how many explicit dimensions are in a given ArraySel.
|
||||
unsigned dim = 0;
|
||||
AstNode* fromp = nodep;
|
||||
AstArraySel* selp;
|
||||
do {
|
||||
selp = fromp->castArraySel();
|
||||
if (!selp) {
|
||||
nodep->user1p(fromp->castVarRef());
|
||||
selp = NULL;
|
||||
break;
|
||||
} else {
|
||||
fromp = selp->fromp();
|
||||
if (fromp) ++dim;
|
||||
}
|
||||
} while (fromp && selp);
|
||||
if (!nodep->user1p()) nodep->v3fatalSrc("Couldn't find VarRef under the ArraySel");
|
||||
return dim;
|
||||
}
|
||||
|
||||
int countClones(AstArraySel* nodep) {
|
||||
// Count how many clones we need to make from this ArraySel
|
||||
int clones = 1;
|
||||
AstNode* fromp = nodep;
|
||||
AstArraySel* selp;
|
||||
do {
|
||||
selp = fromp->castArraySel();
|
||||
fromp = (selp) ? selp->fromp() : NULL;
|
||||
if (fromp && selp) clones *= selp->length();
|
||||
} while (fromp && selp);
|
||||
return clones;
|
||||
}
|
||||
|
||||
AstArraySel* insertImplicit(AstNode* nodep, unsigned startDim, unsigned numDimensions) {
|
||||
// Insert any implicit slices as explicit slices (ArraySel nodes).
|
||||
// Return a new pointer to replace nodep() in the ArraySel.
|
||||
UINFO(9," insertImplicit (startDim="<<startDim<<",c="<<numDimensions<<") "<<nodep<<endl);
|
||||
AstVarRef* refp = nodep->user1p()->castVarRef();
|
||||
if (!refp) nodep->v3fatalSrc("No VarRef in user1 of node "<<nodep);
|
||||
AstVar* varp = refp->varp();
|
||||
AstNode* topp = nodep;
|
||||
for (unsigned i = startDim; i < startDim + numDimensions; ++i) {
|
||||
AstNodeDType* dtypep = varp->dtypep()->dtypeDimensionp(i-1);
|
||||
AstUnpackArrayDType* adtypep = dtypep->castUnpackArrayDType();
|
||||
if (!adtypep) nodep->v3fatalSrc("insertImplicit tried to expand an array without an ArrayDType");
|
||||
vlsint32_t msb = adtypep->msb();
|
||||
vlsint32_t lsb = adtypep->lsb();
|
||||
if (lsb > msb) {
|
||||
// Below code assumes big bit endian; just works out if we swap
|
||||
int x = msb; msb = lsb; lsb = x;
|
||||
}
|
||||
UINFO(9," ArraySel-child: "<<topp<<endl);
|
||||
AstArraySel* newp = new AstArraySel(nodep->fileline(), topp,
|
||||
// Arrays are zero-based so index 0 is always lsb (e.g. lsb-lsb -> 0)
|
||||
new AstConst(nodep->fileline(), 0));
|
||||
if (!newp->dtypep()) {
|
||||
newp->v3fatalSrc("ArraySel dtyping failed when resolving slice"); // see ArraySel constructor
|
||||
}
|
||||
newp->user1p(refp);
|
||||
newp->start(lsb);
|
||||
newp->length(msb - lsb + 1);
|
||||
topp = newp;
|
||||
}
|
||||
return topp->castArraySel();
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
virtual void visit(AstVarRef* nodep) {
|
||||
// The LHS/RHS of an Assign may be to a Var that is an array. In this
|
||||
// case we need to create a slice across the entire Var
|
||||
if (m_assignp && !nodep->backp()->castArraySel()) {
|
||||
pair<uint32_t,uint32_t> arrDim = nodep->varp()->dtypep()->dimensions(false);
|
||||
uint32_t dimensions = arrDim.second; // unpacked only
|
||||
if (dimensions > 0) {
|
||||
AstVarRef* clonep = nodep->cloneTree(false);
|
||||
clonep->user1p(nodep);
|
||||
AstNode* newp = insertImplicit(clonep, 1, dimensions);
|
||||
nodep->replaceWith(newp); VL_DANGLING(nodep);
|
||||
newp->accept(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstExtend* nodep) {
|
||||
m_extend = true;
|
||||
if (m_assignp && m_assignp->user2() > 1 && !m_assignError) {
|
||||
m_assignp->v3error("Unsupported: Assignment between unpacked arrays of different dimensions");
|
||||
AstNode* cloneAndSel(AstNode* nodep, int elements, int offset) {
|
||||
// Insert an ArraySel, except for a few special cases
|
||||
AstUnpackArrayDType* arrayp = nodep->dtypep()->skipRefp()->castUnpackArrayDType();
|
||||
if (!arrayp) { // V3Width should have complained, but...
|
||||
if (!m_assignError) nodep->v3error(nodep->prettyTypeName()<<" is not an unpacked array, but is in an unpacked array context");
|
||||
m_assignError = true;
|
||||
return nodep->cloneTree(false); // Likely will cause downstream errors
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
virtual void visit(AstConst* nodep) {
|
||||
m_extend = true;
|
||||
if (m_assignp && m_assignp->user2() > 1 && !m_assignError) {
|
||||
m_assignp->v3error("Unsupported: Assignment between a constant and an array slice");
|
||||
if (arrayp->rangep()->elementsConst() != elements) {
|
||||
if (!m_assignError) nodep->v3error("Slices of arrays in assignments have different unpacked dimensions, "
|
||||
<<elements<<" versus "<<arrayp->rangep()->elementsConst());
|
||||
m_assignError = true;
|
||||
elements = 1; offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstArraySel* nodep) {
|
||||
if (!m_assignp) return;
|
||||
if (nodep->user3()) return; // Prevent recursion on just created nodes
|
||||
unsigned dim = explicitDimensions(nodep);
|
||||
AstVarRef* refp = nodep->user1p()->castVarRef();
|
||||
pair<uint32_t,uint32_t> arrDim = refp->varp()->dtypep()->dimensions(false);
|
||||
uint32_t implicit = (arrDim.second) - dim;
|
||||
if (implicit > 0) {
|
||||
AstArraySel* newp = insertImplicit(nodep->cloneTree(false), dim+1, implicit);
|
||||
nodep->replaceWith(newp); nodep = newp;
|
||||
nodep->user3(true);
|
||||
}
|
||||
int clones = countClones(nodep);
|
||||
if (m_assignp->user2() > 0 && m_assignp->user2() != clones) {
|
||||
m_assignp->v3error("Slices of arrays in assignments must have the same unpacked dimensions");
|
||||
} else if (!m_assignp->user2()) {
|
||||
if (m_extend && clones > 1 && !m_assignError) {
|
||||
m_assignp->v3error("Unsupported: Assignment between unpacked arrays of different dimensions");
|
||||
m_assignError = true;
|
||||
AstNode* newp;
|
||||
if (AstInitArray* initp = nodep->castInitArray()) {
|
||||
UINFO(9," cloneInitArray("<<elements<<","<<offset<<") "<<nodep<<endl);
|
||||
AstNode* itemp = initp->initsp();
|
||||
int leOffset = !arrayp->rangep()->littleEndian() ? arrayp->rangep()->elementsConst()-1-offset : offset;
|
||||
for (int pos = 0; itemp && pos < leOffset; ++pos) {
|
||||
itemp = itemp->nextp();
|
||||
}
|
||||
if (clones > 1 && !refp->lvalue() && refp->varp() == m_lhsVarRefp->varp()
|
||||
&& !m_assignp->castAssignDly() && !m_assignError) {
|
||||
// LHS Var != RHS Var for a non-delayed assignment
|
||||
m_assignp->v3error("Unsupported: Slices in a non-delayed assignment with the same Var on both sides");
|
||||
m_assignError = true;
|
||||
if (!itemp) {
|
||||
nodep->v3error("Array initialization has too few elements, need element "<<offset);
|
||||
itemp = initp->initsp();
|
||||
}
|
||||
m_assignp->user2(clones);
|
||||
newp = itemp->cloneTree(false);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstSel* nodep) {
|
||||
m_extend = true;
|
||||
if (m_assignp && m_assignp->user2() > 1 && !m_assignError) {
|
||||
m_assignp->v3error("Unsupported: Assignment between unpacked arrays of different dimensions");
|
||||
else if (AstNodeCond* snodep = nodep->castNodeCond()) {
|
||||
UINFO(9," cloneCond("<<elements<<","<<offset<<") "<<nodep<<endl);
|
||||
return snodep->cloneType(snodep->condp()->cloneTree(false),
|
||||
cloneAndSel(snodep->expr1p(), elements, offset),
|
||||
cloneAndSel(snodep->expr2p(), elements, offset));
|
||||
}
|
||||
else if (nodep->castArraySel()
|
||||
|| nodep->castNodeVarRef()
|
||||
|| nodep->castNodeSel()) {
|
||||
UINFO(9," cloneSel("<<elements<<","<<offset<<") "<<nodep<<endl);
|
||||
int leOffset = !arrayp->rangep()->littleEndian() ? arrayp->rangep()->elementsConst()-1-offset : offset;
|
||||
newp = new AstArraySel(nodep->fileline(), nodep->cloneTree(false), leOffset);
|
||||
}
|
||||
else {
|
||||
if (!m_assignError) nodep->v3error(nodep->prettyTypeName()<<" unexpected in assignment to unpacked array");
|
||||
m_assignError = true;
|
||||
newp = nodep->cloneTree(false); // Likely will cause downstream errors
|
||||
}
|
||||
nodep->iterateChildren(*this);
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeCond* nodep) {
|
||||
// The conditional must be a single bit so only look at the expressions
|
||||
nodep->expr1p()->accept(*this);
|
||||
nodep->expr2p()->accept(*this);
|
||||
// Downstream data type may have changed; propagate up
|
||||
nodep->dtypeFrom(nodep->expr1p());
|
||||
}
|
||||
|
||||
// Return the first AstVarRef under the node
|
||||
AstVarRef* findVarRefRecurse(AstNode* nodep) {
|
||||
AstVarRef* refp = nodep->castVarRef();
|
||||
if (refp) return refp;
|
||||
if (nodep->op1p()) {
|
||||
refp = findVarRefRecurse(nodep->op1p());
|
||||
if (refp) return refp;
|
||||
}
|
||||
if (nodep->op2p()) {
|
||||
refp = findVarRefRecurse(nodep->op2p());
|
||||
if (refp) return refp;
|
||||
}
|
||||
if (nodep->op3p()) {
|
||||
refp = findVarRefRecurse(nodep->op3p());
|
||||
if (refp) return refp;
|
||||
}
|
||||
if (nodep->op4p()) {
|
||||
refp = findVarRefRecurse(nodep->op4p());
|
||||
if (refp) return refp;
|
||||
}
|
||||
if (nodep->nextp()) {
|
||||
refp = findVarRefRecurse(nodep->nextp());
|
||||
if (refp) return refp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void findImplicit(AstNodeAssign* nodep) {
|
||||
if (m_assignp) nodep->v3fatalSrc("Found a NodeAssign under another NodeAssign");
|
||||
m_assignp = nodep;
|
||||
m_assignError = false;
|
||||
m_extend = false;
|
||||
nodep->user1(true);
|
||||
// Record the LHS Var so we can check if the Var on the RHS is the same
|
||||
m_lhsVarRefp = findVarRefRecurse(nodep->lhsp());
|
||||
if (!m_lhsVarRefp) nodep->v3fatalSrc("Couldn't find a VarRef on the LHSP of an Assign");
|
||||
// Iterate children looking for ArraySel nodes. From that we get the number of elements
|
||||
// in the array so we know how many times we need to clone this assignment.
|
||||
nodep->iterateChildren(*this);
|
||||
if (nodep->user2() > 1) SliceCloneVisitor scv(nodep);
|
||||
m_assignp = NULL;
|
||||
return newp;
|
||||
}
|
||||
|
||||
virtual void visit(AstNodeAssign* nodep) {
|
||||
if (!nodep->user1()) {
|
||||
// Cleanup initArrays
|
||||
if (AstInitArray* initp = nodep->rhsp()->castInitArray()) {
|
||||
//if (debug()>=9) nodep->dumpTree(cout, "-InitArrayIn: ");
|
||||
AstNode* newp = NULL;
|
||||
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, itemp->unlinkFrBack()));
|
||||
// Deleted from list of items without correcting posIndex, but that's ok as about
|
||||
// to delete the entire InitArray
|
||||
// Called recursively on newly created assignments
|
||||
if (!nodep->user1()
|
||||
&& !nodep->castAssignAlias()) {
|
||||
nodep->user1(true);
|
||||
m_assignError = false;
|
||||
if (debug()>=9) { cout<<endl; nodep->dumpTree(cout," Deslice-In: "); }
|
||||
AstNodeDType* dtp = nodep->lhsp()->dtypep()->skipRefp();
|
||||
if (AstUnpackArrayDType* arrayp = dtp->castUnpackArrayDType()) {
|
||||
// Left and right could have different msb/lsbs/endianness, but #elements is common
|
||||
// and all variables are realigned to start at zero
|
||||
// Assign of a little endian'ed slice to a big endian one must reverse the elements
|
||||
AstNode* newlistp = NULL;
|
||||
int elements = arrayp->rangep()->elementsConst();
|
||||
for (int offset = 0; offset < elements; ++offset) {
|
||||
AstNode* newp = nodep->cloneType // AstNodeAssign
|
||||
(cloneAndSel(nodep->lhsp(), elements, offset),
|
||||
cloneAndSel(nodep->rhsp(), elements, offset));
|
||||
if (debug()>=9) { newp->dumpTree(cout,"-new "); }
|
||||
newlistp = AstNode::addNextNull(newlistp, newp);
|
||||
}
|
||||
//if (debug()>=9) newp->dumpTreeAndNext(cout, "-InitArrayOut: ");
|
||||
nodep->replaceWith(newp);
|
||||
pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
return; // Will iterate in a moment
|
||||
nodep->replaceWith(newlistp); nodep->deleteTree(); VL_DANGLING(nodep);
|
||||
if (debug()>=9) { cout<<endl; nodep->dumpTree(cout," Deslice-Dn: "); }
|
||||
// Normal edit iterator will now iterate on all of the expansion assignments
|
||||
// This will potentially call this function again to resolve next level of slicing
|
||||
return;
|
||||
}
|
||||
// Hasn't been searched for implicit slices yet
|
||||
findImplicit(nodep);
|
||||
m_assignp = nodep;
|
||||
nodep->iterateChildren(*this);
|
||||
m_assignp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void expandUniOp(AstNodeUniop* nodep) {
|
||||
if (!nodep->user1()) {
|
||||
nodep->user1(true);
|
||||
unsigned dim = 0;
|
||||
if (AstArraySel* selp = nodep->lhsp()->castArraySel()) {
|
||||
// We have explicit dimensions, either packed or unpacked
|
||||
dim = explicitDimensions(selp);
|
||||
}
|
||||
if (dim == 0 && !nodep->lhsp()->castVarRef()) {
|
||||
// No ArraySel nor VarRef, not something we can expand
|
||||
nodep->iterateChildren(*this);
|
||||
} else {
|
||||
AstVarRef* refp = findVarRefRecurse(nodep->lhsp());
|
||||
ArrayDimensions varDim = refp->varp()->dtypep()->dimensions(false);
|
||||
if ((int)(dim - varDim.second) < 0) {
|
||||
// Unpacked dimensions are referenced first, make sure we have them all
|
||||
nodep->v3error("Unary operator used across unpacked dimensions");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstInitArray* nodep) {
|
||||
if (m_assignp) {
|
||||
nodep->v3fatalSrc("Array initialization should have been removed earlier");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstRedOr* nodep) {
|
||||
expandUniOp(nodep);
|
||||
}
|
||||
virtual void visit(AstRedAnd* nodep) {
|
||||
expandUniOp(nodep);
|
||||
}
|
||||
virtual void visit(AstRedXor* nodep) {
|
||||
expandUniOp(nodep);
|
||||
}
|
||||
virtual void visit(AstRedXnor* nodep) {
|
||||
expandUniOp(nodep);
|
||||
}
|
||||
|
||||
void expandBiOp(AstNodeBiop* nodep) {
|
||||
if (!nodep->user1()) {
|
||||
nodep->user1(true);
|
||||
// If it's a unpacked array, blow it up into comparing each element
|
||||
// If it's an unpacked array, blow it up into comparing each element
|
||||
AstNodeDType* fromDtp = nodep->lhsp()->dtypep()->skipRefp();
|
||||
UINFO(9, " Bi-Eq/Neq expansion "<<nodep<<endl);
|
||||
if (AstUnpackArrayDType* adtypep = fromDtp->castUnpackArrayDType()) {
|
||||
AstNodeBiop* logp = NULL;
|
||||
for (int index = adtypep->rangep()->lsbConst();
|
||||
index <= adtypep->rangep()->msbConst(); ++index) {
|
||||
for (int index = 0; index <= adtypep->rangep()->elementsConst(); ++index) {
|
||||
// EQ(a,b) -> LOGAND(EQ(ARRAYSEL(a,0), ARRAYSEL(b,0)), ...[1])
|
||||
AstNodeBiop* clonep = nodep->cloneType
|
||||
(new AstArraySel(nodep->fileline(),
|
||||
@ -536,7 +219,7 @@ public:
|
||||
// CONSTUCTORS
|
||||
explicit SliceVisitor(AstNetlist* rootp) {
|
||||
m_assignp = NULL;
|
||||
m_lhsVarRefp = NULL;
|
||||
m_assignError = false;
|
||||
rootp->accept(*this);
|
||||
}
|
||||
virtual ~SliceVisitor() {}
|
||||
|
@ -305,8 +305,6 @@ private:
|
||||
} else {
|
||||
// TODO when unpacked arrays fully supported probably need new data type here
|
||||
AstArraySel* newp = new AstArraySel (nodep->fileline(), fromp, lsbp);
|
||||
newp->start(lsb);
|
||||
newp->length((msb - lsb) + 1);
|
||||
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
|
||||
}
|
||||
}
|
||||
|
@ -35,12 +35,16 @@ module t (/*AUTOARG*/
|
||||
array_3[2] = 4'b0100;
|
||||
array_3[3] = 4'b0100;
|
||||
|
||||
// Comparisons only compare elements 0
|
||||
array_1_ne_array_2 = array_1 != array_2; // 0
|
||||
array_1_eq_array_2 = array_1 == array_2; // 0
|
||||
array_1_ne_array_3 = array_1 != array_3; // 1
|
||||
array_1_eq_array_3 = array_1 == array_3; // 1
|
||||
|
||||
//Not legal: array_rxor = ^ array_1;
|
||||
//Not legal: array_rxnor = ^~ array_1;
|
||||
//Not legal: array_ror = | array_1;
|
||||
//Not legal: array_rand = & array_1;
|
||||
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("array_1_ne_array2==%0d\n", array_1_ne_array_2);
|
||||
$write("array_1_ne_array3==%0d\n", array_1_ne_array_3);
|
||||
|
@ -9,12 +9,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
|
||||
|
||||
compile (
|
||||
verilator_flags2 => ["--lint-only"],
|
||||
fails=>0,
|
||||
fails => 1,
|
||||
verilator_make_gcc => 0,
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
# Once got illegal assignment, but new slicing rules don't always detect this.
|
||||
# Due to V3Width.cpp pinwidth != conwidth requirement on data type mismatches
|
||||
expect=>
|
||||
q{%Error: t/t_inst_misarray_bad.v:16: VARREF 't.foo' is not an unpacked array, but is in an unpacked array context
|
||||
%Error: Exiting due to.*},
|
||||
);
|
||||
|
||||
|
||||
|
@ -13,8 +13,8 @@ compile (
|
||||
verilator_flags2 => ["--lint-only"],
|
||||
fails=>1,
|
||||
expect=>
|
||||
'%Error: t/t_mem_packed_bad.v:\d+: Unsupported: Assignment between unpacked arrays of different dimensions
|
||||
%Error: Exiting due to.*',
|
||||
q{%Error: t/t_mem_packed_bad.v:\d+: CONST '28'h0' unexpected in assignment to unpacked array
|
||||
%Error: Exiting due to.*},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
|
@ -13,11 +13,9 @@ compile (
|
||||
v_flags2 => ["--lint-only"],
|
||||
fails=>1,
|
||||
expect=>
|
||||
'%Error: t/t_mem_slice_bad.v:\d+: Slices of arrays in assignments must have the same unpacked dimensions
|
||||
%Error: t/t_mem_slice_bad.v:\d+: Slices of arrays in assignments must have the same unpacked dimensions
|
||||
%Error: t/t_mem_slice_bad.v:\d+: Unsupported: Slices in a non-delayed assignment with the same Var on both sides
|
||||
%Error: t/t_mem_slice_bad.v:\d+: Slices of arrays in assignments must have the same unpacked dimensions
|
||||
%Error: t/t_mem_slice_bad.v:\d+: Slices of arrays in assignments must have the same unpacked dimensions
|
||||
'%Error: t/t_mem_slice_bad.v:\d+: Slices of arrays in assignments have different unpacked dimensions, 9 versus 8
|
||||
%Error: t/t_mem_slice_bad.v:\d+: Slices of arrays in assignments have different unpacked dimensions, 4 versus 3
|
||||
%Error: t/t_mem_slice_bad.v:\d+: Slices of arrays in assignments have different unpacked dimensions, 9 versus 8
|
||||
%Error: Exiting due to.*',
|
||||
);
|
||||
|
||||
|
14
test_regress/t/t_slice_cond.pl
Executable file
14
test_regress/t/t_slice_cond.pl
Executable file
@ -0,0 +1,14 @@
|
||||
#!/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 (
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
38
test_regress/t/t_slice_cond.v
Normal file
38
test_regress/t/t_slice_cond.v
Normal file
@ -0,0 +1,38 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2017 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Outputs
|
||||
dataout,
|
||||
// Inputs
|
||||
clk, sel, d0, d1
|
||||
);
|
||||
|
||||
input clk;
|
||||
input sel;
|
||||
|
||||
logic [7:0] data [1:0][3:0];
|
||||
input [7:0] d0, d1;
|
||||
|
||||
output wire [8*2*4-1:0] dataout;
|
||||
|
||||
always_comb begin
|
||||
for ( integer j = 0; j <= 1; j++ ) begin
|
||||
if (sel)
|
||||
data[j] = '{ d0, d1, 8'h00, 8'h00 };
|
||||
else
|
||||
data[j] = '{ 8'h00, 8'h00, 8'h00, 8'h00 };
|
||||
end
|
||||
for ( integer j = 0; j <= 1; j++ ) begin
|
||||
data[j] = sel
|
||||
? '{ d0, d1, 8'h00, 8'h00 }
|
||||
: '{ 8'h00, 8'h00, 8'h00, 8'h00 };
|
||||
end
|
||||
end
|
||||
|
||||
assign dataout = {data[0][0], data[0][1], data[0][2], data[0][3],
|
||||
data[1][0], data[1][1], data[1][2], data[1][3]};
|
||||
|
||||
endmodule
|
18
test_regress/t/t_slice_init.pl
Executable file
18
test_regress/t/t_slice_init.pl
Executable 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;
|
59
test_regress/t/t_slice_init.v
Normal file
59
test_regress/t/t_slice_init.v
Normal file
@ -0,0 +1,59 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2017 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk, d0, d1
|
||||
);
|
||||
|
||||
input clk;
|
||||
input [7:0] d0, d1;
|
||||
|
||||
logic [7:0] inia [1:0][3:0] = '{ '{ '0, '1, 8'hfe, 8'hed },
|
||||
'{ '1, '1, 8'h11, 8'h22 }};
|
||||
logic [7:0] inil [0:1][0:3] = '{ '{ '0, '1, 8'hfe, 8'hed },
|
||||
'{ '1, '1, 8'h11, 8'h22 }};
|
||||
|
||||
logic [7:0] data [1:0][3:0];
|
||||
logic [7:0] datl [0:1][0:3];
|
||||
|
||||
initial begin
|
||||
data = '{ '{ d0, d1, 8'hfe, 8'hed },
|
||||
'{ d1, d1, 8'h11, 8'h22 }};
|
||||
data[0] = '{ d0, d1, 8'h19, 8'h39 };
|
||||
|
||||
datl = '{ '{ d0, d1, 8'hfe, 8'hed },
|
||||
'{ d1, d1, 8'h11, 8'h22 }};
|
||||
datl[0] = '{ d0, d1, 8'h19, 8'h39 };
|
||||
|
||||
`ifdef TEST_VERBOSE
|
||||
$display("D=%x %x %x %x -> 39 19 x x", data[0][0], data[0][1], data[0][2], data[0][3]);
|
||||
$display("D=%x %x %x %x -> ed fe x x", data[1][0], data[1][1], data[1][2], data[1][3]);
|
||||
$display("L=%x %x %x %x -> x x 19 39", datl[0][0], datl[0][1], datl[0][2], datl[0][3]);
|
||||
$display("L=%x %x %x %x -> x x 11 12", datl[1][0], datl[1][1], datl[1][2], datl[1][3]);
|
||||
`endif
|
||||
if (inia[0][0] !== 8'h22) $stop;
|
||||
if (inia[0][1] !== 8'h11) $stop;
|
||||
if (inia[1][0] !== 8'hed) $stop;
|
||||
if (inia[1][1] !== 8'hfe) $stop;
|
||||
|
||||
if (inil[0][2] !== 8'hfe) $stop;
|
||||
if (inil[0][3] !== 8'hed) $stop;
|
||||
if (inil[1][2] !== 8'h11) $stop;
|
||||
if (inil[1][3] !== 8'h22) $stop;
|
||||
|
||||
if (data[0][0] !== 8'h39) $stop;
|
||||
if (data[0][1] !== 8'h19) $stop;
|
||||
if (data[1][0] !== 8'hed) $stop;
|
||||
if (data[1][1] !== 8'hfe) $stop;
|
||||
|
||||
if (datl[0][2] !== 8'h19) $stop;
|
||||
if (datl[0][3] !== 8'h39) $stop;
|
||||
if (datl[1][2] !== 8'h11) $stop;
|
||||
if (datl[1][3] !== 8'h22) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
Loading…
Reference in New Issue
Block a user