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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstMT.h"
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3EmitCFunc.h"
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
#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
|
2025-10-10 03:23:11 +02:00
|
|
|
string out;
|
|
|
|
|
putnbs(nodep, "");
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
bool needComma = false;
|
2025-10-10 03:23:11 +02:00
|
|
|
string nextComma;
|
|
|
|
|
auto commaOut = [&out, &nextComma]() {
|
|
|
|
|
if (!nextComma.empty()) {
|
|
|
|
|
out += nextComma;
|
|
|
|
|
nextComma = "";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto putOut = [this, &out]() {
|
|
|
|
|
if (!out.empty()) puts(out);
|
|
|
|
|
out = "";
|
|
|
|
|
};
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
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]) {
|
2025-10-10 03:23:11 +02:00
|
|
|
case '%': out += '%'; break;
|
|
|
|
|
case 'k':
|
|
|
|
|
putOut();
|
|
|
|
|
putbs("");
|
|
|
|
|
break;
|
2021-06-24 18:35:12 +02:00
|
|
|
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?");
|
2025-10-10 03:23:11 +02:00
|
|
|
commaOut();
|
|
|
|
|
putOut();
|
2023-09-08 13:34:35 +02:00
|
|
|
if (!m_wideTempRefp->selfPointer().isEmpty()) {
|
2024-01-25 03:51:47 +01:00
|
|
|
emitDereference(m_wideTempRefp,
|
|
|
|
|
m_wideTempRefp->selfPointerProtect(m_useSelfForThis));
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2025-10-10 03:23:11 +02:00
|
|
|
out += m_wideTempRefp->varp()->nameProtect();
|
2021-06-24 18:35:12 +02:00
|
|
|
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]) {
|
2025-10-10 03:23:11 +02:00
|
|
|
case 'q':
|
|
|
|
|
putOut();
|
|
|
|
|
emitIQW(detailp);
|
|
|
|
|
break;
|
2021-06-24 18:35:12 +02:00
|
|
|
case 'w':
|
2025-10-10 03:23:11 +02:00
|
|
|
commaOut();
|
|
|
|
|
out += cvtToStr(detailp->widthMin());
|
2021-06-24 18:35:12 +02:00
|
|
|
needComma = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'W':
|
|
|
|
|
if (lhsp->isWide()) {
|
2025-10-10 03:23:11 +02:00
|
|
|
commaOut();
|
|
|
|
|
out += cvtToStr(lhsp->widthWords());
|
2021-06-24 18:35:12 +02:00
|
|
|
needComma = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'i':
|
2025-10-10 03:23:11 +02:00
|
|
|
commaOut();
|
2021-06-24 18:35:12 +02:00
|
|
|
UASSERT_OBJ(detailp, nodep, "emitOperator() references undef node");
|
2025-10-10 03:23:11 +02:00
|
|
|
putOut();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(detailp);
|
2021-06-24 18:35:12 +02:00
|
|
|
needComma = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
nodep->v3fatalSrc("Unknown emitOperator format code: %[nlrt]" << pos[0]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (pos[0] == ')') {
|
|
|
|
|
nextComma = "";
|
2025-10-10 03:23:11 +02:00
|
|
|
out += ')';
|
2021-06-24 18:35:12 +02:00
|
|
|
} else if (pos[0] == '(') {
|
2025-10-10 03:23:11 +02:00
|
|
|
commaOut();
|
2021-06-24 18:35:12 +02:00
|
|
|
needComma = false;
|
2025-10-10 03:23:11 +02:00
|
|
|
out += '(';
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
|
|
|
|
// Normal text
|
2023-02-11 02:32:35 +01:00
|
|
|
if (std::isalnum(pos[0])) needComma = true;
|
2025-10-10 03:23:11 +02:00
|
|
|
commaOut();
|
|
|
|
|
out += pos[0];
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-10 03:23:11 +02:00
|
|
|
putOut();
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2024-01-28 17:05:38 +01:00
|
|
|
putns(nodep, "VL_FSCANF_INX(");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(dispp->filep());
|
2021-06-24 18:35:12 +02:00
|
|
|
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());
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(nodep, "VL_SSCANF_I");
|
2021-06-24 18:35:12 +02:00
|
|
|
emitIQW(dispp->fromp());
|
2024-01-28 17:05:38 +01:00
|
|
|
puts("NX(");
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(cvtToStr(dispp->fromp()->widthMin()));
|
|
|
|
|
puts(",");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(dispp->fromp());
|
2021-06-24 18:35:12 +02:00
|
|
|
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()) {
|
2024-01-28 17:05:38 +01:00
|
|
|
putns(nodep, "VL_FWRITEF_NX(");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(dispp->filep());
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(",");
|
|
|
|
|
} else {
|
2024-01-28 17:05:38 +01:00
|
|
|
putns(nodep, "VL_WRITEF_NX(");
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
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;
|
2024-01-28 17:05:38 +01:00
|
|
|
puts("VL_SFORMAT_NX(");
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(cvtToStr(dispp->lhsp()->widthMin()));
|
|
|
|
|
putbs(",");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(dispp->lhsp());
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs(",");
|
|
|
|
|
} else if (VN_IS(nodep, SFormatF)) {
|
|
|
|
|
isStmt = false;
|
2024-01-28 17:05:38 +01:00
|
|
|
putns(nodep, "VL_SFORMATF_N_NX(");
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("Unknown displayEmit node type");
|
|
|
|
|
}
|
2022-07-12 18:51:17 +02:00
|
|
|
ofp()->putsQuoted(m_emitDispState.m_format);
|
2024-01-28 17:05:38 +01:00
|
|
|
ofp()->puts(",0"); // MSVC++ requires va_args to not be off reference
|
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) {
|
2025-03-11 18:32:34 +01:00
|
|
|
const bool addrof = isScan || (fmt == '@') || (fmt == 'p');
|
2021-06-24 18:35:12 +02:00
|
|
|
if (addrof) puts("&(");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(argp);
|
2021-06-24 18:35:12 +02:00
|
|
|
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;
|
2025-10-03 12:49:13 +02:00
|
|
|
if (VL_UNCOVERABLE(!argp)) { // LCOV_EXCL_START
|
2021-06-24 18:35:12 +02:00
|
|
|
// expectDisplay() checks this first, so internal error if found here
|
2025-10-03 12:49:13 +02:00
|
|
|
dispp->v3error("Internal: Missing arguments for $display-like format");
|
|
|
|
|
return;
|
|
|
|
|
} // LCOV_EXCL_STOP
|
2025-07-20 03:12:10 +02:00
|
|
|
// Prep for next parameter
|
|
|
|
|
*elistp = (*elistp)->nextp();
|
2021-06-24 18:35:12 +02:00
|
|
|
if (argp->widthMin() > VL_VALUE_STRING_MAX_WIDTH) {
|
2025-05-17 22:28:09 +02:00
|
|
|
dispp->v3warn(E_UNSUPPORTED, "Unsupported: Exceeded limit of "
|
|
|
|
|
+ cvtToStr(VL_VALUE_STRING_MAX_WIDTH)
|
|
|
|
|
+ " bits for any $display-like arguments");
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
if (argp->widthMin() > 8 && fmtLetter == 'c') {
|
|
|
|
|
// Technically legal, but surely not what the user intended.
|
2023-02-03 00:25:25 +01:00
|
|
|
argp->v3warn(WIDTHTRUNC, dispp->verilogKwd() << "of %c format of > 8 bit value");
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 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);
|
2024-07-14 17:39:45 +02:00
|
|
|
pfmt = "%"s + cvtToStr(nchars) + fmtLetter;
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
2024-07-14 17:39:45 +02:00
|
|
|
pfmt = "%"s + vfmt + fmtLetter;
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
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 == '^') {
|
2025-09-01 20:40:22 +02:00
|
|
|
VTimescale timeunit = VTimescale::NONE;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstDisplay* const nodep = VN_CAST(dispp, Display)) {
|
2025-09-01 20:40:22 +02:00
|
|
|
timeunit = nodep->fmtp()->timeunit();
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstSFormat* const nodep = VN_CAST(dispp, SFormat)) {
|
2025-09-01 20:40:22 +02:00
|
|
|
timeunit = nodep->fmtp()->timeunit();
|
|
|
|
|
} else if (const AstSScanF* const nodep = VN_CAST(dispp, SScanF)) {
|
|
|
|
|
timeunit = nodep->timeunit();
|
|
|
|
|
} else if (const AstSFormatF* const nodep = VN_CAST(dispp, SFormatF)) {
|
|
|
|
|
timeunit = nodep->timeunit();
|
2021-11-26 23:55:36 +01:00
|
|
|
}
|
2025-09-01 20:40:22 +02:00
|
|
|
UASSERT_OBJ(!timeunit.isNone(), dispp,
|
|
|
|
|
"Use of %t must be under AstDisplay, AstSFormat, or AstSFormatF, or "
|
|
|
|
|
"SScanF, and timeunit set");
|
|
|
|
|
m_emitDispState.pushArg(' ', nullptr, cvtToStr((int)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"
|
2023-09-16 16:10:21 +02:00
|
|
|
VL_RESTORER(m_emitDispState);
|
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) {
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(1, "Parse '" << *pos << "' IP" << inPct << " List " << cvtToHex(elistp));
|
2021-06-24 18:35:12 +02:00
|
|
|
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;
|
2023-02-11 02:32:35 +01:00
|
|
|
switch (std::tolower(pos[0])) {
|
2021-06-24 18:35:12 +02:00
|
|
|
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;
|
2025-03-11 18:32:34 +01:00
|
|
|
case 'p': displayArg(nodep, &elistp, isScan, vfmt, ignore, 'p'); break;
|
2021-06-24 18:35:12 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-16 14:02:29 +02:00
|
|
|
void EmitCFunc::emitCCallArgs(const AstNodeCCall* nodep, const string& selfPointer,
|
|
|
|
|
bool inProcess) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(nodep, "(");
|
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;
|
|
|
|
|
}
|
2023-06-01 16:02:08 +02:00
|
|
|
if (nodep->funcp()->needProcess()) {
|
|
|
|
|
if (comma) puts(", ");
|
2023-07-14 17:12:02 +02:00
|
|
|
if (VN_IS(nodep->backp(), CAwait) || !nodep->funcp()->isCoroutine()) {
|
2023-06-01 16:02:08 +02:00
|
|
|
puts("vlProcess");
|
2023-10-16 14:02:29 +02:00
|
|
|
} else if (inProcess) {
|
|
|
|
|
puts("std::make_shared<VlProcess>(vlProcess)");
|
2023-06-01 16:02:08 +02:00
|
|
|
} else {
|
|
|
|
|
puts("std::make_shared<VlProcess>()");
|
|
|
|
|
}
|
|
|
|
|
comma = true;
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2024-01-25 03:51:47 +01:00
|
|
|
void EmitCFunc::emitDereference(AstNode* nodep, const string& pointer) {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (pointer[0] == '(' && pointer[1] == '&') {
|
|
|
|
|
// remove "address of" followed by immediate dereference
|
|
|
|
|
// Note: this relies on only the form '(&OBJECT)' being used by Verilator
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(nodep, pointer.substr(2, pointer.length() - 3));
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(".");
|
|
|
|
|
} else {
|
2024-07-15 15:44:01 +02:00
|
|
|
if (pointer == "vlSelf" && m_usevlSelfRef) {
|
|
|
|
|
puts("vlSelfRef.");
|
|
|
|
|
} else {
|
|
|
|
|
putns(nodep, pointer);
|
|
|
|
|
puts("->");
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::emitCvtPackStr(AstNode* nodep) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstConst* const constp = VN_CAST(nodep, Const)) {
|
2025-01-22 01:51:43 +01:00
|
|
|
emitConstantString(constp);
|
2023-06-14 04:46:42 +02:00
|
|
|
} else if (VN_IS(nodep->dtypep(), StreamDType)) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putnbs(nodep, "VL_CVT_PACK_STR_ND(");
|
2023-06-14 04:46:42 +02:00
|
|
|
iterateAndNextConstNull(nodep);
|
|
|
|
|
puts(")");
|
2021-06-24 18:35:12 +02:00
|
|
|
} else {
|
2024-01-25 03:51:47 +01:00
|
|
|
putnbs(nodep, "VL_CVT_PACK_STR_N");
|
2021-06-24 18:35:12 +02:00
|
|
|
emitIQW(nodep);
|
|
|
|
|
puts("(");
|
|
|
|
|
if (nodep->isWide()) {
|
|
|
|
|
// Note argument width, not node width (which is always 32)
|
|
|
|
|
puts(cvtToStr(nodep->widthWords()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
}
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(nodep);
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCFunc::emitCvtWideArray(AstNode* nodep, AstNode* fromp) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putnbs(nodep, "VL_CVT_W_A(");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep);
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(", ");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(fromp);
|
2021-06-24 18:35:12 +02:00
|
|
|
putbs(".atDefault()"); // Not accessed; only to get the proper type of values
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-04 13:23:13 +02:00
|
|
|
void EmitCFunc::emitConstant(AstConst* nodep) {
|
2021-06-24 18:35:12 +02:00
|
|
|
// Put out constant set to the specified variable, or given variable in a string
|
2025-10-04 13:23:13 +02:00
|
|
|
const V3Number& num = nodep->num();
|
|
|
|
|
if (num.isFourState()) {
|
2021-06-24 18:35:12 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context");
|
2025-10-04 13:23:13 +02:00
|
|
|
return;
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
2025-10-04 13:23:13 +02:00
|
|
|
putns(nodep, num.emitC());
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-22 01:51:43 +01:00
|
|
|
void EmitCFunc::emitConstantString(const AstConst* nodep) {
|
2025-10-01 04:04:24 +02:00
|
|
|
// Const might be a Verilog array-type string, but need to always output std::string
|
2025-01-22 01:51:43 +01:00
|
|
|
putnbs(nodep, "std::string{");
|
|
|
|
|
const string str = nodep->num().toString();
|
|
|
|
|
if (!str.empty()) putsQuoted(str);
|
|
|
|
|
puts("}");
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
void EmitCFunc::emitSetVarConstant(const string& assignString, AstConst* constp) {
|
2025-10-04 13:23:13 +02:00
|
|
|
puts(assignString);
|
|
|
|
|
puts(" = ");
|
|
|
|
|
emitConstant(constp);
|
2021-06-24 18:35:12 +02:00
|
|
|
puts(";\n");
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-26 04:48:53 +01:00
|
|
|
void EmitCFunc::emitVarReset(AstVar* varp, bool constructing) {
|
|
|
|
|
// 'constructing' indicates that the object was just constructed, so no need to clear it also
|
2021-06-24 18:35:12 +02:00
|
|
|
AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
|
2025-04-26 15:52:44 +02:00
|
|
|
const string vlSelf = VSelfPointerText::replaceThis(m_useSelfForThis, "this->");
|
2022-12-23 16:51:52 +01:00
|
|
|
const string varNameProtected = (VN_IS(m_modp, Class) || varp->isFuncLocal())
|
|
|
|
|
? varp->nameProtect()
|
2025-04-26 15:52:44 +02:00
|
|
|
: 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));
|
|
|
|
|
}
|
2025-02-26 04:48:53 +01:00
|
|
|
if (!constructing) puts(varNameProtected + ".clear();");
|
2021-12-11 17:29:01 +01:00
|
|
|
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));
|
|
|
|
|
}
|
2025-02-26 04:48:53 +01:00
|
|
|
if (!constructing) puts(varNameProtected + ".clear();");
|
2022-07-20 15:01:36 +02:00
|
|
|
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 {
|
2025-02-26 04:48:53 +01:00
|
|
|
putns(varp, emitVarResetRecurse(varp, constructing, varNameProtected, dtypep, 0, ""));
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-26 04:48:53 +01:00
|
|
|
string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
|
|
|
|
const string& varNameProtected, AstNodeDType* dtypep,
|
|
|
|
|
int depth, const string& suffix) {
|
2021-06-24 18:35:12 +02:00
|
|
|
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()" : "");
|
2025-02-26 04:48:53 +01:00
|
|
|
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
|
|
|
|
return pre
|
|
|
|
|
+ emitVarResetRecurse(varp, constructing, 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()" : "");
|
2025-02-26 04:48:53 +01:00
|
|
|
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
|
|
|
|
return pre
|
|
|
|
|
+ emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
|
|
|
|
depth + 1, suffix + ".atDefault()" + cvtarray);
|
2023-09-19 03:17:21 +02:00
|
|
|
} else if (VN_IS(dtypep, CDType)) {
|
|
|
|
|
return ""; // Constructor does it
|
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()" : "");
|
2025-02-26 04:48:53 +01:00
|
|
|
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
|
|
|
|
return pre
|
|
|
|
|
+ emitVarResetRecurse(varp, constructing, 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()" : "");
|
2025-02-26 04:48:53 +01:00
|
|
|
const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n";
|
|
|
|
|
return pre
|
|
|
|
|
+ emitVarResetRecurse(varp, constructing, 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.");
|
2024-07-14 17:39:45 +02:00
|
|
|
const string ivar = "__Vi"s + 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");
|
2025-02-26 04:48:53 +01:00
|
|
|
const string below
|
|
|
|
|
= emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(),
|
|
|
|
|
depth + 1, suffix + "[" + ivar + "]");
|
2021-06-24 18:35:12 +02:00
|
|
|
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)) {
|
2025-02-26 04:48:53 +01:00
|
|
|
const std::string line = emitVarResetRecurse(
|
|
|
|
|
varp, constructing, varNameProtected + suffix + "." + itemp->nameProtect(),
|
|
|
|
|
itemp->dtypep(), depth + 1, "");
|
2022-12-21 01:22:42 +01:00
|
|
|
if (!line.empty()) literal += line;
|
|
|
|
|
}
|
|
|
|
|
return literal;
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (basicp && basicp->keyword() == VBasicDTypeKwd::STRING) {
|
2025-02-26 04:48:53 +01:00
|
|
|
if (constructing) return ""; // String's constructor deals with it
|
|
|
|
|
return varNameProtected + suffix + ".clear();\n";
|
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 "";
|
2023-06-01 16:02:08 +02:00
|
|
|
} else if (basicp && basicp->isProcessRef()) {
|
|
|
|
|
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->isDelayScheduler()) {
|
|
|
|
|
return "";
|
|
|
|
|
} else if (basicp && basicp->isTriggerScheduler()) {
|
|
|
|
|
return "";
|
2022-10-22 16:05:39 +02:00
|
|
|
} else if (basicp && basicp->isDynamicTriggerScheduler()) {
|
|
|
|
|
return "";
|
2025-07-25 12:13:46 +02:00
|
|
|
} else if (basicp && (basicp->isRandomGenerator() || basicp->isStdRandomGenerator())) {
|
2024-05-17 16:38:34 +02:00
|
|
|
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] == '_')
|
2025-05-27 15:31:55 +02:00
|
|
|
|| (varp->isXTemp()
|
|
|
|
|
? (v3Global.opt.xAssign() != "unique")
|
|
|
|
|
: (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);
|
2024-01-06 00:00:06 +01:00
|
|
|
UASSERT_OBJ(constp, varp, "non-const initializer for variable");
|
2021-06-24 18:35:12 +02:00
|
|
|
for (int w = 0; w < varp->widthWords(); ++w) {
|
|
|
|
|
out += varNameProtected + suffix + "[" + cvtToStr(w) + "] = ";
|
|
|
|
|
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-05-27 15:31:55 +02:00
|
|
|
out += zeroit ? (slow ? "VL_ZERO_RESET_W(" : "VL_ZERO_W(")
|
2025-06-09 23:59:01 +02:00
|
|
|
: (varp->isXTemp() ? "VL_SCOPED_RAND_RESET_ASSIGN_W("
|
|
|
|
|
: "VL_SCOPED_RAND_RESET_W(");
|
2021-06-24 18:35:12 +02:00
|
|
|
out += cvtToStr(dtypep->widthMin());
|
2025-05-27 15:31:55 +02:00
|
|
|
out += ", " + varNameProtected + suffix;
|
|
|
|
|
if (!zeroit) {
|
|
|
|
|
emitVarResetScopeHash();
|
|
|
|
|
const uint64_t salt = VString::hashMurmur(varp->prettyName());
|
|
|
|
|
out += ", ";
|
|
|
|
|
out += m_classOrPackage ? m_classOrPackageHash : "__VscopeHash";
|
|
|
|
|
out += ", ";
|
|
|
|
|
out += std::to_string(salt);
|
|
|
|
|
out += "ull";
|
|
|
|
|
}
|
|
|
|
|
out += ");\n";
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
} else {
|
|
|
|
|
string out = varNameProtected + suffix;
|
Deprecate clocker attribute and --clk option (#6463)
The only use for the clocker attribute and the AstVar::isUsedClock that
is actually necessary today for correctness is to mark top level inputs
of --lib-create blocks as being (or driving) a clock signal. Correctness
of --lib-create (and hence hierarchical blocks) actually used to depend
on having the right optimizations eliminate intermediate clocks (e.g.:
V3Gate), when the top level port was not used directly in a sensitivity
list, or marking top level signals manually via --clk or the clocker
attribute. However V3Sched::partition already needs to trace through the
logic to figure out what signals might drive a sensitivity list, so it
can very easily mark all top level inputs as such.
In this patch we remove the AstVar::attrClocker and AstVar::isUsedClock
attributes, and replace them with AstVar::isPrimaryClock, automatically
set by V3Sched::partition. This eliminates all need for manual
annotation so we are deprecating the --clk/--no-clk options and the
clocker/no_clocker attributes.
This also eliminates the opportunity for any further mis-optimization
similar to #6453.
Regarding the other uses of the removed AstVar attributes:
- As of 5.000, initial edges are triggered via a separate mechanism
applied in V3Sched, so the use in V3EmitCFunc.cpp is redundant
- Also as of 5.000, we can handle arbitrary sensitivity expressions, so
the restriction on eliminating clock signals in V3Gate is unnecessary
- Since the recent change when Dfg is applied after V3Scope, it does
perform the equivalent of GateClkDecomp, so we can delete that pass.
2025-09-20 16:50:22 +02:00
|
|
|
if (zeroit) {
|
2021-06-24 18:35:12 +02:00
|
|
|
out += " = 0;\n";
|
|
|
|
|
} else {
|
2025-05-27 15:31:55 +02:00
|
|
|
emitVarResetScopeHash();
|
|
|
|
|
const uint64_t salt = VString::hashMurmur(varp->prettyName());
|
|
|
|
|
out += " = VL_SCOPED_RAND_RESET_";
|
2025-06-09 23:59:01 +02:00
|
|
|
if (varp->isXTemp()) out += "ASSIGN_";
|
2021-06-24 18:35:12 +02:00
|
|
|
out += dtypep->charIQWN();
|
2025-05-27 15:31:55 +02:00
|
|
|
out += "(" + cvtToStr(dtypep->widthMin()) + ", "
|
|
|
|
|
+ (m_classOrPackage ? m_classOrPackageHash : "__VscopeHash") + ", "
|
|
|
|
|
+ std::to_string(salt) + "ull);\n";
|
2021-06-24 18:35:12 +02:00
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
2024-07-28 20:18:24 +02:00
|
|
|
} else { // LCOV_EXCL_BR_LINE
|
2021-06-24 18:35:12 +02:00
|
|
|
v3fatalSrc("Unknown node type in reset generator: " << varp->prettyTypeName());
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
2025-05-27 15:31:55 +02:00
|
|
|
|
|
|
|
|
void EmitCFunc::emitVarResetScopeHash() {
|
|
|
|
|
if (VL_LIKELY(m_createdScopeHash)) { return; }
|
|
|
|
|
if (m_classOrPackage) {
|
|
|
|
|
m_classOrPackageHash
|
|
|
|
|
= std::to_string(VString::hashMurmur(m_classOrPackage->name())) + "ULL";
|
|
|
|
|
} else {
|
|
|
|
|
puts(string("const uint64_t __VscopeHash = VL_MURMUR64_HASH(")
|
|
|
|
|
+ (m_useSelfForThis ? "vlSelf" : "this") + "->name());\n");
|
|
|
|
|
}
|
|
|
|
|
m_createdScopeHash = true;
|
|
|
|
|
}
|