2012-04-13 01:08:20 +00:00
|
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Add temporaries, such as for task nodes
|
|
|
|
|
//
|
2008-04-25 12:14:27 +00:00
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2013-01-01 14:42:59 +00:00
|
|
|
|
// Copyright 2003-2013 by Wilson Snyder. This program is free software; you can
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 21:07:57 +00:00
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
// Verilator is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Task's Transformations:
|
2008-06-10 01:25:10 +00:00
|
|
|
|
//
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Each module:
|
|
|
|
|
// Look for TASKREF
|
|
|
|
|
// Insert task's statements into the referrer
|
|
|
|
|
// Look for TASKs
|
|
|
|
|
// Remove them, they're inlined
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2006-12-18 19:20:45 +00:00
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2008-06-30 17:11:25 +00:00
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdarg>
|
2006-08-26 11:35:28 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <map>
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Task.h"
|
|
|
|
|
#include "V3Inst.h"
|
|
|
|
|
#include "V3Ast.h"
|
2006-08-30 21:07:55 +00:00
|
|
|
|
#include "V3EmitCBase.h"
|
2006-10-11 15:41:42 +00:00
|
|
|
|
#include "V3Graph.h"
|
2008-03-28 21:55:23 +00:00
|
|
|
|
#include "V3LinkLValue.h"
|
2006-10-11 15:41:42 +00:00
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Graph subclasses
|
|
|
|
|
|
|
|
|
|
class TaskBaseVertex : public V3GraphVertex {
|
|
|
|
|
AstNode* m_impurep; // Node causing impure function w/ outside references
|
|
|
|
|
bool m_noInline; // Marked with pragma
|
|
|
|
|
public:
|
|
|
|
|
TaskBaseVertex(V3Graph* graphp)
|
|
|
|
|
: V3GraphVertex(graphp), m_impurep(NULL), m_noInline(false) {}
|
|
|
|
|
virtual ~TaskBaseVertex() {}
|
|
|
|
|
bool pure() const { return m_impurep==NULL; }
|
|
|
|
|
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; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TaskFTaskVertex : public TaskBaseVertex {
|
|
|
|
|
// Every task gets a vertex, and we link tasks together based on funcrefs.
|
|
|
|
|
AstNodeFTask* m_nodep;
|
2008-11-25 12:57:02 +00:00
|
|
|
|
AstCFunc* m_cFuncp;
|
2006-10-11 15:41:42 +00:00
|
|
|
|
public:
|
|
|
|
|
TaskFTaskVertex(V3Graph* graphp, AstNodeFTask* nodep)
|
2008-11-25 12:57:02 +00:00
|
|
|
|
: TaskBaseVertex(graphp), m_nodep(nodep) {
|
|
|
|
|
m_cFuncp=NULL;
|
|
|
|
|
}
|
2006-10-11 15:41:42 +00:00
|
|
|
|
virtual ~TaskFTaskVertex() {}
|
|
|
|
|
AstNodeFTask* nodep() const { return m_nodep; }
|
|
|
|
|
virtual string name() const { return nodep()->name(); }
|
|
|
|
|
virtual string dotColor() const { return pure() ? "black" : "red"; }
|
2008-11-25 12:57:02 +00:00
|
|
|
|
AstCFunc* cFuncp() const { return m_cFuncp; }
|
|
|
|
|
void cFuncp(AstCFunc* nodep) { m_cFuncp=nodep; }
|
2006-10-11 15:41:42 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TaskCodeVertex : public TaskBaseVertex {
|
|
|
|
|
// Top vertex for all calls not under another task
|
|
|
|
|
public:
|
|
|
|
|
TaskCodeVertex(V3Graph* graphp)
|
|
|
|
|
: TaskBaseVertex(graphp) {}
|
|
|
|
|
virtual ~TaskCodeVertex() {}
|
|
|
|
|
virtual string name() const { return "*CODE*"; }
|
|
|
|
|
virtual string dotColor() const { return "green"; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class TaskEdge : public V3GraphEdge {
|
|
|
|
|
public:
|
|
|
|
|
TaskEdge(V3Graph* graphp, TaskBaseVertex* fromp, TaskBaseVertex* top)
|
|
|
|
|
: V3GraphEdge(graphp, fromp, top, 1, false) {}
|
|
|
|
|
virtual ~TaskEdge() {}
|
|
|
|
|
virtual string dotLabel() const { return "w"+cvtToStr(weight()); }
|
|
|
|
|
};
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class TaskStateVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Output:
|
|
|
|
|
// AstNodeFTask::user3p // AstScope* this FTask is under
|
2006-10-11 15:41:42 +00:00
|
|
|
|
// AstNodeFTask::user4p // GraphFTaskVertex* this FTask is under
|
|
|
|
|
// AstVar::user4p // GraphFTaskVertex* this variable is declared in
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
2008-11-25 14:03:49 +00:00
|
|
|
|
AstUser3InUse m_inuser3;
|
|
|
|
|
AstUser4InUse m_inuser4;
|
2008-11-25 12:57:02 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// TYPES
|
|
|
|
|
typedef std::map<pair<AstScope*,AstVar*>,AstVarScope*> VarToScopeMap;
|
|
|
|
|
// MEMBERS
|
|
|
|
|
VarToScopeMap m_varToScopeMap; // Map for Var -> VarScope mappings
|
2006-09-05 20:06:23 +00:00
|
|
|
|
AstAssignW* m_assignwp; // Current assignment
|
2006-10-11 15:41:42 +00:00
|
|
|
|
V3Graph m_callGraph; // Task call graph
|
|
|
|
|
TaskBaseVertex* m_curVxp; // Current vertex we're adding to
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
public:
|
|
|
|
|
// METHODS
|
|
|
|
|
AstScope* getScope(AstNodeFTask* nodep) {
|
|
|
|
|
AstScope* scopep = nodep->user3p()->castNode()->castScope();
|
|
|
|
|
if (!scopep) nodep->v3fatalSrc("No scope for function");
|
|
|
|
|
return scopep;
|
|
|
|
|
}
|
|
|
|
|
AstVarScope* findVarScope(AstScope* scopep, AstVar* nodep) {
|
|
|
|
|
VarToScopeMap::iterator iter = m_varToScopeMap.find(make_pair(scopep,nodep));
|
|
|
|
|
if (iter == m_varToScopeMap.end()) nodep->v3fatalSrc("No scope for var");
|
|
|
|
|
return iter->second;
|
|
|
|
|
}
|
2006-10-11 15:41:42 +00:00
|
|
|
|
bool ftaskNoInline(AstNodeFTask* nodep) {
|
|
|
|
|
return (getFTaskVertex(nodep)->noInline());
|
|
|
|
|
}
|
2008-11-25 12:57:02 +00:00
|
|
|
|
AstCFunc* ftaskCFuncp(AstNodeFTask* nodep) {
|
|
|
|
|
return (getFTaskVertex(nodep)->cFuncp());
|
|
|
|
|
}
|
|
|
|
|
void ftaskCFuncp(AstNodeFTask* nodep, AstCFunc* cfuncp) {
|
|
|
|
|
getFTaskVertex(nodep)->cFuncp(cfuncp);
|
|
|
|
|
}
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2006-10-11 15:41:42 +00:00
|
|
|
|
void checkPurity(AstNodeFTask* nodep) {
|
|
|
|
|
checkPurity(nodep, getFTaskVertex(nodep));
|
|
|
|
|
}
|
|
|
|
|
void checkPurity(AstNodeFTask* nodep, TaskBaseVertex* vxp) {
|
|
|
|
|
if (!vxp->pure()) {
|
2012-05-22 01:24:17 +00:00
|
|
|
|
nodep->v3warn(IMPURE,"Unsupported: External variable referenced by non-inlined function/task: "<<nodep->prettyName()<<endl
|
|
|
|
|
<<vxp->impureNode()->warnMore()<<"... Location of the external reference: "<<vxp->impureNode()->prettyName());
|
2006-10-11 15:41:42 +00:00
|
|
|
|
}
|
|
|
|
|
// And, we need to check all tasks this task calls
|
|
|
|
|
for (V3GraphEdge* edgep = vxp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
|
|
|
checkPurity(nodep, static_cast<TaskBaseVertex*>(edgep->top()));
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
private:
|
2008-11-25 12:57:02 +00:00
|
|
|
|
TaskFTaskVertex* getFTaskVertex(AstNodeFTask* nodep) {
|
2006-10-11 15:41:42 +00:00
|
|
|
|
if (!nodep->user4p()) {
|
|
|
|
|
nodep->user4p(new TaskFTaskVertex(&m_callGraph, nodep));
|
|
|
|
|
}
|
2008-11-25 12:57:02 +00:00
|
|
|
|
return static_cast<TaskFTaskVertex*>(nodep->user4p()->castGraphVertex());
|
2006-10-11 15:41:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstScope* nodep, AstNUser*) {
|
|
|
|
|
// 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
|
|
|
|
|
for (AstNode* stmtp = nodep->varsp(); stmtp; stmtp=stmtp->nextp()) {
|
|
|
|
|
if (AstVarScope* vscp = stmtp->castVarScope()) {
|
|
|
|
|
if (vscp->varp()->isFuncLocal()) {
|
|
|
|
|
UINFO(9," funcvsc "<<vscp<<endl);
|
|
|
|
|
m_varToScopeMap.insert(make_pair(make_pair(nodep, vscp->varp()), vscp));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Likewise, all FTask->scope mappings
|
|
|
|
|
for (AstNode* stmtp = nodep->blocksp(); stmtp; stmtp=stmtp->nextp()) {
|
|
|
|
|
if (AstNodeFTask* taskp = stmtp->castNodeFTask()) {
|
|
|
|
|
taskp->user3p(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-09-05 20:06:23 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAssignW* nodep, AstNUser*) {
|
|
|
|
|
m_assignwp = nodep;
|
2006-10-11 15:41:42 +00:00
|
|
|
|
nodep->iterateChildren(*this); nodep=NULL; // May delete nodep.
|
2006-09-05 20:06:23 +00:00
|
|
|
|
m_assignwp = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
|
|
|
|
if (m_assignwp) {
|
|
|
|
|
// Wire assigns must become always statements to deal with insertion
|
|
|
|
|
// of multiple statements. Perhaps someday make all wassigns into always's?
|
|
|
|
|
UINFO(5," IM_WireRep "<<m_assignwp<<endl);
|
2009-10-09 00:42:45 +00:00
|
|
|
|
m_assignwp->convertToAlways(); pushDeletep(m_assignwp); m_assignwp=NULL;
|
2006-09-05 20:06:23 +00:00
|
|
|
|
}
|
2006-10-11 15:41:42 +00:00
|
|
|
|
// We make multiple edges if a task is called multiple times from another task.
|
2009-11-08 02:05:02 +00:00
|
|
|
|
if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked task");
|
2006-10-11 15:41:42 +00:00
|
|
|
|
new TaskEdge (&m_callGraph, m_curVxp, getFTaskVertex(nodep->taskp()));
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
|
|
|
|
UINFO(9," TASK "<<nodep<<endl);
|
|
|
|
|
TaskBaseVertex* lastVxp = m_curVxp;
|
|
|
|
|
m_curVxp = getFTaskVertex(nodep);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (nodep->dpiImport()) m_curVxp->noInline(true);
|
2006-10-11 15:41:42 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_curVxp = lastVxp;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstPragma* nodep, AstNUser*) {
|
|
|
|
|
if (nodep->pragType() == AstPragmaType::NO_INLINE_TASK) {
|
|
|
|
|
// Just mark for the next steps, and we're done with it.
|
|
|
|
|
m_curVxp->noInline(true);
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstVar* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
nodep->user4p(m_curVxp); // Remember what task it's under
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
if (nodep->varp()->user4p() != m_curVxp) {
|
2008-07-14 14:42:58 +00:00
|
|
|
|
if (m_curVxp->pure()
|
2011-12-16 00:13:54 +00:00
|
|
|
|
&& !nodep->varp()->isXTemp()) {
|
2006-10-11 15:41:42 +00:00
|
|
|
|
m_curVxp->impure(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2006-10-11 15:41:42 +00:00
|
|
|
|
TaskStateVisitor(AstNetlist* nodep) {
|
2006-09-05 20:06:23 +00:00
|
|
|
|
m_assignwp = NULL;
|
2006-10-11 15:41:42 +00:00
|
|
|
|
m_curVxp = new TaskCodeVertex(&m_callGraph);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode::user3ClearTree();
|
2006-10-11 15:41:42 +00:00
|
|
|
|
AstNode::user4ClearTree();
|
|
|
|
|
//
|
2009-10-04 21:01:35 +00:00
|
|
|
|
nodep->accept(*this);
|
2006-10-11 15:41:42 +00:00
|
|
|
|
//
|
|
|
|
|
m_callGraph.removeRedundantEdgesSum(&TaskEdge::followAlwaysTrue);
|
|
|
|
|
m_callGraph.dumpDotFilePrefixed("task_call");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
virtual ~TaskStateVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class TaskRelinkVisitor : public AstNVisitor {
|
|
|
|
|
// Replace varrefs with new var pointer
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Input:
|
|
|
|
|
// AstVar::user2p // AstVarScope* to replace varref with
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
|
|
|
|
// Similar code in V3Inline
|
|
|
|
|
if (nodep->varp()->user2p()) { // It's being converted to a alias.
|
|
|
|
|
UINFO(9, " relinkVar "<<(void*)nodep->varp()->user2p()<<" "<<nodep<<endl);
|
|
|
|
|
AstVarScope* newvscp = nodep->varp()->user2p()->castNode()->castVarScope();
|
|
|
|
|
if (!newvscp) nodep->v3fatalSrc("Null?\n");
|
|
|
|
|
nodep->varScopep(newvscp);
|
|
|
|
|
nodep->varp(nodep->varScopep()->varp());
|
|
|
|
|
nodep->name(nodep->varp()->name());
|
|
|
|
|
}
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2009-10-04 21:01:35 +00:00
|
|
|
|
TaskRelinkVisitor(AstBegin* nodep) { // Passed temporary tree
|
|
|
|
|
nodep->accept(*this);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
virtual ~TaskRelinkVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Task state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class TaskVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Each module:
|
2008-11-25 12:57:02 +00:00
|
|
|
|
// AstNodeFTask::user // True if its been expanded
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Each funccall
|
2008-11-25 12:57:02 +00:00
|
|
|
|
// to TaskRelinkVisitor:
|
|
|
|
|
// AstVar::user2p // AstVarScope* to replace varref with
|
|
|
|
|
|
2008-11-25 14:03:49 +00:00
|
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
|
AstUser2InUse m_inuser2;
|
2006-09-05 20:06:23 +00:00
|
|
|
|
|
|
|
|
|
// TYPES
|
|
|
|
|
enum InsertMode {
|
|
|
|
|
IM_BEFORE, // Pointing at statement ref is in, insert before this
|
|
|
|
|
IM_AFTER, // Pointing at last inserted stmt, insert after
|
|
|
|
|
IM_WHILE_PRECOND // Pointing to for loop, add to body end
|
|
|
|
|
};
|
2009-12-03 11:55:29 +00:00
|
|
|
|
typedef map<string,pair<AstCFunc*,string> > DpiNames;
|
2006-09-05 20:06:23 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// STATE
|
2009-12-03 11:55:29 +00:00
|
|
|
|
TaskStateVisitor* m_statep; // Common state between visitors
|
2009-11-07 11:20:20 +00:00
|
|
|
|
AstNodeModule* m_modp; // Current module
|
2009-12-03 11:55:29 +00:00
|
|
|
|
AstTopScope* m_topScopep; // Current top scope
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstScope* m_scopep; // Current scope
|
2006-09-05 20:06:23 +00:00
|
|
|
|
InsertMode m_insMode; // How to insert
|
|
|
|
|
AstNode* m_insStmtp; // Where to insert statement
|
2006-08-26 11:35:28 +00:00
|
|
|
|
int m_modNCalls; // Incrementing func # for making symbols
|
2009-12-03 11:55:29 +00:00
|
|
|
|
DpiNames m_dpiNames; // Map of all created DPI functions
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// METHODS
|
2009-01-21 21:56:50 +00:00
|
|
|
|
static int debug() {
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
return level;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-20 13:27:00 +00:00
|
|
|
|
AstVarScope* createFuncVar(AstCFunc* funcp, const string& name, AstVar* examplep) {
|
|
|
|
|
AstVar* newvarp = new AstVar (funcp->fileline(), AstVarType::BLOCKTEMP, name,
|
|
|
|
|
examplep);
|
|
|
|
|
newvarp->funcLocal(true);
|
|
|
|
|
funcp->addInitsp(newvarp);
|
|
|
|
|
AstVarScope* newvscp = new AstVarScope(funcp->fileline(), m_scopep, newvarp);
|
|
|
|
|
m_scopep->addVarp(newvscp);
|
|
|
|
|
return newvscp;
|
|
|
|
|
}
|
2009-12-05 15:38:49 +00:00
|
|
|
|
AstVarScope* createInputVar(AstCFunc* funcp, const string& name, AstBasicDTypeKwd kwd) {
|
|
|
|
|
AstVar* newvarp = new AstVar (funcp->fileline(), AstVarType::BLOCKTEMP, name,
|
2012-05-10 02:12:57 +00:00
|
|
|
|
funcp->findBasicDType(kwd));
|
2009-12-05 15:38:49 +00:00
|
|
|
|
newvarp->funcLocal(true);
|
|
|
|
|
newvarp->combineType(AstVarType::INPUT);
|
|
|
|
|
funcp->addArgsp(newvarp);
|
|
|
|
|
AstVarScope* newvscp = new AstVarScope(funcp->fileline(), m_scopep, newvarp);
|
|
|
|
|
m_scopep->addVarp(newvscp);
|
|
|
|
|
return newvscp;
|
|
|
|
|
}
|
2006-08-27 14:51:25 +00:00
|
|
|
|
AstVarScope* createVarScope(AstVar* invarp, const string& name) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// 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.
|
|
|
|
|
AstVar* newvarp = new AstVar (invarp->fileline(), AstVarType::BLOCKTEMP,
|
2006-08-27 14:51:25 +00:00
|
|
|
|
name, invarp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
newvarp->funcLocal(false);
|
2007-01-26 21:53:03 +00:00
|
|
|
|
newvarp->propagateAttrFrom(invarp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_modp->addStmtp(newvarp);
|
|
|
|
|
AstVarScope* newvscp = new AstVarScope (newvarp->fileline(), m_scopep, newvarp);
|
|
|
|
|
m_scopep->addVarp(newvscp);
|
|
|
|
|
return newvscp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNode* createInlinedFTask(AstNodeFTaskRef* refp, string namePrefix, AstVarScope* outvscp) {
|
|
|
|
|
// outvscp is the variable for functions only, if NULL, it's a task
|
|
|
|
|
if (!refp->taskp()) refp->v3fatalSrc("Unlinked?");
|
|
|
|
|
AstNode* newbodysp = refp->taskp()->stmtsp()->cloneTree(true); // Maybe NULL
|
|
|
|
|
AstNode* beginp = new AstComment(refp->fileline(), (string)("Function: ")+refp->name());
|
|
|
|
|
if (newbodysp) beginp->addNext(newbodysp);
|
2009-11-30 23:36:31 +00:00
|
|
|
|
if (debug()>=9) { beginp->dumpTreeAndNext(cout,"-newbegi:"); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
// Create input variables
|
|
|
|
|
AstNode::user2ClearTree();
|
2009-11-30 23:36:31 +00:00
|
|
|
|
V3TaskConnects tconnects = V3Task::taskConnects(refp, beginp);
|
|
|
|
|
for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) {
|
|
|
|
|
AstVar* portp = it->first;
|
|
|
|
|
AstNode* pinp = it->second;
|
|
|
|
|
portp->unlinkFrBack(); pushDeletep(portp); // Remove it from the clone (not original)
|
|
|
|
|
if (pinp==NULL) {
|
|
|
|
|
// Too few arguments in function call
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(9, " Port "<<portp<<endl);
|
|
|
|
|
UINFO(9, " pin "<<pinp<<endl);
|
|
|
|
|
pinp->unlinkFrBack(); // Relinked to assignment below
|
|
|
|
|
//
|
2011-02-15 00:25:30 +00:00
|
|
|
|
if ((portp->isInout()||portp->isOutput()) && pinp->castConst()) {
|
|
|
|
|
pinp->v3error("Function/task output connected to constant instead of variable: "+portp->prettyName());
|
|
|
|
|
}
|
|
|
|
|
else if (portp->isInout()) {
|
2009-11-30 23:36:31 +00:00
|
|
|
|
if (AstVarRef* varrefp = pinp->castVarRef()) {
|
|
|
|
|
// Connect to this exact variable
|
|
|
|
|
AstVarScope* localVscp = varrefp->varScopep(); if (!localVscp) varrefp->v3fatalSrc("Null var scope");
|
|
|
|
|
portp->user2p(localVscp);
|
|
|
|
|
pushDeletep(pinp);
|
|
|
|
|
} else {
|
|
|
|
|
pinp->v3warn(E_TASKNSVAR,"Unsupported: Function/task input argument is not simple variable");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (portp->isOutput()) {
|
|
|
|
|
// Make output variables
|
|
|
|
|
// Correct lvalue; we didn't know when we linked
|
|
|
|
|
// This is slightly scary; are we sure no decisions were made
|
|
|
|
|
// before here based on this not being a lvalue?
|
|
|
|
|
// Doesn't seem so; V3Unknown uses it earlier, but works ok.
|
|
|
|
|
V3LinkLValue::linkLValueSet(pinp);
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2009-11-30 23:36:31 +00:00
|
|
|
|
// Even if it's referencing a varref, we still make a temporary
|
|
|
|
|
// Else task(x,x,x) might produce incorrect results
|
|
|
|
|
AstVarScope* outvscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
|
|
|
|
portp->user2p(outvscp);
|
|
|
|
|
AstAssign* assp = new AstAssign (pinp->fileline(),
|
|
|
|
|
pinp,
|
|
|
|
|
new AstVarRef(outvscp->fileline(), outvscp, false));
|
2011-10-08 00:04:15 +00:00
|
|
|
|
assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block
|
2009-11-30 23:36:31 +00:00
|
|
|
|
// Put assignment BEHIND of all other statements
|
|
|
|
|
beginp->addNext(assp);
|
|
|
|
|
}
|
|
|
|
|
else if (portp->isInput()) {
|
|
|
|
|
// Make input variable
|
|
|
|
|
AstVarScope* inVscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
|
|
|
|
portp->user2p(inVscp);
|
|
|
|
|
AstAssign* assp = new AstAssign (pinp->fileline(),
|
|
|
|
|
new AstVarRef(inVscp->fileline(), inVscp, true),
|
|
|
|
|
pinp);
|
2011-10-08 00:04:15 +00:00
|
|
|
|
assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block
|
2009-11-30 23:36:31 +00:00
|
|
|
|
// Put assignment in FRONT of all other statements
|
|
|
|
|
if (AstNode* afterp = beginp->nextp()) {
|
|
|
|
|
afterp->unlinkFrBackWithNext();
|
|
|
|
|
assp->addNext(afterp);
|
|
|
|
|
}
|
|
|
|
|
beginp->addNext(assp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (refp->pinsp()) refp->v3fatalSrc("Pin wasn't removed by above loop");
|
2006-10-11 15:41:42 +00:00
|
|
|
|
{
|
|
|
|
|
AstNode* nextstmtp;
|
2009-11-30 23:36:31 +00:00
|
|
|
|
for (AstNode* stmtp = beginp; stmtp; stmtp=nextstmtp) {
|
2006-10-11 15:41:42 +00:00
|
|
|
|
nextstmtp = stmtp->nextp();
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
2009-11-30 23:36:31 +00:00
|
|
|
|
// Any I/O variables that fell out of above loop were already linked
|
|
|
|
|
if (!portp->user2p()) {
|
2006-10-11 15:41:42 +00:00
|
|
|
|
// Move it to a new localized variable
|
2009-11-30 23:36:31 +00:00
|
|
|
|
portp->unlinkFrBack(); pushDeletep(portp); // Remove it from the clone (not original)
|
2006-10-11 15:41:42 +00:00
|
|
|
|
AstVarScope* localVscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
|
|
|
|
portp->user2p(localVscp);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Create function output variables
|
|
|
|
|
if (outvscp) {
|
|
|
|
|
//UINFO(0, "setflag on "<<funcp->fvarp()<<" to "<<outvscp<<endl);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
refp->taskp()->fvarp()->user2p(outvscp);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
// Replace variable refs
|
2009-10-04 21:01:35 +00:00
|
|
|
|
// Iteration requires a back, so put under temporary node
|
2012-03-20 20:01:53 +00:00
|
|
|
|
{
|
2009-10-04 21:01:35 +00:00
|
|
|
|
AstBegin* tempp = new AstBegin(beginp->fileline(),"[EditWrapper]",beginp);
|
|
|
|
|
TaskRelinkVisitor visit (tempp);
|
|
|
|
|
tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); tempp=NULL;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
2011-02-15 00:25:30 +00:00
|
|
|
|
if (debug()>=9) { beginp->dumpTreeAndNext(cout,"-iotask: "); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
return beginp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-11 15:41:42 +00:00
|
|
|
|
AstNode* createNonInlinedFTask(AstNodeFTaskRef* refp, string namePrefix, AstVarScope* outvscp) {
|
|
|
|
|
// outvscp is the variable for functions only, if NULL, it's a task
|
|
|
|
|
if (!refp->taskp()) refp->v3fatalSrc("Unlinked?");
|
2008-11-25 12:57:02 +00:00
|
|
|
|
AstCFunc* cfuncp = m_statep->ftaskCFuncp(refp->taskp());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
2006-10-11 15:41:42 +00:00
|
|
|
|
if (!cfuncp) refp->v3fatalSrc("No non-inline task associated with this task call?");
|
|
|
|
|
//
|
|
|
|
|
AstNode* beginp = new AstComment(refp->fileline(), (string)("Function: ")+refp->name());
|
|
|
|
|
AstCCall* ccallp = new AstCCall(refp->fileline(), cfuncp, NULL);
|
|
|
|
|
beginp->addNext(ccallp);
|
|
|
|
|
// Convert complicated outputs to temp signals
|
2009-11-30 23:36:31 +00:00
|
|
|
|
|
|
|
|
|
V3TaskConnects tconnects = V3Task::taskConnects(refp, refp->taskp()->stmtsp());
|
|
|
|
|
for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) {
|
|
|
|
|
AstVar* portp = it->first;
|
|
|
|
|
AstNode* pinp = it->second;
|
|
|
|
|
if (!pinp) {
|
|
|
|
|
// Too few arguments in function call
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(9, " Port "<<portp<<endl);
|
|
|
|
|
UINFO(9, " pin "<<pinp<<endl);
|
2011-02-15 00:25:30 +00:00
|
|
|
|
if ((portp->isInout()||portp->isOutput()) && pinp->castConst()) {
|
|
|
|
|
pinp->v3error("Function/task output connected to constant instead of variable: "+portp->prettyName());
|
|
|
|
|
}
|
|
|
|
|
else if (portp->isInout()) {
|
2009-11-30 23:36:31 +00:00
|
|
|
|
if (pinp->castVarRef()) {
|
|
|
|
|
// Connect to this exact variable
|
|
|
|
|
} else {
|
|
|
|
|
pinp->v3warn(E_TASKNSVAR,"Unsupported: Function/task input argument is not simple variable");
|
2006-10-11 15:41:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-11-30 23:36:31 +00:00
|
|
|
|
else if (portp->isOutput()) {
|
|
|
|
|
// Make output variables
|
|
|
|
|
// Correct lvalue; we didn't know when we linked
|
|
|
|
|
// This is slightly scary; are we sure no decisions were made
|
|
|
|
|
// before here based on this not being a lvalue?
|
|
|
|
|
// Doesn't seem so; V3Unknown uses it earlier, but works ok.
|
|
|
|
|
V3LinkLValue::linkLValueSet(pinp);
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2009-11-30 23:36:31 +00:00
|
|
|
|
// Even if it's referencing a varref, we still make a temporary
|
|
|
|
|
// Else task(x,x,x) might produce incorrect results
|
|
|
|
|
AstVarScope* outvscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
|
|
|
|
|
portp->user2p(outvscp);
|
|
|
|
|
pinp->replaceWith(new AstVarRef(outvscp->fileline(), outvscp, true));
|
|
|
|
|
AstAssign* assp = new AstAssign (pinp->fileline(),
|
|
|
|
|
pinp,
|
|
|
|
|
new AstVarRef(outvscp->fileline(), outvscp, false));
|
2011-10-08 00:04:15 +00:00
|
|
|
|
assp->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true); // Ok if in <= block
|
2009-11-30 23:36:31 +00:00
|
|
|
|
// Put assignment BEHIND of all other statements
|
|
|
|
|
beginp->addNext(assp);
|
|
|
|
|
}
|
2006-10-11 15:41:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// First argument is symbol table, then output if a function
|
2009-12-05 15:38:49 +00:00
|
|
|
|
bool needSyms = !refp->taskp()->dpiImport();
|
|
|
|
|
if (needSyms) ccallp->argTypes("vlSymsp");
|
|
|
|
|
|
|
|
|
|
if (refp->taskp()->dpiContext()) {
|
|
|
|
|
// __Vscopep
|
|
|
|
|
AstNode* snp = refp->scopeNamep()->unlinkFrBack(); if (!snp) refp->v3fatalSrc("Missing scoping context");
|
|
|
|
|
ccallp->addArgsp(snp);
|
|
|
|
|
// __Vfilenamep
|
|
|
|
|
ccallp->addArgsp(new AstCMath(refp->fileline(), "\""+refp->fileline()->filename()+"\"", 64, true));
|
|
|
|
|
// __Vlineno
|
|
|
|
|
ccallp->addArgsp(new AstConst(refp->fileline(), refp->fileline()->lineno()));
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-11 15:41:42 +00:00
|
|
|
|
// Create connections
|
|
|
|
|
AstNode* nextpinp;
|
|
|
|
|
for (AstNode* pinp = refp->pinsp(); pinp; pinp=nextpinp) {
|
|
|
|
|
nextpinp = pinp->nextp();
|
|
|
|
|
// Move pin to the CCall
|
|
|
|
|
pinp->unlinkFrBack();
|
|
|
|
|
ccallp->addArgsp(pinp);
|
|
|
|
|
}
|
2009-12-20 13:27:00 +00:00
|
|
|
|
|
|
|
|
|
if (outvscp) {
|
|
|
|
|
ccallp->addArgsp(new AstVarRef(refp->fileline(), outvscp, true));
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-15 00:25:30 +00:00
|
|
|
|
if (debug()>=9) { beginp->dumpTreeAndNext(cout,"-nitask: "); }
|
2006-10-11 15:41:42 +00:00
|
|
|
|
return beginp;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-20 13:27:00 +00:00
|
|
|
|
string dpiprotoName(AstNodeFTask* nodep, AstVar* rtnvarp) {
|
|
|
|
|
// Return fancy export-ish name for DPI function
|
|
|
|
|
// Variable names are NOT included so differences in only IO names won't matter
|
|
|
|
|
string dpiproto;
|
|
|
|
|
if (nodep->pure()) dpiproto += "pure ";
|
|
|
|
|
if (nodep->dpiContext()) dpiproto += "context ";
|
|
|
|
|
dpiproto += rtnvarp ? rtnvarp->dpiArgType(true,true):"void";
|
|
|
|
|
dpiproto += " "+nodep->cname()+" (";
|
|
|
|
|
string args;
|
|
|
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
|
|
|
|
if (portp->isIO() && !portp->isFuncReturn() && portp!=rtnvarp) {
|
|
|
|
|
if (args != "") { args+= ", "; dpiproto+= ", "; }
|
|
|
|
|
args += portp->name(); // Leftover so ,'s look nice
|
|
|
|
|
if (nodep->dpiImport()) dpiproto += portp->dpiArgType(false,false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dpiproto += ")";
|
|
|
|
|
return dpiproto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNode* createDpiTemp(AstVar* portp, const string& suffix) {
|
|
|
|
|
bool bitvec = (portp->basicp()->isBitLogic() && portp->width() > 32);
|
|
|
|
|
string stmt;
|
|
|
|
|
if (bitvec) {
|
|
|
|
|
stmt += "svBitVecVal "+portp->name()+suffix;
|
|
|
|
|
stmt += " ["+cvtToStr(portp->widthWords())+"]";
|
|
|
|
|
} else {
|
|
|
|
|
stmt += portp->dpiArgType(true,true);
|
|
|
|
|
stmt += " "+portp->name()+suffix;
|
|
|
|
|
}
|
|
|
|
|
stmt += ";\n";
|
|
|
|
|
return new AstCStmt(portp->fileline(), stmt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstNode* createAssignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix, const string& toSuffix) {
|
|
|
|
|
// Create assignment from internal format into DPI temporary
|
|
|
|
|
bool bitvec = (portp->basicp()->isBitLogic() && portp->width() > 32);
|
|
|
|
|
string stmt;
|
2010-02-04 13:15:33 +00:00
|
|
|
|
string ket;
|
2009-12-20 13:27:00 +00:00
|
|
|
|
// Someday we'll have better type support, and this can make variables and casts.
|
|
|
|
|
// But for now, we'll just text-bash it.
|
|
|
|
|
if (bitvec) {
|
2011-05-21 01:33:31 +00:00
|
|
|
|
if (portp->isWide()) {
|
|
|
|
|
stmt += ("VL_SET_SVBV_W("+cvtToStr(portp->width())
|
|
|
|
|
+", "+portp->name()+toSuffix+", "+portp->name()+frSuffix+")");
|
|
|
|
|
} else {
|
|
|
|
|
stmt += "VL_SET_WQ("+portp->name()+toSuffix+", "+portp->name()+frSuffix+")";
|
|
|
|
|
}
|
2009-12-20 13:27:00 +00:00
|
|
|
|
} else {
|
|
|
|
|
if (isPtr) stmt += "*"; // DPI outputs are pointers
|
|
|
|
|
stmt += portp->name()+toSuffix+" = ";
|
|
|
|
|
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) {
|
2010-02-04 13:15:33 +00:00
|
|
|
|
stmt += "VL_CVT_Q_VP(";
|
|
|
|
|
ket += ")";
|
2009-12-20 13:27:00 +00:00
|
|
|
|
}
|
|
|
|
|
stmt += portp->name()+frSuffix;
|
2010-01-17 20:10:37 +00:00
|
|
|
|
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::STRING) {
|
|
|
|
|
stmt += ".c_str()";
|
|
|
|
|
}
|
2009-12-20 13:27:00 +00:00
|
|
|
|
}
|
2010-02-04 13:15:33 +00:00
|
|
|
|
stmt += ket + ";\n";
|
2009-12-20 13:27:00 +00:00
|
|
|
|
return new AstCStmt(portp->fileline(), stmt);
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-12 11:35:28 +00:00
|
|
|
|
AstNode* createAssignDpiToInternal(AstVarScope* portvscp, const string& frName, bool cvt) {
|
2009-12-20 13:27:00 +00:00
|
|
|
|
// Create assignment from DPI temporary into internal format
|
|
|
|
|
AstVar* portp = portvscp->varp();
|
|
|
|
|
string stmt;
|
2010-02-04 13:15:33 +00:00
|
|
|
|
string ket;
|
2009-12-20 13:27:00 +00:00
|
|
|
|
if (portp->basicp() && portp->basicp()->keyword()==AstBasicDTypeKwd::CHANDLE) {
|
2010-02-04 13:15:33 +00:00
|
|
|
|
stmt += "VL_CVT_VP_Q(";
|
|
|
|
|
ket += ")";
|
2009-12-20 13:27:00 +00:00
|
|
|
|
}
|
2011-12-23 02:10:28 +00:00
|
|
|
|
else if (portp->basicp() && portp->basicp()->isBitLogic() && portp->width() != 1 && portp->isQuad()) {
|
2011-10-07 23:47:10 +00:00
|
|
|
|
// SV is vector, Verilator isn't
|
|
|
|
|
stmt += "VL_SET_QW(";
|
|
|
|
|
ket += ")";
|
|
|
|
|
}
|
2011-05-12 11:35:28 +00:00
|
|
|
|
if (!cvt
|
2011-12-23 02:10:28 +00:00
|
|
|
|
&& portp->basicp() && portp->basicp()->isBitLogic() && portp->width() != 1 && !portp->isWide() && !portp->isQuad())
|
2011-05-21 01:33:31 +00:00
|
|
|
|
stmt += "*"; // it's a svBitVecVal, which other code won't think is arrayed (as WData aren't), but really is
|
2009-12-20 13:27:00 +00:00
|
|
|
|
stmt += frName;
|
2010-02-04 13:15:33 +00:00
|
|
|
|
stmt += ket;
|
2009-12-20 13:27:00 +00:00
|
|
|
|
// Use a AstCMath, as we want V3Clean to mask off bits that don't make sense.
|
|
|
|
|
int cwidth = VL_WORDSIZE; if (portp->basicp()) cwidth = portp->basicp()->keyword().width();
|
|
|
|
|
if (portp->basicp() && portp->basicp()->isBitLogic()) cwidth = VL_WORDSIZE*portp->widthWords();
|
|
|
|
|
AstNode* newp = new AstAssign(portp->fileline(),
|
|
|
|
|
new AstVarRef(portp->fileline(), portvscp, true),
|
|
|
|
|
new AstSel(portp->fileline(),
|
|
|
|
|
new AstCMath(portp->fileline(), stmt, cwidth, false),
|
|
|
|
|
0, portp->width()));
|
|
|
|
|
return newp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstCFunc* makeDpiExportWrapper(AstNodeFTask* nodep, AstVar* rtnvarp) {
|
|
|
|
|
string dpiproto = dpiprotoName(nodep,rtnvarp);
|
|
|
|
|
|
|
|
|
|
AstCFunc* dpip = new AstCFunc(nodep->fileline(),
|
|
|
|
|
nodep->cname(),
|
|
|
|
|
m_scopep,
|
|
|
|
|
(rtnvarp ? rtnvarp->dpiArgType(true,true) : ""));
|
|
|
|
|
dpip->dontCombine(true);
|
|
|
|
|
dpip->entryPoint(true);
|
|
|
|
|
dpip->isStatic(true);
|
|
|
|
|
dpip->dpiExportWrapper(true);
|
|
|
|
|
dpip->cname(nodep->cname());
|
|
|
|
|
// Add DPI reference to top, since it's a global function
|
|
|
|
|
m_topScopep->scopep()->addActivep(dpip);
|
|
|
|
|
|
|
|
|
|
{// Create dispatch wrapper
|
|
|
|
|
// Note this function may dispatch to myfunc 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.
|
|
|
|
|
string stmt;
|
2012-08-27 01:13:47 +00:00
|
|
|
|
stmt += "static int __Vfuncnum = -1;\n"; // Static doesn't need save-restore as if below will re-fill proper value
|
2009-12-20 13:27:00 +00:00
|
|
|
|
// First time init (faster than what the compiler does if we did a singleton
|
|
|
|
|
stmt += "if (VL_UNLIKELY(__Vfuncnum==-1)) { __Vfuncnum = Verilated::exportFuncNum(\""+nodep->cname()+"\"); }\n";
|
|
|
|
|
// If the find fails, it will throw an error
|
|
|
|
|
stmt += "const VerilatedScope* __Vscopep = Verilated::dpiScope();\n";
|
|
|
|
|
// If dpiScope is fails and is null; the exportFind function throws and error
|
|
|
|
|
string cbtype = v3Global.opt.prefix()+"__Vcb_"+nodep->cname()+"_t";
|
|
|
|
|
stmt += cbtype+" __Vcb = ("+cbtype+")__Vscopep->exportFind(__Vfuncnum);\n";
|
|
|
|
|
// If __Vcb is null the exportFind function throws and error
|
|
|
|
|
dpip->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert input/inout DPI arguments to Internal types
|
|
|
|
|
string args;
|
|
|
|
|
args += "("+v3Global.opt.prefix()+"__Syms*)(__Vscopep->symsp())"; // Upcast w/o overhead
|
|
|
|
|
AstNode* argnodesp = NULL;
|
|
|
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
|
|
|
|
if (portp->isIO() && !portp->isFuncReturn() && portp != rtnvarp) {
|
|
|
|
|
// No createDpiTemp; we make a real internal variable instead
|
|
|
|
|
// SAME CODE BELOW
|
|
|
|
|
args+= ", ";
|
|
|
|
|
if (args != "") { argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true)); args=""; }
|
|
|
|
|
AstVarScope* outvscp = createFuncVar (dpip, portp->name()+"__Vcvt", portp);
|
|
|
|
|
AstVarRef* refp = new AstVarRef(portp->fileline(), outvscp, portp->isOutput());
|
|
|
|
|
argnodesp = argnodesp->addNextNull(refp);
|
|
|
|
|
|
|
|
|
|
if (portp->isInput()) {
|
2011-05-12 11:35:28 +00:00
|
|
|
|
dpip->addStmtsp(createAssignDpiToInternal(outvscp, portp->name(), false));
|
2009-12-20 13:27:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rtnvarp) {
|
|
|
|
|
AstVar* portp = rtnvarp;
|
|
|
|
|
// SAME CODE ABOVE
|
|
|
|
|
args+= ", ";
|
|
|
|
|
if (args != "") { argnodesp = argnodesp->addNext(new AstText(portp->fileline(), args, true)); args=""; }
|
|
|
|
|
AstVarScope* outvscp = createFuncVar (dpip, portp->name()+"__Vcvt", portp);
|
|
|
|
|
AstVarRef* refp = new AstVarRef(portp->fileline(), outvscp, portp->isOutput());
|
|
|
|
|
argnodesp = argnodesp->addNextNull(refp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{// Call the user function
|
|
|
|
|
// Add the variables referenced as VarRef's so that lifetime analysis
|
|
|
|
|
// doesn't rip up the variables on us
|
|
|
|
|
string stmt;
|
|
|
|
|
stmt += "(*__Vcb)(";
|
|
|
|
|
args += ");\n";
|
|
|
|
|
AstCStmt* newp = new AstCStmt(nodep->fileline(), stmt);
|
|
|
|
|
newp->addBodysp(argnodesp); argnodesp=NULL;
|
|
|
|
|
newp->addBodysp(new AstText(nodep->fileline(), args, true));
|
|
|
|
|
dpip->addStmtsp(newp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert output/inout arguments back to internal type
|
|
|
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
|
|
|
|
if (portp->isIO() && portp->isOutput() && !portp->isFuncReturn()) {
|
|
|
|
|
dpip->addStmtsp(createAssignInternalToDpi(portp,true,"__Vcvt",""));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rtnvarp) {
|
|
|
|
|
dpip->addStmtsp(createDpiTemp(rtnvarp,""));
|
|
|
|
|
dpip->addStmtsp(createAssignInternalToDpi(rtnvarp,false,"__Vcvt",""));
|
|
|
|
|
string stmt = "return "+rtnvarp->name()+";\n";
|
|
|
|
|
dpip->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
|
|
|
|
}
|
|
|
|
|
return dpip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstCFunc* makeDpiImportWrapper(AstNodeFTask* nodep, AstVar* rtnvarp) {
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (nodep->cname() != AstNode::prettyName(nodep->cname())) {
|
|
|
|
|
nodep->v3error("DPI function has illegal characters in C identifier name: "<<AstNode::prettyName(nodep->cname()));
|
|
|
|
|
}
|
|
|
|
|
AstCFunc* dpip = new AstCFunc(nodep->fileline(),
|
|
|
|
|
nodep->cname(),
|
|
|
|
|
m_scopep,
|
|
|
|
|
(rtnvarp ? rtnvarp->dpiArgType(true,true)
|
|
|
|
|
// Tasks (but not void functions) return bool indicating disabled
|
|
|
|
|
: nodep->dpiTask() ? "int"
|
|
|
|
|
: ""));
|
|
|
|
|
dpip->dontCombine(true);
|
|
|
|
|
dpip->entryPoint (false);
|
|
|
|
|
dpip->funcPublic (true);
|
|
|
|
|
dpip->isStatic (false);
|
|
|
|
|
dpip->pure (nodep->pure());
|
|
|
|
|
dpip->dpiImport (true);
|
|
|
|
|
// Add DPI reference to top, since it's a global function
|
|
|
|
|
m_topScopep->scopep()->addActivep(dpip);
|
|
|
|
|
return dpip;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-20 13:27:00 +00:00
|
|
|
|
void bodyDpiImportFunc(AstNodeFTask* nodep, AstVarScope* rtnvscp, AstCFunc* cfuncp) {
|
2009-12-03 11:55:29 +00:00
|
|
|
|
// Convert input/inout arguments to DPI types
|
|
|
|
|
string args;
|
|
|
|
|
for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) {
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
|
|
|
|
AstVarScope* portvscp = portp->user2p()->castNode()->castVarScope(); // Remembered when we created it earlier
|
2009-12-05 15:38:49 +00:00
|
|
|
|
if (portp->isIO() && !portp->isFuncReturn() && portvscp != rtnvscp
|
|
|
|
|
&& portp->name() != "__Vscopep" // Passed to dpiContext, not callee
|
|
|
|
|
&& portp->name() != "__Vfilenamep"
|
|
|
|
|
&& portp->name() != "__Vlineno") {
|
2009-12-03 11:55:29 +00:00
|
|
|
|
bool bitvec = (portp->basicp()->isBitLogic() && portp->width() > 32);
|
|
|
|
|
|
|
|
|
|
if (args != "") { args+= ", "; }
|
|
|
|
|
if (bitvec) {}
|
|
|
|
|
else if (portp->isOutput()) args += "&";
|
2011-12-23 02:10:28 +00:00
|
|
|
|
else if (portp->basicp() && portp->basicp()->isBitLogic() && portp->width() != 1) args += "&"; // it's a svBitVecVal
|
2009-12-03 11:55:29 +00:00
|
|
|
|
|
2009-12-20 13:27:00 +00:00
|
|
|
|
args += portp->name()+"__Vcvt";
|
2009-12-03 11:55:29 +00:00
|
|
|
|
|
2009-12-20 13:27:00 +00:00
|
|
|
|
cfuncp->addStmtsp(createDpiTemp(portp,"__Vcvt"));
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (portp->isInput()) {
|
2009-12-20 13:27:00 +00:00
|
|
|
|
cfuncp->addStmtsp(createAssignInternalToDpi(portp,false,"","__Vcvt"));
|
2009-12-03 11:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store context, if needed
|
|
|
|
|
if (nodep->dpiContext()) {
|
2009-12-05 15:38:49 +00:00
|
|
|
|
string stmt = "Verilated::dpiContext(__Vscopep, __Vfilenamep, __Vlineno);\n";
|
|
|
|
|
cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
2009-12-03 11:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{// Call the user function
|
|
|
|
|
string stmt;
|
|
|
|
|
if (rtnvscp) { // isFunction will no longer work as we unlinked the return var
|
2009-12-20 13:27:00 +00:00
|
|
|
|
stmt += rtnvscp->varp()->dpiArgType(true,true) + " "+rtnvscp->varp()->name()+"__Vcvt = ";
|
2009-12-03 11:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
stmt += nodep->cname()+"("+args+");\n";
|
|
|
|
|
cfuncp->addStmtsp(new AstCStmt(nodep->fileline(), stmt));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Convert output/inout arguments back to internal type
|
|
|
|
|
for (AstNode* stmtp = cfuncp->argsp(); stmtp; stmtp=stmtp->nextp()) {
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
2009-12-20 13:27:00 +00:00
|
|
|
|
if (portp->isIO() && (portp->isOutput() || portp->isFuncReturn())) {
|
2009-12-03 11:55:29 +00:00
|
|
|
|
AstVarScope* portvscp = portp->user2p()->castNode()->castVarScope(); // Remembered when we created it earlier
|
2011-05-12 11:35:28 +00:00
|
|
|
|
cfuncp->addStmtsp(createAssignDpiToInternal(portvscp,portp->name()+"__Vcvt",true));
|
2009-12-03 11:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-10-11 15:41:42 +00:00
|
|
|
|
|
2009-12-03 11:55:29 +00:00
|
|
|
|
AstCFunc* makeUserFunc(AstNodeFTask* nodep, bool ftaskNoInline) {
|
2006-10-11 15:41:42 +00:00
|
|
|
|
// Given a already cloned node, make a public C function, or a non-inline C function
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// 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();
|
|
|
|
|
AstVar* rtnvarp = NULL;
|
|
|
|
|
AstVarScope* rtnvscp = NULL;
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (nodep->isFunction()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstVar* portp = NULL;
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (NULL!=(portp = nodep->fvarp()->castVar())) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (!portp->isFuncReturn()) nodep->v3error("Not marked as function return var");
|
2006-10-05 14:53:17 +00:00
|
|
|
|
if (portp->isWide()) nodep->v3error("Unsupported: Public functions with return > 64 bits wide. (Make it a output instead.)");
|
2009-12-20 13:27:00 +00:00
|
|
|
|
if (ftaskNoInline || nodep->dpiExport()) portp->funcReturn(false); // Converting return to 'outputs'
|
2006-08-26 11:35:28 +00:00
|
|
|
|
portp->unlinkFrBack();
|
|
|
|
|
rtnvarp = portp;
|
|
|
|
|
rtnvarp->funcLocal(true);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
rtnvarp->name(rtnvarp->name()+"__Vfuncrtn"); // Avoid conflict with DPI function name
|
2006-08-26 11:35:28 +00:00
|
|
|
|
rtnvscp = new AstVarScope (rtnvarp->fileline(), m_scopep, rtnvarp);
|
|
|
|
|
m_scopep->addVarp(rtnvscp);
|
|
|
|
|
rtnvarp->user2p(rtnvscp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("function without function output variable");
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-03 11:55:29 +00:00
|
|
|
|
string prefix = "";
|
|
|
|
|
if (nodep->dpiImport()) prefix = "__Vdpiimwrap_";
|
2009-12-20 13:27:00 +00:00
|
|
|
|
else if (nodep->dpiExport()) prefix = "__Vdpiexp_";
|
2009-12-03 11:55:29 +00:00
|
|
|
|
else if (ftaskNoInline) prefix = "__VnoInFunc_";
|
2009-12-20 13:27:00 +00:00
|
|
|
|
// Unless public, v3Descope will not uniquify function names even if duplicate per-scope,
|
|
|
|
|
// so make it unique now.
|
2009-12-05 15:38:49 +00:00
|
|
|
|
string suffix = ""; // So, make them unique
|
|
|
|
|
if (!nodep->taskPublic()) suffix = "_"+m_scopep->nameDotless();
|
2006-10-11 15:41:42 +00:00
|
|
|
|
AstCFunc* cfuncp = new AstCFunc(nodep->fileline(),
|
2009-12-05 15:38:49 +00:00
|
|
|
|
prefix + nodep->name() + suffix,
|
2006-10-11 15:41:42 +00:00
|
|
|
|
m_scopep,
|
2009-12-20 13:27:00 +00:00
|
|
|
|
((nodep->taskPublic() && rtnvarp) ? rtnvarp->cPubArgType(true,true)
|
|
|
|
|
: ""));
|
2009-12-03 11:55:29 +00:00
|
|
|
|
// It's ok to combine imports because this is just a wrapper; duplicate wrappers can get merged.
|
|
|
|
|
cfuncp->dontCombine(!nodep->dpiImport());
|
|
|
|
|
cfuncp->entryPoint (!nodep->dpiImport());
|
|
|
|
|
cfuncp->funcPublic (nodep->taskPublic());
|
2009-12-20 13:27:00 +00:00
|
|
|
|
cfuncp->dpiExport (nodep->dpiExport());
|
2009-12-03 11:55:29 +00:00
|
|
|
|
cfuncp->isStatic (!(nodep->dpiImport()||nodep->taskPublic()));
|
|
|
|
|
cfuncp->pure (nodep->pure());
|
|
|
|
|
//cfuncp->dpiImport // Not set in the wrapper - the called function has it set
|
2009-12-20 13:27:00 +00:00
|
|
|
|
if (cfuncp->dpiExport()) cfuncp->cname (nodep->cname());
|
2009-12-03 11:55:29 +00:00
|
|
|
|
|
2009-12-05 15:38:49 +00:00
|
|
|
|
bool needSyms = !nodep->dpiImport();
|
|
|
|
|
if (needSyms) {
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (nodep->taskPublic()) {
|
|
|
|
|
// We need to get a pointer to all of our variables (may have eval'ed something else earlier)
|
|
|
|
|
cfuncp->addInitsp(
|
|
|
|
|
new AstCStmt(nodep->fileline(),
|
|
|
|
|
EmitCBaseVisitor::symClassVar()+" = this->__VlSymsp;\n"));
|
|
|
|
|
} else {
|
|
|
|
|
// Need symbol table
|
|
|
|
|
cfuncp->argTypes(EmitCBaseVisitor::symClassVar());
|
|
|
|
|
}
|
2006-10-11 15:41:42 +00:00
|
|
|
|
}
|
2009-12-05 15:38:49 +00:00
|
|
|
|
if (nodep->dpiContext()) {
|
|
|
|
|
// First three args go to dpiContext call
|
|
|
|
|
createInputVar (cfuncp, "__Vscopep", AstBasicDTypeKwd::SCOPEPTR);
|
|
|
|
|
createInputVar (cfuncp, "__Vfilenamep", AstBasicDTypeKwd::CHARPTR);
|
|
|
|
|
createInputVar (cfuncp, "__Vlineno", AstBasicDTypeKwd::INT);
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (!nodep->dpiImport()) {
|
|
|
|
|
cfuncp->addInitsp(new AstCStmt(nodep->fileline(), EmitCBaseVisitor::symTopAssign()+"\n"));
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-20 13:27:00 +00:00
|
|
|
|
if (nodep->dpiExport()) {
|
|
|
|
|
AstScopeName* snp = nodep->scopeNamep(); if (!snp) nodep->v3fatalSrc("Missing scoping context");
|
|
|
|
|
snp->dpiExport(true); // The AstScopeName is really a statement(ish) for tracking, not a function
|
|
|
|
|
snp->unlinkFrBack();
|
|
|
|
|
cfuncp->addInitsp(snp);
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 11:55:29 +00:00
|
|
|
|
AstCFunc* dpip = NULL;
|
|
|
|
|
string dpiproto;
|
2009-12-20 13:27:00 +00:00
|
|
|
|
if (nodep->dpiImport() || nodep->dpiExport()) {
|
|
|
|
|
dpiproto = dpiprotoName(nodep, rtnvarp);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
|
|
|
|
|
// Only create one DPI extern for each specified cname,
|
|
|
|
|
// as it's legal for the user to attach multiple tasks to one dpi cname
|
2009-12-20 13:27:00 +00:00
|
|
|
|
DpiNames::iterator iter = m_dpiNames.find(nodep->cname());
|
|
|
|
|
if (iter == m_dpiNames.end()) {
|
2009-12-03 11:55:29 +00:00
|
|
|
|
// m_dpiNames insert below
|
2009-12-20 13:27:00 +00:00
|
|
|
|
if (nodep->dpiImport()) {
|
|
|
|
|
dpip = makeDpiImportWrapper(nodep, rtnvarp);
|
|
|
|
|
} else if (nodep->dpiExport()) {
|
|
|
|
|
dpip = makeDpiExportWrapper(nodep, rtnvarp);
|
|
|
|
|
cfuncp->addInitsp(new AstComment(dpip->fileline(), (string)("Function called from: ")+dpip->cname()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_dpiNames.insert(make_pair(nodep->cname(), make_pair(dpip, dpiproto)));
|
|
|
|
|
}
|
|
|
|
|
else if (iter->second.second != dpiproto) {
|
2012-05-22 01:24:17 +00:00
|
|
|
|
nodep->v3error("Duplicate declaration of DPI function with different formal arguments: "<<nodep->prettyName()<<endl
|
|
|
|
|
<<nodep->warnMore()<<"... New prototype: "<<dpiproto<<endl
|
|
|
|
|
<<iter->second.first->warnMore()<<"... Original prototype: "<<iter->second.second);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// Create list of arguments and move to function
|
|
|
|
|
for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp=nextp) {
|
|
|
|
|
nextp = stmtp->nextp();
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
|
|
|
|
if (portp->isIO()) {
|
|
|
|
|
// Move it to new function
|
|
|
|
|
portp->unlinkFrBack();
|
|
|
|
|
portp->funcLocal(true);
|
2006-10-11 15:41:42 +00:00
|
|
|
|
cfuncp->addArgsp(portp);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (dpip) {
|
|
|
|
|
dpip->addArgsp(portp->cloneTree(false));
|
|
|
|
|
if (!portp->basicp() || portp->basicp()->keyword().isDpiUnsupported()) {
|
2012-05-22 01:24:17 +00:00
|
|
|
|
portp->v3error("Unsupported: DPI argument of type "<<portp->basicp()->prettyTypeName()<<endl
|
|
|
|
|
<<portp->warnMore()<<"... For best portability, use bit, byte, int, or longint");
|
2009-12-03 11:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
} else {
|
|
|
|
|
// "Normal" variable, mark inside function
|
|
|
|
|
portp->funcLocal(true);
|
|
|
|
|
}
|
|
|
|
|
AstVarScope* newvscp = new AstVarScope (portp->fileline(), m_scopep, portp);
|
|
|
|
|
m_scopep->addVarp(newvscp);
|
|
|
|
|
portp->user2p(newvscp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-03 11:55:29 +00:00
|
|
|
|
|
2009-12-20 13:27:00 +00: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);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Move body
|
|
|
|
|
AstNode* bodysp = nodep->stmtsp();
|
2006-10-11 15:41:42 +00:00
|
|
|
|
if (bodysp) { bodysp->unlinkFrBackWithNext(); cfuncp->addStmtsp(bodysp); }
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (nodep->dpiImport()) {
|
2009-12-20 13:27:00 +00:00
|
|
|
|
bodyDpiImportFunc(nodep, rtnvscp, cfuncp);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Return statement
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (rtnvscp && nodep->taskPublic()) {
|
2006-10-11 15:41:42 +00:00
|
|
|
|
cfuncp->addFinalsp(new AstCReturn(rtnvscp->fileline(),
|
|
|
|
|
new AstVarRef(rtnvscp->fileline(), rtnvscp, false)));
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
// Replace variable refs
|
2009-10-04 21:01:35 +00:00
|
|
|
|
// Iteration requires a back, so put under temporary node
|
2012-03-20 20:01:53 +00:00
|
|
|
|
{
|
2009-10-04 21:01:35 +00:00
|
|
|
|
AstBegin* tempp = new AstBegin(cfuncp->fileline(),"[EditWrapper]",cfuncp);
|
|
|
|
|
TaskRelinkVisitor visit (tempp);
|
|
|
|
|
tempp->stmtsp()->unlinkFrBackWithNext(); tempp->deleteTree(); tempp=NULL;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Delete rest of cloned task and return new func
|
|
|
|
|
pushDeletep(nodep); nodep=NULL;
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (debug()>=9) { cfuncp->dumpTree(cout,"-userFunc: "); }
|
2006-10-11 15:41:42 +00:00
|
|
|
|
return cfuncp;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void iterateIntoFTask(AstNodeFTask* nodep) {
|
|
|
|
|
// Iterate into the FTask we are calling. Note it may be under a different
|
|
|
|
|
// scope then the caller, so we need to restore state.
|
|
|
|
|
AstScope* oldscopep = m_scopep;
|
2012-12-16 02:41:37 +00:00
|
|
|
|
InsertMode prevInsMode = m_insMode;
|
|
|
|
|
AstNode* prevInsStmtp = m_insStmtp;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_scopep = m_statep->getScope(nodep);
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
m_scopep = oldscopep;
|
2012-12-16 02:41:37 +00:00
|
|
|
|
m_insMode = prevInsMode;
|
|
|
|
|
m_insStmtp = prevInsStmtp;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2006-09-05 20:06:23 +00:00
|
|
|
|
void insertBeforeStmt(AstNode* nodep, AstNode* newp) {
|
2009-10-09 00:42:45 +00:00
|
|
|
|
// See also AstNode::addBeforeStmt; this predates that function
|
2006-09-05 20:06:23 +00:00
|
|
|
|
if (debug()>=9) { nodep->dumpTree(cout,"-newstmt:"); }
|
|
|
|
|
if (!m_insStmtp) nodep->v3fatalSrc("Function not underneath a statement");
|
|
|
|
|
if (m_insMode == IM_BEFORE) {
|
|
|
|
|
// Add the whole thing before insertAt
|
|
|
|
|
UINFO(5," IM_Before "<<m_insStmtp<<endl);
|
|
|
|
|
if (debug()>=9) { newp->dumpTree(cout,"-newfunc:"); }
|
2009-10-09 00:42:45 +00:00
|
|
|
|
m_insStmtp->addHereThisAsNext(newp);
|
2006-09-05 20:06:23 +00:00
|
|
|
|
}
|
|
|
|
|
else if (m_insMode == IM_AFTER) {
|
|
|
|
|
UINFO(5," IM_After "<<m_insStmtp);
|
|
|
|
|
m_insStmtp->addNextHere(newp);
|
|
|
|
|
}
|
|
|
|
|
else if (m_insMode == IM_WHILE_PRECOND) {
|
|
|
|
|
UINFO(5," IM_While_Precond "<<m_insStmtp);
|
|
|
|
|
AstWhile* whilep = m_insStmtp->castWhile();
|
|
|
|
|
if (!whilep) nodep->v3fatalSrc("Insert should be under WHILE");
|
|
|
|
|
whilep->addPrecondsp(newp);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
nodep->v3fatalSrc("Unknown InsertMode");
|
|
|
|
|
}
|
|
|
|
|
m_insMode = IM_AFTER;
|
|
|
|
|
m_insStmtp = newp;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// VISITORS
|
2009-11-07 11:20:20 +00:00
|
|
|
|
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_modp = nodep;
|
2006-09-05 20:06:23 +00:00
|
|
|
|
m_insStmtp = NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_modNCalls = 0;
|
|
|
|
|
nodep->iterateChildren(*this);
|
2006-08-27 14:51:25 +00:00
|
|
|
|
m_modp = NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2009-12-03 11:55:29 +00:00
|
|
|
|
virtual void visit(AstTopScope* nodep, AstNUser*) {
|
|
|
|
|
m_topScopep = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual void visit(AstScope* nodep, AstNUser*) {
|
|
|
|
|
m_scopep = nodep;
|
2006-09-05 20:06:23 +00:00
|
|
|
|
m_insStmtp = NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_scopep = NULL;
|
|
|
|
|
}
|
2011-02-15 00:25:30 +00:00
|
|
|
|
virtual void visit(AstNodeFTaskRef* nodep, AstNUser*) {
|
|
|
|
|
if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked?");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs
|
2011-02-15 00:25:30 +00:00
|
|
|
|
UINFO(4," FTask REF "<<nodep<<endl);
|
|
|
|
|
if (debug()>=9) { nodep->dumpTree(cout,"-inlfunc:"); }
|
2010-05-25 23:37:45 +00:00
|
|
|
|
if (!m_scopep) nodep->v3fatalSrc("func ref not under scope");
|
2011-02-15 00:25:30 +00:00
|
|
|
|
string namePrefix = ((nodep->castFuncRef()?"__Vfunc_":"__Vtask_")
|
|
|
|
|
+nodep->taskp()->shortName()+"__"+cvtToStr(m_modNCalls++));
|
2007-01-26 21:53:03 +00:00
|
|
|
|
// Create output variable
|
2011-02-15 00:25:30 +00:00
|
|
|
|
AstVarScope* outvscp = NULL;
|
|
|
|
|
if (nodep->taskp()->isFunction()) {
|
|
|
|
|
// Not that it's a FUNCREF, but that we're calling a function (perhaps as a task)
|
|
|
|
|
outvscp = createVarScope (nodep->taskp()->fvarp()->castVar(),
|
|
|
|
|
namePrefix+"__Vfuncout");
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Create cloned statements
|
2006-10-11 15:41:42 +00:00
|
|
|
|
AstNode* beginp;
|
|
|
|
|
if (m_statep->ftaskNoInline(nodep->taskp())) {
|
|
|
|
|
// This may share VarScope's with a public task, if any. Yuk.
|
|
|
|
|
beginp = createNonInlinedFTask(nodep, namePrefix, outvscp);
|
|
|
|
|
} else {
|
|
|
|
|
beginp = createInlinedFTask(nodep, namePrefix, outvscp);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Replace the ref
|
2011-02-15 00:25:30 +00:00
|
|
|
|
if (nodep->castFuncRef()) {
|
|
|
|
|
if (!nodep->taskp()->isFunction()) nodep->v3fatalSrc("func reference to non-function");
|
|
|
|
|
AstVarRef* outrefp = new AstVarRef (nodep->fileline(), outvscp, false);
|
|
|
|
|
nodep->replaceWith(outrefp);
|
|
|
|
|
// Insert new statements
|
|
|
|
|
insertBeforeStmt(nodep, beginp);
|
|
|
|
|
} else {
|
|
|
|
|
// outvscp maybe non-NULL if calling a function in a taskref,
|
|
|
|
|
// but if so we want to simply ignore the function result
|
|
|
|
|
nodep->replaceWith(beginp);
|
|
|
|
|
}
|
2006-09-05 20:06:23 +00:00
|
|
|
|
// Cleanup
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->deleteTree(); nodep=NULL;
|
2011-02-15 00:25:30 +00:00
|
|
|
|
UINFO(4," FTask REF Done.\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeFTask* nodep, AstNUser*) {
|
2006-10-11 15:41:42 +00:00
|
|
|
|
UINFO(4," Inline "<<nodep<<endl);
|
2006-09-05 20:06:23 +00:00
|
|
|
|
InsertMode prevInsMode = m_insMode;
|
|
|
|
|
AstNode* prevInsStmtp = m_insStmtp;
|
|
|
|
|
m_insMode = IM_BEFORE;
|
|
|
|
|
m_insStmtp = nodep->stmtsp(); // Might be null if no statements, but we won't use it
|
2012-04-29 12:55:33 +00:00
|
|
|
|
if (!nodep->user1SetOnce()) { // Just one creation needed per function
|
2006-10-11 15:41:42 +00:00
|
|
|
|
// Expand functions in it
|
2009-12-20 13:27:00 +00:00
|
|
|
|
int modes = 0;
|
|
|
|
|
if (nodep->dpiImport()) modes++;
|
|
|
|
|
if (nodep->dpiExport()) modes++;
|
|
|
|
|
if (nodep->taskPublic()) modes++;
|
|
|
|
|
if (modes > 1) nodep->v3error("Cannot mix DPI import, DPI export and/or public on same function: "<<nodep->prettyName());
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2009-12-20 13:27:00 +00:00
|
|
|
|
if (nodep->dpiImport() || nodep->dpiExport()
|
|
|
|
|
|| nodep->taskPublic() || m_statep->ftaskNoInline(nodep)) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Clone it first, because we may have later FTaskRef's that still need
|
|
|
|
|
// the original version.
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (m_statep->ftaskNoInline(nodep)) m_statep->checkPurity(nodep);
|
2008-11-20 01:15:05 +00:00
|
|
|
|
AstNodeFTask* clonedFuncp = nodep->cloneTree(false);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
AstCFunc* cfuncp = makeUserFunc(clonedFuncp, m_statep->ftaskNoInline(nodep));
|
2006-10-11 15:41:42 +00:00
|
|
|
|
nodep->addNextHere(cfuncp);
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (nodep->dpiImport() || m_statep->ftaskNoInline(nodep)) {
|
|
|
|
|
m_statep->ftaskCFuncp(nodep, cfuncp);
|
|
|
|
|
}
|
2006-10-11 15:41:42 +00:00
|
|
|
|
iterateIntoFTask(clonedFuncp); // Do the clone too
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Any variables inside the function still have varscopes pointing to them.
|
|
|
|
|
// We're going to delete the vars, so delete the varscopes.
|
2009-12-03 11:55:29 +00:00
|
|
|
|
if (nodep->isFunction()) {
|
|
|
|
|
if (AstVar* portp = nodep->fvarp()->castVar()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp);
|
|
|
|
|
UINFO(9," funcremovevsc "<<vscp<<endl);
|
|
|
|
|
pushDeletep(vscp->unlinkFrBack()); vscp=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (AstNode* nextp, *stmtp = nodep->stmtsp(); stmtp; stmtp=nextp) {
|
|
|
|
|
nextp = stmtp->nextp();
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
|
|
|
|
AstVarScope* vscp = m_statep->findVarScope(m_scopep, portp);
|
|
|
|
|
UINFO(9," funcremovevsc "<<vscp<<endl);
|
|
|
|
|
pushDeletep(vscp->unlinkFrBack()); vscp=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-11-25 12:57:02 +00:00
|
|
|
|
// Just push for deletion, as other references to func may
|
|
|
|
|
// remain until visitor exits
|
2006-10-11 15:41:42 +00:00
|
|
|
|
nodep->unlinkFrBack();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
pushDeletep(nodep); nodep=NULL;
|
|
|
|
|
}
|
2006-09-05 20:06:23 +00:00
|
|
|
|
m_insMode = prevInsMode;
|
|
|
|
|
m_insStmtp = prevInsStmtp;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstWhile* nodep, AstNUser*) {
|
|
|
|
|
// Special, as statements need to be put in different places
|
|
|
|
|
// Preconditions insert first just before themselves (the normal rule for other statement types)
|
|
|
|
|
m_insStmtp = NULL; // First thing should be new statement
|
|
|
|
|
nodep->precondsp()->iterateAndNext(*this);
|
|
|
|
|
// Conditions insert first at end of precondsp.
|
|
|
|
|
m_insMode = IM_WHILE_PRECOND;
|
|
|
|
|
m_insStmtp = nodep;
|
|
|
|
|
nodep->condp()->iterateAndNext(*this);
|
|
|
|
|
// Body insert just before themselves
|
|
|
|
|
m_insStmtp = NULL; // First thing should be new statement
|
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
2010-02-14 15:01:21 +00:00
|
|
|
|
nodep->incsp()->iterateAndNext(*this);
|
2006-09-05 20:06:23 +00:00
|
|
|
|
// Done the loop
|
|
|
|
|
m_insStmtp = NULL; // Next thing should be new statement
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeFor* nodep, AstNUser*) {
|
|
|
|
|
nodep->v3fatalSrc("For statements should have been converted to while statements in V3Begin.cpp\n");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeStmt* nodep, AstNUser*) {
|
2006-09-05 20:06:23 +00:00
|
|
|
|
m_insMode = IM_BEFORE;
|
|
|
|
|
m_insStmtp = nodep;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
2006-09-05 20:06:23 +00:00
|
|
|
|
m_insStmtp = NULL; // Next thing should be new statement
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
TaskVisitor(AstNetlist* nodep, TaskStateVisitor* statep)
|
|
|
|
|
: m_statep(statep) {
|
|
|
|
|
m_modp = NULL;
|
2009-12-03 11:55:29 +00:00
|
|
|
|
m_topScopep = NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_scopep = NULL;
|
2006-09-05 20:06:23 +00:00
|
|
|
|
m_insStmtp = NULL;
|
2008-11-25 14:03:49 +00:00
|
|
|
|
AstNode::user1ClearTree();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~TaskVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Task class functions
|
|
|
|
|
|
2009-11-30 23:36:31 +00:00
|
|
|
|
V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp) {
|
|
|
|
|
// Output list will be in order of the port declaration variables (so func calls are made right in C)
|
|
|
|
|
// Missing pin/expr? We return (pinvar, NULL)
|
|
|
|
|
// Extra pin/expr? We clean it up
|
|
|
|
|
|
|
|
|
|
V3TaskConnects tconnects;
|
|
|
|
|
if (!nodep->taskp()) nodep->v3fatalSrc("unlinked");
|
|
|
|
|
|
|
|
|
|
// Find ports
|
|
|
|
|
//map<string,int> name_to_pinnum;
|
|
|
|
|
int tpinnum = 0; // Note grammar starts pin counting at one
|
2010-01-18 00:13:44 +00:00
|
|
|
|
AstVar* sformatp = NULL;
|
2009-11-30 23:36:31 +00:00
|
|
|
|
for (AstNode* stmtp = taskStmtsp; stmtp; stmtp=stmtp->nextp()) {
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
|
|
|
|
if (portp->isIO()) {
|
|
|
|
|
tconnects.push_back(make_pair(portp, (AstNode*)NULL));
|
|
|
|
|
// Eventually we'll do name based connections
|
|
|
|
|
// That'll require a AstTpin or somesuch which will replace the ppinnum counting
|
|
|
|
|
//name_to_pinnum.insert(make_pair(portp->name(), tpinnum));
|
|
|
|
|
tpinnum++;
|
2010-01-18 00:13:44 +00:00
|
|
|
|
if (portp->attrSFormat()) {
|
|
|
|
|
sformatp = portp;
|
|
|
|
|
} else if (sformatp) {
|
|
|
|
|
nodep->v3error("/*verilator sformat*/ can only be applied to last argument of a function");
|
|
|
|
|
}
|
2009-11-30 23:36:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Connect pins
|
|
|
|
|
int ppinnum = 0;
|
|
|
|
|
for (AstNode* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()) {
|
|
|
|
|
if (ppinnum >= tpinnum) {
|
2010-01-18 00:13:44 +00:00
|
|
|
|
if (sformatp) {
|
|
|
|
|
tconnects.push_back(make_pair(sformatp, (AstNode*)NULL));
|
|
|
|
|
} else {
|
|
|
|
|
pinp->v3error("Too many arguments in function call to "<<nodep->taskp()->prettyTypeName());
|
|
|
|
|
// We'll just delete them; seems less error prone than making a false argument
|
|
|
|
|
pinp->unlinkFrBackWithNext()->deleteTree(); pinp=NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-11-30 23:36:31 +00:00
|
|
|
|
}
|
2010-01-18 00:13:44 +00:00
|
|
|
|
tconnects[ppinnum].second = pinp;
|
|
|
|
|
ppinnum++;
|
2009-11-30 23:36:31 +00:00
|
|
|
|
}
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2009-11-30 23:36:31 +00:00
|
|
|
|
while (ppinnum < tpinnum) {
|
|
|
|
|
nodep->v3error("Too few arguments in function call to "<<nodep->taskp()->prettyTypeName());
|
|
|
|
|
UINFO(1,"missing argument for '"<<tconnects[ppinnum].first->prettyName()<<"'"<<endl);
|
|
|
|
|
ppinnum++;
|
|
|
|
|
}
|
|
|
|
|
return tconnects;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
void V3Task::taskAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
TaskStateVisitor visitors (nodep);
|
|
|
|
|
TaskVisitor visitor (nodep, &visitors);
|
|
|
|
|
}
|