diff --git a/bin/verilator_difftree b/bin/verilator_difftree index c7e92b228..23d62428b 100755 --- a/bin/verilator_difftree +++ b/bin/verilator_difftree @@ -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"; } diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index b388b690f..189826de2 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -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 { diff --git a/src/V3Error.h b/src/V3Error.h index 3845e8a0c..2991cfdb4 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -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 // diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index 753ff737a..8973cad9d 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -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: " <prettyName()<<" and "<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; } diff --git a/src/V3Simulate.h b/src/V3Simulate.h index c89325ba0..ee4ffb4f8 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -43,6 +43,7 @@ #include "V3Error.h" #include "V3Ast.h" #include "V3Width.h" +#include "V3Task.h" #include @@ -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)); } } } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 6e09e0df8..a85e10117 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -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 "<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 "<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 "<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 "<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 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 "<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 "<taskp()->prettyTypeName()); + UINFO(1,"missing argument for '"<prettyName()<<"'"< + +//============================================================================ + +typedef pair V3TaskConnect; // [port, pin-connects-to] +typedef vector 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 diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 1396cd45a..2038f6222 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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()); } } } diff --git a/test_regress/t/t_func_bad.pl b/test_regress/t/t_func_bad.pl index fe7a8b11f..c08aae55b 100755 --- a/test_regress/t/t_func_bad.pl +++ b/test_regress/t/t_func_bad.pl @@ -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);