Support `$sscanf %t`
This commit is contained in:
parent
33b838f139
commit
67f26508ba
|
|
@ -1329,7 +1329,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
|||
default: {
|
||||
// Deal with all read-and-scan somethings
|
||||
// Note LSBs are preserved if there's an overflow
|
||||
const int obits = inIgnore ? 0 : va_arg(ap, int);
|
||||
int obits = inIgnore ? 0 : va_arg(ap, int);
|
||||
VlWide<VL_WQ_WORDS_E> qowp;
|
||||
VL_SET_WQ(qowp, 0ULL);
|
||||
WDataOutP owp = qowp;
|
||||
|
|
@ -1390,7 +1390,26 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf
|
|||
VL_SET_WQ(owp, u.ld);
|
||||
break;
|
||||
}
|
||||
case 't': // FALLTHRU // Time
|
||||
case 't': { // Time
|
||||
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
||||
_vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "+-.0123456789eE");
|
||||
if (!t_tmp[0]) goto done;
|
||||
union {
|
||||
double r;
|
||||
int64_t ld;
|
||||
} u;
|
||||
// Get pointer argument first, as proceeds the timeunit value
|
||||
if (obits != 64) goto done;
|
||||
QData* const realp = va_arg(ap, QData*);
|
||||
const int timeunit = va_arg(ap, int);
|
||||
const int userUnits
|
||||
= Verilated::threadContextp()->impp()->timeFormatUnits(); // 0..-15
|
||||
const int shift = -userUnits + timeunit; // 0..-15
|
||||
u.r = std::strtod(t_tmp, nullptr) * vl_time_multiplier(-shift);
|
||||
*realp = VL_CLEAN_QQ(obits, obits, u.ld);
|
||||
obits = 0; // Already loaded the value, don't read arg
|
||||
break;
|
||||
}
|
||||
case '#': { // Unsigned decimal
|
||||
_vl_vsss_skipspace(fp, floc, fromp, fstr);
|
||||
_vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "0123456789+-xXzZ?_");
|
||||
|
|
|
|||
|
|
@ -2016,6 +2016,7 @@ class AstSScanF final : public AstNodeExpr {
|
|||
// @astgen op1 := exprsp : List[AstNode] // VarRefs for results
|
||||
// @astgen op2 := fromp : AstNode
|
||||
string m_text;
|
||||
VTimescale m_timeunit; // Parent module time unit
|
||||
|
||||
public:
|
||||
AstSScanF(FileLine* fl, const string& text, AstNode* fromp, AstNode* exprsp)
|
||||
|
|
@ -2035,10 +2036,13 @@ public:
|
|||
bool isOutputter() override { return true; } // SPECIAL: makes output
|
||||
bool cleanOut() const override { return false; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return text() == VN_DBG_AS(samep, SScanF)->text();
|
||||
const AstSScanF* const asamep = VN_DBG_AS(samep, SScanF);
|
||||
return text() == asamep->text() && timeunit() == asamep->timeunit();
|
||||
}
|
||||
string text() const { return m_text; } // * = Text to display
|
||||
void text(const string& text) { m_text = text; }
|
||||
VTimescale timeunit() const { return m_timeunit; }
|
||||
void timeunit(const VTimescale& flag) { m_timeunit = flag; }
|
||||
};
|
||||
class AstSampled final : public AstNodeExpr {
|
||||
// Verilog $sampled
|
||||
|
|
|
|||
|
|
@ -290,18 +290,20 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
|
|||
}
|
||||
m_emitDispState.pushArg(fmtLetter, argp, "");
|
||||
if (fmtLetter == 't' || fmtLetter == '^') {
|
||||
const AstSFormatF* fmtp = nullptr;
|
||||
VTimescale timeunit = VTimescale::NONE;
|
||||
if (const AstDisplay* const nodep = VN_CAST(dispp, Display)) {
|
||||
fmtp = nodep->fmtp();
|
||||
timeunit = nodep->fmtp()->timeunit();
|
||||
} else if (const AstSFormat* const nodep = VN_CAST(dispp, SFormat)) {
|
||||
fmtp = nodep->fmtp();
|
||||
} else {
|
||||
fmtp = VN_CAST(dispp, SFormatF);
|
||||
timeunit = nodep->fmtp()->timeunit();
|
||||
} else if (const AstSScanF* const nodep = VN_CAST(dispp, SScanF)) {
|
||||
timeunit = nodep->timeunit();
|
||||
} else if (const AstSFormatF* const nodep = VN_CAST(dispp, SFormatF)) {
|
||||
timeunit = nodep->timeunit();
|
||||
}
|
||||
UASSERT_OBJ(fmtp, dispp,
|
||||
"Use of %t must be under AstDisplay, AstSFormat, or AstSFormatF");
|
||||
UASSERT_OBJ(!fmtp->timeunit().isNone(), fmtp, "timenunit must be set");
|
||||
m_emitDispState.pushArg(' ', nullptr, cvtToStr((int)fmtp->timeunit().powerOfTen()));
|
||||
UASSERT_OBJ(!timeunit.isNone(), dispp,
|
||||
"Use of %t must be under AstDisplay, AstSFormat, or AstSFormatF, or "
|
||||
"SScanF, and timeunit set");
|
||||
m_emitDispState.pushArg(' ', nullptr, cvtToStr((int)timeunit.powerOfTen()));
|
||||
}
|
||||
} else {
|
||||
m_emitDispState.pushArg(fmtLetter, nullptr, "");
|
||||
|
|
|
|||
|
|
@ -815,6 +815,11 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
nodep->timeunit(m_modp->timeunit());
|
||||
}
|
||||
void visit(AstSScanF* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
nodep->timeunit(m_modp->timeunit());
|
||||
}
|
||||
void visit(AstTime* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
`timescale 1ns / 100ps
|
||||
|
||||
module main;
|
||||
|
||||
real r;
|
||||
integer rc;
|
||||
time t;
|
||||
|
||||
// verilator lint_off REALCVT
|
||||
|
||||
initial begin
|
||||
rc = $sscanf("8.125", "%f", r); // as real
|
||||
`checkd(rc, 1);
|
||||
`checkr(r, 8.125);
|
||||
|
||||
rc = $sscanf("8125", "%t", r); // in ns but round to 100 ps
|
||||
`checkd(rc, 1);
|
||||
t = r;
|
||||
`checkr(t, 813);
|
||||
|
||||
$timeformat(-3, 2, "ms", 10);
|
||||
rc = $sscanf("8.125", "%t", r); // in ms
|
||||
`checkd(rc, 1);
|
||||
t = r;
|
||||
`checkr(t, 8125000);
|
||||
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue