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: Add temporaries, such as for task nodes
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can 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.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Task's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Each module:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Look for TASKREF
|
|
|
|
|
// Insert task's statements into the referrer
|
|
|
|
|
// Look for TASKs
|
|
|
|
|
// Remove them, they're inlined
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Task.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
|
|
|
|
#include "V3Const.h"
|
2025-06-28 02:38:01 +02:00
|
|
|
#include "V3Control.h"
|
2006-08-30 23:07:55 +02:00
|
|
|
#include "V3EmitCBase.h"
|
2006-10-11 17:41:42 +02:00
|
|
|
#include "V3Graph.h"
|
2024-11-26 01:59:10 +01:00
|
|
|
#include "V3Stats.h"
|
2006-10-11 17:41:42 +02:00
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
#include <tuple>
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-10-11 17:41:42 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Graph subclasses
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TaskBaseVertex VL_NOT_FINAL : public V3GraphVertex {
|
2020-08-15 19:11:27 +02:00
|
|
|
AstNode* m_impurep = nullptr; // Node causing impure function w/ outside references
|
|
|
|
|
bool m_noInline = false; // Marked with pragma
|
2006-10-11 17:41:42 +02:00
|
|
|
public:
|
2015-10-04 04:33:06 +02:00
|
|
|
explicit TaskBaseVertex(V3Graph* graphp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: V3GraphVertex{graphp} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TaskBaseVertex() override = default;
|
2020-08-15 16:12:55 +02:00
|
|
|
bool pure() const { return m_impurep == nullptr; }
|
2006-10-11 17:41:42 +02:00
|
|
|
AstNode* impureNode() const { return m_impurep; }
|
|
|
|
|
void impure(AstNode* nodep) { m_impurep = nodep; }
|
|
|
|
|
bool noInline() const { return m_noInline; }
|
|
|
|
|
void noInline(bool flag) { m_noInline = flag; }
|
|
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TaskFTaskVertex final : public TaskBaseVertex {
|
2006-10-11 17:41:42 +02:00
|
|
|
// Every task gets a vertex, and we link tasks together based on funcrefs.
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNodeFTask* const m_nodep;
|
2020-08-16 15:55:36 +02:00
|
|
|
AstCFunc* m_cFuncp = nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2006-10-11 17:41:42 +02:00
|
|
|
public:
|
|
|
|
|
TaskFTaskVertex(V3Graph* graphp, AstNodeFTask* nodep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: TaskBaseVertex{graphp}
|
|
|
|
|
, m_nodep{nodep} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TaskFTaskVertex() override = default;
|
2023-03-18 01:24:15 +01:00
|
|
|
AstNodeFTask* nodep() const VL_MT_STABLE { return m_nodep; }
|
|
|
|
|
string name() const override VL_MT_STABLE { return nodep()->name(); }
|
2022-09-16 12:22:11 +02:00
|
|
|
string dotColor() const override { return pure() ? "black" : "red"; }
|
2008-11-25 13:57:02 +01:00
|
|
|
AstCFunc* cFuncp() const { return m_cFuncp; }
|
2019-05-19 22:13:13 +02:00
|
|
|
void cFuncp(AstCFunc* nodep) { m_cFuncp = nodep; }
|
2006-10-11 17:41:42 +02:00
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TaskCodeVertex final : public TaskBaseVertex {
|
2006-10-11 17:41:42 +02:00
|
|
|
// Top vertex for all calls not under another task
|
|
|
|
|
public:
|
2015-10-04 04:33:06 +02:00
|
|
|
explicit TaskCodeVertex(V3Graph* graphp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: TaskBaseVertex{graphp} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TaskCodeVertex() override = default;
|
2023-03-18 01:24:15 +01:00
|
|
|
string name() const override VL_MT_STABLE { return "*CODE*"; }
|
2022-09-16 12:22:11 +02:00
|
|
|
string dotColor() const override { return "green"; }
|
2006-10-11 17:41:42 +02:00
|
|
|
};
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class TaskEdge final : public V3GraphEdge {
|
2023-09-01 00:00:53 +02:00
|
|
|
VL_RTTI_IMPL(TaskEdge, V3GraphEdge)
|
2006-10-11 17:41:42 +02:00
|
|
|
public:
|
|
|
|
|
TaskEdge(V3Graph* graphp, TaskBaseVertex* fromp, TaskBaseVertex* top)
|
2020-08-16 15:55:36 +02:00
|
|
|
: V3GraphEdge{graphp, fromp, top, 1, false} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TaskEdge() override = default;
|
|
|
|
|
string dotLabel() const override { return "w" + cvtToStr(weight()); }
|
2006-10-11 17:41:42 +02:00
|
|
|
};
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class TaskStateVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Output:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNodeFTask::user3p // AstScope* this FTask is under
|
|
|
|
|
// AstNodeFTask::user4p // GraphFTaskVertex* this FTask is under
|
|
|
|
|
// AstVar::user4p // GraphFTaskVertex* this variable is declared in
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser3InUse m_inuser3;
|
|
|
|
|
const VNUser4InUse m_inuser4;
|
2008-11-25 13:57:02 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// TYPES
|
2021-03-13 00:10:45 +01:00
|
|
|
using VarToScopeMap = std::map<std::pair<AstScope*, AstVar*>, AstVarScope*>;
|
|
|
|
|
using FuncToClassMap = std::unordered_map<const AstNodeFTask*, AstClass*>;
|
2006-08-26 13:35:28 +02:00
|
|
|
// MEMBERS
|
2020-04-15 13:58:34 +02:00
|
|
|
VarToScopeMap m_varToScopeMap; // Map for Var -> VarScope mappings
|
2020-08-24 01:37:56 +02:00
|
|
|
FuncToClassMap m_funcToClassMap; // Map for ctor func -> class
|
2020-08-15 19:11:27 +02:00
|
|
|
AstNodeFTask* m_ctorp = nullptr; // Class constructor
|
2020-08-24 01:37:56 +02:00
|
|
|
AstClass* m_classp = nullptr; // Current class
|
2020-04-15 13:58:34 +02:00
|
|
|
V3Graph m_callGraph; // Task call graph
|
|
|
|
|
TaskBaseVertex* m_curVxp; // Current vertex we're adding to
|
2022-01-02 18:35:44 +01:00
|
|
|
std::vector<AstInitialAutomatic*> m_initialps; // Initial blocks to move
|
2006-10-11 17:41:42 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// METHODS
|
|
|
|
|
AstScope* getScope(AstNodeFTask* nodep) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstScope* const scopep = VN_AS(nodep->user3p(), Scope);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(scopep, nodep, "No scope for function");
|
2019-05-19 22:13:13 +02:00
|
|
|
return scopep;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
AstVarScope* findVarScope(AstScope* scopep, AstVar* nodep) {
|
2021-03-13 00:17:49 +01:00
|
|
|
const auto iter = m_varToScopeMap.find(std::make_pair(scopep, nodep));
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(iter != m_varToScopeMap.end(), nodep, "No scope for var");
|
2019-05-19 22:13:13 +02:00
|
|
|
return iter->second;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-24 01:37:56 +02:00
|
|
|
AstClass* getClassp(AstNodeFTask* nodep) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstClass* const classp = m_funcToClassMap[nodep];
|
2020-08-24 01:37:56 +02:00
|
|
|
UASSERT_OBJ(classp, nodep, "No class for ctor func");
|
|
|
|
|
return classp;
|
|
|
|
|
}
|
|
|
|
|
void remapFuncClassp(AstNodeFTask* nodep, AstNodeFTask* newp) {
|
|
|
|
|
m_funcToClassMap[newp] = getClassp(nodep);
|
|
|
|
|
}
|
2024-11-26 01:59:10 +01:00
|
|
|
bool ftaskNoInline(AstNodeFTask* nodep) {
|
|
|
|
|
return !v3Global.opt.fInlineFuncs() || getFTaskVertex(nodep)->noInline();
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
AstCFunc* ftaskCFuncp(AstNodeFTask* nodep) { return getFTaskVertex(nodep)->cFuncp(); }
|
2008-11-25 13:57:02 +01:00
|
|
|
void ftaskCFuncp(AstNodeFTask* nodep, AstCFunc* cfuncp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
getFTaskVertex(nodep)->cFuncp(cfuncp);
|
2008-11-25 13:57:02 +01:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
void checkPurity(AstNodeFTask* nodep) { checkPurity(nodep, getFTaskVertex(nodep)); }
|
2022-01-05 02:19:58 +01:00
|
|
|
|
|
|
|
|
private:
|
2006-10-11 17:41:42 +02:00
|
|
|
void checkPurity(AstNodeFTask* nodep, TaskBaseVertex* vxp) {
|
2022-01-05 02:19:58 +01:00
|
|
|
if (nodep->recursive()) return; // Impure, but no warning
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!vxp->pure()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3warn(
|
|
|
|
|
IMPURE, "Unsupported: External variable referenced by non-inlined function/task: "
|
2020-11-19 03:03:23 +01:00
|
|
|
<< nodep->prettyNameQ() << '\n'
|
|
|
|
|
<< nodep->warnContextPrimary() << '\n'
|
2020-04-15 13:58:34 +02:00
|
|
|
<< vxp->impureNode()->warnOther()
|
|
|
|
|
<< "... Location of the external reference: "
|
2020-11-19 03:03:23 +01:00
|
|
|
<< vxp->impureNode()->prettyNameQ() << '\n'
|
2020-04-15 13:58:34 +02:00
|
|
|
<< vxp->impureNode()->warnContextSecondary());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// And, we need to check all tasks this task calls
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphEdge& edge : vxp->outEdges()) {
|
|
|
|
|
checkPurity(nodep, static_cast<TaskBaseVertex*>(edge.top()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-10-11 17:41:42 +02:00
|
|
|
}
|
2008-11-25 13:57:02 +01:00
|
|
|
TaskFTaskVertex* getFTaskVertex(AstNodeFTask* nodep) {
|
2022-11-20 23:40:38 +01:00
|
|
|
if (!nodep->user4p()) nodep->user4p(new TaskFTaskVertex{&m_callGraph, nodep});
|
2019-05-19 22:13:13 +02:00
|
|
|
return static_cast<TaskFTaskVertex*>(nodep->user4u().toGraphVertex());
|
2006-10-11 17:41:42 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Each FTask is unique per-scope, so AstNodeFTaskRefs do not need
|
|
|
|
|
// pointers to what scope the FTask is to be invoked under.
|
|
|
|
|
// However, to create variables, we need to track the scopes involved.
|
|
|
|
|
// Find all var->varscope mappings, for later cleanup
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = nodep->varsp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstVarScope* const vscp = VN_CAST(stmtp, VarScope)) {
|
2022-10-19 02:04:09 +02:00
|
|
|
if (vscp->varp()->isFuncLocal() || vscp->varp()->isUsedLoopIdx()) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " funcvsc " << vscp);
|
2023-10-28 12:24:04 +02:00
|
|
|
m_varToScopeMap.emplace(std::make_pair(nodep, vscp->varp()), vscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Likewise, all FTask->scope mappings
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = nodep->blocksp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstNodeFTask* const taskp = VN_CAST(stmtp, NodeFTask)) taskp->user3p(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-09-05 22:06:23 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// We make multiple edges if a task is called multiple times from another task.
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked task");
|
2024-07-24 00:47:04 +02:00
|
|
|
TaskFTaskVertex* const taskVtxp = getFTaskVertex(nodep->taskp());
|
|
|
|
|
new TaskEdge{&m_callGraph, m_curVxp, taskVtxp};
|
|
|
|
|
// Do we have to disable inlining the function?
|
|
|
|
|
const V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
|
|
|
|
|
if (!taskVtxp->noInline()) { // Else short-circuit below
|
|
|
|
|
for (const auto& itr : tconnects) {
|
|
|
|
|
const AstVar* const portp = itr.first;
|
|
|
|
|
const AstArg* const argp = itr.second;
|
|
|
|
|
if (const AstNodeExpr* const pinp = argp->exprp()) {
|
|
|
|
|
if ((portp->isRef() || portp->isConstRef()) && !VN_IS(pinp, VarRef)) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "No function inline due to ref " << pinp);
|
2024-07-24 00:47:04 +02:00
|
|
|
taskVtxp->noInline(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-10-11 17:41:42 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " TASK " << nodep);
|
2024-11-10 16:51:48 +01:00
|
|
|
VL_RESTORER(m_curVxp);
|
|
|
|
|
m_curVxp = getFTaskVertex(nodep);
|
|
|
|
|
if (nodep->dpiImport()) m_curVxp->noInline(true);
|
|
|
|
|
if (nodep->classMethod()) m_curVxp->noInline(true); // Until V3Task supports it
|
|
|
|
|
if (nodep->recursive()) m_curVxp->noInline(true);
|
|
|
|
|
if (nodep->isConstructor()) {
|
|
|
|
|
m_curVxp->noInline(true);
|
|
|
|
|
m_ctorp = nodep;
|
|
|
|
|
UASSERT_OBJ(m_classp, nodep, "Ctor not under class");
|
|
|
|
|
m_funcToClassMap[nodep] = m_classp;
|
2020-04-19 02:20:17 +02:00
|
|
|
}
|
2024-11-10 16:51:48 +01:00
|
|
|
iterateChildren(nodep);
|
2006-10-11 17:41:42 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPragma* nodep) override {
|
2022-01-02 19:56:40 +01:00
|
|
|
if (nodep->pragType() == VPragmaType::NO_INLINE_TASK) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Just mark for the next steps, and we're done with it.
|
|
|
|
|
m_curVxp->noInline(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-10-11 17:41:42 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user4p(m_curVxp); // Remember what task it's under
|
2006-10-11 17:41:42 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->varp()->user4u().toGraphVertex() != m_curVxp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_curVxp->pure() && !nodep->varp()->isXTemp()) m_curVxp->impure(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClass* nodep) override {
|
2020-04-19 02:20:17 +02:00
|
|
|
// Move initial statements into the constructor
|
2025-02-28 03:18:27 +01:00
|
|
|
VL_RESTORER(m_initialps);
|
|
|
|
|
VL_RESTORER(m_ctorp);
|
|
|
|
|
VL_RESTORER(m_classp);
|
2020-04-19 02:20:17 +02:00
|
|
|
m_initialps.clear();
|
2020-08-15 16:12:55 +02:00
|
|
|
m_ctorp = nullptr;
|
2020-08-24 01:37:56 +02:00
|
|
|
m_classp = nodep;
|
2020-04-19 02:20:17 +02:00
|
|
|
{ // Find m_initialps, m_ctor
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(m_ctorp, nodep, "class constructor missing"); // LinkDot always makes it
|
2022-01-02 18:35:44 +01:00
|
|
|
for (AstInitialAutomatic* initialp : m_initialps) {
|
2022-09-15 20:43:56 +02:00
|
|
|
if (AstNode* const newp = initialp->stmtsp()) {
|
2020-04-19 02:20:17 +02:00
|
|
|
newp->unlinkFrBackWithNext();
|
|
|
|
|
if (!m_ctorp->stmtsp()) {
|
|
|
|
|
m_ctorp->addStmtsp(newp);
|
|
|
|
|
} else {
|
|
|
|
|
m_ctorp->stmtsp()->addHereThisAsNext(newp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(initialp->unlinkFrBack()), initialp);
|
|
|
|
|
}
|
|
|
|
|
m_initialps.clear();
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstInitialAutomatic* nodep) override {
|
2020-04-19 02:20:17 +02:00
|
|
|
m_initialps.push_back(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-15 19:11:27 +02:00
|
|
|
explicit TaskStateVisitor(AstNetlist* nodep) {
|
2022-11-20 23:40:38 +01:00
|
|
|
m_curVxp = new TaskCodeVertex{&m_callGraph};
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
m_callGraph.removeRedundantEdgesSum(&TaskEdge::followAlwaysTrue);
|
2023-05-04 00:04:10 +02:00
|
|
|
if (dumpGraphLevel()) m_callGraph.dumpDotFilePrefixed("task_call");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~TaskStateVisitor() override = default;
|
2020-01-09 01:33:47 +01:00
|
|
|
VL_UNCOPYABLE(TaskStateVisitor);
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2020-12-06 04:09:48 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// DPI related utility functions
|
|
|
|
|
|
2024-01-20 21:06:46 +01:00
|
|
|
struct TaskDpiUtils final {
|
2025-10-19 10:44:33 +02:00
|
|
|
// Returns a vector of ('elements', 'stride') pairs for each unpacked dimension
|
|
|
|
|
static std::vector<std::pair<int, int>> unpackDimsAndStrides(AstVar* varp) {
|
|
|
|
|
AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
|
|
|
|
|
std::vector<std::pair<int, int>> dimStrides;
|
|
|
|
|
AstUnpackArrayDType* const unpackp = VN_CAST(dtypep->skipRefp(), UnpackArrayDType);
|
|
|
|
|
if (!unpackp) return dimStrides;
|
|
|
|
|
|
|
|
|
|
const std::vector<AstUnpackArrayDType*> dims = unpackp->unpackDimensions();
|
|
|
|
|
const size_t nDims = dims.size();
|
|
|
|
|
dimStrides.resize(nDims);
|
|
|
|
|
// Stride of fastest varying dimension is 1.
|
|
|
|
|
dimStrides[nDims - 1].first = dims.back()->elementsConst();
|
|
|
|
|
dimStrides[nDims - 1].second = 1;
|
|
|
|
|
// Rest are densly packed
|
|
|
|
|
for (ssize_t i = nDims - 2; i >= 0; --i) {
|
|
|
|
|
dimStrides[i].first = dims[i]->elementsConst();
|
|
|
|
|
dimStrides[i].second = dimStrides[i + 1].first * dimStrides[i + 1].second;
|
2020-12-06 04:09:48 +01:00
|
|
|
}
|
|
|
|
|
return dimStrides;
|
|
|
|
|
}
|
2025-10-19 10:44:33 +02:00
|
|
|
// Returns the prefix of a function-call like statement used to convert
|
|
|
|
|
// from IEEE DPI data types to internal types, and a bool that is true
|
|
|
|
|
// if type uses svBitVecVal/svLogicVecVal and ther result is returned via
|
|
|
|
|
// an output parameter, o false if uses C primitive type and result is the
|
|
|
|
|
// return value.
|
|
|
|
|
static std::pair<std::string, bool> dpiToInternalCvtStmt(AstVar* varp) {
|
|
|
|
|
AstBasicDType* const basicp = varp->basicp();
|
|
|
|
|
|
|
|
|
|
// DPI types using Primitive C types
|
|
|
|
|
if (basicp->keyword() == VBasicDTypeKwd::CHANDLE) return {"VL_CVT_VP_Q(", false};
|
|
|
|
|
if (basicp->keyword() == VBasicDTypeKwd::STRING) return {"VL_CVT_N_CSTR(", false};
|
|
|
|
|
if (basicp->isDpiPrimitive()) return {"(", false};
|
|
|
|
|
|
|
|
|
|
// DPI types using svBitVecVal/svLogicVecVal
|
|
|
|
|
UASSERT_OBJ(basicp->isDpiBitVec() || basicp->isDpiLogicVec(), varp,
|
|
|
|
|
"Should use svBitVecVal/svLogicVecVal");
|
|
|
|
|
|
|
|
|
|
const std::string vecType = basicp->isDpiBitVec() ? "SVBV" : "SVLV";
|
|
|
|
|
const AstNodeDType* const dtypep = varp->dtypep()->skipRefp();
|
|
|
|
|
const char sizeChar = dtypep->width() <= 8 ? 'C'
|
|
|
|
|
: dtypep->width() <= 16 ? 'S'
|
|
|
|
|
: *dtypep->charIQWN();
|
|
|
|
|
const std::string& size = std::to_string(dtypep->width());
|
|
|
|
|
return {"VL_SET_"s + sizeChar + "_" + vecType + "(" + size + ", ", true};
|
2020-12-06 04:09:48 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Task state, as a visitor of each AstNode
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class TaskVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Each module:
|
2020-08-24 01:37:56 +02:00
|
|
|
// AstNodeFTask::user1 // True if its been expanded
|
2006-08-26 13:35:28 +02:00
|
|
|
// Each funccall
|
2022-01-09 23:34:10 +01:00
|
|
|
// to 'relink' function:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstVar::user2p // AstVarScope* to replace varref with
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
|
|
|
|
const VNUser2InUse m_inuser2;
|
2006-09-05 22:06:23 +02:00
|
|
|
|
|
|
|
|
// TYPES
|
2021-06-10 23:41:33 +02:00
|
|
|
using DpiCFuncs = std::map<const string, std::tuple<AstNodeFTask*, std::string, AstCFunc*>>;
|
2006-09-05 22:06:23 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2021-11-26 23:55:36 +01:00
|
|
|
TaskStateVisitor* const m_statep; // Common state between visitors
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2021-10-17 11:29:17 +02:00
|
|
|
AstTopScope* const m_topScopep = v3Global.rootp()->topScopep(); // The AstTopScope
|
2020-08-16 15:55:36 +02:00
|
|
|
AstScope* m_scopep = nullptr; // Current scope
|
|
|
|
|
AstNode* m_insStmtp = nullptr; // Where to insert statement
|
2022-08-19 20:18:38 +02:00
|
|
|
bool m_inSensesp = false; // Are we under a senitem?
|
2025-10-13 13:29:23 +02:00
|
|
|
bool m_inNew = false; // Are we under a constructor?
|
2020-08-16 15:55:36 +02:00
|
|
|
int m_modNCalls = 0; // Incrementing func # for making symbols
|
2024-11-26 01:59:10 +01:00
|
|
|
|
|
|
|
|
// STATE - across all visitors
|
2021-06-10 23:41:33 +02:00
|
|
|
DpiCFuncs m_dpiNames; // Map of all created DPI functions
|
2024-11-26 01:59:10 +01:00
|
|
|
VDouble0 m_statInlines; // Statistic tracking
|
2025-01-20 20:24:09 +01:00
|
|
|
VDouble0 m_statHierDpisWithCosts; // Statistic tracking
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2009-12-20 14:27:00 +01:00
|
|
|
AstVarScope* createFuncVar(AstCFunc* funcp, const string& name, AstVar* examplep) {
|
2022-11-20 23:40:38 +01:00
|
|
|
AstVar* const newvarp = new AstVar{funcp->fileline(), VVarType::BLOCKTEMP, name, examplep};
|
2019-05-19 22:13:13 +02:00
|
|
|
newvarp->funcLocal(true);
|
2025-10-21 17:37:32 +02:00
|
|
|
funcp->addVarsp(newvarp);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstVarScope* const newvscp = new AstVarScope{funcp->fileline(), m_scopep, newvarp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_scopep->addVarsp(newvscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
return newvscp;
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
2022-01-02 19:56:40 +01:00
|
|
|
AstVarScope* createInputVar(AstCFunc* funcp, const string& name, VBasicDTypeKwd kwd) {
|
|
|
|
|
AstVar* const newvarp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstVar{funcp->fileline(), VVarType::BLOCKTEMP, name, funcp->findBasicDType(kwd)};
|
2018-10-27 23:29:00 +02:00
|
|
|
newvarp->funcLocal(true);
|
|
|
|
|
newvarp->direction(VDirection::INPUT);
|
|
|
|
|
funcp->addArgsp(newvarp);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstVarScope* const newvscp = new AstVarScope{funcp->fileline(), m_scopep, newvarp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_scopep->addVarsp(newvscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
return newvscp;
|
2009-12-05 16:38:49 +01:00
|
|
|
}
|
2006-08-27 16:51:25 +02:00
|
|
|
AstVarScope* createVarScope(AstVar* invarp, const string& name) {
|
2021-07-22 22:09:24 +02:00
|
|
|
if (invarp->isParam() && VN_IS(invarp->valuep(), InitArray)) {
|
|
|
|
|
// Move array params in functions into constant pool
|
2021-10-22 14:56:48 +02:00
|
|
|
return v3Global.rootp()->constPoolp()->findTable(VN_AS(invarp->valuep(), InitArray));
|
2021-07-22 22:09:24 +02:00
|
|
|
} else {
|
|
|
|
|
// We could create under either the ref's scope or the ftask's scope.
|
|
|
|
|
// It shouldn't matter, as they are only local variables.
|
|
|
|
|
// We choose to do it under whichever called this function, which results
|
|
|
|
|
// in more cache locality.
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const newvarp
|
2022-01-02 19:56:40 +01:00
|
|
|
= new AstVar{invarp->fileline(), VVarType::BLOCKTEMP, name, invarp};
|
2021-07-22 22:09:24 +02:00
|
|
|
newvarp->funcLocal(false);
|
|
|
|
|
newvarp->propagateAttrFrom(invarp);
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(newvarp);
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVarScope* const newvscp = new AstVarScope{newvarp->fileline(), m_scopep, newvarp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_scopep->addVarsp(newvscp);
|
2021-07-22 22:09:24 +02:00
|
|
|
return newvscp;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-09 23:34:10 +01:00
|
|
|
// Replace varrefs with new var pointer
|
|
|
|
|
void relink(AstNode* nodep) {
|
2022-10-20 14:48:44 +02:00
|
|
|
nodep->foreachAndNext([](AstVarRef* refp) {
|
2022-01-09 23:34:10 +01:00
|
|
|
if (refp->varp()->user2p()) { // It's being converted to an alias.
|
|
|
|
|
AstVarScope* const newvscp = VN_AS(refp->varp()->user2p(), VarScope);
|
|
|
|
|
refp->varScopep(newvscp);
|
|
|
|
|
refp->varp(refp->varScopep()->varp());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-18 00:23:44 +02:00
|
|
|
AstAssign* connectPortMakeInAssign(AstNodeExpr* pinp, AstVarScope* newvscp, bool pureCheck) {
|
|
|
|
|
// Create input assignment to go in FRONT of function call
|
|
|
|
|
AstNodeExpr* inPinp = pinp;
|
|
|
|
|
if (AstResizeLValue* sinPinp = VN_CAST(inPinp, ResizeLValue)) inPinp = sinPinp->lhsp();
|
|
|
|
|
AstNodeExpr* const inPinClonep
|
|
|
|
|
= pureCheck ? inPinp->cloneTreePure(true) : inPinp->cloneTree(true);
|
|
|
|
|
AstAssign* const assp = new AstAssign{
|
|
|
|
|
pinp->fileline(), new AstVarRef{newvscp->fileline(), newvscp, VAccess::WRITE},
|
|
|
|
|
inPinClonep};
|
|
|
|
|
assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block
|
|
|
|
|
return assp;
|
|
|
|
|
}
|
|
|
|
|
AstAssign* connectPortMakeOutAssign(AstVar* portp, AstNodeExpr* pinp, AstVarScope* newvscp,
|
|
|
|
|
bool pureCheck) {
|
|
|
|
|
// If needed, remap size of function to caller's output size
|
|
|
|
|
AstNodeExpr* outPinp = pinp;
|
|
|
|
|
AstNodeExpr* postRhsp = new AstVarRef{newvscp->fileline(), newvscp, VAccess::READ};
|
|
|
|
|
if (AstResizeLValue* soutPinp = VN_CAST(outPinp, ResizeLValue)) {
|
|
|
|
|
outPinp = soutPinp->lhsp();
|
2025-06-28 18:29:41 +02:00
|
|
|
if (AstNodeUniop* aoutPinp = VN_CAST(outPinp, Extend)) {
|
|
|
|
|
outPinp = aoutPinp->lhsp();
|
|
|
|
|
} else if (AstNodeUniop* aoutPinp = VN_CAST(outPinp, ExtendS)) {
|
|
|
|
|
outPinp = aoutPinp->lhsp();
|
|
|
|
|
} else if (AstSel* aoutPinp = VN_CAST(outPinp, Sel)) {
|
|
|
|
|
outPinp = aoutPinp->fromp();
|
2023-09-18 00:23:44 +02:00
|
|
|
} else {
|
|
|
|
|
outPinp->v3fatalSrc("Inout pin resizing should have had extend or select");
|
|
|
|
|
}
|
|
|
|
|
if (outPinp->width() < portp->width()) {
|
|
|
|
|
postRhsp = new AstSel{pinp->fileline(), postRhsp, 0, pinp->width()};
|
|
|
|
|
} else { // pin width > port width
|
|
|
|
|
if (pinp->isSigned() && postRhsp->isSigned()) {
|
|
|
|
|
postRhsp = new AstExtendS{pinp->fileline(), postRhsp};
|
|
|
|
|
} else {
|
|
|
|
|
postRhsp = new AstExtend{pinp->fileline(), postRhsp};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
postRhsp->dtypeFrom(outPinp);
|
|
|
|
|
}
|
|
|
|
|
// Put output assignment AFTER function call
|
|
|
|
|
AstNodeExpr* const outPinClonep
|
|
|
|
|
= pureCheck ? outPinp->cloneTreePure(true) : outPinp->cloneTree(true);
|
|
|
|
|
AstAssign* const assp = new AstAssign{pinp->fileline(), outPinClonep, postRhsp};
|
|
|
|
|
assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block
|
|
|
|
|
return assp;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-14 13:24:49 +02:00
|
|
|
void connectPort(AstVar* portp, AstArg* argp, const string& namePrefix, AstNode* beginp,
|
|
|
|
|
bool inlineTask) {
|
|
|
|
|
AstNodeExpr* const pinp = argp->exprp();
|
|
|
|
|
if (inlineTask) {
|
|
|
|
|
portp->unlinkFrBack();
|
|
|
|
|
pushDeletep(portp); // Remove it from the clone (not original)
|
|
|
|
|
}
|
|
|
|
|
if (!pinp) {
|
|
|
|
|
// Too few arguments in function call
|
|
|
|
|
} else {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " Port " << portp);
|
|
|
|
|
UINFO(9, " pin " << pinp);
|
2023-09-14 13:24:49 +02:00
|
|
|
if (inlineTask) {
|
2024-03-23 23:12:43 +01:00
|
|
|
pushDeletep(pinp->unlinkFrBack()); // Cloned in assignment below
|
2023-09-14 13:24:49 +02:00
|
|
|
VL_DO_DANGLING(argp->unlinkFrBack()->deleteTree(), argp); // Args no longer needed
|
|
|
|
|
}
|
|
|
|
|
if (portp->isWritable() && VN_IS(pinp, Const)) {
|
|
|
|
|
pinp->v3error("Function/task " + portp->direction().prettyName() // e.g. "output"
|
|
|
|
|
+ " connected to constant instead of variable: "
|
|
|
|
|
+ portp->prettyNameQ());
|
2023-09-20 13:33:11 +02:00
|
|
|
} else if (portp->isRef() || portp->isConstRef()) {
|
|
|
|
|
bool refArgOk = false;
|
|
|
|
|
if (VN_IS(pinp, VarRef) || VN_IS(pinp, MemberSel) || VN_IS(pinp, StructSel)
|
|
|
|
|
|| VN_IS(pinp, ArraySel)) {
|
|
|
|
|
refArgOk = true;
|
2024-09-02 15:45:47 +02:00
|
|
|
} else if (AstCMethodHard* const cMethodp = VN_CAST(pinp, CMethodHard)) {
|
|
|
|
|
if (VN_IS(cMethodp->fromp()->dtypep()->skipRefp(), QueueDType)) {
|
2025-09-27 14:22:17 +02:00
|
|
|
refArgOk = cMethodp->method() == VCMethod::DYN_AT_WRITE_APPEND
|
|
|
|
|
|| cMethodp->method() == VCMethod::DYN_AT_WRITE_APPEND_BACK;
|
2024-10-24 15:40:54 +02:00
|
|
|
} else {
|
2025-09-27 14:22:17 +02:00
|
|
|
refArgOk = cMethodp->method() == VCMethod::ARRAY_AT
|
|
|
|
|
|| cMethodp->method() == VCMethod::ARRAY_AT_BACK;
|
2024-09-02 15:45:47 +02:00
|
|
|
}
|
2023-09-20 13:33:11 +02:00
|
|
|
}
|
|
|
|
|
if (refArgOk) {
|
|
|
|
|
if (AstVarRef* const varrefp = VN_CAST(pinp, VarRef)) {
|
|
|
|
|
varrefp->access(VAccess::READWRITE);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
pinp->v3error("Function/task ref argument is not of allowed type");
|
|
|
|
|
}
|
|
|
|
|
if (inlineTask) {
|
|
|
|
|
if (AstVarRef* const varrefp = VN_CAST(pinp, VarRef)) {
|
|
|
|
|
// Connect to this exact variable
|
|
|
|
|
AstVarScope* const localVscp = varrefp->varScopep();
|
|
|
|
|
UASSERT_OBJ(localVscp, varrefp, "Null var scope");
|
|
|
|
|
portp->user2p(localVscp);
|
|
|
|
|
} else {
|
2024-07-24 00:47:04 +02:00
|
|
|
pinp->v3fatalSrc("ref argument should have caused non-inline of function");
|
2023-09-20 13:33:11 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-11-26 00:25:36 +01:00
|
|
|
} else if (portp->isInout()) {
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(9, pinp, "", "pinrsize-");
|
2023-09-14 13:24:49 +02:00
|
|
|
|
2023-09-18 00:23:44 +02:00
|
|
|
AstVarScope* const newvscp
|
|
|
|
|
= createVarScope(portp, namePrefix + "__" + portp->shortName());
|
|
|
|
|
portp->user2p(newvscp);
|
2024-03-23 23:12:43 +01:00
|
|
|
if (!inlineTask) {
|
2023-09-18 00:23:44 +02:00
|
|
|
pinp->replaceWith(
|
|
|
|
|
new AstVarRef{newvscp->fileline(), newvscp, VAccess::READWRITE});
|
2024-03-23 23:12:43 +01:00
|
|
|
pushDeletep(pinp); // Cloned by connectPortMakeInAssign
|
|
|
|
|
}
|
2023-09-18 00:23:44 +02:00
|
|
|
// Put input assignment in FRONT of all other statements
|
|
|
|
|
AstAssign* const preassp = connectPortMakeInAssign(pinp, newvscp, true);
|
|
|
|
|
if (AstNode* const afterp = beginp->nextp()) {
|
|
|
|
|
afterp->unlinkFrBackWithNext();
|
|
|
|
|
AstNode::addNext<AstNode, AstNode>(preassp, afterp);
|
2023-09-14 13:24:49 +02:00
|
|
|
}
|
2023-09-18 00:23:44 +02:00
|
|
|
beginp->addNext(preassp);
|
|
|
|
|
|
|
|
|
|
AstAssign* const postassp = connectPortMakeOutAssign(portp, pinp, newvscp, true);
|
|
|
|
|
beginp->addNext(postassp);
|
|
|
|
|
// if (debug() >= 9) beginp->dumpTreeAndNext(cout, "-pinrsize-out- ");
|
2023-09-14 13:24:49 +02:00
|
|
|
} else if (portp->isWritable()) {
|
|
|
|
|
// Even if it's referencing a varref, we still make a temporary
|
|
|
|
|
// Else task(x,x,x) might produce incorrect results
|
|
|
|
|
AstVarScope* const newvscp
|
|
|
|
|
= createVarScope(portp, namePrefix + "__" + portp->shortName());
|
|
|
|
|
portp->user2p(newvscp);
|
2024-03-23 23:12:43 +01:00
|
|
|
if (!inlineTask) {
|
2023-09-14 13:24:49 +02:00
|
|
|
pinp->replaceWith(new AstVarRef{newvscp->fileline(), newvscp, VAccess::WRITE});
|
2024-03-23 23:12:43 +01:00
|
|
|
pushDeletep(pinp); // Cloned by connectPortMakeOutAssign
|
|
|
|
|
}
|
2023-09-18 00:23:44 +02:00
|
|
|
AstAssign* const postassp = connectPortMakeOutAssign(portp, pinp, newvscp, false);
|
2023-09-14 13:24:49 +02:00
|
|
|
// Put assignment BEHIND of all other statements
|
2023-09-18 00:23:44 +02:00
|
|
|
beginp->addNext(postassp);
|
2023-09-14 13:24:49 +02:00
|
|
|
} else if (inlineTask && portp->isNonOutput()) {
|
|
|
|
|
// Make input variable
|
2023-09-18 00:23:44 +02:00
|
|
|
AstVarScope* const newvscp
|
2023-09-14 13:24:49 +02:00
|
|
|
= createVarScope(portp, namePrefix + "__" + portp->shortName());
|
2023-09-18 00:23:44 +02:00
|
|
|
portp->user2p(newvscp);
|
|
|
|
|
AstAssign* const preassp = connectPortMakeInAssign(pinp, newvscp, false);
|
2023-09-14 13:24:49 +02:00
|
|
|
// Put assignment in FRONT of all other statements
|
|
|
|
|
if (AstNode* const afterp = beginp->nextp()) {
|
|
|
|
|
afterp->unlinkFrBackWithNext();
|
2023-09-18 00:23:44 +02:00
|
|
|
AstNode::addNext<AstNode, AstNode>(preassp, afterp);
|
2023-09-14 13:24:49 +02:00
|
|
|
}
|
2023-09-18 00:23:44 +02:00
|
|
|
beginp->addNext(preassp);
|
2023-09-14 13:24:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
AstNode* createInlinedFTask(AstNodeFTaskRef* refp, const string& namePrefix,
|
|
|
|
|
AstVarScope* outvscp) {
|
2020-08-15 16:12:55 +02:00
|
|
|
// outvscp is the variable for functions only, if nullptr, it's a task
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(refp->taskp(), refp, "Unlinked?");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const newbodysp
|
2023-08-30 02:29:11 +02:00
|
|
|
= refp->taskp()->stmtsp() ? refp->taskp()->stmtsp()->cloneTree(true) : nullptr;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const beginp
|
2024-07-14 17:39:45 +02:00
|
|
|
= new AstComment{refp->fileline(), "Function: "s + refp->name(), true};
|
2019-05-19 22:13:13 +02:00
|
|
|
if (newbodysp) beginp->addNext(newbodysp);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) beginp->dumpTreeAndNext(cout, "- newbegi: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
// Create input variables
|
|
|
|
|
AstNode::user2ClearTree();
|
2023-09-14 13:24:49 +02:00
|
|
|
{
|
|
|
|
|
const V3TaskConnects tconnects = V3Task::taskConnects(refp, beginp);
|
|
|
|
|
for (const auto& itr : tconnects) {
|
|
|
|
|
AstVar* const portp = itr.first;
|
|
|
|
|
AstArg* const argp = itr.second;
|
|
|
|
|
connectPort(portp, argp, namePrefix, beginp, true);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(!refp->pinsp(), refp, "Pin wasn't removed by above loop");
|
2019-05-19 22:13:13 +02:00
|
|
|
{
|
|
|
|
|
AstNode* nextstmtp;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = beginp; stmtp; stmtp = nextstmtp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextstmtp = stmtp->nextp();
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Any I/O variables that fell out of above loop were already linked
|
|
|
|
|
if (!portp->user2p()) {
|
|
|
|
|
// Move it to a new localized variable
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVarScope* const localVscp
|
2020-04-15 13:58:34 +02:00
|
|
|
= createVarScope(portp, namePrefix + "__" + portp->shortName());
|
2019-05-19 22:13:13 +02:00
|
|
|
portp->user2p(localVscp);
|
2025-02-26 04:48:53 +01:00
|
|
|
if (portp->needsCReset() && portp->lifetime().isAutomatic()
|
|
|
|
|
&& !portp->valuep()) {
|
|
|
|
|
// Reset automatic var to its default, on each invocation of function
|
|
|
|
|
AstVarRef* const vrefp
|
|
|
|
|
= new AstVarRef{portp->fileline(), portp, VAccess::WRITE};
|
|
|
|
|
portp->replaceWith(new AstCReset{portp->fileline(), vrefp, false});
|
|
|
|
|
} else {
|
|
|
|
|
portp->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
pushDeletep(portp); // Remove it from the clone (not original)
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Create function output variables
|
|
|
|
|
if (outvscp) {
|
2025-05-23 02:29:32 +02:00
|
|
|
// UINFO(0, "setflag on " << funcp->fvarp() << " to " << outvscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
refp->taskp()->fvarp()->user2p(outvscp);
|
|
|
|
|
}
|
|
|
|
|
// Replace variable refs
|
2022-01-09 23:34:10 +01:00
|
|
|
relink(beginp);
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) beginp->dumpTreeAndNext(cout, "- iotask: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
return beginp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode* createNonInlinedFTask(AstNodeFTaskRef* refp, const string& namePrefix,
|
2020-04-13 00:57:12 +02:00
|
|
|
AstVarScope* outvscp, AstCNew*& cnewpr) {
|
2020-08-15 16:12:55 +02:00
|
|
|
// outvscp is the variable for functions only, if nullptr, it's a task
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(refp->taskp(), refp, "Unlinked?");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstCFunc* const cfuncp = m_statep->ftaskCFuncp(refp->taskp());
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(cfuncp, refp, "No non-inline task associated with this task call?");
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const beginp
|
2024-07-14 17:39:45 +02:00
|
|
|
= new AstComment{refp->fileline(), "Function: "s + refp->name(), true};
|
2020-03-07 18:52:11 +01:00
|
|
|
AstNodeCCall* ccallp;
|
2020-04-21 03:43:05 +02:00
|
|
|
if (VN_IS(refp, New)) {
|
2022-11-20 23:40:38 +01:00
|
|
|
AstCNew* const cnewp = new AstCNew{refp->fileline(), cfuncp};
|
2020-04-13 00:57:12 +02:00
|
|
|
cnewp->dtypep(refp->dtypep());
|
|
|
|
|
ccallp = cnewp;
|
|
|
|
|
// Parent AstNew will replace with this CNew
|
|
|
|
|
cnewpr = cnewp;
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const AstMethodCall* const mrefp = VN_CAST(refp, MethodCall)) {
|
2022-11-20 23:40:38 +01:00
|
|
|
ccallp = new AstCMethodCall{refp->fileline(), mrefp->fromp()->unlinkFrBack(), cfuncp};
|
2022-10-12 11:19:21 +02:00
|
|
|
ccallp->dtypeSetVoid();
|
|
|
|
|
beginp->addNext(ccallp->makeStmt());
|
2020-03-07 18:52:11 +01:00
|
|
|
} else {
|
2022-11-20 23:40:38 +01:00
|
|
|
ccallp = new AstCCall{refp->fileline(), cfuncp};
|
2022-10-12 11:19:21 +02:00
|
|
|
ccallp->dtypeSetVoid();
|
|
|
|
|
beginp->addNext(ccallp->makeStmt());
|
2020-03-07 18:52:11 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2023-10-24 15:51:46 +02:00
|
|
|
if (const AstFuncRef* const funcRefp = VN_CAST(refp, FuncRef)) {
|
|
|
|
|
ccallp->superReference(funcRefp->superReference());
|
|
|
|
|
} else if (const AstTaskRef* const taskRefp = VN_CAST(refp, TaskRef)) {
|
|
|
|
|
ccallp->superReference(taskRefp->superReference());
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-18 16:29:49 +01:00
|
|
|
// Convert complicated outputs to temp signals
|
2023-09-14 13:24:49 +02:00
|
|
|
{
|
|
|
|
|
const V3TaskConnects tconnects = V3Task::taskConnects(refp, refp->taskp()->stmtsp());
|
|
|
|
|
for (const auto& itr : tconnects) {
|
|
|
|
|
AstVar* const portp = itr.first;
|
|
|
|
|
AstArg* const argp = itr.second;
|
|
|
|
|
connectPort(portp, argp, namePrefix, beginp, false);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// First argument is symbol table, then output if a function
|
2025-09-21 17:37:44 +02:00
|
|
|
const bool needSyms = !refp->taskp()->dpiImport() || v3Global.opt.profExec();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (needSyms) ccallp->argTypes("vlSymsp");
|
|
|
|
|
|
|
|
|
|
if (refp->taskp()->dpiContext()) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstScopeName* const snp = refp->scopeNamep()->unlinkFrBack();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(snp, refp, "Missing scoping context");
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
FileLine* const flp = refp->fileline();
|
|
|
|
|
// __Vscopep
|
2019-05-19 22:13:13 +02:00
|
|
|
ccallp->addArgsp(snp);
|
|
|
|
|
// __Vfilenamep
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
ccallp->addArgsp(new AstCExpr{flp, "\"" + flp->filenameEsc() + "\"", 64});
|
2019-05-19 22:13:13 +02:00
|
|
|
// __Vlineno
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
ccallp->addArgsp(new AstConst(flp, flp->lineno()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create connections
|
|
|
|
|
AstNode* nextpinp;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* pinp = refp->pinsp(); pinp; pinp = nextpinp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextpinp = pinp->nextp();
|
|
|
|
|
// Move pin to the CCall, removing all Arg's
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const exprp = VN_AS(pinp, Arg)->exprp();
|
2019-05-19 22:13:13 +02:00
|
|
|
exprp->unlinkFrBack();
|
|
|
|
|
ccallp->addArgsp(exprp);
|
|
|
|
|
}
|
2009-12-20 14:27:00 +01:00
|
|
|
|
2022-11-20 23:40:38 +01:00
|
|
|
if (outvscp) ccallp->addArgsp(new AstVarRef{refp->fileline(), outvscp, VAccess::WRITE});
|
2009-12-20 14:27:00 +01:00
|
|
|
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) beginp->dumpTreeAndNext(cout, "- nitask: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
return beginp;
|
2006-10-11 17:41:42 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
string dpiSignature(AstNodeFTask* nodep, AstVar* rtnvarp) const {
|
|
|
|
|
// Return fancy signature for DPI function. Variable names are not included so differences
|
|
|
|
|
// in only argument names will not matter (as required by the standard).
|
2019-05-19 22:13:13 +02:00
|
|
|
string dpiproto;
|
2023-09-08 08:51:19 +02:00
|
|
|
if (nodep->dpiPure()) dpiproto += "pure ";
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->dpiContext()) dpiproto += "context ";
|
|
|
|
|
dpiproto += rtnvarp ? rtnvarp->dpiArgType(true, true) : "void";
|
2020-04-15 13:58:34 +02:00
|
|
|
dpiproto += " " + nodep->cname() + " (";
|
2019-05-19 22:13:13 +02:00
|
|
|
string args;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (portp->isIO() && !portp->isFuncReturn() && portp != rtnvarp) {
|
|
|
|
|
if (args != "") {
|
|
|
|
|
args += ", ";
|
|
|
|
|
dpiproto += ", ";
|
|
|
|
|
}
|
2023-06-20 12:26:46 +02:00
|
|
|
// Include both the Verilator and C type names, as if either
|
|
|
|
|
// differ we may get C compilation problems later
|
|
|
|
|
const std::string dpiType = portp->dpiArgType(false, false);
|
|
|
|
|
dpiproto += dpiType;
|
2024-06-01 03:51:12 +02:00
|
|
|
const std::string vType = portp->dtypep()->prettyDTypeName(false);
|
2023-06-20 12:26:46 +02:00
|
|
|
if (!portp->isDpiOpenArray() && dpiType != vType) {
|
|
|
|
|
dpiproto += " /* " + vType + " */ ";
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
args += portp->name(); // Leftover so ,'s look nice
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dpiproto += ")";
|
|
|
|
|
return dpiproto;
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2024-09-23 04:06:39 +02:00
|
|
|
static void checkLegalCIdentifier(AstNode* nodep, const string& name) {
|
|
|
|
|
if (name.end() != std::find_if(name.begin(), name.end(), [](char c) {
|
|
|
|
|
return !std::isalnum(c) && c != '_';
|
|
|
|
|
})) {
|
|
|
|
|
nodep->v3error("DPI function has illegal characters in C identifier name: " << name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-25 19:38:27 +02:00
|
|
|
static AstNode* createDpiTemp(AstVar* portp, const string& suffix) {
|
2025-09-26 14:25:47 +02:00
|
|
|
const string stmt = portp->dpiTmpVarType(portp->name() + suffix) + ";";
|
2022-11-20 23:40:38 +01:00
|
|
|
return new AstCStmt{portp->fileline(), stmt};
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2022-01-05 02:19:58 +01:00
|
|
|
void unlinkAndClone(AstNodeFTask* funcp, AstNode* nodep, bool withNext) {
|
|
|
|
|
UASSERT_OBJ(nodep, funcp, "null in function object clone");
|
|
|
|
|
if (funcp->recursive()) {
|
2023-03-18 22:11:39 +01:00
|
|
|
VNRelinker relinkHandle;
|
|
|
|
|
if (withNext) {
|
|
|
|
|
nodep->unlinkFrBackWithNext(&relinkHandle);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack(&relinkHandle);
|
|
|
|
|
}
|
2022-01-05 02:19:58 +01:00
|
|
|
// Recursive functions require the original argument list to
|
|
|
|
|
// still be live for linking purposes.
|
|
|
|
|
// The old function gets clone, so that node pointers are mostly
|
|
|
|
|
// retained through the V3Task transformations
|
|
|
|
|
AstNode* const newp = nodep->cloneTree(withNext);
|
|
|
|
|
relinkHandle.relink(newp);
|
2023-03-18 22:11:39 +01:00
|
|
|
} else {
|
|
|
|
|
if (withNext) {
|
|
|
|
|
nodep->unlinkFrBackWithNext();
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
2022-01-05 02:19:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-25 19:38:27 +02:00
|
|
|
static AstNode* createAssignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix,
|
|
|
|
|
const string& toSuffix) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const string stmt = V3Task::assignInternalToDpi(portp, isPtr, frSuffix, toSuffix);
|
2022-11-20 23:40:38 +01:00
|
|
|
return new AstCStmt{portp->fileline(), stmt};
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-19 10:44:33 +02:00
|
|
|
AstNodeStmt* createAssignDpiToInternal(AstVarScope* vscp, const std::string& rhsName) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Create assignment from DPI temporary into internal format
|
2020-11-19 14:02:58 +01:00
|
|
|
// DPI temporary is scalar or 1D array (if unpacked array)
|
|
|
|
|
// Internal representation is scalar, 1D, or multi-dimensional array (similar to SV)
|
2025-10-19 10:44:33 +02:00
|
|
|
AstVar* const varp = vscp->varp();
|
|
|
|
|
FileLine* const flp = vscp->fileline();
|
|
|
|
|
std::string cvt;
|
|
|
|
|
bool useSvVec;
|
|
|
|
|
std::tie(cvt, useSvVec) = TaskDpiUtils::dpiToInternalCvtStmt(varp);
|
|
|
|
|
const std::vector<std::pair<int, int>> strides = TaskDpiUtils::unpackDimsAndStrides(varp);
|
|
|
|
|
// Total number of elements in unpacked array
|
|
|
|
|
const int total = strides.empty() ? 1 : strides[0].first * strides[0].second;
|
|
|
|
|
// Number of words per element/primitive type
|
|
|
|
|
const int widthWords = varp->basicp()->widthWords();
|
|
|
|
|
// Number of bits in the C expression result, for masking.
|
|
|
|
|
const int cwidth = widthWords * VL_EDATASIZE;
|
|
|
|
|
// The resulting list of statements
|
|
|
|
|
AstNodeStmt* stmtsp = nullptr;
|
2020-11-19 14:02:58 +01:00
|
|
|
for (int i = 0; i < total; ++i) {
|
2025-10-19 10:44:33 +02:00
|
|
|
AstNodeExpr* lhsp = new AstVarRef{flp, vscp, VAccess::WRITE};
|
|
|
|
|
|
|
|
|
|
// Extract a scalar from multi-dimensional array (internal format)
|
|
|
|
|
for (const auto& stride : strides) {
|
|
|
|
|
lhsp = new AstArraySel(flp, lhsp, (i / stride.second) % stride.first);
|
2020-11-19 14:02:58 +01:00
|
|
|
}
|
2025-10-19 10:44:33 +02:00
|
|
|
|
|
|
|
|
// Extract a scalar from DPI temporary var that is scalar or 1D array
|
|
|
|
|
if (useSvVec) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
AstCStmt* const cstmtp = new AstCStmt{flp};
|
|
|
|
|
cstmtp->add(cvt);
|
|
|
|
|
cstmtp->add(lhsp);
|
|
|
|
|
cstmtp->add(", " + rhsName + " + " + std::to_string(i * widthWords) + ");");
|
2025-10-19 10:44:33 +02:00
|
|
|
stmtsp = AstNode::addNext(stmtsp, cstmtp);
|
2020-11-19 14:02:58 +01:00
|
|
|
} else {
|
2025-10-19 10:44:33 +02:00
|
|
|
const std::string elem = strides.empty() ? "" : "[" + std::to_string(i) + "]";
|
|
|
|
|
AstNodeExpr* rhsp = new AstCExpr{flp, cvt + rhsName + elem + ")", cwidth};
|
|
|
|
|
rhsp = new AstSel{flp, rhsp, 0, varp->width()};
|
|
|
|
|
stmtsp = AstNode::addNext(stmtsp, new AstAssign{flp, lhsp, rhsp});
|
2020-11-19 14:02:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-19 10:44:33 +02:00
|
|
|
return stmtsp;
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
// Create dispatch wrapper
|
|
|
|
|
AstCFunc* makeDpiExportDispatcher(AstNodeFTask* const nodep, AstVar* const rtnvarp) {
|
2024-09-23 04:06:39 +02:00
|
|
|
// Verilog name has __ conversion and other tricks, to match DPI C code, back that out
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
const std::string cname = AstNode::prettyName(nodep->cname());
|
|
|
|
|
checkLegalCIdentifier(nodep, cname);
|
2020-11-03 22:08:03 +01:00
|
|
|
const char* const tmpSuffixp = V3Task::dpiTemporaryVarSuffix();
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
const std::string rtnType = rtnvarp ? rtnvarp->dpiArgType(true, true) : "";
|
|
|
|
|
// The function we are building
|
|
|
|
|
AstCFunc* const funcp = new AstCFunc{flp, cname, m_scopep, rtnType};
|
2021-06-10 23:41:33 +02:00
|
|
|
funcp->dpiExportDispatcher(true);
|
2021-08-12 22:43:32 +02:00
|
|
|
funcp->dpiContext(nodep->dpiContext());
|
2021-06-10 23:41:33 +02:00
|
|
|
funcp->dontCombine(true);
|
|
|
|
|
funcp->entryPoint(true);
|
|
|
|
|
funcp->isStatic(true);
|
|
|
|
|
funcp->protect(false);
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
funcp->cname(cname);
|
2021-06-10 23:41:33 +02:00
|
|
|
// Add DPI Export to top, since it's a global function
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topScopep->scopep()->addBlocksp(funcp);
|
2019-05-19 22:13:13 +02:00
|
|
|
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
// Note this function may dispatch on a different class.
|
|
|
|
|
// Thus we need to be careful not to assume a particular function layout.
|
|
|
|
|
//
|
|
|
|
|
// Func numbers must be the same for each function, even when there are
|
|
|
|
|
// completely different models with the same function name.
|
|
|
|
|
// Thus we can't just use a constant computed at Verilation time.
|
|
|
|
|
// We could use 64-bits of a MD5/SHA hash rather than a string here,
|
|
|
|
|
// but the compare is only done on first call then memoized, so
|
|
|
|
|
// it's not worth optimizing.
|
|
|
|
|
|
|
|
|
|
// Peramble - fetch the exproted function from the scope table
|
|
|
|
|
AstCStmt* const prep = new AstCStmt{flp};
|
|
|
|
|
funcp->addStmtsp(prep);
|
|
|
|
|
// Static doesn't need save-restore as if below will re-fill proper value
|
|
|
|
|
prep->add("static int __Vfuncnum = -1;\n");
|
|
|
|
|
// First time init (faster than what the compiler does if we did a singleton
|
|
|
|
|
prep->add("if (VL_UNLIKELY(__Vfuncnum == -1)) {\n");
|
|
|
|
|
prep->add("__Vfuncnum = Verilated::exportFuncNum(\"" + nodep->cname() + "\");\n");
|
|
|
|
|
prep->add("}\n");
|
|
|
|
|
// If the find fails, it will throw an error
|
|
|
|
|
prep->add("const VerilatedScope* const __Vscopep = Verilated::dpiScope();\n");
|
|
|
|
|
// If 'dpiScope()' fails and '__Vscopep' is null; the exportFind function throws an error
|
|
|
|
|
// If __Vcb is null the exportFind function throws and error
|
|
|
|
|
const std::string cbtype
|
|
|
|
|
= VIdProtect::protect(v3Global.opt.prefix() + "__Vcb_" + nodep->cname() + "_t");
|
|
|
|
|
prep->add(cbtype + " __Vcb = reinterpret_cast<" + cbtype
|
|
|
|
|
+ ">(VerilatedScope::exportFind(__Vscopep, __Vfuncnum));");
|
|
|
|
|
|
|
|
|
|
// Convert input/inout DPI arguments to Internal types, and construct the call
|
|
|
|
|
AstCStmt* const callp = new AstCStmt{flp};
|
|
|
|
|
const auto addFuncArg = [&](AstVar* portp) -> AstVarScope* {
|
|
|
|
|
// No createDpiTemp; we make a real internal variable instead
|
|
|
|
|
AstVarScope* const vscp = createFuncVar(funcp, portp->name() + tmpSuffixp, portp);
|
2019-10-06 19:24:21 +02:00
|
|
|
// No information exposure; is already visible in import/export func template
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
vscp->varp()->protect(false);
|
|
|
|
|
portp->protect(false);
|
|
|
|
|
// Add argument to call
|
|
|
|
|
const VAccess access = portp->isWritable() ? VAccess::WRITE : VAccess::READ;
|
|
|
|
|
callp->add(", ");
|
|
|
|
|
callp->add(new AstVarRef{portp->fileline(), vscp, access});
|
|
|
|
|
return vscp;
|
|
|
|
|
};
|
|
|
|
|
// Call callback
|
|
|
|
|
callp->add("(*__Vcb)(");
|
|
|
|
|
// First argument is the Syms
|
|
|
|
|
callp->add("(" + EmitCUtil::symClassName() + "*)(__Vscopep->symsp())");
|
|
|
|
|
// Add function arguments
|
|
|
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
|
|
|
|
AstVar* const portp = VN_CAST(stmtp, Var);
|
|
|
|
|
if (!portp) continue;
|
|
|
|
|
if (!portp->isIO()) continue;
|
|
|
|
|
if (portp->isFuncReturn()) continue;
|
|
|
|
|
if (portp == rtnvarp) continue; // Handled below
|
|
|
|
|
// Add argument to call
|
|
|
|
|
AstVarScope* const outvscp = addFuncArg(portp);
|
|
|
|
|
if (!portp->isNonOutput()) continue;
|
|
|
|
|
// Convert input/inout arguments to dpi type
|
|
|
|
|
const std::string deref
|
|
|
|
|
= portp->isInout() //
|
|
|
|
|
&& portp->basicp()->isDpiPrimitive() //
|
|
|
|
|
&& portp->dtypep()->skipRefp()->dimensions(false).second == 0
|
|
|
|
|
? "*"
|
|
|
|
|
: "";
|
|
|
|
|
funcp->addStmtsp(createAssignDpiToInternal(outvscp, deref + portp->name()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
// Return value argument goes last
|
|
|
|
|
if (rtnvarp) addFuncArg(rtnvarp);
|
|
|
|
|
// Close statement
|
|
|
|
|
callp->add(");");
|
|
|
|
|
// Call the user function
|
|
|
|
|
funcp->addStmtsp(callp);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Convert output/inout arguments back to internal type
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2018-10-27 23:29:00 +02:00
|
|
|
if (portp->isIO() && portp->isWritable() && !portp->isFuncReturn()) {
|
2021-06-10 23:41:33 +02:00
|
|
|
funcp->addStmtsp(createAssignInternalToDpi(portp, true, tmpSuffixp, ""));
|
2018-10-27 23:29:00 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
// Convert return value
|
2019-05-19 22:13:13 +02:00
|
|
|
if (rtnvarp) {
|
2021-06-10 23:41:33 +02:00
|
|
|
funcp->addStmtsp(createDpiTemp(rtnvarp, ""));
|
|
|
|
|
funcp->addStmtsp(createAssignInternalToDpi(rtnvarp, false, tmpSuffixp, ""));
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
const std::string index = rtnvarp->basicp()->isDpiPrimitive() ? "" : "[0]";
|
|
|
|
|
funcp->addStmtsp(new AstCStmt{flp, "return " + rtnvarp->name() + index + ";"});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-09-09 14:55:00 +02:00
|
|
|
if (!makePortList(nodep, funcp)) return nullptr;
|
2021-06-10 23:41:33 +02:00
|
|
|
return funcp;
|
2009-12-20 14:27:00 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
AstCFunc* makeDpiImportPrototype(AstNodeFTask* nodep, AstVar* rtnvarp) {
|
2024-09-23 04:06:39 +02:00
|
|
|
// Verilog name has __ conversion and other tricks, to match DPI C code, back that out
|
|
|
|
|
const string name = AstNode::prettyName(nodep->cname());
|
|
|
|
|
checkLegalCIdentifier(nodep, name);
|
2021-06-10 23:41:33 +02:00
|
|
|
// Tasks (but not void functions) return a boolean 'int' indicating disabled
|
2021-06-14 20:50:40 +02:00
|
|
|
const string rtnType = rtnvarp ? rtnvarp->dpiArgType(true, true)
|
|
|
|
|
: nodep->dpiTask() ? "int"
|
|
|
|
|
: "";
|
2024-09-23 04:06:39 +02:00
|
|
|
AstCFunc* const funcp = new AstCFunc{nodep->fileline(), name, m_scopep, rtnType};
|
2021-08-12 22:43:32 +02:00
|
|
|
funcp->dpiContext(nodep->dpiContext());
|
2021-11-27 23:07:27 +01:00
|
|
|
funcp->dpiImportPrototype(true);
|
2021-06-10 23:41:33 +02:00
|
|
|
funcp->dontCombine(true);
|
|
|
|
|
funcp->entryPoint(false);
|
|
|
|
|
funcp->isMethod(false);
|
|
|
|
|
funcp->protect(false);
|
2023-09-08 08:51:19 +02:00
|
|
|
funcp->dpiPure(nodep->dpiPure());
|
2025-01-20 20:24:09 +01:00
|
|
|
|
2025-06-28 02:38:01 +02:00
|
|
|
const int cost = static_cast<int>(V3Control::getProfileData(funcp->name()));
|
2025-01-20 20:24:09 +01:00
|
|
|
m_statHierDpisWithCosts += (cost != 0);
|
|
|
|
|
funcp->cost(cost);
|
|
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
// Add DPI Import to top, since it's a global function
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topScopep->scopep()->addBlocksp(funcp);
|
2025-09-09 14:55:00 +02:00
|
|
|
if (!makePortList(nodep, funcp)) return nullptr;
|
2021-06-10 23:41:33 +02:00
|
|
|
return funcp;
|
2017-12-16 21:46:21 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
AstCFunc* getDpiFunc(AstNodeFTask* nodep, AstVar* rtnvarp) {
|
|
|
|
|
UASSERT_OBJ(nodep->dpiImport() || nodep->dpiExport(), nodep, "Not a DPI function");
|
|
|
|
|
// Compute unique signature of this DPI function
|
|
|
|
|
const string signature = dpiSignature(nodep, rtnvarp);
|
|
|
|
|
// Only create one DPI Import prototype or DPI Export entry point for each unique cname as
|
|
|
|
|
// it is illegal for the user to attach multiple tasks with different signatures to one DPI
|
|
|
|
|
// cname.
|
2023-10-28 14:38:02 +02:00
|
|
|
const auto pair = m_dpiNames.emplace(std::piecewise_construct, //
|
|
|
|
|
std::forward_as_tuple(nodep->cname()),
|
|
|
|
|
std::forward_as_tuple(nodep, signature, nullptr));
|
|
|
|
|
if (pair.second) {
|
2021-06-10 23:41:33 +02:00
|
|
|
// First time encountering this cname. Create Import prototype / Export entry point
|
|
|
|
|
AstCFunc* const funcp = nodep->dpiExport() ? makeDpiExportDispatcher(nodep, rtnvarp)
|
|
|
|
|
: makeDpiImportPrototype(nodep, rtnvarp);
|
2023-10-28 14:38:02 +02:00
|
|
|
std::get<2>(pair.first->second) = funcp;
|
2021-06-10 23:41:33 +02:00
|
|
|
return funcp;
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2023-06-20 12:26:46 +02:00
|
|
|
// Seen this cname import before. Check if it's the same prototype.
|
2021-06-10 23:41:33 +02:00
|
|
|
const AstNodeFTask* firstNodep;
|
|
|
|
|
string firstSignature;
|
|
|
|
|
AstCFunc* firstFuncp;
|
2023-10-28 14:38:02 +02:00
|
|
|
std::tie(firstNodep, firstSignature, firstFuncp) = pair.first->second;
|
2021-06-10 23:41:33 +02:00
|
|
|
if (signature != firstSignature) {
|
|
|
|
|
// Different signature, so error.
|
2023-06-20 12:26:46 +02:00
|
|
|
nodep->v3error("Duplicate declaration of DPI function with different signature: '"
|
|
|
|
|
<< nodep->cname() << "'\n"
|
2021-06-10 23:41:33 +02:00
|
|
|
<< nodep->warnContextPrimary() << '\n'
|
|
|
|
|
<< nodep->warnMore() //
|
|
|
|
|
<< "... New signature: " << signature << '\n' //
|
|
|
|
|
<< firstNodep->warnOther()
|
|
|
|
|
<< "... Original signature: " << firstSignature << '\n' //
|
|
|
|
|
<< firstNodep->warnContextSecondary());
|
|
|
|
|
return nullptr;
|
|
|
|
|
} else {
|
|
|
|
|
// Same signature, return the previously created CFunc
|
|
|
|
|
return firstFuncp;
|
|
|
|
|
}
|
2017-12-16 21:46:21 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-09 14:55:00 +02:00
|
|
|
static bool makePortList(AstNodeFTask* nodep, AstCFunc* dpip) {
|
|
|
|
|
bool allOk = true;
|
2017-12-16 21:46:21 +01:00
|
|
|
// Copy nodep's list of function I/O to the new dpip c function
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2017-12-16 21:46:21 +01:00
|
|
|
if (portp->isIO()) {
|
|
|
|
|
// Move it to new function
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const newPortp = portp->cloneTree(false);
|
2017-12-16 21:46:21 +01:00
|
|
|
newPortp->funcLocal(true);
|
|
|
|
|
dpip->addArgsp(newPortp);
|
|
|
|
|
if (!portp->basicp()) {
|
2025-09-09 14:55:00 +02:00
|
|
|
allOk = false;
|
2020-06-10 01:20:16 +02:00
|
|
|
portp->v3warn(
|
|
|
|
|
E_UNSUPPORTED,
|
2020-04-15 13:58:34 +02:00
|
|
|
"Unsupported: DPI argument of type "
|
2024-07-28 20:18:24 +02:00
|
|
|
<< portp->dtypep()->prettyTypeName() << '\n'
|
2020-06-10 01:20:16 +02:00
|
|
|
<< portp->warnMore()
|
|
|
|
|
<< "... For best portability, use bit, byte, int, or longint");
|
2017-12-16 21:46:21 +01:00
|
|
|
// We don't warn on logic either, although the 4-stateness is lost.
|
|
|
|
|
// That's what other simulators do.
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-09 14:55:00 +02:00
|
|
|
return allOk;
|
2009-12-03 12:55:29 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
void bodyDpiImportFunc(AstNodeFTask* nodep, AstVarScope* rtnvscp, AstCFunc* cfuncp,
|
|
|
|
|
AstCFunc* dpiFuncp) {
|
2020-11-03 22:08:03 +01:00
|
|
|
const char* const tmpSuffixp = V3Task::dpiTemporaryVarSuffix();
|
2025-09-21 17:37:44 +02:00
|
|
|
|
|
|
|
|
if (v3Global.opt.profExec())
|
|
|
|
|
cfuncp->addStmtsp(
|
|
|
|
|
new AstCStmt{nodep->fileline(),
|
2025-09-26 14:25:47 +02:00
|
|
|
"VL_EXEC_TRACE_ADD_RECORD(vlSymsp).sectionPush(\"dpiimports\");"});
|
2025-09-21 17:37:44 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Convert input/inout arguments to DPI types
|
|
|
|
|
string args;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
|
|
|
|
AstVarScope* const portvscp
|
2021-10-22 14:56:48 +02:00
|
|
|
= VN_AS(portp->user2p(), VarScope); // Remembered when we created it earlier
|
2019-05-19 22:13:13 +02:00
|
|
|
if (portp->isIO() && !portp->isFuncReturn() && portvscp != rtnvscp
|
|
|
|
|
&& portp->name() != "__Vscopep" // Passed to dpiContext, not callee
|
2020-04-15 13:58:34 +02:00
|
|
|
&& portp->name() != "__Vfilenamep" && portp->name() != "__Vlineno") {
|
2009-12-03 12:55:29 +01:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
if (args != "") args += ", ";
|
2009-12-03 12:55:29 +01:00
|
|
|
|
2020-04-08 01:07:47 +02:00
|
|
|
if (portp->isDpiOpenArray()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeDType* const dtypep = portp->dtypep()->skipRefp();
|
2024-01-06 00:00:06 +01:00
|
|
|
UASSERT_OBJ(!VN_IS(dtypep, DynArrayDType) && !VN_IS(dtypep, QueueDType),
|
|
|
|
|
portp,
|
|
|
|
|
"Passing dynamic array or queue as actual argument to DPI "
|
|
|
|
|
"open array is not yet supported");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Ideally we'd make a table of variable
|
|
|
|
|
// characteristics, and reuse it wherever we can
|
2017-12-17 22:28:58 +01:00
|
|
|
// At least put them into the module's CTOR as static?
|
2021-06-21 00:32:57 +02:00
|
|
|
const string propName = portp->name() + "__Vopenprops";
|
|
|
|
|
const string propCode = portp->vlPropDecl(propName);
|
2022-11-20 23:40:38 +01:00
|
|
|
cfuncp->addStmtsp(new AstCStmt{portp->fileline(), propCode});
|
2017-12-17 22:28:58 +01:00
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
// At runtime we need the svOpenArrayHandle to
|
|
|
|
|
// point to this task & thread's data, in addition
|
|
|
|
|
// to static info about the variable
|
2021-06-21 00:32:57 +02:00
|
|
|
const string name = portp->name() + "__Vopenarray";
|
2021-11-26 23:55:36 +01:00
|
|
|
const string varCode
|
2020-04-15 13:58:34 +02:00
|
|
|
= ("VerilatedDpiOpenVar "
|
|
|
|
|
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
|
|
|
|
+ name + " (&" + propName + ", &" + portp->name() + ");\n");
|
2022-11-20 23:40:38 +01:00
|
|
|
cfuncp->addStmtsp(new AstCStmt{portp->fileline(), varCode});
|
2020-04-15 13:58:34 +02:00
|
|
|
args += "&" + name;
|
|
|
|
|
} else {
|
2020-04-08 01:07:47 +02:00
|
|
|
if (portp->isWritable() && portp->basicp()->isDpiPrimitive()) {
|
2020-11-19 14:02:58 +01:00
|
|
|
if (!VN_IS(portp->dtypep()->skipRefp(), UnpackArrayDType)) args += "&";
|
2020-04-08 01:07:47 +02:00
|
|
|
}
|
2017-12-17 22:28:58 +01:00
|
|
|
|
2020-11-03 22:08:03 +01:00
|
|
|
args += portp->name() + tmpSuffixp;
|
2017-12-17 22:28:58 +01:00
|
|
|
|
2020-11-03 22:08:03 +01:00
|
|
|
cfuncp->addStmtsp(createDpiTemp(portp, tmpSuffixp));
|
2018-10-27 23:29:00 +02:00
|
|
|
if (portp->isNonOutput()) {
|
2020-04-08 01:07:47 +02:00
|
|
|
cfuncp->addStmtsp(
|
2020-11-03 22:08:03 +01:00
|
|
|
createAssignInternalToDpi(portp, false, "", tmpSuffixp));
|
2017-12-17 22:28:58 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store context, if needed
|
|
|
|
|
if (nodep->dpiContext()) {
|
2025-09-26 14:25:47 +02:00
|
|
|
const string stmt = "Verilated::dpiContext(__Vscopep, __Vfilenamep, __Vlineno);";
|
2022-11-20 23:40:38 +01:00
|
|
|
cfuncp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
{ // Call the imported function
|
2022-11-20 23:40:38 +01:00
|
|
|
AstCCall* const callp = new AstCCall{nodep->fileline(), dpiFuncp};
|
2022-10-12 11:19:21 +02:00
|
|
|
callp->dtypeSetVoid();
|
2021-06-10 23:41:33 +02:00
|
|
|
callp->argTypes(args);
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
if (rtnvscp) {
|
|
|
|
|
// If it has a return value, capture it
|
|
|
|
|
cfuncp->addStmtsp(createDpiTemp(rtnvscp->varp(), tmpSuffixp));
|
|
|
|
|
const std::string sel = rtnvscp->varp()->basicp()->isDpiPrimitive() ? "" : "[0]";
|
|
|
|
|
AstCStmt* const cstmtp = new AstCStmt{nodep->fileline()};
|
|
|
|
|
cstmtp->add(rtnvscp->varp()->name() + tmpSuffixp + sel); // LHS
|
|
|
|
|
cstmtp->add(" = ");
|
|
|
|
|
cstmtp->add(callp); // RHS
|
|
|
|
|
cstmtp->add(";");
|
|
|
|
|
cfuncp->addStmtsp(cstmtp);
|
|
|
|
|
} else {
|
|
|
|
|
// Othervise just call it
|
|
|
|
|
cfuncp->addStmtsp(callp->makeStmt());
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert output/inout arguments back to internal type
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2019-10-06 19:24:21 +02:00
|
|
|
portp->protect(false); // No additional exposure - already part of shown proto
|
2018-10-27 23:29:00 +02:00
|
|
|
if (portp->isIO() && (portp->isWritable() || portp->isFuncReturn())
|
2017-12-17 22:28:58 +01:00
|
|
|
&& !portp->isDpiOpenArray()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVarScope* const portvscp = VN_AS(
|
2020-04-15 13:58:34 +02:00
|
|
|
portp->user2p(), VarScope); // Remembered when we created it earlier
|
2020-04-08 01:07:47 +02:00
|
|
|
cfuncp->addStmtsp(
|
2020-11-03 22:08:03 +01:00
|
|
|
createAssignDpiToInternal(portvscp, portp->name() + tmpSuffixp));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-21 17:37:44 +02:00
|
|
|
|
|
|
|
|
if (v3Global.opt.profExec())
|
|
|
|
|
cfuncp->addStmtsp(new AstCStmt{nodep->fileline(),
|
2025-09-26 14:25:47 +02:00
|
|
|
"VL_EXEC_TRACE_ADD_RECORD(vlSymsp).sectionPop();"});
|
2009-12-03 12:55:29 +01:00
|
|
|
}
|
2006-10-11 17:41:42 +02:00
|
|
|
|
2022-07-14 13:35:44 +02:00
|
|
|
AstVarScope* getDpiExporTrigger() {
|
2022-05-15 17:03:32 +02:00
|
|
|
AstNetlist* const netlistp = v3Global.rootp();
|
|
|
|
|
AstVarScope* dpiExportTriggerp = netlistp->dpiExportTriggerp();
|
2021-08-12 22:43:32 +02:00
|
|
|
if (!dpiExportTriggerp) {
|
|
|
|
|
// Create the global DPI export trigger flag the first time we encounter a DPI export.
|
|
|
|
|
// This flag is set any time a DPI export is invoked, and cleared at the end of eval.
|
|
|
|
|
FileLine* const fl = m_topScopep->fileline();
|
2022-05-15 17:03:32 +02:00
|
|
|
const string name{"__Vdpi_export_trigger"};
|
|
|
|
|
AstVar* const varp = new AstVar{fl, VVarType::VAR, name, VFlagBitPacked{}, 1};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topScopep->scopep()->modp()->addStmtsp(varp);
|
2021-08-12 22:43:32 +02:00
|
|
|
dpiExportTriggerp = new AstVarScope{fl, m_topScopep->scopep(), varp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_topScopep->scopep()->addVarsp(dpiExportTriggerp);
|
2022-05-15 17:03:32 +02:00
|
|
|
netlistp->dpiExportTriggerp(dpiExportTriggerp);
|
2021-08-12 22:43:32 +02:00
|
|
|
}
|
|
|
|
|
return dpiExportTriggerp;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 12:55:29 +01:00
|
|
|
AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool ftaskNoInline) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Given a already cloned node, make a public C function, or a non-inline C function
|
|
|
|
|
// Probably some of this work should be done later, but...
|
|
|
|
|
// should the type of the function be bool/uint32/64 etc (based on lookup) or IData?
|
|
|
|
|
AstNode::user2ClearTree();
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVar* rtnvarp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->isFunction()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const portp = VN_AS(nodep->fvarp(), Var);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(portp, nodep, "function without function output variable");
|
2023-07-27 11:33:34 +02:00
|
|
|
UASSERT_OBJ(portp->isFuncReturn(), nodep, "Not marked as function return var");
|
2020-04-08 01:07:47 +02:00
|
|
|
if (nodep->dpiImport() || nodep->dpiExport()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstBasicDType* const bdtypep = portp->dtypep()->basicp();
|
2020-04-08 01:07:47 +02:00
|
|
|
if (!bdtypep->isDpiPrimitive()) {
|
|
|
|
|
if (bdtypep->isDpiBitVec() && portp->width() > 32) {
|
|
|
|
|
portp->v3error("DPI function may not return a > 32 bits wide type "
|
|
|
|
|
"other than basic types.\n"
|
2023-01-05 23:59:51 +01:00
|
|
|
+ portp->warnMore()
|
2020-04-08 01:07:47 +02:00
|
|
|
+ "... Suggest make it an output argument instead?");
|
|
|
|
|
}
|
|
|
|
|
if (bdtypep->isDpiLogicVec()) {
|
|
|
|
|
portp->v3error("DPI function may not return a 4-state type "
|
2024-03-02 15:05:21 +01:00
|
|
|
"other than a single 'logic' (IEEE 1800-2023 35.5.5)");
|
2020-04-08 01:07:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-07-25 10:55:58 +02:00
|
|
|
} else if (nodep->taskPublic()) {
|
2020-04-08 01:07:47 +02:00
|
|
|
if (portp->isWide()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Public functions with return > 64 bits wide.\n"
|
2023-01-05 23:59:51 +01:00
|
|
|
+ nodep->warnMore()
|
2020-06-10 01:20:16 +02:00
|
|
|
+ "... Suggest make it an output argument instead?");
|
2020-04-08 01:07:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ftaskNoInline || nodep->dpiExport()) {
|
|
|
|
|
portp->funcReturn(false); // Converting return to 'outputs'
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-01-05 02:19:58 +01:00
|
|
|
unlinkAndClone(nodep, portp, false);
|
2017-12-16 21:46:21 +01:00
|
|
|
rtnvarp = portp;
|
|
|
|
|
rtnvarp->funcLocal(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
rtnvarp->name(rtnvarp->name()
|
|
|
|
|
+ "__Vfuncrtn"); // Avoid conflict with DPI function name
|
2019-10-06 19:24:21 +02:00
|
|
|
if (nodep->dpiImport() || nodep->dpiExport()) rtnvarp->protect(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2017-12-16 21:46:21 +01:00
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
// Create/pick up the DPI CFunc for DPI Import/ DPI Export.
|
|
|
|
|
AstCFunc* dpiFuncp = nullptr;
|
|
|
|
|
if (nodep->dpiImport() || nodep->dpiExport()) {
|
|
|
|
|
UASSERT_OBJ(!(nodep->dpiOpenParent() && nodep->dpiOpenChild()), nodep,
|
|
|
|
|
"DPI task should not be both parent and child");
|
|
|
|
|
dpiFuncp = getDpiFunc(nodep, rtnvarp);
|
2025-09-10 23:42:45 +02:00
|
|
|
if (!dpiFuncp || (nodep->dpiImport() && nodep->dpiOpenParent())) {
|
2021-06-10 23:41:33 +02:00
|
|
|
// No need to make more than just the DPI Import prototype, the children will
|
|
|
|
|
// create the wrapper implementations.
|
2025-09-10 23:42:45 +02:00
|
|
|
if (rtnvarp) VL_DO_DANGLING(pushDeletep(rtnvarp), rtnvarp);
|
2021-06-10 23:41:33 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
return nullptr;
|
2017-12-17 22:28:58 +01:00
|
|
|
}
|
2017-12-16 21:46:21 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVarScope* rtnvscp = nullptr;
|
2017-12-16 21:46:21 +01:00
|
|
|
if (rtnvarp) {
|
2022-11-20 23:40:38 +01:00
|
|
|
rtnvscp = new AstVarScope{rtnvarp->fileline(), m_scopep, rtnvarp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_scopep->addVarsp(rtnvscp);
|
2017-12-16 21:46:21 +01:00
|
|
|
rtnvarp->user2p(rtnvscp);
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-15 00:39:33 +02:00
|
|
|
string prefix;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->dpiImport()) {
|
|
|
|
|
prefix = "__Vdpiimwrap_";
|
|
|
|
|
} else if (nodep->dpiExport()) {
|
|
|
|
|
prefix = "__Vdpiexp_";
|
|
|
|
|
} else if (ftaskNoInline) {
|
|
|
|
|
prefix = "__VnoInFunc_";
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
// Unless public, v3Descope will not uniquify function names even if duplicate per-scope,
|
|
|
|
|
// so make it unique now.
|
2018-10-14 05:06:36 +02:00
|
|
|
string suffix; // So, make them unique
|
2020-08-24 02:27:25 +02:00
|
|
|
if (!nodep->taskPublic() && !nodep->classMethod()) suffix = "_" + m_scopep->nameDotless();
|
2021-06-21 00:32:57 +02:00
|
|
|
const string name = ((nodep->name() == "new") ? "new" : prefix + nodep->name() + suffix);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstCFunc* const cfuncp = new AstCFunc{
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->fileline(), name, m_scopep,
|
2022-11-20 23:40:38 +01:00
|
|
|
((nodep->taskPublic() && rtnvarp) ? rtnvarp->cPubArgType(true, true) : "")};
|
2019-05-19 22:13:13 +02:00
|
|
|
// It's ok to combine imports because this is just a wrapper;
|
|
|
|
|
// duplicate wrappers can get merged.
|
|
|
|
|
cfuncp->dontCombine(!nodep->dpiImport());
|
2018-08-25 15:52:45 +02:00
|
|
|
cfuncp->entryPoint(!nodep->dpiImport());
|
|
|
|
|
cfuncp->funcPublic(nodep->taskPublic());
|
2021-11-27 23:07:27 +01:00
|
|
|
cfuncp->dpiContext(nodep->dpiContext());
|
2021-06-10 23:41:33 +02:00
|
|
|
cfuncp->dpiExportImpl(nodep->dpiExport());
|
2018-07-07 14:02:29 +02:00
|
|
|
cfuncp->dpiImportWrapper(nodep->dpiImport());
|
2023-10-17 13:38:45 +02:00
|
|
|
cfuncp->recursive(nodep->recursive());
|
2021-06-10 23:41:33 +02:00
|
|
|
if (nodep->dpiImport() || nodep->dpiExport()) {
|
|
|
|
|
cfuncp->isStatic(true);
|
|
|
|
|
cfuncp->isLoose(true);
|
|
|
|
|
} else {
|
|
|
|
|
cfuncp->isStatic(false);
|
|
|
|
|
}
|
2020-08-24 02:27:25 +02:00
|
|
|
cfuncp->isVirtual(nodep->isVirtual());
|
2023-09-08 08:51:19 +02:00
|
|
|
cfuncp->dpiPure(nodep->dpiPure());
|
2024-01-24 01:36:11 +01:00
|
|
|
if (nodep->name() == "new") cfuncp->isConstructor(true);
|
2021-06-10 23:41:33 +02:00
|
|
|
if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname());
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2025-01-20 20:24:09 +01:00
|
|
|
if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname());
|
|
|
|
|
|
2025-09-21 17:37:44 +02:00
|
|
|
const bool needSyms
|
|
|
|
|
= (!nodep->dpiImport() && !nodep->taskPublic()) || v3Global.opt.profExec();
|
|
|
|
|
if (needSyms) cfuncp->argTypes(EmitCUtil::symClassVar());
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
if (!nodep->dpiImport() && !nodep->taskPublic()) {
|
|
|
|
|
// Need symbol table
|
|
|
|
|
if (cfuncp->name() == "new") {
|
2025-09-26 14:25:47 +02:00
|
|
|
const string stmt = VIdProtect::protect("_ctor_var_reset") + "(vlSymsp);";
|
2025-10-21 17:37:32 +02:00
|
|
|
cfuncp->addStmtsp(new AstCStmt{nodep->fileline(), stmt});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nodep->dpiContext()) {
|
|
|
|
|
// First three args go to dpiContext call
|
2022-01-02 19:56:40 +01:00
|
|
|
createInputVar(cfuncp, "__Vscopep", VBasicDTypeKwd::SCOPEPTR);
|
|
|
|
|
createInputVar(cfuncp, "__Vfilenamep", VBasicDTypeKwd::CHARPTR);
|
|
|
|
|
createInputVar(cfuncp, "__Vlineno", VBasicDTypeKwd::INT);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nodep->dpiExport()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstScopeName* const snp = nodep->scopeNamep();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(snp, nodep, "Missing scoping context");
|
2025-10-14 13:08:35 +02:00
|
|
|
// The AstScopeName is really a statement(ish) for tracking, not a function
|
|
|
|
|
snp->dpiExport(true);
|
2019-05-19 22:13:13 +02:00
|
|
|
snp->unlinkFrBack();
|
2025-10-21 17:37:32 +02:00
|
|
|
cfuncp->addStmtsp(snp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create list of arguments and move to function
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode *nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = stmtp->nextp();
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2021-07-22 22:09:24 +02:00
|
|
|
if (portp->isParam() && VN_IS(portp->valuep(), InitArray)) {
|
|
|
|
|
// Move array parameters in functions into constant pool
|
2019-05-19 22:13:13 +02:00
|
|
|
portp->unlinkFrBack();
|
2021-07-22 22:09:24 +02:00
|
|
|
pushDeletep(portp);
|
|
|
|
|
AstNode* const tablep = v3Global.rootp()->constPoolp()->findTable(
|
2021-10-22 14:56:48 +02:00
|
|
|
VN_AS(portp->valuep(), InitArray));
|
2021-07-22 22:09:24 +02:00
|
|
|
portp->user2p(tablep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2021-07-22 22:09:24 +02:00
|
|
|
if (portp->isIO()) {
|
|
|
|
|
// Move it to new function
|
2022-01-05 02:19:58 +01:00
|
|
|
unlinkAndClone(nodep, portp, false);
|
2021-07-22 22:09:24 +02:00
|
|
|
portp->funcLocal(true);
|
|
|
|
|
cfuncp->addArgsp(portp);
|
|
|
|
|
} else {
|
|
|
|
|
// "Normal" variable, mark inside function
|
|
|
|
|
portp->funcLocal(true);
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVarScope* const newvscp
|
|
|
|
|
= new AstVarScope{portp->fileline(), m_scopep, portp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_scopep->addVarsp(newvscp);
|
2021-07-22 22:09:24 +02:00
|
|
|
portp->user2p(newvscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fake output variable if was a function. It's more efficient to
|
|
|
|
|
// have it last, rather than first, as the C compiler can sometimes
|
|
|
|
|
// avoid copying variables when calling shells if argument 1
|
|
|
|
|
// remains argument 1 (which it wouldn't if a return got added).
|
|
|
|
|
if (rtnvarp) cfuncp->addArgsp(rtnvarp);
|
|
|
|
|
|
|
|
|
|
// Move body
|
2022-01-05 02:19:58 +01:00
|
|
|
AstNode* bodysp = nodep->stmtsp();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (bodysp) {
|
2022-01-05 02:19:58 +01:00
|
|
|
unlinkAndClone(nodep, bodysp, true);
|
2025-09-20 19:16:03 +02:00
|
|
|
AstBegin* const tempp
|
2025-09-23 20:49:01 +02:00
|
|
|
= new AstBegin{nodep->fileline(), "[EditWrapper]", bodysp, false};
|
2022-01-05 02:19:58 +01:00
|
|
|
VL_DANGLING(bodysp);
|
|
|
|
|
// If we cloned due to recursion, now need to rip out the ports
|
|
|
|
|
// (that remained in place) then got cloned
|
|
|
|
|
for (AstNode *nextp, *stmtp = tempp->stmtsp(); stmtp; stmtp = nextp) {
|
|
|
|
|
nextp = stmtp->nextp();
|
|
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2025-09-10 23:42:45 +02:00
|
|
|
if (portp->isIO()) VL_DO_DANGLING(pushDeletep(portp->unlinkFrBack()), portp);
|
2022-01-05 02:19:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (tempp->stmtsp()) cfuncp->addStmtsp(tempp->stmtsp()->unlinkFrBackWithNext());
|
|
|
|
|
VL_DO_DANGLING(tempp->deleteTree(), tempp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-06-10 23:41:33 +02:00
|
|
|
if (nodep->dpiImport()) bodyDpiImportFunc(nodep, rtnvscp, cfuncp, dpiFuncp);
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// Return statement
|
|
|
|
|
if (rtnvscp && nodep->taskPublic()) {
|
2025-10-21 17:37:32 +02:00
|
|
|
cfuncp->addStmtsp(new AstCReturn{
|
2022-11-20 23:40:38 +01:00
|
|
|
rtnvscp->fileline(), new AstVarRef{rtnvscp->fileline(), rtnvscp, VAccess::READ}});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Replace variable refs
|
2022-01-09 23:34:10 +01:00
|
|
|
relink(cfuncp);
|
2021-08-12 22:43:32 +02:00
|
|
|
|
|
|
|
|
if (cfuncp->dpiExportImpl()) {
|
|
|
|
|
// Mark all non-local variables written by the DPI exported function as being updated
|
|
|
|
|
// by DPI exports. This ensures correct ordering and change detection later.
|
2022-01-09 23:34:10 +01:00
|
|
|
|
2022-07-14 13:35:44 +02:00
|
|
|
// Mark non-local variables written by the exported function
|
|
|
|
|
bool writesNonLocals = false;
|
2022-10-20 14:48:44 +02:00
|
|
|
cfuncp->foreach([&writesNonLocals](AstVarRef* refp) {
|
2022-07-14 13:35:44 +02:00
|
|
|
if (refp->access().isReadOnly()) return; // Ignore read reference
|
|
|
|
|
AstVar* const varp = refp->varScopep()->varp();
|
|
|
|
|
// We are ignoring function locals as they should not be referenced anywhere
|
|
|
|
|
// outside the enclosing AstCFunc, hence they are irrelevant for code ordering.
|
|
|
|
|
if (varp->isFuncLocal()) return;
|
|
|
|
|
// Mark it as written by DPI export
|
|
|
|
|
varp->setWrittenByDpi();
|
|
|
|
|
// Remember we had some
|
|
|
|
|
writesNonLocals = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// If this DPI export writes some non-local variables, set the DPI Export Trigger flag
|
|
|
|
|
// in the function.
|
|
|
|
|
if (writesNonLocals) {
|
|
|
|
|
AstVarScope* const dpiExportTriggerp = getDpiExporTrigger();
|
|
|
|
|
FileLine* const flp = cfuncp->fileline();
|
2021-08-12 22:43:32 +02:00
|
|
|
|
|
|
|
|
// Set DPI export trigger flag every time the DPI export is called.
|
|
|
|
|
AstAssign* const assignp
|
2022-07-14 13:35:44 +02:00
|
|
|
= new AstAssign{flp, new AstVarRef{flp, dpiExportTriggerp, VAccess::WRITE},
|
|
|
|
|
new AstConst{flp, AstConst::BitTrue{}}};
|
|
|
|
|
|
2021-08-12 22:43:32 +02:00
|
|
|
// Add as first statement (to avoid issues with early returns) to exported function
|
|
|
|
|
if (cfuncp->stmtsp()) {
|
|
|
|
|
cfuncp->stmtsp()->addHereThisAsNext(assignp);
|
|
|
|
|
} else {
|
|
|
|
|
cfuncp->addStmtsp(assignp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-01 16:02:08 +02:00
|
|
|
// Mark the fact that this function allocates std::process
|
2023-08-05 09:07:23 +02:00
|
|
|
if (nodep->needProcess()) cfuncp->setNeedProcess();
|
2023-06-01 16:02:08 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Delete rest of cloned task and return new func
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, cfuncp, "", "userFunc");
|
2019-05-19 22:13:13 +02:00
|
|
|
return cfuncp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void iterateIntoFTask(AstNodeFTask* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate into the FTask we are calling. Note it may be under a different
|
|
|
|
|
// scope then the caller, so we need to restore state.
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_scopep);
|
|
|
|
|
VL_RESTORER(m_insStmtp);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_scopep = m_statep->getScope(nodep);
|
|
|
|
|
iterate(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2023-09-01 14:03:16 +02:00
|
|
|
void insertBeforeStmt(AstNode* nodep, AstNode* newp) {
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "newstmt");
|
2023-07-21 09:29:11 +02:00
|
|
|
UASSERT_OBJ(m_insStmtp, nodep, "Function call not underneath a statement");
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, newp, "", "newfunc");
|
2023-09-01 14:03:16 +02:00
|
|
|
m_insStmtp->addHereThisAsNext(newp);
|
2006-09-05 22:06:23 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
VL_RESTORER(m_modNCalls);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_modp = nodep;
|
|
|
|
|
m_modNCalls = 0;
|
|
|
|
|
iterateChildren(nodep);
|
2025-07-27 16:30:19 +02:00
|
|
|
UASSERT_OBJ(!m_insStmtp, nodep, "Didn't finish out last statement");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2023-05-30 15:02:59 +02:00
|
|
|
void visit(AstWith* nodep) override {
|
|
|
|
|
if (nodep->user1SetOnce()) {
|
|
|
|
|
// Make sure that the return expression is converted only once
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AstNodeExpr* const withExprp = VN_AS(nodep->exprp()->unlinkFrBack(), NodeExpr);
|
|
|
|
|
nodep->addExprp(new AstCReturn{withExprp->fileline(), withExprp});
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2025-02-28 03:18:27 +01:00
|
|
|
VL_RESTORER(m_scopep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_scopep = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2025-07-27 16:30:19 +02:00
|
|
|
UASSERT_OBJ(!m_insStmtp, nodep, "Didn't finish out last statement");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-10-13 13:29:23 +02:00
|
|
|
void visit(AstCNew* nodep) override {
|
|
|
|
|
VL_RESTORER(m_inNew);
|
|
|
|
|
m_inNew = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2025-09-10 17:37:34 +02:00
|
|
|
if (m_inSensesp && !nodep->isPure()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Impure function calls in sensitivity lists");
|
2022-08-19 20:18:38 +02:00
|
|
|
nodep->taskp(nullptr); // So V3Broken doesn't complain
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-04-13 00:57:12 +02:00
|
|
|
// Includes handling AstMethodCall, AstNew
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked?");
|
2019-05-19 22:13:13 +02:00
|
|
|
iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " FTask REF " << nodep);
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "inlfunc");
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_scopep, nodep, "func ref not under scope");
|
2021-06-21 00:32:57 +02:00
|
|
|
const string namePrefix = ((VN_IS(nodep, FuncRef) ? "__Vfunc_" : "__Vtask_")
|
|
|
|
|
+ nodep->taskp()->shortName() + "__" + cvtToStr(m_modNCalls++));
|
2019-05-19 22:13:13 +02:00
|
|
|
// Create output variable
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVarScope* outvscp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->taskp()->isFunction()) {
|
|
|
|
|
// Not that it's a FUNCREF, but that we're calling a function (perhaps as a task)
|
2020-04-15 13:58:34 +02:00
|
|
|
outvscp
|
2021-10-22 14:56:48 +02:00
|
|
|
= createVarScope(VN_AS(nodep->taskp()->fvarp(), Var), namePrefix + "__Vfuncout");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Create cloned statements
|
|
|
|
|
AstNode* beginp;
|
2020-08-15 16:12:55 +02:00
|
|
|
AstCNew* cnewp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_statep->ftaskNoInline(nodep->taskp())) {
|
|
|
|
|
// This may share VarScope's with a public task, if any. Yuk.
|
2020-04-13 00:57:12 +02:00
|
|
|
beginp = createNonInlinedFTask(nodep, namePrefix, outvscp, cnewp /*ref*/);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
beginp = createInlinedFTask(nodep, namePrefix, outvscp);
|
2024-11-26 01:59:10 +01:00
|
|
|
++m_statInlines;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
|
2025-07-27 16:31:15 +02:00
|
|
|
if (VN_IS(nodep, New)) { // New not legal as while() condition
|
2023-09-01 14:03:16 +02:00
|
|
|
insertBeforeStmt(nodep, beginp);
|
2020-04-13 00:57:12 +02:00
|
|
|
UASSERT_OBJ(cnewp, nodep, "didn't create cnew for new");
|
|
|
|
|
nodep->replaceWith(cnewp);
|
2022-10-12 11:19:21 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2023-08-28 15:44:41 +02:00
|
|
|
} else if (VN_IS(nodep->backp(), NodeAssign)) {
|
2023-09-16 23:37:25 +02:00
|
|
|
UASSERT_OBJ(nodep->taskp()->isFunction(), nodep,
|
|
|
|
|
"funcref-like assign to non-function");
|
2023-09-01 14:03:16 +02:00
|
|
|
insertBeforeStmt(nodep, beginp);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstVarRef* const outrefp = new AstVarRef{nodep->fileline(), outvscp, VAccess::READ};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(outrefp);
|
2022-10-12 11:19:21 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2023-08-28 15:44:41 +02:00
|
|
|
} else if (!VN_IS(nodep->backp(), StmtExpr)) {
|
2023-09-16 23:37:25 +02:00
|
|
|
UASSERT_OBJ(nodep->taskp()->isFunction(), nodep,
|
|
|
|
|
"funcref-like expression to non-function");
|
2023-08-28 15:44:41 +02:00
|
|
|
AstVarRef* const outrefp = new AstVarRef{nodep->fileline(), outvscp, VAccess::READ};
|
2025-10-13 13:29:23 +02:00
|
|
|
AstExprStmt* lambdap = new AstExprStmt{nodep->fileline(), beginp, outrefp};
|
|
|
|
|
|
|
|
|
|
if (m_inNew) {
|
|
|
|
|
AstVar* varp = outvscp->varp();
|
|
|
|
|
varp->funcLocal(true);
|
|
|
|
|
|
|
|
|
|
// Create a new var that will be inside the lambda
|
|
|
|
|
AstVar* newvarp = varp->cloneTree(false);
|
|
|
|
|
|
|
|
|
|
// Replace all references so they point to the new var
|
|
|
|
|
lambdap->stmtsp()->foreachAndNext([varp, newvarp](AstVarRef* refp) {
|
|
|
|
|
if (refp->varp() == varp) refp->varp(newvarp);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add variable initialization
|
|
|
|
|
lambdap->stmtsp()->addHereThisAsNext(newvarp);
|
|
|
|
|
lambdap->hasResult(false);
|
|
|
|
|
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
// Add return statement - TODO: Why not VarRef(outvscp)?
|
|
|
|
|
AstCExpr* const exprp = new AstCExpr{nodep->fileline(), varp->name()};
|
2025-10-13 13:29:23 +02:00
|
|
|
exprp->dtypeSetString();
|
|
|
|
|
lambdap->addStmtsp(new AstCReturn{nodep->fileline(), exprp});
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-18 15:21:30 +02:00
|
|
|
// AstExprStmt is currently treated as impure, so clear the cached purity of its
|
|
|
|
|
// parents
|
2025-10-13 13:29:23 +02:00
|
|
|
nodep->replaceWith(lambdap);
|
2023-08-28 15:44:41 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2023-10-15 12:25:42 +02:00
|
|
|
VIsCached::clearCacheTree();
|
2025-07-27 16:30:19 +02:00
|
|
|
} else { // VN_IS(nodep->backp(), StmtExpr)
|
2023-09-01 14:03:16 +02:00
|
|
|
insertBeforeStmt(nodep, beginp);
|
2025-08-08 11:10:40 +02:00
|
|
|
if (nodep->taskp()->isFunction()
|
|
|
|
|
&& !nodep->backp()->fileline()->warnIsOff(V3ErrorCode::IGNOREDRETURN)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3warn(
|
|
|
|
|
IGNOREDRETURN,
|
2024-03-02 15:05:21 +01:00
|
|
|
"Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)");
|
2019-03-10 19:57:01 +01:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-03-10 19:57:01 +01:00
|
|
|
}
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " FTask REF Done.");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " visitFTask " << nodep);
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_insStmtp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_insStmtp = nodep->stmtsp(); // Might be null if no statements, but we won't use it
|
|
|
|
|
if (!nodep->user1SetOnce()) { // Just one creation needed per function
|
|
|
|
|
// Expand functions in it
|
|
|
|
|
int modes = 0;
|
|
|
|
|
if (nodep->dpiImport()) modes++;
|
|
|
|
|
if (nodep->dpiExport()) modes++;
|
|
|
|
|
if (nodep->taskPublic()) modes++;
|
2020-04-05 15:30:23 +02:00
|
|
|
if (nodep->classMethod()) modes++;
|
2019-10-06 19:24:21 +02:00
|
|
|
if (v3Global.opt.protectIds() && nodep->taskPublic()) {
|
|
|
|
|
// We always call protect() on names, we don't check if public or not
|
|
|
|
|
// Hence any external references wouldn't be able to find the refed public object.
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Using --protect-ids with public function");
|
2019-10-06 19:24:21 +02:00
|
|
|
}
|
2020-04-05 15:30:23 +02:00
|
|
|
if (modes > 1) {
|
|
|
|
|
nodep->v3error("Cannot mix DPI import, DPI export, class methods, and/or public "
|
|
|
|
|
"on same function: "
|
|
|
|
|
<< nodep->prettyNameQ());
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2020-04-05 15:30:23 +02:00
|
|
|
if (nodep->dpiImport() || nodep->dpiExport() || nodep->taskPublic()
|
|
|
|
|
|| m_statep->ftaskNoInline(nodep)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Clone it first, because we may have later FTaskRef's that still need
|
|
|
|
|
// the original version.
|
2020-04-05 15:30:23 +02:00
|
|
|
if (m_statep->ftaskNoInline(nodep) && !nodep->classMethod()) {
|
|
|
|
|
m_statep->checkPurity(nodep);
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeFTask* const clonedFuncp = nodep->cloneTree(false);
|
2020-08-24 01:37:56 +02:00
|
|
|
if (nodep->isConstructor()) m_statep->remapFuncClassp(nodep, clonedFuncp);
|
|
|
|
|
|
2021-11-26 23:55:36 +01:00
|
|
|
AstCFunc* const cfuncp = makeUserFunc(clonedFuncp, m_statep->ftaskNoInline(nodep));
|
2017-12-17 22:28:58 +01:00
|
|
|
if (cfuncp) {
|
|
|
|
|
nodep->addNextHere(cfuncp);
|
|
|
|
|
if (nodep->dpiImport() || m_statep->ftaskNoInline(nodep)) {
|
|
|
|
|
m_statep->ftaskCFuncp(nodep, cfuncp);
|
|
|
|
|
}
|
|
|
|
|
iterateIntoFTask(clonedFuncp); // Do the clone too
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-10-11 17:41:42 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Any variables inside the function still have varscopes pointing to them.
|
|
|
|
|
// We're going to delete the vars, so delete the varscopes.
|
|
|
|
|
if (nodep->isFunction()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(nodep->fvarp(), Var)) {
|
|
|
|
|
AstVarScope* const vscp = m_statep->findVarScope(m_scopep, portp);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " funcremovevsc " << vscp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(vscp->unlinkFrBack()), vscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode *nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = stmtp->nextp();
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
|
|
|
|
AstVarScope* const vscp = m_statep->findVarScope(m_scopep, portp);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, " funcremovevsc " << vscp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(vscp->unlinkFrBack()), vscp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Just push for deletion, as other references to func may
|
|
|
|
|
// remain until visitor exits
|
|
|
|
|
nodep->unlinkFrBack();
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-09-05 22:06:23 +02:00
|
|
|
}
|
2024-08-02 14:29:05 +02:00
|
|
|
void visit(AstNodeForeach* nodep) override { // LCOV_EXCL_LINE
|
|
|
|
|
nodep->v3fatalSrc(
|
|
|
|
|
"Foreach statements should have been converted to while statements in V3Begin.cpp");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeStmt* nodep) override {
|
2025-07-27 16:30:19 +02:00
|
|
|
VL_RESTORER(m_insStmtp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_insStmtp = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstStmtExpr* nodep) override {
|
2025-07-27 16:30:19 +02:00
|
|
|
VL_RESTORER(m_insStmtp);
|
2022-10-12 11:19:21 +02:00
|
|
|
m_insStmtp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (!nodep->exprp()) VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstSenItem* nodep) override {
|
2022-08-19 20:18:38 +02:00
|
|
|
UASSERT_OBJ(!m_inSensesp, nodep, "Senitem under senitem?");
|
|
|
|
|
VL_RESTORER(m_inSensesp);
|
|
|
|
|
m_inSensesp = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2006-08-26 13:35:28 +02:00
|
|
|
TaskVisitor(AstNetlist* nodep, TaskStateVisitor* statep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_statep{statep} {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-01-20 20:24:09 +01:00
|
|
|
~TaskVisitor() {
|
|
|
|
|
V3Stats::addStat("Optimizations, Functions inlined", m_statInlines);
|
|
|
|
|
V3Stats::addStat("Optimizations, Hierarchical DPI wrappers with costs",
|
|
|
|
|
m_statHierDpisWithCosts);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Task class functions
|
|
|
|
|
|
2023-09-11 15:01:09 +02:00
|
|
|
const char* const V3Task::s_dpiTemporaryVarSuffix = "__Vcvt";
|
|
|
|
|
|
2023-09-16 23:37:25 +02:00
|
|
|
V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
|
2024-04-27 01:26:21 +02:00
|
|
|
V3TaskConnectState* statep, bool makeChanges) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Output list will be in order of the port declaration variables (so
|
|
|
|
|
// func calls are made right in C)
|
2020-08-15 16:12:55 +02:00
|
|
|
// Missing pin/expr? We return (pinvar, nullptr)
|
2009-12-01 00:36:31 +01:00
|
|
|
// Extra pin/expr? We clean it up
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "taskConnects " << nodep);
|
2021-03-12 23:26:53 +01:00
|
|
|
std::map<const std::string, int> nameToIndex;
|
2009-12-01 00:36:31 +01:00
|
|
|
V3TaskConnects tconnects;
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
|
2009-12-01 00:36:31 +01:00
|
|
|
|
|
|
|
|
// Find ports
|
2013-08-18 02:34:49 +02:00
|
|
|
int tpinnum = 0;
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVar* sformatp = nullptr;
|
2020-04-12 14:26:14 +02:00
|
|
|
for (AstNode* stmtp = taskStmtsp; stmtp; stmtp = stmtp->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstVar* const portp = VN_CAST(stmtp, Var)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (portp->isIO()) {
|
2023-10-28 12:24:04 +02:00
|
|
|
tconnects.emplace_back(portp, static_cast<AstArg*>(nullptr));
|
|
|
|
|
nameToIndex.emplace(portp->name(), tpinnum); // For name based connections
|
2024-04-27 01:26:21 +02:00
|
|
|
++tpinnum;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (portp->attrSFormat()) {
|
|
|
|
|
sformatp = portp;
|
|
|
|
|
} else if (sformatp) {
|
2020-04-12 14:26:14 +02:00
|
|
|
portp->v3error("/*verilator sformat*/ can only be applied to last argument of "
|
|
|
|
|
"a function");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-01 00:36:31 +01:00
|
|
|
}
|
|
|
|
|
|
2013-08-18 02:34:49 +02:00
|
|
|
// Find pins
|
2009-12-01 00:36:31 +01:00
|
|
|
int ppinnum = 0;
|
2013-08-18 02:34:49 +02:00
|
|
|
bool reorganize = false;
|
2020-04-12 14:26:14 +02:00
|
|
|
for (AstNode *nextp, *pinp = nodep->pinsp(); pinp; pinp = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = pinp->nextp();
|
2024-07-12 16:18:18 +02:00
|
|
|
if (VN_IS(pinp, With)) continue;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstArg* const argp = VN_AS(pinp, Arg);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(argp, pinp, "Non-arg under ftask reference");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (argp->name() != "") {
|
|
|
|
|
// By name
|
2020-08-16 17:43:49 +02:00
|
|
|
const auto it = nameToIndex.find(argp->name());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (it == nameToIndex.end()) {
|
2024-04-27 01:26:21 +02:00
|
|
|
if (makeChanges) {
|
|
|
|
|
pinp->v3error("No such argument " << argp->prettyNameQ()
|
|
|
|
|
<< " in function call to "
|
|
|
|
|
<< nodep->taskp()->prettyTypeName());
|
|
|
|
|
// We'll just delete it; seems less error prone than making a false argument
|
|
|
|
|
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2024-04-27 01:26:21 +02:00
|
|
|
if (tconnects[it->second].second && makeChanges) {
|
2020-04-12 14:26:14 +02:00
|
|
|
pinp->v3error("Duplicate argument " << argp->prettyNameQ()
|
2020-04-15 13:58:34 +02:00
|
|
|
<< " in function call to "
|
|
|
|
|
<< nodep->taskp()->prettyTypeName());
|
2025-09-11 13:01:36 +02:00
|
|
|
tconnects[it->second].second->unlinkFrBack()->deleteTree();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
tconnects[it->second].second = argp;
|
|
|
|
|
reorganize = true;
|
|
|
|
|
}
|
|
|
|
|
} else { // By pin number
|
2024-08-13 20:20:31 +02:00
|
|
|
if (nodep->taskp()->prettyName() == "randomize") {
|
|
|
|
|
// Arguments to randomize() are special, will be handled in V3Randomize
|
|
|
|
|
} else if (ppinnum >= tpinnum) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (sformatp) {
|
2023-10-28 12:24:04 +02:00
|
|
|
tconnects.emplace_back(sformatp, static_cast<AstArg*>(nullptr));
|
2019-05-19 22:13:13 +02:00
|
|
|
tconnects[ppinnum].second = argp;
|
2024-04-27 01:26:21 +02:00
|
|
|
++tpinnum;
|
|
|
|
|
} else if (makeChanges) {
|
2019-05-19 22:13:13 +02:00
|
|
|
pinp->v3error("Too many arguments in function call to "
|
2020-04-12 14:26:14 +02:00
|
|
|
<< nodep->taskp()->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
// We'll just delete it; seems less error prone than making a false argument
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
tconnects[ppinnum].second = argp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-27 01:26:21 +02:00
|
|
|
++ppinnum;
|
2009-12-01 00:36:31 +01:00
|
|
|
}
|
2012-03-20 21:01:53 +01:00
|
|
|
|
2024-04-27 01:26:21 +02:00
|
|
|
if (!makeChanges) return tconnects;
|
|
|
|
|
|
2013-08-18 02:34:49 +02:00
|
|
|
// Connect missing ones
|
2024-04-27 01:26:21 +02:00
|
|
|
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
|
2020-04-12 14:26:14 +02:00
|
|
|
for (int i = 0; i < tpinnum; ++i) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const portp = tconnects[i].first;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!tconnects[i].second || !tconnects[i].second->exprp()) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* newvaluep = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!portp->valuep()) {
|
2020-04-12 14:26:14 +02:00
|
|
|
nodep->v3error("Missing argument on non-defaulted argument "
|
|
|
|
|
<< portp->prettyNameQ() << " in function call to "
|
|
|
|
|
<< nodep->taskp()->prettyTypeName());
|
2022-11-20 23:40:38 +01:00
|
|
|
newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0};
|
2023-07-21 09:27:00 +02:00
|
|
|
} else if (AstFuncRef* const funcRefp = VN_CAST(portp->valuep(), FuncRef)) {
|
|
|
|
|
const AstNodeFTask* const funcp = funcRefp->taskp();
|
2023-10-31 13:15:54 +01:00
|
|
|
if (funcp->classMethod() && funcp->isStatic()) newvaluep = funcRefp;
|
2023-07-21 09:27:00 +02:00
|
|
|
} else if (AstConst* const constp = VN_CAST(portp->valuep(), Const)) {
|
|
|
|
|
newvaluep = constp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!newvaluep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// The default value for this port might be a constant
|
|
|
|
|
// expression that hasn't been folded yet. Try folding it
|
|
|
|
|
// now; we don't have much to lose if it fails.
|
2023-09-16 23:37:25 +02:00
|
|
|
newvaluep = V3Const::constifyEdit(VN_AS(portp->valuep(), NodeExpr));
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(newvaluep, Const)) {
|
2023-09-16 23:37:25 +02:00
|
|
|
if (statep) {
|
|
|
|
|
portp->pinNum(i + 1); // Make sure correct, will use to build name
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "taskConnects arg wrapper needed " << portp->valuep());
|
2023-09-16 23:37:25 +02:00
|
|
|
argWrap.emplace(portp);
|
|
|
|
|
} else { // statep = nullptr, called too late or otherwise to handle args
|
|
|
|
|
// Problem otherwise is we might have a varref, task
|
|
|
|
|
// call, or something else that only makes sense in the
|
|
|
|
|
// domain of the function (or class containing the method),
|
|
|
|
|
// versus that of the callee.
|
|
|
|
|
nodep->v3warn(
|
|
|
|
|
E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Non-constant default value in missing argument "
|
|
|
|
|
<< portp->prettyNameQ() << " in function call to "
|
|
|
|
|
<< nodep->taskp()->prettyTypeName());
|
|
|
|
|
newvaluep = new AstConst{nodep->fileline(), AstConst::Unsized32{}, 0};
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-09-09 23:39:44 +02:00
|
|
|
newvaluep = newvaluep->backp() ? newvaluep->cloneTree(true) : newvaluep;
|
2019-05-19 22:13:13 +02:00
|
|
|
// To avoid problems with callee needing to know to deleteTree
|
|
|
|
|
// or not, we make this into a pin
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Default pin for " << portp);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstArg* const newp = new AstArg{nodep->fileline(), portp->name(), newvaluep};
|
2020-08-15 16:12:55 +02:00
|
|
|
if (tconnects[i].second) { // Have a "nullptr" pin already defined for it
|
2020-01-18 16:29:49 +01:00
|
|
|
VL_DO_CLEAR(tconnects[i].second->unlinkFrBack()->deleteTree(),
|
2020-08-15 16:12:55 +02:00
|
|
|
tconnects[i].second = nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
tconnects[i].second = newp;
|
|
|
|
|
reorganize = true;
|
|
|
|
|
}
|
2020-04-12 14:26:14 +02:00
|
|
|
if (tconnects[i].second) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Connect " << portp << " -> " << tconnects[i].second);
|
2020-04-12 14:26:14 +02:00
|
|
|
} else {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Connect " << portp << " -> NONE");
|
2020-04-12 14:26:14 +02:00
|
|
|
}
|
2013-08-18 02:34:49 +02:00
|
|
|
}
|
|
|
|
|
|
2024-04-27 01:26:21 +02:00
|
|
|
for (const auto& tconnect : tconnects) {
|
|
|
|
|
AstArg* const argp = tconnect.second;
|
|
|
|
|
argp->name(""); // Can forget name as will add back in pin order
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-18 02:34:49 +02:00
|
|
|
if (reorganize) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// To simplify downstream, put argument list back into pure pinnumber ordering
|
2020-04-12 14:26:14 +02:00
|
|
|
while (nodep->pinsp()) {
|
|
|
|
|
// Must unlink each pin, not all pins linked together as one list
|
|
|
|
|
nodep->pinsp()->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < tpinnum; ++i) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstArg* const argp = tconnects[i].second;
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(argp, nodep, "Lost argument in func conversion");
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->addPinsp(argp);
|
|
|
|
|
}
|
2013-08-18 02:34:49 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-17 17:06:14 +02:00
|
|
|
if (debug() >= 9) { // LCOV_EXCL_START
|
2022-11-27 14:31:22 +01:00
|
|
|
nodep->dumpTree("- ftref-out: ");
|
2020-04-12 14:26:14 +02:00
|
|
|
for (int i = 0; i < tpinnum; ++i) {
|
2023-09-16 23:37:25 +02:00
|
|
|
UINFO(0, " pin " << i << " pin=" << cvtToHex(tconnects[i].first)
|
2025-05-23 02:29:32 +02:00
|
|
|
<< " conn=" << cvtToHex(tconnects[i].second));
|
2018-10-14 22:25:36 +02:00
|
|
|
}
|
2020-05-23 16:34:58 +02:00
|
|
|
} // LCOV_EXCL_STOP
|
2023-09-16 23:37:25 +02:00
|
|
|
|
|
|
|
|
if (!argWrap.empty()) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Arg wrapper generation " << nodep);
|
2023-09-16 23:37:25 +02:00
|
|
|
// Create wrapper function with default argument settings.
|
|
|
|
|
// Needed because the default needs symbol table of the called function.
|
|
|
|
|
taskConnectWrap(nodep, tconnects, statep, argWrap);
|
|
|
|
|
// Regenerate all connections, this time connecting to the wrapper
|
|
|
|
|
return taskConnects(nodep, nodep->taskp()->stmtsp(),
|
|
|
|
|
// statep null, so can't recurse forever
|
|
|
|
|
nullptr);
|
|
|
|
|
}
|
2009-12-01 00:36:31 +01:00
|
|
|
return tconnects;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-16 23:37:25 +02:00
|
|
|
void V3Task::taskConnectWrap(AstNodeFTaskRef* nodep, const V3TaskConnects& tconnects,
|
|
|
|
|
V3TaskConnectState* statep, const std::set<const AstVar*>& argWrap) {
|
|
|
|
|
statep->setDidWrap();
|
|
|
|
|
// Make wrapper name such that is same iff same args are defaulted
|
|
|
|
|
std::string newname = nodep->name() + "__Vtcwrap";
|
|
|
|
|
for (const AstVar* varp : argWrap) newname += "_" + cvtToStr(varp->pinNum());
|
2023-10-28 14:38:02 +02:00
|
|
|
const auto pair = statep->wrapMap().emplace(std::piecewise_construct,
|
|
|
|
|
std::forward_as_tuple(nodep->taskp(), newname),
|
|
|
|
|
std::forward_as_tuple(nullptr));
|
|
|
|
|
if (pair.second) {
|
|
|
|
|
pair.first->second = taskConnectWrapNew(nodep->taskp(), newname, tconnects, argWrap);
|
2023-09-16 23:37:25 +02:00
|
|
|
}
|
2023-10-28 14:38:02 +02:00
|
|
|
AstNodeFTask* const newTaskp = pair.first->second;
|
2023-09-16 23:37:25 +02:00
|
|
|
|
|
|
|
|
// Remove the defaulted arguments from original outside call
|
|
|
|
|
for (const auto& tconnect : tconnects) {
|
|
|
|
|
const AstVar* const portp = tconnect.first;
|
|
|
|
|
AstArg* const argp = tconnect.second;
|
|
|
|
|
if (argWrap.find(portp) != argWrap.end()) { // Removed arg
|
|
|
|
|
statep->pushDeletep(argp->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Change outside call to connect to new function
|
|
|
|
|
nodep->taskp(newTaskp);
|
|
|
|
|
nodep->name(newTaskp->name());
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(9, nodep, "", "taskConnectWrap-call");
|
2023-09-16 23:37:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newname,
|
|
|
|
|
const V3TaskConnects& tconnects,
|
|
|
|
|
const std::set<const AstVar*>& argWrap) {
|
|
|
|
|
std::map<const AstVar*, AstVar*> oldNewVars; // Old -> new var mappings
|
|
|
|
|
|
|
|
|
|
AstNodeFTask* const newTaskp = taskp->cloneType(newname);
|
|
|
|
|
newTaskp->propagateAttrFrom(taskp);
|
|
|
|
|
taskp->addNextHere(newTaskp);
|
|
|
|
|
|
|
|
|
|
AstNodeFTaskRef* newCallp = nullptr;
|
|
|
|
|
AstNode* newCallInsertp = nullptr;
|
|
|
|
|
if (VN_IS(taskp, Func)) {
|
|
|
|
|
AstVar* const fvarp = VN_AS(taskp->fvarp(), Var);
|
2025-09-05 04:18:46 +02:00
|
|
|
UASSERT_OBJ(fvarp, taskp, "FuncRef without fvar");
|
2023-09-16 23:37:25 +02:00
|
|
|
AstVar* const newFVarp = fvarp->cloneTree(true);
|
|
|
|
|
oldNewVars.emplace(fvarp, newFVarp);
|
|
|
|
|
newFVarp->name(newTaskp->name());
|
|
|
|
|
newTaskp->fvarp(newFVarp);
|
|
|
|
|
newTaskp->dtypeFrom(newFVarp);
|
2024-12-15 15:15:49 +01:00
|
|
|
newCallp = new AstFuncRef{taskp->fileline(), VN_AS(taskp, Func), nullptr};
|
2023-09-16 23:37:25 +02:00
|
|
|
newCallInsertp
|
|
|
|
|
= new AstAssign{taskp->fileline(),
|
|
|
|
|
new AstVarRef{fvarp->fileline(), newFVarp, VAccess::WRITE}, newCallp};
|
|
|
|
|
newCallInsertp->dtypeFrom(newFVarp);
|
|
|
|
|
} else if (VN_IS(taskp, Task)) {
|
2024-12-15 15:15:49 +01:00
|
|
|
newCallp = new AstTaskRef{taskp->fileline(), VN_AS(taskp, Task), nullptr};
|
2023-09-16 23:37:25 +02:00
|
|
|
newCallInsertp = new AstStmtExpr{taskp->fileline(), newCallp};
|
|
|
|
|
} else {
|
|
|
|
|
taskp->v3fatalSrc("Unsupported: Non-constant default value in missing argument in a "
|
|
|
|
|
<< taskp->prettyTypeName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create wrapper's ports matching original's
|
|
|
|
|
for (const auto& tconnect : tconnects) {
|
|
|
|
|
AstVar* const portp = tconnect.first;
|
|
|
|
|
AstVar* newPortp;
|
|
|
|
|
if (argWrap.find(portp) == argWrap.end()) { // Not removed arg
|
|
|
|
|
newPortp = new AstVar{portp->fileline(), portp->varType(), portp->name(), portp};
|
|
|
|
|
newPortp->propagateWrapAttrFrom(portp);
|
|
|
|
|
newPortp->funcLocal(true);
|
|
|
|
|
if (newPortp->valuep()) newPortp->valuep()->unlinkFrBack()->deleteTree();
|
|
|
|
|
newTaskp->addStmtsp(newPortp);
|
|
|
|
|
} else { // Defaulting arg
|
|
|
|
|
AstNodeExpr* const valuep = VN_AS(portp->valuep(), NodeExpr);
|
|
|
|
|
// Create local temporary
|
|
|
|
|
newPortp = new AstVar{portp->fileline(), VVarType::BLOCKTEMP, portp->name(),
|
|
|
|
|
portp->dtypep()};
|
|
|
|
|
newPortp->propagateAttrFrom(portp);
|
|
|
|
|
newPortp->funcLocal(true);
|
|
|
|
|
newTaskp->addStmtsp(newPortp);
|
|
|
|
|
// Runtime-assign it to the default
|
2024-04-18 18:53:23 +02:00
|
|
|
if (!VN_IS(valuep, EmptyQueue)) {
|
|
|
|
|
AstAssign* const newAssignp
|
|
|
|
|
= new AstAssign{valuep->fileline(),
|
|
|
|
|
new AstVarRef{valuep->fileline(), newPortp, VAccess::WRITE},
|
|
|
|
|
valuep->cloneTree(true)};
|
|
|
|
|
newTaskp->addStmtsp(newAssignp);
|
|
|
|
|
}
|
2023-09-16 23:37:25 +02:00
|
|
|
}
|
|
|
|
|
oldNewVars.emplace(portp, newPortp);
|
2023-11-14 23:02:13 +01:00
|
|
|
const VAccess pinAccess = portp->isWritable() ? VAccess::WRITE : VAccess::READ;
|
|
|
|
|
AstArg* const newArgp = new AstArg{portp->fileline(), portp->name(),
|
|
|
|
|
new AstVarRef{portp->fileline(), newPortp, pinAccess}};
|
2023-09-16 23:37:25 +02:00
|
|
|
newCallp->addPinsp(newArgp);
|
|
|
|
|
}
|
|
|
|
|
// Create wrapper call to original, passing arguments, adding setting of return value
|
|
|
|
|
newTaskp->addStmtsp(newCallInsertp);
|
|
|
|
|
// Replace any varref's to original to new ports (e.g. in argument equations)
|
|
|
|
|
newTaskp->foreach([=](AstVarRef* refp) {
|
|
|
|
|
const auto it = oldNewVars.find(refp->varp());
|
|
|
|
|
if (it != oldNewVars.end()) refp->varp(it->second);
|
|
|
|
|
});
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(9, newTaskp, "", "taskConnectWrap-new");
|
2023-09-16 23:37:25 +02:00
|
|
|
return newTaskp;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
string V3Task::assignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix,
|
|
|
|
|
const string& toSuffix, const string& frPrefix) {
|
2019-10-09 12:47:26 +02:00
|
|
|
// Create assignment from internal format into DPI temporary
|
2020-11-19 14:02:58 +01:00
|
|
|
// Internal representation is scalar, 1D, or multi-dimensional array (similar to SV)
|
|
|
|
|
// DPI temporary is scalar or 1D array (if unpacked array)
|
2019-10-09 12:47:26 +02:00
|
|
|
string stmt;
|
|
|
|
|
string ket;
|
|
|
|
|
// Someday we'll have better type support, and this can make variables and casts.
|
|
|
|
|
// But for now, we'll just text-bash it.
|
2021-06-21 00:32:57 +02:00
|
|
|
const string frName = frPrefix + portp->name() + frSuffix;
|
|
|
|
|
const string toName = portp->name() + toSuffix;
|
2025-06-10 14:17:26 +02:00
|
|
|
const string idx = portp->name() + "__Vidx";
|
2020-11-19 14:02:58 +01:00
|
|
|
size_t unpackSize = 1; // non-unpacked array is treated as size 1
|
|
|
|
|
int unpackDim = 0;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstUnpackArrayDType* const unpackp
|
|
|
|
|
= VN_CAST(portp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
2020-11-19 14:02:58 +01:00
|
|
|
unpackSize = unpackp->arrayUnpackedElements();
|
|
|
|
|
unpackDim = unpackp->dimensions(false).second;
|
|
|
|
|
if (unpackDim > 0) UASSERT_OBJ(unpackSize > 0, portp, "size must be greater than 0");
|
|
|
|
|
}
|
|
|
|
|
if (portp->basicp()->isDpiBitVec() || portp->basicp()->isDpiLogicVec()) {
|
|
|
|
|
const bool isBit = portp->basicp()->isDpiBitVec();
|
2025-06-10 14:17:26 +02:00
|
|
|
const bool needsFor = unpackSize > 1;
|
|
|
|
|
if (needsFor) {
|
|
|
|
|
stmt = "for (size_t " + idx + " = 0; " + idx + " < " + cvtToStr(unpackSize) + "; ++"
|
|
|
|
|
+ idx + ") ";
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-28 00:46:59 +01:00
|
|
|
stmt += (isBit ? "VL_SET_SVBV_"s : "VL_SET_SVLV_"s)
|
|
|
|
|
+ portp->dtypep()->skipRefp()->charIQWN() + "(" + cvtToStr(portp->width()) + ", ";
|
2025-06-10 14:17:26 +02:00
|
|
|
stmt += toName;
|
|
|
|
|
if (needsFor) {
|
|
|
|
|
stmt += " + " + cvtToStr(portp->dtypep()->skipRefp()->widthWords()) + " * " + idx;
|
|
|
|
|
}
|
|
|
|
|
stmt += ", ";
|
2020-11-19 14:02:58 +01:00
|
|
|
if (unpackDim > 0) { // Access multi-dimensional array as a 1D array
|
|
|
|
|
stmt += "(&" + frName;
|
|
|
|
|
for (int i = 0; i < unpackDim; ++i) stmt += "[0]";
|
2025-06-10 14:17:26 +02:00
|
|
|
stmt += ")[" + (needsFor ? idx : "0") + "])";
|
2020-11-19 14:02:58 +01:00
|
|
|
} else {
|
|
|
|
|
stmt += frName + ")";
|
|
|
|
|
}
|
2019-10-09 12:47:26 +02:00
|
|
|
} else {
|
2020-11-19 14:02:58 +01:00
|
|
|
const bool isChandle
|
2022-01-02 19:56:40 +01:00
|
|
|
= portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::CHANDLE;
|
2020-11-19 14:02:58 +01:00
|
|
|
const bool isString
|
2022-01-02 19:56:40 +01:00
|
|
|
= portp->basicp() && portp->basicp()->keyword() == VBasicDTypeKwd::STRING;
|
2025-06-10 14:17:26 +02:00
|
|
|
const string unpackAt = unpackSize > 1 ? "[" + idx + "]" : "[0]";
|
2020-11-19 14:02:58 +01:00
|
|
|
if (unpackDim > 0) {
|
2025-06-10 14:17:26 +02:00
|
|
|
const string forStmt = unpackSize > 1
|
|
|
|
|
? "for (size_t " + idx + " = 0; " + idx + " < "
|
|
|
|
|
+ cvtToStr(unpackSize) + "; ++" + idx + ") "
|
|
|
|
|
: "";
|
|
|
|
|
stmt += forStmt + toName + unpackAt;
|
2020-11-19 14:02:58 +01:00
|
|
|
} else {
|
|
|
|
|
if (isPtr) stmt += "*"; // DPI outputs are pointers
|
|
|
|
|
stmt += toName;
|
|
|
|
|
}
|
|
|
|
|
stmt += " = ";
|
|
|
|
|
if (isChandle) {
|
2019-10-09 12:47:26 +02:00
|
|
|
stmt += "VL_CVT_Q_VP(";
|
|
|
|
|
ket += ")";
|
|
|
|
|
}
|
2020-11-19 14:02:58 +01:00
|
|
|
if (unpackDim > 0) {
|
|
|
|
|
stmt += "(&" + frName;
|
|
|
|
|
for (int i = 0; i < unpackDim; ++i) stmt += "[0]";
|
2025-06-10 14:17:26 +02:00
|
|
|
stmt += ")" + unpackAt;
|
2020-11-19 14:02:58 +01:00
|
|
|
} else {
|
|
|
|
|
stmt += frName;
|
2019-10-09 12:47:26 +02:00
|
|
|
}
|
2020-11-19 14:02:58 +01:00
|
|
|
if (isString) stmt += ".c_str()";
|
2019-10-09 12:47:26 +02:00
|
|
|
}
|
2025-09-26 14:25:47 +02:00
|
|
|
stmt += ket + ";";
|
2019-10-09 12:47:26 +02:00
|
|
|
return stmt;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-19 10:44:33 +02:00
|
|
|
// Create assignment from DPI temporary into internal format
|
|
|
|
|
// DPI temporary is scalar or 1D array (if unpacked array)
|
|
|
|
|
// Internal representation is scalar, 1D, or multi-dimensional array (similar to SV)
|
|
|
|
|
std::string V3Task::assignDpiToInternal(const std::string& lhsName, AstVar* varp) {
|
|
|
|
|
std::string cvt;
|
|
|
|
|
bool useSvVec;
|
|
|
|
|
std::tie(cvt, useSvVec) = TaskDpiUtils::dpiToInternalCvtStmt(varp);
|
|
|
|
|
const std::vector<std::pair<int, int>> strides = TaskDpiUtils::unpackDimsAndStrides(varp);
|
|
|
|
|
const int total = strides.empty() ? 1 : strides[0].first * strides[0].second;
|
2020-12-09 00:29:45 +01:00
|
|
|
const int widthWords = varp->basicp()->widthWords();
|
|
|
|
|
string statements;
|
|
|
|
|
for (int i = 0; i < total; ++i) {
|
2025-10-19 10:44:33 +02:00
|
|
|
std::string lhs = lhsName;
|
2020-12-09 00:29:45 +01:00
|
|
|
// extract a scalar from multi-dimensional array (internal format)
|
2025-10-19 10:44:33 +02:00
|
|
|
for (const auto& stride : strides) {
|
|
|
|
|
lhs += "[" + std::to_string((i / stride.second) % stride.first) + "]";
|
2020-12-09 00:29:45 +01:00
|
|
|
}
|
|
|
|
|
// extract a scalar from DPI temporary var that is scalar or 1D array
|
2025-10-19 10:44:33 +02:00
|
|
|
if (useSvVec) {
|
|
|
|
|
const std::string offset = std::to_string(i * widthWords);
|
|
|
|
|
statements += cvt + " " + lhs + ", " + varp->name() + " + " + offset + ");\n";
|
2020-12-09 00:29:45 +01:00
|
|
|
} else {
|
2025-10-19 10:44:33 +02:00
|
|
|
const std::string elem = strides.empty() ? "" : "[" + std::to_string(i) + "]";
|
|
|
|
|
statements += lhs + " = " + cvt + varp->name() + elem + ")" + ";\n";
|
2020-12-09 00:29:45 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return statements;
|
2019-10-09 12:47:26 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void V3Task::taskAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
2021-07-12 00:42:01 +02:00
|
|
|
TaskStateVisitor visitors{nodep};
|
2021-11-26 23:55:36 +01:00
|
|
|
const TaskVisitor visitor{nodep, &visitors};
|
2018-03-10 18:57:50 +01:00
|
|
|
} // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("task", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|