forked from github/verilator
Emit parameter values as 'static constexpr' instead of enum
All parameters that are required in the output are now emitted as 'static constexpr, except for string or array of strings parameters, which are still emitted as 'static const' (required as std::string is not a literal type, so cannot be constexpr). This simplifies handling of parameters and supports 'real' parameters.
This commit is contained in:
parent
9907d211ff
commit
4ab4c0c8ba
2
Changes
2
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
|
||||
|
20
src/V3Ast.h
20
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;
|
||||
|
@ -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
|
||||
|
@ -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});
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "verilatedos.h"
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3EmitCConstInit.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
@ -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<AstChangeDet*> 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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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") {
|
||||
|
@ -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.
|
||||
|
21
test_regress/t/t_param_array7.pl
Executable file
21
test_regress/t/t_param_array7.pl
Executable file
@ -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;
|
57
test_regress/t/t_param_array7.v
Normal file
57
test_regress/t/t_param_array7.v
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user