Internals: Use common function to resolve task pins

This commit is contained in:
Wilson Snyder 2009-11-30 18:36:31 -05:00
parent 5a502d451d
commit ad0fcb745e
9 changed files with 197 additions and 152 deletions

View File

@ -70,6 +70,7 @@ sub diff_dir {
print "="x70,"\n";
print "= $a <-> $b\n";
diff_file($a,$b);
$any = 1;
}
$any or warn "%Warning: No .tree files found\n";
}

View File

@ -328,7 +328,7 @@ private:
&& nodep->lhsp()->castSel()->fromp()->castArraySel())) {
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
AstNode* newlhsp = createDlyArray(nodep, lhsp);
if (m_inLoop) nodep->v3warn(BLKLOOPINIT,"Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
if (m_inLoop) nodep->v3warn(E_BLKLOOPINIT,"Unsupported: Delayed assignment to array inside for loops (non-delayed is ok - see docs)");
if (newlhsp) {
nodep->lhsp(newlhsp);
} else {

View File

@ -45,9 +45,9 @@ public:
I_COVERAGE, // Coverage is on/off from /*verilator coverage_on/off*/
I_TRACING, // Tracing is on/off from /*verilator tracing_on/off*/
// Error codes:
MULTITOP, // Error: Multiple top level modules
TASKNSVAR, // Error: Task I/O not simple
BLKLOOPINIT, // Error: Delayed assignment to array inside for loops
E_MULTITOP, // Error: Multiple top level modules
E_TASKNSVAR, // Error: Task I/O not simple
E_BLKLOOPINIT, // Error: Delayed assignment to array inside for loops
// Warning codes:
FIRST_WARN, // Just a code so the program knows where to start warnings
//

View File

@ -60,9 +60,9 @@ void V3LinkLevel::modSortByLevel() {
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep; nodep=nodep->nextp()->castNodeModule()) {
if (nodep->level()<=2) {
if (topp) {
nodep->v3warn(MULTITOP, "Unsupported: Multiple top level modules: "
nodep->v3warn(E_MULTITOP, "Unsupported: Multiple top level modules: "
<<nodep->prettyName()<<" and "<<topp->prettyName());
nodep->v3warn(MULTITOP, "Fix, or use --top-module option to select which you want.");
nodep->v3warn(E_MULTITOP, "Fix, or use --top-module option to select which you want.");
}
topp = nodep;
}

View File

@ -43,6 +43,7 @@
#include "V3Error.h"
#include "V3Ast.h"
#include "V3Width.h"
#include "V3Task.h"
#include <deque>
@ -509,25 +510,22 @@ private:
funcp = nodep->taskp()->castFunc(); if (!funcp) nodep->v3fatalSrc("Not linked");
// Apply function call values to function
// Note we'd need a stack if we allowed recursive functions!
AstNode* pinp = nodep->pinsp(); AstNode* nextpinp = NULL;
for (AstNode* stmtp = funcp->stmtsp(); stmtp; pinp=nextpinp, stmtp=stmtp->nextp()) {
if (AstVar* portp = stmtp->castVar()) {
if (portp->isIO()) {
if (pinp==NULL) {
nodep->v3error("Too few arguments in function call");
} else {
nextpinp = pinp->nextp();
if (portp->isOutput()) {
clearOptimizable(portp,"Language violation: Outputs not allowed in constant functions");
return;
}
// Evaluate pin value
pinp->accept(*this);
// Apply value to the function
if (!m_checkOnly && optimizable()) {
newNumber(stmtp)->opAssign(*fetchNumber(pinp));
}
}
V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) {
AstVar* portp = it->first;
AstNode* pinp = it->second;
if (pinp==NULL) {
// Too few arguments in function call - ignore it
} else {
if (portp->isOutput()) {
clearOptimizable(portp,"Language violation: Outputs not allowed in constant functions");
return;
}
// Evaluate pin value
pinp->accept(*this);
// Apply value to the function
if (!m_checkOnly && optimizable()) {
newNumber(portp)->opAssign(*fetchNumber(pinp));
}
}
}

View File

@ -341,85 +341,84 @@ private:
AstNode* newbodysp = refp->taskp()->stmtsp()->cloneTree(true); // Maybe NULL
AstNode* beginp = new AstComment(refp->fileline(), (string)("Function: ")+refp->name());
if (newbodysp) beginp->addNext(newbodysp);
if (debug()>=9) { beginp->dumpTree(cout,"-newbody:"); }
if (debug()>=9) { beginp->dumpTreeAndNext(cout,"-newbegi:"); }
//
// Create input variables
AstNode::user2ClearTree();
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
//
if (portp->isInout()) {
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() && outvscp) {
refp->v3error("Outputs not allowed in function declarations");
}
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);
// 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));
// 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);
// 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");
{
AstNode* pinp = refp->pinsp();
AstNode* nextpinp = pinp;
AstNode* nextstmtp;
for (AstNode* stmtp = newbodysp; stmtp; pinp=nextpinp, stmtp=nextstmtp) {
for (AstNode* stmtp = beginp; stmtp; stmtp=nextstmtp) {
nextstmtp = stmtp->nextp();
if (AstVar* portp = stmtp->castVar()) {
portp->unlinkFrBack(); // Remove it from the clone (not original)
pushDeletep(portp);
if (portp->isIO()) {
if (pinp==NULL) {
refp->v3error("Too few arguments in function call");
pinp = new AstConst(refp->fileline(), 0);
m_modp->addStmtp(pinp); // For below unlink
}
UINFO(9, " Port "<<portp<<endl);
UINFO(9, " pin "<<pinp<<endl);
//
nextpinp = pinp->nextp();
pinp->unlinkFrBack(); // Relinked to assignment below
//
if (portp->isInout()) {
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(TASKNSVAR,"Unsupported: Function/task input argument is not simple variable");
}
}
else if (portp->isOutput() && outvscp) {
refp->v3error("Outputs not allowed in function declarations");
}
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);
// 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));
// 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);
// Put assignment in FRONT of all other statements
if (AstNode* afterp = beginp->nextp()) {
afterp->unlinkFrBackWithNext();
assp->addNext(afterp);
}
beginp->addNext(assp);
}
}
else { // Var is not I/O
// Any I/O variables that fell out of above loop were already linked
if (!portp->user2p()) {
// Move it to a new localized variable
portp->unlinkFrBack(); pushDeletep(portp); // Remove it from the clone (not original)
AstVarScope* localVscp = createVarScope (portp, namePrefix+"__"+portp->shortName());
portp->user2p(localVscp);
}
}
}
if (pinp!=NULL) refp->v3error("Too many arguments in function call");
}
// Create function output variables
if (outvscp) {
@ -449,49 +448,43 @@ private:
AstCCall* ccallp = new AstCCall(refp->fileline(), cfuncp, NULL);
beginp->addNext(ccallp);
// Convert complicated outputs to temp signals
{
AstNode* pinp = refp->pinsp();
AstNode* nextpinp = pinp;
AstNode* nextstmtp;
for (AstNode* stmtp = refp->taskp()->stmtsp(); stmtp; pinp=nextpinp, stmtp=nextstmtp) {
nextstmtp = stmtp->nextp();
if (AstVar* portp = stmtp->castVar()) {
if (portp->isIO()) {
UINFO(9, " Port "<<portp<<endl);
UINFO(9, " pin "<<pinp<<endl);
//
nextpinp = pinp->nextp();
//
if (portp->isInout()) {
if (pinp->castVarRef()) {
// Connect to this exact variable
} else {
pinp->v3warn(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);
// 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));
// Put assignment BEHIND of all other statements
beginp->addNext(assp);
}
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);
if (portp->isInout()) {
if (pinp->castVarRef()) {
// Connect to this exact variable
} 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);
// 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));
// Put assignment BEHIND of all other statements
beginp->addNext(assp);
}
}
if (pinp!=NULL) refp->v3error("Too many arguments in function call");
}
// First argument is symbol table, then output if a function
ccallp->argTypes("vlSymsp");
@ -791,6 +784,52 @@ public:
//######################################################################
// Task class functions
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
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++;
}
}
}
// Connect pins
int ppinnum = 0;
for (AstNode* pinp = nodep->pinsp(); pinp; pinp=pinp->nextp()) {
if (ppinnum >= tpinnum) {
// Use v3warn so we'll only get the error once for each function
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;
} else {
tconnects[ppinnum].second = pinp;
ppinnum++;
}
}
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;
}
void V3Task::taskAll(AstNetlist* nodep) {
UINFO(2,__FUNCTION__<<": "<<endl);
TaskStateVisitor visitors (nodep);

View File

@ -26,12 +26,19 @@
#include "verilatedos.h"
#include "V3Error.h"
#include "V3Ast.h"
#include <vector>
//============================================================================
typedef pair<AstVar*,AstNode*> V3TaskConnect; // [port, pin-connects-to]
typedef vector<V3TaskConnect> V3TaskConnects; // [ [port, pin-connects-to] ... ]
//============================================================================
class V3Task {
public:
static void taskAll(AstNetlist* nodep);
static V3TaskConnects taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp); // Return vector of [port, pin-connects-to] (SLOW)
};
#endif // Guard

View File

@ -55,6 +55,7 @@
#include "V3Signed.h"
#include "V3Number.h"
#include "V3Const.h"
#include "V3Task.h"
//######################################################################
// Width state, as a visitor of each AstNode
@ -884,23 +885,19 @@ private:
}
// And do the arguments to the task/function too
for (int accept_mode=1; accept_mode>=0; accept_mode--) { // Avoid duplicate code; just do inner stuff twice
AstNode* pinp = nodep->pinsp();
for (AstNode* stmt_nextp, *stmtp = nodep->taskp()->stmtsp(); stmtp; stmtp=stmt_nextp) {
stmt_nextp = stmtp->nextp();
if (AstVar* portp = stmtp->castVar()) {
if (portp->isIO()
&& pinp!=NULL) { // Else argument error we'll find later
AstNode* pin_nextp = pinp->nextp(); // List may change, so remember nextp
if (accept_mode) {
// Prelim may cause the node to get replaced; we've lost our
// pointer, so need to iterate separately later
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),PRELIM).p()); pinp=NULL;
} else {
// Do PRELIM again, because above accept may have exited early due to node replacement
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),BOTH).p());
widthCheck(nodep,"Function Argument",pinp,portp->width(),portp->widthMin());
}
pinp = pin_nextp;
V3TaskConnects tconnects = V3Task::taskConnects(nodep, nodep->taskp()->stmtsp());
for (V3TaskConnects::iterator it=tconnects.begin(); it!=tconnects.end(); ++it) {
AstVar* portp = it->first;
AstNode* pinp = it->second;
if (pinp!=NULL) { // Else argument error we'll find later
if (accept_mode) {
// Prelim may cause the node to get replaced; we've lost our
// pointer, so need to iterate separately later
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),PRELIM).p()); pinp=NULL;
} else {
// Do PRELIM again, because above accept may have exited early due to node replacement
pinp->accept(*this,WidthVP(portp->width(),portp->widthMin(),BOTH).p());
widthCheck(nodep,"Function Argument",pinp,portp->width(),portp->widthMin());
}
}
}

View File

@ -11,11 +11,14 @@ compile (
v_flags2 => ["--lint-only"],
fails=>1,
expect=>
'%Error: t/t_func_bad.v:\d+: Too few arguments in function call
%Error: t/t_func_bad.v:\d+: Too many arguments in function call
%Error: t/t_func_bad.v:\d+: Too few arguments in function call
%Error: t/t_func_bad.v:\d+: Outputs not allowed in function declarations
%Error: Exiting due to',
q{%Error: t/t_func_bad.v:\d+: Too few arguments in function call to FUNC 'add'
%Error: t/t_func_bad.v:\d+: Too few arguments in function call to FUNC 'add'
%Error: t/t_func_bad.v:\d+: Too few arguments in function call to FUNC 'add'
%Error: t/t_func_bad.v:\d+: Too few arguments in function call to FUNC 'add'
%Error: t/t_func_bad.v:\d+: Too many arguments in function call to FUNC 'add'
%Error: t/t_func_bad.v:\d+: Too few arguments in function call to TASK 'x'
%Error: t/t_func_bad.v:\d+: Too few arguments in function call to TASK 'x'
%Error: Exiting due to},
);
ok(1);