2012-04-13 03:08:20 +02:00
|
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
// DESCRIPTION: Verilator: Emit C++ for tree
|
|
|
|
|
|
//
|
2008-04-25 14:14:27 +02:00
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
2006-08-26 13:35:28 +02:00
|
|
|
|
//
|
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
//
|
2018-01-03 00:05:06 +01:00
|
|
|
|
// Copyright 2003-2018 by Wilson Snyder. This program is free software; you can
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
|
// Version 2.0.
|
2006-08-26 13:35:28 +02:00
|
|
|
|
//
|
|
|
|
|
|
// Verilator is distributed in the hope that it will be useful,
|
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
|
//
|
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
|
#include "config_build.h"
|
|
|
|
|
|
#include "verilatedos.h"
|
2008-06-30 19:11:25 +02:00
|
|
|
|
#include <cstdio>
|
|
|
|
|
|
#include <cstdarg>
|
2006-08-26 13:35:28 +02:00
|
|
|
|
#include <unistd.h>
|
2008-06-30 19:11:25 +02:00
|
|
|
|
#include <cmath>
|
2006-08-26 13:35:28 +02:00
|
|
|
|
#include <map>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <algorithm>
|
2017-10-18 23:38:10 +02:00
|
|
|
|
#include VL_INCLUDE_UNORDERED_SET
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
2012-08-27 03:13:47 +02:00
|
|
|
|
#include "V3String.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
#include "V3EmitC.h"
|
|
|
|
|
|
#include "V3EmitCBase.h"
|
2014-04-10 02:29:35 +02:00
|
|
|
|
#include "V3Number.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2012-04-06 03:46:55 +02:00
|
|
|
|
#define VL_VALUE_STRING_MAX_WIDTH 8192 // We use a static char array in VL_VALUE_STRING
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2017-09-19 03:36:18 +02:00
|
|
|
|
#define EMITC_NUM_CONSTW 8 // Number of VL_CONST_W_*X's in verilated.h (IE VL_CONST_W_8X is last)
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
//######################################################################
|
|
|
|
|
|
// Emit statements and math operators
|
|
|
|
|
|
|
|
|
|
|
|
class EmitCStmts : public EmitCBaseVisitor {
|
|
|
|
|
|
private:
|
|
|
|
|
|
bool m_suppressSemi;
|
|
|
|
|
|
AstVarRef* m_wideTempRefp; // Variable that _WW macros should be setting
|
|
|
|
|
|
vector<AstVar*> m_ctorVarsVec; // All variables in constructor order
|
2008-11-17 23:13:57 +01:00
|
|
|
|
int m_splitSize; // # of cfunc nodes placed into output file
|
|
|
|
|
|
int m_splitFilenum; // File number being created, 0 = primary
|
2009-01-21 22:56:50 +01:00
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
public:
|
2009-01-21 22:56:50 +01:00
|
|
|
|
// METHODS
|
|
|
|
|
|
static int debug() {
|
|
|
|
|
|
static int level = -1;
|
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
|
return level;
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
|
// ACCESSORS
|
2011-08-05 03:58:45 +02:00
|
|
|
|
int splitFilenum() const { return m_splitFilenum; }
|
2008-11-17 23:13:57 +01:00
|
|
|
|
int splitFilenumInc() { m_splitSize = 0; return ++m_splitFilenum; }
|
2011-08-05 03:58:45 +02:00
|
|
|
|
int splitSize() const { return m_splitSize; }
|
2013-09-04 01:35:32 +02:00
|
|
|
|
void splitSizeInc(int count) { m_splitSize += count; }
|
|
|
|
|
|
void splitSizeInc(AstNode* nodep) { splitSizeInc(EmitCBaseCounterVisitor(nodep).count()); }
|
|
|
|
|
|
bool splitNeeded() { return (splitSize() && v3Global.opt.outputSplit()
|
2008-11-17 23:13:57 +01:00
|
|
|
|
&& v3Global.opt.outputSplit() < splitSize()); }
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// METHODS
|
2010-01-17 21:53:12 +01:00
|
|
|
|
void displayNode(AstNode* nodep, AstScopeName* scopenamep,
|
|
|
|
|
|
const string& vformat, AstNode* exprsp, bool isScan);
|
2008-07-01 20:15:10 +02:00
|
|
|
|
void displayEmit(AstNode* nodep, bool isScan);
|
|
|
|
|
|
void displayArg(AstNode* dispp, AstNode** elistp, bool isScan,
|
2017-07-07 02:25:59 +02:00
|
|
|
|
const string& vfmt, char fmtLetter);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
void emitVarDecl(AstVar* nodep, const string& prefixIfImp);
|
2017-12-09 17:52:35 +01:00
|
|
|
|
typedef enum {EVL_CLASS_IO, EVL_CLASS_SIG, EVL_CLASS_TEMP, EVL_CLASS_PAR, EVL_CLASS_ALL,
|
|
|
|
|
|
EVL_FUNC_ALL} EisWhich;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
void emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp);
|
2018-05-30 01:49:27 +02:00
|
|
|
|
void emitVarCtors(bool* firstp);
|
|
|
|
|
|
void emitCtorSep(bool* firstp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
bool emitSimpleOk(AstNodeMath* nodep);
|
|
|
|
|
|
void emitIQW(AstNode* nodep) {
|
2017-12-09 20:44:55 +01:00
|
|
|
|
// Other abbrevs: "C"har, "S"hort, "F"loat, "D"ouble, stri"N"g
|
|
|
|
|
|
puts (nodep->dtypep()->charIQWN());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2009-03-14 03:58:55 +01:00
|
|
|
|
void emitScIQW(AstVar* nodep) {
|
2015-02-26 03:09:55 +01:00
|
|
|
|
if (!nodep->isSc()) nodep->v3fatalSrc("emitting SystemC operator on non-SC variable");
|
2013-04-27 03:02:32 +02:00
|
|
|
|
puts (nodep->isScBigUint() ? "SB"
|
|
|
|
|
|
: nodep->isScUint() ? "SU"
|
|
|
|
|
|
: nodep->isScBv() ? "SW"
|
|
|
|
|
|
: (nodep->isScQuad() ? "SQ" : "SI"));
|
2009-03-14 03:58:55 +01:00
|
|
|
|
}
|
2008-06-30 02:02:24 +02:00
|
|
|
|
void emitOpName(AstNode* nodep, const string& format,
|
|
|
|
|
|
AstNode* lhsp, AstNode* rhsp, AstNode* thsp);
|
2013-05-19 02:17:17 +02:00
|
|
|
|
void emitDeclArrayBrackets(AstVar* nodep) {
|
|
|
|
|
|
// This isn't very robust and may need cleanup for other data types
|
|
|
|
|
|
for (AstUnpackArrayDType* arrayp=nodep->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
|
|
|
|
|
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
|
|
|
|
|
puts("["+cvtToStr(arrayp->elementsConst())+"]");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2014-11-07 13:50:11 +01:00
|
|
|
|
void emitTypedefs(AstNode* firstp) {
|
|
|
|
|
|
bool first = true;
|
|
|
|
|
|
for (AstNode* loopp=firstp; loopp; loopp = loopp->nextp()) {
|
|
|
|
|
|
if (AstTypedef* nodep = loopp->castTypedef()) {
|
|
|
|
|
|
if (nodep->attrPublic()) {
|
|
|
|
|
|
if (first) {
|
|
|
|
|
|
first = false;
|
|
|
|
|
|
puts("\n// TYPEDEFS\n");
|
|
|
|
|
|
puts("// That were declared public\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (AstEnumDType* adtypep = nodep->dtypep()->skipRefToEnump()->castEnumDType()) {
|
|
|
|
|
|
if (adtypep->width()>64) {
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// enum "+nodep->name()+" // Ignored: Too wide for C++\n");
|
2014-11-07 13:50:11 +01:00
|
|
|
|
} else {
|
|
|
|
|
|
puts("enum "+nodep->name()+" {\n");
|
|
|
|
|
|
for (AstEnumItem* itemp = adtypep->itemsp(); itemp; itemp=itemp->nextp()->castEnumItem()) {
|
|
|
|
|
|
puts(itemp->name());
|
|
|
|
|
|
puts(" = ");
|
|
|
|
|
|
itemp->valuep()->iterateAndNext(*this);
|
2016-05-06 04:48:53 +02:00
|
|
|
|
if (itemp->nextp()->castEnumItem()) puts(",");
|
2014-11-07 13:50:11 +01:00
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
puts("};\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// VISITORS
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNodeAssign* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
bool paren = true; bool decind = false;
|
|
|
|
|
|
if (AstSel* selp=nodep->lhsp()->castSel()) {
|
|
|
|
|
|
if (selp->widthMin()==1) {
|
|
|
|
|
|
putbs("VL_ASSIGNBIT_");
|
|
|
|
|
|
emitIQW(selp->fromp());
|
|
|
|
|
|
if (nodep->rhsp()->isAllOnesV()) {
|
2009-11-24 03:24:55 +01:00
|
|
|
|
puts("O(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2009-11-24 03:24:55 +01:00
|
|
|
|
puts("I(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
puts(cvtToStr(nodep->widthMin())+",");
|
|
|
|
|
|
selp->lsbp()->iterateAndNext(*this); puts(", ");
|
|
|
|
|
|
selp->fromp()->iterateAndNext(*this); puts(", ");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
putbs("VL_ASSIGNSEL_");
|
|
|
|
|
|
emitIQW (selp->fromp());
|
2009-11-24 03:24:55 +01:00
|
|
|
|
puts("II");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
emitIQW(nodep->rhsp());
|
|
|
|
|
|
puts("(");
|
|
|
|
|
|
puts(cvtToStr(nodep->widthMin())+",");
|
|
|
|
|
|
selp->lsbp()->iterateAndNext(*this); puts(", ");
|
|
|
|
|
|
selp->fromp()->iterateAndNext(*this); puts(", ");
|
|
|
|
|
|
}
|
2015-02-26 03:09:55 +01:00
|
|
|
|
} else if (AstVar* varp = AstVar::scVarRecurse(nodep->lhsp())) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
putbs("VL_ASSIGN_"); // Set a systemC variable
|
2015-02-26 03:09:55 +01:00
|
|
|
|
emitScIQW(varp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
|
puts("(");
|
|
|
|
|
|
puts(cvtToStr(nodep->widthMin())+",");
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this); puts(", ");
|
2015-02-26 03:09:55 +01:00
|
|
|
|
} else if (AstVar* varp = AstVar::scVarRecurse(nodep->rhsp())) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
putbs("VL_ASSIGN_"); // Get a systemC variable
|
|
|
|
|
|
emitIQW(nodep);
|
2015-02-26 03:09:55 +01:00
|
|
|
|
emitScIQW(varp);
|
2009-03-14 03:58:55 +01:00
|
|
|
|
puts("(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(cvtToStr(nodep->widthMin())+",");
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this); puts(", ");
|
|
|
|
|
|
} else if (nodep->isWide()
|
|
|
|
|
|
&& nodep->lhsp()->castVarRef()
|
2011-06-29 02:45:50 +02:00
|
|
|
|
&& !nodep->rhsp()->castCMath()
|
2006-08-26 13:35:28 +02:00
|
|
|
|
&& !nodep->rhsp()->castVarRef()
|
|
|
|
|
|
&& !nodep->rhsp()->castArraySel()) {
|
|
|
|
|
|
// Wide functions assign into the array directly, don't need separate assign statement
|
|
|
|
|
|
m_wideTempRefp = nodep->lhsp()->castVarRef();
|
|
|
|
|
|
paren = false;
|
|
|
|
|
|
} else if (nodep->isWide()) {
|
|
|
|
|
|
putbs("VL_ASSIGN_W(");
|
|
|
|
|
|
puts(cvtToStr(nodep->widthMin())+",");
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this); puts(", ");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
paren = false;
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts(" ");
|
|
|
|
|
|
ofp()->blockInc(); decind = true;
|
|
|
|
|
|
if (!nodep->rhsp()->castConst()) ofp()->putBreak();
|
|
|
|
|
|
puts("= ");
|
|
|
|
|
|
}
|
|
|
|
|
|
nodep->rhsp()->iterateAndNext(*this);
|
|
|
|
|
|
if (paren) puts(")");
|
|
|
|
|
|
if (decind) ofp()->blockDec();
|
|
|
|
|
|
if (!m_suppressSemi) puts(";\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstAlwaysPublic*) {
|
2010-04-06 02:01:17 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCCall* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(nodep->hiername());
|
|
|
|
|
|
puts(nodep->funcp()->name());
|
|
|
|
|
|
puts("(");
|
2006-08-30 23:07:55 +02:00
|
|
|
|
puts(nodep->argTypes());
|
|
|
|
|
|
bool comma = (nodep->argTypes() != "");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
2006-08-30 23:07:55 +02:00
|
|
|
|
if (comma) puts(", ");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
subnodep->accept(*this);
|
2006-08-30 23:07:55 +02:00
|
|
|
|
comma = true;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
if (nodep->backp()->castNodeMath() || nodep->backp()->castCReturn()) {
|
|
|
|
|
|
// We should have a separate CCall for math and statement usage, but...
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNodeCase* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// In V3Case...
|
2017-04-29 02:09:27 +02:00
|
|
|
|
nodep->v3fatalSrc("Case statements should have been reduced out");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstComment* nodep) {
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration((string)"// "+nodep->name()+" at "+nodep->fileline()->ascii()+"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCoverDecl* nodep) {
|
2006-08-30 03:14:29 +02:00
|
|
|
|
puts("__vlCoverInsert("); // As Declared in emitCoverageDecl
|
2008-12-05 16:54:14 +01:00
|
|
|
|
puts("&(vlSymsp->__Vcoverage[");
|
2008-12-12 21:34:02 +01:00
|
|
|
|
puts(cvtToStr(nodep->dataDeclThisp()->binNum())); puts("])");
|
2008-12-05 16:54:14 +01:00
|
|
|
|
// If this isn't the first instantiation of this module under this
|
2014-11-24 04:00:00 +01:00
|
|
|
|
// design, don't really count the bucket, and rely on verilator_cov to
|
2008-12-05 16:54:14 +01:00
|
|
|
|
// aggregate counts. This is because Verilator combines all
|
2014-11-24 04:00:00 +01:00
|
|
|
|
// hiearchies itself, and if verilator_cov also did it, you'd end up
|
2008-12-05 16:54:14 +01:00
|
|
|
|
// with (number-of-instant) times too many counts in this bin.
|
|
|
|
|
|
puts(", first"); // Enable, passed from __Vconfigure parameter
|
2009-05-08 19:16:19 +02:00
|
|
|
|
puts(", "); putsQuoted(nodep->fileline()->filename());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(", "); puts(cvtToStr(nodep->fileline()->lineno()));
|
|
|
|
|
|
puts(", "); puts(cvtToStr(nodep->column()));
|
2009-05-08 19:16:19 +02:00
|
|
|
|
puts(", "); putsQuoted((nodep->hier()!=""?".":"")+nodep->hier());
|
|
|
|
|
|
puts(", "); putsQuoted(nodep->page());
|
|
|
|
|
|
puts(", "); putsQuoted(nodep->comment());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCoverInc* nodep) {
|
2008-12-05 16:54:14 +01:00
|
|
|
|
puts("++(vlSymsp->__Vcoverage[");
|
2008-12-12 21:34:02 +01:00
|
|
|
|
puts(cvtToStr(nodep->declp()->dataDeclThisp()->binNum()));
|
2008-12-05 16:54:14 +01:00
|
|
|
|
puts("]);\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCReturn* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("return (");
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstDisplay* nodep) {
|
2010-01-17 21:53:12 +01:00
|
|
|
|
string text = nodep->fmtp()->text();
|
2009-10-23 03:16:52 +02:00
|
|
|
|
if (nodep->addNewline()) text += "\n";
|
2010-01-17 21:53:12 +01:00
|
|
|
|
displayNode(nodep, nodep->fmtp()->scopeNamep(), text, nodep->fmtp()->exprsp(), false);
|
2008-07-01 20:15:10 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstScopeName* nodep) {
|
2009-12-05 16:38:49 +01:00
|
|
|
|
// For use under AstCCalls for dpiImports. ScopeNames under displays are handled in AstDisplay
|
2009-12-20 14:27:00 +01:00
|
|
|
|
if (!nodep->dpiExport()) {
|
2014-07-22 02:55:52 +02:00
|
|
|
|
// this is where the DPI import context scope is set
|
|
|
|
|
|
string scope = nodep->scopeDpiName();
|
|
|
|
|
|
putbs("(&(vlSymsp->__Vscope_"+scope+"))");
|
2009-12-20 14:27:00 +01:00
|
|
|
|
}
|
2009-12-05 16:38:49 +01:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstSFormat* nodep) {
|
2010-01-17 21:53:12 +01:00
|
|
|
|
displayNode(nodep, nodep->fmtp()->scopeNamep(), nodep->fmtp()->text(), nodep->fmtp()->exprsp(), false);
|
2009-11-24 03:24:55 +01:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstSFormatF* nodep) {
|
2010-01-18 01:13:44 +01:00
|
|
|
|
displayNode(nodep, nodep->scopeNamep(), nodep->text(), nodep->exprsp(), false);
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstFScanF* nodep) {
|
2010-01-17 21:53:12 +01:00
|
|
|
|
displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true);
|
2008-07-01 20:15:10 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstSScanF* nodep) {
|
2010-01-17 21:53:12 +01:00
|
|
|
|
displayNode(nodep, NULL, nodep->text(), nodep->exprsp(), true);
|
2008-07-01 20:15:10 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstValuePlusArgs* nodep) {
|
2017-05-19 04:41:43 +02:00
|
|
|
|
puts("VL_VALUEPLUSARGS_IN");
|
|
|
|
|
|
emitIQW(nodep->outp());
|
2009-11-19 23:04:21 +01:00
|
|
|
|
puts("(");
|
2017-05-19 04:41:43 +02:00
|
|
|
|
puts(cvtToStr(nodep->outp()->widthMin()));
|
2009-11-24 03:24:55 +01:00
|
|
|
|
puts(",");
|
2017-05-19 04:41:43 +02:00
|
|
|
|
emitCvtPackStr(nodep->searchp());
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
putbs("");
|
|
|
|
|
|
nodep->outp()->iterateAndNext(*this);
|
2009-11-19 23:04:21 +01:00
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstTestPlusArgs* nodep) {
|
2009-11-19 23:04:21 +01:00
|
|
|
|
puts("VL_TESTPLUSARGS_I(");
|
|
|
|
|
|
putsQuoted(nodep->text());
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstFGetS* nodep) {
|
2008-11-05 16:52:23 +01:00
|
|
|
|
checkMaxWords(nodep);
|
|
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
|
|
|
|
|
|
}
|
2008-07-01 20:15:10 +02:00
|
|
|
|
|
|
|
|
|
|
void checkMaxWords(AstNode* nodep) {
|
|
|
|
|
|
if (nodep->widthWords() > VL_TO_STRING_MAX_WORDS) {
|
2010-01-16 02:07:16 +01:00
|
|
|
|
nodep->v3error("String of "<<nodep->width()<<" bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h");
|
2008-07-01 20:15:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstFOpen* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->filep()->iterateAndNext(*this);
|
|
|
|
|
|
puts(" = VL_FOPEN_");
|
|
|
|
|
|
emitIQW(nodep->filenamep());
|
|
|
|
|
|
emitIQW(nodep->modep());
|
|
|
|
|
|
if (nodep->modep()->width()>4*8) nodep->modep()->v3error("$fopen mode should be <= 4 characters");
|
|
|
|
|
|
puts("(");
|
2006-12-21 16:28:32 +01:00
|
|
|
|
if (nodep->filenamep()->isWide()) {
|
|
|
|
|
|
puts(cvtToStr(nodep->filenamep()->widthWords()));
|
|
|
|
|
|
putbs(", ");
|
|
|
|
|
|
}
|
2008-07-01 20:15:10 +02:00
|
|
|
|
checkMaxWords(nodep->filenamep());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->filenamep()->iterateAndNext(*this);
|
|
|
|
|
|
putbs(", ");
|
|
|
|
|
|
nodep->modep()->iterateAndNext(*this);
|
|
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
2018-03-12 21:44:01 +01:00
|
|
|
|
virtual void visit(AstNodeReadWriteMem* nodep) {
|
|
|
|
|
|
puts(nodep->cFuncPrefixp());
|
2006-12-19 15:09:57 +01:00
|
|
|
|
emitIQW(nodep->filenamep());
|
2007-11-30 23:12:53 +01:00
|
|
|
|
puts(" ("); // We take a void* rather than emitIQW(nodep->memp());
|
2006-12-19 15:09:57 +01:00
|
|
|
|
puts(nodep->isHex()?"true":"false");
|
|
|
|
|
|
putbs(",");
|
|
|
|
|
|
puts(cvtToStr(nodep->memp()->widthMin())); // Need real storage width
|
|
|
|
|
|
putbs(",");
|
|
|
|
|
|
uint32_t array_lsb = 0;
|
|
|
|
|
|
{
|
|
|
|
|
|
AstVarRef* varrefp = nodep->memp()->castVarRef();
|
2018-03-12 21:44:01 +01:00
|
|
|
|
if (!varrefp) {
|
|
|
|
|
|
nodep->v3error(nodep->verilogKwd() << " loading non-variable");
|
|
|
|
|
|
}
|
2013-01-15 03:49:22 +01:00
|
|
|
|
else if (AstUnpackArrayDType* adtypep = varrefp->varp()->dtypeSkipRefp()->castUnpackArrayDType()) {
|
|
|
|
|
|
puts(cvtToStr(varrefp->varp()->dtypep()->arrayUnpackedElements()));
|
2009-11-05 04:31:53 +01:00
|
|
|
|
array_lsb = adtypep->lsb();
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
2018-03-12 21:44:01 +01:00
|
|
|
|
nodep->v3error(nodep->verilogKwd()
|
|
|
|
|
|
<< " loading other than unpacked-array variable");
|
2006-12-19 15:09:57 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
putbs(", ");
|
|
|
|
|
|
puts(cvtToStr(array_lsb));
|
|
|
|
|
|
putbs(",");
|
2018-03-12 21:44:01 +01:00
|
|
|
|
if (!nodep->filenamep()->dtypep()->isString()) {
|
|
|
|
|
|
puts(cvtToStr(nodep->filenamep()->widthWords()));
|
|
|
|
|
|
checkMaxWords(nodep->filenamep());
|
|
|
|
|
|
putbs(", ");
|
|
|
|
|
|
}
|
2006-12-19 15:09:57 +01:00
|
|
|
|
nodep->filenamep()->iterateAndNext(*this);
|
|
|
|
|
|
putbs(", ");
|
|
|
|
|
|
nodep->memp()->iterateAndNext(*this);
|
|
|
|
|
|
putbs(","); if (nodep->lsbp()) { nodep->lsbp()->iterateAndNext(*this); }
|
|
|
|
|
|
else puts(cvtToStr(array_lsb));
|
|
|
|
|
|
putbs(","); if (nodep->msbp()) { nodep->msbp()->iterateAndNext(*this); } else puts("~0");
|
|
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstFClose* nodep) {
|
2011-07-01 19:41:21 +02:00
|
|
|
|
puts("VL_FCLOSE_I(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->filep()->iterateAndNext(*this);
|
2011-07-01 19:41:21 +02:00
|
|
|
|
puts("); ");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->filep()->iterateAndNext(*this); // For saftey, so user doesn't later WRITE with it.
|
2011-07-01 19:41:21 +02:00
|
|
|
|
puts("=0;\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstFFlush* nodep) {
|
2008-07-16 20:06:08 +02:00
|
|
|
|
if (!nodep->filep()) {
|
|
|
|
|
|
puts("fflush (stdout);\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("if (");
|
|
|
|
|
|
nodep->filep()->iterateAndNext(*this);
|
2011-07-01 19:41:21 +02:00
|
|
|
|
puts(") { fflush (VL_CVT_I_FP(");
|
2008-07-16 20:06:08 +02:00
|
|
|
|
nodep->filep()->iterateAndNext(*this);
|
|
|
|
|
|
puts(")); }\n");
|
|
|
|
|
|
}
|
2008-06-27 14:45:05 +02:00
|
|
|
|
}
|
2018-03-09 05:40:19 +01:00
|
|
|
|
virtual void visit(AstSysFuncAsTask* nodep) {
|
|
|
|
|
|
if (!nodep->lhsp()->isWide()) puts("(void)");
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
if (!nodep->lhsp()->isWide()) puts(";");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstSystemT* nodep) {
|
2011-11-20 08:01:48 +01:00
|
|
|
|
puts("(void)VL_SYSTEM_I");
|
|
|
|
|
|
emitIQW(nodep->lhsp());
|
|
|
|
|
|
puts("(");
|
|
|
|
|
|
if (nodep->lhsp()->isWide()) {
|
|
|
|
|
|
puts(cvtToStr(nodep->lhsp()->widthWords()));
|
|
|
|
|
|
putbs(", ");
|
|
|
|
|
|
}
|
|
|
|
|
|
checkMaxWords(nodep->lhsp());
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstSystemF* nodep) {
|
2011-11-20 08:01:48 +01:00
|
|
|
|
puts("VL_SYSTEM_I");
|
|
|
|
|
|
emitIQW(nodep->lhsp());
|
|
|
|
|
|
puts("(");
|
|
|
|
|
|
if (nodep->lhsp()->isWide()) {
|
|
|
|
|
|
puts(cvtToStr(nodep->lhsp()->widthWords()));
|
|
|
|
|
|
putbs(", ");
|
|
|
|
|
|
}
|
|
|
|
|
|
checkMaxWords(nodep->lhsp());
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstJumpGo* nodep) {
|
2010-02-14 16:01:21 +01:00
|
|
|
|
puts("goto __Vlabel"+cvtToStr(nodep->labelp()->labelNum())+";\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstJumpLabel* nodep) {
|
2010-02-14 16:01:21 +01:00
|
|
|
|
puts("{\n");
|
|
|
|
|
|
nodep->stmtsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
|
puts("__Vlabel"+cvtToStr(nodep->labelNum())+": ;\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstWhile* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->precondsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts("while (");
|
|
|
|
|
|
nodep->condp()->iterateAndNext(*this);
|
|
|
|
|
|
puts(") {\n");
|
|
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
2010-02-14 16:01:21 +01:00
|
|
|
|
nodep->incsp()->iterateAndNext(*this);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->precondsp()->iterateAndNext(*this); // Need to recompute before next loop
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNodeIf* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("if (");
|
2010-02-02 02:15:48 +01:00
|
|
|
|
if (nodep->branchPred() != AstBranchPred::BP_UNKNOWN) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(nodep->branchPred().ascii()); puts("(");
|
|
|
|
|
|
}
|
|
|
|
|
|
nodep->condp()->iterateAndNext(*this);
|
2010-02-02 02:15:48 +01:00
|
|
|
|
if (nodep->branchPred() != AstBranchPred::BP_UNKNOWN) puts(")");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(") {\n");
|
|
|
|
|
|
nodep->ifsp()->iterateAndNext(*this);
|
|
|
|
|
|
if (nodep->elsesp()) {
|
|
|
|
|
|
puts("} else {\n");
|
|
|
|
|
|
nodep->elsesp()->iterateAndNext(*this);
|
|
|
|
|
|
}
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstStop* nodep) {
|
2017-10-20 01:40:51 +02:00
|
|
|
|
puts("VL_STOP_MT(");
|
2009-05-08 19:16:19 +02:00
|
|
|
|
putsQuoted(nodep->fileline()->filename());
|
|
|
|
|
|
puts(",");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(cvtToStr(nodep->fileline()->lineno()));
|
|
|
|
|
|
puts(",\"\");\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstFinish* nodep) {
|
2017-10-20 01:40:51 +02:00
|
|
|
|
puts("VL_FINISH_MT(");
|
2009-05-08 19:16:19 +02:00
|
|
|
|
putsQuoted(nodep->fileline()->filename());
|
|
|
|
|
|
puts(",");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(cvtToStr(nodep->fileline()->lineno()));
|
|
|
|
|
|
puts(",\"\");\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstText* nodep) {
|
2009-12-03 01:32:41 +01:00
|
|
|
|
if (nodep->tracking()) {
|
|
|
|
|
|
puts(nodep->text());
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ofp()->putsNoTracking(nodep->text());
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCStmt* nodep) {
|
2009-12-02 03:55:56 +01:00
|
|
|
|
putbs("");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCMath* nodep) {
|
2009-12-02 03:55:56 +01:00
|
|
|
|
putbs("");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstUCStmt* nodep) {
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// $c statement at "+nodep->fileline()->ascii()+"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstUCFunc* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("\n");
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// $c function at "+nodep->fileline()->ascii()+"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Operators
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNodeTermop* nodep) {
|
2008-06-30 02:02:24 +02:00
|
|
|
|
emitOpName(nodep, nodep->emitC(), NULL, NULL, NULL);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNodeUniop* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (emitSimpleOk(nodep)) {
|
|
|
|
|
|
putbs("("); puts(nodep->emitSimpleOperator()); puts(" ");
|
2008-06-30 02:02:24 +02:00
|
|
|
|
nodep->lhsp()->iterateAndNext(*this); puts(")");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2008-06-30 02:02:24 +02:00
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), NULL, NULL);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNodeBiop* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (emitSimpleOk(nodep)) {
|
|
|
|
|
|
putbs("("); nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts(" "); putbs(nodep->emitSimpleOperator()); puts(" ");
|
2008-06-30 02:02:24 +02:00
|
|
|
|
nodep->rhsp()->iterateAndNext(*this); puts(")");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2008-06-30 02:02:24 +02:00
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstRedXor* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (nodep->lhsp()->isWide()) {
|
2016-11-27 14:11:38 +01:00
|
|
|
|
visit(nodep->castNodeUniop());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
putbs("VL_REDXOR_");
|
2012-04-29 17:34:25 +02:00
|
|
|
|
puts(cvtToStr(nodep->lhsp()->dtypep()->widthPow2()));
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("(");
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstMulS* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
|
2018-03-10 22:32:04 +01:00
|
|
|
|
nodep->v3error("Unsupported: Signed multiply of "<<nodep->width()
|
|
|
|
|
|
<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
|
2017-06-06 02:30:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
visit(nodep->castNodeBiop());
|
|
|
|
|
|
}
|
|
|
|
|
|
virtual void visit(AstPow* nodep) {
|
|
|
|
|
|
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
|
2018-03-10 22:32:04 +01:00
|
|
|
|
nodep->v3error("Unsupported: Power of "<<nodep->width()
|
|
|
|
|
|
<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
|
2017-06-06 02:30:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
visit(nodep->castNodeBiop());
|
|
|
|
|
|
}
|
|
|
|
|
|
virtual void visit(AstPowSS* nodep) {
|
|
|
|
|
|
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
|
2018-03-10 22:32:04 +01:00
|
|
|
|
nodep->v3error("Unsupported: Power of "<<nodep->width()
|
|
|
|
|
|
<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
|
2017-06-06 02:30:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
visit(nodep->castNodeBiop());
|
|
|
|
|
|
}
|
|
|
|
|
|
virtual void visit(AstPowSU* nodep) {
|
|
|
|
|
|
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
|
2018-03-10 22:32:04 +01:00
|
|
|
|
nodep->v3error("Unsupported: Power of "<<nodep->width()
|
|
|
|
|
|
<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
|
2017-06-06 02:30:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
visit(nodep->castNodeBiop());
|
|
|
|
|
|
}
|
|
|
|
|
|
virtual void visit(AstPowUS* nodep) {
|
|
|
|
|
|
if (nodep->widthWords() > VL_MULS_MAX_WORDS) {
|
2018-03-10 22:32:04 +01:00
|
|
|
|
nodep->v3error("Unsupported: Power of "<<nodep->width()
|
|
|
|
|
|
<<" bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
visit(nodep->castNodeBiop());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCCast* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Extending a value of the same word width is just a NOP.
|
|
|
|
|
|
if (nodep->size()>VL_WORDSIZE) {
|
|
|
|
|
|
puts("(QData)(");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("(IData)(");
|
|
|
|
|
|
}
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNodeCond* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Widths match up already, so we'll just use C++'s operator w/o any temps.
|
|
|
|
|
|
if (nodep->expr1p()->isWide()) {
|
2008-06-30 02:02:24 +02:00
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->condp(), nodep->expr1p(), nodep->expr2p());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
putbs("(");
|
|
|
|
|
|
nodep->condp()->iterateAndNext(*this); putbs(" ? ");
|
|
|
|
|
|
nodep->expr1p()->iterateAndNext(*this); putbs(" : ");
|
|
|
|
|
|
nodep->expr2p()->iterateAndNext(*this); puts(")");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstSel* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Note ASSIGN checks for this on a LHS
|
2008-06-30 02:02:24 +02:00
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->fromp(), nodep->lsbp(), nodep->thsp());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstReplicate* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (nodep->lhsp()->widthMin() == 1 && !nodep->isWide()) {
|
2008-09-04 17:03:46 +02:00
|
|
|
|
if (((int)nodep->rhsp()->castConst()->toUInt()
|
2006-08-26 13:35:28 +02:00
|
|
|
|
* nodep->lhsp()->widthMin()) != nodep->widthMin())
|
|
|
|
|
|
nodep->v3fatalSrc("Replicate non-constant or width miscomputed");
|
|
|
|
|
|
puts("VL_REPLICATE_");
|
|
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
|
puts("OI(");
|
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()));
|
|
|
|
|
|
if (nodep->lhsp()) { puts(","+cvtToStr(nodep->lhsp()->widthMin())); }
|
|
|
|
|
|
if (nodep->rhsp()) { puts(","+cvtToStr(nodep->rhsp()->widthMin())); }
|
|
|
|
|
|
puts(",");
|
2008-06-30 02:02:24 +02:00
|
|
|
|
nodep->lhsp()->iterateAndNext(*this); puts(", ");
|
|
|
|
|
|
nodep->rhsp()->iterateAndNext(*this); puts(")");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2008-06-30 02:02:24 +02:00
|
|
|
|
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstStreamL* nodep) {
|
2014-04-10 02:29:35 +02:00
|
|
|
|
// Attempt to use a "fast" stream function for slice size = power of 2
|
|
|
|
|
|
if (!nodep->isWide()) {
|
|
|
|
|
|
uint32_t isPow2 = nodep->rhsp()->castConst()->num().countOnes() == 1;
|
|
|
|
|
|
uint32_t sliceSize = nodep->rhsp()->castConst()->toUInt();
|
|
|
|
|
|
if (isPow2 && sliceSize <= (nodep->isQuad() ? sizeof(uint64_t) : sizeof(uint32_t))) {
|
|
|
|
|
|
puts("VL_STREAML_FAST_");
|
|
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
|
emitIQW(nodep->lhsp());
|
|
|
|
|
|
puts("I(");
|
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()));
|
|
|
|
|
|
puts(","+cvtToStr(nodep->lhsp()->widthMin()));
|
|
|
|
|
|
puts(","+cvtToStr(nodep->rhsp()->widthMin()));
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this); puts(", ");
|
|
|
|
|
|
uint32_t rd_log2 = V3Number::log2b(nodep->rhsp()->castConst()->toUInt());
|
|
|
|
|
|
puts(cvtToStr(rd_log2)+")");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(), nodep->rhsp(), NULL);
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Terminals
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstVarRef* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(nodep->hiername());
|
2008-06-12 18:03:47 +02:00
|
|
|
|
puts(nodep->varp()->name());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2017-09-12 01:18:58 +02:00
|
|
|
|
void emitCvtPackStr(AstNode* nodep) {
|
2017-05-19 04:41:43 +02:00
|
|
|
|
if (AstConst* constp = nodep->castConst()) {
|
2017-09-23 13:32:37 +02:00
|
|
|
|
putbs("std::string(");
|
2017-05-19 04:41:43 +02:00
|
|
|
|
putsQuoted(constp->num().toString());
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
putbs("VL_CVT_PACK_STR_N");
|
|
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
|
puts("(");
|
|
|
|
|
|
if (nodep->isWide()) {
|
|
|
|
|
|
puts(cvtToStr(nodep->widthWords())); // Note argument width, not node width (which is always 32)
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
}
|
|
|
|
|
|
nodep->iterateAndNext(*this);
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
void emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString) {
|
|
|
|
|
|
// Put out constant set to the specified variable, or given variable in a string
|
|
|
|
|
|
if (nodep->num().isFourState()) {
|
|
|
|
|
|
nodep->v3error("Unsupported: 4-state numbers in this context");
|
2014-11-28 21:01:50 +01:00
|
|
|
|
} else if (nodep->num().isString()) {
|
2017-09-23 13:32:37 +02:00
|
|
|
|
putbs("std::string(");
|
2014-11-28 21:01:50 +01:00
|
|
|
|
putsQuoted(nodep->num().toString());
|
|
|
|
|
|
puts(")");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (nodep->isWide()) {
|
2017-09-19 03:36:18 +02:00
|
|
|
|
int upWidth = nodep->num().widthMin();
|
|
|
|
|
|
int chunks = 0;
|
|
|
|
|
|
if (upWidth > EMITC_NUM_CONSTW*VL_WORDSIZE) {
|
|
|
|
|
|
// Output e.g. 8 words in groups of e.g. 8
|
|
|
|
|
|
chunks = (upWidth-1) / (EMITC_NUM_CONSTW*VL_WORDSIZE);
|
|
|
|
|
|
upWidth %= (EMITC_NUM_CONSTW*VL_WORDSIZE);
|
|
|
|
|
|
if (upWidth == 0) upWidth = (EMITC_NUM_CONSTW*VL_WORDSIZE);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2017-09-19 03:36:18 +02:00
|
|
|
|
{ // Upper e.g. 8 words
|
|
|
|
|
|
if (chunks) {
|
|
|
|
|
|
putbs("VL_CONSTHI_W_");
|
|
|
|
|
|
puts(cvtToStr(VL_WORDS_I(upWidth)));
|
|
|
|
|
|
puts("X(");
|
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()));
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
puts(cvtToStr(chunks*EMITC_NUM_CONSTW*VL_WORDSIZE));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
putbs("VL_CONST_W_");
|
|
|
|
|
|
puts(cvtToStr(VL_WORDS_I(upWidth)));
|
|
|
|
|
|
puts("X(");
|
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()));
|
|
|
|
|
|
}
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
if (!assigntop) {
|
|
|
|
|
|
puts(assignString);
|
|
|
|
|
|
} else if (assigntop->castVarRef()) {
|
|
|
|
|
|
puts(assigntop->hiername());
|
|
|
|
|
|
puts(assigntop->varp()->name());
|
|
|
|
|
|
} else {
|
|
|
|
|
|
assigntop->iterateAndNext(*this);
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int word=VL_WORDS_I(upWidth)-1; word>=0; word--) {
|
|
|
|
|
|
// Only 32 bits - llx + long long here just to appease CPP format warning
|
|
|
|
|
|
ofp()->printf(",0x%08" VL_PRI64 "x", (vluint64_t)(nodep->num().dataWord(word+chunks*EMITC_NUM_CONSTW)));
|
|
|
|
|
|
}
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
|
|
|
|
|
for (chunks--; chunks >= 0; chunks--) {
|
|
|
|
|
|
puts(";\n");
|
|
|
|
|
|
putbs("VL_CONSTLO_W_");
|
|
|
|
|
|
puts(cvtToStr(EMITC_NUM_CONSTW));
|
|
|
|
|
|
puts("X(");
|
|
|
|
|
|
puts(cvtToStr(chunks*EMITC_NUM_CONSTW*VL_WORDSIZE));
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
if (!assigntop) {
|
|
|
|
|
|
puts(assignString);
|
|
|
|
|
|
} else if (assigntop->castVarRef()) {
|
|
|
|
|
|
puts(assigntop->hiername());
|
|
|
|
|
|
puts(assigntop->varp()->name());
|
|
|
|
|
|
} else {
|
|
|
|
|
|
assigntop->iterateAndNext(*this);
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int word=EMITC_NUM_CONSTW-1; word>=0; word--) {
|
|
|
|
|
|
// Only 32 bits - llx + long long here just to appease CPP format warning
|
|
|
|
|
|
ofp()->printf(",0x%08" VL_PRI64 "x", (vluint64_t)(nodep->num().dataWord(word+chunks*EMITC_NUM_CONSTW)));
|
|
|
|
|
|
}
|
|
|
|
|
|
puts(")");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2011-07-24 21:01:51 +02:00
|
|
|
|
} else if (nodep->isDouble()) {
|
2014-04-16 00:50:04 +02:00
|
|
|
|
if (int(nodep->num().toDouble()) == nodep->num().toDouble()
|
|
|
|
|
|
&& nodep->num().toDouble() < 1000
|
|
|
|
|
|
&& nodep->num().toDouble() > -1000) {
|
|
|
|
|
|
ofp()->printf("%3.1f", nodep->num().toDouble()); // Force decimal point
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Not %g as will not always put in decimal point, so not obvious to compiler
|
|
|
|
|
|
// is a real number
|
|
|
|
|
|
ofp()->printf("%.17e", nodep->num().toDouble());
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (nodep->isQuad()) {
|
2008-09-03 23:40:01 +02:00
|
|
|
|
vluint64_t num = nodep->toUQuad();
|
2015-10-04 04:33:06 +02:00
|
|
|
|
if (num<10) ofp()->printf("VL_ULL(%" VL_PRI64 "u)", num);
|
2010-04-10 03:51:15 +02:00
|
|
|
|
else ofp()->printf("VL_ULL(0x%" VL_PRI64 "x)", num);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2008-09-04 17:03:46 +02:00
|
|
|
|
uint32_t num = nodep->toUInt();
|
2010-04-10 03:51:15 +02:00
|
|
|
|
// Only 32 bits - llx + long long here just to appease CPP format warning
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (num<10) puts(cvtToStr(num));
|
2010-04-10 03:51:15 +02:00
|
|
|
|
else ofp()->printf("0x%" VL_PRI64 "x", (vluint64_t)num);
|
2014-05-24 14:00:01 +02:00
|
|
|
|
// If signed, we'll do our own functions
|
|
|
|
|
|
// But must be here, or <= comparisons etc may end up signed
|
|
|
|
|
|
puts("U");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void emitSetVarConstant(const string& assignString, AstConst* constp) {
|
|
|
|
|
|
if (!constp->isWide()) {
|
|
|
|
|
|
puts(assignString);
|
|
|
|
|
|
puts(" = ");
|
|
|
|
|
|
}
|
|
|
|
|
|
emitConstant(constp, NULL, assignString);
|
|
|
|
|
|
puts(";\n");
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstConst* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (nodep->isWide()) {
|
|
|
|
|
|
if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Constant w/ no temp");
|
|
|
|
|
|
emitConstant(nodep, m_wideTempRefp, "");
|
|
|
|
|
|
m_wideTempRefp = NULL; // We used it, barf if set it a second time
|
|
|
|
|
|
} else {
|
|
|
|
|
|
emitConstant(nodep, NULL, "");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Just iterate
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNetlist* nodep) {
|
2009-12-02 03:55:56 +01:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstTopScope* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstScope* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
|
}
|
|
|
|
|
|
// NOPs
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstTypedef*) {}
|
|
|
|
|
|
virtual void visit(AstPragma*) {}
|
|
|
|
|
|
virtual void visit(AstCell*) {} // Handled outside the Visit class
|
|
|
|
|
|
virtual void visit(AstVar*) {} // Handled outside the Visit class
|
|
|
|
|
|
virtual void visit(AstNodeText*) {} // Handled outside the Visit class
|
|
|
|
|
|
virtual void visit(AstTraceDecl*) {} // Handled outside the Visit class
|
|
|
|
|
|
virtual void visit(AstTraceInc*) {} // Handled outside the Visit class
|
|
|
|
|
|
virtual void visit(AstCFile*) {} // Handled outside the Visit class
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Default
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2009-07-09 23:39:24 +02:00
|
|
|
|
puts((string)"\n???? // "+nodep->prettyTypeName()+"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->iterateChildren(*this);
|
2009-07-09 23:39:24 +02:00
|
|
|
|
nodep->v3fatalSrc("Unknown node type reached emitter: "<<nodep->prettyTypeName());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
EmitCStmts() {
|
|
|
|
|
|
m_suppressSemi = false;
|
|
|
|
|
|
m_wideTempRefp = NULL;
|
2008-11-17 23:13:57 +01:00
|
|
|
|
m_splitSize = 0;
|
|
|
|
|
|
m_splitFilenum = 0;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
virtual ~EmitCStmts() {}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
// Internal EmitC implementation
|
|
|
|
|
|
|
|
|
|
|
|
class EmitCImp : EmitCStmts {
|
|
|
|
|
|
// MEMBERS
|
2009-11-07 12:20:20 +01:00
|
|
|
|
AstNodeModule* m_modp;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
vector<AstChangeDet*> m_blkChangeDetVec; // All encountered changes in block
|
|
|
|
|
|
bool m_slow; // Creating __Slow file
|
|
|
|
|
|
bool m_fast; // Creating non __Slow file (or both)
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------
|
|
|
|
|
|
// METHODS
|
|
|
|
|
|
|
|
|
|
|
|
void doubleOrDetect(AstChangeDet* changep, bool& gotOne) {
|
|
|
|
|
|
if (!changep->rhsp()) {
|
|
|
|
|
|
if (!gotOne) gotOne = true;
|
|
|
|
|
|
else puts(" | ");
|
|
|
|
|
|
changep->lhsp()->iterateAndNext(*this);
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
2012-04-10 02:17:51 +02:00
|
|
|
|
AstNode* lhsp = changep->lhsp();
|
|
|
|
|
|
AstNode* rhsp = changep->rhsp();
|
2015-02-10 03:05:27 +01:00
|
|
|
|
static int addDoubleOr = 10; // Determined experimentally as best
|
2012-04-10 02:17:51 +02:00
|
|
|
|
if (!lhsp->castVarRef() && !lhsp->castArraySel()) changep->v3fatalSrc("Not ref?");
|
|
|
|
|
|
if (!rhsp->castVarRef() && !rhsp->castArraySel()) changep->v3fatalSrc("Not ref?");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
for (int word=0; word<changep->lhsp()->widthWords(); word++) {
|
|
|
|
|
|
if (!gotOne) {
|
|
|
|
|
|
gotOne = true;
|
|
|
|
|
|
addDoubleOr = 10; // Determined experimentally as best
|
|
|
|
|
|
puts("(");
|
|
|
|
|
|
} else if (--addDoubleOr == 0) {
|
|
|
|
|
|
puts("|| (");
|
|
|
|
|
|
addDoubleOr = 10;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts(" | (");
|
|
|
|
|
|
}
|
|
|
|
|
|
changep->lhsp()->iterateAndNext(*this);
|
2012-02-21 02:48:13 +01:00
|
|
|
|
if (changep->lhsp()->isWide()) puts("["+cvtToStr(word)+"]");
|
2014-04-02 04:01:25 +02:00
|
|
|
|
if (changep->lhsp()->isDouble()) puts(" != ");
|
|
|
|
|
|
else puts(" ^ ");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
changep->rhsp()->iterateAndNext(*this);
|
2012-02-21 02:48:13 +01:00
|
|
|
|
if (changep->lhsp()->isWide()) puts("["+cvtToStr(word)+"]");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
V3OutCFile* newOutCFile(AstNodeModule* modp, bool slow, bool source, int filenum=0) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
string filenameNoExt = v3Global.opt.makeDir()+"/"+ modClassName(modp);
|
|
|
|
|
|
if (filenum) filenameNoExt += "__"+cvtToStr(filenum);
|
|
|
|
|
|
filenameNoExt += (slow ? "__Slow":"");
|
|
|
|
|
|
V3OutCFile* ofp = NULL;
|
2007-04-18 20:26:38 +02:00
|
|
|
|
if (v3Global.opt.lintOnly()) {
|
|
|
|
|
|
// Unfortunately we have some lint checks here, so we can't just skip processing.
|
|
|
|
|
|
// We should move them to a different stage.
|
2013-11-29 14:28:48 +01:00
|
|
|
|
string filename = VL_DEV_NULL;
|
2007-04-18 20:26:38 +02:00
|
|
|
|
newCFile(filename, slow, source);
|
2017-09-08 03:08:49 +02:00
|
|
|
|
ofp = new V3OutCFile (filename);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
else if (optSystemC()) {
|
|
|
|
|
|
string filename = filenameNoExt+(source?".cpp":".h");
|
|
|
|
|
|
newCFile(filename, slow, source);
|
|
|
|
|
|
ofp = new V3OutScFile (filename);
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
string filename = filenameNoExt+(source?".cpp":".h");
|
|
|
|
|
|
newCFile(filename, slow, source);
|
|
|
|
|
|
ofp = new V3OutCFile (filename);
|
|
|
|
|
|
}
|
2009-12-03 03:15:56 +01:00
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp->putsHeader();
|
2009-12-03 03:15:56 +01:00
|
|
|
|
if (modp->isTop() && !source) {
|
|
|
|
|
|
ofp->puts("// DESCR" "IPTION: Verilator output: Primary design header\n");
|
|
|
|
|
|
ofp->puts("//\n");
|
|
|
|
|
|
ofp->puts("// This header should be included by all source files instantiating the design.\n");
|
|
|
|
|
|
ofp->puts("// The class here is then constructed to instantiate the design.\n");
|
|
|
|
|
|
ofp->puts("// See the Verilator manual for examples.\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (source) {
|
|
|
|
|
|
ofp->puts("// DESCR" "IPTION: Verilator output: Design implementation internals\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ofp->puts("// DESCR" "IPTION: Verilator output: Design internal header\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
ofp->puts("// See "+v3Global.opt.prefix()+".h for the primary calling header\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
ofp->puts("\n");
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
return ofp;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------
|
|
|
|
|
|
// VISITORS
|
2018-01-25 02:19:52 +01:00
|
|
|
|
using EmitCStmts::visit; // Suppress hidden overloaded virtual function warning
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCFunc* nodep) {
|
2009-12-03 12:55:29 +01:00
|
|
|
|
// TRACE_* and DPI handled elsewhere
|
|
|
|
|
|
if (nodep->funcType().isTrace()) return;
|
|
|
|
|
|
if (nodep->dpiImport()) return;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (!(nodep->slow() ? m_slow : m_fast)) return;
|
|
|
|
|
|
|
|
|
|
|
|
m_blkChangeDetVec.clear();
|
|
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
|
splitSizeInc(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
puts("\n");
|
2017-11-06 03:47:55 +01:00
|
|
|
|
if (nodep->ifdef()!="") puts("#ifdef "+nodep->ifdef()+"\n");
|
2014-11-27 16:52:38 +01:00
|
|
|
|
if (nodep->isInline()) puts("VL_INLINE_OPT ");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(nodep->rtnTypeVoid()); puts(" ");
|
|
|
|
|
|
puts(modClassName(m_modp)+"::"+nodep->name()
|
|
|
|
|
|
+"("+cFuncArgs(nodep)+") {\n");
|
|
|
|
|
|
|
2017-10-25 04:56:58 +02:00
|
|
|
|
// "+" in the debug indicates a print from the model
|
|
|
|
|
|
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ ");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
for (int i=0;i<m_modp->level();i++) { puts(" "); }
|
|
|
|
|
|
puts(modClassName(m_modp)+"::"+nodep->name()
|
2010-01-15 03:03:06 +01:00
|
|
|
|
+"\\n\"); );\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2018-05-30 01:49:27 +02:00
|
|
|
|
// Declare and set vlTOPp
|
2006-08-30 23:07:55 +02:00
|
|
|
|
if (nodep->symProlog()) puts(EmitCBaseVisitor::symTopAssign()+"\n");
|
|
|
|
|
|
|
2016-09-14 04:28:07 +02:00
|
|
|
|
if (nodep->initsp()) putsDecoration("// Variables\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
for (AstNode* subnodep=nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
|
|
|
|
|
|
if (AstVar* varp=subnodep->castVar()) {
|
|
|
|
|
|
if (varp->isFuncReturn()) emitVarDecl(varp, "");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-12-09 17:52:35 +01:00
|
|
|
|
emitVarList(nodep->initsp(), EVL_FUNC_ALL, "");
|
|
|
|
|
|
emitVarList(nodep->stmtsp(), EVL_FUNC_ALL, "");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
nodep->initsp()->iterateAndNext(*this);
|
|
|
|
|
|
|
2016-09-14 04:28:07 +02:00
|
|
|
|
if (nodep->stmtsp()) putsDecoration("// Body\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->stmtsp()->iterateAndNext(*this);
|
|
|
|
|
|
if (!m_blkChangeDetVec.empty()) emitChangeDet();
|
|
|
|
|
|
|
2016-09-14 04:28:07 +02:00
|
|
|
|
if (nodep->finalsp()) putsDecoration("// Final\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->finalsp()->iterateAndNext(*this);
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
if (!m_blkChangeDetVec.empty()) puts("return __req;\n");
|
|
|
|
|
|
|
2013-05-19 02:17:17 +02:00
|
|
|
|
//puts("__Vm_activity = true;\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("}\n");
|
2017-11-06 03:47:55 +01:00
|
|
|
|
if (nodep->ifdef()!="") puts("#endif // "+nodep->ifdef()+"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void emitChangeDet() {
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Change detection\n");
|
2014-05-10 18:40:35 +02:00
|
|
|
|
puts("QData __req = false; // Logically a bool\n"); // But not because it results in faster code
|
2006-08-26 13:35:28 +02:00
|
|
|
|
bool gotOne = false;
|
|
|
|
|
|
for (vector<AstChangeDet*>::iterator it = m_blkChangeDetVec.begin();
|
|
|
|
|
|
it != m_blkChangeDetVec.end(); ++it) {
|
|
|
|
|
|
AstChangeDet* changep = *it;
|
|
|
|
|
|
if (changep->lhsp()) {
|
|
|
|
|
|
if (!gotOne) { // Not a clocked block
|
|
|
|
|
|
puts("__req |= (");
|
|
|
|
|
|
}
|
|
|
|
|
|
else puts("\n");
|
|
|
|
|
|
doubleOrDetect(changep, gotOne);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (gotOne) {
|
|
|
|
|
|
puts(");\n");
|
|
|
|
|
|
//puts("VL_DEBUG_IF( if (__req) cout<<\"\tCLOCKREQ );");
|
2007-01-31 22:49:13 +01:00
|
|
|
|
for (vector<AstChangeDet*>::iterator it = m_blkChangeDetVec.begin();
|
|
|
|
|
|
it != m_blkChangeDetVec.end(); ++it) {
|
|
|
|
|
|
AstChangeDet* nodep = *it;
|
|
|
|
|
|
if (nodep->lhsp()) {
|
|
|
|
|
|
puts("VL_DEBUG_IF( if(__req && (");
|
|
|
|
|
|
bool gotOneIgnore = false;
|
|
|
|
|
|
doubleOrDetect(nodep, gotOneIgnore);
|
|
|
|
|
|
string varname;
|
|
|
|
|
|
if (nodep->lhsp()->castVarRef()) {
|
|
|
|
|
|
varname = ": "+nodep->lhsp()->castVarRef()->varp()->prettyName();
|
|
|
|
|
|
}
|
2017-10-25 04:56:58 +02:00
|
|
|
|
puts(")) VL_DBG_MSGF(\" CHANGE: "+nodep->fileline()->ascii()
|
2010-01-15 03:03:06 +01:00
|
|
|
|
+varname+"\\n\"); );\n");
|
2007-01-31 22:49:13 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstChangeDet* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
m_blkChangeDetVec.push_back(nodep);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCReset* nodep) {
|
2016-05-12 13:19:02 +02:00
|
|
|
|
AstVar* varp = nodep->varrefp()->varp();
|
|
|
|
|
|
emitVarReset(varp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
//---------------------------------------
|
|
|
|
|
|
// ACCESSORS
|
|
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
|
// Low level
|
2016-05-12 13:19:02 +02:00
|
|
|
|
void emitVarReset(AstVar* modp);
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void emitCellCtors(AstNodeModule* modp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
void emitSensitives();
|
|
|
|
|
|
// Medium level
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void emitCtorImp(AstNodeModule* modp);
|
|
|
|
|
|
void emitConfigureImp(AstNodeModule* modp);
|
|
|
|
|
|
void emitCoverageDecl(AstNodeModule* modp);
|
|
|
|
|
|
void emitCoverageImp(AstNodeModule* modp);
|
|
|
|
|
|
void emitDestructorImp(AstNodeModule* modp);
|
2012-08-27 03:13:47 +02:00
|
|
|
|
void emitSavableImp(AstNodeModule* modp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
void emitTextSection(AstType type);
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void emitIntFuncDecls(AstNodeModule* modp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// High level
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void emitImp(AstNodeModule* modp);
|
|
|
|
|
|
void emitStaticDecl(AstNodeModule* modp);
|
2018-06-15 00:59:24 +02:00
|
|
|
|
void emitSettleLoop(const std::string& eval_call, bool initial);
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void emitWrapEval(AstNodeModule* modp);
|
|
|
|
|
|
void emitInt(AstNodeModule* modp);
|
2018-05-30 01:49:27 +02:00
|
|
|
|
void maybeSplit(AstNodeModule* modp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
EmitCImp() {
|
|
|
|
|
|
m_modp = NULL;
|
2011-08-05 03:58:45 +02:00
|
|
|
|
m_slow = false;
|
|
|
|
|
|
m_fast = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
virtual ~EmitCImp() {}
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void main(AstNodeModule* modp, bool slow, bool fast);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
void mainDoFunc(AstCFunc* nodep) {
|
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
// Internal EmitCStmts
|
|
|
|
|
|
|
|
|
|
|
|
void EmitCStmts::emitVarDecl(AstVar* nodep, const string& prefixIfImp) {
|
2009-11-15 14:52:19 +01:00
|
|
|
|
AstBasicDType* basicp = nodep->basicp(); if (!basicp) nodep->v3fatalSrc("Unimplemented: Outputting this data type");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (nodep->isIO()) {
|
|
|
|
|
|
if (nodep->isSc()) {
|
|
|
|
|
|
m_ctorVarsVec.push_back(nodep);
|
|
|
|
|
|
if (nodep->attrScClocked() && nodep->isInput()) {
|
2017-11-14 00:24:18 +01:00
|
|
|
|
puts("sc_in_clk ");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2006-09-25 22:40:52 +02:00
|
|
|
|
if (nodep->isInout()) puts("sc_inout<");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
else if (nodep->isInput()) puts("sc_in<");
|
|
|
|
|
|
else if (nodep->isOutput()) puts("sc_out<");
|
|
|
|
|
|
else nodep->v3fatalSrc("Unknown type");
|
|
|
|
|
|
|
2006-10-05 16:53:17 +02:00
|
|
|
|
puts(nodep->scType());
|
2017-11-14 00:24:18 +01:00
|
|
|
|
puts("> ");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
puts(nodep->name());
|
2013-05-19 02:17:17 +02:00
|
|
|
|
emitDeclArrayBrackets(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(";\n");
|
2014-04-16 00:50:04 +02:00
|
|
|
|
} else if (basicp && basicp->isOpaque()) {
|
|
|
|
|
|
// strings and other fundamental c types; no VL_ macro can be used
|
|
|
|
|
|
puts(nodep->vlArgType(true,false,false));
|
|
|
|
|
|
emitDeclArrayBrackets(nodep);
|
|
|
|
|
|
puts(";\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else { // C++ signals
|
2006-09-25 22:40:52 +02:00
|
|
|
|
if (nodep->isInout()) puts("VL_INOUT");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
else if (nodep->isInput()) puts("VL_IN");
|
|
|
|
|
|
else if (nodep->isOutput()) puts("VL_OUT");
|
|
|
|
|
|
else nodep->v3fatalSrc("Unknown type");
|
|
|
|
|
|
|
2013-05-19 02:17:17 +02:00
|
|
|
|
if (nodep->isQuad()) puts("64");
|
|
|
|
|
|
else if (nodep->widthMin() <= 8) puts("8");
|
|
|
|
|
|
else if (nodep->widthMin() <= 16) puts("16");
|
|
|
|
|
|
else if (nodep->isWide()) puts("W");
|
|
|
|
|
|
|
|
|
|
|
|
puts("("+nodep->name());
|
|
|
|
|
|
emitDeclArrayBrackets(nodep);
|
|
|
|
|
|
// If it's a packed struct/array then nodep->width is the whole thing, msb/lsb is just lowest dimension
|
|
|
|
|
|
puts(","+cvtToStr(basicp->lsb()+nodep->width()-1)
|
|
|
|
|
|
+","+cvtToStr(basicp->lsb()));
|
|
|
|
|
|
if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords()));
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
2010-01-17 21:10:37 +01:00
|
|
|
|
} else if (basicp && basicp->isOpaque()) {
|
|
|
|
|
|
// strings and other fundamental c types
|
2014-04-16 00:50:04 +02:00
|
|
|
|
puts(nodep->vlArgType(true,false,false));
|
2013-05-19 02:17:17 +02:00
|
|
|
|
emitDeclArrayBrackets(nodep);
|
2010-01-17 21:10:37 +01:00
|
|
|
|
puts(";\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
// Arrays need a small alignment, but may need different padding after.
|
|
|
|
|
|
// For example three VL_SIG8's needs alignment 1 but size 3.
|
2006-08-29 02:58:48 +02:00
|
|
|
|
if (nodep->isStatic() && prefixIfImp=="") puts("static ");
|
2006-08-29 14:01:02 +02:00
|
|
|
|
if (nodep->isStatic()) puts("VL_ST_"); else puts("VL_");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (nodep->widthMin() <= 8) {
|
2006-08-29 14:01:02 +02:00
|
|
|
|
puts("SIG8(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (nodep->widthMin() <= 16) {
|
2006-08-29 14:01:02 +02:00
|
|
|
|
puts("SIG16(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (nodep->isQuad()) {
|
2006-08-29 14:01:02 +02:00
|
|
|
|
puts("SIG64(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (!nodep->isWide()) {
|
2006-08-29 14:01:02 +02:00
|
|
|
|
puts("SIG(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2006-08-29 14:01:02 +02:00
|
|
|
|
puts("SIGW(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
if (prefixIfImp!="") { puts(prefixIfImp); puts("::"); }
|
|
|
|
|
|
puts(nodep->name());
|
2013-05-19 02:17:17 +02:00
|
|
|
|
emitDeclArrayBrackets(nodep);
|
2013-01-15 03:49:22 +01:00
|
|
|
|
// If it's a packed struct/array then nodep->width is the whole thing, msb/lsb is just lowest dimension
|
2013-05-19 02:17:17 +02:00
|
|
|
|
puts(","+cvtToStr(basicp->lsb()+nodep->width()-1)
|
|
|
|
|
|
+","+cvtToStr(basicp->lsb()));
|
2013-01-15 03:49:22 +01:00
|
|
|
|
if (nodep->isWide()) puts(","+cvtToStr(nodep->widthWords()));
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-30 01:49:27 +02:00
|
|
|
|
void EmitCStmts::emitCtorSep(bool* firstp) {
|
|
|
|
|
|
if (*firstp) {
|
|
|
|
|
|
puts(" : "); *firstp = false;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts(", ");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ofp()->exceededWidth()) puts("\n ");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EmitCStmts::emitVarCtors(bool* firstp) {
|
2012-02-02 02:20:43 +01:00
|
|
|
|
if (!m_ctorVarsVec.empty()) {
|
|
|
|
|
|
ofp()->indentInc();
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
puts("#if (SYSTEMC_VERSION>20011000)\n"); // SystemC 2.0.1 and newer
|
|
|
|
|
|
for (vector<AstVar*>::iterator it = m_ctorVarsVec.begin(); it != m_ctorVarsVec.end(); ++it) {
|
|
|
|
|
|
AstVar* varp = *it;
|
|
|
|
|
|
bool isArray = !varp->dtypeSkipRefp()->castBasicDType();
|
|
|
|
|
|
if (isArray) {
|
|
|
|
|
|
puts("// Skipping array: ");
|
|
|
|
|
|
puts(varp->name());
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
} else {
|
2018-05-30 01:49:27 +02:00
|
|
|
|
emitCtorSep(firstp);
|
2012-02-02 02:20:43 +01:00
|
|
|
|
puts(varp->name());
|
|
|
|
|
|
puts("("); putsQuoted(varp->name()); puts(")");
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2012-02-02 02:20:43 +01:00
|
|
|
|
puts ("\n#endif\n");
|
|
|
|
|
|
ofp()->indentDec();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool EmitCStmts::emitSimpleOk(AstNodeMath* nodep) {
|
|
|
|
|
|
// Can we put out a simple (A + B) instead of VL_ADD_III(A,B)?
|
|
|
|
|
|
if (nodep->emitSimpleOperator() == "") return false;
|
|
|
|
|
|
if (nodep->isWide()) return false;
|
|
|
|
|
|
if (nodep->op1p()) { if (nodep->op1p()->isWide()) return false; }
|
|
|
|
|
|
if (nodep->op2p()) { if (nodep->op2p()->isWide()) return false; }
|
|
|
|
|
|
if (nodep->op3p()) { if (nodep->op3p()->isWide()) return false; }
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-06-30 02:02:24 +02:00
|
|
|
|
void EmitCStmts::emitOpName(AstNode* nodep, const string& format,
|
|
|
|
|
|
AstNode* lhsp, AstNode* rhsp, AstNode* thsp) {
|
|
|
|
|
|
// Look at emitOperator() format for term/uni/dual/triops,
|
|
|
|
|
|
// and write out appropriate text.
|
|
|
|
|
|
// %n* node
|
|
|
|
|
|
// %nq emitIQW on the [node]
|
|
|
|
|
|
// %nw width in bits
|
|
|
|
|
|
// %nW width in words
|
|
|
|
|
|
// %ni iterate
|
|
|
|
|
|
// %l* lhsp - if appropriate, then second char as above
|
|
|
|
|
|
// %r* rhsp - if appropriate, then second char as above
|
|
|
|
|
|
// %t* thsp - if appropriate, then second char as above
|
|
|
|
|
|
// %k Potential line break
|
|
|
|
|
|
// %P Wide temporary name
|
|
|
|
|
|
// , Commas suppressed if the previous field is suppressed
|
|
|
|
|
|
string nextComma;
|
|
|
|
|
|
bool needComma = false;
|
|
|
|
|
|
#define COMMA { if (nextComma!="") { puts(nextComma); nextComma=""; } }
|
|
|
|
|
|
|
|
|
|
|
|
putbs("");
|
|
|
|
|
|
for (string::const_iterator pos = format.begin(); pos != format.end(); ++pos) {
|
|
|
|
|
|
if (pos[0]==',') {
|
|
|
|
|
|
// Remember we need to add one, but don't do yet to avoid ",)"
|
|
|
|
|
|
if (needComma) {
|
|
|
|
|
|
if (pos[1]==' ') { nextComma=", "; }
|
|
|
|
|
|
else nextComma = ",";
|
|
|
|
|
|
needComma = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (pos[1]==' ') { ++pos; } // Must do even if no nextComma
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (pos[0]=='%') {
|
|
|
|
|
|
++pos;
|
|
|
|
|
|
bool detail = false;
|
|
|
|
|
|
AstNode* detailp = NULL;
|
|
|
|
|
|
switch (pos[0]) {
|
|
|
|
|
|
case '%': puts("%"); break;
|
|
|
|
|
|
case 'k': putbs(""); break;
|
|
|
|
|
|
case 'n': detail = true; detailp = nodep; break;
|
|
|
|
|
|
case 'l': detail = true; detailp = lhsp; break;
|
|
|
|
|
|
case 'r': detail = true; detailp = rhsp; break;
|
|
|
|
|
|
case 't': detail = true; detailp = thsp; break;
|
|
|
|
|
|
case 'P':
|
|
|
|
|
|
if (nodep->isWide()) {
|
|
|
|
|
|
if (!m_wideTempRefp) nodep->v3fatalSrc("Wide Op w/ no temp, perhaps missing op in V3EmitC?");
|
|
|
|
|
|
COMMA;
|
|
|
|
|
|
puts(m_wideTempRefp->hiername());
|
|
|
|
|
|
puts(m_wideTempRefp->varp()->name());
|
|
|
|
|
|
m_wideTempRefp = NULL;
|
|
|
|
|
|
needComma = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
nodep->v3fatalSrc("Unknown emitOperator format code: %"<<pos[0]);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (detail) {
|
|
|
|
|
|
// Get next letter of %[nlrt]
|
|
|
|
|
|
++pos;
|
|
|
|
|
|
switch (pos[0]) {
|
|
|
|
|
|
case 'q': emitIQW(detailp); break;
|
2012-03-20 21:01:53 +01:00
|
|
|
|
case 'w':
|
2008-06-30 02:02:24 +02:00
|
|
|
|
COMMA;
|
|
|
|
|
|
puts(cvtToStr(detailp->widthMin()));
|
|
|
|
|
|
needComma = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'W':
|
|
|
|
|
|
if (lhsp->isWide()) {
|
|
|
|
|
|
COMMA;
|
|
|
|
|
|
puts(cvtToStr(lhsp->widthWords()));
|
|
|
|
|
|
needComma = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'i':
|
|
|
|
|
|
COMMA;
|
|
|
|
|
|
if (!detailp) { nodep->v3fatalSrc("emitOperator() references undef node"); }
|
|
|
|
|
|
else detailp->iterateAndNext(*this);
|
|
|
|
|
|
needComma = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
nodep->v3fatalSrc("Unknown emitOperator format code: %[nlrt]"<<pos[0]);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-04-05 21:44:49 +02:00
|
|
|
|
} else if (pos[0] == ')') {
|
|
|
|
|
|
nextComma=""; puts(")");
|
|
|
|
|
|
} else if (pos[0] == '(') {
|
|
|
|
|
|
COMMA; needComma = false; puts("(");
|
2008-06-30 02:02:24 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
// Normal text
|
2014-04-05 21:44:49 +02:00
|
|
|
|
if (isalnum(pos[0])) needComma = true;
|
2008-06-30 02:02:24 +02:00
|
|
|
|
COMMA;
|
|
|
|
|
|
string s; s+=pos[0]; puts(s);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
// Mid level - VISITS
|
|
|
|
|
|
|
|
|
|
|
|
// We only do one display at once, so can just use static state
|
|
|
|
|
|
|
|
|
|
|
|
struct EmitDispState {
|
|
|
|
|
|
string m_format; // "%s" and text from user
|
2014-11-28 21:01:50 +01:00
|
|
|
|
vector<char> m_argsChar; // Format of each argument to be printed
|
2006-08-26 13:35:28 +02:00
|
|
|
|
vector<AstNode*> m_argsp; // Each argument to be printed
|
|
|
|
|
|
vector<string> m_argsFunc; // Function before each argument to be printed
|
|
|
|
|
|
EmitDispState() { clear(); }
|
|
|
|
|
|
void clear() {
|
|
|
|
|
|
m_format = "";
|
2014-11-28 21:01:50 +01:00
|
|
|
|
m_argsChar.clear();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
m_argsp.clear();
|
|
|
|
|
|
m_argsFunc.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
void pushFormat(const string& fmt) { m_format += fmt; }
|
|
|
|
|
|
void pushFormat(char fmt) { m_format += fmt; }
|
2014-11-28 21:01:50 +01:00
|
|
|
|
void pushArg(char fmtChar, AstNode* nodep, const string& func) {
|
|
|
|
|
|
m_argsChar.push_back(fmtChar);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
m_argsp.push_back(nodep); m_argsFunc.push_back(func);
|
|
|
|
|
|
}
|
|
|
|
|
|
} emitDispState;
|
|
|
|
|
|
|
2008-07-01 20:15:10 +02:00
|
|
|
|
void EmitCStmts::displayEmit(AstNode* nodep, bool isScan) {
|
|
|
|
|
|
if (emitDispState.m_format == ""
|
|
|
|
|
|
&& nodep->castDisplay()) { // not fscanf etc, as they need to return value
|
|
|
|
|
|
// NOP
|
|
|
|
|
|
} else {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Format
|
2010-01-19 16:29:13 +01:00
|
|
|
|
bool isStmt = false;
|
2008-07-01 20:15:10 +02:00
|
|
|
|
if (AstFScanF* dispp = nodep->castFScanF()) {
|
|
|
|
|
|
isStmt = false;
|
|
|
|
|
|
puts("VL_FSCANF_IX(");
|
|
|
|
|
|
dispp->filep()->iterate(*this);
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
} else if (AstSScanF* dispp = nodep->castSScanF()) {
|
|
|
|
|
|
isStmt = false;
|
|
|
|
|
|
checkMaxWords(dispp->fromp());
|
|
|
|
|
|
puts("VL_SSCANF_I"); emitIQW(dispp->fromp()); puts("X(");
|
|
|
|
|
|
puts(cvtToStr(dispp->fromp()->widthMin()));
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
dispp->fromp()->iterate(*this);
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
} else if (AstDisplay* dispp = nodep->castDisplay()) {
|
|
|
|
|
|
isStmt = true;
|
|
|
|
|
|
if (dispp->filep()) {
|
|
|
|
|
|
puts("VL_FWRITEF(");
|
|
|
|
|
|
dispp->filep()->iterate(*this);
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("VL_WRITEF(");
|
|
|
|
|
|
}
|
2009-11-24 03:24:55 +01:00
|
|
|
|
} else if (AstSFormat* dispp = nodep->castSFormat()) {
|
|
|
|
|
|
isStmt = true;
|
|
|
|
|
|
puts("VL_SFORMAT_X(");
|
|
|
|
|
|
puts(cvtToStr(dispp->lhsp()->widthMin()));
|
|
|
|
|
|
putbs(",");
|
|
|
|
|
|
dispp->lhsp()->iterate(*this);
|
|
|
|
|
|
putbs(",");
|
2010-01-18 01:13:44 +01:00
|
|
|
|
} else if (AstSFormatF* dispp = nodep->castSFormatF()) {
|
2010-01-19 16:29:13 +01:00
|
|
|
|
isStmt = false;
|
2010-01-18 01:13:44 +01:00
|
|
|
|
if (dispp) {}
|
|
|
|
|
|
puts("VL_SFORMATF_NX(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2008-07-01 20:15:10 +02:00
|
|
|
|
nodep->v3fatalSrc("Unknown displayEmit node type");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2009-10-23 03:16:52 +02:00
|
|
|
|
ofp()->putsQuoted(emitDispState.m_format);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Arguments
|
|
|
|
|
|
for (unsigned i=0; i < emitDispState.m_argsp.size(); i++) {
|
|
|
|
|
|
puts(",");
|
2014-11-28 21:01:50 +01:00
|
|
|
|
char fmt = emitDispState.m_argsChar[i];
|
2006-08-26 13:35:28 +02:00
|
|
|
|
AstNode* argp = emitDispState.m_argsp[i];
|
|
|
|
|
|
string func = emitDispState.m_argsFunc[i];
|
|
|
|
|
|
ofp()->indentInc();
|
|
|
|
|
|
ofp()->putbs("");
|
|
|
|
|
|
if (func!="") puts(func);
|
2008-07-01 20:15:10 +02:00
|
|
|
|
if (argp) {
|
|
|
|
|
|
if (isScan) puts("&(");
|
2014-11-28 21:01:50 +01:00
|
|
|
|
else if (fmt == '@') puts("&(");
|
2008-07-01 20:15:10 +02:00
|
|
|
|
argp->iterate(*this);
|
|
|
|
|
|
if (isScan) puts(")");
|
2014-11-28 21:01:50 +01:00
|
|
|
|
else if (fmt == '@') puts(")");
|
2008-07-01 20:15:10 +02:00
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->indentDec();
|
|
|
|
|
|
}
|
|
|
|
|
|
// End
|
2008-07-01 20:15:10 +02:00
|
|
|
|
puts(")");
|
|
|
|
|
|
if (isStmt) puts(";\n");
|
|
|
|
|
|
else puts(" ");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Prep for next
|
|
|
|
|
|
emitDispState.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-07-01 20:15:10 +02:00
|
|
|
|
void EmitCStmts::displayArg(AstNode* dispp, AstNode** elistp, bool isScan,
|
2017-07-07 02:25:59 +02:00
|
|
|
|
const string& vfmt, char fmtLetter) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Print display argument, edits elistp
|
2008-06-30 20:31:58 +02:00
|
|
|
|
AstNode* argp = *elistp;
|
|
|
|
|
|
if (!argp) {
|
|
|
|
|
|
// expectDisplay() checks this first, so internal error if found here
|
2012-05-03 03:04:50 +02:00
|
|
|
|
dispp->v3error("Internal: Missing arguments for $display-like format");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2008-06-30 20:31:58 +02:00
|
|
|
|
if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
|
2012-05-03 03:04:50 +02:00
|
|
|
|
dispp->v3error("Exceeded limit of "+cvtToStr(VL_VALUE_STRING_MAX_WIDTH)+" bits for any $display-like arguments");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2008-06-30 20:31:58 +02:00
|
|
|
|
if (argp && argp->widthMin()>8 && fmtLetter=='c') {
|
|
|
|
|
|
// Technically legal, but surely not what the user intended.
|
2015-11-11 03:12:15 +01:00
|
|
|
|
argp->v3warn(WIDTH,dispp->verilogKwd()<<"of %c format of > 8 bit value");
|
2008-06-30 20:31:58 +02:00
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2008-06-30 20:31:58 +02:00
|
|
|
|
//string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
|
|
|
|
|
|
string pfmt;
|
2015-11-11 03:12:15 +01:00
|
|
|
|
if ((fmtLetter=='#' || fmtLetter=='d' || fmtLetter=='t')
|
2008-07-01 20:15:10 +02:00
|
|
|
|
&& !isScan
|
2008-06-30 20:31:58 +02:00
|
|
|
|
&& vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros
|
|
|
|
|
|
double mantissabits = argp->widthMin() - ((fmtLetter=='d')?1:0);
|
|
|
|
|
|
double maxval = pow(2.0, mantissabits);
|
|
|
|
|
|
double dchars = log10(maxval)+1.0;
|
|
|
|
|
|
if (fmtLetter=='d') dchars++; // space for sign
|
|
|
|
|
|
int nchars = int(dchars);
|
|
|
|
|
|
pfmt = string("%") + cvtToStr(nchars) + fmtLetter;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2008-06-30 20:31:58 +02:00
|
|
|
|
pfmt = string("%") + vfmt + fmtLetter;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2008-06-30 20:31:58 +02:00
|
|
|
|
emitDispState.pushFormat(pfmt);
|
2014-11-28 21:01:50 +01:00
|
|
|
|
emitDispState.pushArg(' ',NULL,cvtToStr(argp->widthMin()));
|
|
|
|
|
|
emitDispState.pushArg(fmtLetter,argp,"");
|
2008-06-30 20:31:58 +02:00
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Next parameter
|
|
|
|
|
|
*elistp = (*elistp)->nextp();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-17 21:53:12 +01:00
|
|
|
|
void EmitCStmts::displayNode(AstNode* nodep, AstScopeName* scopenamep,
|
|
|
|
|
|
const string& vformat, AstNode* exprsp,
|
2008-07-01 20:15:10 +02:00
|
|
|
|
bool isScan) {
|
|
|
|
|
|
AstNode* elistp = exprsp;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
// Convert Verilog display to C printf formats
|
|
|
|
|
|
// "%0t" becomes "%d"
|
|
|
|
|
|
emitDispState.clear();
|
2008-06-30 20:31:58 +02:00
|
|
|
|
string vfmt = "";
|
2008-07-01 20:15:10 +02:00
|
|
|
|
string::const_iterator pos = vformat.begin();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
bool inPct = false;
|
|
|
|
|
|
for (; pos != vformat.end(); ++pos) {
|
2008-06-30 20:31:58 +02:00
|
|
|
|
//UINFO(1,"Parse '"<<*pos<<"' IP"<<inPct<<" List "<<(void*)(elistp)<<endl);
|
|
|
|
|
|
if (!inPct && pos[0]=='%') {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
inPct = true;
|
2008-06-30 20:31:58 +02:00
|
|
|
|
vfmt = "";
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (!inPct) { // Normal text
|
|
|
|
|
|
emitDispState.pushFormat(*pos);
|
|
|
|
|
|
} else { // Format character
|
2008-06-30 20:31:58 +02:00
|
|
|
|
inPct = false;
|
|
|
|
|
|
switch (tolower(pos[0])) {
|
|
|
|
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
|
|
|
|
case '5': case '6': case '7': case '8': case '9':
|
2011-07-24 01:58:34 +02:00
|
|
|
|
case '.':
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Digits, like %5d, etc.
|
2008-06-30 20:31:58 +02:00
|
|
|
|
vfmt += pos[0];
|
|
|
|
|
|
inPct = true; // Get more digits
|
|
|
|
|
|
break;
|
|
|
|
|
|
case '%':
|
|
|
|
|
|
emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the %
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Special codes
|
2008-07-01 20:15:10 +02:00
|
|
|
|
case '~': displayArg(nodep,&elistp,isScan, vfmt,'d'); break; // Signed decimal
|
2014-11-28 21:01:50 +01:00
|
|
|
|
case '@': displayArg(nodep,&elistp,isScan, vfmt,'@'); break; // Packed string
|
2008-06-30 20:31:58 +02:00
|
|
|
|
// Spec: h d o b c l
|
2008-07-01 20:15:10 +02:00
|
|
|
|
case 'b': displayArg(nodep,&elistp,isScan, vfmt,'b'); break;
|
|
|
|
|
|
case 'c': displayArg(nodep,&elistp,isScan, vfmt,'c'); break;
|
2008-08-05 20:45:20 +02:00
|
|
|
|
case 't': displayArg(nodep,&elistp,isScan, vfmt,'t'); break;
|
2015-11-11 03:12:15 +01:00
|
|
|
|
case 'd': displayArg(nodep,&elistp,isScan, vfmt,'#'); break; // Unsigned decimal
|
2008-07-01 20:15:10 +02:00
|
|
|
|
case 'o': displayArg(nodep,&elistp,isScan, vfmt,'o'); break;
|
2015-11-11 03:12:15 +01:00
|
|
|
|
case 'h': //FALLTHRU
|
2008-07-01 20:15:10 +02:00
|
|
|
|
case 'x': displayArg(nodep,&elistp,isScan, vfmt,'x'); break;
|
|
|
|
|
|
case 's': displayArg(nodep,&elistp,isScan, vfmt,'s'); break;
|
2011-07-24 01:58:34 +02:00
|
|
|
|
case 'e': displayArg(nodep,&elistp,isScan, vfmt,'e'); break;
|
|
|
|
|
|
case 'f': displayArg(nodep,&elistp,isScan, vfmt,'f'); break;
|
|
|
|
|
|
case 'g': displayArg(nodep,&elistp,isScan, vfmt,'g'); break;
|
2015-11-11 03:12:15 +01:00
|
|
|
|
case 'v': displayArg(nodep,&elistp,isScan, vfmt,'v'); break;
|
2008-06-30 20:31:58 +02:00
|
|
|
|
case 'm': {
|
2008-07-01 20:15:10 +02:00
|
|
|
|
if (!scopenamep) nodep->v3fatalSrc("Display with %m but no AstScopeName");
|
2015-06-17 01:27:18 +02:00
|
|
|
|
string suffix = scopenamep->scopePrettySymName();
|
2010-02-04 01:19:18 +01:00
|
|
|
|
if (suffix=="") emitDispState.pushFormat("%S");
|
|
|
|
|
|
else emitDispState.pushFormat("%N"); // Add a . when needed
|
2014-11-28 21:01:50 +01:00
|
|
|
|
emitDispState.pushArg(' ',NULL, "vlSymsp->name()");
|
2010-02-04 01:19:18 +01:00
|
|
|
|
emitDispState.pushFormat(suffix);
|
2008-06-30 20:31:58 +02:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2015-10-28 01:37:52 +01:00
|
|
|
|
case 'l': {
|
|
|
|
|
|
// Better than not compiling
|
|
|
|
|
|
emitDispState.pushFormat("----");
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2008-06-30 20:31:58 +02:00
|
|
|
|
default:
|
2012-05-03 03:04:50 +02:00
|
|
|
|
nodep->v3error("Unknown $display-like format code: %"<<pos[0]);
|
2008-06-30 20:31:58 +02:00
|
|
|
|
break;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (elistp != NULL) {
|
2008-06-30 20:31:58 +02:00
|
|
|
|
// expectFormat also checks this, and should have found it first, so internal
|
2012-05-03 03:04:50 +02:00
|
|
|
|
elistp->v3error("Internal: Extra arguments for $display-like format");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2008-07-01 20:15:10 +02:00
|
|
|
|
displayEmit(nodep, isScan);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
// Internal EmitC
|
|
|
|
|
|
|
2016-05-12 13:19:02 +02:00
|
|
|
|
void EmitCImp::emitVarReset(AstVar* varp) {
|
|
|
|
|
|
if (varp->isIO() && m_modp->isTop() && optSystemC()) {
|
|
|
|
|
|
// System C top I/O doesn't need loading, as the lower level subinst code does it.}
|
|
|
|
|
|
} else if (varp->isParam()) {
|
|
|
|
|
|
if (!varp->valuep()) varp->v3fatalSrc("No init for a param?");
|
|
|
|
|
|
// If a simple CONST value we initialize it using an enum
|
|
|
|
|
|
// If an ARRAYINIT we initialize it using an initial block similar to a signal
|
|
|
|
|
|
//puts("// parameter "+varp->name()+" = "+varp->valuep()->name()+"\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (AstInitArray* initarp = varp->valuep()->castInitArray()) {
|
|
|
|
|
|
if (AstUnpackArrayDType* arrayp = varp->dtypeSkipRefp()->castUnpackArrayDType()) {
|
2016-06-16 04:46:34 +02:00
|
|
|
|
if (initarp->defaultp()) {
|
|
|
|
|
|
// MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block
|
|
|
|
|
|
puts("{ int __Vi=0;");
|
|
|
|
|
|
puts(" for (; __Vi<"+cvtToStr(arrayp->elementsConst()));
|
|
|
|
|
|
puts("; ++__Vi) {\n");
|
|
|
|
|
|
emitSetVarConstant(varp->name()+"[__Vi]", initarp->defaultp()->castConst());
|
|
|
|
|
|
puts("}}\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
int pos = 0;
|
|
|
|
|
|
for (AstNode* itemp = initarp->initsp(); itemp; ++pos, itemp=itemp->nextp()) {
|
|
|
|
|
|
int index = initarp->posIndex(pos);
|
|
|
|
|
|
if (!initarp->defaultp() && index!=pos) initarp->v3fatalSrc("Not enough values in array initalizement");
|
|
|
|
|
|
emitSetVarConstant(varp->name()+"["+cvtToStr(index)+"]", itemp->castConst());
|
2014-11-29 02:24:42 +01:00
|
|
|
|
}
|
2016-05-12 13:19:02 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
varp->v3fatalSrc("InitArray under non-arrayed var");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (varp->basicp() && varp->basicp()->keyword() == AstBasicDTypeKwd::STRING) {
|
|
|
|
|
|
// Constructor deals with it
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
int vects = 0;
|
|
|
|
|
|
// This isn't very robust and may need cleanup for other data types
|
|
|
|
|
|
for (AstUnpackArrayDType* arrayp=varp->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
|
|
|
|
|
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
|
|
|
|
|
int vecnum = vects++;
|
|
|
|
|
|
if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier.");
|
|
|
|
|
|
string ivar = string("__Vi")+cvtToStr(vecnum);
|
|
|
|
|
|
// MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block
|
|
|
|
|
|
puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";");
|
|
|
|
|
|
puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst()));
|
|
|
|
|
|
puts("; ++"+ivar+") {\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
bool zeroit = (varp->attrFileDescr() // Zero it out, so we don't core dump if never call $fopen
|
|
|
|
|
|
|| (varp->basicp() && varp->basicp()->isZeroInit())
|
2017-10-02 03:31:40 +02:00
|
|
|
|
|| (varp->name().size()>=1 && varp->name()[0]=='_' && v3Global.opt.underlineZero())
|
|
|
|
|
|
|| (v3Global.opt.xInitial() == "fast" || v3Global.opt.xInitial() == "0"));
|
2016-05-12 13:19:02 +02:00
|
|
|
|
if (varp->isWide()) {
|
|
|
|
|
|
// DOCUMENT: We randomize everything. If the user wants a _var to be zero,
|
|
|
|
|
|
// there should be a initial statement. (Different from verilator2.)
|
|
|
|
|
|
if (zeroit) puts("VL_ZERO_RESET_W(");
|
|
|
|
|
|
else puts("VL_RAND_RESET_W(");
|
|
|
|
|
|
puts(cvtToStr(varp->widthMin()));
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
puts(varp->name());
|
|
|
|
|
|
for (int v=0; v<vects; ++v) puts( "[__Vi"+cvtToStr(v)+"]");
|
|
|
|
|
|
puts(");\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts(varp->name());
|
|
|
|
|
|
for (int v=0; v<vects; ++v) puts( "[__Vi"+cvtToStr(v)+"]");
|
|
|
|
|
|
// If --x-initial-edge is set, we want to force an initial
|
|
|
|
|
|
// edge on uninitialized clocks (from 'X' to whatever the
|
|
|
|
|
|
// first value is). Since the class is instantiated before
|
|
|
|
|
|
// initial blocks are evaluated, this should not clash
|
|
|
|
|
|
// with any initial block settings.
|
|
|
|
|
|
if (zeroit || (v3Global.opt.xInitialEdge() && varp->isUsedClock())) {
|
|
|
|
|
|
puts(" = 0;\n");
|
|
|
|
|
|
} else if (v3Global.opt.xInitialEdge()
|
|
|
|
|
|
&& (0 == varp->name().find("__Vclklast__"))) {
|
|
|
|
|
|
puts(" = 1;\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts(" = VL_RAND_RESET_");
|
|
|
|
|
|
emitIQW(varp);
|
|
|
|
|
|
puts("(");
|
|
|
|
|
|
puts(cvtToStr(varp->widthMin()));
|
|
|
|
|
|
puts(");\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-05-12 13:19:02 +02:00
|
|
|
|
for (int v=0; v<vects; ++v) puts( "}}\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-05-12 13:19:02 +02:00
|
|
|
|
splitSizeInc(1);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitCoverageDecl(AstNodeModule* modp) {
|
2008-12-05 16:54:14 +01:00
|
|
|
|
if (v3Global.opt.coverage()) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->putsPrivate(true);
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Coverage\n");
|
2008-12-12 22:04:56 +01:00
|
|
|
|
puts("void __vlCoverInsert(uint32_t* countp, bool enable, const char* filenamep, int lineno, int column,\n");
|
|
|
|
|
|
puts( "const char* hierp, const char* pagep, const char* commentp);\n");
|
2006-08-30 03:14:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitCtorImp(AstNodeModule* modp) {
|
2006-08-30 19:27:53 +02:00
|
|
|
|
puts("\n");
|
2018-05-30 01:49:27 +02:00
|
|
|
|
bool first = true;
|
2017-09-08 03:08:49 +02:00
|
|
|
|
if (optSystemC() && modp->isTop()) {
|
2006-08-30 19:27:53 +02:00
|
|
|
|
puts("VL_SC_CTOR_IMP("+modClassName(modp)+")");
|
|
|
|
|
|
} else {
|
2018-05-30 01:49:27 +02:00
|
|
|
|
puts("VL_CTOR_IMP("+modClassName(modp)+")");
|
|
|
|
|
|
first = false; // VL_CTOR_IMP includes the first ':'
|
2006-08-30 19:27:53 +02:00
|
|
|
|
}
|
2018-05-30 01:49:27 +02:00
|
|
|
|
emitVarCtors(&first);
|
2006-08-30 19:27:53 +02:00
|
|
|
|
puts(" {\n");
|
|
|
|
|
|
emitCellCtors(modp);
|
|
|
|
|
|
emitSensitives();
|
2016-05-12 13:19:02 +02:00
|
|
|
|
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Reset internal values\n");
|
2016-05-12 13:19:02 +02:00
|
|
|
|
if (modp->isTop()) {
|
|
|
|
|
|
if (v3Global.opt.inhibitSim()) puts("__Vm_inhibitSim = false;\n");
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Reset structure values\n");
|
2016-05-12 13:19:02 +02:00
|
|
|
|
puts("_ctor_var_reset();\n");
|
2016-11-05 14:47:56 +01:00
|
|
|
|
emitTextSection(AstType::atScCtor);
|
2006-08-30 19:27:53 +02:00
|
|
|
|
puts("}\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
|
2008-12-05 16:54:14 +01:00
|
|
|
|
puts("\nvoid "+modClassName(modp)+"::__Vconfigure("+symClassName()+"* vlSymsp, bool first) {\n");
|
|
|
|
|
|
puts( "if (0 && first) {} // Prevent unused\n");
|
|
|
|
|
|
puts( "this->__VlSymsp = vlSymsp;\n"); // First, as later stuff needs it.
|
2016-10-23 20:27:57 +02:00
|
|
|
|
if (v3Global.opt.coverage() ) {
|
|
|
|
|
|
puts("this->_configure_coverage(vlSymsp, first);\n");
|
2006-08-30 19:27:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2006-08-30 19:27:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitCoverageImp(AstNodeModule* modp) {
|
2008-12-05 16:54:14 +01:00
|
|
|
|
if (v3Global.opt.coverage() ) {
|
2006-08-30 03:14:29 +02:00
|
|
|
|
puts("\n// Coverage\n");
|
2014-11-24 03:06:10 +01:00
|
|
|
|
// Rather than putting out VL_COVER_INSERT calls directly, we do it via this function
|
2017-09-08 03:08:49 +02:00
|
|
|
|
// This gets around gcc slowness constructing all of the template arguments.
|
2018-03-10 22:32:04 +01:00
|
|
|
|
puts("void "+modClassName(m_modp)+"::__vlCoverInsert(uint32_t* countp, bool enable,"
|
|
|
|
|
|
" const char* filenamep, int lineno, int column,\n");
|
2008-12-12 22:04:56 +01:00
|
|
|
|
puts( "const char* hierp, const char* pagep, const char* commentp) {\n");
|
2012-08-27 03:13:47 +02:00
|
|
|
|
puts( "static uint32_t fake_zero_count = 0;\n"); // static doesn't need save-restore as constant
|
2008-12-05 16:54:14 +01:00
|
|
|
|
puts( "if (!enable) countp = &fake_zero_count;\n"); // Used for second++ instantiation of identical bin
|
|
|
|
|
|
puts( "*countp = 0;\n");
|
2014-11-24 03:06:10 +01:00
|
|
|
|
puts( "VL_COVER_INSERT(countp,");
|
2008-12-12 22:04:56 +01:00
|
|
|
|
puts( " \"filename\",filenamep,");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts( " \"lineno\",lineno,");
|
|
|
|
|
|
puts( " \"column\",column,\n");
|
2017-09-23 13:32:37 +02:00
|
|
|
|
//puts( "\"hier\",std::string(__VlSymsp->name())+hierp,"); // Need to move hier into scopes and back out if do this
|
|
|
|
|
|
puts( "\"hier\",std::string(name())+hierp,");
|
2008-12-12 22:04:56 +01:00
|
|
|
|
puts( " \"page\",pagep,");
|
|
|
|
|
|
puts( " \"comment\",commentp);\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitDestructorImp(AstNodeModule* modp) {
|
2006-08-30 19:27:53 +02:00
|
|
|
|
puts("\n");
|
|
|
|
|
|
puts(modClassName(modp)+"::~"+modClassName(modp)+"() {\n");
|
2016-11-05 14:47:56 +01:00
|
|
|
|
emitTextSection(AstType::atScDtor);
|
2006-08-30 19:27:53 +02:00
|
|
|
|
if (modp->isTop()) puts("delete __VlSymsp; __VlSymsp=NULL;\n");
|
|
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2006-08-30 19:27:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-27 03:13:47 +02:00
|
|
|
|
void EmitCImp::emitSavableImp(AstNodeModule* modp) {
|
|
|
|
|
|
if (v3Global.opt.savable() ) {
|
|
|
|
|
|
puts("\n// Savable\n");
|
|
|
|
|
|
for (int de=0; de<2; ++de) {
|
|
|
|
|
|
string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
|
|
|
|
|
|
string funcname = de ? "__Vdeserialize" : "__Vserialize";
|
|
|
|
|
|
string writeread = de ? "read" : "write";
|
|
|
|
|
|
string op = de ? ">>" : "<<";
|
|
|
|
|
|
puts("void "+modClassName(modp)+"::"+funcname+"("+classname+"& os) {\n");
|
|
|
|
|
|
// Place a computed checksum to insure proper structure save/restore formatting
|
|
|
|
|
|
// OK if this hash includes some things we won't dump, since just looking for loading the wrong model
|
2015-09-20 00:49:54 +02:00
|
|
|
|
VHashSha1 hash;
|
2012-08-27 03:13:47 +02:00
|
|
|
|
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstVar* varp = nodep->castVar()) {
|
2015-07-07 01:37:20 +02:00
|
|
|
|
hash.insert(varp->name());
|
|
|
|
|
|
hash.insert(varp->dtypep()->width());
|
2012-08-27 03:13:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ofp()->printf( "vluint64_t __Vcheckval = VL_ULL(0x%" VL_PRI64 "x);\n",
|
2015-07-07 01:37:20 +02:00
|
|
|
|
hash.digestUInt64());
|
2012-08-27 03:13:47 +02:00
|
|
|
|
if (de) {
|
|
|
|
|
|
puts("os.readAssert(__Vcheckval);\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("os<<__Vcheckval;\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Save all members
|
|
|
|
|
|
if (v3Global.opt.inhibitSim()) puts("os"+op+"__Vm_inhibitSim;\n");
|
|
|
|
|
|
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstVar* varp = nodep->castVar()) {
|
|
|
|
|
|
if (varp->isIO() && modp->isTop() && optSystemC()) {
|
|
|
|
|
|
// System C top I/O doesn't need loading, as the lower level subinst code does it.
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (varp->isParam()) {}
|
|
|
|
|
|
else if (varp->isStatic() && varp->isConst()) {}
|
|
|
|
|
|
else {
|
|
|
|
|
|
int vects = 0;
|
|
|
|
|
|
// This isn't very robust and may need cleanup for other data types
|
2013-01-15 03:49:22 +01:00
|
|
|
|
for (AstUnpackArrayDType* arrayp=varp->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
|
|
|
|
|
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
2012-08-27 03:13:47 +02:00
|
|
|
|
int vecnum = vects++;
|
|
|
|
|
|
if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier.");
|
|
|
|
|
|
string ivar = string("__Vi")+cvtToStr(vecnum);
|
|
|
|
|
|
// MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block
|
|
|
|
|
|
puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(0)+";");
|
|
|
|
|
|
puts(" for (; "+ivar+"<"+cvtToStr(arrayp->elementsConst()));
|
|
|
|
|
|
puts("; ++"+ivar+") {\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (varp->basicp() && (varp->basicp()->keyword() == AstBasicDTypeKwd::STRING
|
|
|
|
|
|
|| !varp->basicp()->isWide())) {
|
|
|
|
|
|
puts("os"+op+varp->name());
|
|
|
|
|
|
for (int v=0; v<vects; ++v) puts( "[__Vi"+cvtToStr(v)+"]");
|
|
|
|
|
|
puts(";\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("os."+writeread+"(&"+varp->name());
|
|
|
|
|
|
for (int v=0; v<vects; ++v) puts( "[__Vi"+cvtToStr(v)+"]");
|
|
|
|
|
|
puts(",sizeof("+varp->name());
|
|
|
|
|
|
for (int v=0; v<vects; ++v) puts( "[__Vi"+cvtToStr(v)+"]");
|
|
|
|
|
|
puts("));\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int v=0; v<vects; ++v) puts( "}}\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (modp->isTop()) { // Save the children
|
|
|
|
|
|
puts( "__VlSymsp->"+funcname+"(os);\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitStaticDecl(AstNodeModule* modp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Need implementation here. Be careful of alignment code; needs to be uniquified
|
|
|
|
|
|
// with module name to avoid multiple symbols.
|
2017-12-09 17:52:35 +01:00
|
|
|
|
//emitVarList(modp->stmtsp(), EVL_FUNC_ALL, modp->name());
|
2011-08-05 03:58:45 +02:00
|
|
|
|
puts(""); // NOP for cppcheck, otherwise const function
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EmitCImp::emitTextSection(AstType type) {
|
|
|
|
|
|
int last_line = -999;
|
|
|
|
|
|
for (AstNode* nodep = m_modp->stmtsp(); nodep != NULL; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstNodeText* textp = nodep->castNodeText()) {
|
|
|
|
|
|
if (nodep->type() == type) {
|
|
|
|
|
|
if (last_line != nodep->fileline()->lineno()) {
|
|
|
|
|
|
if (last_line < 0) {
|
|
|
|
|
|
puts("\n//*** Below code from `systemc in Verilog file\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
ofp()->putsNoTracking("//#line "+cvtToStr(nodep->fileline()->lineno())
|
2009-05-08 19:16:19 +02:00
|
|
|
|
+" ");
|
|
|
|
|
|
ofp()->putsQuoted(nodep->fileline()->filename());
|
|
|
|
|
|
ofp()->putsNoTracking("\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
last_line = nodep->fileline()->lineno();
|
|
|
|
|
|
}
|
|
|
|
|
|
ofp()->putsNoTracking(textp->text());
|
|
|
|
|
|
last_line++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (last_line > 0) {
|
|
|
|
|
|
puts("//*** Above code from `systemc in Verilog file\n\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitCellCtors(AstNodeModule* modp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (modp->isTop()) {
|
2006-08-30 03:14:29 +02:00
|
|
|
|
// Must be before other constructors, as __vlCoverInsert calls it
|
2006-08-30 23:07:55 +02:00
|
|
|
|
puts(EmitCBaseVisitor::symClassVar()+" = __VlSymsp = new "+symClassName()+"(this, name());\n");
|
|
|
|
|
|
puts(EmitCBaseVisitor::symTopAssign()+"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstCell* cellp=nodep->castCell()) {
|
|
|
|
|
|
puts("VL_CELL ("+cellp->name()+", "+modClassName(cellp->modp())+");\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EmitCImp::emitSensitives() {
|
|
|
|
|
|
// Create sensitivity list for when to evaluate the model.
|
|
|
|
|
|
// If C++ code, the user must call this routine themself.
|
|
|
|
|
|
if (m_modp->isTop() && optSystemC()) {
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Sensitivities on all clocks and combo inputs\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("SC_METHOD(eval);\n");
|
|
|
|
|
|
for (AstNode* nodep=m_modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstVar* varp = nodep->castVar()) {
|
|
|
|
|
|
if (varp->isInput() && (varp->isScSensitive() || varp->isUsedClock())) {
|
2012-02-02 02:20:43 +01:00
|
|
|
|
int vects = 0;
|
|
|
|
|
|
// This isn't very robust and may need cleanup for other data types
|
2013-01-15 03:49:22 +01:00
|
|
|
|
for (AstUnpackArrayDType* arrayp=varp->dtypeSkipRefp()->castUnpackArrayDType(); arrayp;
|
|
|
|
|
|
arrayp = arrayp->subDTypep()->skipRefp()->castUnpackArrayDType()) {
|
2012-02-02 02:20:43 +01:00
|
|
|
|
int vecnum = vects++;
|
|
|
|
|
|
if (arrayp->msb() < arrayp->lsb()) varp->v3fatalSrc("Should have swapped msb & lsb earlier.");
|
|
|
|
|
|
string ivar = string("__Vi")+cvtToStr(vecnum);
|
|
|
|
|
|
// MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block
|
|
|
|
|
|
puts("{ int __Vi"+cvtToStr(vecnum)+"="+cvtToStr(arrayp->lsb())+";");
|
|
|
|
|
|
puts(" for (; "+ivar+"<="+cvtToStr(arrayp->msb()));
|
|
|
|
|
|
puts("; ++"+ivar+") {\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
puts("sensitive << "+varp->name());
|
|
|
|
|
|
for (int v=0; v<vects; ++v) puts( "[__Vi"+cvtToStr(v)+"]");
|
|
|
|
|
|
puts(";\n");
|
|
|
|
|
|
for (int v=0; v<vects; ++v) puts( "}}\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-06-15 00:59:24 +02:00
|
|
|
|
void EmitCImp::emitSettleLoop(const std::string& eval_call, bool initial) {
|
2018-03-10 18:52:11 +01:00
|
|
|
|
putsDecoration("// Evaluate till stable\n");
|
|
|
|
|
|
puts("int __VclockLoop = 0;\n");
|
|
|
|
|
|
puts("QData __Vchange = 1;\n");
|
|
|
|
|
|
puts("do {\n");
|
|
|
|
|
|
puts( eval_call + "\n");
|
|
|
|
|
|
puts( "if (VL_UNLIKELY(++__VclockLoop > "+cvtToStr(v3Global.opt.convergeLimit())
|
|
|
|
|
|
+")) {\n");
|
|
|
|
|
|
puts( "// About to fail, so enable debug to see what's not settling.\n");
|
|
|
|
|
|
puts( "// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n");
|
|
|
|
|
|
puts( "int __Vsaved_debug = Verilated::debug();\n");
|
|
|
|
|
|
puts( "Verilated::debug(1);\n");
|
|
|
|
|
|
puts( "__Vchange = _change_request(vlSymsp);\n");
|
|
|
|
|
|
puts( "Verilated::debug(__Vsaved_debug);\n");
|
|
|
|
|
|
puts( "VL_FATAL_MT(__FILE__,__LINE__,__FILE__,\"Verilated model didn't ");
|
|
|
|
|
|
if (initial) puts ("DC ");
|
|
|
|
|
|
puts( "converge\");\n");
|
|
|
|
|
|
puts( "} else {\n");
|
|
|
|
|
|
puts( "__Vchange = _change_request(vlSymsp);\n");
|
|
|
|
|
|
puts( "}\n");
|
|
|
|
|
|
puts("} while (VL_UNLIKELY(__Vchange));\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitWrapEval(AstNodeModule* modp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("\nvoid "+modClassName(modp)+"::eval() {\n");
|
2017-11-06 03:47:55 +01:00
|
|
|
|
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate "+modClassName(modp)+"::eval\\n\"); );\n");
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts(EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp; // Setup global symbol table\n");
|
2006-08-30 23:07:55 +02:00
|
|
|
|
puts(EmitCBaseVisitor::symTopAssign()+"\n");
|
2017-11-06 03:47:55 +01:00
|
|
|
|
puts("#ifdef VL_DEBUG\n");
|
|
|
|
|
|
putsDecoration("// Debug assertions\n");
|
|
|
|
|
|
puts("_eval_debug_assertions();\n");
|
|
|
|
|
|
puts("#endif // VL_DEBUG\n");
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Initialize\n");
|
2006-08-30 23:07:55 +02:00
|
|
|
|
puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) _eval_initial_loop(vlSymsp);\n");
|
2006-08-29 02:27:04 +02:00
|
|
|
|
if (v3Global.opt.inhibitSim()) {
|
2006-08-30 23:07:55 +02:00
|
|
|
|
puts("if (VL_UNLIKELY(__Vm_inhibitSim)) return;\n");
|
2006-08-29 02:27:04 +02:00
|
|
|
|
}
|
2017-10-27 03:51:51 +02:00
|
|
|
|
|
2018-05-30 01:49:27 +02:00
|
|
|
|
if (v3Global.opt.threads() == 1) {
|
2018-05-09 03:43:32 +02:00
|
|
|
|
uint32_t mtaskId = 0;
|
|
|
|
|
|
putsDecoration("// MTask "+cvtToStr(mtaskId)+" start\n");
|
|
|
|
|
|
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"MTask starting, mtaskId="+cvtToStr(mtaskId)+"\\n\"););\n");
|
|
|
|
|
|
puts("Verilated::mtaskId("+cvtToStr(mtaskId)+");\n");
|
2017-10-27 03:51:51 +02:00
|
|
|
|
}
|
2018-03-10 18:52:11 +01:00
|
|
|
|
emitSettleLoop(
|
|
|
|
|
|
(string("VL_DEBUG_IF(VL_DBG_MSGF(\"+ Clock loop\\n\"););\n")
|
|
|
|
|
|
+ (v3Global.opt.trace() ? "vlSymsp->__Vm_activity = true;\n" : "")
|
|
|
|
|
|
+ "_eval(vlSymsp);"), false);
|
2018-05-30 01:49:27 +02:00
|
|
|
|
if (v3Global.opt.threads() == 1) {
|
2018-05-09 03:43:32 +02:00
|
|
|
|
puts("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);\n");
|
2017-10-27 03:51:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
if (v3Global.opt.threads()) {
|
|
|
|
|
|
puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
//
|
2006-08-30 23:07:55 +02:00
|
|
|
|
puts("\nvoid "+modClassName(modp)+"::_eval_initial_loop("+EmitCBaseVisitor::symClassVar()+") {\n");
|
|
|
|
|
|
puts("vlSymsp->__Vm_didInit = true;\n");
|
|
|
|
|
|
puts("_eval_initial(vlSymsp);\n");
|
2017-09-23 04:27:03 +02:00
|
|
|
|
if (v3Global.opt.trace()) {
|
|
|
|
|
|
puts("vlSymsp->__Vm_activity = true;\n");
|
|
|
|
|
|
}
|
2018-03-10 18:52:11 +01:00
|
|
|
|
emitSettleLoop((string("_eval_settle(vlSymsp);\n")
|
|
|
|
|
|
+"_eval(vlSymsp);"), true);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
// Top interface/ implementation
|
|
|
|
|
|
|
|
|
|
|
|
void EmitCStmts::emitVarList(AstNode* firstp, EisWhich which, const string& prefixIfImp) {
|
|
|
|
|
|
// Put out a list of signal declarations
|
2006-09-19 17:27:15 +02:00
|
|
|
|
// in order of 0:clocks, 1:vluint8, 2:vluint16, 4:vluint32, 5:vluint64, 6:wide, 7:arrays
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// This aids cache packing and locality
|
2006-08-29 02:58:48 +02:00
|
|
|
|
// Largest->smallest reduces the number of pad variables.
|
|
|
|
|
|
// But for now, Smallest->largest makes it more likely a small offset will allow access to the signal.
|
2017-12-09 17:52:35 +01:00
|
|
|
|
// TODO: Move this sort to an earlier visitor stage.
|
|
|
|
|
|
typedef std::multimap<int, AstVar*> VarSortMap;
|
|
|
|
|
|
VarSortMap varAnonMap;
|
|
|
|
|
|
VarSortMap varNonanonMap;
|
|
|
|
|
|
int anonMembers = 0;
|
|
|
|
|
|
|
2006-08-29 02:58:48 +02:00
|
|
|
|
for (int isstatic=1; isstatic>=0; isstatic--) {
|
2017-12-09 17:52:35 +01:00
|
|
|
|
if (prefixIfImp!="" && !isstatic) continue;
|
|
|
|
|
|
for (AstNode* nodep=firstp; nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstVar* varp = nodep->castVar()) {
|
|
|
|
|
|
bool doit = true;
|
|
|
|
|
|
switch (which) {
|
|
|
|
|
|
case EVL_CLASS_IO: doit = varp->isIO(); break;
|
|
|
|
|
|
case EVL_CLASS_SIG: doit = (varp->isSignal() && !varp->isIO()); break;
|
|
|
|
|
|
case EVL_CLASS_TEMP: doit = (varp->isTemp() && !varp->isIO()); break;
|
|
|
|
|
|
case EVL_CLASS_PAR: doit = (varp->isParam() && !varp->valuep()->castConst()); break;
|
|
|
|
|
|
case EVL_CLASS_ALL: doit = true; break;
|
|
|
|
|
|
case EVL_FUNC_ALL: doit = true; break;
|
|
|
|
|
|
default: v3fatalSrc("Bad Case");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (varp->isStatic() ? !isstatic : isstatic) doit=false;
|
|
|
|
|
|
if (doit) {
|
|
|
|
|
|
int sigbytes = varp->dtypeSkipRefp()->widthAlignBytes();
|
|
|
|
|
|
int sortbytes = 9;
|
|
|
|
|
|
if (varp->isUsedClock() && varp->widthMin()==1) sortbytes = 0;
|
|
|
|
|
|
else if (varp->dtypeSkipRefp()->castUnpackArrayDType()) sortbytes=8;
|
|
|
|
|
|
else if (varp->basicp() && varp->basicp()->isOpaque()) sortbytes=7;
|
|
|
|
|
|
else if (varp->isScBv() || varp->isScBigUint()) sortbytes=6;
|
|
|
|
|
|
else if (sigbytes==8) sortbytes=5;
|
|
|
|
|
|
else if (sigbytes==4) sortbytes=4;
|
|
|
|
|
|
else if (sigbytes==2) sortbytes=2;
|
|
|
|
|
|
else if (sigbytes==1) sortbytes=1;
|
|
|
|
|
|
|
|
|
|
|
|
bool anonOk = (v3Global.opt.compLimitMembers() != 0 // Enabled
|
|
|
|
|
|
&& !varp->isStatic()
|
|
|
|
|
|
&& !varp->isIO() // Confusing to user
|
|
|
|
|
|
&& !varp->isSc() // Aggregates can't be anon
|
|
|
|
|
|
&& (varp->basicp() && !varp->basicp()->isOpaque()) // Aggregates can't be anon
|
|
|
|
|
|
&& which != EVL_FUNC_ALL); // Anon not legal in funcs, and gcc bug free there anyhow
|
|
|
|
|
|
if (anonOk) {
|
|
|
|
|
|
++anonMembers;
|
|
|
|
|
|
varAnonMap.insert(make_pair(sortbytes, varp));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
varNonanonMap.insert(make_pair(sortbytes, varp));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Output anons
|
|
|
|
|
|
{
|
|
|
|
|
|
int lim = v3Global.opt.compLimitMembers();
|
|
|
|
|
|
int anonL3s = 1;
|
|
|
|
|
|
int anonL2s = 1;
|
|
|
|
|
|
int anonL1s = 1;
|
|
|
|
|
|
if (anonMembers > (lim*lim*lim)) {
|
|
|
|
|
|
anonL3s = (anonMembers + (lim*lim*lim) - 1) / (lim*lim*lim);
|
|
|
|
|
|
anonL2s = lim;
|
|
|
|
|
|
anonL1s = lim;
|
|
|
|
|
|
} else if (anonMembers > (lim*lim)) {
|
|
|
|
|
|
anonL2s = (anonMembers + (lim*lim) - 1) / (lim*lim);
|
|
|
|
|
|
anonL1s = lim;
|
|
|
|
|
|
} else if (anonMembers > lim) {
|
|
|
|
|
|
anonL1s = (anonMembers + lim - 1) / lim;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (anonL1s != 1) puts("// Anonymous structures to workaround compiler member-count bugs\n");
|
|
|
|
|
|
VarSortMap::iterator it = varAnonMap.begin();
|
|
|
|
|
|
for (int l3=0; l3<anonL3s && it != varAnonMap.end(); ++l3) {
|
|
|
|
|
|
if (anonL3s != 1) puts("struct {\n");
|
|
|
|
|
|
for (int l2=0; l2<anonL2s && it != varAnonMap.end(); ++l2) {
|
|
|
|
|
|
if (anonL2s != 1) puts("struct {\n");
|
|
|
|
|
|
for (int l1=0; l1<anonL1s && it != varAnonMap.end(); ++l1) {
|
|
|
|
|
|
if (anonL1s != 1) puts("struct {\n");
|
|
|
|
|
|
for (int l0=0; l0<lim && it != varAnonMap.end(); ++l0) {
|
|
|
|
|
|
AstVar* varp = it->second;
|
|
|
|
|
|
emitVarDecl(varp, prefixIfImp);
|
|
|
|
|
|
++it;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (anonL1s != 1) puts("};\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (anonL2s != 1) puts("};\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (anonL3s != 1) puts("};\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
// Leftovers, just in case off by one error somewhere above
|
|
|
|
|
|
for (; it != varAnonMap.end(); ++it) {
|
|
|
|
|
|
AstVar* varp = it->second;
|
|
|
|
|
|
emitVarDecl(varp, prefixIfImp);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Output nonanons
|
|
|
|
|
|
for (VarSortMap::iterator it = varNonanonMap.begin(); it != varNonanonMap.end(); ++it) {
|
|
|
|
|
|
AstVar* varp = it->second;
|
|
|
|
|
|
emitVarDecl(varp, prefixIfImp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct CmpName {
|
|
|
|
|
|
inline bool operator () (const AstNode* lhsp, const AstNode* rhsp) const {
|
|
|
|
|
|
return lhsp->name() < rhsp->name();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitIntFuncDecls(AstNodeModule* modp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
vector<AstCFunc*> funcsp;
|
|
|
|
|
|
|
|
|
|
|
|
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstCFunc* funcp = nodep->castCFunc()) {
|
|
|
|
|
|
if (!funcp->skipDecl()) {
|
|
|
|
|
|
funcsp.push_back(funcp);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-05-24 02:19:51 +02:00
|
|
|
|
stable_sort(funcsp.begin(), funcsp.end(), CmpName());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
for (vector<AstCFunc*>::iterator it = funcsp.begin(); it != funcsp.end(); ++it) {
|
|
|
|
|
|
AstCFunc* funcp = *it;
|
2009-12-03 12:55:29 +01:00
|
|
|
|
if (!funcp->dpiImport()) { // DPI is prototyped in __Dpi.h
|
|
|
|
|
|
ofp()->putsPrivate(funcp->declPrivate());
|
2017-11-06 03:47:55 +01:00
|
|
|
|
if (funcp->ifdef()!="") puts("#ifdef "+funcp->ifdef()+"\n");
|
2009-12-03 12:55:29 +01:00
|
|
|
|
if (funcp->isStatic()) puts("static ");
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts(funcp->rtnTypeVoid()); puts(" ");
|
2009-12-03 12:55:29 +01:00
|
|
|
|
puts(funcp->name()); puts("("+cFuncArgs(funcp)+");\n");
|
2017-11-06 03:47:55 +01:00
|
|
|
|
if (funcp->ifdef()!="") puts("#endif // "+funcp->ifdef()+"\n");
|
2009-12-03 12:55:29 +01:00
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitInt(AstNodeModule* modp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Always have this first; gcc has short circuiting if #ifdef is first in a file
|
2017-09-08 03:08:49 +02:00
|
|
|
|
puts("#ifndef _"+modClassName(modp)+"_H_\n");
|
|
|
|
|
|
puts("#define _"+modClassName(modp)+"_H_\n");
|
|
|
|
|
|
puts("\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
ofp()->putsIntTopInclude();
|
2010-01-17 21:10:37 +01:00
|
|
|
|
if (v3Global.needHeavy()) {
|
2010-01-24 14:38:17 +01:00
|
|
|
|
puts("#include \"verilated_heavy.h\"\n");
|
2010-01-17 21:10:37 +01:00
|
|
|
|
} else {
|
|
|
|
|
|
puts("#include \"verilated.h\"\n");
|
|
|
|
|
|
}
|
2012-08-27 03:13:47 +02:00
|
|
|
|
if (v3Global.opt.savable()) {
|
|
|
|
|
|
puts("#include \"verilated_save.h\"\n");
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (v3Global.opt.coverage()) {
|
2014-11-24 03:06:10 +01:00
|
|
|
|
puts("#include \"verilated_cov.h\"\n");
|
2012-08-27 03:13:47 +02:00
|
|
|
|
if (v3Global.opt.savable()) v3error("--coverage and --savable not supported together");
|
2007-11-30 23:38:21 +01:00
|
|
|
|
}
|
|
|
|
|
|
if (v3Global.needHInlines()) { // Set by V3EmitCInlines; should have been called before us
|
|
|
|
|
|
puts("#include \""+topClassName()+"__Inlines.h\"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2011-05-12 12:59:13 +02:00
|
|
|
|
if (v3Global.dpi()) {
|
|
|
|
|
|
// do this before including our main .h file so that any references to
|
|
|
|
|
|
// types defined in svdpi.h are available
|
|
|
|
|
|
puts("#include \""+ topClassName() +"__Dpi.h\"\n");
|
|
|
|
|
|
}
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts("\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
// Declare foreign instances up front to make C++ happy
|
|
|
|
|
|
puts("class "+symClassName()+";\n");
|
2017-10-19 00:22:58 +02:00
|
|
|
|
vl_unordered_set<string> didClassName;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstCell* cellp=nodep->castCell()) {
|
2017-10-18 23:38:10 +02:00
|
|
|
|
string className = modClassName(cellp->modp());
|
|
|
|
|
|
if (didClassName.find(className)==didClassName.end()) {
|
|
|
|
|
|
puts("class "+className+";\n");
|
|
|
|
|
|
didClassName.insert(className);
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2008-01-15 19:36:47 +01:00
|
|
|
|
if (v3Global.opt.trace()) {
|
2010-01-25 00:37:01 +01:00
|
|
|
|
puts("class "+v3Global.opt.traceClassBase()+";\n");
|
2008-01-15 19:36:47 +01:00
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
puts("\n//----------\n\n");
|
2016-11-05 14:47:56 +01:00
|
|
|
|
emitTextSection(AstType::atScHdr);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
if (optSystemC() && modp->isTop()) {
|
|
|
|
|
|
puts("SC_MODULE("+modClassName(modp)+") {\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("VL_MODULE("+modClassName(modp)+") {\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
ofp()->resetPrivate();
|
|
|
|
|
|
ofp()->putsPrivate(false); // public:
|
|
|
|
|
|
|
2017-09-23 04:27:03 +02:00
|
|
|
|
{ // Instantiated cells
|
|
|
|
|
|
bool did = false;
|
|
|
|
|
|
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstCell* cellp=nodep->castCell()) {
|
2017-09-30 19:30:01 +02:00
|
|
|
|
if (!did) {
|
|
|
|
|
|
did = true;
|
2017-09-23 04:27:03 +02:00
|
|
|
|
putsDecoration("// CELLS\n");
|
|
|
|
|
|
if (modp->isTop()) puts("// Public to allow access to /*verilator_public*/ items;\n");
|
|
|
|
|
|
if (modp->isTop()) puts("// otherwise the application code can consider these internals.\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
ofp()->putsCellDecl(modClassName(cellp->modp()), cellp->name());
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-11-07 13:50:11 +01:00
|
|
|
|
emitTypedefs(modp->stmtsp());
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("\n// PORTS\n");
|
2009-12-03 03:15:56 +01:00
|
|
|
|
if (modp->isTop()) puts("// The application code writes and reads these signals to\n");
|
|
|
|
|
|
if (modp->isTop()) puts("// propagate new values into/out from the Verilated model.\n");
|
2017-12-09 17:52:35 +01:00
|
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_IO, "");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
puts("\n// LOCAL SIGNALS\n");
|
2009-12-03 03:15:56 +01:00
|
|
|
|
if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
|
2017-12-09 17:52:35 +01:00
|
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_SIG, "");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
puts("\n// LOCAL VARIABLES\n");
|
2009-12-03 03:15:56 +01:00
|
|
|
|
if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
|
2017-12-09 17:52:35 +01:00
|
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_TEMP, "");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
puts("\n// INTERNAL VARIABLES\n");
|
2009-12-03 03:15:56 +01:00
|
|
|
|
if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->putsPrivate(!modp->isTop()); // private: unless top
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts(symClassName()+"* __VlSymsp; // Symbol table\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->putsPrivate(false); // public:
|
|
|
|
|
|
if (modp->isTop()) {
|
2006-08-29 02:27:04 +02:00
|
|
|
|
if (v3Global.opt.inhibitSim()) {
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts("bool __Vm_inhibitSim; ///< Set true to disable evaluation of module\n");
|
2006-08-29 02:27:04 +02:00
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
emitCoverageDecl(modp); // may flip public/private
|
|
|
|
|
|
|
|
|
|
|
|
puts("\n// PARAMETERS\n");
|
2009-12-03 03:15:56 +01:00
|
|
|
|
if (modp->isTop()) puts("// Parameters marked /*verilator public*/ for use by application code\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->putsPrivate(false); // public:
|
2017-12-09 17:52:35 +01:00
|
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_PAR, ""); // Only those that are non-CONST
|
2006-08-26 13:35:28 +02:00
|
|
|
|
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstVar* varp = nodep->castVar()) {
|
|
|
|
|
|
if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) {
|
2010-01-22 00:20:47 +01:00
|
|
|
|
if (!varp->valuep()) nodep->v3fatalSrc("No init for a param?");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// These should be static const values, however microsloth VC++ doesn't
|
|
|
|
|
|
// support them. They also cause problems with GDB under GCC2.95.
|
2009-03-13 19:17:30 +01:00
|
|
|
|
if (varp->isWide()) { // Unsupported for output
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// enum WData "+varp->name()+" //wide");
|
2010-01-22 00:20:47 +01:00
|
|
|
|
} else if (!varp->valuep()->castConst()) { // Unsupported for output
|
2016-09-14 04:28:07 +02:00
|
|
|
|
//putsDecoration("// enum ..... "+varp->name()+" //not simple value, see variable above instead");
|
2018-01-17 01:53:50 +01:00
|
|
|
|
} else if (varp->dtypep()->castBasicDType()
|
|
|
|
|
|
&& varp->dtypep()->castBasicDType()->isOpaque()) { // Can't put out e.g. doubles
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
puts("enum ");
|
|
|
|
|
|
puts(varp->isQuad()?"_QData":"_IData");
|
|
|
|
|
|
puts(""+varp->name()+" { "+varp->name()+" = ");
|
2010-01-22 00:20:47 +01:00
|
|
|
|
varp->valuep()->iterateAndNext(*this);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("};");
|
|
|
|
|
|
}
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-12-03 03:15:56 +01:00
|
|
|
|
puts("\n// CONSTRUCTORS\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->resetPrivate();
|
2006-08-30 23:07:55 +02:00
|
|
|
|
// We don't need a private copy constructor, as VerilatedModule has one for us.
|
|
|
|
|
|
ofp()->putsPrivate(true);
|
2017-12-07 05:26:27 +01:00
|
|
|
|
puts("VL_UNCOPYABLE("+modClassName(modp)+"); ///< Copying not allowed\n");
|
2006-08-30 23:07:55 +02:00
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->putsPrivate(false); // public:
|
|
|
|
|
|
if (optSystemC() && modp->isTop()) {
|
|
|
|
|
|
puts("SC_CTOR("+modClassName(modp)+");\n");
|
2006-08-30 19:27:53 +02:00
|
|
|
|
puts("virtual ~"+modClassName(modp)+"();\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (optSystemC()) {
|
|
|
|
|
|
puts("VL_CTOR("+modClassName(modp)+");\n");
|
2006-08-30 19:27:53 +02:00
|
|
|
|
puts("~"+modClassName(modp)+"();\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
2010-02-03 03:09:11 +01:00
|
|
|
|
if (modp->isTop()) {
|
|
|
|
|
|
puts("/// Construct the model; called by application code\n");
|
|
|
|
|
|
puts("/// The special name "" may be used to make a wrapper with a\n");
|
2017-12-07 05:26:27 +01:00
|
|
|
|
puts("/// single model invisible with respect to DPI scope names.\n");
|
2010-02-03 03:09:11 +01:00
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(modClassName(modp)+"(const char* name=\"TOP\");\n");
|
2009-12-03 03:15:56 +01:00
|
|
|
|
if (modp->isTop()) puts("/// Destroy the model; called (often implicitly) by application code\n");
|
2006-08-30 19:27:53 +02:00
|
|
|
|
puts("~"+modClassName(modp)+"();\n");
|
2007-12-13 14:54:04 +01:00
|
|
|
|
}
|
2017-09-08 03:08:49 +02:00
|
|
|
|
if (v3Global.opt.trace()) {
|
2009-12-03 03:15:56 +01:00
|
|
|
|
if (modp->isTop()) puts("/// Trace signals in the model; called by application code\n");
|
2018-01-30 01:04:37 +01:00
|
|
|
|
puts("void trace(VerilatedVcdC* tfp, int levels, int options=0);\n");
|
2015-02-11 04:38:05 +01:00
|
|
|
|
if (modp->isTop() && optSystemC()) {
|
|
|
|
|
|
puts("/// SC tracing; avoid overloaded virtual function lint warning\n");
|
2018-01-30 01:04:37 +01:00
|
|
|
|
puts("virtual void trace(sc_trace_file* tfp) const { ::sc_core::sc_module::trace(tfp); }\n");
|
2015-02-11 04:38:05 +01:00
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-11-05 14:47:56 +01:00
|
|
|
|
emitTextSection(AstType::atScInt);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2009-12-03 03:15:56 +01:00
|
|
|
|
puts("\n// API METHODS\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (modp->isTop()) {
|
|
|
|
|
|
if (optSystemC()) ofp()->putsPrivate(true); ///< eval() is invoked by our sensitive() calls.
|
2009-12-03 03:15:56 +01:00
|
|
|
|
else puts("/// Evaluate the model. Application must call when inputs change.\n");
|
|
|
|
|
|
puts("void eval();\n");
|
|
|
|
|
|
ofp()->putsPrivate(false); // public:
|
|
|
|
|
|
if (!optSystemC()) puts("/// Simulation complete, run final blocks. Application must call on completion.\n");
|
|
|
|
|
|
puts("void final();\n");
|
2006-08-29 02:27:04 +02:00
|
|
|
|
if (v3Global.opt.inhibitSim()) {
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts("void inhibitSim(bool flag) { __Vm_inhibitSim=flag; } ///< Set true to disable evaluation of module\n");
|
2006-08-29 02:27:04 +02:00
|
|
|
|
}
|
2009-12-03 03:15:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
puts("\n// INTERNAL METHODS\n");
|
|
|
|
|
|
if (modp->isTop()) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->putsPrivate(true); // private:
|
2006-08-31 00:00:55 +02:00
|
|
|
|
puts("static void _eval_initial_loop("+EmitCBaseVisitor::symClassVar()+");\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2009-12-03 03:15:56 +01:00
|
|
|
|
ofp()->putsPrivate(false); // public:
|
|
|
|
|
|
puts("void __Vconfigure("+symClassName()+"* symsp, bool first);\n");
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
emitIntFuncDecls(modp);
|
|
|
|
|
|
|
2017-09-08 03:08:49 +02:00
|
|
|
|
if (v3Global.opt.trace()) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->putsPrivate(false); // public:
|
2018-01-30 01:04:37 +01:00
|
|
|
|
puts("static void traceInit("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n");
|
|
|
|
|
|
puts("static void traceFull("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n");
|
|
|
|
|
|
puts("static void traceChg("+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code);\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2012-08-27 03:13:47 +02:00
|
|
|
|
if (v3Global.opt.savable()) {
|
2016-05-12 13:19:02 +02:00
|
|
|
|
ofp()->putsPrivate(false); // public:
|
2012-08-27 03:13:47 +02:00
|
|
|
|
puts("void __Vserialize(VerilatedSerialize& os);\n");
|
|
|
|
|
|
puts("void __Vdeserialize(VerilatedDeserialize& os);\n");
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2012-08-27 03:13:47 +02:00
|
|
|
|
puts("} VL_ATTR_ALIGNED(128);\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("\n");
|
|
|
|
|
|
|
2012-08-27 03:13:47 +02:00
|
|
|
|
// Save/restore
|
|
|
|
|
|
if (v3Global.opt.savable() && modp->isTop()) {
|
2017-10-27 03:51:51 +02:00
|
|
|
|
puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, "+modClassName(modp)+"& rhs) {\n"
|
|
|
|
|
|
"Verilated::quiesce(); rhs.__Vserialize(os); return os; }\n");
|
|
|
|
|
|
puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "+modClassName(modp)+"& rhs) {\n"
|
|
|
|
|
|
"Verilated::quiesce(); rhs.__Vdeserialize(os); return os; }\n");
|
2012-08-27 03:13:47 +02:00
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// finish up h-file
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts("#endif // guard\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::emitImp(AstNodeModule* modp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
ofp()->printf("#include \"%-20s // For This\n",
|
|
|
|
|
|
(modClassName(modp)+".h\"").c_str());
|
|
|
|
|
|
|
|
|
|
|
|
// Us
|
2006-08-30 23:07:55 +02:00
|
|
|
|
puts("#include \""+ symClassName() +".h\"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2011-05-21 03:33:31 +02:00
|
|
|
|
if (v3Global.dpi()) {
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
puts("#include \"verilated_dpi.h\"\n");
|
|
|
|
|
|
}
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts("\n");
|
2011-05-21 03:33:31 +02:00
|
|
|
|
|
2016-11-05 14:47:56 +01:00
|
|
|
|
emitTextSection(AstType::atScImpHdr);
|
2006-08-29 02:58:48 +02:00
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
|
if (m_slow && splitFilenum()==0) {
|
2006-08-29 02:58:48 +02:00
|
|
|
|
puts("\n//--------------------\n");
|
|
|
|
|
|
puts("// STATIC VARIABLES\n\n");
|
2017-12-09 17:52:35 +01:00
|
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_ALL, modClassName(modp));
|
2006-08-29 02:58:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
|
if (m_fast && splitFilenum()==0) {
|
2016-11-05 14:47:56 +01:00
|
|
|
|
emitTextSection(AstType::atScImp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
emitStaticDecl(modp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
|
if (m_slow && splitFilenum()==0) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("\n//--------------------\n");
|
2006-08-30 19:27:53 +02:00
|
|
|
|
emitCtorImp(modp);
|
|
|
|
|
|
emitConfigureImp(modp);
|
|
|
|
|
|
emitDestructorImp(modp);
|
2012-08-27 03:13:47 +02:00
|
|
|
|
emitSavableImp(modp);
|
2006-08-30 03:14:29 +02:00
|
|
|
|
emitCoverageImp(modp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2006-08-30 19:27:53 +02:00
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
|
if (m_fast && splitFilenum()==0) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (modp->isTop()) {
|
|
|
|
|
|
emitStaticDecl(modp);
|
|
|
|
|
|
puts("\n//--------------------\n");
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
emitWrapEval(modp);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Blocks
|
|
|
|
|
|
puts("\n//--------------------\n");
|
|
|
|
|
|
puts("// Internal Methods\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
2018-05-30 01:49:27 +02:00
|
|
|
|
void EmitCImp::maybeSplit(AstNodeModule* modp) {
|
|
|
|
|
|
if (splitNeeded()) {
|
|
|
|
|
|
// Close old file
|
|
|
|
|
|
delete m_ofp; m_ofp=NULL;
|
|
|
|
|
|
// Open a new file
|
|
|
|
|
|
m_ofp = newOutCFile(modp, !m_fast, true/*source*/, splitFilenumInc());
|
|
|
|
|
|
emitImp(modp);
|
|
|
|
|
|
}
|
|
|
|
|
|
splitSizeInc(10); // Even blank functions get a file with a low csplit
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
|
void EmitCImp::main(AstNodeModule* modp, bool slow, bool fast) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Output a module
|
|
|
|
|
|
m_modp = modp;
|
|
|
|
|
|
m_slow = slow;
|
|
|
|
|
|
m_fast = fast;
|
|
|
|
|
|
|
|
|
|
|
|
if (debug()>=5) {
|
2009-01-21 22:56:50 +01:00
|
|
|
|
UINFO(0," Emitting "<<modClassName(modp)<<endl);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-30 01:49:27 +02:00
|
|
|
|
if (m_fast) {
|
|
|
|
|
|
m_ofp = newOutCFile(modp, !m_fast, false/*source*/);
|
|
|
|
|
|
emitInt(modp);
|
|
|
|
|
|
delete m_ofp; m_ofp=NULL;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-30 01:49:27 +02:00
|
|
|
|
m_ofp = newOutCFile(modp, !m_fast, true/*source*/);
|
|
|
|
|
|
emitImp(modp);
|
2008-06-10 03:25:10 +02:00
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
for (AstNode* nodep=modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
|
if (AstCFunc* funcp = nodep->castCFunc()) {
|
2018-05-30 01:49:27 +02:00
|
|
|
|
maybeSplit(modp);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
mainDoFunc(funcp);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
delete m_ofp; m_ofp=NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
// Tracing routines
|
|
|
|
|
|
|
|
|
|
|
|
class EmitCTrace : EmitCStmts {
|
|
|
|
|
|
AstCFunc* m_funcp; // Function we're in now
|
|
|
|
|
|
bool m_slow; // Making slow file
|
2008-11-17 23:13:57 +01:00
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// METHODS
|
2008-11-17 23:13:57 +01:00
|
|
|
|
void newOutCFile(int filenum) {
|
|
|
|
|
|
string filename = (v3Global.opt.makeDir()+"/"+ topClassName()
|
|
|
|
|
|
+ (m_slow?"__Trace__Slow":"__Trace"));
|
|
|
|
|
|
if (filenum) filename += "__"+cvtToStr(filenum);
|
|
|
|
|
|
filename += ".cpp";
|
|
|
|
|
|
|
|
|
|
|
|
AstCFile* cfilep = newCFile(filename, m_slow, true/*source*/);
|
|
|
|
|
|
cfilep->support(true);
|
|
|
|
|
|
|
|
|
|
|
|
if (m_ofp) v3fatalSrc("Previous file not closed");
|
|
|
|
|
|
m_ofp = new V3OutCFile (filename);
|
|
|
|
|
|
m_ofp->putsHeader();
|
2009-12-03 03:15:56 +01:00
|
|
|
|
m_ofp->puts("// DESCR" "IPTION: Verilator output: Tracing implementation internals\n");
|
2008-11-17 23:13:57 +01:00
|
|
|
|
|
|
|
|
|
|
emitTraceHeader();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
void emitTraceHeader() {
|
|
|
|
|
|
// Includes
|
2017-09-08 03:08:49 +02:00
|
|
|
|
puts("#include \"verilated_vcd_c.h\"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("#include \""+ symClassName() +".h\"\n");
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void emitTraceSlow() {
|
|
|
|
|
|
puts("\n//======================\n\n");
|
|
|
|
|
|
|
2018-01-30 01:04:37 +01:00
|
|
|
|
puts("void "+topClassName()+"::trace(");
|
2017-09-08 03:08:49 +02:00
|
|
|
|
puts("VerilatedVcdC* tfp, int, int) {\n");
|
2018-01-30 01:04:37 +01:00
|
|
|
|
puts( "tfp->spTrace()->addCallback("
|
2006-08-26 13:35:28 +02:00
|
|
|
|
"&"+topClassName()+"::traceInit"
|
|
|
|
|
|
+", &"+topClassName()+"::traceFull"
|
|
|
|
|
|
+", &"+topClassName()+"::traceChg, this);\n");
|
|
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
2010-01-25 00:37:01 +01:00
|
|
|
|
puts("void "+topClassName()+"::traceInit("
|
|
|
|
|
|
+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n");
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Callback from vcd->open()\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n");
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n");
|
2018-03-10 22:32:04 +01:00
|
|
|
|
puts("if (!Verilated::calcUnusedSigs()) {\n");
|
|
|
|
|
|
puts( "VL_FATAL_MT(__FILE__,__LINE__,__FILE__,\"Turning on wave traces requires Verilated::traceEverOn(true) call before time 0.\");\n");
|
|
|
|
|
|
puts("}\n");
|
2009-09-07 21:55:54 +02:00
|
|
|
|
puts("vcdp->scopeEscape(' ');\n");
|
2018-01-30 01:04:37 +01:00
|
|
|
|
puts("t->traceInitThis(vlSymsp, vcdp, code);\n");
|
2017-09-08 03:08:49 +02:00
|
|
|
|
puts("vcdp->scopeEscape('.');\n"); // Restore so later traced files won't break
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2008-06-10 03:25:10 +02:00
|
|
|
|
|
2010-01-25 00:37:01 +01:00
|
|
|
|
puts("void "+topClassName()+"::traceFull("
|
|
|
|
|
|
+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n");
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Callback from vcd->dump()\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n");
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n");
|
2018-01-30 01:04:37 +01:00
|
|
|
|
puts("t->traceFullThis(vlSymsp, vcdp, code);\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
puts("\n//======================\n\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void emitTraceFast() {
|
|
|
|
|
|
puts("\n//======================\n\n");
|
|
|
|
|
|
|
2010-01-25 00:37:01 +01:00
|
|
|
|
puts("void "+topClassName()+"::traceChg("
|
|
|
|
|
|
+v3Global.opt.traceClassBase()+"* vcdp, void* userthis, uint32_t code) {\n");
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Callback from vcd->dump()\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(topClassName()+"* t=("+topClassName()+"*)userthis;\n");
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts(EmitCBaseVisitor::symClassVar()+" = t->__VlSymsp; // Setup global symbol table\n");
|
2006-08-30 23:07:55 +02:00
|
|
|
|
puts("if (vlSymsp->getClearActivity()) {\n");
|
2018-01-30 01:04:37 +01:00
|
|
|
|
puts("t->traceChgThis(vlSymsp, vcdp, code);\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("}\n");
|
|
|
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
puts("\n//======================\n\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2009-03-13 19:17:30 +01:00
|
|
|
|
bool emitTraceIsScBv(AstTraceInc* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
AstVarRef* varrefp = nodep->valuep()->castVarRef();
|
|
|
|
|
|
if (!varrefp) return false;
|
|
|
|
|
|
AstVar* varp = varrefp->varp();
|
2009-03-13 19:17:30 +01:00
|
|
|
|
return varp->isSc() && varp->isScBv();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2013-04-27 03:02:32 +02:00
|
|
|
|
|
|
|
|
|
|
bool emitTraceIsScBigUint(AstTraceInc* nodep) {
|
|
|
|
|
|
AstVarRef* varrefp = nodep->valuep()->castVarRef();
|
|
|
|
|
|
if (!varrefp) return false;
|
|
|
|
|
|
AstVar* varp = varrefp->varp();
|
|
|
|
|
|
return varp->isSc() && varp->isScBigUint();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool emitTraceIsScUint(AstTraceInc* nodep) {
|
|
|
|
|
|
AstVarRef* varrefp = nodep->valuep()->castVarRef();
|
|
|
|
|
|
if (!varrefp) return false;
|
|
|
|
|
|
AstVar* varp = varrefp->varp();
|
|
|
|
|
|
return varp->isSc() && varp->isScUint();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
void emitTraceInitOne(AstTraceDecl* nodep) {
|
2014-03-15 01:36:47 +01:00
|
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
2011-07-24 21:01:51 +02:00
|
|
|
|
puts("vcdp->declDouble");
|
|
|
|
|
|
} else if (nodep->isWide()) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("vcdp->declArray");
|
|
|
|
|
|
} else if (nodep->isQuad()) {
|
|
|
|
|
|
puts("vcdp->declQuad ");
|
2013-12-14 18:17:31 +01:00
|
|
|
|
} else if (nodep->bitRange().ranged()) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("vcdp->declBus ");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("vcdp->declBit ");
|
|
|
|
|
|
}
|
|
|
|
|
|
puts("(c+"+cvtToStr(nodep->code()));
|
2013-12-14 18:17:31 +01:00
|
|
|
|
if (nodep->arrayRange().ranged()) puts("+i*"+cvtToStr(nodep->widthWords()));
|
2009-05-08 19:16:19 +02:00
|
|
|
|
puts(",");
|
|
|
|
|
|
putsQuoted(nodep->showname());
|
2013-12-14 18:17:31 +01:00
|
|
|
|
if (nodep->arrayRange().ranged()) {
|
|
|
|
|
|
puts(",(i+"+cvtToStr(nodep->arrayRange().lo())+")");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
puts(",-1");
|
|
|
|
|
|
}
|
2014-03-15 01:36:47 +01:00
|
|
|
|
if (!nodep->dtypep()->basicp()->isDouble() // When float/double no longer have widths this can go
|
2013-12-14 18:17:31 +01:00
|
|
|
|
&& nodep->bitRange().ranged()) {
|
|
|
|
|
|
puts(","+cvtToStr(nodep->bitRange().left())+","+cvtToStr(nodep->bitRange().right()));
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
puts(");");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) {
|
|
|
|
|
|
nodep->precondsp()->iterateAndNext(*this);
|
2008-11-17 23:13:57 +01:00
|
|
|
|
string full = ((m_funcp->funcType() == AstCFuncType::TRACE_FULL
|
|
|
|
|
|
|| m_funcp->funcType() == AstCFuncType::TRACE_FULL_SUB)
|
|
|
|
|
|
? "full":"chg");
|
2018-05-19 13:52:07 +02:00
|
|
|
|
bool emitWidth = false;
|
2014-03-15 01:36:47 +01:00
|
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
2011-07-24 21:01:51 +02:00
|
|
|
|
puts("vcdp->"+full+"Double");
|
2013-04-27 03:02:32 +02:00
|
|
|
|
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("vcdp->"+full+"Array");
|
2018-05-19 13:52:07 +02:00
|
|
|
|
emitWidth = true;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (nodep->isQuad()) {
|
|
|
|
|
|
puts("vcdp->"+full+"Quad ");
|
2018-05-19 13:52:07 +02:00
|
|
|
|
emitWidth = true;
|
|
|
|
|
|
} else if (nodep->declp()->bitRange().ranged()
|
|
|
|
|
|
// 1 element smaller to use Bit dump
|
|
|
|
|
|
&& nodep->declp()->bitRange().elements() != 1) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("vcdp->"+full+"Bus ");
|
2018-05-19 13:52:07 +02:00
|
|
|
|
emitWidth = true;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
puts("vcdp->"+full+"Bit ");
|
|
|
|
|
|
}
|
|
|
|
|
|
puts("(c+"+cvtToStr(nodep->declp()->code()
|
|
|
|
|
|
+ ((arrayindex<0) ? 0 : (arrayindex*nodep->declp()->widthWords()))));
|
|
|
|
|
|
puts(",");
|
|
|
|
|
|
emitTraceValue(nodep, arrayindex);
|
2018-05-19 13:52:07 +02:00
|
|
|
|
if (emitWidth) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(","+cvtToStr(nodep->declp()->widthMin()));
|
|
|
|
|
|
}
|
|
|
|
|
|
puts(");\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
void emitTraceValue(AstTraceInc* nodep, int arrayindex) {
|
|
|
|
|
|
if (nodep->valuep()->castVarRef()) {
|
|
|
|
|
|
AstVarRef* varrefp = nodep->valuep()->castVarRef();
|
|
|
|
|
|
AstVar* varp = varrefp->varp();
|
|
|
|
|
|
puts("(");
|
2013-04-27 03:02:32 +02:00
|
|
|
|
if (emitTraceIsScBigUint(nodep)) puts("(vluint32_t*)");
|
|
|
|
|
|
else if (emitTraceIsScBv(nodep)) puts("VL_SC_BV_DATAP(");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
varrefp->iterate(*this); // Put var name out
|
2009-11-05 04:31:53 +01:00
|
|
|
|
// Tracing only supports 1D arrays
|
2013-12-14 22:51:08 +01:00
|
|
|
|
if (nodep->declp()->arrayRange().ranged()) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (arrayindex==-2) puts("[i]");
|
|
|
|
|
|
else if (arrayindex==-1) puts("[0]");
|
|
|
|
|
|
else puts("["+cvtToStr(arrayindex)+"]");
|
|
|
|
|
|
}
|
|
|
|
|
|
if (varp->isSc()) puts(".read()");
|
2013-04-27 03:02:32 +02:00
|
|
|
|
if (emitTraceIsScUint(nodep)) puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()");
|
|
|
|
|
|
else if (emitTraceIsScBigUint(nodep)) puts(".get_raw()");
|
|
|
|
|
|
else if (emitTraceIsScBv(nodep)) puts(")");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts(")");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
puts("(");
|
|
|
|
|
|
nodep->valuep()->iterate(*this);
|
|
|
|
|
|
puts(")");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2014-09-12 03:28:53 +02:00
|
|
|
|
using EmitCStmts::visit; // Suppress hidden overloaded virtual function warnng
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNetlist* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// Top module only
|
|
|
|
|
|
nodep->topModulep()->accept(*this);
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstNodeModule* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCFunc* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
if (nodep->slow() != m_slow) return;
|
2008-11-17 23:13:57 +01:00
|
|
|
|
if (nodep->funcType().isTrace()) { // TRACE_*
|
2006-08-26 13:35:28 +02:00
|
|
|
|
m_funcp = nodep;
|
|
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
|
if (splitNeeded()) {
|
|
|
|
|
|
// Close old file
|
|
|
|
|
|
delete m_ofp; m_ofp=NULL;
|
|
|
|
|
|
// Open a new file
|
|
|
|
|
|
newOutCFile (splitFilenumInc());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
splitSizeInc(nodep);
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("\n");
|
|
|
|
|
|
puts(nodep->rtnTypeVoid()); puts(" ");
|
|
|
|
|
|
puts(topClassName()+"::"+nodep->name()
|
|
|
|
|
|
+"("+cFuncArgs(nodep)+") {\n");
|
|
|
|
|
|
|
2006-08-31 00:00:55 +02:00
|
|
|
|
if (nodep->symProlog()) puts(EmitCBaseVisitor::symTopAssign()+"\n");
|
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("int c=code;\n");
|
|
|
|
|
|
puts("if (0 && vcdp && c) {} // Prevent unused\n");
|
|
|
|
|
|
if (nodep->funcType() == AstCFuncType::TRACE_INIT) {
|
2017-09-23 04:27:03 +02:00
|
|
|
|
puts("vcdp->module(vlSymsp->name()); // Setup signal names\n");
|
2008-11-17 23:13:57 +01:00
|
|
|
|
} else if (nodep->funcType() == AstCFuncType::TRACE_INIT_SUB) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (nodep->funcType() == AstCFuncType::TRACE_FULL) {
|
2008-11-17 23:13:57 +01:00
|
|
|
|
} else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE) {
|
2008-11-17 23:13:57 +01:00
|
|
|
|
} else if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
} else nodep->v3fatalSrc("Bad Case");
|
|
|
|
|
|
|
2016-09-14 04:28:07 +02:00
|
|
|
|
if (nodep->initsp()) putsDecoration("// Variables\n");
|
2017-12-09 17:52:35 +01:00
|
|
|
|
emitVarList(nodep->initsp(), EVL_FUNC_ALL, "");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->initsp()->iterateAndNext(*this);
|
|
|
|
|
|
|
2016-09-14 04:28:07 +02:00
|
|
|
|
putsDecoration("// Body\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
puts("{\n");
|
|
|
|
|
|
nodep->stmtsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts("}\n");
|
2016-09-14 04:28:07 +02:00
|
|
|
|
if (nodep->finalsp()) putsDecoration("// Final\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
nodep->finalsp()->iterateAndNext(*this);
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
m_funcp = NULL;
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstTraceDecl* nodep) {
|
2013-12-14 18:17:31 +01:00
|
|
|
|
if (nodep->arrayRange().ranged()) {
|
|
|
|
|
|
puts("{int i; for (i=0; i<"+cvtToStr(nodep->arrayRange().elements())+"; i++) {\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
emitTraceInitOne(nodep);
|
|
|
|
|
|
puts("}}\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
emitTraceInitOne(nodep);
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstTraceInc* nodep) {
|
2013-12-14 18:17:31 +01:00
|
|
|
|
if (nodep->declp()->arrayRange().ranged()) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
// It traces faster if we unroll the loop
|
2013-12-14 18:17:31 +01:00
|
|
|
|
for (int i=0; i<nodep->declp()->arrayRange().elements(); i++) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
emitTraceChangeOne(nodep, i);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
emitTraceChangeOne(nodep, -1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCoverDecl* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2016-11-27 14:11:38 +01:00
|
|
|
|
virtual void visit(AstCoverInc* nodep) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
2008-06-10 03:25:10 +02:00
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
|
public:
|
2015-10-04 04:33:06 +02:00
|
|
|
|
explicit EmitCTrace(bool slow) {
|
2006-08-26 13:35:28 +02:00
|
|
|
|
m_funcp = NULL;
|
|
|
|
|
|
m_slow = slow;
|
|
|
|
|
|
}
|
|
|
|
|
|
virtual ~EmitCTrace() {}
|
|
|
|
|
|
void main() {
|
|
|
|
|
|
// Put out the file
|
2008-11-17 23:13:57 +01:00
|
|
|
|
newOutCFile(0);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
|
|
if (m_slow) emitTraceSlow();
|
|
|
|
|
|
else emitTraceFast();
|
|
|
|
|
|
|
|
|
|
|
|
v3Global.rootp()->accept(*this);
|
|
|
|
|
|
|
2008-11-17 23:13:57 +01:00
|
|
|
|
delete m_ofp; m_ofp=NULL;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
// EmitC class functions
|
|
|
|
|
|
|
|
|
|
|
|
void V3EmitC::emitc() {
|
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
|
// Process each module in turn
|
2009-11-07 12:20:20 +01:00
|
|
|
|
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
|
2018-05-01 02:34:52 +02:00
|
|
|
|
if (v3Global.opt.outputSplit()) {
|
|
|
|
|
|
{ EmitCImp fast; fast.main(nodep, false, true); }
|
|
|
|
|
|
{ EmitCImp slow; slow.main(nodep, true, false); }
|
|
|
|
|
|
} else {
|
|
|
|
|
|
{ EmitCImp both; both.main(nodep, true, true); }
|
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void V3EmitC::emitcTrace() {
|
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
|
if (v3Global.opt.trace()) {
|
2018-05-01 02:34:52 +02:00
|
|
|
|
{ EmitCTrace slow(true); slow.main(); }
|
|
|
|
|
|
{ EmitCTrace fast(false); fast.main(); }
|
2009-12-03 12:55:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|