|
|
|
|
@ -30,46 +30,37 @@
|
|
|
|
|
//
|
|
|
|
|
// What this pass does looks as below.
|
|
|
|
|
//
|
|
|
|
|
// <pre>
|
|
|
|
|
// logic [1:0] unpcked_array_var[0:3]; /*verilator split_var*/
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// unpacked_array_var[1] = unpacked_array_var[0]; // UNOPTFLAT warning
|
|
|
|
|
// unpacked_array_var[2] = unpacked_array_var[1];
|
|
|
|
|
// unpacked_array_var[3] = unpacked_array_var[2];
|
|
|
|
|
// end
|
|
|
|
|
// logic [3:0] packed_var; /*verilator split_var*/
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// if (some_cond) begin
|
|
|
|
|
// packed_var = 4'b0;
|
|
|
|
|
// end else begin
|
|
|
|
|
// packed_var[3] = some_input0;
|
|
|
|
|
// packed_var[2:0] = some_input1;
|
|
|
|
|
// end
|
|
|
|
|
// end
|
|
|
|
|
// </pre>
|
|
|
|
|
// logic [1:0] unpcked_array_var[0:1]; /*verilator split_var*/
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// unpacked_array_var[1] = unpacked_array_var[0]; // UNOPTFLAT warning
|
|
|
|
|
// end
|
|
|
|
|
// logic [3:0] packed_var; /*verilator split_var*/
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// if (some_cond) begin
|
|
|
|
|
// packed_var = 4'b0;
|
|
|
|
|
// end else begin
|
|
|
|
|
// packed_var[3] = some_input0;
|
|
|
|
|
// packed_var[2:0] = some_input1;
|
|
|
|
|
// end
|
|
|
|
|
// end
|
|
|
|
|
//
|
|
|
|
|
// is converted to
|
|
|
|
|
//
|
|
|
|
|
// <pre>
|
|
|
|
|
// logic [1:0] unpcked_array_var0;
|
|
|
|
|
// logic [1:0] unpcked_array_var1;
|
|
|
|
|
// logic [1:0] unpcked_array_var2;
|
|
|
|
|
// logic [1:0] unpcked_array_var3;
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// unpacked_array_var1 = unpacked_array_var0;
|
|
|
|
|
// unpacked_array_var2 = unpacked_array_var1;
|
|
|
|
|
// unpacked_array_var3 = unpacked_array_var2;
|
|
|
|
|
// end
|
|
|
|
|
// logic packed_var__BRA__3__KET__;
|
|
|
|
|
// logic [2:0] packed_var__BRA__2_0__KET__;
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// if (some_cond) begin
|
|
|
|
|
// {packed_var__BRA__3__KET__, packed_var__BRA__2_0__KET__} = 4'b0;
|
|
|
|
|
// end else begin
|
|
|
|
|
// packed_var__BRA__3__KET__ = some_input0;
|
|
|
|
|
// packed_var__BRA__2_0__KET__ = some_input1;
|
|
|
|
|
// end
|
|
|
|
|
// end
|
|
|
|
|
// logic [1:0] unpcked_array_var0;
|
|
|
|
|
// logic [1:0] unpcked_array_var1;
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// unpacked_array_var1 = unpacked_array_var0;
|
|
|
|
|
// end
|
|
|
|
|
// logic packed_var__BRA__3__KET__;
|
|
|
|
|
// logic [2:0] packed_var__BRA__2_0__KET__;
|
|
|
|
|
// always_comb begin
|
|
|
|
|
// if (some_cond) begin
|
|
|
|
|
// {packed_var__BRA__3__KET__, packed_var__BRA__2_0__KET__} = 4'b0;
|
|
|
|
|
// end else begin
|
|
|
|
|
// packed_var__BRA__3__KET__ = some_input0;
|
|
|
|
|
// packed_var__BRA__2_0__KET__ = some_input1;
|
|
|
|
|
// end
|
|
|
|
|
// end
|
|
|
|
|
// </pre>
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
@ -89,29 +80,6 @@
|
|
|
|
|
#include VL_INCLUDE_UNORDERED_MAP
|
|
|
|
|
#include VL_INCLUDE_UNORDERED_SET
|
|
|
|
|
|
|
|
|
|
static AstConst* constifyIfNot(AstNode* nodep) {
|
|
|
|
|
AstConst* constp = VN_CAST(nodep, Const);
|
|
|
|
|
if (!constp) {
|
|
|
|
|
UINFO(4, nodep << " is expected to be constant, but not\n");
|
|
|
|
|
AstNode* const constified = V3Const::constifyEdit(nodep);
|
|
|
|
|
UINFO(4, "After constified:" << constified << '\n');
|
|
|
|
|
constp = VN_CAST(constified, Const);
|
|
|
|
|
}
|
|
|
|
|
return constp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// returns <msb,lsb> of outer most dimension of an unpacked array
|
|
|
|
|
static std::pair<int, int> outerMostSizeOfUnpackedArray(AstVar* nodep) {
|
|
|
|
|
AstUnpackArrayDType* const dtypep = VN_CAST(nodep->dtypep(), UnpackArrayDType);
|
|
|
|
|
AstConst* const lsbp = constifyIfNot(dtypep->rangep()->lsbp());
|
|
|
|
|
AstConst* const msbp = constifyIfNot(dtypep->rangep()->msbp());
|
|
|
|
|
UASSERT_OBJ(lsbp, dtypep->rangep()->lsbp(), "must be constant");
|
|
|
|
|
UASSERT_OBJ(msbp, dtypep->rangep()->msbp(), "must be constant");
|
|
|
|
|
const vlsint32_t lsb = lsbp->toSInt(), msb = msbp->toSInt();
|
|
|
|
|
UASSERT_OBJ(lsb <= msb, dtypep->rangep(), "lsb must not greater than msb");
|
|
|
|
|
return std::make_pair(msb, lsb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Find a variable with pragma
|
|
|
|
|
|
|
|
|
|
@ -165,18 +133,36 @@ class SplitUnpackedVarVisitor : public AstNVisitor {
|
|
|
|
|
// key:variable to be split. value:location where the variable is referenced.
|
|
|
|
|
vl_unordered_map<AstVar*, std::vector<AstArraySel*> > m_refs;
|
|
|
|
|
|
|
|
|
|
// This visitor is used before V3Const::constifyAllLint(),
|
|
|
|
|
// some parameters need to be resolved here, but don't abuse this function.
|
|
|
|
|
static AstConst* constifyIfNot(AstNode* nodep) {
|
|
|
|
|
AstConst* constp = VN_CAST(nodep, Const);
|
|
|
|
|
if (!constp) {
|
|
|
|
|
UINFO(4, nodep << " is expected to be constant, but not\n");
|
|
|
|
|
AstNode* constified = V3Const::constifyEdit(nodep);
|
|
|
|
|
UINFO(4, "After constified:" << constified << '\n');
|
|
|
|
|
constp = VN_CAST(constified, Const);
|
|
|
|
|
}
|
|
|
|
|
return constp;
|
|
|
|
|
}
|
|
|
|
|
static int outerMostSizeOfUnpackedArray(AstVar* nodep) {
|
|
|
|
|
AstUnpackArrayDType* dtypep = VN_CAST(nodep->dtypep(), UnpackArrayDType);
|
|
|
|
|
UASSERT_OBJ(dtypep, nodep, "Must be unapcked array");
|
|
|
|
|
return dtypep->msb() - dtypep->lsb() + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
|
|
|
|
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
|
|
|
|
UASSERT_OBJ(m_modp == NULL, m_modp, "Nested module declration");
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
std::vector<std::pair<AstPragma*, AstVar*> > vars = ScanPragmaVisitor::scan(nodep);
|
|
|
|
|
for (size_t i = 0; i < vars.size(); ++i) {
|
|
|
|
|
AstPragma* const pragmap = vars[i].first;
|
|
|
|
|
AstVar* const varp = vars[i].second;
|
|
|
|
|
AstPragma* pragmap = vars[i].first;
|
|
|
|
|
AstVar* varp = vars[i].second;
|
|
|
|
|
if (pragmap->pragType() != AstPragmaType::SPLIT_VAR) continue; // nothing to do
|
|
|
|
|
bool keepPragma = false;
|
|
|
|
|
if (!varp) {
|
|
|
|
|
pragmap->v3warn(SPLITVAR, "Stray pragma of split_var is detected.");
|
|
|
|
|
pragmap->v3warn(SPLITVAR, "Unexpected location for split_var pragma.");
|
|
|
|
|
} else if (!canSplit(varp)) {
|
|
|
|
|
// maybe packed variable which will be split later.
|
|
|
|
|
keepPragma = true; // SplitPackedVarVisitor will read this pragma again later.
|
|
|
|
|
@ -185,8 +171,7 @@ class SplitUnpackedVarVisitor : public AstNVisitor {
|
|
|
|
|
m_refs.insert(std::make_pair(varp, std::vector<AstArraySel*>()));
|
|
|
|
|
}
|
|
|
|
|
if (!keepPragma) {
|
|
|
|
|
pragmap->unlinkFrBack()->deleteTree();
|
|
|
|
|
VL_DANGLING(vars[i].first);
|
|
|
|
|
pragmap->unlinkFrBack()->deleteTree(); VL_DANGLING(vars[i].first);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
@ -194,7 +179,7 @@ class SplitUnpackedVarVisitor : public AstNVisitor {
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
|
|
|
|
|
AstVar* const varp = nodep->varp();
|
|
|
|
|
AstVar* varp = nodep->varp();
|
|
|
|
|
if (m_refs.find(varp) == m_refs.end()) return; // variable without split_var pragma
|
|
|
|
|
|
|
|
|
|
if (m_firstRun)
|
|
|
|
|
@ -206,12 +191,13 @@ class SplitUnpackedVarVisitor : public AstNVisitor {
|
|
|
|
|
m_refs.erase(varp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void splitSimpleAssign(AstNodeAssign* asnp, AstVarRef* lhsp, AstVarRef *rhsp, int lstart, int rstart, int num) {
|
|
|
|
|
static void splitSimpleAssign(AstNodeAssign* asnp, AstVarRef* lhsp, AstVarRef* rhsp,
|
|
|
|
|
int lstart, int rstart, int num) {
|
|
|
|
|
for (int i = 0; i < num; ++i) {
|
|
|
|
|
AstVarRef* const lrefp = new AstVarRef(lhsp->fileline(), lhsp->varp(), true);
|
|
|
|
|
AstVarRef* const rrefp = new AstVarRef(rhsp->fileline(), rhsp->varp(), false);
|
|
|
|
|
AstArraySel* const lselp = new AstArraySel(lhsp->fileline(), lrefp, lstart + i);
|
|
|
|
|
AstArraySel* const rselp = new AstArraySel(rhsp->fileline(), rrefp, rstart + i);
|
|
|
|
|
AstVarRef* lrefp = new AstVarRef(lhsp->fileline(), lhsp->varp(), true);
|
|
|
|
|
AstVarRef* rrefp = new AstVarRef(rhsp->fileline(), rhsp->varp(), false);
|
|
|
|
|
AstArraySel* lselp = new AstArraySel(lhsp->fileline(), lrefp, lstart + i);
|
|
|
|
|
AstArraySel* rselp = new AstArraySel(rhsp->fileline(), rrefp, rstart + i);
|
|
|
|
|
// the added new assignment statement will be visited later.
|
|
|
|
|
asnp->addNext(asnp->cloneType(lselp, rselp));
|
|
|
|
|
}
|
|
|
|
|
@ -221,8 +207,8 @@ class SplitUnpackedVarVisitor : public AstNVisitor {
|
|
|
|
|
virtual void visit(AstNodeAssign* nodep) VL_OVERRIDE {
|
|
|
|
|
AstSliceSel* lsel = VN_CAST(nodep->lhsp(), SliceSel);
|
|
|
|
|
AstSliceSel* rsel = VN_CAST(nodep->rhsp(), SliceSel);
|
|
|
|
|
AstVarRef* const lhsp = VN_CAST(lsel ? lsel->fromp() : nodep->lhsp(), VarRef);
|
|
|
|
|
AstVarRef* const rhsp = VN_CAST(rsel ? rsel->fromp() : nodep->rhsp(), VarRef);
|
|
|
|
|
AstVarRef* lhsp = VN_CAST(lsel ? lsel->fromp() : nodep->lhsp(), VarRef);
|
|
|
|
|
AstVarRef* rhsp = VN_CAST(rsel ? rsel->fromp() : nodep->rhsp(), VarRef);
|
|
|
|
|
// unless simple assignment, nothing to do in this function
|
|
|
|
|
if (!lhsp || !rhsp) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
@ -230,43 +216,40 @@ class SplitUnpackedVarVisitor : public AstNVisitor {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if nodep is a simple assignment of variables without split_var pragma, quick exit
|
|
|
|
|
if (m_refs.find(lhsp->varp()) == m_refs.end() &&
|
|
|
|
|
m_refs.find(rhsp->varp()) == m_refs.end()) return;
|
|
|
|
|
if (m_refs.find(lhsp->varp()) == m_refs.end() && m_refs.find(rhsp->varp()) == m_refs.end())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int lstart, lnum, rstart, rnum;
|
|
|
|
|
if (lsel) {
|
|
|
|
|
lstart = lsel->declRange().lo();
|
|
|
|
|
lnum = lsel->declRange().elements();
|
|
|
|
|
} else { // LHS is entire array
|
|
|
|
|
const std::pair<int, int> lrange = outerMostSizeOfUnpackedArray(lhsp->varp());
|
|
|
|
|
lstart = 0;
|
|
|
|
|
lnum = lrange.first - lrange.second + 1;
|
|
|
|
|
lnum = outerMostSizeOfUnpackedArray(lhsp->varp());
|
|
|
|
|
}
|
|
|
|
|
if (rsel) {
|
|
|
|
|
rstart = rsel->declRange().lo();
|
|
|
|
|
rnum = rsel->declRange().elements();
|
|
|
|
|
} else { // RHS is entire array
|
|
|
|
|
const std::pair<int, int> rrange = outerMostSizeOfUnpackedArray(rhsp->varp());
|
|
|
|
|
rstart = 0;
|
|
|
|
|
rnum = rrange.first - rrange.second + 1;
|
|
|
|
|
rnum = outerMostSizeOfUnpackedArray(rhsp->varp());
|
|
|
|
|
}
|
|
|
|
|
if (lnum != rnum) return; // strange. V3Slice will show proper diagnosis
|
|
|
|
|
splitSimpleAssign(nodep, lhsp, rhsp, lstart, rstart, lnum);
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree();
|
|
|
|
|
VL_DANGLING(nodep);
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstArraySel* nodep) VL_OVERRIDE {
|
|
|
|
|
AstVarRef* const vrefp = VN_CAST(nodep->fromp(), VarRef);
|
|
|
|
|
AstVarRef* vrefp = VN_CAST(nodep->fromp(), VarRef);
|
|
|
|
|
if (!vrefp) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstVar* const varp = vrefp->varp();
|
|
|
|
|
AstVar* varp = vrefp->varp();
|
|
|
|
|
if (m_refs.find(varp) == m_refs.end()) return; // variable without split_var pragma
|
|
|
|
|
|
|
|
|
|
AstConst* const indexp = constifyIfNot(nodep->bitp());
|
|
|
|
|
AstConst* indexp = constifyIfNot(nodep->bitp());
|
|
|
|
|
|
|
|
|
|
if (indexp) { // OK
|
|
|
|
|
m_refs[varp].push_back(nodep);
|
|
|
|
|
@ -290,16 +273,14 @@ class SplitUnpackedVarVisitor : public AstNVisitor {
|
|
|
|
|
<< " refs will be split.\n");
|
|
|
|
|
AstVar* varp = it->first;
|
|
|
|
|
AstNode* insertp = varp;
|
|
|
|
|
AstUnpackArrayDType* const dtypep = VN_CAST(varp->dtypep(), UnpackArrayDType);
|
|
|
|
|
AstUnpackArrayDType* dtypep = VN_CAST(varp->dtypep(), UnpackArrayDType);
|
|
|
|
|
std::vector<AstVar*> vars;
|
|
|
|
|
// Add the split variables
|
|
|
|
|
const std::pair<int, int> arraySize = outerMostSizeOfUnpackedArray(varp);
|
|
|
|
|
const int msb = arraySize.first, lsb = arraySize.second;
|
|
|
|
|
for (vlsint32_t i = 0; i <= msb - lsb; ++i) {
|
|
|
|
|
// const std::string name = varp->name() + "__BRA__" + AstNode::encodeNumber(i + lsb) + "__KET__";
|
|
|
|
|
for (vlsint32_t i = 0; i <= dtypep->msb() - dtypep->lsb(); ++i) {
|
|
|
|
|
// const std::string name = varp->name() + "__BRA__" + AstNode::encodeNumber(i + dtypep->lsb()) + "__KET__";
|
|
|
|
|
// unpacked array is traced as var(idx).
|
|
|
|
|
const std::string name = varp->name() + AstNode::encodeName('(' + cvtToStr(i + lsb) + ')');
|
|
|
|
|
AstVar* const newp = new AstVar(varp->fileline(), varp->varType(), name, dtypep->subDTypep());
|
|
|
|
|
const std::string name = varp->name() + AstNode::encodeName('(' + cvtToStr(i + dtypep->lsb()) + ')');
|
|
|
|
|
AstVar* newp = new AstVar(varp->fileline(), varp->varType(), name, dtypep->subDTypep());
|
|
|
|
|
newp->trace(varp->isTrace());
|
|
|
|
|
insertp->addNextHere(newp);
|
|
|
|
|
if (newp->width() == 1) { // no need to try splitting
|
|
|
|
|
@ -313,18 +294,16 @@ class SplitUnpackedVarVisitor : public AstNVisitor {
|
|
|
|
|
// refer the split variable
|
|
|
|
|
for (size_t i = 0; i < it->second.size(); ++i) {
|
|
|
|
|
AstArraySel* selp = it->second[i];
|
|
|
|
|
AstVarRef* const vrefp = VN_CAST(selp->fromp(), VarRef);
|
|
|
|
|
AstConst* const indexp = VN_CAST(selp->bitp(), Const);
|
|
|
|
|
AstVarRef* vrefp = VN_CAST(selp->fromp(), VarRef);
|
|
|
|
|
AstConst* indexp = VN_CAST(selp->bitp(), Const);
|
|
|
|
|
UASSERT_OBJ(vrefp && indexp, selp, "already checked");
|
|
|
|
|
const uint32_t idx = indexp->toUInt();
|
|
|
|
|
// V3Width:width() removes VAR_BASE attribute and make index 0-origin.
|
|
|
|
|
AstVarRef* const new_vref = new AstVarRef(selp->fileline(), vars.at(idx), vrefp->lvalue());
|
|
|
|
|
AstVarRef* new_vref = new AstVarRef(selp->fileline(), vars.at(idx), vrefp->lvalue());
|
|
|
|
|
selp->replaceWith(new_vref);
|
|
|
|
|
selp->deleteTree();
|
|
|
|
|
VL_DANGLING(selp);
|
|
|
|
|
selp->deleteTree(); VL_DANGLING(selp);
|
|
|
|
|
}
|
|
|
|
|
varp->unlinkFrBack()->deleteTree();
|
|
|
|
|
VL_DANGLING(varp);
|
|
|
|
|
varp->unlinkFrBack()->deleteTree(); VL_DANGLING(varp);
|
|
|
|
|
++m_numSplit;
|
|
|
|
|
}
|
|
|
|
|
m_refs.clear(); // done
|
|
|
|
|
@ -339,7 +318,7 @@ public:
|
|
|
|
|
}
|
|
|
|
|
~SplitUnpackedVarVisitor() {
|
|
|
|
|
UASSERT(m_refs.empty(), "Don't forget to call split()");
|
|
|
|
|
if (m_firstRun) V3Stats::addStat("SplitVar, Split Unpacked Array", m_numSplit);
|
|
|
|
|
if (m_firstRun) V3Stats::addStat("SplitVar, Split unpacked arrays", m_numSplit);
|
|
|
|
|
}
|
|
|
|
|
int numSplit() const { return m_numSplit; }
|
|
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
|
|
|
|
@ -384,53 +363,50 @@ public:
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// one Entry instance for an AstVarRef instance
|
|
|
|
|
class PackedVarRefEntry {
|
|
|
|
|
AstNode* m_nodep; // either AstSel or AstVarRef is expected.
|
|
|
|
|
int m_lsb;
|
|
|
|
|
int m_bitwidth;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
PackedVarRefEntry(AstSel* selp, int lsb, int bitwidth)
|
|
|
|
|
: m_nodep(selp)
|
|
|
|
|
, m_lsb(lsb)
|
|
|
|
|
, m_bitwidth(bitwidth) {}
|
|
|
|
|
PackedVarRefEntry(AstVarRef* refp, int lsb, int bitwidth)
|
|
|
|
|
: m_nodep(refp)
|
|
|
|
|
, m_lsb(lsb)
|
|
|
|
|
, m_bitwidth(bitwidth) {}
|
|
|
|
|
AstNode* nodep() const { return m_nodep; }
|
|
|
|
|
int lsb() const { return m_lsb; }
|
|
|
|
|
int msb() const { return m_lsb + m_bitwidth - 1; }
|
|
|
|
|
int bitwidth() const { return m_bitwidth; }
|
|
|
|
|
void replaceNodeWith(AstNode* nodep) {
|
|
|
|
|
m_nodep->replaceWith(nodep);
|
|
|
|
|
m_nodep->deleteTree(); VL_DANGLING(m_nodep);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// How a variable is used
|
|
|
|
|
class PackedVarRef {
|
|
|
|
|
public:
|
|
|
|
|
// one Entry instance for an AstVarRef instance
|
|
|
|
|
class Entry {
|
|
|
|
|
AstNode* m_nodep; // either AstSel or AstVarRef is expected.
|
|
|
|
|
int m_lsb;
|
|
|
|
|
int m_bitwidth;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
Entry(AstSel* selp, int lsb, int bitwidth)
|
|
|
|
|
: m_nodep(selp)
|
|
|
|
|
, m_lsb(lsb)
|
|
|
|
|
, m_bitwidth(bitwidth) {}
|
|
|
|
|
Entry(AstVarRef* refp, int lsb, int bitwidth)
|
|
|
|
|
: m_nodep(refp)
|
|
|
|
|
, m_lsb(lsb)
|
|
|
|
|
, m_bitwidth(bitwidth) {}
|
|
|
|
|
AstNode* nodep() const { return m_nodep; }
|
|
|
|
|
int lsb() const { return m_lsb; }
|
|
|
|
|
int msb() const { return m_lsb + m_bitwidth - 1; }
|
|
|
|
|
int bitwidth() const { return m_bitwidth; }
|
|
|
|
|
void replaceNodeWith(AstNode* nodep) {
|
|
|
|
|
m_nodep->replaceWith(nodep);
|
|
|
|
|
m_nodep->deleteTree();
|
|
|
|
|
VL_DANGLING(m_nodep);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
struct SortByFirst {
|
|
|
|
|
bool operator()(const std::pair<int, bool>& a, const std::pair<int, bool>& b) const {
|
|
|
|
|
if (a.first == b.first) return a.second < b.second;
|
|
|
|
|
return a.first < b.first;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
std::vector<Entry> m_lhs, m_rhs;
|
|
|
|
|
std::vector<PackedVarRefEntry> m_lhs, m_rhs;
|
|
|
|
|
AstBasicDType* m_basicp; // cache the ptr since varp->dtypep()->basicp() is expensive
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
typedef std::vector<Entry>::iterator iterator;
|
|
|
|
|
typedef std::vector<Entry>::const_iterator const_iterator;
|
|
|
|
|
std::vector<Entry>& lhs() { return m_lhs; }
|
|
|
|
|
std::vector<Entry>& rhs() { return m_rhs; }
|
|
|
|
|
typedef std::vector<PackedVarRefEntry>::iterator iterator;
|
|
|
|
|
typedef std::vector<PackedVarRefEntry>::const_iterator const_iterator;
|
|
|
|
|
std::vector<PackedVarRefEntry>& lhs() { return m_lhs; }
|
|
|
|
|
std::vector<PackedVarRefEntry>& rhs() { return m_rhs; }
|
|
|
|
|
explicit PackedVarRef(AstVar* varp)
|
|
|
|
|
: m_basicp(varp->dtypep()->basicp()) {}
|
|
|
|
|
void append(const Entry& e, bool lvalue) {
|
|
|
|
|
void append(const PackedVarRefEntry& e, bool lvalue) {
|
|
|
|
|
if (lvalue)
|
|
|
|
|
m_lhs.push_back(e);
|
|
|
|
|
else
|
|
|
|
|
@ -442,7 +418,7 @@ public:
|
|
|
|
|
std::vector<SplitNewVar> splitPlan(bool skipUnused) const {
|
|
|
|
|
std::vector<SplitNewVar> plan;
|
|
|
|
|
std::vector<std::pair<int, bool> > points; // <bit location, is end>
|
|
|
|
|
points.reserve(m_lhs.size() * 2 + 2); // 2 points will be added per one Entry
|
|
|
|
|
points.reserve(m_lhs.size() * 2 + 2); // 2 points will be added per one PackedVarRefEntry
|
|
|
|
|
for (const_iterator it = m_lhs.begin(), itend = m_lhs.end(); it != itend; ++it) {
|
|
|
|
|
points.push_back(std::make_pair(it->lsb(), false)); // start of a region
|
|
|
|
|
points.push_back(std::make_pair(it->msb() + 1, true)); // end of a region
|
|
|
|
|
@ -488,14 +464,7 @@ class SplitPackedVarVisitor : public AstNVisitor {
|
|
|
|
|
// key:variable to be split. value:location where the variable is referenced.
|
|
|
|
|
vl_unordered_map<AstVar*, PackedVarRef> m_refs;
|
|
|
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
|
|
|
|
virtual void visit(AstAssign* nodep) VL_OVERRIDE {
|
|
|
|
|
UASSERT_OBJ(m_isLhs == false, nodep, "unexpected nested assign");
|
|
|
|
|
m_isLhs = true;
|
|
|
|
|
iterate(nodep->lhsp());
|
|
|
|
|
m_isLhs = false;
|
|
|
|
|
iterate(nodep->rhsp());
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignW* nodep) VL_OVERRIDE {
|
|
|
|
|
virtual void visit(AstNodeAssign* nodep) VL_OVERRIDE {
|
|
|
|
|
UASSERT_OBJ(m_isLhs == false, nodep, "unexpected nested assign");
|
|
|
|
|
m_isLhs = true;
|
|
|
|
|
iterate(nodep->lhsp());
|
|
|
|
|
@ -508,13 +477,13 @@ class SplitPackedVarVisitor : public AstNVisitor {
|
|
|
|
|
UINFO(3, "Start analyzing module " << nodep->prettyName() << '\n');
|
|
|
|
|
std::vector<std::pair<AstPragma*, AstVar*> > vars = ScanPragmaVisitor::scan(nodep);
|
|
|
|
|
for (size_t i = 0; i < vars.size(); ++i) {
|
|
|
|
|
AstPragma* const pragmap = vars[i].first;
|
|
|
|
|
AstVar* const varp = vars[i].second;
|
|
|
|
|
AstPragma* pragmap = vars[i].first;
|
|
|
|
|
AstVar* varp = vars[i].second;
|
|
|
|
|
if (pragmap->pragType() != AstPragmaType::SPLIT_VAR) continue; // nothing to do
|
|
|
|
|
UASSERT_OBJ(varp, pragmap,
|
|
|
|
|
"Stray pragma must have been consumed in SplitUnpackedVarVisitor");
|
|
|
|
|
"Unexpected pragma must have been consumed in SplitUnpackedVarVisitor");
|
|
|
|
|
if (!canSplit(varp, true)) {
|
|
|
|
|
pragmap->v3warn(SPLITVAR,
|
|
|
|
|
varp->v3warn(SPLITVAR,
|
|
|
|
|
"Pragma split_var is specified on a variable whose type is "
|
|
|
|
|
"unsupported or public. "
|
|
|
|
|
"Packed portion must be an aggregate type of bit or logic.");
|
|
|
|
|
@ -522,48 +491,49 @@ class SplitPackedVarVisitor : public AstNVisitor {
|
|
|
|
|
UINFO(3, varp->prettyNameQ() << " is added to candidate list.\n");
|
|
|
|
|
m_refs.insert(std::make_pair(varp, PackedVarRef(varp)));
|
|
|
|
|
}
|
|
|
|
|
pragmap->unlinkFrBack()->deleteTree(); // consume the pragma here anyway.
|
|
|
|
|
VL_DANGLING(vars[i].first);
|
|
|
|
|
// consume the pragma here anyway.
|
|
|
|
|
pragmap->unlinkFrBack()->deleteTree(); VL_DANGLING(vars[i].first);
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
split();
|
|
|
|
|
m_modp = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstVarRef* nodep) VL_OVERRIDE {
|
|
|
|
|
AstVar* const varp = nodep->varp();
|
|
|
|
|
AstVar* varp = nodep->varp();
|
|
|
|
|
vl_unordered_map<AstVar*, PackedVarRef>::iterator refit = m_refs.find(varp);
|
|
|
|
|
if (refit == m_refs.end()) return; // variable without split_var pragma
|
|
|
|
|
UASSERT_OBJ(nodep->lvalue() == m_isLhs, nodep,
|
|
|
|
|
(m_isLhs ? 'l' : 'r') << "value is expected");
|
|
|
|
|
const AstBasicDType* const basicp = refit->second.basicp();
|
|
|
|
|
refit->second.append(PackedVarRef::Entry(nodep, basicp->lsb(), basicp->width()), m_isLhs);
|
|
|
|
|
const AstBasicDType* basicp = refit->second.basicp();
|
|
|
|
|
refit->second.append(PackedVarRefEntry(nodep, basicp->lsb(), basicp->width()), m_isLhs);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstSel* nodep) VL_OVERRIDE {
|
|
|
|
|
AstVarRef* const vrefp = VN_CAST(nodep->fromp(), VarRef);
|
|
|
|
|
AstVarRef* vrefp = VN_CAST(nodep->fromp(), VarRef);
|
|
|
|
|
if (!vrefp) return;
|
|
|
|
|
|
|
|
|
|
AstVar* const varp = vrefp->varp();
|
|
|
|
|
AstVar* varp = vrefp->varp();
|
|
|
|
|
vl_unordered_map<AstVar*, PackedVarRef>::iterator refit = m_refs.find(varp);
|
|
|
|
|
if (refit == m_refs.end()) return; // variable without split_var pragma
|
|
|
|
|
|
|
|
|
|
AstConst* const consts[2] = {constifyIfNot(nodep->lsbp()), constifyIfNot(nodep->widthp())};
|
|
|
|
|
|
|
|
|
|
AstConst* consts[2] = {VN_CAST(nodep->lsbp(), Const), VN_CAST(nodep->widthp(), Const)};
|
|
|
|
|
if (consts[0] && consts[1]) { // OK
|
|
|
|
|
refit->second.append(
|
|
|
|
|
PackedVarRef::Entry(nodep, consts[0]->toSInt() + refit->second.basicp()->lsb(),
|
|
|
|
|
consts[1]->toUInt()),
|
|
|
|
|
PackedVarRefEntry(nodep, consts[0]->toSInt() + refit->second.basicp()->lsb(),
|
|
|
|
|
consts[1]->toUInt()),
|
|
|
|
|
m_isLhs);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3warn(SPLITVAR, "Variable "
|
|
|
|
|
<< vrefp->prettyNameQ()
|
|
|
|
|
<< " will not be split"
|
|
|
|
|
" because bit range cannot be determined statically.");
|
|
|
|
|
if(!consts[0]) UINFO(4, "LSB " << nodep->lsbp() << " is expected to be constant, but not\n");
|
|
|
|
|
if(!consts[1]) UINFO(4, "WIDTH " << nodep->widthp() << " is expected to be constant, but not\n");
|
|
|
|
|
m_refs.erase(varp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// extract necessary bit range from a newly created variable to meet ref
|
|
|
|
|
static AstNode* extractBits(const PackedVarRef::Entry& ref, const SplitNewVar& var, bool lvalue) {
|
|
|
|
|
AstVarRef* const refp = new AstVarRef(ref.nodep()->fileline(), var.varp(), lvalue);
|
|
|
|
|
static AstNode* extractBits(const PackedVarRefEntry& ref, const SplitNewVar& var, bool lvalue) {
|
|
|
|
|
AstVarRef* refp = new AstVarRef(ref.nodep()->fileline(), var.varp(), lvalue);
|
|
|
|
|
if (ref.lsb() <= var.lsb() && var.msb() <= ref.msb()) { // use the entire bits
|
|
|
|
|
return refp;
|
|
|
|
|
} else { // use slice
|
|
|
|
|
@ -573,7 +543,7 @@ class SplitPackedVarVisitor : public AstNVisitor {
|
|
|
|
|
UINFO(4, var.varp()->prettyNameQ() << "[" << msb << ":" << lsb << "] used for "
|
|
|
|
|
<< ref.nodep()->prettyNameQ() << '\n');
|
|
|
|
|
// LSB of varp is always 0. "lsb - var.lsb()" means this. see also SplitNewVar
|
|
|
|
|
AstSel* const selp = new AstSel(ref.nodep()->fileline(), refp, lsb - var.lsb(), bitwidth);
|
|
|
|
|
AstSel* selp = new AstSel(ref.nodep()->fileline(), refp, lsb - var.lsb(), bitwidth);
|
|
|
|
|
return selp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -583,7 +553,7 @@ class SplitPackedVarVisitor : public AstNVisitor {
|
|
|
|
|
it_end = m_refs.end();
|
|
|
|
|
it != it_end; ++it) {
|
|
|
|
|
AstVar* varp = it->first;
|
|
|
|
|
const AstBasicDType* const basicp = it->second.basicp();
|
|
|
|
|
const AstBasicDType* basicp = it->second.basicp();
|
|
|
|
|
UINFO(3, "In module " << m_modp->name() << " var " << varp->prettyNameQ()
|
|
|
|
|
<< " which has " << it->second.lhs().size() << " lhs refs and "
|
|
|
|
|
<< it->second.rhs().size() << " rhs refs will be split.\n");
|
|
|
|
|
@ -592,47 +562,51 @@ class SplitPackedVarVisitor : public AstNVisitor {
|
|
|
|
|
if (vars.empty()) continue;
|
|
|
|
|
// Add the split variables
|
|
|
|
|
for (size_t i = 0; i < vars.size(); ++i) {
|
|
|
|
|
SplitNewVar& var = vars[i];
|
|
|
|
|
int left = var.msb(), right = var.lsb();
|
|
|
|
|
SplitNewVar* newvarp = &vars[i];
|
|
|
|
|
int left = newvarp->msb(), right = newvarp->lsb();
|
|
|
|
|
if (basicp->littleEndian()) std::swap(left, right);
|
|
|
|
|
const std::string name = varp->name() + "__BRA__" + AstNode::encodeNumber(left)
|
|
|
|
|
+ AstNode::encodeName(":") + AstNode::encodeNumber(right)
|
|
|
|
|
+ "__KET__";
|
|
|
|
|
|
|
|
|
|
AstBasicDType* dtypep;
|
|
|
|
|
switch (basicp->keyword().m_e) {
|
|
|
|
|
switch (basicp->keyword()) {
|
|
|
|
|
case AstBasicDTypeKwd::BIT:
|
|
|
|
|
dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagBitPacked(), var.bitwidth());
|
|
|
|
|
dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagBitPacked(),
|
|
|
|
|
newvarp->bitwidth());
|
|
|
|
|
break;
|
|
|
|
|
case AstBasicDTypeKwd::LOGIC:
|
|
|
|
|
dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagLogicPacked(), var.bitwidth());
|
|
|
|
|
dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagLogicPacked(),
|
|
|
|
|
newvarp->bitwidth());
|
|
|
|
|
break;
|
|
|
|
|
default: UASSERT_OBJ(false, basicp, "Only bit and logic are allowed");
|
|
|
|
|
}
|
|
|
|
|
dtypep->rangep(new AstRange(varp->fileline(), var.msb(), var.lsb()));
|
|
|
|
|
dtypep->rangep(new AstRange(varp->fileline(), newvarp->msb(), newvarp->lsb()));
|
|
|
|
|
dtypep->rangep()->littleEndian(basicp->littleEndian());
|
|
|
|
|
var.varp(new AstVar(varp->fileline(), varp->varType(), name, dtypep));
|
|
|
|
|
// var.varp()->trace(varp->isTrace()); // enable this line to trace split variable directly
|
|
|
|
|
newvarp->varp(new AstVar(varp->fileline(), varp->varType(), name, dtypep));
|
|
|
|
|
// newvarp->varp()->trace(varp->isTrace()); // enable this line to trace split variable directly
|
|
|
|
|
m_netp->typeTablep()->addTypesp(dtypep);
|
|
|
|
|
varp->addNextHere(var.varp());
|
|
|
|
|
UINFO(4, var.varp()->prettyNameQ()
|
|
|
|
|
varp->addNextHere(newvarp->varp());
|
|
|
|
|
UINFO(4, newvarp->varp()->prettyNameQ()
|
|
|
|
|
<< " is added for " << varp->prettyNameQ() << '\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int lvalue = 0; lvalue <= 1; ++lvalue) { // refer the new split variables
|
|
|
|
|
std::vector<PackedVarRef::Entry>& refs = lvalue ? it->second.lhs() : it->second.rhs();
|
|
|
|
|
std::vector<PackedVarRefEntry>& refs = lvalue ? it->second.lhs() : it->second.rhs();
|
|
|
|
|
for (PackedVarRef::iterator refit = refs.begin(), refitend = refs.end();
|
|
|
|
|
refit != refitend; ++refit) {
|
|
|
|
|
NewVars::const_iterator varit = std::upper_bound(
|
|
|
|
|
vars.begin(), vars.end(), refit->lsb(), SplitNewVar::Match());
|
|
|
|
|
UASSERT_OBJ(varit != vars.end(), refit->nodep(), "Not found");
|
|
|
|
|
UASSERT(!(varit->msb() < refit->lsb() || refit->msb() < varit->lsb()), "wrong search result");
|
|
|
|
|
UASSERT(!(varit->msb() < refit->lsb() || refit->msb() < varit->lsb()),
|
|
|
|
|
"wrong search result");
|
|
|
|
|
AstNode* prev = extractBits(*refit, *varit, lvalue);
|
|
|
|
|
for (int residue = refit->msb() - varit->msb(); residue > 0;
|
|
|
|
|
residue -= varit->bitwidth()) {
|
|
|
|
|
++varit;
|
|
|
|
|
UASSERT_OBJ(varit != vars.end(), refit->nodep(), "not enough split variables");
|
|
|
|
|
AstNode* const bitsp = extractBits(*refit, *varit, lvalue);
|
|
|
|
|
UASSERT_OBJ(varit != vars.end(), refit->nodep(),
|
|
|
|
|
"not enough split variables");
|
|
|
|
|
AstNode* bitsp = extractBits(*refit, *varit, lvalue);
|
|
|
|
|
prev = new AstConcat(refit->nodep()->fileline(), bitsp, prev);
|
|
|
|
|
}
|
|
|
|
|
refit->replaceNodeWith(prev);
|
|
|
|
|
@ -643,7 +617,7 @@ class SplitPackedVarVisitor : public AstNVisitor {
|
|
|
|
|
vars.front().varp()->trace(varp->isTrace());
|
|
|
|
|
} else if (varp->isTrace()) {
|
|
|
|
|
// Let's create a dedicated variable for trace which is concat of split variables
|
|
|
|
|
AstVar* const traceVar = new AstVar(varp->fileline(), varp->varType(), varp->name(), varp->dtypep());
|
|
|
|
|
AstVar* traceVar = new AstVar(varp->fileline(), varp->varType(), varp->name(), varp->dtypep());
|
|
|
|
|
traceVar->trace(true);
|
|
|
|
|
varp->addNextHere(traceVar);
|
|
|
|
|
AstNode* rhs = new AstVarRef(vars.front().varp()->fileline(), vars.front().varp(), false);
|
|
|
|
|
@ -655,8 +629,7 @@ class SplitPackedVarVisitor : public AstNVisitor {
|
|
|
|
|
AstAssignW* assignp = new AstAssignW(varp->fileline(), new AstVarRef(varp->fileline(), traceVar, true), rhs);
|
|
|
|
|
traceVar->addNextHere(assignp);
|
|
|
|
|
}
|
|
|
|
|
varp->unlinkFrBack()->deleteTree();
|
|
|
|
|
VL_DANGLING(varp);
|
|
|
|
|
varp->unlinkFrBack()->deleteTree(); VL_DANGLING(varp);
|
|
|
|
|
++m_numSplit;
|
|
|
|
|
}
|
|
|
|
|
m_refs.clear(); // done
|
|
|
|
|
@ -672,7 +645,7 @@ public:
|
|
|
|
|
}
|
|
|
|
|
~SplitPackedVarVisitor() {
|
|
|
|
|
UASSERT(m_refs.empty(), "Don't forget to call split()");
|
|
|
|
|
V3Stats::addStat("SplitVar, Split Packed variables", m_numSplit);
|
|
|
|
|
V3Stats::addStat("SplitVar, Split packed variables", m_numSplit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if the passed variable can be split.
|
|
|
|
|
@ -680,11 +653,11 @@ public:
|
|
|
|
|
// when the access to the variable cannot be determined statically.
|
|
|
|
|
static bool canSplit(const AstVar* nodep, bool checkUnpacked) {
|
|
|
|
|
if (AstBasicDType* const basicp = nodep->dtypep()->basicp()) {
|
|
|
|
|
// floating point, string are not supported
|
|
|
|
|
const std::pair<uint32_t, uint32_t> dim = nodep->dtypep()->dimensions(false);
|
|
|
|
|
// unpacked array will be split in SplitUnpackedVarVisitor() beforehand.
|
|
|
|
|
return (!checkUnpacked || dim.second == 0) && nodep->dtypep()->widthMin() > 1
|
|
|
|
|
&& basicp->isBitLogic() && !nodep->isSigPublic();
|
|
|
|
|
&& basicp->isBitLogic() // floating point and string are not supported
|
|
|
|
|
&& !nodep->isSigPublic();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@ -702,7 +675,7 @@ void V3SplitVar::splitUnpackedVariable(AstNetlist* nodep) {
|
|
|
|
|
UINFO(3, visitor.numSplit() << " variables are split in trial " << trial << '\n');
|
|
|
|
|
if (visitor.numSplit() == 0) done = 1; // nothing to do anymore
|
|
|
|
|
} // Destruct before checking
|
|
|
|
|
V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
|
|
|
|
V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 9);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -711,7 +684,7 @@ void V3SplitVar::splitPackedVariable(AstNetlist* nodep) {
|
|
|
|
|
{
|
|
|
|
|
SplitPackedVarVisitor visitor(nodep);
|
|
|
|
|
} // Destruct before checking
|
|
|
|
|
V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
|
|
|
|
V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 9);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool V3SplitVar::canSplitVar(const AstVar* varp) {
|
|
|
|
|
|