diff --git a/Changes b/Changes index bdec837a5..0e8730ef6 100644 --- a/Changes +++ b/Changes @@ -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 diff --git a/src/V3Ast.h b/src/V3Ast.h index 7281c0718..3ba7cd3ce 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -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) { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 7f2fbe3a9..083ff714a 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -906,6 +906,12 @@ void AstSel::dump(ostream& str) { if (declElWidth()!=1) str<<"/"<AstNode::dump(str); + if (declRange().ranged()) { + str<<" decl"<AstNode::dump(str); for (int i=0; i<(int)(AstBasicDTypeKwd::_ENUM_MAX); ++i) { diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 3271e4b3c..8a55cd52f 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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 diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 0dc35912a..3a497e118 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -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(" "); diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp index 25f4a3ab0..68df62932 100644 --- a/src/V3Slice.cpp +++ b/src/V3Slice.cpp @@ -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("<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<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; diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 54160c1f1..b189cdcdc 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -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); } diff --git a/src/V3Undriven.cpp b/src/V3Undriven.cpp index e4787a81a..d7ff25165 100644 --- a/src/V3Undriven.cpp +++ b/src/V3Undriven.cpp @@ -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(); diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index 5728d60e4..7d8590a3e 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -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()) { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a78ec6cd5..3beb19e66 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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: "<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 diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 5be3c603e..2fec85bb2 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -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)) diff --git a/test_regress/t/t_array_backw_index_bad.pl b/test_regress/t/t_array_backw_index_bad.pl new file mode 100755 index 000000000..ee69233a3 --- /dev/null +++ b/test_regress/t/t_array_backw_index_bad.pl @@ -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; diff --git a/test_regress/t/t_array_backw_index_bad.v b/test_regress/t/t_array_backw_index_bad.v new file mode 100644 index 000000000..8b6f9dd2c --- /dev/null +++ b/test_regress/t/t_array_backw_index_bad.v @@ -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 diff --git a/test_regress/t/t_array_pattern_2d.pl b/test_regress/t/t_array_pattern_2d.pl new file mode 100755 index 000000000..f91289753 --- /dev/null +++ b/test_regress/t/t_array_pattern_2d.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 by Wilson Snyder. This program is free software; you can +# redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_array_pattern_2d.v b/test_regress/t/t_array_pattern_2d.v new file mode 100644 index 000000000..797c9d485 --- /dev/null +++ b/test_regress/t/t_array_pattern_2d.v @@ -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 diff --git a/test_regress/t/t_mem_slice_bad.pl b/test_regress/t/t_mem_slice_bad.pl index b8c2da841..0d06abd76 100755 --- a/test_regress/t/t_mem_slice_bad.pl +++ b/test_regress/t/t_mem_slice_bad.pl @@ -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);