Fix partial slicing with pattern assignments, bug991.

This commit is contained in:
Wilson Snyder 2017-11-23 14:55:32 -05:00
parent 4c35a76cdb
commit ae9179f412
16 changed files with 229 additions and 15 deletions

10
Changes
View File

@ -10,20 +10,22 @@ The contributors that suggested a given feature are shown in []. Thanks!
*** Support $size/$bits/etc on type references.
**** Fix MacOS portability, bug1232. [Jeff Bush]
*** Add error when driving input-only modport, bug1110. [Trevor Elbourne]
*** Add BSSPACE and COLONPLUS lint warnings.
**** Detect MSB overflow when under VL_DEBUG, bug1238. [Junyi Xi]
**** Add data types to --xml. [Rui Terra]
**** Add error when driving input-only modport, bug1110. [Trevor Elbourne]
**** Add BSSPACE and COLONPLUS lint warnings.
**** Fix partial slicing with pattern assignments, bug991. [Johan Bjork]
**** Fix false unused warning on interfaces, bug1241. [Laurens van Dam]
**** Fix error on "unique case" with no cases.
**** Fix MacOS portability, bug1232. [Jeff Bush]
* Verilator 3.914 2017-10-14

View File

@ -681,10 +681,14 @@ struct VNumRange {
return false;
}
//
class LeftRight {};
VNumRange() : m_hi(0), m_lo(0), mu_flags(0) {}
VNumRange(int hi, int lo, bool littleEndian)
: m_hi(0), m_lo(0), mu_flags(0)
{ init(hi,lo,littleEndian); }
VNumRange(LeftRight, int left, int right)
: m_hi(0), m_lo(0), mu_flags(0)
{ init((right>left)?right:left, (right>left)?left:right, (right>left)); }
~VNumRange() {}
// MEMBERS
void init(int hi, int lo, bool littleEndian) {

View File

@ -906,6 +906,12 @@ void AstSel::dump(ostream& str) {
if (declElWidth()!=1) str<<"/"<<declElWidth();
}
}
void AstSliceSel::dump(ostream& str) {
this->AstNode::dump(str);
if (declRange().ranged()) {
str<<" decl"<<declRange();
}
}
void AstTypeTable::dump(ostream& str) {
this->AstNode::dump(str);
for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) {

View File

@ -931,6 +931,40 @@ public:
void declElWidth(int flag) { m_declElWidth = flag; }
};
class AstSliceSel : public AstNodeTriop {
// Multiple array element extraction
// Parents: math|stmt
// Children: varref|arraysel, math, constant math
private:
VNumRange m_declRange; // Range of the 'from' array if isRanged() is set, else invalid
public:
AstSliceSel(FileLine* fl, AstNode* fromp, const VNumRange& declRange)
: AstNodeTriop(fl, fromp,
new AstConst(fl, declRange.lo()),
new AstConst(fl, declRange.elements()))
, m_declRange(declRange) { }
ASTNODE_NODE_FUNCS(SliceSel)
virtual void dump(ostream& str);
virtual void numberOperate(V3Number& out, const V3Number& from, const V3Number& lo, const V3Number& width) {
V3ERROR_NA; }
virtual string emitVerilog() { V3ERROR_NA; return ""; } // Implemented specially
virtual string emitC() { V3ERROR_NA; return ""; } // Removed before EmitC
virtual bool cleanOut() { return false; }
virtual bool cleanLhs() { return false; }
virtual bool cleanRhs() { return true; }
virtual bool cleanThs() { return true; }
virtual bool sizeMattersLhs() { return false; }
virtual bool sizeMattersRhs() { return false; }
virtual bool sizeMattersThs() { return false; }
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode*) const { return true; }
virtual int instrCount() const { return 10; } // Removed before matters
AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing)
// For widthConst()/loConst etc, see declRange().elements() and other VNumRange methods
VNumRange& declRange() { return m_declRange; }
void declRange(const VNumRange& flag) { m_declRange = flag; }
};
class AstMemberSel : public AstNodeMath {
// Parents: math|stmt
// Children: varref|arraysel, math

View File

@ -493,6 +493,10 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
}
puts("]");
}
virtual void visit(AstSliceSel* nodep) {
nodep->fromp()->iterateAndNext(*this);
puts(cvtToStr(nodep->declRange()));
}
virtual void visit(AstTypedef* nodep) {
putfs(nodep,"typedef ");
nodep->dtypep()->iterateAndNext(*this); puts(" ");

View File

@ -34,6 +34,9 @@
// Clone and iterate the clone:
// ARRAYSEL
// Modify bitp() for the new value and set ->length(1)
//
// TODO: This code was written before SLICESEL was a type it might be
// simplified to look primarily for SLICESELs.
//*************************************************************************
#include "config_build.h"
@ -101,6 +104,12 @@ class SliceVisitor : public AstNVisitor {
cloneAndSel(snodep->expr1p(), elements, offset),
cloneAndSel(snodep->expr2p(), elements, offset));
}
else if (AstSliceSel* snodep = nodep->castSliceSel()) {
UINFO(9," cloneSliceSel("<<elements<<","<<offset<<") "<<nodep<<endl);
int leOffset = (snodep->declRange().lo()
+ (!snodep->declRange().littleEndian() ? snodep->declRange().elements()-1-offset : offset));
newp = new AstArraySel(nodep->fileline(), snodep->fromp()->cloneTree(false), leOffset);
}
else if (nodep->castArraySel()
|| nodep->castNodeVarRef()
|| nodep->castNodeSel()) {
@ -137,8 +146,8 @@ class SliceVisitor : public AstNVisitor {
if (debug()>=9) { newp->dumpTree(cout,"-new "); }
newlistp = AstNode::addNextNull(newlistp, newp);
}
nodep->replaceWith(newlistp); nodep->deleteTree(); VL_DANGLING(nodep);
if (debug()>=9) { cout<<endl; nodep->dumpTree(cout," Deslice-Dn: "); }
nodep->replaceWith(newlistp); nodep->deleteTree(); VL_DANGLING(nodep);
// 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;

View File

@ -303,6 +303,11 @@ class TristatePinVisitor : public TristateBaseVisitor {
if (m_lvalue) nodep->v3fatalSrc("ArraySel conversion to output, under tristate node");
nodep->iterateChildren(*this);
}
virtual void visit(AstSliceSel* nodep) {
// Doesn't work because we'd set lvalue on the array index's var
if (m_lvalue) nodep->v3fatalSrc("SliceSel conversion to output, under tristate node");
nodep->iterateChildren(*this);
}
virtual void visit(AstNode* nodep) {
nodep->iterateChildren(*this);
}

View File

@ -295,6 +295,10 @@ private:
// Arrays are rarely constant assigned, so for now we punt and do all entries
nodep->iterateChildren(*this);
}
virtual void visit(AstSliceSel* nodep) {
// Arrays are rarely constant assigned, so for now we punt and do all entries
nodep->iterateChildren(*this);
}
virtual void visit(AstSel* nodep) {
AstNodeVarRef* varrefp = nodep->fromp()->castNodeVarRef();
AstConst* constp = nodep->lsbp()->castConst();

View File

@ -375,6 +375,9 @@ private:
}
}
// visit(AstSliceSel) not needed as its bounds are constant and checked
// in V3Width.
virtual void visit(AstArraySel* nodep) {
nodep->iterateChildren(*this);
if (!nodep->user1SetOnce()) {

View File

@ -700,6 +700,43 @@ private:
}
}
virtual void visit(AstSliceSel* nodep) {
// Always creates as output an unpacked array
if (m_vup->prelim()) {
userIterateAndNext(nodep->fromp(), WidthVP(SELF,BOTH).p());
//
// Array indices are always constant
AstNodeDType* fromDtp = nodep->fromp()->dtypep()->skipRefp();
AstUnpackArrayDType* adtypep = fromDtp->castUnpackArrayDType();
if (!adtypep) {
UINFO(1," Related dtype: "<<fromDtp<<endl);
nodep->v3fatalSrc("Packed array reference exceeds dimension of array");
}
// Build new array Dtype based on the original's base type, but with new bounds
AstNodeDType* newDtp = new AstUnpackArrayDType(nodep->fileline(),
adtypep->subDTypep(),
new AstRange(nodep->fileline(),
nodep->declRange()));
v3Global.rootp()->typeTablep()->addTypesp(newDtp);
nodep->dtypeFrom(newDtp);
if (!m_doGenerate) {
// Must check bounds before adding a select that truncates the bound
// Note we've already subtracted off LSB
if ((nodep->declRange().hi() > adtypep->declRange().hi())
|| nodep->declRange().lo() < adtypep->declRange().lo()) {
// Other simulators warn too
nodep->v3error("Slice selection index '"<< nodep->declRange() << "'"
<<" outside data type's '"<< adtypep->declRange() << "'");
}
else if ((nodep->declRange().littleEndian() != adtypep->declRange().littleEndian())) {
nodep->v3error("Slice selection '"<< nodep->declRange() << "'"
<<" has backward indexing versus data type's '"<< adtypep->declRange() << "'");
}
}
}
}
virtual void visit(AstSelBit* nodep) {
// Just a quick check as after V3Param these nodes instead are AstSel's
userIterateAndNext(nodep->fromp(), WidthVP(CONTEXT,PRELIM).p()); //FINAL in AstSel

View File

@ -294,19 +294,24 @@ private:
AstNode* lsbp = nodep->thsp()->unlinkFrBack();
vlsint32_t msb = msbp->castConst()->toSInt();
vlsint32_t lsb = lsbp->castConst()->toSInt();
vlsint32_t elem = (msb>lsb) ? (msb-lsb+1) : (lsb-msb+1);
FromData fromdata = fromDataForArray(nodep, fromp, false);
AstNodeDType* ddtypep = fromdata.m_dtypep;
VNumRange fromRange = fromdata.m_fromRange;
if (ddtypep->castUnpackArrayDType()) {
// Slice extraction
if (fromRange.elements() == (msb-lsb+1)
if (fromRange.elements() == elem
&& fromRange.lo() == lsb) { // Extracting whole of original array
nodep->replaceWith(fromp); pushDeletep(nodep); VL_DANGLING(nodep);
} else {
// TODO when unpacked arrays fully supported probably need new data type here
AstArraySel* newp = new AstArraySel (nodep->fileline(), fromp, lsbp);
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
}
} else if (fromRange.elements() == 1) { // Extracting single element
AstArraySel* newp = new AstArraySel(nodep->fileline(), fromp, lsbp);
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
} else { // Slice
AstSliceSel* newp = new AstSliceSel(nodep->fileline(), fromp,
VNumRange(VNumRange::LeftRight(),
msb, lsb));
nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep);
}
}
else if (AstPackArrayDType* adtypep = ddtypep->castPackArrayDType()) {
// SELEXTRACT(array, msb, lsb) -> SEL(array, lsb*width-of-subindex, width-of-subindex*(msb-lsb))

View File

@ -0,0 +1,21 @@
#!/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 (
fails=>1,
expect=>
q{%Error: t/t_array_backw_index_bad.v:\d+: Slice selection '\[1:3\]' has backward indexing versus data type's '\[3:0\]'
%Error: t/t_array_backw_index_bad.v:\d+: Slice selection '\[3:1\]' has backward indexing versus data type's '\[0:3\]'
%Error: t/t_array_backw_index_bad.v:\d+: Slice selection index '\[4:3\]' outside data type's '\[3:0\]'
%Error: t/t_array_backw_index_bad.v:\d+: Slice selection index '\[1:-1\]' outside data type's '\[3:0\]'
.*%Error: Exiting due to.*},
);
ok(1);
1;

View File

@ -0,0 +1,22 @@
// 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*/);
logic [31:0] array_assign [3:0];
logic [31:0] larray_assign [0:3];
initial begin
array_assign[1:3] = '{32'd4, 32'd3, 32'd2};
larray_assign[3:1] = '{32'd4, 32'd3, 32'd2};
array_assign[4:3] = '{32'd4, 32'd3};
array_assign[1:-1] = '{32'd4, 32'd3};
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View 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;

View File

@ -0,0 +1,39 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2009 by Iztok Jeras.
//bug991
module t (/*AUTOARG*/);
logic [31:0] array_assign [3:0];
logic [31:0] array_other [3:0];
logic [31:0] larray_assign [0:3];
logic [31:0] larray_other [0:3];
initial begin
array_assign[0] = 32'd1;
array_assign[3:1] = '{32'd4, 32'd3, 32'd2};
array_other[0] = array_assign[0]+10;
array_other[3:1] = array_assign[3:1];
if (array_other[0] != 11) $stop;
if (array_other[1] != 2) $stop;
if (array_other[2] != 3) $stop;
if (array_other[3] != 4) $stop;
larray_assign[0] = 32'd1;
larray_assign[1:3] = '{32'd4, 32'd3, 32'd2};
larray_other[0] = larray_assign[0]+10;
larray_other[1:3] = larray_assign[1:3];
if (larray_other[0] != 11) $stop;
if (larray_other[1] != 4) $stop;
if (larray_other[2] != 3) $stop;
if (larray_other[3] != 2) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -13,10 +13,11 @@ compile (
v_flags2 => ["--lint-only"],
fails=>1,
expect=>
'%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.*',
q{%Error: t/t_mem_slice_bad.v:\d+: Slice selection index '\[2:0\]' outside data type's '\[1:0\]'
%Error: t/t_mem_slice_bad.v:\d+: Slice selection index '\[3:0\]' outside data type's '\[2:0\]'
%Error: t/t_mem_slice_bad.v:\d+: Slice selection index '\[3:0\]' outside data type's '\[1:0\]'
%Error: t/t_mem_slice_bad.v:\d+: Slice selection index '\[8:0\]' outside data type's '\[7:0\]'
%Error: Exiting due to.*},
);
ok(1);