diff --git a/Changes b/Changes index a1151d3b5..e4ed2a737 100644 --- a/Changes +++ b/Changes @@ -16,6 +16,8 @@ Verilator 4.211 devel * Fix -G to treat simple integer literals as signed (#3060). [Anikin1610] * Output files are split based on the set of headers required in order to aid incremental compilation via ccache (#3071). [Geza Lore] +* Parameter values are now emitted as 'static constexpr' instead of enum. + C++ direct references to parameters might require updating (#3077). [Geza Lore] Verilator 4.210 2021-07-07 diff --git a/src/V3Ast.h b/src/V3Ast.h index 1afe34c83..eace39dce 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -529,6 +529,25 @@ public: bool isEventValue() const { return m_e == EVENTVALUE; } bool isString() const { return m_e == STRING; } bool isMTaskState() const { return m_e == MTASKSTATE; } + // Does this represent a C++ LiteralType? (can be constexpr) + bool isLiteralType() const { + switch (m_e) { + case BIT: + case BYTE: + case CHANDLE: + case INT: + case INTEGER: + case LOGIC: + case LONGINT: + case DOUBLE: + case SHORTINT: + case SCOPEPTR: + case CHARPTR: + case UINT32: + case UINT64: return true; + default: return false; + } + } }; inline bool operator==(const AstBasicDTypeKwd& lhs, const AstBasicDTypeKwd& rhs) { return lhs.m_e == rhs.m_e; @@ -2436,6 +2455,7 @@ public: return (isString() ? "N" : isWide() ? "W" : isQuad() ? "Q" : "I"); } string cType(const string& name, bool forFunc, bool isRef) const; + bool isLiteralType() const; // Does this represent a C++ LiteralType? (can be constexpr) private: class CTypeRecursed; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 52f5b6755..7fbc3be71 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -776,6 +776,20 @@ int AstNodeDType::widthPow2() const { return 1; } +bool AstNodeDType::isLiteralType() const { + if (auto* const dtypep = VN_CAST_CONST(skipRefp(), BasicDType)) { + return dtypep->keyword().isLiteralType(); + } else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), UnpackArrayDType)) { + return dtypep->basicp()->isLiteralType(); + } else if (auto* const dtypep = VN_CAST_CONST(skipRefp(), StructDType)) { + // Currently all structs are packed, later this can be expanded to + // 'forall members _.isLiteralType()' + return dtypep->packed(); + } else { + return false; + } +} + /// What is the base variable (or const) this dereferences? AstNode* AstArraySel::baseFromp(AstNode* nodep, bool overMembers) { // Else AstArraySel etc; search for the base diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index d1b9daba0..40b328966 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -183,7 +183,8 @@ void V3CCtors::cctorsAll() { for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) { if (AstVar* const varp = VN_CAST(np, Var)) { - if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset()) { + if (!varp->isIfaceParent() && !varp->isIfaceRef() && !varp->noReset() + && !varp->isParam()) { const auto vrefp = new AstVarRef{varp->fileline(), varp, VAccess::WRITE}; var_reset.add(new AstCReset{varp->fileline(), vrefp}); } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index eb1aa4de3..fddf3e348 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -21,7 +21,7 @@ #include "verilatedos.h" #include "V3Global.h" -#include "V3EmitCBase.h" +#include "V3EmitCConstInit.h" #include #include @@ -113,13 +113,14 @@ public: //###################################################################### // Emit statements and math operators -class EmitCFunc VL_NOT_FINAL : public EmitCBaseVisitor { +class EmitCFunc VL_NOT_FINAL : public EmitCConstInit { private: AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting int m_labelNum; // Next label number int m_splitSize; // # of cfunc nodes placed into output file bool m_inUC = false; // Inside an AstUCStmt or AstUCMath std::vector m_blkChangeDetVec; // All encountered changes in block + bool m_emitConstInit = false; // Emitting constant initializer protected: EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations @@ -178,8 +179,16 @@ public: AstNodeDType* dtypep, int depth, const string& suffix); void doubleOrDetect(AstChangeDet* changep, bool& gotOne); void emitChangeDet(); + void emitConstInit(AstNode* initp) { + // We should refactor emit to produce output into a provided buffer, not go through members + // variables. That way we could just invoke the appropriate emitter as needed. + VL_RESTORER(m_emitConstInit); + m_emitConstInit = true; + iterate(initp); + } // VISITORS + using EmitCConstInit::visit; virtual void visit(AstCFunc* nodep) override { VL_RESTORER(m_useSelfForThis); VL_RESTORER(m_cfuncp); @@ -1120,7 +1129,9 @@ public: puts(funcNameProtect(funcp)); } virtual void visit(AstConst* nodep) override { - if (nodep->isWide()) { + if (m_emitConstInit) { + EmitCConstInit::visit(nodep); + } else if (nodep->isWide()) { UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Constant w/ no temp"); emitConstant(nodep, m_wideTempRefp, ""); m_wideTempRefp = nullptr; // We used it, barf if set it a second time diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 6f230ac57..2ef0903c3 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -103,8 +103,7 @@ class EmitCHeader final : public EmitCConstInit { // Emit variables in consecutive anon and non-anon batches for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp() - || (varp->isParam() && !VN_IS(varp->valuep(), Const))) { + if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) { const bool anon = isAnonOk(varp); if (anon != lastAnon) emitCurrentList(); lastAnon = anon; @@ -126,31 +125,19 @@ class EmitCHeader final : public EmitCConstInit { bool first = true; for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST(nodep, Var)) { - if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { + if (varp->isParam()) { decorateFirst(first, "\n// PARAMETERS\n"); UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); - // These should be static const values, however older MSVC++ did't - // support them; should be ok now under C++11, need to refactor. - if (varp->isWide()) { // Unsupported for output - putsDecoration("// enum WData " + varp->nameProtect() + " //wide"); - } else if (varp->isString()) { - puts("static const std::string " + protect("var_" + varp->name()) + ";\n"); - } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output - // putsDecoration("// enum ..... "+varp->nameProtect() - // +"not simple value, see variable above instead"); - } else if (VN_IS(varp->dtypep(), BasicDType) - && VN_CAST(varp->dtypep(), BasicDType) - ->isOpaque()) { // Can't put out e.g. doubles - } else { - // enum - puts(varp->isQuad() ? "enum _QData" : "enum _IData"); - puts("" + varp->nameProtect() + " { " + varp->nameProtect() + " = "); - iterateAndNextNull(varp->valuep()); - puts("};\n"); - // var - puts(varp->isQuad() ? "static const QData " : "static const IData "); - puts(protect("var_" + varp->name()) + ";\n"); + // Only C++ LiteralTypes can be constexpr + const bool canBeConstexpr = varp->dtypep()->isLiteralType(); + puts("static "); + puts(canBeConstexpr ? "constexpr " : "const "); + puts(varp->dtypep()->cType(varp->nameProtect(), false, false)); + if (canBeConstexpr) { + puts(" = "); + iterate(varp->valuep()); } + puts(";\n"); } } } diff --git a/src/V3EmitCImp.cpp b/src/V3EmitCImp.cpp index be6bca502..69b5f776e 100644 --- a/src/V3EmitCImp.cpp +++ b/src/V3EmitCImp.cpp @@ -202,35 +202,31 @@ class EmitCImp final : EmitCFunc { } } void emitParamDefns(const AstNodeModule* modp) { + const string modName = prefixNameProtect(modp); + bool first = true; for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST_CONST(nodep, Var)) { - if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) { - UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); - // These should be static const values, however older MSVC++ did't - // support them; should be ok now under C++11, need to refactor. - if (varp->isWide()) { // Unsupported for output - } else if (varp->isString()) { - puts("const std::string "); - puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) - + "("); - iterateAndNextNull(varp->valuep()); - puts(");\n"); - } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output - // putsDecoration("// enum ..... "+varp->nameProtect() - // +"not simple value, see variable above instead"); - } else if (VN_IS(varp->dtypep(), BasicDType) - && VN_CAST(varp->dtypep(), BasicDType) - ->isOpaque()) { // Can't put out e.g. doubles - } else { - puts(varp->isQuad() ? "const QData " : "const IData "); - puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name()) - + "("); - iterateAndNextNull(varp->valuep()); - puts(");\n"); + if (varp->isParam()) { + if (first) { + puts("\n"); + putsDecoration("// Parameter definitions for " + modName + "\n"); + first = false; } + UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?"); + // Only C++ LiteralTypes can be constexpr + const bool canBeConstexpr = varp->dtypep()->isLiteralType(); + puts(canBeConstexpr ? "constexpr " : "const "); + const string scopedName = modName + "::" + varp->nameProtect(); + puts(varp->dtypep()->cType(scopedName, false, false)); + if (!canBeConstexpr) { + puts(" = "); + emitConstInit(varp->valuep()); + } + puts(";\n"); } } } + if (!first) puts("\n"); } void emitCtorImp(const AstNodeModule* modp) { const string modName = prefixNameProtect(modp); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 335f09426..7cd836c91 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -857,13 +857,8 @@ void EmitCSyms::emitSymImp() { putsQuoted(protect(it->second.m_varBasePretty)); std::string varName; - varName += (protectIf(scopep->nameDotless(), scopep->protect()) + "."); - - if (varp->isParam()) { - varName += protect("var_" + varp->name()); - } else { - varName += protect(varp->name()); - } + varName += protectIf(scopep->nameDotless(), scopep->protect()) + "."; + varName += protect(varp->name()); if (varp->isParam()) { if (varp->vlEnumType() == "VLVT_STRING") { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 9976028bb..3552d6a4a 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -962,17 +962,8 @@ class ParamVisitor final : public AstNVisitor { V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init() if (!VN_IS(nodep->valuep(), Const) && !VN_IS(nodep->valuep(), Unbounded)) { // Complex init, like an array - // Make a new INITIAL to set the value. - // This allows the normal array/struct handling code to properly - // initialize the parameter. - nodep->addNext(new AstInitial( - nodep->fileline(), - new AstAssign(nodep->fileline(), - new AstVarRef(nodep->fileline(), nodep, VAccess::WRITE), - nodep->valuep()->cloneTree(true)))); if (nodep->isFuncLocal()) { - // We put the initial in wrong place under a function. We - // should move the parameter out of the function and to the + // We should move the parameter out of the function and to the // module, with appropriate dotting, but this confuses LinkDot // (as then name isn't found later), so punt - probably can // treat as static function variable when that is supported. diff --git a/test_regress/t/t_param_array7.pl b/test_regress/t/t_param_array7.pl new file mode 100755 index 000000000..b46d46042 --- /dev/null +++ b/test_regress/t/t_param_array7.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 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(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_param_array7.v b/test_regress/t/t_param_array7.v new file mode 100644 index 000000000..72380d0a9 --- /dev/null +++ b/test_regress/t/t_param_array7.v @@ -0,0 +1,57 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Geza Lore. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct packed { + longint a; + longint b; + longint c; +} s_t; + +module t; + localparam int c0 [4] = '{5, 6, 7, 8}; + localparam bit [255:0] c1 [4] = '{9, 10, 11, 12}; + localparam string c2 [2] = '{"baz", "quux"}; + localparam s_t c3 [2] = '{'{a: 100, b: 200, c: 300}, + '{a: 1000, b: 2000, c: 3000}}; + + a #( + .p0(c0), + .p1(c1), + .p2(c2), + .p3(c3) + ) i_a (); +endmodule + +module a + #( + parameter int p0 [4] = '{1, 2, 3, 4}, + parameter bit [255:0] p1 [4] = '{1, 2, 3, 4}, + parameter string p2 [2] = '{"foo", "bar"}, + parameter s_t p3 [2] = '{'{a: 1, b: 2, c: 3}, + '{a: 1, b: 2, c: 3}} + ); + initial begin + // Go via $c to ensure parameters are emitted + if (p0[$c("0")] != 5) $stop; + if (p0[$c("1")] != 6) $stop; + if (p0[$c("2")] != 7) $stop; + if (p0[$c("3")] != 8) $stop; + if (p1[$c("0")] != 9) $stop; + if (p1[$c("1")] != 10) $stop; + if (p1[$c("2")] != 11) $stop; + if (p1[$c("3")] != 12) $stop; + if (p2[$c("0")] != "baz") $stop; + if (p2[$c("1")] != "quux") $stop; + if (p3[$c("0")].a != 100) $stop; + if (p3[$c("0")].b != 200) $stop; + if (p3[$c("0")].c != 300) $stop; + if (p3[$c("1")].a != 1000) $stop; + if (p3[$c("1")].b != 2000) $stop; + if (p3[$c("1")].c != 3000) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule