2021-06-24 18:35:12 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Emit C++ for tree
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2023-01-01 16:18:39 +01:00
|
|
|
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
|
2021-06-24 18:35:12 +02:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3EmitCFunc.h"
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3String.h"
|
|
|
|
|
#include "V3TSP.h"
|
|
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
// We use a static char array in VL_VALUE_STRING
|
|
|
|
|
constexpr int VL_VALUE_STRING_MAX_WIDTH = 8192;
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// EmitCFunc
|
|
|
|
|
|
2022-10-12 11:19:21 +02:00
|
|
|
bool EmitCFunc::emitSimpleOk(AstNodeExpr* nodep) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// 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;
|
2022-11-30 00:17:50 +01:00
|
|
|
if (nodep->op1p() && nodep->op1p()->isWide()) return false;
|
|
|
|
|
if (nodep->op2p() && nodep->op2p()->isWide()) return false;
|
|
|
|
|
if (nodep->op3p() && nodep->op3p()->isWide()) return false;
|
2021-06-24 18:35:12 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::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 \
|
|
|
|
|
do { \
|
|
|
|
|
if (!nextComma.empty()) { \
|
|
|
|
|
puts(nextComma); \
|
|
|
|
|
nextComma = ""; \
|
|
|
|
|
} \
|
|
|
|
|
} while (false)
|
|
|
|
|
|
|
|
|
|
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 = ", ";
|
2022-01-08 18:01:39 +01:00
|
|
|
} else {
|
2021-06-24 18:35:12 +02:00
|
|
|
nextComma = ",";
|
2022-01-08 18:01:39 +01:00
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
needComma = false;
|
|
|
|
|
}
|
|
|
|
|
if (pos[1] == ' ') ++pos; // Must do even if no nextComma
|
|
|
|
|
} else if (pos[0] == '%') {
|
|
|
|
|
++pos;
|
|
|
|
|
bool detail = false;
|
|
|
|
|
AstNode* detailp = nullptr;
|
|
|
|
|
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()) {
|
|
|
|
|
UASSERT_OBJ(m_wideTempRefp, nodep,
|
|
|
|
|
"Wide Op w/ no temp, perhaps missing op in V3EmitC?");
|
|
|
|
|
COMMA;
|
|
|
|
|
if (!m_wideTempRefp->selfPointer().empty()) {
|
|
|
|
|
emitDereference(m_wideTempRefp->selfPointerProtect(m_useSelfForThis));
|
|
|
|
|
}
|
|
|
|
|
puts(m_wideTempRefp->varp()->nameProtect());
|
|
|
|
|
m_wideTempRefp = nullptr;
|
|
|
|
|
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;
|
|
|
|
|
case 'w':
|
|
|
|
|
COMMA;
|
|
|
|
|
puts(cvtToStr(detailp->widthMin()));
|
|
|
|
|
needComma = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'W':
|
|
|
|
|
if (lhsp->isWide()) {
|
|
|
|
|
COMMA;
|
|
|
|
|
puts(cvtToStr(lhsp->widthWords()));
|
|
|
|
|
needComma = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'i':
|
|
|
|
|
COMMA;
|
|
|
|
|
UASSERT_OBJ(detailp, nodep, "emitOperator() references undef node");
|
|
|
|
|
iterateAndNextNull(detailp);
|
|
|
|
|
needComma = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
nodep->v3fatalSrc("Unknown emitOperator format code: %[nlrt]" << pos[0]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (pos[0] == ')') {
|
|
|
|
|
nextComma = "";
|
|
|
|
|
puts(")");
|
|
|
|
|
} else if (pos[0] == '(') {
|
|
|
|
|
COMMA;
|
|
|
|
|
needComma = false;
|
|
|
|
|
puts("(");
|
|
|
|
|
} else {
|
|
|
|
|
// Normal text
|
|
|
|
|
if (isalnum(pos[0])) needComma = true;
|
|
|
|
|
COMMA;
|
|
|
|
|
string s;
|
|
|
|
|
s += pos[0];
|
|
|
|
|
puts(s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) {
|
2022-07-12 18:51:17 +02:00
|
|
|
if (m_emitDispState.m_format == ""
|
2021-06-24 18:35:12 +02:00
|
|
|
&& VN_IS(nodep, Display)) { // not fscanf etc, as they need to return value
|
|
|
|
|
// NOP
|
|
|
|
|
} else {
|
|
|
|
|
// Format
|
|
|
|
|
bool isStmt = false;
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstFScanF* const dispp = VN_CAST(nodep, FScanF)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
isStmt = false;
|
|
|
|
|
puts("VL_FSCANF_IX(");
|
|
|
|
|
iterate(dispp->filep());
|
|
|
|
|
puts(",");
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstSScanF* const dispp = VN_CAST(nodep, SScanF)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
isStmt = false;
|
|
|
|
|
checkMaxWords(dispp->fromp());
|
|
|
|
|
puts("VL_SSCANF_I");
|
|
|
|
|
emitIQW(dispp->fromp());
|
|
|
|
|
puts("X(");
|
|
|
|
|
puts(cvtToStr(dispp->fromp()->widthMin()));
|
|
|
|
|
puts(",");
|
|
|
|
|
iterate(dispp->fromp());
|
|
|
|
|
puts(",");
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstDisplay* const dispp = VN_CAST(nodep, Display)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
isStmt = true;
|
|
|
|
|
if (dispp->filep()) {
|
|
|
|
|
puts("VL_FWRITEF(");
|
|
|
|
|
iterate(dispp->filep());
|
|
|
|
|
puts(",");
|
|
|
|
|
} else {
|
|
|
|
|
puts("VL_WRITEF(");
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstSFormat* const dispp = VN_CAST(nodep, SFormat)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
isStmt = true;
|
|
|
|
|
puts("VL_SFORMAT_X(");
|
|
|
|
|
puts(cvtToStr(dispp->lhsp()->widthMin()));
|
|
|
|
|
putbs(",");
|
|
|
|
|
iterate(dispp->lhsp());
|
|
|
|
|
putbs(",");
|
|
|
|
|
} else if (VN_IS(nodep, SFormatF)) {
|
|
|
|
|
isStmt = false;
|
|
|
|
|
puts("VL_SFORMATF_NX(");
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("Unknown displayEmit node type");
|
|
|
|
|
}
|
2022-07-12 18:51:17 +02:00
|
|
|
ofp()->putsQuoted(m_emitDispState.m_format);
|
2021-06-24 18:35:12 +02:00
|
|
|
// Arguments
|
2022-07-12 18:51:17 +02:00
|
|
|
for (unsigned i = 0; i < m_emitDispState.m_argsp.size(); i++) {
|
|
|
|
|
const char fmt = m_emitDispState.m_argsChar[i];
|
|
|
|
|
AstNode* const argp = m_emitDispState.m_argsp[i];
|
|
|
|
|
const string func = m_emitDispState.m_argsFunc[i];
|
2021-06-24 18:35:12 +02:00
|
|
|
if (func != "" || argp) {
|
|
|
|
|
puts(",");
|
|
|
|
|
ofp()->indentInc();
|
|
|
|
|
ofp()->putbs("");
|
|
|
|
|
if (func != "") {
|
|
|
|
|
puts(func);
|
|
|
|
|
} else if (argp) {
|
|
|
|
|
const bool addrof = isScan || (fmt == '@');
|
|
|
|
|
if (addrof) puts("&(");
|
|
|
|
|
iterate(argp);
|
|
|
|
|
if (!addrof) emitDatap(argp);
|
|
|
|
|
if (addrof) puts(")");
|
|
|
|
|
}
|
|
|
|
|
ofp()->indentDec();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// End
|
|
|
|
|
puts(")");
|
|
|
|
|
if (isStmt) {
|
|
|
|
|
puts(";\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts(" ");
|
|
|
|
|
}
|
|
|
|
|
// Prep for next
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.clear();
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const string& vfmt,
|
|
|
|
|
bool ignore, char fmtLetter) {
|
|
|
|
|
// Print display argument, edits elistp
|
|
|
|
|
AstNode* argp = nullptr;
|
|
|
|
|
if (!ignore) {
|
|
|
|
|
argp = *elistp;
|
|
|
|
|
// Prep for next parameter
|
|
|
|
|
*elistp = (*elistp)->nextp();
|
|
|
|
|
if (VL_UNCOVERABLE(!argp)) {
|
|
|
|
|
// expectDisplay() checks this first, so internal error if found here
|
|
|
|
|
dispp->v3error(
|
|
|
|
|
"Internal: Missing arguments for $display-like format"); // LCOV_EXCL_LINE
|
|
|
|
|
return; // LCOV_EXCL_LINE
|
|
|
|
|
}
|
|
|
|
|
if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
|
|
|
|
|
dispp->v3error("Exceeded limit of " + cvtToStr(VL_VALUE_STRING_MAX_WIDTH)
|
|
|
|
|
+ " bits for any $display-like arguments");
|
|
|
|
|
}
|
|
|
|
|
if (argp->widthMin() > 8 && fmtLetter == 'c') {
|
|
|
|
|
// Technically legal, but surely not what the user intended.
|
|
|
|
|
argp->v3warn(WIDTH, dispp->verilogKwd() << "of %c format of > 8 bit value");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
|
|
|
|
|
string pfmt;
|
|
|
|
|
if ((fmtLetter == '#' || fmtLetter == 'd') && !isScan
|
|
|
|
|
&& vfmt == "") { // Size decimal output. Spec says leading spaces, not zeros
|
|
|
|
|
const double mantissabits = ignore ? 0 : (argp->widthMin() - ((fmtLetter == 'd') ? 1 : 0));
|
|
|
|
|
// This is log10(2**mantissabits) as log2(2**mantissabits)/log2(10),
|
|
|
|
|
// + 1.0 rounding bias.
|
|
|
|
|
double dchars = mantissabits / 3.321928094887362 + 1.0;
|
|
|
|
|
if (fmtLetter == 'd') dchars++; // space for sign
|
|
|
|
|
const int nchars = int(dchars);
|
|
|
|
|
pfmt = string("%") + cvtToStr(nchars) + fmtLetter;
|
|
|
|
|
} else {
|
|
|
|
|
pfmt = string("%") + vfmt + fmtLetter;
|
|
|
|
|
}
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushFormat(pfmt);
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!ignore) {
|
2022-05-15 15:29:15 +02:00
|
|
|
if (argp->dtypep()->basicp()
|
|
|
|
|
&& argp->dtypep()->basicp()->keyword() == VBasicDTypeKwd::STRING) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// string in SystemVerilog is std::string in C++ which is not POD
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushArg(' ', nullptr, "-1");
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushArg(' ', nullptr, cvtToStr(argp->widthMin()));
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushArg(fmtLetter, argp, "");
|
2021-06-24 18:35:12 +02:00
|
|
|
if (fmtLetter == 't' || fmtLetter == '^') {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstSFormatF* fmtp = nullptr;
|
|
|
|
|
if (const AstDisplay* const nodep = VN_CAST(dispp, Display)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
fmtp = nodep->fmtp();
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstSFormat* const nodep = VN_CAST(dispp, SFormat)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
fmtp = nodep->fmtp();
|
2021-11-26 23:55:36 +01:00
|
|
|
} else {
|
2021-06-24 18:35:12 +02:00
|
|
|
fmtp = VN_CAST(dispp, SFormatF);
|
2021-11-26 23:55:36 +01:00
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
UASSERT_OBJ(fmtp, dispp,
|
|
|
|
|
"Use of %t must be under AstDisplay, AstSFormat, or AstSFormatF");
|
|
|
|
|
UASSERT_OBJ(!fmtp->timeunit().isNone(), fmtp, "timenunit must be set");
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushArg(' ', nullptr, cvtToStr((int)fmtp->timeunit().powerOfTen()));
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushArg(fmtLetter, nullptr, "");
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const string& vformat,
|
|
|
|
|
AstNode* exprsp, bool isScan) {
|
|
|
|
|
AstNode* elistp = exprsp;
|
|
|
|
|
|
|
|
|
|
// Convert Verilog display to C printf formats
|
|
|
|
|
// "%0t" becomes "%d"
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.clear();
|
2021-06-24 18:35:12 +02:00
|
|
|
string vfmt;
|
|
|
|
|
string::const_iterator pos = vformat.begin();
|
|
|
|
|
bool inPct = false;
|
|
|
|
|
bool ignore = false;
|
|
|
|
|
for (; pos != vformat.end(); ++pos) {
|
|
|
|
|
// UINFO(1, "Parse '" << *pos << "' IP" << inPct << " List " << cvtToHex(elistp) << endl);
|
|
|
|
|
if (!inPct && pos[0] == '%') {
|
|
|
|
|
inPct = true;
|
|
|
|
|
ignore = false;
|
|
|
|
|
vfmt = "";
|
|
|
|
|
} else if (!inPct) { // Normal text
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushFormat(*pos);
|
2021-06-24 18:35:12 +02:00
|
|
|
} else { // Format character
|
|
|
|
|
inPct = false;
|
|
|
|
|
switch (tolower(pos[0])) {
|
|
|
|
|
case '0': // FALLTHRU
|
|
|
|
|
case '1': // FALLTHRU
|
|
|
|
|
case '2': // FALLTHRU
|
|
|
|
|
case '3': // FALLTHRU
|
|
|
|
|
case '4': // FALLTHRU
|
|
|
|
|
case '5': // FALLTHRU
|
|
|
|
|
case '6': // FALLTHRU
|
|
|
|
|
case '7': // FALLTHRU
|
|
|
|
|
case '8': // FALLTHRU
|
|
|
|
|
case '9': // FALLTHRU
|
|
|
|
|
case '.': // FALLTHRU
|
|
|
|
|
case '-':
|
|
|
|
|
// Digits, like %5d, etc.
|
|
|
|
|
vfmt += pos[0];
|
|
|
|
|
inPct = true; // Get more digits
|
|
|
|
|
break;
|
|
|
|
|
case '%':
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushFormat("%%"); // We're printf'ing it, so need to quote the %
|
2021-06-24 18:35:12 +02:00
|
|
|
break;
|
|
|
|
|
case '*':
|
|
|
|
|
vfmt += pos[0];
|
|
|
|
|
inPct = true; // Get more digits
|
|
|
|
|
ignore = true;
|
|
|
|
|
break;
|
|
|
|
|
// Special codes
|
|
|
|
|
case '~':
|
|
|
|
|
displayArg(nodep, &elistp, isScan, vfmt, ignore, 'd');
|
|
|
|
|
break; // Signed decimal
|
|
|
|
|
case '@':
|
|
|
|
|
displayArg(nodep, &elistp, isScan, vfmt, ignore, '@');
|
|
|
|
|
break; // Packed string
|
|
|
|
|
// Spec: h d o b c l
|
|
|
|
|
case 'b': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'b'); break;
|
|
|
|
|
case 'c': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'c'); break;
|
|
|
|
|
case 't': displayArg(nodep, &elistp, isScan, vfmt, ignore, 't'); break;
|
|
|
|
|
case 'd':
|
|
|
|
|
displayArg(nodep, &elistp, isScan, vfmt, ignore, '#');
|
|
|
|
|
break; // Unsigned decimal
|
|
|
|
|
case 'o': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'o'); break;
|
|
|
|
|
case 'h': // FALLTHRU
|
|
|
|
|
case 'x': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'x'); break;
|
|
|
|
|
case 's': displayArg(nodep, &elistp, isScan, vfmt, ignore, 's'); break;
|
|
|
|
|
case 'e': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'e'); break;
|
|
|
|
|
case 'f': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'f'); break;
|
|
|
|
|
case 'g': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'g'); break;
|
|
|
|
|
case '^': displayArg(nodep, &elistp, isScan, vfmt, ignore, '^'); break; // Realtime
|
|
|
|
|
case 'v': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'v'); break;
|
|
|
|
|
case 'u': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'u'); break;
|
|
|
|
|
case 'z': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'z'); break;
|
|
|
|
|
case 'm': {
|
|
|
|
|
UASSERT_OBJ(scopenamep, nodep, "Display with %m but no AstScopeName");
|
|
|
|
|
const string suffix = scopenamep->scopePrettySymName();
|
|
|
|
|
if (suffix == "") {
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushFormat("%S");
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushFormat("%N"); // Add a . when needed
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushArg(' ', nullptr, "vlSymsp->name()");
|
|
|
|
|
m_emitDispState.pushFormat(suffix);
|
2021-06-24 18:35:12 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'l': {
|
|
|
|
|
// Better than not compiling
|
2022-07-12 18:51:17 +02:00
|
|
|
m_emitDispState.pushFormat("----");
|
2021-06-24 18:35:12 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
nodep->v3error("Unknown $display-like format code: '%" << pos[0] << "'");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (VL_UNCOVERABLE(elistp)) {
|
|
|
|
|
// expectFormat also checks this, and should have found it first, so internal
|
|
|
|
|
elistp->v3error("Internal: Extra arguments for $display-like format"); // LCOV_EXCL_LINE
|
|
|
|
|
}
|
|
|
|
|
displayEmit(nodep, isScan);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-13 18:42:17 +02:00
|
|
|
void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer) {
|
|
|
|
|
puts("(");
|
2021-06-24 18:35:12 +02:00
|
|
|
bool comma = false;
|
|
|
|
|
if (nodep->funcp()->isLoose() && !nodep->funcp()->isStatic()) {
|
2021-07-13 18:42:17 +02:00
|
|
|
UASSERT_OBJ(!selfPointer.empty(), nodep, "Call to loose method without self pointer");
|
|
|
|
|
puts(selfPointer);
|
2021-06-24 18:35:12 +02:00
|
|
|
comma = true;
|
|
|
|
|
}
|
|
|
|
|
if (!nodep->argTypes().empty()) {
|
|
|
|
|
if (comma) puts(", ");
|
|
|
|
|
puts(nodep->argTypes());
|
|
|
|
|
comma = true;
|
|
|
|
|
}
|
2022-11-30 12:42:48 +01:00
|
|
|
putCommaIterateNext(nodep->argsp(), comma);
|
2022-10-12 11:19:21 +02:00
|
|
|
puts(")");
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::emitDereference(const string& pointer) {
|
|
|
|
|
if (pointer[0] == '(' && pointer[1] == '&') {
|
|
|
|
|
// remove "address of" followed by immediate dereference
|
|
|
|
|
// Note: this relies on only the form '(&OBJECT)' being used by Verilator
|
|
|
|
|
puts(pointer.substr(2, pointer.length() - 3));
|
|
|
|
|
puts(".");
|
|
|
|
|
} else {
|
|
|
|
|
puts(pointer);
|
|
|
|
|
puts("->");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstConst* const constp = VN_CAST(nodep, Const)) {
|
2022-08-30 05:50:32 +02:00
|
|
|
putbs("std::string{");
|
2021-06-24 18:35:12 +02:00
|
|
|
putsQuoted(constp->num().toString());
|
2022-08-30 05:50:32 +02:00
|
|
|
puts("}");
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
|
|
|
|
putbs("VL_CVT_PACK_STR_N");
|
|
|
|
|
emitIQW(nodep);
|
|
|
|
|
puts("(");
|
|
|
|
|
if (nodep->isWide()) {
|
|
|
|
|
// Note argument width, not node width (which is always 32)
|
|
|
|
|
puts(cvtToStr(nodep->widthWords()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextNull(nodep);
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::emitCvtWideArray(AstNode* nodep, AstNode* fromp) {
|
|
|
|
|
putbs("VL_CVT_W_A(");
|
|
|
|
|
iterate(nodep);
|
|
|
|
|
puts(", ");
|
|
|
|
|
iterate(fromp);
|
|
|
|
|
putbs(".atDefault()"); // Not accessed; only to get the proper type of values
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString) {
|
|
|
|
|
// Put out constant set to the specified variable, or given variable in a string
|
2022-10-13 14:33:15 +02:00
|
|
|
if (nodep->num().isNull()) {
|
|
|
|
|
puts("VlNull{}");
|
|
|
|
|
} else if (nodep->num().isFourState()) {
|
2021-06-24 18:35:12 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context");
|
|
|
|
|
} else if (nodep->num().isString()) {
|
2022-08-30 05:50:32 +02:00
|
|
|
putbs("std::string{");
|
2021-06-24 18:35:12 +02:00
|
|
|
putsQuoted(nodep->num().toString());
|
2022-08-30 05:50:32 +02:00
|
|
|
puts("}");
|
2021-06-24 18:35:12 +02:00
|
|
|
} else if (nodep->isWide()) {
|
|
|
|
|
int upWidth = nodep->num().widthMin();
|
|
|
|
|
int chunks = 0;
|
|
|
|
|
if (upWidth > EMITC_NUM_CONSTW * VL_EDATASIZE) {
|
|
|
|
|
// Output e.g. 8 words in groups of e.g. 8
|
|
|
|
|
chunks = (upWidth - 1) / (EMITC_NUM_CONSTW * VL_EDATASIZE);
|
|
|
|
|
upWidth %= (EMITC_NUM_CONSTW * VL_EDATASIZE);
|
|
|
|
|
if (upWidth == 0) upWidth = (EMITC_NUM_CONSTW * VL_EDATASIZE);
|
|
|
|
|
}
|
|
|
|
|
{ // 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_EDATASIZE));
|
|
|
|
|
} else {
|
|
|
|
|
putbs("VL_CONST_W_");
|
|
|
|
|
puts(cvtToStr(VL_WORDS_I(upWidth)));
|
|
|
|
|
puts("X(");
|
|
|
|
|
puts(cvtToStr(nodep->widthMin()));
|
|
|
|
|
}
|
|
|
|
|
puts(",");
|
|
|
|
|
if (!assigntop) {
|
|
|
|
|
puts(assignString);
|
2021-10-22 18:36:58 +02:00
|
|
|
} else {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!assigntop->selfPointer().empty()) {
|
|
|
|
|
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
|
|
|
|
|
}
|
|
|
|
|
puts(assigntop->varp()->nameProtect());
|
|
|
|
|
}
|
|
|
|
|
for (int word = VL_WORDS_I(upWidth) - 1; word >= 0; word--) {
|
|
|
|
|
// Only 32 bits - llx + long long here just to appease CPP format warning
|
2022-03-27 21:27:40 +02:00
|
|
|
ofp()->printf(",0x%08" PRIx64, static_cast<uint64_t>(nodep->num().edataWord(
|
2022-01-01 22:04:20 +01:00
|
|
|
word + chunks * EMITC_NUM_CONSTW)));
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
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_EDATASIZE));
|
|
|
|
|
puts(",");
|
|
|
|
|
if (!assigntop) {
|
|
|
|
|
puts(assignString);
|
2021-10-22 18:36:58 +02:00
|
|
|
} else {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!assigntop->selfPointer().empty()) {
|
|
|
|
|
emitDereference(assigntop->selfPointerProtect(m_useSelfForThis));
|
|
|
|
|
}
|
|
|
|
|
puts(assigntop->varp()->nameProtect());
|
|
|
|
|
}
|
|
|
|
|
for (int word = EMITC_NUM_CONSTW - 1; word >= 0; word--) {
|
|
|
|
|
// Only 32 bits - llx + long long here just to appease CPP format warning
|
2022-03-27 21:27:40 +02:00
|
|
|
ofp()->printf(",0x%08" PRIx64, static_cast<uint64_t>(nodep->num().edataWord(
|
2022-01-01 22:04:20 +01:00
|
|
|
word + chunks * EMITC_NUM_CONSTW)));
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
} else if (nodep->isDouble()) {
|
|
|
|
|
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
|
2023-01-29 04:11:09 +01:00
|
|
|
} else if (std::isinf(nodep->num().toDouble())) {
|
|
|
|
|
if (std::signbit(nodep->num().toDouble())) puts("-");
|
|
|
|
|
ofp()->puts("std::numeric_limits<double>::infinity()");
|
|
|
|
|
} else if (std::isnan(nodep->num().toDouble())) {
|
|
|
|
|
if (std::signbit(nodep->num().toDouble())) puts("-");
|
|
|
|
|
ofp()->puts("std::numeric_limits<double>::quiet_NaN()");
|
2021-06-24 18:35:12 +02:00
|
|
|
} 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());
|
|
|
|
|
}
|
|
|
|
|
} else if (nodep->isQuad()) {
|
2022-03-27 21:27:40 +02:00
|
|
|
const uint64_t num = nodep->toUQuad();
|
2021-06-24 18:35:12 +02:00
|
|
|
if (num < 10) {
|
2022-01-01 22:04:20 +01:00
|
|
|
ofp()->printf("%" PRIu64 "ULL", num);
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
2022-01-01 22:04:20 +01:00
|
|
|
ofp()->printf("0x%" PRIx64 "ULL", num);
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2021-11-26 23:55:36 +01:00
|
|
|
const uint32_t num = nodep->toUInt();
|
2021-06-24 18:35:12 +02:00
|
|
|
// Only 32 bits - llx + long long here just to appease CPP format warning
|
|
|
|
|
if (num < 10) {
|
|
|
|
|
puts(cvtToStr(num));
|
|
|
|
|
} else {
|
2022-03-27 21:27:40 +02:00
|
|
|
ofp()->printf("0x%" PRIx64, static_cast<uint64_t>(num));
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
// If signed, we'll do our own functions
|
|
|
|
|
// But must be here, or <= comparisons etc may end up signed
|
|
|
|
|
puts("U");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::emitSetVarConstant(const string& assignString, AstConst* constp) {
|
|
|
|
|
if (!constp->isWide()) {
|
|
|
|
|
puts(assignString);
|
|
|
|
|
puts(" = ");
|
|
|
|
|
}
|
|
|
|
|
emitConstant(constp, nullptr, assignString);
|
|
|
|
|
puts(";\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::emitVarReset(AstVar* varp) {
|
|
|
|
|
AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
|
2022-12-23 16:51:52 +01:00
|
|
|
const string varNameProtected = (VN_IS(m_modp, Class) || varp->isFuncLocal())
|
|
|
|
|
? varp->nameProtect()
|
|
|
|
|
: "vlSelf->" + varp->nameProtect();
|
2021-06-24 18:35:12 +02:00
|
|
|
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()) {
|
|
|
|
|
UASSERT_OBJ(varp->valuep(), varp, "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->nameProtect()+" = "+varp->valuep()->name()+"\n");
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstInitArray* const initarp = VN_CAST(varp->valuep(), InitArray)) {
|
2022-07-30 16:01:25 +02:00
|
|
|
if (VN_IS(dtypep, AssocArrayDType)) {
|
2021-12-11 17:29:01 +01:00
|
|
|
if (initarp->defaultp()) {
|
|
|
|
|
emitSetVarConstant(varNameProtected + ".atDefault()",
|
|
|
|
|
VN_AS(initarp->defaultp(), Const));
|
|
|
|
|
}
|
|
|
|
|
const auto& mapr = initarp->map();
|
|
|
|
|
for (const auto& itr : mapr) {
|
|
|
|
|
AstNode* const valuep = itr.second->valuep();
|
|
|
|
|
emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")",
|
|
|
|
|
VN_AS(valuep, Const));
|
|
|
|
|
}
|
2022-07-30 16:01:25 +02:00
|
|
|
} else if (VN_IS(dtypep, WildcardArrayDType)) {
|
2022-07-20 15:01:36 +02:00
|
|
|
if (initarp->defaultp()) {
|
|
|
|
|
emitSetVarConstant(varNameProtected + ".atDefault()",
|
|
|
|
|
VN_AS(initarp->defaultp(), Const));
|
|
|
|
|
}
|
|
|
|
|
const auto& mapr = initarp->map();
|
|
|
|
|
for (const auto& itr : mapr) {
|
|
|
|
|
AstNode* const valuep = itr.second->valuep();
|
|
|
|
|
emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")",
|
|
|
|
|
VN_AS(valuep, Const));
|
|
|
|
|
}
|
2021-12-11 17:29:01 +01:00
|
|
|
} else if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (initarp->defaultp()) {
|
2022-10-16 00:47:10 +02:00
|
|
|
puts("for (int __Vi = 0; __Vi < " + cvtToStr(adtypep->elementsConst()));
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("; ++__Vi) {\n");
|
2021-10-22 14:56:48 +02:00
|
|
|
emitSetVarConstant(varNameProtected + "[__Vi]", VN_AS(initarp->defaultp(), Const));
|
2021-06-24 18:35:12 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
}
|
2021-12-11 17:22:04 +01:00
|
|
|
const auto& mapr = initarp->map();
|
2021-06-24 18:35:12 +02:00
|
|
|
for (const auto& itr : mapr) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const valuep = itr.second->valuep();
|
2021-06-24 18:35:12 +02:00
|
|
|
emitSetVarConstant(varNameProtected + "[" + cvtToStr(itr.first) + "]",
|
2021-10-22 14:56:48 +02:00
|
|
|
VN_AS(valuep, Const));
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
varp->v3fatalSrc("InitArray under non-arrayed var");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
puts(emitVarResetRecurse(varp, varNameProtected, dtypep, 0, ""));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameProtected,
|
|
|
|
|
AstNodeDType* dtypep, int depth, const string& suffix) {
|
|
|
|
|
dtypep = dtypep->skipRefp();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstBasicDType* const basicp = dtypep->basicp();
|
2021-06-24 18:35:12 +02:00
|
|
|
// Returns string to do resetting, empty to do nothing (which caller should handle)
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstAssocArrayDType* const adtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Access std::array as C array
|
|
|
|
|
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
|
|
|
|
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
|
|
|
|
suffix + ".atDefault()" + cvtarray);
|
2022-07-20 15:01:36 +02:00
|
|
|
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
|
|
|
|
|
// Access std::array as C array
|
|
|
|
|
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
|
|
|
|
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
|
|
|
|
suffix + ".atDefault()" + cvtarray);
|
2021-06-24 18:35:12 +02:00
|
|
|
} else if (VN_IS(dtypep, ClassRefDType)) {
|
|
|
|
|
return ""; // Constructor does it
|
2022-10-20 12:31:00 +02:00
|
|
|
} else if (VN_IS(dtypep, IfaceRefDType)) {
|
|
|
|
|
return varNameProtected + suffix + " = nullptr;\n";
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Access std::array as C array
|
|
|
|
|
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
|
|
|
|
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
|
|
|
|
suffix + ".atDefault()" + cvtarray);
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Access std::array as C array
|
|
|
|
|
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
|
|
|
|
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
|
|
|
|
suffix + ".atDefault()" + cvtarray);
|
2022-12-23 13:34:49 +01:00
|
|
|
} else if (VN_IS(dtypep, SampleQueueDType)) {
|
|
|
|
|
return "";
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
2021-06-24 18:35:12 +02:00
|
|
|
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
|
|
|
|
|
"Should have swapped msb & lsb earlier.");
|
|
|
|
|
const string ivar = string("__Vi") + cvtToStr(depth);
|
2022-10-16 00:47:10 +02:00
|
|
|
const string pre = ("for (int " + ivar + " = " + cvtToStr(0) + "; " + ivar + " < "
|
2021-06-24 18:35:12 +02:00
|
|
|
+ cvtToStr(adtypep->elementsConst()) + "; ++" + ivar + ") {\n");
|
|
|
|
|
const string below = emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(),
|
|
|
|
|
depth + 1, suffix + "[" + ivar + "]");
|
|
|
|
|
const string post = "}\n";
|
|
|
|
|
return below.empty() ? "" : pre + below + post;
|
2023-01-28 04:41:12 +01:00
|
|
|
} else if (VN_IS(dtypep, NodeUOrStructDType) && !VN_AS(dtypep, NodeUOrStructDType)->packed()) {
|
|
|
|
|
const auto* const sdtypep = VN_AS(dtypep, NodeUOrStructDType);
|
2022-12-21 01:22:42 +01:00
|
|
|
string literal;
|
|
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
|
|
|
const std::string line
|
|
|
|
|
= emitVarResetRecurse(varp, varNameProtected + suffix + "." + itemp->nameProtect(),
|
|
|
|
|
itemp->dtypep(), depth + 1, "");
|
|
|
|
|
if (!line.empty()) literal += line;
|
|
|
|
|
}
|
|
|
|
|
return literal;
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (basicp && basicp->keyword() == VBasicDTypeKwd::STRING) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// String's constructor deals with it
|
|
|
|
|
return "";
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
} else if (basicp && basicp->isForkSync()) {
|
|
|
|
|
return "";
|
|
|
|
|
} else if (basicp && basicp->isDelayScheduler()) {
|
|
|
|
|
return "";
|
|
|
|
|
} else if (basicp && basicp->isTriggerScheduler()) {
|
|
|
|
|
return "";
|
2022-10-22 16:05:39 +02:00
|
|
|
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
|
|
|
|
|
return "";
|
2021-06-24 18:35:12 +02:00
|
|
|
} else if (basicp) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const bool zeroit
|
2021-11-24 00:22:16 +01:00
|
|
|
= (varp->attrFileDescr() // Zero so we don't do file IO if never $fopen
|
2022-12-23 16:51:52 +01:00
|
|
|
|| varp->isFuncLocal() // Randomization too slow
|
2021-06-24 18:35:12 +02:00
|
|
|
|| (basicp && basicp->isZeroInit())
|
|
|
|
|
|| (v3Global.opt.underlineZero() && !varp->name().empty() && varp->name()[0] == '_')
|
|
|
|
|
|| (v3Global.opt.xInitial() == "fast" || v3Global.opt.xInitial() == "0"));
|
2022-12-23 16:51:52 +01:00
|
|
|
const bool slow = !varp->isFuncLocal() && !varp->isClassMember();
|
2021-06-24 18:35:12 +02:00
|
|
|
splitSizeInc(1);
|
|
|
|
|
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
|
|
|
|
|
string out;
|
|
|
|
|
if (varp->valuep()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstConst* const constp = VN_AS(varp->valuep(), Const);
|
2021-06-24 18:35:12 +02:00
|
|
|
if (!constp) varp->v3fatalSrc("non-const initializer for variable");
|
|
|
|
|
for (int w = 0; w < varp->widthWords(); ++w) {
|
|
|
|
|
out += varNameProtected + suffix + "[" + cvtToStr(w) + "] = ";
|
|
|
|
|
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-12-23 16:51:52 +01:00
|
|
|
out += zeroit ? (slow ? "VL_ZERO_RESET_W(" : "VL_ZERO_W(") : "VL_RAND_RESET_W(";
|
2021-06-24 18:35:12 +02:00
|
|
|
out += cvtToStr(dtypep->widthMin());
|
|
|
|
|
out += ", " + varNameProtected + suffix + ");\n";
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
} else {
|
|
|
|
|
string out = varNameProtected + suffix;
|
|
|
|
|
// 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())) {
|
|
|
|
|
out += " = 0;\n";
|
|
|
|
|
} else {
|
|
|
|
|
out += " = VL_RAND_RESET_";
|
|
|
|
|
out += dtypep->charIQWN();
|
|
|
|
|
out += "(" + cvtToStr(dtypep->widthMin()) + ");\n";
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
v3fatalSrc("Unknown node type in reset generator: " << varp->prettyTypeName());
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|