2021-06-24 18:35:12 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Emit C++ for tree
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
|
2021-06-24 18:35:12 +02:00
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#ifndef VERILATOR_V3EMITCFUNC_H_
|
|
|
|
|
#define VERILATOR_V3EMITCFUNC_H_
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
2021-07-22 19:59:03 +02:00
|
|
|
#include "V3EmitCConstInit.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Global.h"
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <unordered_set>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
// Number of VL_CONST_W_*X's in verilated.h (IE VL_CONST_W_8X is last)
|
|
|
|
|
constexpr int EMITC_NUM_CONSTW = 8;
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Emit lazy forward declarations
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class EmitCLazyDecls final : public VNVisitor {
|
2021-06-24 18:35:12 +02:00
|
|
|
// NODE STATE/TYPES
|
2022-09-12 17:59:14 +02:00
|
|
|
// None allowed to support threaded emitting
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
// MEMBERS
|
|
|
|
|
std::unordered_set<string> m_emittedManually; // Set of names already declared manually.
|
|
|
|
|
EmitCBaseVisitor& m_emitter; // For access to file output
|
|
|
|
|
bool m_needsBlankLine = false; // Emit blank line if any declarations were emitted (cosmetic)
|
2022-09-12 17:59:14 +02:00
|
|
|
std::set<AstNode*> m_emitted; // -> in set. Already emitted decl for symbols.
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2022-09-12 17:59:14 +02:00
|
|
|
bool declaredOnce(AstNode* nodep) { return m_emitted.insert(nodep).second; }
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
void lazyDeclare(AstCFunc* funcp) {
|
|
|
|
|
// Already declared in this compilation unit
|
2022-09-12 17:59:14 +02:00
|
|
|
if (!declaredOnce(funcp)) return;
|
2021-06-24 18:35:12 +02:00
|
|
|
// Check if this kind of function is lazily declared
|
|
|
|
|
if (!(funcp->isMethod() && funcp->isLoose()) && !funcp->dpiImportPrototype()) return;
|
|
|
|
|
// Already declared manually
|
|
|
|
|
if (m_emittedManually.count(funcp->nameProtect())) return;
|
|
|
|
|
// Needs lazy declaration, emit one
|
2021-07-22 16:53:42 +02:00
|
|
|
m_emitter.emitCFuncDecl(funcp, EmitCParentModule::get(funcp), funcp->dpiImportPrototype());
|
2021-06-24 18:35:12 +02:00
|
|
|
m_needsBlankLine = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void lazyDeclareConstPoolVar(AstVar* varp) {
|
2022-09-12 17:59:14 +02:00
|
|
|
if (!declaredOnce(varp)) return; // Already declared
|
2021-06-24 18:35:12 +02:00
|
|
|
const string nameProtect
|
|
|
|
|
= m_emitter.topClassName() + "__ConstPool__" + varp->nameProtect();
|
|
|
|
|
m_emitter.puts("extern const ");
|
|
|
|
|
m_emitter.puts(varp->dtypep()->cType(nameProtect, false, false));
|
|
|
|
|
m_emitter.puts(";\n");
|
|
|
|
|
m_needsBlankLine = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeCCall* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
lazyDeclare(nodep->funcp());
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAddrOfCFunc* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
lazyDeclare(nodep->funcp());
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
AstVar* const varp = nodep->varp();
|
|
|
|
|
// Only constant pool symbols are lazy declared for now ...
|
2021-07-22 16:53:42 +02:00
|
|
|
if (EmitCBaseVisitor::isConstPoolMod(EmitCParentModule::get(varp))) {
|
|
|
|
|
lazyDeclareConstPoolVar(varp);
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
public:
|
2021-07-25 19:38:27 +02:00
|
|
|
explicit EmitCLazyDecls(EmitCBaseVisitor& emitter)
|
2021-06-24 18:35:12 +02:00
|
|
|
: m_emitter(emitter) {}
|
|
|
|
|
void emit(AstNode* nodep) {
|
|
|
|
|
m_needsBlankLine = false;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
if (m_needsBlankLine) m_emitter.puts("\n");
|
|
|
|
|
}
|
|
|
|
|
void emit(const string& prefix, const string& name, const string& suffix) {
|
|
|
|
|
m_emittedManually.insert(name);
|
|
|
|
|
m_emitter.ensureNewLine();
|
|
|
|
|
m_emitter.puts(prefix);
|
|
|
|
|
m_emitter.puts(name);
|
|
|
|
|
m_emitter.puts(suffix);
|
|
|
|
|
m_emitter.ensureNewLine();
|
|
|
|
|
}
|
2022-09-12 17:59:14 +02:00
|
|
|
void declared(AstCFunc* nodep) { m_emitted.insert(nodep); }
|
|
|
|
|
void reset() { m_emitted.clear(); }
|
2021-06-24 18:35:12 +02:00
|
|
|
};
|
|
|
|
|
|
2022-10-12 11:19:21 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Emit statements and expressions
|
2021-06-24 18:35:12 +02:00
|
|
|
|
2021-07-22 19:59:03 +02:00
|
|
|
class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
|
2021-06-24 18:35:12 +02:00
|
|
|
private:
|
2022-01-01 17:46:49 +01:00
|
|
|
AstVarRef* m_wideTempRefp = nullptr; // Variable that _WW macros should be setting
|
|
|
|
|
int m_labelNum = 0; // Next label number
|
|
|
|
|
int m_splitSize = 0; // # of cfunc nodes placed into output file
|
2022-10-12 11:19:21 +02:00
|
|
|
bool m_inUC = false; // Inside an AstUCStmt or AstUCExpr
|
2021-07-22 19:59:03 +02:00
|
|
|
bool m_emitConstInit = false; // Emitting constant initializer
|
2021-06-24 18:35:12 +02:00
|
|
|
|
2022-07-12 18:51:17 +02:00
|
|
|
// State associated with processing $display style string formatting
|
|
|
|
|
struct EmitDispState {
|
|
|
|
|
string m_format; // "%s" and text from user
|
|
|
|
|
std::vector<char> m_argsChar; // Format of each argument to be printed
|
|
|
|
|
std::vector<AstNode*> m_argsp; // Each argument to be printed
|
|
|
|
|
std::vector<string> m_argsFunc; // Function before each argument to be printed
|
|
|
|
|
EmitDispState() { clear(); }
|
|
|
|
|
void clear() {
|
|
|
|
|
m_format = "";
|
|
|
|
|
m_argsChar.clear();
|
|
|
|
|
m_argsp.clear();
|
|
|
|
|
m_argsFunc.clear();
|
|
|
|
|
}
|
|
|
|
|
void pushFormat(const string& fmt) { m_format += fmt; }
|
|
|
|
|
void pushFormat(char fmt) { m_format += fmt; }
|
|
|
|
|
void pushArg(char fmtChar, AstNode* nodep, const string& func) {
|
|
|
|
|
m_argsChar.push_back(fmtChar);
|
|
|
|
|
m_argsp.push_back(nodep);
|
|
|
|
|
m_argsFunc.push_back(func);
|
|
|
|
|
}
|
|
|
|
|
} m_emitDispState;
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
protected:
|
|
|
|
|
EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations
|
|
|
|
|
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
|
2021-07-07 20:16:40 +02:00
|
|
|
const AstNodeModule* m_modp = nullptr; // Current module being emitted
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstCFunc* m_cfuncp = nullptr; // Current function being emitted
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// METHODS
|
|
|
|
|
|
|
|
|
|
// ACCESSORS
|
|
|
|
|
void splitSizeInc(int count) { m_splitSize += count; }
|
2022-01-09 23:34:10 +01:00
|
|
|
void splitSizeInc(AstNode* nodep) { splitSizeInc(nodep->nodeCount()); }
|
2021-07-14 23:37:37 +02:00
|
|
|
void splitSizeReset() { m_splitSize = 0; }
|
2021-06-24 18:35:12 +02:00
|
|
|
bool splitNeeded() const {
|
2021-07-14 23:37:37 +02:00
|
|
|
return v3Global.opt.outputSplit() && m_splitSize >= v3Global.opt.outputSplit();
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void displayNode(AstNode* nodep, AstScopeName* scopenamep, const string& vformat,
|
|
|
|
|
AstNode* exprsp, bool isScan);
|
|
|
|
|
void displayEmit(AstNode* nodep, bool isScan);
|
|
|
|
|
void displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt, bool ignore,
|
|
|
|
|
char fmtLetter);
|
|
|
|
|
|
2022-10-12 11:19:21 +02:00
|
|
|
bool emitSimpleOk(AstNodeExpr* nodep);
|
2021-06-24 18:35:12 +02:00
|
|
|
void emitIQW(AstNode* nodep) {
|
|
|
|
|
// Other abbrevs: "C"har, "S"hort, "F"loat, "D"ouble, stri"N"g
|
|
|
|
|
puts(nodep->dtypep()->charIQWN());
|
|
|
|
|
}
|
|
|
|
|
void emitScIQW(AstVar* nodep) {
|
|
|
|
|
UASSERT_OBJ(nodep->isSc(), nodep, "emitting SystemC operator on non-SC variable");
|
|
|
|
|
// clang-format off
|
|
|
|
|
puts(nodep->isScBigUint() ? "SB"
|
|
|
|
|
: nodep->isScUint() ? "SU"
|
|
|
|
|
: nodep->isScBv() ? "SW"
|
|
|
|
|
: (nodep->isScQuad() ? "SQ" : "SI"));
|
|
|
|
|
// clang-format on
|
|
|
|
|
}
|
|
|
|
|
void emitDatap(AstNode* nodep) {
|
|
|
|
|
// When passing to a function with va_args the compiler doesn't
|
|
|
|
|
// know need a pointer so when wide, need to look inside VlWide
|
|
|
|
|
if (nodep->isWide()) puts(".data()");
|
|
|
|
|
}
|
|
|
|
|
void emitOpName(AstNode* nodep, const string& format, AstNode* lhsp, AstNode* rhsp,
|
|
|
|
|
AstNode* thsp);
|
2021-07-13 18:42:17 +02:00
|
|
|
void emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer);
|
2021-06-24 18:35:12 +02:00
|
|
|
void emitDereference(const string& pointer);
|
|
|
|
|
void emitCvtPackStr(AstNode* nodep);
|
|
|
|
|
void emitCvtWideArray(AstNode* nodep, AstNode* fromp);
|
|
|
|
|
void emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString);
|
|
|
|
|
void emitSetVarConstant(const string& assignString, AstConst* constp);
|
|
|
|
|
void emitVarReset(AstVar* varp);
|
|
|
|
|
string emitVarResetRecurse(const AstVar* varp, const string& varNameProtected,
|
|
|
|
|
AstNodeDType* dtypep, int depth, const string& suffix);
|
|
|
|
|
void emitChangeDet();
|
2021-07-22 19:59:03 +02:00
|
|
|
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);
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2021-07-22 19:59:03 +02:00
|
|
|
using EmitCConstInit::visit;
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
VL_RESTORER(m_useSelfForThis);
|
2021-07-02 17:55:57 +02:00
|
|
|
VL_RESTORER(m_cfuncp);
|
|
|
|
|
m_cfuncp = nodep;
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
splitSizeInc(nodep);
|
|
|
|
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
m_lazyDecls.emit(nodep);
|
|
|
|
|
if (nodep->ifdef() != "") puts("#ifdef " + nodep->ifdef() + "\n");
|
|
|
|
|
if (nodep->isInline()) puts("VL_INLINE_OPT ");
|
|
|
|
|
emitCFuncHeader(nodep, m_modp, /* withScope: */ true);
|
|
|
|
|
|
|
|
|
|
// TODO perhaps better to have a new AstCCtorInit so we can pass arguments
|
|
|
|
|
// rather than requiring a string here
|
|
|
|
|
if (!nodep->ctorInits().empty()) {
|
|
|
|
|
puts(": ");
|
|
|
|
|
puts(nodep->ctorInits());
|
|
|
|
|
}
|
|
|
|
|
puts(" {\n");
|
|
|
|
|
|
|
|
|
|
if (nodep->isLoose()) {
|
|
|
|
|
m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration
|
|
|
|
|
if (!nodep->isStatic()) { // Standard prologue
|
|
|
|
|
m_useSelfForThis = true;
|
|
|
|
|
puts("if (false && vlSelf) {} // Prevent unused\n");
|
|
|
|
|
if (!VN_IS(m_modp, Class)) puts(symClassAssign());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// "+" in the debug indicates a print from the model
|
|
|
|
|
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ ");
|
2022-10-16 00:47:10 +02:00
|
|
|
for (int i = 0; i < m_modp->level(); ++i) puts(" ");
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(prefixNameProtect(m_modp));
|
|
|
|
|
puts(nodep->isLoose() ? "__" : "::");
|
|
|
|
|
puts(nodep->nameProtect() + "\\n\"); );\n");
|
|
|
|
|
|
|
|
|
|
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const varp = VN_CAST(subnodep, Var)) {
|
Add V3VariableOrder pass
A separate V3VariableOrder pass is now used to order module variables
before Emit. All variables are now ordered together, without
consideration for whether they are ports, signals form the design, or
additional internal variables added by Verilator (which used to be
ordered and emitted as separate groups in Emit). For single threaded
models, this is performance neutral. For multi-threaded models, the
MTask affinity based sorting was slightly modified, so variables with no
MTask affinity are emitted last, otherwise the MTask affinity sets are
sorted using the TSP sorter as before, but again, ports, signals, and
internal variables are not differentiated. This yields a 2%+ speedup for
the multithreaded model on OpenTitan.
2021-06-29 18:57:07 +02:00
|
|
|
if (varp->isFuncReturn()) emitVarDecl(varp);
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-02 17:55:57 +02:00
|
|
|
if (nodep->initsp()) {
|
|
|
|
|
putsDecoration("// Init\n");
|
|
|
|
|
iterateAndNextNull(nodep->initsp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nodep->stmtsp()) {
|
|
|
|
|
putsDecoration("// Body\n");
|
|
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
|
2021-07-02 17:55:57 +02:00
|
|
|
if (nodep->finalsp()) {
|
|
|
|
|
putsDecoration("// Final\n");
|
|
|
|
|
iterateAndNextNull(nodep->finalsp());
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
if (nodep->ifdef() != "") puts("#endif // " + nodep->ifdef() + "\n");
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2021-07-02 17:55:57 +02:00
|
|
|
UASSERT_OBJ(m_cfuncp, nodep, "Cannot emit non-local variable");
|
Add V3VariableOrder pass
A separate V3VariableOrder pass is now used to order module variables
before Emit. All variables are now ordered together, without
consideration for whether they are ports, signals form the design, or
additional internal variables added by Verilator (which used to be
ordered and emitted as separate groups in Emit). For single threaded
models, this is performance neutral. For multi-threaded models, the
MTask affinity based sorting was slightly modified, so variables with no
MTask affinity are emitted last, otherwise the MTask affinity sets are
sorted using the TSP sorter as before, but again, ports, signals, and
internal variables are not differentiated. This yields a 2%+ speedup for
the multithreaded model on OpenTitan.
2021-06-29 18:57:07 +02:00
|
|
|
emitVarDecl(nodep);
|
2021-07-02 17:55:57 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
bool paren = true;
|
|
|
|
|
bool decind = false;
|
2021-11-28 19:44:16 +01:00
|
|
|
bool rhs = true;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstSel* const selp = VN_CAST(nodep->lhsp(), Sel)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (selp->widthMin() == 1) {
|
|
|
|
|
putbs("VL_ASSIGNBIT_");
|
|
|
|
|
emitIQW(selp->fromp());
|
|
|
|
|
if (nodep->rhsp()->isAllOnesV()) {
|
|
|
|
|
puts("O(");
|
2021-11-28 19:44:16 +01:00
|
|
|
rhs = false;
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
|
|
|
|
puts("I(");
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextNull(selp->lsbp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(selp->fromp());
|
2021-11-28 19:44:16 +01:00
|
|
|
if (rhs) puts(", ");
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
|
|
|
|
putbs("VL_ASSIGNSEL_");
|
|
|
|
|
emitIQW(selp->fromp());
|
|
|
|
|
emitIQW(nodep->rhsp());
|
|
|
|
|
puts("(");
|
|
|
|
|
puts(cvtToStr(selp->fromp()->widthMin()) + ",");
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()) + ",");
|
|
|
|
|
iterateAndNextNull(selp->lsbp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(selp->fromp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstGetcRefN* const selp = VN_CAST(nodep->lhsp(), GetcRefN)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(selp->lhsp());
|
|
|
|
|
puts(" = ");
|
|
|
|
|
putbs("VL_PUTC_N(");
|
|
|
|
|
iterateAndNextNull(selp->lhsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(selp->rhsp());
|
|
|
|
|
puts(", ");
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (AstVar* const varp = AstVar::scVarRecurse(nodep->lhsp())) {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs("VL_ASSIGN_"); // Set a systemC variable
|
|
|
|
|
emitScIQW(varp);
|
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
puts("(");
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()) + ",");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(", ");
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (AstVar* const varp = AstVar::scVarRecurse(nodep->rhsp())) {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs("VL_ASSIGN_"); // Get a systemC variable
|
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
emitScIQW(varp);
|
|
|
|
|
puts("(");
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()) + ",");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
} else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) //
|
2022-10-12 11:19:21 +02:00
|
|
|
&& !VN_IS(nodep->rhsp(), CExpr) //
|
2021-06-24 18:35:12 +02:00
|
|
|
&& !VN_IS(nodep->rhsp(), CMethodHard) //
|
|
|
|
|
&& !VN_IS(nodep->rhsp(), VarRef) //
|
|
|
|
|
&& !VN_IS(nodep->rhsp(), AssocSel) //
|
|
|
|
|
&& !VN_IS(nodep->rhsp(), ArraySel)) {
|
|
|
|
|
// Wide functions assign into the array directly, don't need separate assign statement
|
2021-10-22 14:56:48 +02:00
|
|
|
m_wideTempRefp = VN_AS(nodep->lhsp(), VarRef);
|
2021-06-24 18:35:12 +02:00
|
|
|
paren = false;
|
|
|
|
|
} else if (nodep->isWide()) {
|
|
|
|
|
putbs("VL_ASSIGN_W(");
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()) + ",");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
} else {
|
|
|
|
|
paren = false;
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(" ");
|
|
|
|
|
ofp()->blockInc();
|
|
|
|
|
decind = true;
|
|
|
|
|
if (!VN_IS(nodep->rhsp(), Const)) ofp()->putBreak();
|
|
|
|
|
puts("= ");
|
|
|
|
|
}
|
2021-11-28 19:44:16 +01:00
|
|
|
if (rhs) iterateAndNextNull(nodep->rhsp());
|
2021-06-24 18:35:12 +02:00
|
|
|
if (paren) puts(")");
|
|
|
|
|
if (decind) ofp()->blockDec();
|
2021-07-13 22:17:41 +02:00
|
|
|
puts(";\n");
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlwaysPublic*) override {}
|
|
|
|
|
void visit(AstAssocSel* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(nodep->fromp());
|
|
|
|
|
putbs(".at(");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstAssocArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), AssocArrayDType);
|
2021-06-24 18:35:12 +02:00
|
|
|
UASSERT_OBJ(adtypep, nodep, "Associative select on non-associative type");
|
|
|
|
|
if (adtypep->keyDTypep()->isWide()) {
|
|
|
|
|
emitCvtWideArray(nodep->bitp(), nodep->fromp());
|
|
|
|
|
} else {
|
|
|
|
|
iterateAndNextNull(nodep->bitp());
|
|
|
|
|
}
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstWildcardSel* nodep) override {
|
2022-07-20 15:01:36 +02:00
|
|
|
iterateAndNextNull(nodep->fromp());
|
|
|
|
|
putbs(".at(");
|
|
|
|
|
AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType);
|
|
|
|
|
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
|
|
|
|
|
iterateAndNextNull(nodep->bitp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCCall* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
const AstCFunc* const funcp = nodep->funcp();
|
2021-07-22 16:53:42 +02:00
|
|
|
const AstNodeModule* const funcModp = EmitCParentModule::get(funcp);
|
2021-07-13 22:17:41 +02:00
|
|
|
if (funcp->dpiImportPrototype()) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Calling DPI import
|
|
|
|
|
puts(funcp->name());
|
|
|
|
|
} else if (funcp->isProperMethod() && funcp->isStatic()) {
|
|
|
|
|
// Call static method via the containing class
|
2021-07-22 16:53:42 +02:00
|
|
|
puts(prefixNameProtect(funcModp) + "::");
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(funcp->nameProtect());
|
2021-07-22 16:53:42 +02:00
|
|
|
} else if (VN_IS(funcModp, Class) && funcModp != m_modp) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Calling superclass method
|
2021-07-22 16:53:42 +02:00
|
|
|
puts(prefixNameProtect(funcModp) + "::");
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(funcp->nameProtect());
|
|
|
|
|
} else if (funcp->isLoose()) {
|
|
|
|
|
// Calling loose method
|
|
|
|
|
puts(funcNameProtect(funcp));
|
|
|
|
|
} else {
|
|
|
|
|
// Calling regular method/function
|
|
|
|
|
if (!nodep->selfPointer().empty()) {
|
|
|
|
|
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
|
|
|
|
|
}
|
|
|
|
|
puts(funcp->nameProtect());
|
|
|
|
|
}
|
2021-07-13 18:42:17 +02:00
|
|
|
emitCCallArgs(nodep, nodep->selfPointerProtect(m_useSelfForThis));
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCMethodCall* nodep) override {
|
2021-07-13 18:42:17 +02:00
|
|
|
const AstCFunc* const funcp = nodep->funcp();
|
|
|
|
|
UASSERT_OBJ(!funcp->isLoose(), nodep, "Loose method called via AstCMethodCall");
|
|
|
|
|
iterate(nodep->fromp());
|
|
|
|
|
putbs("->");
|
|
|
|
|
puts(funcp->nameProtect());
|
|
|
|
|
emitCCallArgs(nodep, "");
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstCAwait* nodep) override {
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
puts("co_await ");
|
|
|
|
|
iterate(nodep->exprp());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCNew* nodep) override {
|
2021-10-10 01:19:31 +02:00
|
|
|
bool comma = false;
|
2022-09-29 00:54:18 +02:00
|
|
|
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", ");
|
2021-07-13 18:42:17 +02:00
|
|
|
puts("vlSymsp"); // TODO make this part of argsp, and eliminate when unnecessary
|
2021-10-10 01:19:31 +02:00
|
|
|
if (nodep->argsp()) comma = true;
|
|
|
|
|
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
|
|
|
|
if (comma) puts(", ");
|
|
|
|
|
iterate(subnodep);
|
|
|
|
|
comma = true;
|
|
|
|
|
}
|
2021-07-13 18:42:17 +02:00
|
|
|
puts(")");
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCMethodHard* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
iterate(nodep->fromp());
|
|
|
|
|
puts(".");
|
2022-04-23 15:06:26 +02:00
|
|
|
puts(nodep->name());
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("(");
|
|
|
|
|
bool comma = false;
|
|
|
|
|
for (AstNode* subnodep = nodep->pinsp(); subnodep; subnodep = subnodep->nextp()) {
|
|
|
|
|
if (comma) puts(", ");
|
|
|
|
|
// handle wide arguments to the queues
|
|
|
|
|
if (VN_IS(nodep->fromp()->dtypep(), QueueDType) && subnodep->dtypep()->isWide()) {
|
|
|
|
|
emitCvtWideArray(subnodep, nodep->fromp());
|
|
|
|
|
} else {
|
|
|
|
|
iterate(subnodep);
|
|
|
|
|
}
|
|
|
|
|
comma = true;
|
|
|
|
|
}
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstLambdaArgRef* nodep) override { putbs(nodep->nameProtect()); }
|
|
|
|
|
void visit(AstWith* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
// With uses a C++11 lambda
|
|
|
|
|
putbs("[=](");
|
2021-11-26 23:55:36 +01:00
|
|
|
if (auto* const argrefp = nodep->indexArgRefp()) {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false));
|
|
|
|
|
puts(",");
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
if (auto* const argrefp = nodep->valueArgRefp()) {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs(argrefp->dtypep()->cType(argrefp->nameProtect(), false, false));
|
|
|
|
|
}
|
|
|
|
|
// Probably fragile, V3Task may need to convert to a AstCReturn
|
|
|
|
|
puts(") { return ");
|
|
|
|
|
iterateAndNextNull(nodep->exprp());
|
|
|
|
|
puts("; }\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeCase* nodep) override { // LCOV_EXCL_LINE
|
2021-06-24 18:35:12 +02:00
|
|
|
// In V3Case...
|
|
|
|
|
nodep->v3fatalSrc("Case statements should have been reduced out");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstComment* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
string at;
|
|
|
|
|
if (nodep->showAt()) {
|
|
|
|
|
at = " at " + nodep->fileline()->ascii();
|
|
|
|
|
// If protecting, passthru less information about the design
|
|
|
|
|
if (!v3Global.opt.protectIds()) return;
|
|
|
|
|
}
|
|
|
|
|
if (!(nodep->protect() && v3Global.opt.protectIds())) {
|
|
|
|
|
putsDecoration(string("// ") + nodep->name() + at + "\n");
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCoverDecl* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("vlSelf->__vlCoverInsert("); // As Declared in emitCoverageDecl
|
|
|
|
|
puts("&(vlSymsp->__Vcoverage[");
|
|
|
|
|
puts(cvtToStr(nodep->dataDeclThisp()->binNum()));
|
|
|
|
|
puts("])");
|
|
|
|
|
// If this isn't the first instantiation of this module under this
|
|
|
|
|
// design, don't really count the bucket, and rely on verilator_cov to
|
|
|
|
|
// aggregate counts. This is because Verilator combines all
|
|
|
|
|
// hierarchies itself, and if verilator_cov also did it, you'd end up
|
|
|
|
|
// with (number-of-instant) times too many counts in this bin.
|
|
|
|
|
puts(", first"); // Enable, passed from __Vconfigure parameter
|
|
|
|
|
puts(", ");
|
|
|
|
|
putsQuoted(protect(nodep->fileline()->filename()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(cvtToStr(nodep->fileline()->lineno()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(cvtToStr(nodep->offset() + nodep->fileline()->firstColumn()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
putsQuoted((!nodep->hier().empty() ? "." : "")
|
|
|
|
|
+ protectWordsIf(nodep->hier(), nodep->protect()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
putsQuoted(protectWordsIf(nodep->page(), nodep->protect()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
putsQuoted(protectWordsIf(nodep->comment(), nodep->protect()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
putsQuoted(nodep->linescov());
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCoverInc* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (v3Global.opt.threads()) {
|
|
|
|
|
puts("vlSymsp->__Vcoverage[");
|
|
|
|
|
puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum()));
|
|
|
|
|
puts("].fetch_add(1, std::memory_order_relaxed);\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts("++(vlSymsp->__Vcoverage[");
|
|
|
|
|
puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum()));
|
|
|
|
|
puts("]);\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCReturn* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("return (");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDisplay* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
string text = nodep->fmtp()->text();
|
|
|
|
|
if (nodep->addNewline()) text += "\n";
|
|
|
|
|
displayNode(nodep, nodep->fmtp()->scopeNamep(), text, nodep->fmtp()->exprsp(), false);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDumpCtl* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
switch (nodep->ctlType()) {
|
|
|
|
|
case VDumpCtlType::FILE:
|
|
|
|
|
puts("vlSymsp->_vm_contextp__->dumpfile(");
|
|
|
|
|
emitCvtPackStr(nodep->exprp());
|
|
|
|
|
puts(");\n");
|
|
|
|
|
break;
|
|
|
|
|
case VDumpCtlType::VARS:
|
|
|
|
|
// We ignore number of levels to dump in exprp()
|
|
|
|
|
if (v3Global.opt.trace()) {
|
Introduce model interface class, make $root part or Syms (#3036)
This patch implements #3032. Verilator creates a module representing the
SystemVerilog $root scope (V3LinkLevel::wrapTop). Until now, this was
called the "TOP" module, which also acted as the user instantiated model
class. Syms used to hold a pointer to this root module, but hold
instances of any submodule. This patch renames this root scope module
from "TOP" to "$root", and introduces a separate model class which is
now an interface class. As the root module is no longer the user
interface class, it can now be made an instance of Syms, just like any
other submodule. This allows absolute references into the root module to
avoid an additional pointer indirection resulting in a potential speedup
(about 1.5% on OpenTitan). The model class now also contains all non
design specific generated code (e.g.: eval loops, trace config, etc),
which additionally simplifies Verilator internals.
Please see the updated documentation for the model interface changes.
2021-06-21 16:30:20 +02:00
|
|
|
puts("vlSymsp->_traceDumpOpen();\n");
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
|
|
|
|
puts("VL_PRINTF_MT(\"-Info: ");
|
|
|
|
|
puts(protect(nodep->fileline()->filename()));
|
|
|
|
|
puts(":");
|
|
|
|
|
puts(cvtToStr(nodep->fileline()->lineno()));
|
|
|
|
|
puts(": $dumpvar ignored, as Verilated without --trace");
|
|
|
|
|
puts("\\n\");\n");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case VDumpCtlType::ALL:
|
|
|
|
|
// $dumpall currently ignored
|
|
|
|
|
break;
|
|
|
|
|
case VDumpCtlType::FLUSH:
|
|
|
|
|
// $dumpall currently ignored; would need rework of VCD single thread,
|
|
|
|
|
// or flag we pass-through to next eval() iteration
|
|
|
|
|
break;
|
|
|
|
|
case VDumpCtlType::LIMIT:
|
|
|
|
|
// $dumplimit currently ignored
|
|
|
|
|
break;
|
|
|
|
|
case VDumpCtlType::OFF:
|
|
|
|
|
// Currently ignored as both Vcd and Fst do not support them, as would need "X" dump
|
|
|
|
|
break;
|
|
|
|
|
case VDumpCtlType::ON:
|
|
|
|
|
// Currently ignored as $dumpoff is also ignored
|
|
|
|
|
break;
|
|
|
|
|
default: nodep->v3fatalSrc("Bad case, unexpected " << nodep->ctlType().ascii());
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScopeName* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
// For use under AstCCalls for dpiImports. ScopeNames under
|
|
|
|
|
// displays are handled in AstDisplay
|
|
|
|
|
if (!nodep->dpiExport()) {
|
|
|
|
|
// this is where the DPI import context scope is set
|
|
|
|
|
const string scope = nodep->scopeDpiName();
|
|
|
|
|
putbs("(&(vlSymsp->" + protect("__Vscope_" + scope) + "))");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSFormat* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
displayNode(nodep, nodep->fmtp()->scopeNamep(), nodep->fmtp()->text(),
|
|
|
|
|
nodep->fmtp()->exprsp(), false);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSFormatF* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
displayNode(nodep, nodep->scopeNamep(), nodep->text(), nodep->exprsp(), false);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFScanF* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
displayNode(nodep, nullptr, nodep->text(), nodep->exprsp(), true);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSScanF* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
displayNode(nodep, nullptr, nodep->text(), nodep->exprsp(), true);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstValuePlusArgs* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_VALUEPLUSARGS_IN");
|
|
|
|
|
emitIQW(nodep->outp());
|
|
|
|
|
puts("(");
|
|
|
|
|
puts(cvtToStr(nodep->outp()->widthMin()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
emitCvtPackStr(nodep->searchp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
putbs("");
|
|
|
|
|
iterateAndNextNull(nodep->outp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTestPlusArgs* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_TESTPLUSARGS_I(");
|
2022-07-11 12:21:35 +02:00
|
|
|
emitCvtPackStr(nodep->searchp());
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFError* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_FERROR_IN(");
|
|
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
putbs(", ");
|
|
|
|
|
iterateAndNextNull(nodep->strp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFGetS* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
checkMaxWords(nodep);
|
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkMaxWords(AstNode* nodep) {
|
2021-08-24 03:13:33 +02:00
|
|
|
if (nodep->widthWords() > VL_VALUE_STRING_MAX_WORDS) {
|
2021-06-24 18:35:12 +02:00
|
|
|
nodep->v3error(
|
|
|
|
|
"String of "
|
|
|
|
|
<< nodep->width()
|
2021-08-24 03:13:33 +02:00
|
|
|
<< " bits exceeds hardcoded limit VL_VALUE_STRING_MAX_WORDS in verilatedos.h");
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFOpen* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
puts(" = VL_FOPEN_NN(");
|
|
|
|
|
emitCvtPackStr(nodep->filenamep());
|
|
|
|
|
putbs(", ");
|
|
|
|
|
if (nodep->modep()->width() > 4 * 8)
|
|
|
|
|
nodep->modep()->v3error("$fopen mode should be <= 4 characters");
|
|
|
|
|
emitCvtPackStr(nodep->modep());
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFOpenMcd* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
puts(" = VL_FOPEN_MCD_N(");
|
|
|
|
|
emitCvtPackStr(nodep->filenamep());
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeReadWriteMem* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(nodep->cFuncPrefixp());
|
|
|
|
|
puts("N(");
|
|
|
|
|
puts(nodep->isHex() ? "true" : "false");
|
|
|
|
|
putbs(", ");
|
|
|
|
|
// Need real storage width
|
|
|
|
|
puts(cvtToStr(nodep->memp()->dtypep()->subDTypep()->widthMin()));
|
|
|
|
|
uint32_t array_lo = 0;
|
|
|
|
|
{
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarRef* const varrefp = VN_CAST(nodep->memp(), VarRef);
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!varrefp) {
|
|
|
|
|
nodep->v3error(nodep->verilogKwd() << " loading non-variable");
|
|
|
|
|
} else if (VN_IS(varrefp->varp()->dtypeSkipRefp(), AssocArrayDType)) {
|
|
|
|
|
// nodep->memp() below will when verilated code is compiled create a C++ template
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstUnpackArrayDType* const adtypep
|
2021-06-24 18:35:12 +02:00
|
|
|
= VN_CAST(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType)) {
|
|
|
|
|
putbs(", ");
|
|
|
|
|
puts(cvtToStr(varrefp->varp()->dtypep()->arrayUnpackedElements()));
|
|
|
|
|
array_lo = adtypep->lo();
|
|
|
|
|
putbs(", ");
|
|
|
|
|
puts(cvtToStr(array_lo));
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3error(nodep->verilogKwd()
|
|
|
|
|
<< " loading other than unpacked/associative-array variable");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
putbs(", ");
|
|
|
|
|
emitCvtPackStr(nodep->filenamep());
|
|
|
|
|
putbs(", ");
|
|
|
|
|
{
|
|
|
|
|
const bool need_ptr = !VN_IS(nodep->memp()->dtypep(), AssocArrayDType);
|
|
|
|
|
if (need_ptr) puts(" &(");
|
|
|
|
|
iterateAndNextNull(nodep->memp());
|
|
|
|
|
if (need_ptr) puts(")");
|
|
|
|
|
}
|
|
|
|
|
putbs(", ");
|
|
|
|
|
if (nodep->lsbp()) {
|
|
|
|
|
iterateAndNextNull(nodep->lsbp());
|
|
|
|
|
} else {
|
|
|
|
|
puts(cvtToStr(array_lo));
|
|
|
|
|
}
|
|
|
|
|
putbs(", ");
|
|
|
|
|
if (nodep->msbp()) {
|
|
|
|
|
iterateAndNextNull(nodep->msbp());
|
|
|
|
|
} else {
|
|
|
|
|
puts("~0ULL");
|
|
|
|
|
}
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFClose* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_FCLOSE_I(");
|
|
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
puts("); ");
|
|
|
|
|
iterateAndNextNull(nodep->filep()); // For safety, so user doesn't later WRITE with it.
|
|
|
|
|
puts(" = 0;\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFFlush* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!nodep->filep()) {
|
|
|
|
|
puts("Verilated::runFlushCallbacks();\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts("if (");
|
|
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
puts(") { VL_FFLUSH_I(");
|
|
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
puts("); }\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFSeek* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("(VL_FSEEK_I(");
|
|
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
puts(",");
|
|
|
|
|
iterateAndNextNull(nodep->offset());
|
|
|
|
|
puts(",");
|
|
|
|
|
iterateAndNextNull(nodep->operation());
|
|
|
|
|
puts(") == -1 ? -1 : 0)");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFTell* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_FTELL_I(");
|
|
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFRewind* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("(VL_FSEEK_I(");
|
|
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
puts(", 0, 0) == -1 ? -1 : 0)");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFRead* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_FREAD_I(");
|
|
|
|
|
puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width
|
|
|
|
|
putbs(",");
|
|
|
|
|
uint32_t array_lo = 0;
|
|
|
|
|
uint32_t array_size = 0;
|
|
|
|
|
{
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarRef* const varrefp = VN_CAST(nodep->memp(), VarRef);
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!varrefp) {
|
|
|
|
|
nodep->v3error(nodep->verilogKwd() << " loading non-variable");
|
|
|
|
|
} else if (VN_CAST(varrefp->varp()->dtypeSkipRefp(), BasicDType)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstUnpackArrayDType* const adtypep
|
2021-06-24 18:35:12 +02:00
|
|
|
= VN_CAST(varrefp->varp()->dtypeSkipRefp(), UnpackArrayDType)) {
|
|
|
|
|
array_lo = adtypep->lo();
|
|
|
|
|
array_size = adtypep->elementsConst();
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3error(nodep->verilogKwd()
|
|
|
|
|
<< " loading other than unpacked-array variable");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
puts(cvtToStr(array_lo));
|
|
|
|
|
putbs(",");
|
|
|
|
|
puts(cvtToStr(array_size));
|
|
|
|
|
putbs(", ");
|
|
|
|
|
puts("&(");
|
|
|
|
|
iterateAndNextNull(nodep->memp());
|
|
|
|
|
puts(")");
|
|
|
|
|
putbs(", ");
|
|
|
|
|
iterateAndNextNull(nodep->filep());
|
|
|
|
|
putbs(", ");
|
|
|
|
|
if (nodep->startp()) {
|
|
|
|
|
iterateAndNextNull(nodep->startp());
|
|
|
|
|
} else {
|
|
|
|
|
puts(cvtToStr(array_lo));
|
|
|
|
|
}
|
|
|
|
|
putbs(", ");
|
|
|
|
|
if (nodep->countp()) {
|
|
|
|
|
iterateAndNextNull(nodep->countp());
|
|
|
|
|
} else {
|
|
|
|
|
puts(cvtToStr(array_size));
|
|
|
|
|
}
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSysFuncAsTask* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!nodep->lhsp()->isWide()) puts("(void)");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
if (!nodep->lhsp()->isWide()) puts(";");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSystemT* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("(void)VL_SYSTEM_I");
|
|
|
|
|
emitIQW(nodep->lhsp());
|
|
|
|
|
puts("(");
|
|
|
|
|
if (nodep->lhsp()->isWide()) {
|
|
|
|
|
puts(cvtToStr(nodep->lhsp()->widthWords()));
|
|
|
|
|
putbs(", ");
|
|
|
|
|
}
|
|
|
|
|
checkMaxWords(nodep->lhsp());
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSystemF* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_SYSTEM_I");
|
|
|
|
|
emitIQW(nodep->lhsp());
|
|
|
|
|
puts("(");
|
|
|
|
|
if (nodep->lhsp()->isWide()) {
|
|
|
|
|
puts(cvtToStr(nodep->lhsp()->widthWords()));
|
|
|
|
|
putbs(", ");
|
|
|
|
|
}
|
|
|
|
|
checkMaxWords(nodep->lhsp());
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstStmtExpr* node) override {
|
|
|
|
|
iterate(node->exprp());
|
|
|
|
|
puts(";\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpBlock* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
nodep->labelNum(++m_labelNum);
|
|
|
|
|
puts("{\n"); // Make it visually obvious label jumps outside these
|
|
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
|
|
|
|
iterateAndNextNull(nodep->endStmtsp());
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpGo* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("goto __Vlabel" + cvtToStr(nodep->labelp()->blockp()->labelNum()) + ";\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstJumpLabel* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("__Vlabel" + cvtToStr(nodep->blockp()->labelNum()) + ": ;\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstWhile* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(nodep->precondsp());
|
|
|
|
|
puts("while (");
|
|
|
|
|
iterateAndNextNull(nodep->condp());
|
|
|
|
|
puts(") {\n");
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(nodep->incsp());
|
|
|
|
|
iterateAndNextNull(nodep->precondsp()); // Need to recompute before next loop
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeIf* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("if (");
|
|
|
|
|
if (!nodep->branchPred().unknown()) {
|
|
|
|
|
puts(nodep->branchPred().ascii());
|
|
|
|
|
puts("(");
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextNull(nodep->condp());
|
|
|
|
|
if (!nodep->branchPred().unknown()) puts(")");
|
|
|
|
|
puts(") {\n");
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->thensp());
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("}");
|
|
|
|
|
if (!nodep->elsesp()) {
|
|
|
|
|
puts("\n");
|
|
|
|
|
} else {
|
|
|
|
|
if (VN_IS(nodep->elsesp(), NodeIf) && !nodep->elsesp()->nextp()) {
|
|
|
|
|
puts(" else ");
|
|
|
|
|
iterateAndNextNull(nodep->elsesp());
|
|
|
|
|
} else {
|
|
|
|
|
puts(" else {\n");
|
|
|
|
|
iterateAndNextNull(nodep->elsesp());
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstExprStmt* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
// GCC allows compound statements in expressions, but this is not standard.
|
|
|
|
|
// So we use an immediate-evaluation lambda and comma operator
|
|
|
|
|
putbs("([&]() {\n");
|
|
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
|
|
|
|
puts("}(), ");
|
|
|
|
|
iterateAndNextNull(nodep->resultp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstStop* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_STOP_MT(");
|
|
|
|
|
putsQuoted(protect(nodep->fileline()->filename()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(cvtToStr(nodep->fileline()->lineno()));
|
|
|
|
|
puts(", \"\"");
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFinish* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_FINISH_MT(");
|
|
|
|
|
putsQuoted(protect(nodep->fileline()->filename()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(cvtToStr(nodep->fileline()->lineno()));
|
|
|
|
|
puts(", \"\");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPrintTimeScale* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_PRINTTIMESCALE(");
|
2022-11-17 00:17:24 +01:00
|
|
|
putsQuoted(protect(nodep->prettyName()));
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(", ");
|
|
|
|
|
putsQuoted(nodep->timeunit().ascii());
|
|
|
|
|
puts(", vlSymsp->_vm_contextp__);\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRand* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->seedp(), nullptr, nullptr);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTime* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_TIME_UNITED_Q(");
|
|
|
|
|
if (nodep->timeunit().isNone()) nodep->v3fatalSrc("$time has no units");
|
|
|
|
|
puts(cvtToStr(nodep->timeunit().multiplier()
|
|
|
|
|
/ v3Global.rootp()->timeprecision().multiplier()));
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTimeD* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_TIME_UNITED_D(");
|
|
|
|
|
if (nodep->timeunit().isNone()) nodep->v3fatalSrc("$realtime has no units");
|
|
|
|
|
puts(cvtToStr(nodep->timeunit().multiplier()
|
|
|
|
|
/ v3Global.rootp()->timeprecision().multiplier()));
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTimeFormat* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_TIMEFORMAT_IINI(");
|
|
|
|
|
iterateAndNextNull(nodep->unitsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(nodep->precisionp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
emitCvtPackStr(nodep->suffixp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(nodep->widthp());
|
|
|
|
|
puts(", vlSymsp->_vm_contextp__);\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeSimpleText* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
const string text = m_inUC && m_useSelfForThis
|
|
|
|
|
? VString::replaceWord(nodep->text(), "this", "vlSelf")
|
|
|
|
|
: nodep->text();
|
|
|
|
|
if (nodep->tracking() || m_trackText) {
|
|
|
|
|
puts(text);
|
|
|
|
|
} else {
|
|
|
|
|
ofp()->putsNoTracking(text);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTextBlock* nodep) override {
|
2021-10-22 18:36:58 +02:00
|
|
|
visit(static_cast<AstNodeSimpleText*>(nodep));
|
2021-06-24 18:35:12 +02:00
|
|
|
for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) {
|
|
|
|
|
iterate(childp);
|
|
|
|
|
if (nodep->commas() && childp->nextp()) puts(", ");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCStmt* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs("");
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->exprsp());
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstCExpr* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs("");
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->exprsp());
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstUCStmt* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
VL_RESTORER(m_inUC);
|
|
|
|
|
m_inUC = true;
|
|
|
|
|
putsDecoration(ifNoProtect("// $c statement at " + nodep->fileline()->ascii() + "\n"));
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->exprsp());
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstUCFunc* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
VL_RESTORER(m_inUC);
|
|
|
|
|
m_inUC = true;
|
|
|
|
|
puts("\n");
|
|
|
|
|
putsDecoration(ifNoProtect("// $c function at " + nodep->fileline()->ascii() + "\n"));
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->exprsp());
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Operators
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeTermop* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
emitOpName(nodep, nodep->emitC(), nullptr, nullptr, nullptr);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeUniop* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (nodep->emitCheckMaxWords()
|
|
|
|
|
&& (nodep->widthWords() > VL_MULS_MAX_WORDS
|
|
|
|
|
|| nodep->lhsp()->widthWords() > VL_MULS_MAX_WORDS)) {
|
|
|
|
|
nodep->v3warn(
|
|
|
|
|
E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: "
|
|
|
|
|
<< nodep->prettyOperatorName() << " operator of " << nodep->width()
|
|
|
|
|
<< " bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
|
|
|
|
|
}
|
|
|
|
|
if (emitSimpleOk(nodep)) {
|
|
|
|
|
putbs("(");
|
|
|
|
|
puts(nodep->emitSimpleOperator());
|
|
|
|
|
puts(" ");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
} else {
|
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nullptr, nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeBiop* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (nodep->emitCheckMaxWords() && nodep->widthWords() > VL_MULS_MAX_WORDS) {
|
|
|
|
|
nodep->v3warn(
|
|
|
|
|
E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: "
|
|
|
|
|
<< nodep->prettyOperatorName() << " operator of " << nodep->width()
|
|
|
|
|
<< " bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
|
|
|
|
|
}
|
|
|
|
|
if (emitSimpleOk(nodep)) {
|
|
|
|
|
putbs("(");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(" ");
|
|
|
|
|
putbs(nodep->emitSimpleOperator());
|
|
|
|
|
puts(" ");
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
} else {
|
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeTriop* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way");
|
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstRedXor* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (nodep->lhsp()->isWide()) {
|
2021-10-22 18:36:58 +02:00
|
|
|
visit(static_cast<AstNodeUniop*>(nodep));
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarRef* const vrefp = VN_CAST(nodep->lhsp(), VarRef);
|
2021-08-18 11:54:45 +02:00
|
|
|
const int widthPow2 = vrefp ? vrefp->varp()->dtypep()->widthPow2()
|
|
|
|
|
: nodep->lhsp()->dtypep()->widthPow2();
|
|
|
|
|
UASSERT_OBJ(widthPow2 > 1, nodep,
|
|
|
|
|
"Reduction over single bit value should have been folded");
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs("VL_REDXOR_");
|
2021-08-18 11:54:45 +02:00
|
|
|
puts(cvtToStr(widthPow2));
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("(");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCCast* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Extending a value of the same word width is just a NOP.
|
|
|
|
|
if (nodep->size() <= VL_IDATASIZE) {
|
|
|
|
|
puts("(IData)(");
|
|
|
|
|
} else {
|
|
|
|
|
puts("(QData)(");
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeCond* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Widths match up already, so we'll just use C++'s operator w/o any temps.
|
2022-09-15 20:43:56 +02:00
|
|
|
if (nodep->thenp()->isWide()) {
|
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->condp(), nodep->thenp(), nodep->elsep());
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
|
|
|
|
putbs("(");
|
|
|
|
|
iterateAndNextNull(nodep->condp());
|
|
|
|
|
putbs(" ? ");
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->thenp());
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs(" : ");
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->elsep());
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMemberSel* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(nodep->fromp());
|
|
|
|
|
putbs("->");
|
|
|
|
|
puts(nodep->varp()->nameProtect());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNullCheck* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("VL_NULL_CHECK(");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
putsQuoted(protect(nodep->fileline()->filename()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(cvtToStr(nodep->fileline()->lineno()));
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNewCopy* nodep) override {
|
2022-09-29 00:54:18 +02:00
|
|
|
puts("VL_NEW(" + prefixNameProtect(nodep->dtypep()) + ", ");
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("*"); // i.e. make into a reference
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSel* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Note ASSIGN checks for this on a LHS
|
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->fromp(), nodep->lsbp(), nodep->thsp());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstReplicate* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (nodep->lhsp()->widthMin() == 1 && !nodep->isWide()) {
|
2021-10-22 14:56:48 +02:00
|
|
|
UASSERT_OBJ((static_cast<int>(VN_AS(nodep->rhsp(), Const)->toUInt())
|
2021-06-24 18:35:12 +02:00
|
|
|
* nodep->lhsp()->widthMin())
|
|
|
|
|
== nodep->widthMin(),
|
|
|
|
|
nodep, "Replicate non-constant or width miscomputed");
|
|
|
|
|
puts("VL_REPLICATE_");
|
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
puts("OI(");
|
2021-11-28 19:44:16 +01:00
|
|
|
if (nodep->lhsp()) puts(cvtToStr(nodep->lhsp()->widthMin()));
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(",");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
} else {
|
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstStreamL* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Attempt to use a "fast" stream function for slice size = power of 2
|
|
|
|
|
if (!nodep->isWide()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const uint32_t isPow2 = VN_AS(nodep->rhsp(), Const)->num().countOnes() == 1;
|
|
|
|
|
const uint32_t sliceSize = VN_AS(nodep->rhsp(), Const)->toUInt();
|
2021-06-24 18:35:12 +02:00
|
|
|
if (isPow2 && sliceSize <= (nodep->isQuad() ? sizeof(uint64_t) : sizeof(uint32_t))) {
|
|
|
|
|
puts("VL_STREAML_FAST_");
|
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
emitIQW(nodep->lhsp());
|
|
|
|
|
puts("I(");
|
2021-11-28 19:44:16 +01:00
|
|
|
puts(cvtToStr(nodep->lhsp()->widthMin()));
|
|
|
|
|
puts(", ");
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(", ");
|
2021-11-26 23:55:36 +01:00
|
|
|
const uint32_t rd_log2 = V3Number::log2b(VN_AS(nodep->rhsp(), Const)->toUInt());
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(cvtToStr(rd_log2) + ")");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-28 19:44:16 +01:00
|
|
|
emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%lw, %P, %li, %ri)", nodep->lhsp(), nodep->rhsp(),
|
|
|
|
|
nullptr);
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCastDynamic* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs("VL_CAST_DYNAMIC(");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCountBits* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs("VL_COUNTBITS_");
|
|
|
|
|
emitIQW(nodep->lhsp());
|
|
|
|
|
puts("(");
|
|
|
|
|
puts(cvtToStr(nodep->lhsp()->widthMin()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
if (nodep->lhsp()->isWide()) {
|
|
|
|
|
puts(cvtToStr(nodep->lhsp()->widthWords())); // Note argument width, not node width
|
|
|
|
|
// (which is always 32)
|
|
|
|
|
puts(", ");
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(nodep->thsp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterateAndNextNull(nodep->fhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstInitItem* nodep) override { iterateChildren(nodep); }
|
2021-06-24 18:35:12 +02:00
|
|
|
// Terminals
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
const AstVar* const varp = nodep->varp();
|
2021-07-22 16:53:42 +02:00
|
|
|
const AstNodeModule* const varModp = EmitCParentModule::get(varp);
|
|
|
|
|
if (isConstPoolMod(varModp)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Reference to constant pool variable
|
|
|
|
|
puts(topClassName() + "__ConstPool__");
|
|
|
|
|
} else if (varp->isStatic()) {
|
|
|
|
|
// Access static variable via the containing class
|
2021-07-22 16:53:42 +02:00
|
|
|
puts(prefixNameProtect(varModp) + "::");
|
|
|
|
|
} else if (VN_IS(varModp, Class) && varModp != m_modp) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Superclass member reference
|
2021-07-22 16:53:42 +02:00
|
|
|
puts(prefixNameProtect(varModp) + "::");
|
2022-10-20 12:31:00 +02:00
|
|
|
} else if (varp->isIfaceRef()) {
|
|
|
|
|
puts(nodep->selfPointerProtect(m_useSelfForThis));
|
|
|
|
|
return;
|
2021-06-24 18:35:12 +02:00
|
|
|
} else if (!nodep->selfPointer().empty()) {
|
|
|
|
|
emitDereference(nodep->selfPointerProtect(m_useSelfForThis));
|
|
|
|
|
}
|
|
|
|
|
puts(nodep->varp()->nameProtect());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAddrOfCFunc* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Note: Can be thought to handle more, but this is all that is needed right now
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstCFunc* const funcp = nodep->funcp();
|
2021-06-24 18:35:12 +02:00
|
|
|
UASSERT_OBJ(funcp->isLoose(), nodep, "Cannot take address of non-loose method");
|
|
|
|
|
puts("&");
|
|
|
|
|
puts(funcNameProtect(funcp));
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConst* nodep) override {
|
2021-07-22 19:59:03 +02:00
|
|
|
if (m_emitConstInit) {
|
|
|
|
|
EmitCConstInit::visit(nodep);
|
|
|
|
|
} else if (nodep->isWide()) {
|
2021-06-24 18:35:12 +02:00
|
|
|
UASSERT_OBJ(m_wideTempRefp, nodep, "Wide Constant w/ no temp");
|
|
|
|
|
emitConstant(nodep, m_wideTempRefp, "");
|
2022-03-31 02:17:59 +02:00
|
|
|
m_wideTempRefp = nullptr; // We used it, fail if set it a second time
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
|
|
|
|
emitConstant(nodep, nullptr, "");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-14 14:55:55 +02:00
|
|
|
void visit(AstThisRef* nodep) override {
|
|
|
|
|
putbs(nodep->dtypep()->cType("", false, false));
|
|
|
|
|
puts("{");
|
|
|
|
|
puts(m_useSelfForThis ? "vlSelf" : "this");
|
|
|
|
|
puts("}");
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
//
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMTaskBody* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
VL_RESTORER(m_useSelfForThis);
|
|
|
|
|
m_useSelfForThis = true;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConsAssoc* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs(nodep->dtypep()->cType("", false, false));
|
|
|
|
|
puts("()");
|
|
|
|
|
if (nodep->defaultp()) {
|
|
|
|
|
putbs(".setDefault(");
|
|
|
|
|
iterateAndNextNull(nodep->defaultp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSetAssoc* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
putbs(".set(");
|
|
|
|
|
iterateAndNextNull(nodep->keyp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
putbs("");
|
|
|
|
|
iterateAndNextNull(nodep->valuep());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConsWildcard* nodep) override {
|
2022-07-20 15:01:36 +02:00
|
|
|
putbs(nodep->dtypep()->cType("", false, false));
|
|
|
|
|
puts("()");
|
|
|
|
|
if (nodep->defaultp()) {
|
|
|
|
|
putbs(".setDefault(");
|
|
|
|
|
iterateAndNextNull(nodep->defaultp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSetWildcard* nodep) override {
|
2022-07-20 15:01:36 +02:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
putbs(".set(");
|
|
|
|
|
iterateAndNextNull(nodep->keyp());
|
|
|
|
|
puts(", ");
|
|
|
|
|
putbs("");
|
|
|
|
|
iterateAndNextNull(nodep->valuep());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConsDynArray* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs(nodep->dtypep()->cType("", false, false));
|
|
|
|
|
if (!nodep->lhsp()) {
|
|
|
|
|
puts("()");
|
|
|
|
|
} else {
|
|
|
|
|
puts("::cons(");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
if (nodep->rhsp()) {
|
|
|
|
|
puts(", ");
|
|
|
|
|
putbs("");
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConsQueue* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs(nodep->dtypep()->cType("", false, false));
|
|
|
|
|
if (!nodep->lhsp()) {
|
|
|
|
|
puts("()");
|
|
|
|
|
} else {
|
|
|
|
|
puts("::cons(");
|
|
|
|
|
iterateAndNextNull(nodep->lhsp());
|
|
|
|
|
if (nodep->rhsp()) {
|
|
|
|
|
puts(", ");
|
|
|
|
|
putbs("");
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextNull(nodep->rhsp());
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCReset* nodep) override {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const varp = nodep->varrefp()->varp();
|
2021-06-24 18:35:12 +02:00
|
|
|
emitVarReset(varp);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstExecGraph* nodep) override {
|
2022-04-10 12:37:41 +02:00
|
|
|
// The location of the AstExecGraph within the containing AstCFunc is where we want to
|
|
|
|
|
// invoke the graph and wait for it to complete. Emitting the children does just that.
|
|
|
|
|
UASSERT_OBJ(!nodep->mTaskBodiesp(), nodep, "These should have been lowered");
|
2021-06-24 18:35:12 +02:00
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Default
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(string("\n???? // ") + nodep->prettyTypeName() + "\n");
|
|
|
|
|
iterateChildren(nodep);
|
2021-07-13 22:17:41 +02:00
|
|
|
// LCOV_EXCL_START
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!v3Global.opt.lintOnly()) { // An internal problem, so suppress
|
|
|
|
|
nodep->v3fatalSrc("Unknown node type reached emitter: " << nodep->prettyTypeName());
|
|
|
|
|
}
|
2021-07-13 22:17:41 +02:00
|
|
|
// LCOV_EXCL_STOP
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EmitCFunc()
|
2022-01-01 17:46:49 +01:00
|
|
|
: m_lazyDecls(*this) {}
|
2021-06-24 18:35:12 +02:00
|
|
|
EmitCFunc(AstNode* nodep, V3OutCFile* ofp, bool trackText = false)
|
|
|
|
|
: EmitCFunc{} {
|
|
|
|
|
m_ofp = ofp;
|
|
|
|
|
m_trackText = trackText;
|
|
|
|
|
iterate(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~EmitCFunc() override = default;
|
2021-06-24 18:35:12 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif // guard
|