1043 lines
36 KiB
C++
1043 lines
36 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Emit Verilog from tree
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2004-2025 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
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "V3PchAstMT.h"
|
|
|
|
#include "V3EmitV.h"
|
|
|
|
#include "V3EmitCBase.h"
|
|
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
// ######################################################################
|
|
// Emit statements and expressions
|
|
|
|
class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst {
|
|
// STATE - across all visitors
|
|
const bool m_suppressUnknown = false;
|
|
|
|
// STATE - for current visit position (use VL_RESTORER)
|
|
AstSenTree* m_sensesp; // Domain for printing one a ALWAYS under a ACTIVE
|
|
bool m_suppressSemi = false; // Non-statement, don't print ;
|
|
bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars
|
|
bool m_arrayPost = false; // Print array information that goes after identifier (vs after)
|
|
std::deque<AstNodeArrayDType*> m_packedps; // Packed arrays to print with BasicDType
|
|
std::unordered_map<AstJumpBlock*, size_t> m_labelNumbers; // Label numbers for JumpBlocks
|
|
|
|
// METHODS
|
|
virtual void puts(const string& str) = 0;
|
|
virtual void putbs(const string& str) = 0;
|
|
virtual void putfs(AstNode* nodep, const string& str) = 0; // Fileline and node %% mark
|
|
virtual void putqs(AstNode* nodep, const string& str) = 0; // Fileline quiet w/o %% mark
|
|
virtual void putsNoTracking(const string& str) = 0;
|
|
virtual void putsQuoted(const string& str) {
|
|
// Quote \ and " for use inside C programs
|
|
// Don't use to quote a filename for #include - #include doesn't \ escape.
|
|
// Duplicate in V3File - here so we can print to string
|
|
putsNoTracking("\"");
|
|
putsNoTracking(V3OutFormatter::quoteNameControls(str));
|
|
putsNoTracking("\"");
|
|
}
|
|
|
|
void iterateAndCommaConstNull(AstNode* nodep) {
|
|
for (; nodep; nodep = nodep->nextp()) {
|
|
iterateConst(nodep);
|
|
if (nodep->nextp()) puts(", ");
|
|
}
|
|
}
|
|
void emitPacked() {
|
|
for (AstNodeArrayDType* packedp : m_packedps) {
|
|
puts(" ");
|
|
iterateConstNull(packedp->rangep());
|
|
}
|
|
m_packedps.clear();
|
|
}
|
|
|
|
// VISITORS
|
|
void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); }
|
|
void visit(AstNodeModule* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd() + " " + prefixNameProtect(nodep) + ";\n");
|
|
iterateChildrenConst(nodep);
|
|
putqs(nodep, "end" + nodep->verilogKwd() + "\n");
|
|
}
|
|
void visit(AstPort* nodep) override {}
|
|
void visit(AstNodeFTask* nodep) override {
|
|
const bool func = nodep->isFunction() || nodep->name() == "new";
|
|
putfs(nodep, func ? "function" : "task");
|
|
puts(" ");
|
|
puts(nodep->prettyName());
|
|
puts(";\n");
|
|
// Only putfs the first time for each visitor; later for same node is putqs
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
putfs(nodep, func ? "endfunction\n" : "endtask\n");
|
|
}
|
|
|
|
void visit(AstBegin* nodep) override {
|
|
if (nodep->name() == "") {
|
|
putbs("begin\n");
|
|
} else {
|
|
putbs("begin : " + nodep->name() + "\n");
|
|
}
|
|
iterateChildrenConst(nodep);
|
|
puts("end\n");
|
|
}
|
|
void visit(AstFork* nodep) override {
|
|
if (nodep->name() == "") {
|
|
putbs("fork\n");
|
|
} else {
|
|
putbs("fork : " + nodep->name() + "\n");
|
|
}
|
|
iterateChildrenConst(nodep);
|
|
puts(nodep->joinType().verilogKwd());
|
|
puts("\n");
|
|
}
|
|
void visit(AstFinal* nodep) override {
|
|
putfs(nodep, "final begin\n");
|
|
iterateChildrenConst(nodep);
|
|
putqs(nodep, "end\n");
|
|
}
|
|
void visit(AstInitial* nodep) override {
|
|
putfs(nodep, "initial begin\n");
|
|
iterateChildrenConst(nodep);
|
|
putqs(nodep, "end\n");
|
|
}
|
|
void visit(AstInitialAutomatic* nodep) override { iterateChildrenConst(nodep); }
|
|
void visit(AstInitialStatic* nodep) override { iterateChildrenConst(nodep); }
|
|
void visit(AstAlways* nodep) override {
|
|
putfs(nodep, "always ");
|
|
if (m_sensesp) {
|
|
iterateAndNextConstNull(m_sensesp);
|
|
} // In active
|
|
else {
|
|
iterateAndNextConstNull(nodep->sensesp());
|
|
}
|
|
putbs(" begin\n");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
putqs(nodep, "end\n");
|
|
}
|
|
void visit(AstAlwaysPublic* nodep) override {
|
|
putfs(nodep, "/*verilator public_flat_rw ");
|
|
if (m_sensesp) {
|
|
iterateAndNextConstNull(m_sensesp);
|
|
} // In active
|
|
else {
|
|
iterateAndNextConstNull(nodep->sensesp());
|
|
}
|
|
putqs(nodep, " ");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
putqs(nodep, "*/\n");
|
|
}
|
|
void visit(AstNodeAssign* nodep) override {
|
|
if (VN_IS(nodep, AssignForce)) puts("force ");
|
|
iterateAndNextConstNull(nodep->lhsp());
|
|
putfs(nodep, " " + nodep->verilogKwd() + " ");
|
|
iterateAndNextConstNull(nodep->rhsp());
|
|
if (!m_suppressSemi) puts(";\n");
|
|
}
|
|
void visit(AstAssignDly* nodep) override {
|
|
iterateAndNextConstNull(nodep->lhsp());
|
|
putfs(nodep, " <= ");
|
|
iterateAndNextConstNull(nodep->rhsp());
|
|
puts(";\n");
|
|
}
|
|
void visit(AstAssignAlias* nodep) override {
|
|
putbs("alias ");
|
|
iterateAndNextConstNull(nodep->lhsp());
|
|
putfs(nodep, " = ");
|
|
iterateAndNextConstNull(nodep->rhsp());
|
|
if (!m_suppressSemi) puts(";\n");
|
|
}
|
|
void visit(AstAssignW* nodep) override {
|
|
putfs(nodep, "assign ");
|
|
iterateAndNextConstNull(nodep->lhsp());
|
|
putbs(" = ");
|
|
iterateAndNextConstNull(nodep->rhsp());
|
|
if (!m_suppressSemi) puts(";\n");
|
|
}
|
|
void visit(AstRelease* nodep) override {
|
|
puts("release ");
|
|
iterateAndNextConstNull(nodep->lhsp());
|
|
if (!m_suppressSemi) puts(";\n");
|
|
}
|
|
void visit(AstBreak*) override {
|
|
putbs("break");
|
|
if (!m_suppressSemi) puts(";\n");
|
|
}
|
|
void visit(AstSenTree* nodep) override {
|
|
// AstSenItem is called for dumping in isolation by V3Order
|
|
putfs(nodep, "@(");
|
|
for (AstNode* expp = nodep->sensesp(); expp; expp = expp->nextp()) {
|
|
iterateConst(expp);
|
|
if (expp->nextp()) putqs(expp->nextp(), " or ");
|
|
}
|
|
puts(")");
|
|
}
|
|
void visit(AstSenItem* nodep) override {
|
|
putfs(nodep, "");
|
|
if (nodep->edgeType() != VEdgeType::ET_CHANGED) puts(nodep->edgeType().verilogKwd());
|
|
if (nodep->sensp()) puts(" ");
|
|
iterateChildrenConst(nodep);
|
|
}
|
|
void visit(AstNodeCase* nodep) override {
|
|
putfs(nodep, "");
|
|
if (const AstCase* const casep = VN_CAST(nodep, Case)) {
|
|
if (casep->priorityPragma()) puts("priority ");
|
|
if (casep->uniquePragma()) puts("unique ");
|
|
if (casep->unique0Pragma()) puts("unique0 ");
|
|
}
|
|
puts(nodep->verilogKwd());
|
|
puts(" (");
|
|
iterateAndNextConstNull(nodep->exprp());
|
|
puts(")\n");
|
|
if (const AstCase* const casep = VN_CAST(nodep, Case)) {
|
|
if (casep->fullPragma() || casep->parallelPragma()) {
|
|
puts(" // synopsys");
|
|
if (casep->fullPragma()) puts(" full_case");
|
|
if (casep->parallelPragma()) puts(" parallel_case");
|
|
}
|
|
}
|
|
iterateAndNextConstNull(nodep->itemsp());
|
|
putqs(nodep, "endcase\n");
|
|
}
|
|
void visit(AstCaseItem* nodep) override {
|
|
if (nodep->condsp()) {
|
|
iterateAndNextConstNull(nodep->condsp());
|
|
} else {
|
|
putbs("default");
|
|
}
|
|
putfs(nodep, ": begin ");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
putqs(nodep, "end\n");
|
|
}
|
|
void visit(AstComment* nodep) override {
|
|
puts("// "s + nodep->name() + "\n");
|
|
iterateChildrenConst(nodep);
|
|
}
|
|
void visit(AstContinue*) override {
|
|
putbs("continue");
|
|
if (!m_suppressSemi) puts(";\n");
|
|
}
|
|
void visit(AstCoverDecl*) override {} // N/A
|
|
void visit(AstCoverInc*) override {} // N/A
|
|
void visit(AstCoverToggle*) override {} // N/A
|
|
|
|
void visit(AstTestPlusArgs* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
iterateChildrenConst(nodep);
|
|
puts(")");
|
|
}
|
|
void visit(AstValuePlusArgs* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
iterateChildrenConst(nodep);
|
|
puts(")");
|
|
}
|
|
void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, const string& text,
|
|
AstNode* exprsp) {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
if (fileOrStrgp) {
|
|
iterateConstNull(fileOrStrgp);
|
|
putbs(", ");
|
|
}
|
|
putsQuoted(text);
|
|
for (AstNode* expp = exprsp; expp; expp = expp->nextp()) {
|
|
puts(", ");
|
|
iterateConstNull(expp);
|
|
}
|
|
puts(");\n");
|
|
}
|
|
void visit(AstDisable* nodep) override { putbs("disable " + nodep->name() + ";\n"); }
|
|
void visit(AstDisplay* nodep) override {
|
|
visitNodeDisplay(nodep, nodep->filep(), nodep->fmtp()->text(), nodep->fmtp()->exprsp());
|
|
}
|
|
void visit(AstElabDisplay* nodep) override {
|
|
visitNodeDisplay(nodep, nullptr, nodep->fmtp()->text(), nodep->fmtp()->exprsp());
|
|
}
|
|
void visit(AstFScanF* nodep) override {
|
|
visitNodeDisplay(nodep, nodep->filep(), nodep->text(), nodep->exprsp());
|
|
}
|
|
void visit(AstSScanF* nodep) override {
|
|
visitNodeDisplay(nodep, nodep->fromp(), nodep->text(), nodep->exprsp());
|
|
}
|
|
void visit(AstSFormat* nodep) override {
|
|
visitNodeDisplay(nodep, nodep->lhsp(), nodep->fmtp()->text(), nodep->fmtp()->exprsp());
|
|
}
|
|
void visit(AstSFormatF* nodep) override {
|
|
visitNodeDisplay(nodep, nullptr, nodep->text(), nodep->exprsp());
|
|
}
|
|
void visit(AstFOpen* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
iterateAndNextConstNull(nodep->filenamep());
|
|
putbs(", ");
|
|
iterateAndNextConstNull(nodep->modep());
|
|
puts(");\n");
|
|
}
|
|
void visit(AstFOpenMcd* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
iterateAndNextConstNull(nodep->filenamep());
|
|
puts(");\n");
|
|
}
|
|
void visit(AstFClose* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
if (nodep->filep()) iterateAndNextConstNull(nodep->filep());
|
|
puts(");\n");
|
|
}
|
|
void visit(AstFFlush* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
if (nodep->filep()) iterateAndNextConstNull(nodep->filep());
|
|
puts(");\n");
|
|
}
|
|
void visit(AstJumpBlock* nodep) override {
|
|
// Allocate label number
|
|
const size_t n = m_labelNumbers.size();
|
|
const bool newEntry = m_labelNumbers.emplace(nodep, n).second;
|
|
UASSERT_OBJ(newEntry, nodep, "AstJumpBlock visited twide");
|
|
// Emit
|
|
putbs("begin : label" + std::to_string(n) + "\n");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
puts("end\n");
|
|
}
|
|
void visit(AstJumpGo* nodep) override {
|
|
// Retrieve target label number - Sometimes EmitV is used by debug code,
|
|
// so allow printing with an unknown target
|
|
const auto it = m_labelNumbers.find(nodep->blockp());
|
|
const std::string label
|
|
= it != m_labelNumbers.end() ? "label" + std::to_string(it->second) : "<UNKNOWN>";
|
|
putbs("disable " + label + ";\n");
|
|
}
|
|
void visit(AstNodeReadWriteMem* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
if (nodep->filenamep()) iterateAndNextConstNull(nodep->filenamep());
|
|
putbs(", ");
|
|
if (nodep->memp()) iterateAndNextConstNull(nodep->memp());
|
|
if (nodep->lsbp()) {
|
|
putbs(", ");
|
|
iterateAndNextConstNull(nodep->lsbp());
|
|
}
|
|
if (nodep->msbp()) {
|
|
putbs(", ");
|
|
iterateAndNextConstNull(nodep->msbp());
|
|
}
|
|
puts(");\n");
|
|
}
|
|
void visit(AstSysFuncAsTask* nodep) override {
|
|
iterateAndNextConstNull(nodep->lhsp());
|
|
puts(";\n");
|
|
}
|
|
void visit(AstSysIgnore* nodep) override {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
putbs("(");
|
|
iterateAndNextConstNull(nodep->exprsp());
|
|
puts(");\n");
|
|
}
|
|
void visit(AstNodeFor* nodep) override {
|
|
putfs(nodep, "for (");
|
|
{
|
|
VL_RESTORER(m_suppressSemi);
|
|
m_suppressSemi = true;
|
|
iterateAndNextConstNull(nodep->initsp());
|
|
puts(";");
|
|
iterateAndNextConstNull(nodep->condp());
|
|
puts(";");
|
|
iterateAndNextConstNull(nodep->incsp());
|
|
}
|
|
puts(") begin\n");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
putqs(nodep, "end\n");
|
|
}
|
|
void visit(AstRepeat* nodep) override {
|
|
putfs(nodep, "repeat (");
|
|
iterateAndNextConstNull(nodep->countp());
|
|
puts(") begin\n");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
putfs(nodep, "end\n");
|
|
}
|
|
void visit(AstWhile* nodep) override {
|
|
putfs(nodep, "while (");
|
|
iterateAndNextConstNull(nodep->condp());
|
|
puts(") begin\n");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
iterateAndNextConstNull(nodep->incsp());
|
|
putfs(nodep, "end\n");
|
|
}
|
|
void visit(AstNodeIf* nodep) override {
|
|
putfs(nodep, "");
|
|
if (const AstIf* const ifp = VN_CAST(nodep, If)) {
|
|
if (ifp->priorityPragma()) puts("priority ");
|
|
if (ifp->uniquePragma()) puts("unique ");
|
|
if (ifp->unique0Pragma()) puts("unique0 ");
|
|
}
|
|
puts("if (");
|
|
iterateAndNextConstNull(nodep->condp());
|
|
puts(") begin\n");
|
|
iterateAndNextConstNull(nodep->thensp());
|
|
if (nodep->elsesp()) {
|
|
putqs(nodep, "end\n");
|
|
putqs(nodep, "else begin\n");
|
|
iterateAndNextConstNull(nodep->elsesp());
|
|
}
|
|
putqs(nodep, "end\n");
|
|
}
|
|
void visit(AstPast* nodep) override {
|
|
putfs(nodep, "$past(");
|
|
iterateAndNextConstNull(nodep->exprp());
|
|
if (nodep->ticksp() || nodep->sentreep()) {
|
|
puts(", ");
|
|
iterateAndNextConstNull(nodep->ticksp());
|
|
if (nodep->sentreep()) {
|
|
puts(", ");
|
|
iterateAndNextConstNull(nodep->sentreep());
|
|
}
|
|
}
|
|
puts(")");
|
|
}
|
|
void visit(AstSampled* nodep) override {
|
|
putfs(nodep, "$sampled(");
|
|
iterateAndNextConstNull(nodep->exprp());
|
|
puts(")");
|
|
}
|
|
void visit(AstRose* nodep) override {
|
|
putfs(nodep, "$rose(");
|
|
iterateAndNextConstNull(nodep->exprp());
|
|
if (nodep->sentreep()) {
|
|
puts(", ");
|
|
iterateAndNextConstNull(nodep->sentreep());
|
|
}
|
|
puts(")");
|
|
}
|
|
void visit(AstFell* nodep) override {
|
|
putfs(nodep, "$fell(");
|
|
iterateAndNextConstNull(nodep->exprp());
|
|
if (nodep->sentreep()) {
|
|
puts(", ");
|
|
iterateAndNextConstNull(nodep->sentreep());
|
|
}
|
|
puts(")");
|
|
}
|
|
void visit(AstStable* nodep) override {
|
|
putfs(nodep, "$stable(");
|
|
iterateAndNextConstNull(nodep->exprp());
|
|
if (nodep->sentreep()) {
|
|
puts(", ");
|
|
iterateAndNextConstNull(nodep->sentreep());
|
|
}
|
|
puts(")");
|
|
}
|
|
void visit(AstReturn* nodep) override {
|
|
putfs(nodep, "return ");
|
|
iterateAndNextConstNull(nodep->lhsp());
|
|
puts(";\n");
|
|
}
|
|
void visit(AstStop* nodep) override {
|
|
emitVerilogFormat(nodep, nodep->emitVerilog());
|
|
puts(";\n");
|
|
}
|
|
void visit(AstFinish* nodep) override { putfs(nodep, "$finish;\n"); }
|
|
void visit(AstStmtExpr* nodep) override {
|
|
iterateConst(nodep->exprp());
|
|
puts(";\n");
|
|
}
|
|
void visit(AstNodeSimpleText* nodep) override {
|
|
if (nodep->tracking() || m_trackText) {
|
|
puts(nodep->text());
|
|
} else {
|
|
putsNoTracking(nodep->text());
|
|
}
|
|
}
|
|
void visit(AstTextBlock* nodep) override {
|
|
visit(static_cast<AstNodeSimpleText*>(nodep));
|
|
VL_RESTORER(m_suppressVarSemi);
|
|
m_suppressVarSemi = nodep->commas();
|
|
for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) {
|
|
iterateConst(childp);
|
|
if (nodep->commas() && childp->nextp()) puts(", ");
|
|
}
|
|
}
|
|
void visit(AstScopeName* nodep) override {}
|
|
void visit(AstCStmt* nodep) override {
|
|
putfs(nodep, "$_CSTMT(");
|
|
iterateAndCommaConstNull(nodep->exprsp());
|
|
puts(");\n");
|
|
}
|
|
void visit(AstCExpr* nodep) override {
|
|
putfs(nodep, "$_CEXPR(");
|
|
iterateAndCommaConstNull(nodep->exprsp());
|
|
puts(")");
|
|
}
|
|
void visit(AstUCStmt* nodep) override {
|
|
putfs(nodep, "$c(");
|
|
iterateAndCommaConstNull(nodep->exprsp());
|
|
puts(");\n");
|
|
}
|
|
void visit(AstUCFunc* nodep) override {
|
|
putfs(nodep, "$c(");
|
|
iterateAndNextConstNull(nodep->exprsp());
|
|
puts(")");
|
|
}
|
|
void visit(AstExprStmt* nodep) override {
|
|
putfs(nodep, "$_EXPRSTMT(\n");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
putbs(", ");
|
|
puts(");\n");
|
|
}
|
|
|
|
void visit(AstCMethodHard* nodep) override {
|
|
iterateConst(nodep->fromp());
|
|
puts("." + nodep->name() + "(");
|
|
iterateAndCommaConstNull(nodep->pinsp());
|
|
puts(")");
|
|
}
|
|
void visit(AstCMethodCall* nodep) override {
|
|
iterateConst(nodep->fromp());
|
|
puts("." + nodep->name() + "(");
|
|
iterateAndCommaConstNull(nodep->argsp());
|
|
puts(")");
|
|
}
|
|
|
|
// Operators
|
|
virtual void emitVerilogFormat(AstNode* nodep, const string& format, AstNode* lhsp = nullptr,
|
|
AstNode* const rhsp = nullptr, AstNode* thsp = nullptr,
|
|
AstNode* fhsp = nullptr) {
|
|
// Look at emitVerilog() format for term/uni/dual/triops,
|
|
// and write out appropriate text.
|
|
// %f Potential fileline-if-change and line break
|
|
// %l lhsp - if appropriate
|
|
// %r rhsp - if appropriate
|
|
// %t thsp - if appropriate
|
|
// %o fhsp - if appropriate
|
|
// %d dtypep - if appropriate
|
|
// %k Potential line break
|
|
bool inPct = false;
|
|
putbs("");
|
|
for (const char c : format) {
|
|
if (!inPct && c == '%') {
|
|
inPct = true;
|
|
} else if (!inPct) { // Normal text
|
|
string s;
|
|
s += c;
|
|
puts(s);
|
|
} else { // Format character
|
|
inPct = false;
|
|
switch (c) {
|
|
case '%': puts("%"); break;
|
|
case 'f': putfs(nodep, ""); break;
|
|
case 'k': putbs(""); break;
|
|
case 'l': {
|
|
UASSERT_OBJ(lhsp, nodep, "emitVerilog() references undef node");
|
|
iterateAndNextConstNull(lhsp);
|
|
break;
|
|
}
|
|
case 'r': {
|
|
UASSERT_OBJ(rhsp, nodep, "emitVerilog() references undef node");
|
|
iterateAndNextConstNull(rhsp);
|
|
break;
|
|
}
|
|
case 't': {
|
|
UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node");
|
|
iterateAndNextConstNull(thsp);
|
|
break;
|
|
}
|
|
case 'o': {
|
|
UASSERT_OBJ(thsp, nodep, "emitVerilog() references undef node");
|
|
iterateAndNextConstNull(fhsp);
|
|
break;
|
|
}
|
|
case 'd': {
|
|
UASSERT_OBJ(nodep->dtypep(), nodep, "emitVerilog() references undef node");
|
|
iterateAndNextConstNull(nodep->dtypep());
|
|
break;
|
|
}
|
|
default: nodep->v3fatalSrc("Unknown emitVerilog format code: %" << c); break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void visit(AstNodeTermop* nodep) override { emitVerilogFormat(nodep, nodep->emitVerilog()); }
|
|
void visit(AstNodeUniop* nodep) override {
|
|
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp());
|
|
}
|
|
void visit(AstNodeBiop* nodep) override {
|
|
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp());
|
|
}
|
|
void visit(AstNodeTriop* nodep) override {
|
|
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->lhsp(), nodep->rhsp(),
|
|
nodep->thsp());
|
|
}
|
|
void visit(AstMemberSel* nodep) override {
|
|
iterateConst(nodep->fromp());
|
|
puts(".");
|
|
puts(nodep->prettyName());
|
|
}
|
|
void visit(AstStructSel* nodep) override {
|
|
iterateConst(nodep->fromp());
|
|
puts(".");
|
|
puts(nodep->prettyName());
|
|
}
|
|
void visit(AstAttrOf* nodep) override {
|
|
putfs(nodep, "$_ATTROF(");
|
|
iterateAndNextConstNull(nodep->fromp());
|
|
if (nodep->dimp()) {
|
|
putbs(", ");
|
|
iterateAndNextConstNull(nodep->dimp());
|
|
}
|
|
puts(")");
|
|
}
|
|
void visit(AstInitArray* nodep) override {
|
|
putfs(nodep, "'{");
|
|
int comma = 0;
|
|
const auto& mapr = nodep->map();
|
|
for (const auto& itr : mapr) {
|
|
if (comma++) putbs(", ");
|
|
puts(cvtToStr(itr.first));
|
|
puts(":");
|
|
AstNode* const valuep = itr.second->valuep();
|
|
iterateConst(valuep);
|
|
}
|
|
puts("}");
|
|
}
|
|
void visit(AstNodeCond* nodep) override {
|
|
putbs("(");
|
|
iterateAndNextConstNull(nodep->condp());
|
|
putfs(nodep, " ? ");
|
|
iterateAndNextConstNull(nodep->thenp());
|
|
putbs(" : ");
|
|
iterateAndNextConstNull(nodep->elsep());
|
|
puts(")");
|
|
}
|
|
void visit(AstRange* nodep) override {
|
|
puts("[");
|
|
if (VN_IS(nodep->leftp(), Const) && VN_IS(nodep->rightp(), Const)) {
|
|
// Looks nicer if we print [1:0] rather than [32'sh1:32sh0]
|
|
puts(cvtToStr(nodep->leftConst()));
|
|
puts(":");
|
|
puts(cvtToStr(nodep->rightConst()));
|
|
} else {
|
|
iterateAndNextConstNull(nodep->leftp());
|
|
puts(":");
|
|
iterateAndNextConstNull(nodep->rightp());
|
|
}
|
|
puts("]");
|
|
}
|
|
void visit(AstRand* nodep) override {
|
|
emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->seedp());
|
|
}
|
|
void visit(AstSel* nodep) override {
|
|
iterateAndNextConstNull(nodep->fromp());
|
|
int offset = 0;
|
|
AstNodeDType* const dtypep = nodep->fromp()->dtypep();
|
|
if (VN_IS(dtypep, BasicDType)) {
|
|
AstBasicDType* const basicDtypep = VN_AS(dtypep, BasicDType);
|
|
offset = basicDtypep->lo();
|
|
}
|
|
puts("[");
|
|
if (VN_IS(nodep->lsbp(), Const)) {
|
|
if (nodep->widthConst() == 1) {
|
|
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt() + offset));
|
|
} else {
|
|
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt() + nodep->widthConst() + offset
|
|
- 1));
|
|
puts(":");
|
|
puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt() + offset));
|
|
}
|
|
} else {
|
|
iterateAndNextConstNull(nodep->lsbp());
|
|
if (offset != 0) {
|
|
puts(" + ");
|
|
puts(cvtToStr(offset));
|
|
}
|
|
putfs(nodep, "+:");
|
|
puts(cvtToStr(nodep->widthConst()));
|
|
puts("]");
|
|
}
|
|
puts("]");
|
|
}
|
|
void visit(AstSliceSel* nodep) override {
|
|
iterateAndNextConstNull(nodep->fromp());
|
|
puts(cvtToStr(nodep->declRange()));
|
|
}
|
|
void visit(AstThisRef* nodep) override { puts("this"); }
|
|
void visit(AstTypedef* nodep) override {
|
|
putfs(nodep, "typedef ");
|
|
iterateConstNull(nodep->subDTypep());
|
|
puts(" ");
|
|
puts(nodep->prettyName());
|
|
puts(";\n");
|
|
}
|
|
void visit(AstAssocArrayDType* nodep) override {
|
|
if (!m_arrayPost) {
|
|
iterateConst(nodep->subDTypep());
|
|
} else {
|
|
VL_RESTORER(m_arrayPost);
|
|
m_arrayPost = false;
|
|
puts("[");
|
|
iterateConst(nodep->keyDTypep());
|
|
puts("]");
|
|
m_arrayPost = true;
|
|
iterateConst(nodep->subDTypep()); // For post's key
|
|
}
|
|
}
|
|
void visit(AstBasicDType* nodep) override {
|
|
if (m_arrayPost) return;
|
|
putfs(nodep, nodep->prettyName());
|
|
if (nodep->isSigned() && !nodep->keyword().isDouble()) putfs(nodep, " signed");
|
|
// Do not emit ranges for integer atoms.
|
|
if (nodep->keyword().isIntNumeric() && !nodep->keyword().isBitLogic()) return;
|
|
emitPacked();
|
|
if (nodep->rangep()) {
|
|
puts(" ");
|
|
iterateAndNextConstNull(nodep->rangep());
|
|
puts(" ");
|
|
} else if (nodep->isRanged()) {
|
|
puts(" [");
|
|
puts(cvtToStr(nodep->hi()));
|
|
puts(":");
|
|
puts(cvtToStr(nodep->lo()));
|
|
puts("] ");
|
|
}
|
|
}
|
|
void visit(AstConstDType* nodep) override {
|
|
if (m_arrayPost) return;
|
|
putfs(nodep, "const ");
|
|
iterateConst(nodep->subDTypep());
|
|
}
|
|
void visit(AstDynArrayDType* nodep) override {
|
|
if (!m_arrayPost) {
|
|
iterateConst(nodep->subDTypep());
|
|
} else {
|
|
puts("[]");
|
|
iterateConst(nodep->subDTypep()); // For post's key
|
|
}
|
|
}
|
|
void visit(AstEnumDType* nodep) override {
|
|
if (m_arrayPost) return;
|
|
putfs(nodep, "enum ");
|
|
iterateConst(nodep->subDTypep());
|
|
puts("{\n");
|
|
iterateAndNextConstNull(nodep->itemsp());
|
|
puts("}");
|
|
}
|
|
void visit(AstEnumItemRef* nodep) override {
|
|
if (AstNodeModule* const classOrPackagep = nodep->classOrPackagep()) {
|
|
putfs(nodep, classOrPackagep->prettyName());
|
|
puts("::");
|
|
}
|
|
putfs(nodep, nodep->name());
|
|
}
|
|
void visit(AstEnumItem* nodep) override {
|
|
putfs(nodep, nodep->name());
|
|
iterateConstNull(nodep->rangep());
|
|
puts(" = ");
|
|
iterateConstNull(nodep->valuep());
|
|
if (nodep->nextp()) puts(",");
|
|
puts("\n");
|
|
}
|
|
void visit(AstNodeArrayDType* nodep) override {
|
|
if (!m_arrayPost) {
|
|
if (VN_IS(nodep, PackArrayDType)) {
|
|
// Unpacked ranges handled in BasicDType, as they print "backwards"
|
|
m_packedps.push_back(nodep);
|
|
}
|
|
iterateConst(nodep->subDTypep());
|
|
} else {
|
|
if (VN_IS(nodep, UnpackArrayDType)) {
|
|
VL_RESTORER(m_arrayPost);
|
|
m_arrayPost = false;
|
|
iterateAndNextConstNull(nodep->rangep());
|
|
m_arrayPost = true;
|
|
}
|
|
iterateConst(nodep->subDTypep()); // For post's key
|
|
}
|
|
}
|
|
void visit(AstIfaceRefDType* nodep) override {
|
|
if (m_arrayPost) {
|
|
puts(" (");
|
|
if (nodep->cellp()) {
|
|
iterateConst(nodep->cellp());
|
|
} else {
|
|
puts("????");
|
|
}
|
|
puts(")");
|
|
return;
|
|
}
|
|
puts(nodep->ifaceName());
|
|
}
|
|
void visit(AstRefDType* nodep) override {
|
|
if (nodep->subDTypep()) {
|
|
iterateConst(nodep->skipRefp());
|
|
} else {
|
|
puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n");
|
|
}
|
|
}
|
|
void visit(AstRequireDType* nodep) override { iterateConst(nodep->lhsp()); }
|
|
void visit(AstModport* nodep) override {
|
|
puts(nodep->verilogKwd());
|
|
puts(" ");
|
|
puts(nodep->prettyName());
|
|
puts(" (\n");
|
|
if (nodep->varsp()) {
|
|
iterateConst(nodep->varsp());
|
|
} else {
|
|
puts("????");
|
|
}
|
|
puts(");\n");
|
|
}
|
|
void visit(AstModportVarRef* nodep) override {
|
|
puts(nodep->direction().verilogKwd());
|
|
puts(" ");
|
|
if (nodep->varp()) {
|
|
VL_RESTORER(m_suppressVarSemi);
|
|
m_suppressVarSemi = true;
|
|
iterateConst(nodep->varp());
|
|
} else {
|
|
puts(nodep->prettyName());
|
|
}
|
|
if (nodep->nextp()) puts(", ");
|
|
}
|
|
void visit(AstNodeUOrStructDType* nodep) override {
|
|
if (m_arrayPost) return;
|
|
puts(nodep->verilogKwd() + " ");
|
|
if (nodep->packed()) puts("packed ");
|
|
{
|
|
puts("{\n");
|
|
VL_RESTORER(m_packedps);
|
|
m_packedps.clear();
|
|
for (AstMemberDType* itemp = nodep->membersp(); itemp;
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
iterateConst(itemp);
|
|
}
|
|
puts("}");
|
|
}
|
|
emitPacked();
|
|
}
|
|
void visit(AstMemberDType* nodep) override {
|
|
if (m_arrayPost) return;
|
|
iterateConst(nodep->subDTypep());
|
|
puts(" ");
|
|
puts(nodep->name());
|
|
puts(";\n");
|
|
}
|
|
void visit(AstQueueDType* nodep) override {
|
|
if (!m_arrayPost) {
|
|
iterateConst(nodep->subDTypep());
|
|
} else {
|
|
VL_RESTORER(m_arrayPost);
|
|
m_arrayPost = false;
|
|
puts("[$");
|
|
if (nodep->boundp()) {
|
|
puts(":");
|
|
iterateConst(nodep->boundp());
|
|
}
|
|
puts("]");
|
|
m_arrayPost = true;
|
|
iterateConst(nodep->subDTypep()); // For post's key
|
|
}
|
|
}
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
|
if (nodep->dotted() != "") {
|
|
putfs(nodep, nodep->dotted());
|
|
puts(".");
|
|
puts(nodep->prettyName());
|
|
} else {
|
|
putfs(nodep, nodep->prettyName());
|
|
}
|
|
puts("(");
|
|
iterateAndNextConstNull(nodep->pinsp());
|
|
puts(")");
|
|
}
|
|
void visit(AstCCall* nodep) override {
|
|
puts(nodep->funcp()->name());
|
|
puts("(");
|
|
iterateAndNextConstNull(nodep->argsp());
|
|
puts(")");
|
|
}
|
|
void visit(AstArg* nodep) override { iterateAndNextConstNull(nodep->exprp()); }
|
|
void visit(AstPrintTimeScale* nodep) override {
|
|
puts(nodep->verilogKwd());
|
|
puts(";\n");
|
|
}
|
|
|
|
// Terminals
|
|
void visit(AstVarRef* nodep) override {
|
|
if (nodep->varScopep()) {
|
|
putfs(nodep, nodep->varScopep()->prettyName());
|
|
} else {
|
|
if (nodep->varp()) {
|
|
if (nodep->selfPointer().isEmpty()) {
|
|
putfs(nodep, nodep->varp()->prettyName());
|
|
} else {
|
|
putfs(nodep, nodep->selfPointer().asString());
|
|
putfs(nodep, "->");
|
|
puts(nodep->varp()->prettyName());
|
|
}
|
|
} else {
|
|
putfs(nodep, nodep->name());
|
|
}
|
|
}
|
|
}
|
|
void visit(AstVarXRef* nodep) override {
|
|
putfs(nodep, nodep->prettyName(nodep->dotted()));
|
|
puts(".");
|
|
if (nodep->varp()) {
|
|
puts(nodep->varp()->prettyName());
|
|
} else {
|
|
puts(nodep->prettyName());
|
|
}
|
|
}
|
|
void visit(AstConst* nodep) override { putfs(nodep, nodep->num().ascii(true, true)); }
|
|
|
|
// Just iterate
|
|
void visit(AstTopScope* nodep) override { iterateChildrenConst(nodep); }
|
|
void visit(AstScope* nodep) override { iterateChildrenConst(nodep); }
|
|
void visit(AstVar* nodep) override {
|
|
if (nodep->isIO()) {
|
|
putfs(nodep, nodep->verilogKwd());
|
|
puts(" ");
|
|
}
|
|
VL_RESTORER(m_arrayPost);
|
|
m_arrayPost = false;
|
|
iterateConstNull(nodep->dtypep()); // Dtype part before identifier
|
|
puts(" ");
|
|
puts(nodep->prettyName());
|
|
m_arrayPost = true;
|
|
iterateConstNull(nodep->dtypep()); // Dtype part after identifier
|
|
puts(m_suppressVarSemi ? "\n" : ";\n");
|
|
}
|
|
void visit(AstActive* nodep) override {
|
|
VL_RESTORER(m_sensesp);
|
|
m_sensesp = nodep->sensesp();
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
}
|
|
void visit(AstDelay* nodep) override {
|
|
puts(""); // this is for proper alignment
|
|
puts("#");
|
|
iterateConst(nodep->lhsp());
|
|
puts(";\n");
|
|
iterateAndNextConstNull(nodep->stmtsp());
|
|
}
|
|
void visit(AstCAwait* nodep) override {
|
|
AstCMethodHard* methodp = VN_CAST(nodep->exprp(), CMethodHard);
|
|
UASSERT_OBJ(methodp, nodep, "AstCAwait expression must be an AstCMethodHard");
|
|
puts(""); // this is for proper alignment
|
|
puts("#");
|
|
iterateConst(methodp->pinsp());
|
|
}
|
|
void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); }
|
|
void visit(AstNodeText*) override {}
|
|
void visit(AstVarScope*) override {}
|
|
void visit(AstTraceDecl*) override {}
|
|
void visit(AstTraceInc*) override {}
|
|
// NOPs
|
|
void visit(AstPragma*) override {}
|
|
void visit(AstCell*) override {} // Handled outside the Visit class
|
|
// Default
|
|
void visit(AstNode* nodep) override {
|
|
puts("\n???? // "s + nodep->prettyTypeName() + "\n");
|
|
iterateChildrenConst(nodep);
|
|
// Not v3fatalSrc so we keep processing
|
|
if (!m_suppressUnknown) {
|
|
nodep->v3error(
|
|
"Internal: Unknown node type reached emitter: " << nodep->prettyTypeName());
|
|
}
|
|
}
|
|
|
|
public:
|
|
explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp)
|
|
: m_suppressUnknown{suppressUnknown}
|
|
, m_sensesp{domainp} {}
|
|
~EmitVBaseVisitorConst() override = default;
|
|
};
|
|
|
|
//######################################################################
|
|
// Emit to an output file
|
|
|
|
class EmitVFileVisitor final : public EmitVBaseVisitorConst {
|
|
// METHODS
|
|
void puts(const string& str) override { ofp()->puts(str); }
|
|
void putbs(const string& str) override { ofp()->putbs(str); }
|
|
void putfs(AstNode*, const string& str) override { putbs(str); }
|
|
void putqs(AstNode*, const string& str) override { putbs(str); }
|
|
void putsNoTracking(const string& str) override { ofp()->putsNoTracking(str); }
|
|
|
|
public:
|
|
EmitVFileVisitor(AstNode* nodep, V3OutVFile* ofp, bool trackText, bool suppressUnknown)
|
|
: EmitVBaseVisitorConst{suppressUnknown, nullptr} {
|
|
m_ofp = ofp;
|
|
m_trackText = trackText;
|
|
iterateConst(nodep);
|
|
}
|
|
~EmitVFileVisitor() override = default;
|
|
};
|
|
|
|
//######################################################################
|
|
// Emit to a stream (perhaps stringstream)
|
|
|
|
class EmitVStreamVisitor final : public EmitVBaseVisitorConst {
|
|
// MEMBERS
|
|
std::ostream& m_os;
|
|
// METHODS
|
|
void putsNoTracking(const string& str) override { m_os << str; }
|
|
void puts(const string& str) override { putsNoTracking(str); }
|
|
void putbs(const string& str) override { puts(str); }
|
|
void putfs(AstNode*, const string& str) override { putbs(str); }
|
|
void putqs(AstNode*, const string& str) override { putbs(str); }
|
|
|
|
public:
|
|
EmitVStreamVisitor(const AstNode* nodep, std::ostream& os)
|
|
: EmitVBaseVisitorConst{false, nullptr}
|
|
, m_os(os) { // Need () or GCC 4.8 false warning
|
|
iterateConst(const_cast<AstNode*>(nodep));
|
|
}
|
|
~EmitVStreamVisitor() override = default;
|
|
};
|
|
|
|
//######################################################################
|
|
// EmitV class functions
|
|
|
|
void V3EmitV::verilogForTree(const AstNode* nodep, std::ostream& os) {
|
|
{ EmitVStreamVisitor{nodep, os}; }
|
|
}
|
|
|
|
void V3EmitV::emitvFiles() {
|
|
UINFO(2, __FUNCTION__ << ":");
|
|
for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep;
|
|
filep = VN_AS(filep->nextp(), NodeFile)) {
|
|
AstVFile* const vfilep = VN_CAST(filep, VFile);
|
|
if (vfilep && vfilep->tblockp()) {
|
|
V3OutVFile of{vfilep->name()};
|
|
of.puts("// DESCR"
|
|
"IPTION: Verilator generated Verilog\n");
|
|
{ EmitVFileVisitor{vfilep->tblockp(), &of, true, false}; }
|
|
}
|
|
}
|
|
}
|
|
|
|
void V3EmitV::debugEmitV(const string& filename) {
|
|
UINFO(2, __FUNCTION__ << ":");
|
|
V3OutVFile of{filename};
|
|
{ EmitVFileVisitor{v3Global.rootp(), &of, true, true}; }
|
|
}
|