From 28cbf399953992855833af32da2e7e6deeb80b2c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 9 Nov 2019 18:31:24 -0500 Subject: [PATCH] Support some unpacked arrays in parameters, bug1315. --- Changes | 2 + src/V3AstNodes.h | 2 +- src/V3Const.cpp | 10 +- src/V3Simulate.h | 203 +++++++++++++++++++++++-------- test_regress/t/t_param_array3.pl | 2 - test_regress/t/t_param_array3.v | 4 +- test_regress/t/t_param_array4.pl | 20 +++ test_regress/t/t_param_array4.v | 41 +++++++ test_regress/t/t_param_array5.pl | 20 +++ test_regress/t/t_param_array5.v | 38 ++++++ 10 files changed, 282 insertions(+), 60 deletions(-) create mode 100755 test_regress/t/t_param_array4.pl create mode 100644 test_regress/t/t_param_array4.v create mode 100755 test_regress/t/t_param_array5.pl create mode 100644 test_regress/t/t_param_array5.v diff --git a/Changes b/Changes index 2886131a2..b13aa6b33 100644 --- a/Changes +++ b/Changes @@ -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] diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 2d548fbf2..af9b25451 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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(); } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index cc8bb79ea..bce61826a 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -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->"<replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); } } diff --git a/src/V3Simulate.h b/src/V3Simulate.h index c5ad1006c..252e8a3ab 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -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 m_stringValuesp; // List of allocated string numbers + std::deque 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(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(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 "<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 "<name()<<" on "<user3p((void*)valuep); } void setOutValue(AstNode* nodep, const AstNode* valuep) { + UASSERT_OBJ(valuep, nodep, "Simulate setting null value"); UINFO(9, " set oval "<name()<<" on "<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["<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::iterator it = m_stringValuesp.begin(); - it != m_stringValuesp.end(); ++it) { + for (std::deque::iterator it = m_reclaimValuesp.begin(); + it != m_reclaimValuesp.end(); ++it) { delete (*it); } - m_stringValuesp.clear(); + m_reclaimValuesp.clear(); m_constFreeps.clear(); m_constAllps.clear(); } diff --git a/test_regress/t/t_param_array3.pl b/test_regress/t/t_param_array3.pl index 10b095703..89a4e77d9 100755 --- a/test_regress/t/t_param_array3.pl +++ b/test_regress/t/t_param_array3.pl @@ -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( diff --git a/test_regress/t/t_param_array3.v b/test_regress/t/t_param_array3.v index 3a3c9f5fa..72cd4afac 100644 --- a/test_regress/t/t_param_array3.v +++ b/test_regress/t/t_param_array3.v @@ -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; diff --git a/test_regress/t/t_param_array4.pl b/test_regress/t/t_param_array4.pl new file mode 100755 index 000000000..89a4e77d9 --- /dev/null +++ b/test_regress/t/t_param_array4.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 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; diff --git a/test_regress/t/t_param_array4.v b/test_regress/t/t_param_array4.v new file mode 100644 index 000000000..e8f3a1d36 --- /dev/null +++ b/test_regress/t/t_param_array4.v @@ -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 diff --git a/test_regress/t/t_param_array5.pl b/test_regress/t/t_param_array5.pl new file mode 100755 index 000000000..89a4e77d9 --- /dev/null +++ b/test_regress/t/t_param_array5.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 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; diff --git a/test_regress/t/t_param_array5.v b/test_regress/t/t_param_array5.v new file mode 100644 index 000000000..cd3dfe4c7 --- /dev/null +++ b/test_regress/t/t_param_array5.v @@ -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