From 89e03a86b798e5435823887af6194010972c57c8 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 19 Jan 2010 10:52:11 -0500 Subject: [PATCH] Support assignments of multidimensional slices, bug170 --- Changes | 2 + src/Makefile_obj.in | 1 + src/V3Ast.h | 6 +- src/V3AstNodes.cpp | 21 ++ src/V3AstNodes.h | 14 +- src/V3Delayed.cpp | 1 + src/V3Expand.cpp | 1 + src/V3Slice.cpp | 353 ++++++++++++++++++++++++++ src/V3Slice.h | 37 +++ src/V3WidthSel.cpp | 16 +- src/Verilator.cpp | 5 + test_regress/t/t_mem_multi_ref_bad.pl | 9 +- test_regress/t/t_mem_packed_assign.pl | 20 ++ test_regress/t/t_mem_packed_assign.v | 36 +++ test_regress/t/t_mem_slice.pl | 19 ++ test_regress/t/t_mem_slice.v | 117 +++++++++ test_regress/t/t_mem_slice_bad.pl | 25 ++ test_regress/t/t_mem_slice_bad.v | 58 +++++ 18 files changed, 728 insertions(+), 13 deletions(-) create mode 100644 src/V3Slice.cpp create mode 100644 src/V3Slice.h create mode 100755 test_regress/t/t_mem_packed_assign.pl create mode 100644 test_regress/t/t_mem_packed_assign.v create mode 100755 test_regress/t/t_mem_slice.pl create mode 100644 test_regress/t/t_mem_slice.v create mode 100755 test_regress/t/t_mem_slice_bad.pl create mode 100644 test_regress/t/t_mem_slice_bad.v diff --git a/Changes b/Changes index f68adc9c5..48dbddafe 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,8 @@ indicates the contributor was also the author of the fix; Thanks! ** Support direct programming interface (DPI) "import" and "export". Includes an extension to map user $system PLI calls to the DPI. +*** Support assignments of multidimensional slices, bug170. [by Byron Bradley] + *** Support "reg [1:0][1:0][1:0]" and "reg x [3][2]", bug176. [Byron Bradley] *** Support declarations in loop initializers, bug172. [by Byron Bradley] diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index e667bf5e5..07e9d626d 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -206,6 +206,7 @@ RAW_OBJS = \ V3Premit.o \ V3Scope.o \ V3Signed.o \ + V3Slice.o \ V3Split.o \ V3SplitAs.o \ V3Stats.o \ diff --git a/src/V3Ast.h b/src/V3Ast.h index d3e39b239..10aa07592 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1228,8 +1228,10 @@ struct AstNodeSel : public AstNodeBiop { AstNodeSel(FileLine* fl, AstNode* fromp, AstNode* bitp) :AstNodeBiop(fl, fromp, bitp) {} ASTNODE_BASE_FUNCS(NodeSel) - AstNode* fromp() const { return op1p()->castNode(); } // op1 = Extracting what (NULL=TBD during parsing) - AstNode* bitp() const { return op2p()->castNode(); } // op2 = Msb selection expression + AstNode* fromp() const { return op1p()->castNode(); } // op1 = Extracting what (NULL=TBD during parsing) + void fromp(AstNode* nodep) { setOp1p(nodep); } + AstNode* bitp() const { return op2p()->castNode(); } // op2 = Msb selection expression + void bitp(AstNode* nodep) { setOp2p(nodep); } int bitConst() const; }; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 72383260b..db5ff0ffa 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -258,6 +258,23 @@ uint32_t AstVar::arrayElements() const { return entries; } +uint32_t AstVar::dimensions() const { + // How many array dimensions does this Var have? + uint32_t dim = 0; + for (AstNodeDType* dtypep=this->dtypep(); dtypep; ) { + dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + if (AstArrayDType* adtypep = dtypep->castArrayDType()) { + dim += 1; + dtypep = adtypep->dtypep(); + } + else { + // AstBasicDType - nothing below, 1 + break; + } + } + return dim; +} + // Special operators int AstArraySel::dimension(AstNode* nodep) { // How many dimensions is this reference from the base variable? @@ -445,6 +462,10 @@ void AstNode::dump(ostream& os) { if (name()!="") os<<" "<AstNode::dump(str); + str<<" [start:"<AstNode::dump(str); str<<" ["<skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) AstBasicDType* basicp() const { return dtypep()->basicp(); } // (Slow) recurse down to find basic data type (Note don't need virtual - AstVar isn't a NodeDType) AstNodeDType* dtypeDimensionp(int depth) const; + uint32_t dimensions() const; AstNode* initp() const { return op3p()->castNode(); } // op3 = Initial value that never changes (static const) void initp(AstNode* nodep) { setOp3p(nodep); } void addAttrsp(AstNode* nodep) { addNOp4p(nodep); } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index eae03ad7b..b1f175d37 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -158,6 +158,7 @@ private: arrayselp = lhsp->castArraySel(); } if (!arrayselp) nodep->v3fatalSrc("No arraysel under bitsel?"); + if (arrayselp->length()!=1) nodep->v3fatalSrc("ArraySel with length!=1 should have been removed in V3Slice"); UINFO(4,"AssignDlyArray: "<length()!=1) nodep->v3fatalSrc("ArraySel with length!=1 should have been removed in V3Slice"); for (int w=0; wwidthWords(); w++) { addWordAssign(nodep, w, newAstWordSelClone (rhsp, w)); } diff --git a/src/V3Slice.cpp b/src/V3Slice.cpp new file mode 100644 index 000000000..07799d337 --- /dev/null +++ b/src/V3Slice.cpp @@ -0,0 +1,353 @@ +//************************************************************************* +// DESCRIPTION: Verilator: Parse module/signal name references +// +// Code available from: http://www.veripool.org/verilator +// +// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli +// +//************************************************************************* +// +// Copyright 2003-2010 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. +// +// Verilator is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +//************************************************************************* +// Slice TRANSFORMATIONS: +// Top-down traversal (SliceVisitor): +// NODEASSIGN +// ARRAYSEL +// Compare the dimensions to the Var to check for implicit slices. +// Using ->length() calculate the number of clones needed. +// VARREF +// Check the dimensions of the Var for an implicit slice. +// Replace with ArraySel nodes if needed. +// SEL, EXTEND +// We might be assigning a 1-D packed array to a 2-D packed array, +// this is unsupported. +// SliceCloneVisitor (called if this node is a slice): +// NODEASSIGN +// Clone and iterate the clone: +// ARRAYSEL +// Modify bitp() for the new value and set ->length(1) +//************************************************************************* + +#include "config_build.h" +#include "verilatedos.h" +#include +#include +#include + +#include "V3Global.h" +#include "V3Slice.h" +#include "V3Ast.h" +#include + +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 + + // STATE + vector > m_selBits; // Indexes of the ArraySel we are expanding + unsigned 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, AstNUser*) { + if (!nodep->backp()->castArraySel()) { + // This is the top of an ArraySel, setup + m_refp = nodep->user1p()->castNode()->castVarRef(); + m_vecIdx += 1; + if (m_vecIdx == m_selBits.size()) { + m_selBits.push_back(vector()); + AstVar* varp = m_refp->varp(); + int dimensions = varp->dimensions(); + for (int 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()) { + unsigned idx = nodep->start() + m_selBits[m_vecIdx][m_depth]; + 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, AstNUser*) { + m_selBits.clear(); + UINFO(4, "Cloning "<user2()<<" times: "<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(); nodep = NULL; + } + + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + SliceCloneVisitor(AstNodeAssign* assignp) { + assignp->accept(*this); + } + virtual ~SliceCloneVisitor() {} +}; + +//************************************************************************* + +class SliceVisitor : public AstNVisitor { + // NODE STATE + // Cleared on netlist + // AstNodeAssign::user1() -> bool. True if find is complete + // AstNodeAssign::user2() -> int. The number of clones needed for this assign + // AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + + // STATE + AstNode* m_assignp; // Assignment we are under + AstNodeVarRef* m_lhsVarRefp; // Var on the LHS + bool m_extend; // We have found an extend node + + // METHODS + static int debug() { + static int level = -1; + if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); + return level; + } + + // Find out how many explicit dimensions are in a given ArraySel. + unsigned explicitDimensions(AstArraySel* nodep) { + unsigned dim = 0; + AstNode* fromp = nodep; + AstArraySel* selp; + do { + selp = fromp->castArraySel(); + if (!selp) { + nodep->user1p(fromp->castVarRef()); + selp = NULL; + } else { + fromp = selp->fromp(); + if (fromp) ++dim; + } + } while (fromp && selp); + if (!m_assignp->user1p()) nodep->v3fatalSrc("Couldn't find VarRef under the ArraySel"); + return dim; + } + + AstNode* insertImplicit(AstVarRef* nodep, unsigned start, unsigned count) { + // Insert any implicit slices as explicit slices (ArraySel nodes). + // Return a new pointer to replace fromp() in the ArraySel. + AstVarRef* fromp = nodep; + if (!fromp) nodep->v3fatalSrc("NULL VarRef passed to insertImplicit"); + AstVar* varp = fromp->varp(); + // Get the DType and insert a new ArraySel + AstArraySel* topp = NULL; + AstArraySel* bottomp = NULL; + for (unsigned i = start; i < start + count; ++i) { + AstNodeDType* dtypep = varp->dtypeDimensionp(i-1); + AstArrayDType* adtypep = dtypep->castArrayDType(); + 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; + } + AstArraySel* newp = new AstArraySel(nodep->fileline(), fromp, new AstConst(nodep->fileline(),lsb)); + newp->start(lsb); + newp->length(msb - lsb + 1); + if (!topp) topp = newp; + fromp = newp->fromp()->unlinkFrBack()->castVarRef(); + + if (bottomp) bottomp->fromp(newp); + bottomp = newp; + } + bottomp->fromp(fromp); + return topp; + } + + 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; + } + + // VISITORS + virtual void visit(AstVarRef* nodep, AstNUser*) { + // The LHS/RHS of an Assign may be to a Var that is an array. In this + // case we need to create a slice accross the entire Var + if (m_assignp && !nodep->backp()->castArraySel()) { + uint32_t dimensions = nodep->varp()->dimensions(); + if (dimensions > 0) { + AstNode* newp = insertImplicit(nodep->cloneTree(false), 1, dimensions); + nodep->replaceWith(newp); nodep = NULL; + newp->iterateChildren(*this); + } + } + } + + virtual void visit(AstExtend* nodep, AstNUser*) { + m_extend = true; + if (m_assignp && m_assignp->user2() > 1) { + m_assignp->v3error("Unsupported: Assignment between packed arrays of different dimensions"); + } + nodep->iterateChildren(*this); + } + + virtual void visit(AstArraySel* nodep, AstNUser*) { + if (!m_assignp) return; + unsigned dim = explicitDimensions(nodep); + AstVarRef* refp = nodep->user1p()->castNode()->castVarRef(); + unsigned implicit = refp->varp()->dimensions() - dim; + if (implicit > 0) { + AstNode* backp = refp->backp(); + AstNode* newp = insertImplicit(refp->cloneTree(false), dim+1, implicit); + backp->castArraySel()->fromp()->replaceWith(newp); + } + 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() == 0) { + if (m_extend && clones > 1) { + m_assignp->v3error("Unsupported: Assignment between packed arrays of different dimensions"); + } + if (clones > 1 && !refp->lvalue() && refp->varp() == m_lhsVarRefp->varp() && !m_assignp->castAssignDly()) { + // 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_assignp->user2(clones); + } + } + + virtual void visit(AstSel* nodep, AstNUser*) { + m_extend = true; + if (m_assignp && m_assignp->user2() > 1) { + m_assignp->v3error("Unsupported: Assignment between packed arrays of different dimensions"); + } + nodep->iterateChildren(*this); + } + + // 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->op3p()) { + refp = findVarRefRecurse(nodep->op3p()); + 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_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; + } + + virtual void visit(AstNodeAssign* nodep, AstNUser*) { + if (!nodep->user1()) { + // Hasn't been searched for implicit slices yet + findImplicit(nodep); + } + } + + virtual void visit(AstNode* nodep, AstNUser*) { + // Default: Just iterate + nodep->iterateChildren(*this); + } + +public: + // CONSTUCTORS + SliceVisitor(AstNetlist* rootp) { + m_assignp = NULL; + m_lhsVarRefp = NULL; + rootp->accept(*this); + } + virtual ~SliceVisitor() {} +}; + +//###################################################################### +// Link class functions + +void V3Slice::sliceAll(AstNetlist* rootp) { + UINFO(4,__FUNCTION__<<": "<=9) nodep->backp()->dumpTree(cout,"-selcheck: "); AstNodeDType* ddtypep = varp->dtypeDimensionp(dimension); if (AstArrayDType* adtypep = ddtypep->castArrayDType()) { - if (rangedSelect) { - nodep->v3error("Illegal bit select; can't bit extract from arrayed dimension: "<prettyName()); - return NULL; - } return adtypep; } else if (AstBasicDType* adtypep = ddtypep->castBasicDType()) { @@ -247,7 +243,17 @@ private: vlsint32_t msb = msbp->castConst()->toSInt(); vlsint32_t lsb = lsbp->castConst()->toSInt(); AstNodeDType* ddtypep = dtypeForExtractp(nodep, basefromp, dimension, msb!=lsb); - if (AstBasicDType* adtypep = ddtypep->castBasicDType()) { + if (AstArrayDType* adtypep = ddtypep->castArrayDType()) { + if (adtypep) {} + if (msb!=lsb) { + AstArraySel* newp = new AstArraySel (nodep->fileline(), fromp, lsbp); + newp->start(lsb); + newp->length((msb - lsb) + 1); + nodep->replaceWith(newp); pushDeletep(nodep); nodep=NULL; + } else { + nodep->v3error("Illegal bit select; can't bit extract from arrayed dimension: "<prettyName()); + } + } else if (AstBasicDType* adtypep = ddtypep->castBasicDType()) { if (adtypep) {} // Unused if (varp->basicp()->rangep() && varp->basicp()->rangep()->littleEndian()) { // Below code assumes big bit endian; just works out if we swap diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 867385f4d..4cfd18e3a 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -74,6 +74,7 @@ #include "V3Premit.h" #include "V3Scope.h" #include "V3Signed.h" +#include "V3Slice.h" #include "V3Split.h" #include "V3SplitAs.h" #include "V3Stats.h" @@ -280,6 +281,10 @@ void process () { V3Unroll::unrollAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("unroll.tree")); + // Expand slices of arrays + V3Slice::sliceAll(v3Global.rootp()); + v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("slices.tree")); + // Convert case statements to if() blocks. Must be after V3Unknown V3Case::caseAll(v3Global.rootp()); v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("case.tree")); diff --git a/test_regress/t/t_mem_multi_ref_bad.pl b/test_regress/t/t_mem_multi_ref_bad.pl index 1e005cf1a..d3bf5be21 100755 --- a/test_regress/t/t_mem_multi_ref_bad.pl +++ b/test_regress/t/t_mem_multi_ref_bad.pl @@ -12,15 +12,16 @@ compile ( nc=>0, # Need to get it not to give the prompt expect=> q{%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension: dimn +.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal range select; variable already selected, or bad dimension .*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension: dim0 +.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension .*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension: dim1 -.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; can't bit extract from arrayed dimension: dim2 -.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; can't bit extract from arrayed dimension: dim2 -.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit select; can't bit extract from arrayed dimension: dim0nv +.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension +.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal \+: or -: select; variable already selected, or bad dimension .*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension: dim0nv +.*%Error: t/t_mem_multi_ref_bad.v:\d+: Illegal bit or array select; variable already selected, or bad dimension .*%Error: Exiting due to.*}, ); ok(1); 1; - diff --git a/test_regress/t/t_mem_packed_assign.pl b/test_regress/t/t_mem_packed_assign.pl new file mode 100755 index 000000000..636c720ab --- /dev/null +++ b/test_regress/t/t_mem_packed_assign.pl @@ -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 2010 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 ( + v_flags => ["--lint-only"], + fails=>1, + expect=> +'%Error: t/t_mem_packed_assign.v:\d+: Unsupported: Assignment between packed arrays of different dimensions +%Error: t/t_mem_packed_assign.v:\d+: Unsupported: Assignment between packed arrays of different dimensions +%Error: Exiting due to.*', + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_packed_assign.v b/test_regress/t/t_mem_packed_assign.v new file mode 100644 index 000000000..dfec487d5 --- /dev/null +++ b/test_regress/t/t_mem_packed_assign.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2009 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + /* verilator lint_off WIDTH */ + + input clk; + + integer cyc; initial cyc = 0; + logic [31:0] arr_c; initial arr_c = 0; + logic [7:0] [3:0] arr; + + logic [31:0] arr2_c; initial arr2_c = 0; + logic [7:0] [3:0] arr2; + assign arr2_c = arr2; + + always @ (posedge clk) begin + cyc <= cyc + 1; + arr_c <= arr_c + 1; + arr2 <= arr2 + 1; + $write("cyc%0d c:%0x a0:%0x a1:%0x a2:%0x a3:%0x\n", cyc, arr_c, arr[0], arr[1], arr[2], arr[3]); + if (cyc==99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + /* verilator lint_on WIDTH */ + +endmodule diff --git a/test_regress/t/t_mem_slice.pl b/test_regress/t/t_mem_slice.pl new file mode 100755 index 000000000..639bb98f5 --- /dev/null +++ b/test_regress/t/t_mem_slice.pl @@ -0,0 +1,19 @@ +#!/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 ( + v_flags => [], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_mem_slice.v b/test_regress/t/t_mem_slice.v new file mode 100644 index 000000000..a87d6a0b5 --- /dev/null +++ b/test_regress/t/t_mem_slice.v @@ -0,0 +1,117 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2009 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + logic use_AnB; + logic [1:0] active_command [8:0]; + logic [1:0] command_A [8:0]; + logic [1:0] command_B [8:0]; + + logic [1:0] active_command2 [8:0]; + logic [1:0] command_A2 [8:0]; + logic [1:0] command_B2 [8:0]; + + logic [1:0] active_command3 [1:0][2:0][3:0]; + logic [1:0] command_A3 [1:0][2:0][3:0]; + logic [1:0] command_B3 [1:0][2:0][3:0]; + + logic [8:0] pipe1 [7:0]; + logic [8:0] pipe1_input; + + integer cyc; + + assign active_command[8:0] = (use_AnB) ? command_A[8:0] : command_B[8:0]; + assign active_command2 = (use_AnB) ? command_A2 : command_B2; + assign active_command3[1:0][2:0][3:0] = (use_AnB) ? command_A3[1:0][2:0][3:0] : command_B3[1:0][2:0][3:0]; + + always @ (posedge clk) begin + pipe1_input <= pipe1_input + 1; + pipe1[0] <= pipe1_input; + pipe1[7:1] <= pipe1[6:0]; + end + + logic [3:0][13:0] iq_read_data [15:0]; + logic [3:0][13:0] iq_data; + logic [3:0] sel; + + assign iq_data = iq_read_data[sel]; + + always @ (posedge clk) begin + sel = sel + 1; + end + + initial begin + cyc = 0; + use_AnB = 0; + for (int i = 0; i < 7; ++i) begin + command_A[i] = 2'b00; + command_B[i] = 2'b11; + command_A2[i] = 2'b00; + command_B2[i] = 2'b11; + pipe1_input = 9'b0; + end + for (int i = 0; i < 2; ++i) begin + for (int j = 0; j < 3; ++j) begin + for (int k = 0; k < 4; ++k) begin + command_A3[i][j][k] = 2'b00; + command_B3[i][j][k] = 2'b11; + end + end + end + end + + always @ (posedge clk) begin + use_AnB <= ~use_AnB; + cyc <= cyc + 1; + if (use_AnB) begin + if (active_command[3] != 2'b00) begin + $stop; + end + if (active_command2[3] != 2'b00) begin + $stop; + end + if (active_command3[0][1][2] != 2'b00) begin + $stop; + end + end + if (!use_AnB) begin + if (active_command[3] != 2'b11) begin + $stop; + end + if (active_command2[3] != 2'b11) begin + $stop; + end + if (active_command3[3][1][2] != 2'b11) begin + $stop; + end + end + end + + logic [8:0] last_pipe; + always @(posedge clk) begin + if (cyc < 3) begin + last_pipe <= pipe1[0]; + end + else begin + if (last_pipe + 1 != pipe1[0]) begin + $stop; + end + else begin + last_pipe <= pipe1[0]; + end + end + if (cyc > 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule : t diff --git a/test_regress/t/t_mem_slice_bad.pl b/test_regress/t/t_mem_slice_bad.pl new file mode 100755 index 000000000..b1f10867e --- /dev/null +++ b/test_regress/t/t_mem_slice_bad.pl @@ -0,0 +1,25 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 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 ( + 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+: 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: Exiting due to.*', + ) if $Self->{v3}; + +ok(1); +1; diff --git a/test_regress/t/t_mem_slice_bad.v b/test_regress/t/t_mem_slice_bad.v new file mode 100644 index 000000000..f14bc22e1 --- /dev/null +++ b/test_regress/t/t_mem_slice_bad.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2009 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + logic use_AnB; + + logic [1:0] active_command [8:0]; + logic [1:0] command_A [8:0]; + logic [1:0] command_B [8:0]; + + logic [1:0] active_command2 [8:0]; + logic [1:0] command_A2 [7:0]; + logic [1:0] command_B2 [8:0]; + + logic [1:0] active_command3 [1:0][2:0][3:0]; + logic [1:0] command_A3 [1:0][2:0][3:0]; + logic [1:0] command_B3 [1:0][2:0][3:0]; + + logic [1:0] active_command4 [8:0]; + logic [1:0] command_A4 [7:0]; + + logic [1:0] active_command5 [8:0]; + logic [1:0] command_A5 [7:0]; + + // Single dimension assign + assign active_command[3:0] = (use_AnB) ? command_A[7:0] : command_B[7:0]; + // Assignment of entire arrays + assign active_command2 = (use_AnB) ? command_A2 : command_B2; + // Multi-dimension assign + assign active_command3[1:0][2:0][3:0] = (use_AnB) ? command_A3[1:0][2:0][3:0] : command_B3[1:0][1:0][3:0]; + + // Supported: Delayed assigment with RHS Var == LHS Var + logic [7:0] arrd [7:0]; + always_ff @(posedge clk) arrd[7:4] <= arrd[3:0]; + + // Unsupported: Non-delayed assigment with RHS Var == LHS Var + logic [7:0] arr [7:0]; + assign arr[7:4] = arr[3:0]; + + // Delayed assign + always @(posedge clk) begin + active_command4[7:0] <= command_A4[8:0]; + end + + // Combinational assign + always_comb begin + active_command5[8:0] = command_A5[7:0]; + end + +endmodule : t