diff --git a/Changes b/Changes index 9857e2d32..26a7feef3 100644 --- a/Changes +++ b/Changes @@ -7,6 +7,8 @@ indicates the contributor was also the author of the fix; Thanks! *** Support runtime access to public signal names. +*** Add /*verilator public_flat_rw*/ for timing-specific access. + **** Improve error handling on slices of arrays, bug226. [by Bryon Bradley] **** Report errors when extra underscores used in meta-comments. diff --git a/bin/verilator b/bin/verilator index 9f60fb22c..393b3d482 100755 --- a/bin/verilator +++ b/bin/verilator @@ -1770,6 +1770,18 @@ signal. This will not declare this module public, which means the name of the signal or path to it may change based upon the module inlining which takes place. +=item /*verilator public_flat_rd*/ (variable) + +Used after a input, output, register, or wire declaration to indicate the +signal should be declared public_flat (see above), but read-only. + +=item /*verilator public_flat_rw @() */ (variable) + +Used after a input, output, register, or wire declaration to indicate the +signal should be declared public_flat_rd (see above), and also writable, +where writes should be considered to have the timing specified by the given +sensitivity edge list. + =item /*verilator public_module*/ Used after a module statement to indicate the module should not be inlined diff --git a/include/verilated.cpp b/include/verilated.cpp index 3bee89ebc..d14a8472f 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -995,6 +995,10 @@ void Verilated::scopesDump() { VerilatedImp::scopesDump(); } +const VerilatedScope* Verilated::scopeFind(const char* namep) { + return VerilatedImp::scopeFind(namep); +} + int Verilated::exportFuncNum(const char* namep) { return VerilatedImp::exportFind(namep); } @@ -1010,6 +1014,23 @@ VerilatedModule::~VerilatedModule() { if (m_namep) free((void*)m_namep); m_namep=NULL; } +//====================================================================== +// VerilatedVar:: Methods + +vluint32_t VerilatedVar::entSize() const { + vluint32_t size = 1; + switch (vltype()) { + case VLVT_PTR: size=sizeof(void*); break; + case VLVT_UINT8: size=sizeof(CData); break; + case VLVT_UINT16: size=sizeof(SData); break; + case VLVT_UINT32: size=sizeof(IData); break; + case VLVT_UINT64: size=sizeof(QData); break; + case VLVT_WDATA: size=VL_WORDS_I(range().bits())*sizeof(IData); break; + default: size=0; break; + } + return size; +} + //====================================================================== // VerilatedScope:: Methods @@ -1060,13 +1081,14 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) { } } -void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, VerilatedVarType vltype, int dims, ...) { +void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, + VerilatedVarType vltype, int vlflags, int dims, ...) { // Grab dimensions // In the future we may just create a large table at emit time and statically construct from that. if (!finalize) return; if (!m_varsp) m_varsp = new VerilatedVarNameMap(); - VerilatedVar var (namep, datap, vltype); + VerilatedVar var (namep, datap, vltype, (VerilatedVarFlags)vlflags, dims); va_list ap; va_start(ap,dims); diff --git a/include/verilated.h b/include/verilated.h index 522ce699a..9c79fbd7d 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -82,6 +82,17 @@ enum VerilatedVarType { VLVT_STRING // C++ string }; +enum VerilatedVarFlags { + VLVD_IN=1, // == vpiInput + VLVD_OUT=2, // == vpiOutput + VLVD_INOUT=3, // == vpiInOut + VLVD_NODIR=5, // == vpiNoDirection + VLVF_MASK_DIR=7, // Bit mask for above directions + // Flags + VLVF_PUB_RD=(1<<8), // Public readable + VLVF_PUB_RW=(1<<9) // Public writable +}; + //========================================================================= /// Base class for all Verilated module classes @@ -151,6 +162,9 @@ public: #ifndef VL_PRINTF # define VL_PRINTF printf ///< Print ala printf; may redefine if desired #endif +#ifndef VL_VPRINTF +# define VL_VPRINTF vprintf ///< Print ala vprintf; may redefine if desired +#endif //=========================================================================== /// Verilator symbol table base class @@ -176,7 +190,8 @@ public: // But internals only - called from VerilatedModule's ~VerilatedScope(); void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp); void exportInsert(int finalize, const char* namep, void* cb); - void varInsert(int finalize, const char* namep, void* datap, VerilatedVarType vltype, int dims, ...); + void varInsert(int finalize, const char* namep, void* datap, + VerilatedVarType vltype, int vlflags, int dims, ...); // ACCESSORS const char* name() const { return m_namep; } inline VerilatedSyms* symsp() const { return m_symsp; } @@ -264,6 +279,8 @@ public: // METHODS - INTERNAL USE ONLY // Internal: Create a new module name by concatenating two strings static const char* catName(const char* n1, const char* n2); // Returns new'ed data + // Internal: Find scope + static const VerilatedScope* scopeFind(const char* namep); // Internal: Get and set DPI context static const VerilatedScope* dpiScope() { return t_dpiScopep; } static void dpiScope(const VerilatedScope* scopep) { t_dpiScopep=scopep; } diff --git a/include/verilated_syms.h b/include/verilated_syms.h index ec11504cb..a6b6608b0 100644 --- a/include/verilated_syms.h +++ b/include/verilated_syms.h @@ -57,6 +57,7 @@ public: ~VerilatedRange() {} int lhs() const { return m_lhs; } int rhs() const { return m_rhs; } + int bits() const { return (VL_LIKELY(m_lhs>=m_rhs)?(m_lhs-m_rhs+1):(m_rhs-m_lhs+1)); } }; //=========================================================================== @@ -65,20 +66,27 @@ public: class VerilatedVar { void* m_datap; // Location of data VerilatedVarType m_vltype; // Data type + VerilatedVarFlags m_vlflags; // Direction VerilatedRange m_range; // First range VerilatedRange m_array; // Array + int m_dims; // Dimensions const char* m_namep; // Name - slowpath protected: friend class VerilatedScope; - VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype) - : m_datap(datap), m_vltype(vltype), m_namep(namep) {} + VerilatedVar(const char* namep, void* datap, + VerilatedVarType vltype, VerilatedVarFlags vlflags, int dims) + : m_datap(datap), m_vltype(vltype), m_vlflags(vlflags), m_dims(dims), m_namep(namep) {} public: ~VerilatedVar() {} void* datap() const { return m_datap; } VerilatedVarType vltype() const { return m_vltype; } + VerilatedVarFlags vldir() const { return (VerilatedVarFlags)((int)m_vlflags & VLVF_MASK_DIR); } + vluint32_t entSize() const; + bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); } const VerilatedRange& range() const { return m_range; } const VerilatedRange& array() const { return m_array; } - const char* namep() const { return m_namep; } + const char* name() const { return m_namep; } + int dims() const { return m_dims; } }; //====================================================================== diff --git a/src/V3Active.cpp b/src/V3Active.cpp index b421a4681..03d9ddfd7 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -263,22 +263,14 @@ private: } // METHODS - virtual void visit(AstAlways* nodep, AstNUser*) { + void visitAlways(AstNode* nodep, AstSenTree* oldsensesp) { // Move always to appropriate ACTIVE based on its sense list - UINFO(4," ALW "<=9) nodep->dumpTree(cout," Alw: "); - - if (!nodep->bodysp()) { - // Empty always. Kill it. - nodep->unlinkFrBack()->deleteTree(); nodep=NULL; - return; - } - if (nodep->sensesp() - && nodep->sensesp()->sensesp() - && nodep->sensesp()->sensesp()->castSenItem() - && nodep->sensesp()->sensesp()->castSenItem()->isNever()) { + if (oldsensesp + && oldsensesp->sensesp() + && oldsensesp->sensesp()->castSenItem() + && oldsensesp->sensesp()->castSenItem()->isNever()) { // Never executing. Kill it. - if (nodep->sensesp()->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated."); + if (oldsensesp->sensesp()->nextp()) nodep->v3fatalSrc("Never senitem should be alone, else the never should be eliminated."); nodep->unlinkFrBack()->deleteTree(); nodep=NULL; return; } @@ -286,7 +278,7 @@ private: // Read sensitivitues m_itemCombo = false; m_itemSequent = false; - nodep->sensesp()->iterateAndNext(*this); + oldsensesp->iterateAndNext(*this); bool combo = m_itemCombo; bool sequent = m_itemSequent; @@ -308,15 +300,15 @@ private: // always (posedge RESET) { if (RESET).... } we know RESET is true. // Summarize a long list of combo inputs as just "combo" #ifndef __COVERITY__ // Else dead code on next line. - if (combo) nodep->sensesp()->addSensesp + if (combo) oldsensesp->addSensesp (new AstSenItem(nodep->fileline(),AstSenItem::Combo())); #endif - wantactivep = m_namer.getActive(nodep->fileline(), nodep->sensesp()); + wantactivep = m_namer.getActive(nodep->fileline(), oldsensesp); } // Delete sensitivity list - if (AstNode* oldsense = nodep->sensesp()) { - oldsense->unlinkFrBackWithNext()->deleteTree(); oldsense=NULL; + if (oldsensesp) { + oldsensesp->unlinkFrBackWithNext()->deleteTree(); oldsensesp=NULL; } // Move node to new active @@ -328,6 +320,24 @@ private: ActiveDlyVisitor dlyvisitor (nodep); } } + virtual void visit(AstAlways* nodep, AstNUser*) { + // Move always to appropriate ACTIVE based on its sense list + UINFO(4," ALW "<=9) nodep->dumpTree(cout," Alw: "); + + if (!nodep->bodysp()) { + // Empty always. Kill it. + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + return; + } + visitAlways(nodep, nodep->sensesp()); + } + virtual void visit(AstAlwaysPublic* nodep, AstNUser*) { + // Move always to appropriate ACTIVE based on its sense list + UINFO(4," ALWPub "<=9) nodep->dumpTree(cout," Alw: "); + visitAlways(nodep, nodep->sensesp()); + } virtual void visit(AstSenGate* nodep, AstNUser*) { AstSenItem* subitemp = nodep->sensesp(); if (subitemp->edgeType() != AstEdgeType::ET_ANYEDGE diff --git a/src/V3ActiveTop.cpp b/src/V3ActiveTop.cpp index 113ef227f..4a9bdea3b 100644 --- a/src/V3ActiveTop.cpp +++ b/src/V3ActiveTop.cpp @@ -133,6 +133,9 @@ private: virtual void visit(AstAlways* nodep, AstNUser*) { nodep->v3fatalSrc("Node should have been under ACTIVE"); } + virtual void visit(AstAlwaysPublic* nodep, AstNUser*) { + nodep->v3fatalSrc("Node should have been under ACTIVE"); + } virtual void visit(AstFinal* nodep, AstNUser*) { nodep->v3fatalSrc("Node should have been deleted"); } diff --git a/src/V3Ast.h b/src/V3Ast.h index 2a5e4cd5f..8b1f3bf6a 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -179,6 +179,8 @@ public: VAR_CLOCK_ENABLE, // V3LinkParse moves to AstVar::attrClockEn VAR_PUBLIC, // V3LinkParse moves to AstVar::sigPublic VAR_PUBLIC_FLAT, // V3LinkParse moves to AstVar::sigPublic + VAR_PUBLIC_FLAT_RD, // V3LinkParse moves to AstVar::sigPublic + VAR_PUBLIC_FLAT_RW, // V3LinkParse moves to AstVar::sigPublic VAR_ISOLATE_ASSIGNMENTS, // V3LinkParse moves to AstVar::attrIsolateAssign VAR_SFORMAT // V3LinkParse moves to AstVar::attrSFormat }; @@ -186,7 +188,8 @@ public: const char* ascii() const { static const char* names[] = { "EXPR_BITS", "VAR_BASE", - "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT", + "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", + "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD","VAR_PUBLIC_FLAT_RW", "VAR_ISOLATE_ASSIGNMENTS", "VAR_SFORMAT" }; return names[m_e]; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 0ba219aae..da70c336f 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -162,6 +162,18 @@ string AstVar::vlEnumType() const { return arg; } +string AstVar::vlEnumDir() const { + if (isInout()) { + return "VLVD_INOUT"; + } else if (isInOnly()) { + return "VLVD_IN"; + } else if (isOutOnly()) { + return "VLVD_OUT"; + } else { + return "VLVD_NODIR"; + } +} + string AstVar::cPubArgType(bool named, bool forReturn) const { if (forReturn) named=false; string arg; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 5568ebfeb..a611bdc0a 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -544,7 +544,8 @@ private: bool m_scSensitive:1;// SystemC sensitive() needed bool m_sigPublic:1; // User C code accesses this signal or is top signal bool m_sigModPublic:1;// User C code accesses this signal and module - bool m_sigUserPublic:1; // User C code accesses this signal + bool m_sigUserRdPublic:1; // User C code accesses this signal, read only + bool m_sigUserRWPublic:1; // User C code accesses this signal, read-write bool m_usedClock:1; // Signal used as a clock bool m_usedParam:1; // Parameter is referenced (on link; later signals not setup) bool m_funcLocal:1; // Local variable for a function @@ -562,7 +563,7 @@ private: m_primaryIO=false; m_sc=false; m_scClocked=false; m_scSensitive=false; m_usedClock=false; m_usedParam=false; - m_sigPublic=false; m_sigModPublic=false; m_sigUserPublic=false; + m_sigPublic=false; m_sigModPublic=false; m_sigUserRdPublic=false; m_sigUserRWPublic=false; m_funcLocal=false; m_funcReturn=false; m_attrClockEn=false; m_attrIsolateAssign=false; m_attrSFormat=false; m_fileDescr=false; m_isConst=false; m_isStatic=false; @@ -608,7 +609,8 @@ public: string cPubArgType(bool named, bool forReturn) const; // Return C /*public*/ type for argument: bool, uint32_t, uint64_t, etc. string dpiArgType(bool named, bool forReturn) const; // Return DPI-C type for argument string vlArgType(bool named, bool forReturn) const; // Return Verilator internal type for argument: CData, SData, IData, WData - string vlEnumType() const; // Return VerilatorImp enum name for argument: VLVT_UINT32, etc + string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc + string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc void combineType(AstVarType type); AstNodeDType* dtypep() const { return op1p()->castNodeDType(); } // op1 = Range of variable AstNodeDType* dtypeSkipRefp() const { return dtypep()->skipRefp(); } // op1 = Range of variable (Note don't need virtual - AstVar isn't a NodeDType) @@ -630,7 +632,8 @@ public: void usedParam(bool flag) { m_usedParam = flag; } void sigPublic(bool flag) { m_sigPublic = flag; } void sigModPublic(bool flag) { m_sigModPublic = flag; } - void sigUserPublic(bool flag) { m_sigUserPublic = flag; if (flag) m_sigPublic = flag; } + void sigUserRdPublic(bool flag) { m_sigUserRdPublic = flag; if (flag) sigPublic(true); } + void sigUserRWPublic(bool flag) { m_sigUserRWPublic = flag; if (flag) sigUserRdPublic(true); } void sc(bool flag) { m_sc = flag; } void scSensitive(bool flag) { m_scSensitive = flag; } void primaryIO(bool flag) { m_primaryIO = flag; } @@ -673,7 +676,8 @@ public: bool isScSensitive() const { return m_scSensitive; } bool isSigPublic() const; bool isSigModPublic() const { return m_sigModPublic; } - bool isSigUserPublic() const { return m_sigUserPublic; } + bool isSigUserRdPublic() const { return m_sigUserRdPublic; } + bool isSigUserRWPublic() const { return m_sigUserRWPublic; } bool isTrace() const { return m_trace; } bool isConst() const { return m_isConst; } bool isStatic() const { return m_isStatic; } @@ -703,7 +707,8 @@ public: combineType(typevarp->varType()); if (typevarp->isSigPublic()) sigPublic(true); if (typevarp->isSigModPublic()) sigModPublic(true); - if (typevarp->isSigUserPublic()) sigUserPublic(true); + if (typevarp->isSigUserRdPublic()) sigUserRdPublic(true); + if (typevarp->isSigUserRWPublic()) sigUserRWPublic(true); if (typevarp->attrScClocked()) attrScClocked(true); } void inlineAttrReset(const string& name) { @@ -1270,6 +1275,24 @@ struct AstAlways : public AstNode { bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); } }; +struct AstAlwaysPublic : public AstNodeStmt { + // "Fake" sensitivity created by /*verilator public_flat_rw @(edgelist)*/ + // Body statements are just AstVarRefs to the public signals + AstAlwaysPublic(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp) + : AstNodeStmt(fl) { + addNOp1p(sensesp); addNOp2p(bodysp); + } + ASTNODE_NODE_FUNCS(AlwaysPublic, ALWAYSPUBLIC) + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(AstNode* samep) const { return true; } + // + AstSenTree* sensesp() const { return op1p()->castSenTree(); } // op1 = Sensitivity list + AstNode* bodysp() const { return op2p()->castNode(); } // op2 = Statements to evaluate + void addStmtp(AstNode* nodep) { addOp2p(nodep); } + // Special accessors + bool isJustOneBodyStmt() const { return bodysp() && !bodysp()->nextp(); } +}; + struct AstAlwaysPost : public AstNode { // Like always but post assignments for memory assignment IFs AstAlwaysPost(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp) diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp index 99f25276b..f05a8c25f 100644 --- a/src/V3Cdc.cpp +++ b/src/V3Cdc.cpp @@ -642,6 +642,9 @@ private: virtual void visit(AstAlways* nodep, AstNUser*) { iterateNewStmt(nodep); } + virtual void visit(AstAlwaysPublic* nodep, AstNUser*) { + // CDC doesn't care about public variables + } virtual void visit(AstCFunc* nodep, AstNUser*) { iterateNewStmt(nodep); } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index a2f3ef4fa..c8af37f14 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -149,6 +149,8 @@ public: if (decind) ofp()->blockDec(); if (!m_suppressSemi) puts(";\n"); } + virtual void visit(AstAlwaysPublic*, AstNUser*) { + } virtual void visit(AstCCall* nodep, AstNUser*) { puts(nodep->hiername()); puts(nodep->funcp()->name()); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 2c0504c62..d74b7b95a 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -205,7 +205,7 @@ class EmitCSyms : EmitCBaseVisitor { } virtual void visit(AstVar* nodep, AstNUser*) { nodep->iterateChildren(*this); - if (nodep->isSigUserPublic()) { + if (nodep->isSigUserRdPublic()) { m_modVars.push_back(make_pair(m_modp, nodep)); } } @@ -487,6 +487,10 @@ void EmitCSyms::emitSymImp() { puts("), "); puts(varp->vlEnumType()); // VLVT_UINT32 etc puts(","); + puts(varp->vlEnumDir()); // VLVD_IN etc + if (varp->isSigUserRWPublic()) puts("|VLVF_PUB_RW"); + else if (varp->isSigUserRdPublic()) puts("|VLVF_PUB_RD"); + puts(","); puts(cvtToStr(dim)); puts(bounds); puts(");\n"); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 253f26153..6b5f64a31 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -109,6 +109,14 @@ class EmitVBaseVisitor : public EmitCBaseVisitor { nodep->bodysp()->iterateAndNext(*this); putqs(nodep,"end\n"); } + virtual void visit(AstAlwaysPublic* nodep, AstNUser*) { + putfs(nodep,"/*verilator public_flat_rw "); + if (m_sensesp) m_sensesp->iterateAndNext(*this); // In active + else nodep->sensesp()->iterateAndNext(*this); + putqs(nodep," "); + nodep->bodysp()->iterateAndNext(*this); + putqs(nodep,"*/\n"); + } virtual void visit(AstNodeAssign* nodep, AstNUser*) { nodep->lhsp()->iterateAndNext(*this); putfs(nodep," "+nodep->verilogKwd()+" "); diff --git a/src/V3Gate.cpp b/src/V3Gate.cpp index cb7090fee..7a1d1a20a 100644 --- a/src/V3Gate.cpp +++ b/src/V3Gate.cpp @@ -357,6 +357,12 @@ private: virtual void visit(AstAlways* nodep, AstNUser*) { iterateNewStmt(nodep, (nodep->isJustOneBodyStmt()?NULL:"Multiple Stmts"), NULL); } + virtual void visit(AstAlwaysPublic* nodep, AstNUser*) { + bool lastslow = m_inSlow; + m_inSlow = true; + iterateNewStmt(nodep, "AlwaysPublic", NULL); + m_inSlow = lastslow; + } virtual void visit(AstCFunc* nodep, AstNUser*) { iterateNewStmt(nodep, "User C Function", "User C Function"); } diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index aa9c8c6ab..c027bc64a 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -155,7 +155,7 @@ private: // Public output inside the cell must go via an assign rather than alias // Else the public logic will set the alias, loosing the value to be propagated up // (InOnly isn't a problem as the AssignAlias will create the assignment for us) - pinNewVarp->user3(pinNewVarp->isSigUserPublic() && pinNewVarp->isOutOnly()); + pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly()); } // Cleanup var names, etc, to not conflict m_cellp = nodep; diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 3a8251ac9..533117fbb 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -274,12 +274,22 @@ private: } else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) { if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->sigUserPublic(true); m_varp->sigModPublic(true); + m_varp->sigUserRWPublic(true); m_varp->sigModPublic(true); nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) { if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); - m_varp->sigUserPublic(true); + m_varp->sigUserRWPublic(true); + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RD) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->sigUserRdPublic(true); + nodep->unlinkFrBack()->deleteTree(); nodep=NULL; + } + else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RW) { + if (!m_varp) nodep->v3fatalSrc("Attribute not attached to variable"); + m_varp->sigUserRWPublic(true); nodep->unlinkFrBack()->deleteTree(); nodep=NULL; } else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) { @@ -294,6 +304,20 @@ private: } } + virtual void visit(AstAlwaysPublic* nodep, AstNUser*) { + // AlwaysPublic was attached under a var, but it's a statement that should be + // at the same level as the var + nodep->iterateChildren(*this); + if (m_varp) { + nodep->unlinkFrBack(); + m_varp->addNext(nodep); + // lvalue is true, because we know we have a verilator public_flat_rw + // but someday we may be more general + bool lvalue = m_varp->isSigUserRWPublic(); + nodep->addStmtp(new AstVarRef(nodep->fileline(), m_varp, lvalue)); + } + } + virtual void visit(AstDefImplicitDType* nodep, AstNUser*) { UINFO(8," DEFIMPLICIT "< "/*verilator public_flat_rw*/ @(foo)" + cmd = cmd.substr(pos+strlen("public_flat_rw")); + while (isspace(cmd[0])) cmd = cmd.substr(1); + if ((pos=cmd.find("*/")) != string::npos) + cmd.replace(pos, 2, ""); + insertUnreadback ("/*verilator public_flat_rw*/ "+cmd+" /**/"); } else { insertUnreadback ("/*verilator "+cmd+"*/"); } diff --git a/src/V3Scope.cpp b/src/V3Scope.cpp index 897dc6229..1ffa02de8 100644 --- a/src/V3Scope.cpp +++ b/src/V3Scope.cpp @@ -165,6 +165,14 @@ private: m_scopep->addActivep(clonep); clonep->iterateChildren(*this); // We iterate under the *clone* } + virtual void visit(AstAlwaysPublic* nodep, AstNUser*) { + // Add to list of blocks under this scope + UINFO(4," Move "<cloneTree(false); + nodep->user2p(clonep); + m_scopep->addActivep(clonep); + clonep->iterateChildren(*this); // We iterate under the *clone* + } virtual void visit(AstCoverToggle* nodep, AstNUser*) { // Add to list of blocks under this scope UINFO(4," Move "<verilatorCmtLintRestore(); } "/*verilator lint_save*/" {PARSEP->verilatorCmtLintSave(); } - "/*"[^*]*"*/" {PARSEP->verilatorCmtBad(yytext); } + "/**/" { } + "/*"[^*]+"*/" {PARSEP->verilatorCmtBad(yytext); } } /************************************************************************/ diff --git a/src/verilog.y b/src/verilog.y index 6de86c676..70f4086de 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -434,6 +434,8 @@ class AstSenTree; %token yVL_PARALLEL_CASE "/*verilator parallel_case*/" %token yVL_PUBLIC "/*verilator public*/" %token yVL_PUBLIC_FLAT "/*verilator public_flat*/" +%token yVL_PUBLIC_FLAT_RD "/*verilator public_flat_rd*/" +%token yVL_PUBLIC_FLAT_RW "/*verilator public_flat_rw*/" %token yVL_PUBLIC_MODULE "/*verilator public_module*/" %token yP_TICK "'" @@ -1541,6 +1543,10 @@ sigAttr: | yVL_CLOCK_ENABLE { $$ = new AstAttrOf($1,AstAttrType::VAR_CLOCK_ENABLE); } | yVL_PUBLIC { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC); } | yVL_PUBLIC_FLAT { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT); } + | yVL_PUBLIC_FLAT_RD { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RD); } + | yVL_PUBLIC_FLAT_RW { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RW); } + | yVL_PUBLIC_FLAT_RW attr_event_control { $$ = new AstAttrOf($1,AstAttrType::VAR_PUBLIC_FLAT_RW); + $$ = $$->addNext(new AstAlwaysPublic($1,$2,NULL)); } | yVL_ISOLATE_ASSIGNMENTS { $$ = new AstAttrOf($1,AstAttrType::VAR_ISOLATE_ASSIGNMENTS); } | yVL_SFORMAT { $$ = new AstAttrOf($1,AstAttrType::VAR_SFORMAT); } ; @@ -1679,6 +1685,12 @@ cellpinItemE: // IEEE: named_port_connection + named_parameter_assignment //************************************************ // EventControl lists +attr_event_control: // ==IEEE: event_control + '@' '(' event_expression ')' { $$ = new AstSenTree($1,$3); } + | '@' '(' '*' ')' { $$ = NULL; } + | '@' '*' { $$ = NULL; } + ; + event_controlE: /* empty */ { $$ = NULL; } | event_control { $$ = $1; } diff --git a/test_regress/t/t_dpi_var.cpp b/test_regress/t/t_dpi_var.cpp index 4a608dc88..aac4966a8 100644 --- a/test_regress/t/t_dpi_var.cpp +++ b/test_regress/t/t_dpi_var.cpp @@ -100,11 +100,11 @@ void mon_register_done() { } } -bool mon_eval() { +extern "C" void mon_eval(); +void mon_eval() { // Callback from always@ negedge mon_do(&mons[0]); mon_do(&mons[1]); - return false; } //====================================================================== diff --git a/test_regress/t/t_dpi_var.v b/test_regress/t/t_dpi_var.v index c83765273..8c42c420d 100644 --- a/test_regress/t/t_dpi_var.v +++ b/test_regress/t/t_dpi_var.v @@ -44,11 +44,17 @@ module t (/*AUTOARG*/ end end + + always @(posedge t.monclk) begin + mon_eval(); + end + endmodule import "DPI-C" context function void mon_scope_name (input string formatted /*verilator sformat*/ ); import "DPI-C" context function void mon_register_b(string name, int isOut); import "DPI-C" context function void mon_register_done(); +import "DPI-C" context function void mon_eval(); module sub (/*AUTOARG*/ // Outputs @@ -60,17 +66,13 @@ module sub (/*AUTOARG*/ `systemc_imp_header void mon_class_name(const char* namep); void mon_register_a(const char* namep, void* sigp, bool isOut); - bool mon_eval(); `verilog - input int in /*verilator public_flat*/; - output int fr_a /*verilator public_flat*/; - output int fr_b /*verilator public_flat*/; + input int in /*verilator public_flat_rd*/; + output int fr_a /*verilator public_flat_rw @(posedge t.monclk)*/; + output int fr_b /*verilator public_flat_rw @(posedge t.monclk)*/; output int fr_chk; - reg [3:0] onearray [1:2] /*verilator public_flat*/; - reg [3:0] twoarray [1:2][3:4] /*verilator public_flat*/; - always @* fr_chk = in + 1; initial begin @@ -86,15 +88,4 @@ module sub (/*AUTOARG*/ mon_register_done(); end - always @(posedge t.monclk) begin - // Hack - Write all outputs, so the data will get scheduled to propagate out of this module - // FUTURE Long term, we should detect a public signal has been written - // with a new value, and create an event that flips monclk automatically - if ($c1("mon_eval()")) begin - // This code doesn't execute, just is here so verilator schedules the code. - fr_a = 0; - fr_b = 0; - end - end - endmodule