Support some unpacked arrays in parameters, bug1315.

This commit is contained in:
Wilson Snyder 2019-11-09 18:31:24 -05:00
parent 4767083a72
commit 28cbf39995
10 changed files with 282 additions and 60 deletions

View File

@ -20,6 +20,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
*** Suppress 'command failed' on normal errors.
*** Support some unpacked arrays in parameters, bug1315. [Marshal Qiao]
*** Add interface port visibility in traces, bug1594. [Todd Strader]
**** Increase case duplicate/incomplete to 16 bit tables, bug1545. [Yossi Nivin]

View File

@ -919,7 +919,7 @@ public:
virtual bool sizeMattersLhs() const { return false; }
virtual bool sizeMattersRhs() const { return false; }
virtual bool isGateOptimizable() const { return true; } // esp for V3Const::ifSameAssign
virtual bool isPredictOptimizable() const { return false; }
virtual bool isPredictOptimizable() const { return true; }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
virtual int instrCount() const { return widthInstrs(); }

View File

@ -1233,10 +1233,14 @@ private:
replaceZero(nodep); VL_DANGLING(nodep);
} else {
// Fetch the result
V3Number* outnump = simvis.fetchNumberNull(nodep);
UASSERT_OBJ(outnump, nodep, "No number returned from simulation");
AstNode* valuep = simvis.fetchValueNull(nodep); // valuep is owned by Simulate
UASSERT_OBJ(valuep, nodep, "No value returned from simulation");
// Replace it
replaceNum(nodep,*outnump); VL_DANGLING(nodep);
AstNode* newp = valuep->cloneTree(false);
newp->dtypeFrom(nodep);
newp->fileline(nodep->fileline());
UINFO(4, "Simulate->"<<newp<<endl);
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
}
}

View File

@ -113,7 +113,7 @@ private:
// Cleanup
// V3Numbers that represents strings are a bit special and the API for
// V3Number does not allow changing them.
std::deque<AstConst*> m_stringValuesp; // List of allocated string numbers
std::deque<AstNode*> m_reclaimValuesp; // List of allocated string numbers
// Note level 8&9 include debugging each simulation value
VL_DEBUG_FUNC; // Declare debug()
@ -232,7 +232,29 @@ private:
constp->num().isString(nodep->isString());
return constp;
}
public:
void newValue(AstNode* nodep, const AstNode* valuep) {
if (const AstConst* constp = VN_CAST_CONST(valuep, Const)) {
newConst(nodep)->num().opAssign(constp->num());
} else if (fetchValueNull(nodep) != valuep) {
// const_cast, as clonep() is set on valuep, but nothing should care
setValue(nodep, newTrackedClone(const_cast<AstNode*>(valuep)));
}
}
void newOutValue(AstNode* nodep, const AstNode* valuep) {
if (const AstConst* constp = VN_CAST_CONST(valuep, Const)) {
newOutConst(nodep)->num().opAssign(constp->num());
} else if (fetchOutValueNull(nodep) != valuep) {
// const_cast, as clonep() is set on valuep, but nothing should care
setOutValue(nodep, newTrackedClone(const_cast<AstNode*>(valuep)));
}
}
private:
AstNode* newTrackedClone(AstNode* nodep) {
AstNode* newp = nodep->cloneTree(false);
m_reclaimValuesp.push_back(newp);
return newp;
}
AstConst* newConst(AstNode* nodep) {
// Set a constant value for this node
if (!VN_IS(nodep->user3p(), Const)) {
@ -244,7 +266,7 @@ private:
}
}
AstConst* newOutConst(AstNode* nodep) {
// Set a constant value for this node
// Set a var-output constant value for this node
if (!VN_IS(nodep->user2p(), Const)) {
AstConst* constp = allocConst(nodep);
setOutValue(nodep, constp);
@ -253,12 +275,11 @@ private:
return fetchOutConst(nodep);
}
}
void newOutConst(AstNode* nodep, const AstConst* constr) {
newOutConst(nodep)->num().opAssign(constr->num());
}
public:
AstNode* fetchValueNull(AstNode* nodep) {
return (AstNode*)(nodep->user3p());
}
private:
AstNode* fetchOutValueNull(AstNode* nodep) {
return (AstNode*)(nodep->user2p());
}
@ -268,6 +289,12 @@ private:
AstConst* fetchOutConstNull(AstNode* nodep) {
return VN_CAST(fetchOutValueNull(nodep), Const);
}
AstNode* fetchValue(AstNode* nodep) {
AstNode* valuep = fetchValueNull(nodep);
UASSERT_OBJ(valuep, nodep, "No value found for node.");
//UINFO(9, " fetch val "<<*valuep<<" on "<<nodep<<endl);
return valuep;
}
AstConst* fetchConst(AstNode* nodep) {
AstConst* constp = fetchConstNull(nodep);
UASSERT_OBJ(constp, nodep, "No value found for node.");
@ -280,12 +307,6 @@ private:
return constp;
}
public:
void newValue(AstNode* nodep, const AstConst* constp) {
newConst(nodep, constp);
}
void newConst(AstNode* nodep, const AstConst* constp) {
newConst(nodep)->num().opAssign(constp->num());
}
V3Number* fetchNumberNull(AstNode* nodep) {
AstConst* constp = fetchConstNull(nodep);
if (constp) return &constp->num();
@ -298,10 +319,12 @@ public:
}
private:
void setValue(AstNode* nodep, const AstNode* valuep) {
UASSERT_OBJ(valuep, nodep, "Simulate setting null value");
UINFO(9, " set val "<<valuep->name()<<" on "<<nodep<<endl);
nodep->user3p((void*)valuep);
}
void setOutValue(AstNode* nodep, const AstNode* valuep) {
UASSERT_OBJ(valuep, nodep, "Simulate setting null value");
UINFO(9, " set oval "<<valuep->name()<<" on "<<nodep<<endl);
nodep->user2p((void*)valuep);
}
@ -344,13 +367,13 @@ private:
// True to jump over this node - all visitors must call this up front
return (m_jumpp && m_jumpp->labelp() != nodep);
}
void assignOutConst(AstNodeAssign* nodep, AstNode* vscp, const AstConst* valuep) {
void assignOutValue(AstNodeAssign* nodep, AstNode* vscp, const AstNode* valuep) {
if (VN_IS(nodep, AssignDly)) {
// Don't do setValue, as value isn't yet visible to following statements
newOutConst(vscp, valuep);
newOutValue(vscp, valuep);
} else {
newConst(vscp, valuep);
newOutConst(vscp, valuep);
newValue(vscp, valuep);
newOutValue(vscp, valuep);
}
}
@ -375,6 +398,7 @@ private:
// Delayed is OK though, as we'll decode the next state separately.
if (!VN_IS(nodep->varp()->dtypeSkipRefp(), BasicDType)
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), PackArrayDType)
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), UnpackArrayDType)
&& !VN_IS(nodep->varp()->dtypeSkipRefp(), StructDType))
clearOptimizable(nodep, "Array references/not basic");
if (nodep->lvalue()) {
@ -399,10 +423,10 @@ private:
}
vscp->user1(vscp->user1() | VU_RV);
bool isConst = nodep->varp()->isParam() && nodep->varp()->valuep();
AstConst* constp = isConst ? fetchConstNull(nodep->varp()->valuep()) : NULL;
if (isConst && constp) { // Propagate PARAM constants for constant function analysis
AstNode* valuep = isConst ? fetchValueNull(nodep->varp()->valuep()) : NULL;
if (isConst && valuep) { // Propagate PARAM constants for constant function analysis
if (!m_checkOnly && optimizable()) {
newConst(vscp, constp);
newValue(vscp, valuep);
}
} else {
if (m_checkOnly) varRefCb(nodep);
@ -414,16 +438,16 @@ private:
"LHS varref should be handled in AstAssign visitor.");
{
// Return simulation value - copy by reference instead of value for speed
AstConst* constp = fetchConstNull(vscp);
if (!constp) {
AstNode* valuep = fetchValueNull(vscp);
if (!valuep) {
if (m_params) {
clearOptimizable(nodep, "Language violation: reference to non-function-local variable");
} else {
nodep->v3fatalSrc("Variable value should have been set before any visitor called.");
}
constp = allocConst(nodep); // Any value; just so recover from error
valuep = allocConst(nodep); // Any value; just so recover from error
}
setValue(nodep, constp);
setValue(nodep, valuep);
}
}
}
@ -461,7 +485,13 @@ private:
virtual void visit(AstConst* nodep) {
checkNodeInfo(nodep);
if (!m_checkOnly && optimizable()) {
newConst(nodep, nodep);
newValue(nodep, nodep);
}
}
virtual void visit(AstInitArray* nodep) {
checkNodeInfo(nodep);
if (!m_checkOnly && optimizable()) {
newValue(nodep, nodep);
}
}
virtual void visit(AstEnumItemRef* nodep) {
@ -472,7 +502,7 @@ private:
if (valuep) {
iterateAndNextNull(valuep);
if (optimizable()) {
newConst(nodep, fetchConst(valuep));
newValue(nodep, fetchValue(valuep));
}
} else {
clearOptimizable(nodep, "No value found for enum item");
@ -520,9 +550,9 @@ private:
if (optimizable()) {
if (fetchConst(nodep->lhsp())->num().isNeqZero()) {
iterate(nodep->rhsp());
newConst(nodep, fetchConst(nodep->rhsp()));
newValue(nodep, fetchValue(nodep->rhsp()));
} else {
newConst(nodep, fetchConst(nodep->lhsp())); // a zero
newValue(nodep, fetchValue(nodep->lhsp())); // a zero
}
}
}
@ -537,10 +567,10 @@ private:
iterate(nodep->lhsp());
if (optimizable()) {
if (fetchConst(nodep->lhsp())->num().isNeqZero()) {
newConst(nodep, fetchConst(nodep->lhsp())); // a one
newValue(nodep, fetchValue(nodep->lhsp())); // a one
} else {
iterate(nodep->rhsp());
newConst(nodep, fetchConst(nodep->rhsp()));
newValue(nodep, fetchValue(nodep->rhsp()));
}
}
}
@ -556,10 +586,10 @@ private:
if (optimizable()) {
if (fetchConst(nodep->lhsp())->num().isEqZero()) {
AstConst cnst(nodep->fileline(), AstConst::WidthedValue(), 1, 1); // a one
newConst(nodep, &cnst); // a one
newValue(nodep, &cnst); // a one
} else {
iterate(nodep->rhsp());
newConst(nodep, fetchConst(nodep->rhsp()));
newValue(nodep, fetchValue(nodep->rhsp()));
}
}
}
@ -577,15 +607,63 @@ private:
if (optimizable()) {
if (fetchConst(nodep->condp())->num().isNeqZero()) {
iterate(nodep->expr1p());
newConst(nodep, fetchConst(nodep->expr1p()));
newValue(nodep, fetchValue(nodep->expr1p()));
} else {
iterate(nodep->expr2p());
newConst(nodep, fetchConst(nodep->expr2p()));
newValue(nodep, fetchValue(nodep->expr2p()));
}
}
}
}
void handleAssignArray(AstNodeAssign* nodep, AstArraySel* selp) {
iterateAndNextNull(nodep->rhsp()); // Value to assign
// At present we only handle single dimensional assignments
// To do better, we need the concept of lvalues, or similar, to know where/how to insert
checkNodeInfo(selp);
iterateAndNextNull(selp->bitp()); // Bit index
AstVarRef* varrefp = VN_CAST(selp->fromp(), VarRef);
if (!varrefp) {
clearOptimizable(nodep, "Array select LHS isn't simple variable");
return;
}
AstUnpackArrayDType* arrayp = VN_CAST(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType);
UASSERT_OBJ(arrayp, nodep, "Array select of non-array dtype");
AstBasicDType* basicp = VN_CAST(arrayp->subDTypep()->skipRefp(), BasicDType);
if (!basicp) {
clearOptimizable(nodep, "Array of non-basic dtype (e.g. array-of-array)");
return;
}
if (!m_checkOnly && optimizable()) {
AstNode* vscp = varOrScope(varrefp);
AstInitArray* initp = NULL;
if (AstInitArray* vscpnump = VN_CAST(fetchOutValueNull(vscp), InitArray)) {
initp = vscpnump;
} else if (AstInitArray* vscpnump = VN_CAST(fetchValueNull(vscp), InitArray)) {
initp = vscpnump;
} else { // Assignment to unassigned variable, all bits are X
// TODO generic initialization which builds X/arrays by recursion
AstConst* outconstp = new AstConst(nodep->fileline(), AstConst::WidthedValue(),
basicp->widthMin(), 0);
if (basicp->isZeroInit()) {
outconstp->num().setAllBits0();
} else {
outconstp->num().setAllBitsX();
}
initp = new AstInitArray(nodep->fileline(), arrayp, outconstp);
m_reclaimValuesp.push_back(initp);
}
uint32_t index = fetchConst(selp->bitp())->toUInt();
AstNode* valuep = newTrackedClone(fetchValue(nodep->rhsp()));
UINFO(9, " set val["<<index<<"] = "<<valuep<<endl);
// Values are in the "real" tree under the InitArray so can eventually extract it,
// Not in the usual setValue (pointed to by user2/3p)
initp->addIndexValuep(index, valuep);
if (debug() >= 9) initp->dumpTree(cout, "-array-");
assignOutValue(nodep, vscp, initp);
}
}
void handleAssignSel(AstNodeAssign* nodep, AstSel* selp) {
AstVarRef* varrefp = NULL;
V3Number lsb(nodep);
@ -595,31 +673,31 @@ private:
UASSERT_OBJ(varrefp, nodep,
"Indicated optimizable, but no variable found on RHS of select");
AstNode* vscp = varOrScope(varrefp);
AstConst* outconst = NULL;
AstConst* outconstp = NULL;
if (AstConst* vscpnump = fetchOutConstNull(vscp)) {
outconst = vscpnump;
outconstp = vscpnump;
} else if (AstConst* vscpnump = fetchConstNull(vscp)) {
outconst = vscpnump;
outconstp = vscpnump;
} else { // Assignment to unassigned variable, all bits are X or 0
outconst = new AstConst(nodep->fileline(), AstConst::WidthedValue(),
varrefp->varp()->widthMin(), 0);
outconstp = new AstConst(nodep->fileline(), AstConst::WidthedValue(),
varrefp->varp()->widthMin(), 0);
if (varrefp->varp()->basicp() && varrefp->varp()->basicp()->isZeroInit()) {
outconst->num().setAllBits0();
outconstp->num().setAllBits0();
} else {
outconst->num().setAllBitsX();
outconstp->num().setAllBitsX();
}
}
outconst->num().opSelInto(fetchConst(nodep->rhsp())->num(),
lsb,
selp->widthConst());
assignOutConst(nodep, vscp, outconst);
outconstp->num().opSelInto(fetchConst(nodep->rhsp())->num(),
lsb,
selp->widthConst());
assignOutValue(nodep, vscp, outconstp);
}
}
void handleAssignSelRecurse(AstNodeAssign* nodep, AstSel* selp,
AstVarRef*& outVarrefpRef, V3Number& lsbRef,
int depth) {
// Recurse down to find final variable being set (outVarrefp), with
// value to write on nodep->rhsp()
// lsb to be eventually set on lsbRef
checkNodeInfo(selp);
iterateAndNextNull(selp->lsbp()); // Bit index
if (AstVarRef* varrefp = VN_CAST(selp->fromp(), VarRef)) {
@ -654,6 +732,10 @@ private:
if (!m_params) { clearOptimizable(nodep, "LHS has select"); return; }
handleAssignSel(nodep, selp);
}
else if (AstArraySel* selp = VN_CAST(nodep->lhsp(), ArraySel)) {
if (!m_params) { clearOptimizable(nodep, "LHS has select"); return; }
handleAssignArray(nodep, selp);
}
else if (!VN_IS(nodep->lhsp(), VarRef)) {
clearOptimizable(nodep, "LHS isn't simple variable");
}
@ -664,11 +746,28 @@ private:
iterateAndNextNull(nodep->rhsp());
if (optimizable()) {
AstNode* vscp = varOrScope(VN_CAST(nodep->lhsp(), VarRef));
assignOutConst(nodep, vscp, fetchConst(nodep->rhsp()));
assignOutValue(nodep, vscp, fetchValue(nodep->rhsp()));
}
}
m_inDlyAssign = false;
}
virtual void visit(AstArraySel* nodep) {
checkNodeInfo(nodep);
iterateChildren(nodep);
if (AstInitArray* initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) {
AstConst* indexp = fetchConst(nodep->bitp());
uint32_t offset = indexp->num().toUInt();
AstNode* itemp = initp->getIndexDefaultedValuep(offset);
if (!itemp) {
clearOptimizable(nodep, "Array initialization has too few elements, need element "
+cvtToStr(offset));
} else {
setValue(nodep, itemp);
}
} else {
clearOptimizable(nodep, "Array select of non-array");
}
}
virtual void visit(AstBegin* nodep) {
checkNodeInfo(nodep);
iterateChildren(nodep);
@ -841,7 +940,7 @@ private:
if (pinp) { // Else too few arguments in function call - ignore it
// Apply value to the function
if (!m_checkOnly && optimizable()) {
newConst(portp, fetchConst(pinp));
newValue(portp, fetchValue(pinp));
}
}
}
@ -853,7 +952,7 @@ private:
if (!m_checkOnly && optimizable()) {
// Grab return value from output variable (if it's a function)
UASSERT_OBJ(funcp->fvarp(), nodep, "Function reference points at non-function");
newConst(nodep, fetchConst(funcp->fvarp()));
newValue(nodep, fetchValue(funcp->fvarp()));
}
}
@ -914,7 +1013,7 @@ private:
AstConst* resultConstp = new AstConst(nodep->fileline(), AstConst::String(), result);
setValue(nodep, resultConstp);
m_stringValuesp.push_back(resultConstp);
m_reclaimValuesp.push_back(resultConstp);
}
}
@ -938,7 +1037,7 @@ private:
// default
// These types are definitely not reducible
// AstCoverInc, AstArraySel, AstFinish,
// AstCoverInc, AstFinish,
// AstRand, AstTime, AstUCFunc, AstCCall, AstCStmt, AstUCStmt
virtual void visit(AstNode* nodep) {
if (jumpingOver(nodep)) return;
@ -1004,11 +1103,11 @@ public:
delete (*it2);
}
}
for (std::deque<AstConst*>::iterator it = m_stringValuesp.begin();
it != m_stringValuesp.end(); ++it) {
for (std::deque<AstNode*>::iterator it = m_reclaimValuesp.begin();
it != m_reclaimValuesp.end(); ++it) {
delete (*it);
}
m_stringValuesp.clear();
m_reclaimValuesp.clear();
m_constFreeps.clear();
m_constAllps.clear();
}

View File

@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
$Self->{vlt_all} and unsupported("Verilator unsupported, bug1315 unpacked array parameter simulation");
scenarios(simulator => 1);
compile(

View File

@ -11,14 +11,14 @@ module t;
int sum = 0;
for (int i=0; i<4; i++) begin
sum = sum + SIZES[i];
calc_sums[i][31:0] = sum;
calc_sums[i] = sum;
//TODO: calc_sums[i][31:0] = sum;
end
endfunction
parameter int SUMS[3:0] = calc_sums();
initial begin
$display("%d ",SUMS[0]);
if (SUMS[0] != 4) $stop;
if (SUMS[1] != 4+3) $stop;
if (SUMS[2] != 4+3+2) $stop;

View File

@ -0,0 +1,20 @@
#!/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.
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,41 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Wilson Snyder.
module t;
parameter int SIZES [3:1] = '{10,20,30};
parameter int SUMS3 = SIZES[3];
parameter int SUMS2 = SIZES[2];
parameter int SUMS1 = SIZES[1];
parameter int LE_SIZES [1:3] = '{10,20,30};
parameter int LE_SUMS3 = LE_SIZES[3];
parameter int LE_SUMS2 = LE_SIZES[2];
parameter int LE_SUMS1 = LE_SIZES[1];
function int from_array(int index);
if (index != 0); return SIZES[index];
endfunction
function int from_array_le(int index);
if (index != 0); return LE_SIZES[index];
endfunction
initial begin
if (SUMS1 != 30) $stop;
if (SUMS2 != 20) $stop;
if (SUMS3 != 10) $stop;
if (LE_SUMS1 != 10) $stop;
if (LE_SUMS2 != 20) $stop;
if (LE_SUMS3 != 30) $stop;
if (from_array(1) != 30) $stop;
if (from_array(2) != 20) $stop;
if (from_array(3) != 10) $stop;
if (from_array_le(1) != 10) $stop;
if (from_array_le(2) != 20) $stop;
if (from_array_le(3) != 30) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,20 @@
#!/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.
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,38 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Wilson Snyder.
//bug1578
module t;
parameter N = 4;
typedef logic array_t[N];
parameter array_t MASK = mask_array();
//TODO bug1578: parameter MASK = mask_array();
function array_t mask_array();
for(int i = 0; i < N; i++) begin
mask_array[i] = i[0];
end
endfunction
array_t norm;
initial begin
if (N != 4) $stop;
norm = mask_array();
if (norm[0] != 1'b0) $stop;
if (norm[1] != 1'b1) $stop;
if (norm[2] != 1'b0) $stop;
if (norm[3] != 1'b1) $stop;
if (MASK[0] != 1'b0) $stop;
if (MASK[1] != 1'b1) $stop;
if (MASK[2] != 1'b0) $stop;
if (MASK[3] != 1'b1) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule