fmt: rename TIME to VLOG_TIME.

The behavior of these format specifiers is highly specific to Verilog
(`$time` and `$realtime` are only defined relative to `$timescale`)
and may not fit other languages well, if at all. If they choose to use
it, it is now clear what they are opting into.

This commit also simplifies the CXXRTL code generation for these format
specifiers.
This commit is contained in:
Catherine 2024-01-19 13:33:14 +00:00
parent 08d7f54726
commit b74d33d1b8
5 changed files with 37 additions and 33 deletions

View File

@ -1072,9 +1072,9 @@ struct CxxrtlWorker {
dump_sigspec_rhs(cell->getPort(ID::EN)); dump_sigspec_rhs(cell->getPort(ID::EN));
f << " == value<1>{1u}) {\n"; f << " == value<1>{1u}) {\n";
inc_indent(); inc_indent();
f << indent << "auto formatter = [&](int64_t itime, double ftime) {\n"; f << indent << "auto formatter = [&](struct performer *performer) {\n";
inc_indent(); inc_indent();
fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }); fmt.emit_cxxrtl(f, indent, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); }, "performer");
dec_indent(); dec_indent();
f << indent << "};\n"; f << indent << "};\n";
f << indent << "if (performer) {\n"; f << indent << "if (performer) {\n";
@ -1082,11 +1082,11 @@ struct CxxrtlWorker {
f << indent << "static const metadata_map attributes = "; f << indent << "static const metadata_map attributes = ";
dump_metadata_map(cell->attributes); dump_metadata_map(cell->attributes);
f << ";\n"; f << ";\n";
f << indent << "performer->on_print(formatter(performer->time(), performer->realtime()), attributes);\n"; f << indent << "performer->on_print(formatter(performer), attributes);\n";
dec_indent(); dec_indent();
f << indent << "} else {\n"; f << indent << "} else {\n";
inc_indent(); inc_indent();
f << indent << print_output << " << formatter(0, 0.0);\n"; f << indent << print_output << " << formatter(performer);\n";
dec_indent(); dec_indent();
f << indent << "}\n"; f << indent << "}\n";
dec_indent(); dec_indent();

View File

@ -948,10 +948,10 @@ typedef std::map<std::string, metadata> metadata_map;
// An object that can be passed to a `eval()` method in order to act on side effects. // An object that can be passed to a `eval()` method in order to act on side effects.
struct performer { struct performer {
// Called to evaluate a Verilog `$time` expression. // Called to evaluate a Verilog `$time` expression.
virtual int64_t time() const { return 0; } virtual int64_t vlog_time() const { return 0; }
// Called to evaluate a Verilog `$realtime` expression. // Called to evaluate a Verilog `$realtime` expression.
virtual double realtime() const { return time(); } virtual double vlog_realtime() const { return vlog_time(); }
// Called when a `$print` cell is triggered. // Called when a `$print` cell is triggered.
virtual void on_print(const std::string &output, const metadata_map &attributes) { std::cout << output; } virtual void on_print(const std::string &output, const metadata_map &attributes) { std::cout << output; }
@ -976,10 +976,10 @@ struct observer {
// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. // Default member initializers would make this a non-aggregate-type in C++11, so they are commented out.
struct fmt_part { struct fmt_part {
enum { enum {
STRING = 0, STRING = 0,
INTEGER = 1, INTEGER = 1,
CHARACTER = 2, CHARACTER = 2,
TIME = 3, VLOG_TIME = 3,
} type; } type;
// STRING type // STRING type
@ -988,7 +988,7 @@ struct fmt_part {
// INTEGER/CHARACTER types // INTEGER/CHARACTER types
// + value<Bits> val; // + value<Bits> val;
// INTEGER/CHARACTER/TIME types // INTEGER/CHARACTER/VLOG_TIME types
enum { enum {
RIGHT = 0, RIGHT = 0,
LEFT = 1, LEFT = 1,
@ -1001,16 +1001,16 @@ struct fmt_part {
bool signed_; // = false; bool signed_; // = false;
bool plus; // = false; bool plus; // = false;
// TIME type // VLOG_TIME type
bool realtime; // = false; bool realtime; // = false;
// + int64_t itime; // + int64_t itime;
// + double ftime; // + double ftime;
// Format the part as a string. // Format the part as a string.
// //
// The values of `itime` and `ftime` are used for `$time` and `$realtime`, correspondingly. // The values of `vlog_time` and `vlog_realtime` are used for Verilog `$time` and `$realtime`, correspondingly.
template<size_t Bits> template<size_t Bits>
std::string render(value<Bits> val, int64_t itime, double ftime) std::string render(value<Bits> val, performer *performer = nullptr)
{ {
// We might want to replace some of these bit() calls with direct // We might want to replace some of these bit() calls with direct
// chunk access if it turns out to be slow enough to matter. // chunk access if it turns out to be slow enough to matter.
@ -1077,8 +1077,12 @@ struct fmt_part {
break; break;
} }
case TIME: { case VLOG_TIME: {
buf = realtime ? std::to_string(ftime) : std::to_string(itime); if (performer) {
buf = realtime ? std::to_string(performer->vlog_realtime()) : std::to_string(performer->vlog_time());
} else {
buf = realtime ? std::to_string(0.0) : std::to_string(0);
}
break; break;
} }
} }

View File

@ -110,9 +110,9 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
} else if (fmt[i] == 'c') { } else if (fmt[i] == 'c') {
part.type = FmtPart::CHARACTER; part.type = FmtPart::CHARACTER;
} else if (fmt[i] == 't') { } else if (fmt[i] == 't') {
part.type = FmtPart::TIME; part.type = FmtPart::VLOG_TIME;
} else if (fmt[i] == 'r') { } else if (fmt[i] == 'r') {
part.type = FmtPart::TIME; part.type = FmtPart::VLOG_TIME;
part.realtime = true; part.realtime = true;
} else { } else {
log_assert(false && "Unexpected character in format substitution"); log_assert(false && "Unexpected character in format substitution");
@ -172,7 +172,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
} }
break; break;
case FmtPart::TIME: case FmtPart::VLOG_TIME:
log_assert(part.sig.size() == 0); log_assert(part.sig.size() == 0);
YS_FALLTHROUGH YS_FALLTHROUGH
case FmtPart::CHARACTER: case FmtPart::CHARACTER:
@ -205,7 +205,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
fmt += part.signed_ ? 's' : 'u'; fmt += part.signed_ ? 's' : 'u';
} else if (part.type == FmtPart::CHARACTER) { } else if (part.type == FmtPart::CHARACTER) {
fmt += 'c'; fmt += 'c';
} else if (part.type == FmtPart::TIME) { } else if (part.type == FmtPart::VLOG_TIME) {
if (part.realtime) if (part.realtime)
fmt += 'r'; fmt += 'r';
else else
@ -328,7 +328,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
case VerilogFmtArg::TIME: { case VerilogFmtArg::TIME: {
FmtPart part = {}; FmtPart part = {};
part.type = FmtPart::TIME; part.type = FmtPart::VLOG_TIME;
part.realtime = arg->realtime; part.realtime = arg->realtime;
part.padding = ' '; part.padding = ' ';
part.width = 20; part.width = 20;
@ -419,7 +419,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
part.padding = ' '; part.padding = ' ';
} else if (fmt[i] == 't' || fmt[i] == 'T') { } else if (fmt[i] == 't' || fmt[i] == 'T') {
if (arg->type == VerilogFmtArg::TIME) { if (arg->type == VerilogFmtArg::TIME) {
part.type = FmtPart::TIME; part.type = FmtPart::VLOG_TIME;
part.realtime = arg->realtime; part.realtime = arg->realtime;
if (!has_width && !has_leading_zero) if (!has_width && !has_leading_zero)
part.width = 20; part.width = 20;
@ -541,7 +541,7 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
break; break;
} }
case FmtPart::TIME: { case FmtPart::VLOG_TIME: {
VerilogFmtArg arg; VerilogFmtArg arg;
arg.type = VerilogFmtArg::TIME; arg.type = VerilogFmtArg::TIME;
if (part.realtime) if (part.realtime)
@ -592,7 +592,7 @@ std::string escape_cxx_string(const std::string &input)
return output; return output;
} }
void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig) const void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig, const std::string &context) const
{ {
os << indent << "std::string buf;\n"; os << indent << "std::string buf;\n";
for (auto &part : parts) { for (auto &part : parts) {
@ -602,7 +602,7 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(c
case FmtPart::STRING: os << "STRING"; break; case FmtPart::STRING: os << "STRING"; break;
case FmtPart::INTEGER: os << "INTEGER"; break; case FmtPart::INTEGER: os << "INTEGER"; break;
case FmtPart::CHARACTER: os << "CHARACTER"; break; case FmtPart::CHARACTER: os << "CHARACTER"; break;
case FmtPart::TIME: os << "TIME"; break; case FmtPart::VLOG_TIME: os << "VLOG_TIME"; break;
} }
os << ", "; os << ", ";
os << escape_cxx_string(part.str) << ", "; os << escape_cxx_string(part.str) << ", ";
@ -620,7 +620,7 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(c
os << part.realtime; os << part.realtime;
os << " }.render("; os << " }.render(";
emit_sig(part.sig); emit_sig(part.sig);
os << ", itime, ftime);\n"; os << ", " << context << ");\n";
} }
os << indent << "return buf;\n"; os << indent << "return buf;\n";
} }
@ -636,8 +636,8 @@ std::string Fmt::render() const
break; break;
case FmtPart::INTEGER: case FmtPart::INTEGER:
case FmtPart::TIME: case FmtPart::CHARACTER:
case FmtPart::CHARACTER: { case FmtPart::VLOG_TIME: {
std::string buf; std::string buf;
if (part.type == FmtPart::INTEGER) { if (part.type == FmtPart::INTEGER) {
RTLIL::Const value = part.sig.as_const(); RTLIL::Const value = part.sig.as_const();
@ -720,7 +720,7 @@ std::string Fmt::render() const
} else log_abort(); } else log_abort();
} else if (part.type == FmtPart::CHARACTER) { } else if (part.type == FmtPart::CHARACTER) {
buf = part.sig.as_const().decode_string(); buf = part.sig.as_const().decode_string();
} else if (part.type == FmtPart::TIME) { } else if (part.type == FmtPart::VLOG_TIME) {
// We only render() during initial, so time is always zero. // We only render() during initial, so time is always zero.
buf = "0"; buf = "0";
} }

View File

@ -56,7 +56,7 @@ struct FmtPart {
STRING = 0, STRING = 0,
INTEGER = 1, INTEGER = 1,
CHARACTER = 2, CHARACTER = 2,
TIME = 3, VLOG_TIME = 3,
} type; } type;
// STRING type // STRING type
@ -65,7 +65,7 @@ struct FmtPart {
// INTEGER/CHARACTER types // INTEGER/CHARACTER types
RTLIL::SigSpec sig; RTLIL::SigSpec sig;
// INTEGER/CHARACTER/TIME types // INTEGER/CHARACTER/VLOG_TIME types
enum { enum {
RIGHT = 0, RIGHT = 0,
LEFT = 1, LEFT = 1,
@ -78,7 +78,7 @@ struct FmtPart {
bool signed_ = false; bool signed_ = false;
bool plus = false; bool plus = false;
// TIME type // VLOG_TIME type
bool realtime = false; bool realtime = false;
}; };
@ -94,7 +94,7 @@ public:
void parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name); void parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name);
std::vector<VerilogFmtArg> emit_verilog() const; std::vector<VerilogFmtArg> emit_verilog() const;
void emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig) const; void emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig, const std::string &context) const;
std::string render() const; std::string render() const;

View File

@ -3,7 +3,7 @@
int main() int main()
{ {
struct : public performer { struct : public performer {
int64_t time() const override { return 1; } int64_t vlog_time() const override { return 1; }
void on_print(const std::string &output, const cxxrtl::metadata_map &) override { std::cerr << output; } void on_print(const std::string &output, const cxxrtl::metadata_map &) override { std::cerr << output; }
} performer; } performer;