diff --git a/src/V3Ast.h b/src/V3Ast.h index 241eb0383..fee64ac13 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -336,6 +336,7 @@ public: NO_INLINE_TASK, PUBLIC_MODULE, PUBLIC_TASK, + TIMEUNIT_SET, UNROLL_DISABLE, UNROLL_FULL, FULL_CASE, diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 7d0180908..5aa5b6a0f 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1494,18 +1494,24 @@ public: }; class AstPragma final : public AstNode { const VPragmaType m_pragType; // Type of pragma + const VTimescale m_timescale; // For TIMEUNIT_SET public: // Pragmas don't result in any output code, they're just flags that affect // other processing in verilator. AstPragma(FileLine* fl, VPragmaType pragType) : ASTGEN_SUPER_Pragma(fl) , m_pragType{pragType} {} + AstPragma(FileLine* fl, VPragmaType pragType, const VTimescale& timescale) + : ASTGEN_SUPER_Pragma(fl) + , m_pragType{pragType} + , m_timescale(timescale) {} ASTGEN_MEMBERS_AstPragma; VPragmaType pragType() const { return m_pragType; } // *=type of the pragma bool isPredictOptimizable() const override { return false; } bool sameNode(const AstNode* samep) const override { return pragType() == VN_DBG_AS(samep, Pragma)->pragType(); } + VTimescale timescale() const { return m_timescale; } }; class AstPropSpec final : public AstNode { // A clocked property diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index b982dbbe0..e90eda0a0 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -88,9 +88,24 @@ void V3LinkLevel::modSortByLevel() { void V3LinkLevel::timescaling(const ModVec& mods) { // Timescale determination const AstNodeModule* modTimedp = nullptr; - VTimescale unit(VTimescale::NONE); + VTimescale unit{VTimescale::NONE}; + + // Move timeunit attributes from parse to module unit + // Grammar only allows timeunit as module_item, so no need to recurse full tree + for (AstNodeModule* modp : mods) { + for (AstNode *nextp, *childp = modp->stmtsp(); childp; childp = nextp) { + nextp = childp->nextp(); + if (AstPragma* pragp = VN_CAST(childp, Pragma)) { + if (pragp->pragType() == VPragmaType::TIMEUNIT_SET) { + modp->timeunit(pragp->timescale()); + VL_DO_DANGLING(pragp->unlinkFrBack()->deleteTree(), pragp); + } + } + } + } // Use highest level module as default unit - already sorted in proper order - for (const auto& modp : mods) { + // Combine timing into later modules + for (AstNodeModule* modp : mods) { if (!modTimedp && !modp->timeunit().isNone()) { modTimedp = modp; unit = modTimedp->timeunit(); @@ -106,24 +121,24 @@ void V3LinkLevel::timescaling(const ModVec& mods) { if (!upkgp->timeunit().isNone()) dunitTimed = true; } - for (AstNodeModule* nodep : mods) { - if (!v3Global.opt.timeOverrideUnit().isNone()) nodep->timeunit(unit); - if (nodep->timeunit().isNone()) { + for (AstNodeModule* modp : mods) { + if (!v3Global.opt.timeOverrideUnit().isNone()) modp->timeunit(unit); + if (modp->timeunit().isNone()) { if (modTimedp // Got previous && !dunitTimed && ( // unit doesn't already include an override v3Global.opt.timeOverrideUnit().isNone() && v3Global.opt.timeDefaultUnit().isNone()) - && nodep->timescaleMatters()) { - nodep->v3warn(TIMESCALEMOD, - "Timescale missing on this module as other modules have " - "it (IEEE 1800-2023 3.14.2.3)\n" - << nodep->warnContextPrimary() << '\n' - << modTimedp->warnOther() - << "... Location of module with timescale\n" - << modTimedp->warnContextSecondary()); + && modp->timescaleMatters()) { + modp->v3warn(TIMESCALEMOD, + "Timescale missing on this module as other modules have " + "it (IEEE 1800-2023 3.14.2.3)\n" + << modp->warnContextPrimary() << '\n' + << modTimedp->warnOther() + << "... Location of module with timescale\n" + << modTimedp->warnContextSecondary()); } - nodep->timeunit(unit); + modp->timeunit(unit); } } diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index d71d6a090..8e6613fe1 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -126,8 +126,8 @@ void V3ParseImp::lexTimescaleParse(FileLine* fl, const char* textp) { m_timeLastUnit = v3Global.opt.timeComputeUnit(unit); v3Global.rootp()->timeprecisionMerge(fl, prec); } -void V3ParseImp::timescaleMod(FileLine* fl, AstNodeModule* modp, bool unitSet, double unitVal, - bool precSet, double precVal) { +AstPragma* V3ParseImp::createTimescale(FileLine* fl, bool unitSet, double unitVal, bool precSet, + double precVal) { VTimescale unit{VTimescale::NONE}; if (unitSet) { bool bad; @@ -146,16 +146,13 @@ void V3ParseImp::timescaleMod(FileLine* fl, AstNodeModule* modp, bool unitSet, d fl->v3error("timeprecision illegal value"); } } - if (!unit.isNone()) { - unit = v3Global.opt.timeComputeUnit(unit); - if (modp) { - modp->timeunit(unit); - } else { - v3Global.rootp()->timeunit(unit); - unitPackage(fl)->timeunit(unit); - } - } v3Global.rootp()->timeprecisionMerge(fl, prec); + if (unit.isNone()) { + return nullptr; + } else { + unit = v3Global.opt.timeComputeUnit(unit); + return new AstPragma{fl, VPragmaType::TIMEUNIT_SET, unit}; + } } void V3ParseImp::lexVerilatorCmtLintSave(const FileLine* fl) { m_lexLintState.push_back(*fl); } diff --git a/src/V3ParseImp.h b/src/V3ParseImp.h index e6ebcbc92..5fcf49490 100644 --- a/src/V3ParseImp.h +++ b/src/V3ParseImp.h @@ -181,8 +181,8 @@ public: void tagNodep(AstNode* nodep) { m_tagNodep = nodep; } AstNode* tagNodep() const { return m_tagNodep; } void lexTimescaleParse(FileLine* fl, const char* textp) VL_MT_DISABLED; - void timescaleMod(FileLine* fl, AstNodeModule* modp, bool unitSet, double unitVal, - bool precSet, double precVal) VL_MT_DISABLED; + AstPragma* createTimescale(FileLine* fl, bool unitSet, double unitVal, bool precSet, + double precVal) VL_MT_DISABLED; VTimescale timeLastUnit() const { return m_timeLastUnit; } void lexFileline(FileLine* fl) { m_lexFileline = fl; } diff --git a/src/verilog.y b/src/verilog.y index 80ed887c1..1a4940496 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1227,14 +1227,11 @@ description: // ==IEEE: description timeunits_declaration: // ==IEEE: timeunits_declaration yTIMEUNIT yaTIMENUM ';' - { PARSEP->timescaleMod($2, SYMP->findTopNodeModule($1, false), true, $2, false, 0); - $$ = nullptr; } + { $$ = PARSEP->createTimescale($2, true, $2, false, 0); } | yTIMEUNIT yaTIMENUM '/' yaTIMENUM ';' - { PARSEP->timescaleMod($2, SYMP->findTopNodeModule($1, false), true, $2, true, $4); - $$ = nullptr; } + { $$ = PARSEP->createTimescale($2, true, $2, true, $4); } | yTIMEPRECISION yaTIMENUM ';' - { PARSEP->timescaleMod($2, SYMP->findTopNodeModule($1, false), false, 0, true, $2); - $$ = nullptr; } + { $$ = PARSEP->createTimescale($2, false, 0, true, $2); } ; //**********************************************************************