From 5064ec2806334d8d12aa1c071b3273a2bb13b405 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 12 Feb 2023 20:09:10 -0500 Subject: [PATCH] Support type case and type equality comparisons. --- Changes | 1 + src/V3Ast.h | 3 +- src/V3AstNodeExpr.h | 48 +++++++++++++++ src/V3ParseImp.cpp | 39 ++++++++++++ src/V3ParseImp.h | 1 + src/V3Width.cpp | 88 ++++++++++++++++++++++++--- src/verilog.l | 2 +- src/verilog.y | 54 +++++++++++----- test_regress/t/t_type_compare.pl | 21 +++++++ test_regress/t/t_type_compare.v | 68 +++++++++++++++++++++ test_regress/t/t_type_compare_bad.out | 5 ++ test_regress/t/t_type_compare_bad.pl | 19 ++++++ test_regress/t/t_type_compare_bad.v | 20 ++++++ 13 files changed, 344 insertions(+), 25 deletions(-) create mode 100755 test_regress/t/t_type_compare.pl create mode 100644 test_regress/t/t_type_compare.v create mode 100644 test_regress/t/t_type_compare_bad.out create mode 100755 test_regress/t/t_type_compare_bad.pl create mode 100644 test_regress/t/t_type_compare_bad.v diff --git a/Changes b/Changes index e0bd5e8ed..b6cdd4b08 100644 --- a/Changes +++ b/Changes @@ -60,6 +60,7 @@ Verilator 5.006 2023-01-22 * Support property calls without parenthesis (#3879) (#3893). [Ryszard Rozak, Antmicro Ltd] * Support import/export lists in modport (#3886). [Gökçe Aydos] * Support class queue equality (#3895). [Ilya Barkov] +* Support type case and type equality comparisons. * Add IMPLICITSTATIC warning when a ftask/function is implicitly static (#3839). [Ryszard Rozak, Antmicro Ltd] * Add VL_VALUE_STRING_MAX_WORDS override (#3869). [Andrew Nolte] * Optimize expansion of extend operators. diff --git a/src/V3Ast.h b/src/V3Ast.h index 63aa59ea8..ffdc57ce4 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -379,6 +379,7 @@ public: ENUM_NAME, // V3Width processes ENUM_VALID, // V3Width processes // + TYPEID, // V3Width processes TYPENAME, // V3Width processes // VAR_BASE, // V3LinkResolve creates for AstPreSel, V3LinkParam removes @@ -406,7 +407,7 @@ public: "DT_PUBLIC", "ENUM_FIRST", "ENUM_LAST", "ENUM_NUM", "ENUM_NEXT", "ENUM_PREV", "ENUM_NAME", "ENUM_VALID", - "TYPENAME", + "TYPEID", "TYPENAME", "VAR_BASE", "VAR_CLOCK_ENABLE", "VAR_FORCEABLE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW", "VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT", "VAR_CLOCKER", diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 5b68e2914..e2217ca1d 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -3352,6 +3352,30 @@ public: int instrCount() const override { return INSTR_COUNT_STR; } bool stringFlavor() const override { return true; } }; +class AstEqT final : public AstNodeBiCom { + // Equal (==) for data types +public: + AstEqT(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) + : ASTGEN_SUPER_EqT(fl, lhsp, rhsp) { + dtypeSetBit(); + } + ASTGEN_MEMBERS_AstEqT; + AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + return new AstEqT{fileline(), lhsp, rhsp}; + } + void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { + V3ERROR_NA; + } + string emitVerilog() override { return "%k(%l %f== %r)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { return "=="; } + bool cleanOut() const override { return true; } + bool cleanLhs() const override { return false; } + bool cleanRhs() const override { return false; } + bool sizeMattersLhs() const override { return false; } + bool sizeMattersRhs() const override { return false; } + int instrCount() const override { return INSTR_COUNT_STR; } +}; class AstLogEq final : public AstNodeBiCom { public: AstLogEq(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) @@ -3467,6 +3491,30 @@ public: int instrCount() const override { return INSTR_COUNT_STR; } bool stringFlavor() const override { return true; } }; +class AstNeqT final : public AstNodeBiCom { + // Not-equal (!=) for data types +public: + AstNeqT(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp) + : ASTGEN_SUPER_NeqT(fl, lhsp, rhsp) { + dtypeSetBit(); + } + ASTGEN_MEMBERS_AstNeqT; + AstNodeExpr* cloneType(AstNodeExpr* lhsp, AstNodeExpr* rhsp) override { + return new AstNeqT{fileline(), lhsp, rhsp}; + } + void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { + V3ERROR_NA; + } + string emitVerilog() override { return "%k(%l %f!= %r)"; } + string emitC() override { V3ERROR_NA_RETURN(""); } + string emitSimpleOperator() override { return "!="; } + bool cleanOut() const override { return true; } + bool cleanLhs() const override { return false; } + bool cleanRhs() const override { return false; } + bool sizeMattersLhs() const override { return false; } + bool sizeMattersRhs() const override { return false; } + int instrCount() const override { return INSTR_COUNT_STR; } +}; // === AstNodeBiComAsv === class AstAdd final : public AstNodeBiComAsv { diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index 1f264a38a..b310beb96 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -374,6 +374,32 @@ size_t V3ParseImp::tokenPipeScanParam(size_t depth) { return depth; } +size_t V3ParseImp::tokenPipeScanType(size_t depth) { + // Search around IEEE type_reference to see if is expression + // Return location of following token, or input if not found + // yTYPE__ETC '(' ... ')' ['==' '===' '!=' '!==='] + if (tokenPeekp(depth)->token != '(') return depth; + depth += 1; // Past the ( + int parens = 1; // Count first ( + while (true) { + const int tok = tokenPeekp(depth)->token; + if (tok == 0) { + UINFO(9, "tokenPipeScanType hit EOF; probably syntax error to come"); + break; + } else if (tok == '(') { + ++parens; + } else if (tok == ')') { + --parens; + if (parens == 0) { + ++depth; + break; + } + } + ++depth; + } + return depth; +} + void V3ParseImp::tokenPipeline() { // called from bison's "yylex", has a "this" if (m_tokensAhead.empty()) tokenPull(); // corrupts yylval @@ -388,6 +414,7 @@ void V3ParseImp::tokenPipeline() { || token == yLOCAL__LEX // || token == yNEW__LEX // || token == ySTATIC__LEX // + || token == yTYPE__LEX // || token == yVIRTUAL__LEX // || token == yWITH__LEX // || token == yaID__LEX // @@ -443,6 +470,18 @@ void V3ParseImp::tokenPipeline() { } else { token = ySTATIC__ETC; } + } else if (token == yTYPE__LEX) { + VL_RESTORER(yylval); // Remember value, as about to read ahead + const size_t depth = tokenPipeScanType(0); + const int postToken = tokenPeekp(depth)->token; + if ( // v-- token v-- postToken + // yTYPE__EQ '(' .... ')' EQ_OPERATOR yTYPE_ETC '(' ... ')' + postToken == yP_EQUAL || postToken == yP_NOTEQUAL || postToken == yP_CASEEQUAL + || postToken == yP_CASENOTEQUAL) { + token = yTYPE__EQ; + } else { + token = yTYPE__ETC; + } } else if (token == yVIRTUAL__LEX) { if (nexttok == yCLASS) { token = yVIRTUAL__CLASS; diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index 14db28acc..e1037b130 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -303,6 +303,7 @@ private: void tokenPipeline(); // Internal; called from tokenToBison void tokenPipelineSym(); size_t tokenPipeScanParam(size_t depth); + size_t tokenPipeScanType(size_t depth); const V3ParseBisonYYSType* tokenPeekp(size_t depth); void preprocDumps(std::ostream& os); }; diff --git a/src/V3Width.cpp b/src/V3Width.cpp index fb41c5cd4..4bed49e02 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -106,6 +106,7 @@ std::ostream& operator<<(std::ostream& str, const Determ& rhs) { enum Castable : uint8_t { UNSUPPORTED, + SAMEISH, COMPATIBLE, ENUM_EXPLICIT, ENUM_IMPLICIT, @@ -114,7 +115,7 @@ enum Castable : uint8_t { }; std::ostream& operator<<(std::ostream& str, const Castable& rhs) { static const char* const s_det[] - = {"UNSUP", "COMPAT", "ENUM_EXP", "ENUM_IMP", "DYN_CLS", "INCOMPAT"}; + = {"UNSUP", "IDENT", "COMPAT", "ENUM_EXP", "ENUM_IMP", "DYN_CLS", "INCOMPAT"}; return str << s_det[rhs]; } @@ -314,6 +315,9 @@ private: void visit(AstLteN* nodep) override { visit_cmp_string(nodep); } void visit(AstGtN* nodep) override { visit_cmp_string(nodep); } void visit(AstGteN* nodep) override { visit_cmp_string(nodep); } + // ... Data type compares + void visit(AstEqT* nodep) override { visit_cmp_type(nodep); } + void visit(AstNeqT* nodep) override { visit_cmp_type(nodep); } // Widths: out width = lhs width = rhs width // Signed: Output signed iff LHS & RHS signed. @@ -1569,6 +1573,10 @@ private: VL_DO_DANGLING(nodep->deleteTree(), nodep); break; } + case VAttrType::TYPEID: + // Soon to be handled in AstEqT + nodep->dtypeSetSigned32(); + break; default: { // Everything else resolved earlier nodep->dtypeSetLogicUnsized(32, 1, VSigning::UNSIGNED); // Approximation, unsized 32 @@ -1841,7 +1849,7 @@ private: nodep->fromp()->unlinkFrBack()}, new AstConst{fl, AstConst::Signed32{}, 1}}, new AstConst{fl, AstConst::Signed32{}, 0}}; - } else if (castable == COMPATIBLE) { + } else if (castable == SAMEISH || castable == COMPATIBLE) { nodep->v3warn(CASTCONST, "$cast will always return one as " << toDtp->prettyDTypeNameQ() << " is always castable from " @@ -1904,7 +1912,7 @@ private: << toDtp->prettyDTypeNameQ() << " from " << fromDtp->prettyDTypeNameQ()); bad = true; - } else if (castable == COMPATIBLE || castable == ENUM_IMPLICIT + } else if (castable == SAMEISH || castable == COMPATIBLE || castable == ENUM_IMPLICIT || castable == ENUM_EXPLICIT) { ; // Continue } else if (castable == DYNAMIC_CLASS) { @@ -4246,6 +4254,45 @@ private: } } + // Deal with case(type(data_type)) + if (AstAttrOf* const exprap = VN_CAST(nodep->exprp(), AttrOf)) { + if (exprap->attrType() == VAttrType::TYPEID) { + AstNodeDType* const exprDtp = VN_AS(exprap->fromp(), NodeDType); + UINFO(9, "case type exprDtp " << exprDtp << endl); + // V3Param may have a pointer to this case statement, and we need + // dotted references to remain properly named, so rather than + // removing we convert it to a "normal" expression "case (1) ..." + FileLine* const newfl = nodep->fileline(); + newfl->warnOff(V3ErrorCode::CASEINCOMPLETE, true); // Side effect of transform + newfl->warnOff(V3ErrorCode::CASEOVERLAP, true); // Side effect of transform + nodep->fileline(newfl); + for (AstCaseItem* itemp = nodep->itemsp(); itemp; + itemp = VN_AS(itemp->nextp(), CaseItem)) { + if (!itemp->isDefault()) { + bool hit = false; + for (AstNode* condp = itemp->condsp(); condp; condp = condp->nextp()) { + const AstAttrOf* const condAttrp = VN_CAST(condp, AttrOf); + if (!condAttrp) { + condp->v3error( + "Case(type) statement requires items that have type() items"); + } else { + AstNodeDType* const condDtp = VN_AS(condAttrp->fromp(), NodeDType); + if (computeCastable(exprDtp, condDtp, nodep) == SAMEISH) { + hit = true; + break; + } + } + } + pushDeletep(itemp->condsp()->unlinkFrBackWithNext()); + // Item condition becomes constant 1 if hits else 0 + itemp->addCondsp(new AstConst{newfl, AstConst::BitTrue{}, hit}); + } + } + VL_DO_DANGLING(pushDeletep(exprap->unlinkFrBack()), exprap); + nodep->exprp(new AstConst{newfl, AstConst::BitTrue{}}); + } + } + // Take width as maximum across all items, if any is real whole thing is real AstNodeDType* subDTypep = nodep->exprp()->dtypep(); for (AstCaseItem* itemp = nodep->itemsp(); itemp; @@ -5729,6 +5776,32 @@ private: nodep->dtypeSetBit(); } } + void visit_cmp_type(AstNodeBiop* nodep) { + // CALLER: EqT, LtT + // Widths: 1 bit out + // Data type compare (not output) + if (m_vup->prelim()) { + userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); + userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p()); + const AstAttrOf* const lhsap = VN_AS(nodep->lhsp(), AttrOf); + const AstAttrOf* const rhsap = VN_AS(nodep->rhsp(), AttrOf); + UASSERT_OBJ(lhsap->attrType() == VAttrType::TYPEID, lhsap, + "Type compare expects type reference"); + UASSERT_OBJ(rhsap->attrType() == VAttrType::TYPEID, rhsap, + "Type compare expects type reference"); + AstNodeDType* const lhsDtp = VN_AS(lhsap->fromp(), NodeDType); + AstNodeDType* const rhsDtp = VN_AS(rhsap->fromp(), NodeDType); + UINFO(9, "==type lhsDtp " << lhsDtp << endl); + UINFO(9, "==type rhsDtp " << lhsDtp << endl); + const bool invert = VN_IS(nodep, NeqT); + const bool identical = computeCastable(lhsDtp, rhsDtp, nodep) == SAMEISH; + UINFO(9, "== " << identical << endl); + const bool eq = invert ^ identical; + AstNode* const newp = new AstConst{nodep->fileline(), AstConst::BitTrue{}, eq}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + } void visit_negate_not(AstNodeUniop* nodep, bool real_ok) { // CALLER: (real_ok=false) Not @@ -6346,8 +6419,8 @@ private: if (const AstEnumDType* const expEnump = VN_CAST(expDTypep->skipRefToEnump(), EnumDType)) { const auto castable = computeCastable(expEnump, underp->dtypep(), underp); - if (castable != COMPATIBLE && castable != ENUM_IMPLICIT && !VN_IS(underp, Cast) - && !VN_IS(underp, CastDynamic) && !m_enumItemp + if (castable != SAMEISH && castable != COMPATIBLE && castable != ENUM_IMPLICIT + && !VN_IS(underp, Cast) && !VN_IS(underp, CastDynamic) && !m_enumItemp && !nodep->fileline()->warnIsOff(V3ErrorCode::ENUMVALUE) && warnOn) { underp->v3warn(ENUMVALUE, "Implicit conversion to enum " @@ -7080,10 +7153,11 @@ private: const Castable castable = UNSUPPORTED; toDtp = toDtp->skipRefToEnump(); fromDtp = fromDtp->skipRefToEnump(); - if (toDtp == fromDtp) return COMPATIBLE; - + if (toDtp == fromDtp) return SAMEISH; + if (toDtp->similarDType(fromDtp)) return SAMEISH; // UNSUP unpacked struct/unions (treated like BasicDType) const AstNodeDType* fromBaseDtp = computeCastableBase(fromDtp); + const bool fromNumericable = VN_IS(fromBaseDtp, BasicDType) || VN_IS(fromBaseDtp, EnumDType) || VN_IS(fromBaseDtp, NodeUOrStructDType); diff --git a/src/verilog.l b/src/verilog.l index 4d469100f..8e5b8f91e 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -577,7 +577,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "throughout" { ERROR_RSVD_WORD("SystemVerilog 2005"); } "timeprecision" { FL; return yTIMEPRECISION; } "timeunit" { FL; return yTIMEUNIT; } - "type" { FL; return yTYPE; } + "type" { FL; return yTYPE__LEX; } "typedef" { FL; return yTYPEDEF; } "union" { FL; return yUNION; } "unique" { FL; return yUNIQUE; } diff --git a/src/verilog.y b/src/verilog.y index 05ba67da4..9f708da32 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -757,8 +757,10 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yTRIOR "trior" %token yTRIREG "trireg" %token yTRUE "true" -%token yTYPE "type" %token yTYPEDEF "typedef" +%token yTYPE__EQ "type-then-eqneq" +%token yTYPE__ETC "type" +%token yTYPE__LEX "type-in-lex" %token yUNION "union" %token yUNIQUE "unique" %token yUNIQUE0 "unique0" @@ -1865,7 +1867,7 @@ parameter_declarationFront: // IEEE: local_ or parameter_declaration w/o ass parameter_declarationTypeFront: // IEEE: local_ or parameter_declaration w/o assignment // // Front must execute first so VARDTYPE is ready before list of vars - varParamReset yTYPE { /*VARRESET-in-varParam*/ VARDTYPE(new AstParseTypeDType{$2}); } + varParamReset yTYPE__ETC { /*VARRESET-in-varParam*/ VARDTYPE(new AstParseTypeDType{$2}); } ; parameter_port_declarationFrontE: // IEEE: local_ or parameter_port_declaration w/o assignment @@ -1882,8 +1884,8 @@ parameter_port_declarationTypeFrontE: // IEEE: parameter_port_declaration w/o as // // IEEE: parameter_declaration (minus assignment) // // IEEE: local_parameter_declaration (minus assignment) // // Front must execute first so VARDTYPE is ready before list of vars - varParamReset yTYPE { /*VARRESET-in-varParam*/ VARDTYPE(new AstParseTypeDType{$2}); } - | yTYPE { /*VARRESET-in-varParam*/ VARDTYPE(new AstParseTypeDType{$1}); } + varParamReset yTYPE__ETC { /*VARRESET-in-varParam*/ VARDTYPE(new AstParseTypeDType{$2}); } + | yTYPE__ETC { /*VARRESET-in-varParam*/ VARDTYPE(new AstParseTypeDType{$1}); } ; net_declaration: // IEEE: net_declaration - excluding implict @@ -2105,7 +2107,7 @@ data_typeNoRef: // ==IEEE: data_type, excluding class_ty { $$ = new AstBasicDType{$1, VBasicDTypeKwd::CHANDLE}; } | yEVENT { $$ = new AstBasicDType{$1, VBasicDTypeKwd::EVENT}; v3Global.setHasEvents(); } - | type_reference { $$ = $1; } + | type_referenceDecl { $$ = $1; } // // IEEE: class_scope: see data_type above // // IEEE: class_type: see data_type above // // IEEE: ps_covergroup: see data_type above @@ -2137,11 +2139,21 @@ var_data_type: // ==IEEE: var_data_type | yVAR implicit_typeE { $$ = $2; } ; -type_reference: // ==IEEE: type_reference - yTYPE '(' exprOrDataType ')' +type_referenceBoth: // IEEE: type_reference + yTYPE__ETC '(' exprOrDataType ')' + { $$ = new AstAttrOf{$1, VAttrType::TYPEID, $3}; } + ; + +type_referenceDecl: // IEEE: type_reference (as a data type for declaration) + yTYPE__ETC '(' exprOrDataType ')' { $$ = new AstRefDType{$1, AstRefDType::FlagTypeOfExpr{}, $3}; } ; +type_referenceEq: // IEEE: type_reference (as an ==/!== expression) + yTYPE__EQ '(' exprOrDataType ')' + { $$ = new AstAttrOf{$1, VAttrType::TYPEID, $3}; } + ; + struct_unionDecl: // IEEE: part of data_type // // packedSigningE is NOP for unpacked ySTRUCT packedSigningE '{' @@ -2768,11 +2780,11 @@ c_generate_item: // IEEE: generate_item (for checkers) ; conditional_generate_construct: // ==IEEE: conditional_generate_construct - yCASE '(' expr ')' ~c~case_generate_itemListE yENDCASE + yCASE '(' exprTypeCompare ')' ~c~case_generate_itemListE yENDCASE { $$ = new AstGenCase{$1, $3, $5}; } - | yIF '(' expr ')' ~c~generate_block_or_null %prec prLOWER_THAN_ELSE + | yIF '(' exprTypeCompare ')' ~c~generate_block_or_null %prec prLOWER_THAN_ELSE { $$ = new AstGenIf{$1, $3, $5, nullptr}; } - | yIF '(' expr ')' ~c~generate_block_or_null yELSE ~c~generate_block_or_null + | yIF '(' exprTypeCompare ')' ~c~generate_block_or_null yELSE ~c~generate_block_or_null { $$ = new AstGenIf{$1, $3, $5, $7}; } ; @@ -3691,11 +3703,11 @@ unique_priorityE: // IEEE: unique_priority + empty ; caseStart: // IEEE: part of case_statement - yCASE '(' expr ')' + yCASE '(' exprTypeCompare ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase{$1, VCaseType::CT_CASE, $3, nullptr}; } - | yCASEX '(' expr ')' + | yCASEX '(' exprTypeCompare ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase{$1, VCaseType::CT_CASEX, $3, nullptr}; } - | yCASEZ '(' expr ')' + | yCASEZ '(' exprTypeCompare ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase{$1, VCaseType::CT_CASEZ, $3, nullptr}; } ; @@ -3764,8 +3776,8 @@ value_range: // ==IEEE: value_range //UNSUP ; caseCondList: // IEEE: part of case_item - expr { $$ = $1; } - | caseCondList ',' expr { $$ = $1->addNext($3); } + exprTypeCompare { $$ = $1; } + | caseCondList ',' exprTypeCompare { $$ = $1->addNext($3); } ; patternNoExpr: // IEEE: pattern **Excluding Expr* @@ -4709,6 +4721,11 @@ expr: // IEEE: part of expression/constant_expression/ | ~l~expr yP_SSRIGHT ~r~expr { $$ = new AstShiftRS{$2, $1, $3}; } | ~l~expr yP_LTMINUSGT ~r~expr { $$ = new AstLogEq{$2, $1, $3}; } // + // // IEEE: expression binary_operator expression (type compare see IEEE footnote) + | type_referenceEq yP_CASEEQUAL type_referenceBoth { $$ = new AstEqT{$2, $1, $3}; } + | type_referenceEq yP_CASENOTEQUAL type_referenceBoth { $$ = new AstNeqT{$2, $1, $3}; } + | type_referenceEq yP_EQUAL type_referenceBoth { $$ = new AstEqT{$2, $1, $3}; } + | type_referenceEq yP_NOTEQUAL type_referenceBoth { $$ = new AstNeqT{$2, $1, $3}; } // // IEEE: expr yP_MINUSGT expr (1800-2009) // // Conflicts with constraint_expression:"expr yP_MINUSGT constraint_set" // // To duplicating expr for constraints, just allow the more general form @@ -4771,7 +4788,7 @@ expr: // IEEE: part of expression/constant_expression/ // // expanded from casting_type | simple_type yP_TICK '(' expr ')' { $$ = new AstCast{$1->fileline(), $4, VFlagChildDType{}, $1}; } - | yTYPE '(' exprOrDataType ')' yP_TICK '(' expr ')' + | yTYPE__ETC '(' exprOrDataType ')' yP_TICK '(' expr ')' { $$ = new AstCast{$1, $7, VFlagChildDType{}, new AstRefDType{$1, AstRefDType::FlagTypeOfExpr{}, $3}}; } | ySIGNED yP_TICK '(' expr ')' { $$ = new AstSigned{$1, $4}; } @@ -4946,6 +4963,11 @@ exprStrText: | strAsText { $$ = $1; } ; +exprTypeCompare: + expr { $$ = $1; } + | type_referenceBoth { $$ = $1; } + ; + cStrList: exprStrText { $$ = $1; } | exprStrText ',' cStrList { $$ = $1->addNext($3); } diff --git a/test_regress/t/t_type_compare.pl b/test_regress/t/t_type_compare.pl new file mode 100755 index 000000000..a17622844 --- /dev/null +++ b/test_regress/t/t_type_compare.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2004 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. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_type_compare.v b/test_regress/t/t_type_compare.v new file mode 100644 index 000000000..a209dffee --- /dev/null +++ b/test_regress/t/t_type_compare.v @@ -0,0 +1,68 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module Sub #(parameter type T = type(logic[11:0])); +endmodule + +module t; + + int case_ok; + + Sub #(.T(int)) sub(); + + typedef logic [12:0] logic12_t; + + // Generate if + if (type(logic[12:0]) !== type(logic[12:0])) initial $stop; + if (type(logic[12:0]) != type(logic12_t)) initial $stop; + if (type(logic[12:0]) !== type(logic12_t)) initial $stop; + if (type(logic[22:0]) == type(logic12_t)) initial $stop; + if (type(logic[22:0]) === type(logic12_t)) initial $stop; + // Generate case + case (type(real)) + type(int): initial $stop; + type(real): ; + default: initial $stop; + endcase + + initial begin + if (type(real) == type(logic[12:0])) $stop; + if (type(real) === type(logic[12:0])) $stop; + if (type(real) != type(real)) $stop; + if (type(real) !== type(real)) $stop; + if (type(logic[12:0]) !== type(logic[12:0])) $stop; + if (type(logic[12:0]) != type(logic12_t)) $stop; + if (type(logic[12:0]) !== type(logic12_t)) $stop; + if (type(logic[22:0]) == type(logic12_t)) $stop; + if (type(logic[22:0]) === type(logic12_t)) $stop; + + // Item selected + case (type(real)) + type(real): case_ok = 1; + type(int): $stop; + type(chandle): $stop; + default: $stop; + endcase + if (case_ok != 1) $stop; + + // Default selected + case (type(real)) + type(int): $stop; + default: case_ok = 2; + endcase + if (case_ok != 2) $stop; + + // No body selected + case (type(real)) + type(int): $stop; + endcase + if (case_ok != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_type_compare_bad.out b/test_regress/t/t_type_compare_bad.out new file mode 100644 index 000000000..e3827de74 --- /dev/null +++ b/test_regress/t/t_type_compare_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_type_compare_bad.v:12:9: Case(type) statement requires items that have type() items + : ... In instance t + 12 | 1: $stop; + | ^ +%Error: Exiting due to diff --git a/test_regress/t/t_type_compare_bad.pl b/test_regress/t/t_type_compare_bad.pl new file mode 100755 index 000000000..59ba0d6c6 --- /dev/null +++ b/test_regress/t/t_type_compare_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env 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. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(linter => 1); + +lint( + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_type_compare_bad.v b/test_regress/t/t_type_compare_bad.v new file mode 100644 index 000000000..1b22fabb3 --- /dev/null +++ b/test_regress/t/t_type_compare_bad.v @@ -0,0 +1,20 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t; + initial begin + // Syntax error, so not checking: if (type(real) == 1)) $stop; // Bad + + case (type(real)) + 1: $stop; // Bad + default: $finish; + endcase + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule