diff --git a/Changes b/Changes index 5120975d3..4e1f71fe7 100644 --- a/Changes +++ b/Changes @@ -18,6 +18,7 @@ Verilator 5.037 devel * Support assignments to concatenations with impure RHS (#6002). [Ryszard Rozak, Antmicro Ltd.] * Support SARIF JSON diagnostic output with `--diagnostics-sarif`. (#6017) * Support 1-bit params with -G and -pvalue (#6051) (#6082). [Paul Swirhun] +* Support `$timeformat` with missing arguments (#6113). [Alex Solomatnikov] * Support parameter forward types. * Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon] * Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu] diff --git a/include/verilated.cpp b/include/verilated.cpp index a1e6975a0..eb59da7fa 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -2567,12 +2567,13 @@ void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp, contextp->timeprecisionString()); } -void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width, +void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int precision, bool hasSuffix, + const std::string& suffix, bool hasWidth, int width, VerilatedContext* contextp) VL_MT_SAFE { - contextp->impp()->timeFormatUnits(units); - contextp->impp()->timeFormatPrecision(precision); - contextp->impp()->timeFormatSuffix(suffix); - contextp->impp()->timeFormatWidth(width); + if (hasUnits) contextp->impp()->timeFormatUnits(units); + if (hasPrecision) contextp->impp()->timeFormatPrecision(precision); + if (hasSuffix) contextp->impp()->timeFormatSuffix(suffix); + if (hasWidth) contextp->impp()->timeFormatWidth(width); } //====================================================================== diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index e62e9f172..03b3ed685 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -2800,7 +2800,8 @@ extern IData VL_SSCANF_INNX(int lbits, const std::string& ld, const std::string& extern void VL_SFORMAT_NX(int obits_ignored, std::string& output, const std::string& format, int argc, ...) VL_MT_SAFE; extern std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE; -extern void VL_TIMEFORMAT_IINI(int units, int precision, const std::string& suffix, int width, +extern void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int precision, + bool hasSuffix, const std::string& suffix, bool hasWidth, int width, VerilatedContext* contextp) VL_MT_SAFE; extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE; inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE { diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index af08c34e6..6dfe7d32e 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -3490,10 +3490,10 @@ public: }; class AstTimeFormat final : public AstNodeStmt { // Parents: stmtlist - // @astgen op1 := unitsp : AstNodeExpr - // @astgen op2 := precisionp : AstNodeExpr - // @astgen op3 := suffixp : AstNodeExpr - // @astgen op4 := widthp : AstNodeExpr + // @astgen op1 := unitsp : Optional[AstNodeExpr] + // @astgen op2 := precisionp : Optional[AstNodeExpr] + // @astgen op3 := suffixp : Optional[AstNodeExpr] + // @astgen op4 := widthp : Optional[AstNodeExpr] public: AstTimeFormat(FileLine* fl, AstNodeExpr* unitsp, AstNodeExpr* precisionp, AstNodeExpr* suffixp, AstNodeExpr* widthp) diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 99cc3a259..a5bc9f002 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1083,13 +1083,33 @@ public: } void visit(AstTimeFormat* nodep) override { putns(nodep, "VL_TIMEFORMAT_IINI("); - iterateAndNextConstNull(nodep->unitsp()); + if (nodep->unitsp()) { + puts("true, "); + iterateAndNextConstNull(nodep->unitsp()); + } else { + puts("false, 0"); + } puts(", "); - iterateAndNextConstNull(nodep->precisionp()); + if (nodep->precisionp()) { + puts("true, "); + iterateAndNextConstNull(nodep->precisionp()); + } else { + puts("false, 0"); + } puts(", "); - emitCvtPackStr(nodep->suffixp()); + if (nodep->suffixp()) { + puts("true, "); + emitCvtPackStr(nodep->suffixp()); + } else { + puts("false, \"\""); + } puts(", "); - iterateAndNextConstNull(nodep->widthp()); + if (nodep->widthp()) { + puts("true, "); + iterateAndNextConstNull(nodep->widthp()); + } else { + puts("false, 0"); + } puts(", vlSymsp->_vm_contextp__);\n"); } void visit(AstTimePrecision* nodep) override { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 048480fb9..aa4fff739 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5718,10 +5718,11 @@ class WidthVisitor final : public VNVisitor { } void visit(AstTimeFormat* nodep) override { assertAtStatement(nodep); - iterateCheckSigned32(nodep, "units", nodep->unitsp(), BOTH); - iterateCheckSigned32(nodep, "precision", nodep->precisionp(), BOTH); - iterateCheckString(nodep, "suffix", nodep->suffixp(), BOTH); - iterateCheckSigned32(nodep, "width", nodep->widthp(), BOTH); + if (nodep->unitsp()) iterateCheckSigned32(nodep, "units", nodep->unitsp(), BOTH); + if (nodep->precisionp()) + iterateCheckSigned32(nodep, "precision", nodep->precisionp(), BOTH); + if (nodep->suffixp()) iterateCheckString(nodep, "suffix", nodep->suffixp(), BOTH); + if (nodep->widthp()) iterateCheckSigned32(nodep, "width", nodep->widthp(), BOTH); } void visit(AstUCStmt* nodep) override { // Just let all arguments seek their natural sizes diff --git a/src/verilog.y b/src/verilog.y index 3e4ed1251..fc3dc5224 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -4362,8 +4362,14 @@ system_t_call: // IEEE: system_tf_call (as task) | yD_PRINTTIMESCALE { $$ = new AstPrintTimeScale{$1}; } | yD_PRINTTIMESCALE '(' ')' { $$ = new AstPrintTimeScale{$1}; } | yD_PRINTTIMESCALE '(' idClassSel ')' { $$ = new AstPrintTimeScale{$1}; DEL($3); } - | yD_TIMEFORMAT '(' expr ',' expr ',' expr ',' expr ')' + | yD_TIMEFORMAT '(' exprE ',' exprE ',' exprE ',' exprE ')' { $$ = new AstTimeFormat{$1, $3, $5, $7, $9}; } + | yD_TIMEFORMAT '(' exprE ',' exprE ',' exprE ')' + { $$ = new AstTimeFormat{$1, $3, $5, $7, nullptr}; } + | yD_TIMEFORMAT '(' exprE ',' exprE ')' + { $$ = new AstTimeFormat{$1, $3, $5, nullptr, nullptr}; } + | yD_TIMEFORMAT '(' exprE ')' + { $$ = new AstTimeFormat{$1, $3, nullptr, nullptr, nullptr}; } // | yD_READMEMB '(' expr ',' idClassSel ')' { $$ = new AstReadMem{$1, false, $3, $5, nullptr, nullptr}; } | yD_READMEMB '(' expr ',' idClassSel ',' expr ')' { $$ = new AstReadMem{$1, false, $3, $5, $7, nullptr}; } diff --git a/test_regress/t/t_display_time.out b/test_regress/t/t_display_time.out index 9f28e2cd5..93b8a3cb5 100644 --- a/test_regress/t/t_display_time.out +++ b/test_regress/t/t_display_time.out @@ -3,5 +3,10 @@ default: [10] 0t time [ 10] No0 time p= 10 -9,0,,10: [10] 0t time [ 10] No0 time p= 10 0p='ha -9,0,ns,5: [10ns] 0t time [ 10ns] No0 time p= 10 0p='ha -9,3,ns,8: [10.000ns] 0t time [10.000ns] No0 time p= 10 0p='ha +-9,3,ns : [10.000ns] 0t time [10.000ns] No0 time p= 10 0p='ha +-9,3: [10.000ns] 0t time [10.000ns] No0 time p= 10 0p='ha +-9: [10.000ns] 0t time [10.000ns] No0 time p= 10 0p='ha +: [10.000ns] 0t time [10.000ns] No0 time p= 10 0p='ha +-9,,,: [10.000ns] 0t time [10.000ns] No0 time p= 10 0p='ha *-* All Finished *-* diff --git a/test_regress/t/t_display_time.v b/test_regress/t/t_display_time.v index 72b84a94c..f5d4c75da 100644 --- a/test_regress/t/t_display_time.v +++ b/test_regress/t/t_display_time.v @@ -30,6 +30,21 @@ module t (/*AUTOARG*/ $timeformat(-9, 3, "ns", 8); $write("-9,3,ns,8: [%0t] 0t time [%t] No0 time p=%p 0p=%0p\n", $time, $time, $time, $time); + $timeformat(-9, 3, "ns"); + $write("-9,3,ns : [%0t] 0t time [%t] No0 time p=%p 0p=%0p\n", + $time, $time, $time, $time); + $timeformat(-9, 3); + $write("-9,3: [%0t] 0t time [%t] No0 time p=%p 0p=%0p\n", + $time, $time, $time, $time); + $timeformat(-9); + $write("-9: [%0t] 0t time [%t] No0 time p=%p 0p=%0p\n", + $time, $time, $time, $time); + $timeformat(); + $write(": [%0t] 0t time [%t] No0 time p=%p 0p=%0p\n", + $time, $time, $time, $time); + $timeformat(-9,,,); + $write("-9,,,: [%0t] 0t time [%t] No0 time p=%p 0p=%0p\n", + $time, $time, $time, $time); $write("\n"); $write("*-* All Finished *-*\n"); $finish;