diff --git a/Changes b/Changes index fa279f2d5..fd82726a6 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,7 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.856 devel +*** Support case inside, bug708. [Jan Egil Ruud] * Verilator 3.855 2014-01-18 diff --git a/src/V3Ast.h b/src/V3Ast.h index d796062ac..bc946cf72 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -496,22 +496,23 @@ public: //###################################################################### -class AstCaseType { +class VCaseType { public: enum en { CT_CASE, CT_CASEX, - CT_CASEZ + CT_CASEZ, + CT_CASEINSIDE }; enum en m_e; - inline AstCaseType () : m_e(CT_CASE) {} - inline AstCaseType (en _e) : m_e(_e) {} - explicit inline AstCaseType (int _e) : m_e(static_cast(_e)) {} + inline VCaseType () : m_e(CT_CASE) {} + inline VCaseType (en _e) : m_e(_e) {} + explicit inline VCaseType (int _e) : m_e(static_cast(_e)) {} operator en () const { return m_e; } }; - inline bool operator== (AstCaseType lhs, AstCaseType rhs) { return (lhs.m_e == rhs.m_e); } - inline bool operator== (AstCaseType lhs, AstCaseType::en rhs) { return (lhs.m_e == rhs); } - inline bool operator== (AstCaseType::en lhs, AstCaseType rhs) { return (lhs == rhs.m_e); } + inline bool operator== (VCaseType lhs, VCaseType rhs) { return (lhs.m_e == rhs.m_e); } + inline bool operator== (VCaseType lhs, VCaseType::en rhs) { return (lhs.m_e == rhs); } + inline bool operator== (VCaseType::en lhs, VCaseType rhs) { return (lhs == rhs.m_e); } //###################################################################### diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index ac3ec930b..4749c7246 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -112,6 +112,35 @@ int AstNodeClassDType::widthAlignBytes() const { else return 8; } +AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { + if (lhsp->isDouble() && rhsp->isDouble()) { + return new AstEqD(fl, lhsp, rhsp); + } else { + return new AstEq(fl, lhsp, rhsp); + } +} + +AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { + if (lhsp->isDouble() && rhsp->isDouble()) { + return new AstGteD(fl, lhsp, rhsp); + } else if (lhsp->isSigned() && rhsp->isSigned()) { + return new AstGteS(fl, lhsp, rhsp); + } else { + return new AstGte(fl, lhsp, rhsp); + } +} + +AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) { + if (lhsp->isDouble() && rhsp->isDouble()) { + return new AstLteD(fl, lhsp, rhsp); + } else if (lhsp->isSigned() && rhsp->isSigned()) { + return new AstLteS(fl, lhsp, rhsp); + } else { + return new AstLte(fl, lhsp, rhsp); + } +} + + bool AstVar::isSigPublic() const { return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar())); } diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index b6552fd77..ccf906dfa 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -2053,14 +2053,14 @@ struct AstCase : public AstNodeCase { // exprp Children: MATHs // casesp Children: CASEITEMs private: - AstCaseType m_casex; // 0=case, 1=casex, 2=casez + VCaseType m_casex; // 0=case, 1=casex, 2=casez bool m_fullPragma; // Synthesis full_case bool m_parallelPragma; // Synthesis parallel_case bool m_uniquePragma; // unique case bool m_unique0Pragma; // unique0 case bool m_priorityPragma; // priority case public: - AstCase(FileLine* fileline, AstCaseType casex, AstNode* exprp, AstNode* casesp) + AstCase(FileLine* fileline, VCaseType casex, AstNode* exprp, AstNode* casesp) : AstNodeCase(fileline, exprp, casesp) { m_casex=casex; m_fullPragma=false; m_parallelPragma=false; @@ -2070,8 +2070,11 @@ public: virtual string verilogKwd() const { return casez()?"casez":casex()?"casex":"case"; } virtual bool same(AstNode* samep) const { return m_casex==samep->castCase()->m_casex; } - bool casex() const { return m_casex==AstCaseType::CT_CASEX; } - bool casez() const { return m_casex==AstCaseType::CT_CASEZ; } + bool casex() const { return m_casex==VCaseType::CT_CASEX; } + bool casez() const { return m_casex==VCaseType::CT_CASEZ; } + bool caseInside() const { return m_casex==VCaseType::CT_CASEINSIDE; } + bool caseSimple() const { return m_casex==VCaseType::CT_CASE; } + void caseInsideSet() { m_casex=VCaseType::CT_CASEINSIDE; } bool fullPragma() const { return m_fullPragma; } void fullPragma(bool flag) { m_fullPragma=flag; } bool parallelPragma() const { return m_parallelPragma; } @@ -3610,6 +3613,7 @@ struct AstEq : public AstNodeBiCom { AstEq(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) { dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Eq, EQ) + static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstEq/AstEqD virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEq(lhs,rhs); } virtual string emitVerilog() { return "%k(%l %f== %r)"; } virtual string emitC() { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; } @@ -3740,6 +3744,7 @@ struct AstGte : public AstNodeBiop { AstGte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Gte, GTE) + static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstGte/AstGteS/AstGteD virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGte(lhs,rhs); } virtual string emitVerilog() { return "%k(%l %f>= %r)"; } virtual string emitC() { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; } @@ -3779,6 +3784,7 @@ struct AstLte : public AstNodeBiop { AstLte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { dtypeSetLogicBool(); } ASTNODE_NODE_FUNCS(Lte, LTE) + static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstLte/AstLteS/AstLteD virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLte(lhs,rhs); } virtual string emitVerilog() { return "%k(%l %f<= %r)"; } virtual string emitC() { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; } diff --git a/src/V3Case.cpp b/src/V3Case.cpp index 582f377e3..111dbe551 100644 --- a/src/V3Case.cpp +++ b/src/V3Case.cpp @@ -97,7 +97,8 @@ private: nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')"); } else if (m_caseExprp->castCase() && m_caseExprp->castCase()->casex()) { // Don't sweat it, we already complained about casex in general - } else if (m_caseExprp->castCase() && m_caseExprp->castCase()->casez()) { + } else if (m_caseExprp->castCase() && (m_caseExprp->castCase()->casez() + || m_caseExprp->castCase()->caseInside())) { if (nodep->num().isUnknown()) { nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)"); } @@ -322,36 +323,42 @@ private: icondNextp = icondp->nextp(); icondp->unlinkFrBack(); - AstNode* and1p; - AstNode* and2p; + AstNode* condp = NULL; // Default is to use and1p/and2p AstConst* iconstp = icondp->castConst(); if (iconstp && neverItem(nodep, iconstp)) { // X in casez can't ever be executed icondp->deleteTree(); icondp=NULL; iconstp=NULL; // For simplicity, make expression that is not equal, and let later // optimizations remove it - and1p = new AstConst(itemp->fileline(), AstConst::LogicFalse()); - and2p = new AstConst(itemp->fileline(), AstConst::LogicTrue()); + condp = new AstConst(itemp->fileline(), AstConst::LogicFalse()); + } else if (AstInsideRange* irangep = icondp->castInsideRange()) { + // Similar logic in V3Width::visit(AstInside) + AstNode* ap = AstGte::newTyped(itemp->fileline(), + cexprp->cloneTree(false), + irangep->lhsp()->unlinkFrBack()); + AstNode* bp = AstLte::newTyped(itemp->fileline(), + cexprp->cloneTree(false), + irangep->rhsp()->unlinkFrBack()); + condp = new AstAnd(itemp->fileline(), ap, bp); } else if (iconstp && iconstp->num().isFourState() - && (nodep->casex() || nodep->casez())) { + && (nodep->casex() || nodep->casez() || nodep->caseInside())) { V3Number nummask (itemp->fileline(), iconstp->width()); nummask.opBitsNonX(iconstp->num()); V3Number numval (itemp->fileline(), iconstp->width()); numval.opBitsOne(iconstp->num()); - and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false), - new AstConst(itemp->fileline(), nummask)); - and2p = new AstAnd(itemp->fileline(), - new AstConst(itemp->fileline(), numval), - new AstConst(itemp->fileline(), nummask)); + AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false), + new AstConst(itemp->fileline(), nummask)); + AstNode* and2p = new AstAnd(itemp->fileline(), + new AstConst(itemp->fileline(), numval), + new AstConst(itemp->fileline(), nummask)); icondp->deleteTree(); icondp=NULL; iconstp=NULL; + condp = AstEq::newTyped(itemp->fileline(), and1p, and2p); } else { // Not a caseX mask, we can simply build CASEEQ(cexpr icond) - and1p = cexprp->cloneTree(false); - and2p = icondp; + AstNode* and1p = cexprp->cloneTree(false); + AstNode* and2p = icondp; + condp = AstEq::newTyped(itemp->fileline(), and1p, and2p); } - AstNodeBiop* condp = (and1p->isDouble() - ? (new AstEqD(itemp->fileline(), and1p, and2p))->castNodeBiop() - : (new AstEq(itemp->fileline(), and1p, and2p))->castNodeBiop()); if (!ifexprp) { ifexprp = condp; } else { @@ -435,7 +442,7 @@ private: bool neverItem(AstCase* casep, AstConst* itemp) { // Xs in case or casez are impossible due to two state simulations if (casep->casex()) { - } else if (casep->casez()) { + } else if (casep->casez() || casep->caseInside()) { if (itemp->num().isUnknown()) return true; } else { if (itemp->num().isFourState()) return true; diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 8b33fa4d6..adef465d8 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -504,7 +504,7 @@ private: UINFO(6," -to "<user2p(connectRefp); // Public output inside the cell must go via an assign rather than alias - // Else the public logic will set the alias, loosing the value to be propagated up + // Else the public logic will set the alias, losing the value to be propagated up // (InOnly isn't a problem as the AssignAlias will create the assignment for us) pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly()); } diff --git a/src/V3Unknown.cpp b/src/V3Unknown.cpp index d00cd36c2..49033f7f4 100644 --- a/src/V3Unknown.cpp +++ b/src/V3Unknown.cpp @@ -163,13 +163,13 @@ private: m_assignwp = NULL; } virtual void visit(AstCaseItem* nodep, AstNUser*) { - m_constXCvt = false; // Avoid loosing the X's in casex + m_constXCvt = false; // Avoid losing the X's in casex nodep->condsp()->iterateAndNext(*this); m_constXCvt = true; nodep->bodysp()->iterateAndNext(*this); } virtual void visit(AstNodeDType* nodep, AstNUser*) { - m_constXCvt = false; // Avoid loosing the X's in casex + m_constXCvt = false; // Avoid losing the X's in casex nodep->iterateChildren(*this); m_constXCvt = true; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 03f500f07..7d6a801ba 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1050,6 +1050,7 @@ private: nextip = itemp->nextp(); // Will be unlinking AstNode* inewp; if (AstInsideRange* irangep = itemp->castInsideRange()) { + // Similar logic in V3Case inewp = new AstAnd(itemp->fileline(), new AstGte(itemp->fileline(), nodep->exprp()->cloneTree(true), diff --git a/src/verilog.y b/src/verilog.y index 6d2bc5d03..91da357b2 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2190,7 +2190,12 @@ statement_item: // IEEE: statement_item if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true); if ($1 == uniq_PRIORITY) $2->priorityPragma(true); } //UNSUP caseStart caseAttrE yMATCHES case_patternListE yENDCASE { } - //UNSUP caseStart caseAttrE yINSIDE case_insideListE yENDCASE { } + | unique_priorityE caseStart caseAttrE yINSIDE case_insideListE yENDCASE { $$ = $2; if ($5) $2->addItemsp($5); + if (!$2->caseSimple()) $2->v3error("Illegal to have inside on a casex/casez"); + $2->caseInsideSet(); + if ($1 == uniq_UNIQUE) $2->uniquePragma(true); + if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true); + if ($1 == uniq_PRIORITY) $2->priorityPragma(true); } // // // IEEE: conditional_statement | unique_priorityE yIF '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE @@ -2328,9 +2333,9 @@ unique_priorityE: // IEEE: unique_priority + empty ; caseStart: // IEEE: part of case_statement - yCASE '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASE,$3,NULL); } - | yCASEX '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASEX,$3,NULL); } - | yCASEZ '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASEZ,$3,NULL); } + yCASE '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASE,$3,NULL); } + | yCASEX '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASEX,$3,NULL); } + | yCASEZ '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASEZ,$3,NULL); } ; caseAttrE: @@ -2344,6 +2349,11 @@ case_itemListE: // IEEE: [ { case_item } ] | case_itemList { $$ = $1; } ; +case_insideListE: // IEEE: [ { case_inside_item } ] + /* empty */ { $$ = NULL; } + | case_inside_itemList { $$ = $1; } + ; + case_itemList: // IEEE: { case_item + ... } caseCondList ':' stmtBlock { $$ = new AstCaseItem($2,$1,$3); } | yDEFAULT ':' stmtBlock { $$ = new AstCaseItem($2,NULL,$3); } @@ -2353,6 +2363,15 @@ case_itemList: // IEEE: { case_item + ... } | case_itemList yDEFAULT ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,NULL,$4)); } ; +case_inside_itemList: // IEEE: { case_inside_item + open_range_list ... } + open_range_list ':' stmtBlock { $$ = new AstCaseItem($2,$1,$3); } + | yDEFAULT ':' stmtBlock { $$ = new AstCaseItem($2,NULL,$3); } + | yDEFAULT stmtBlock { $$ = new AstCaseItem($1,NULL,$2); } + | case_inside_itemList open_range_list ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,$2,$4)); } + | case_inside_itemList yDEFAULT stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($2,NULL,$3)); } + | case_inside_itemList yDEFAULT ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,NULL,$4)); } + ; + open_range_list: // ==IEEE: open_range_list + open_value_range open_value_range { $$ = $1; } | open_range_list ',' open_value_range { $$ = $1;$1->addNext($3); } diff --git a/test_regress/t/t_case_inside.pl b/test_regress/t/t_case_inside.pl new file mode 100755 index 000000000..f91289753 --- /dev/null +++ b/test_regress/t/t_case_inside.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_case_inside.v b/test_regress/t/t_case_inside.v new file mode 100644 index 000000000..20aac26aa --- /dev/null +++ b/test_regress/t/t_case_inside.v @@ -0,0 +1,66 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2014 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; initial cyc=0; + reg [63:0] crc; + reg [63:0] sum; + + reg out1; + reg [4:0] out2; + sub sub (.in(crc[23:0]), .out1(out1), .out2(out2)); + + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x sum=%x in[3:0]=%x out=%x,%x\n",$time, cyc, crc, sum, crc[3:0], out1,out2); +`endif + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + sum <= {sum[62:0], sum[63]^sum[2]^sum[0]} ^ {58'h0,out1,out2}; + if (cyc==0) begin + // Setup + crc <= 64'h00000000_00000097; + sum <= 64'h0; + end + else if (cyc==99) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum); +`define EXPECTED_SUM 64'h10204fa5567c8a4b + if (sum !== `EXPECTED_SUM) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module sub (/*AUTOARG*/ + // Outputs + out1, out2, + // Inputs + in + ); + + input [23:0] in; + output reg out1; + output reg [4:0] out2; + + always @* begin + case (in[3:0]) inside + default: {out1,out2} = {1'b0,5'h0F}; // Note not last item + 4'h1, 4'h2, 4'h3: {out1,out2} = {1'b1,5'h01}; + 4'h4: {out1,out2} = {1'b1,5'h04}; + [4'h6:4'h5]: {out1,out2} = {1'b1,5'h05}; // order backwards, will not match + 4'b100?:/*8,9*/ {out1,out2} = {1'b1,5'h08}; + [4'hc:4'hf]: {out1,out2} = {1'b1,5'h0C}; + endcase + end + +endmodule