From c382d7d3ac2b713c5b3f55e81430e121520a1787 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Wed, 28 Jun 2023 11:51:20 +1000 Subject: [PATCH] fmt: %t/$time support --- backends/cxxrtl/cxxrtl.h | 3 ++ backends/verilog/verilog_backend.cc | 6 +++ frontends/ast/genrtlil.cc | 5 ++ frontends/verilog/verilog_lexer.l | 2 +- kernel/fmt.cc | 74 ++++++++++++++++++++++++++++- kernel/fmt.h | 13 ++++- tests/fmt/always_full.v | 56 +++++++++++++--------- 7 files changed, 133 insertions(+), 26 deletions(-) diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h index c7a632105..8ea800ed9 100644 --- a/backends/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/cxxrtl.h @@ -1330,7 +1330,10 @@ struct module { virtual bool eval() = 0; virtual bool commit() = 0; + unsigned int steps = 0; + size_t step() { + ++steps; size_t deltas = 0; bool converged = false; do { diff --git a/backends/verilog/verilog_backend.cc b/backends/verilog/verilog_backend.cc index a6fd859a3..573fa6d5b 100644 --- a/backends/verilog/verilog_backend.cc +++ b/backends/verilog/verilog_backend.cc @@ -1797,6 +1797,12 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell) dump_sigspec(f, arg.sig); f << ")"; break; + case VerilogFmtArg::TIME: + if (arg.realtime) + f << "$realtime"; + else + f << "$time"; + break; default: log_abort(); } } diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index 40e71e8bd..2e2d0de74 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -748,6 +748,11 @@ struct AST_INTERNAL::ProcessGenerator // and in case this will be used as an argument... arg.sig = node->bitsAsConst(); arg.signed_ = false; + } else if (node->type == AST_IDENTIFIER && node->str == "$time") { + arg.type = VerilogFmtArg::TIME; + } else if (node->type == AST_IDENTIFIER && node->str == "$realtime") { + arg.type = VerilogFmtArg::TIME; + arg.realtime = true; } else { arg.type = VerilogFmtArg::INTEGER; arg.sig = node->genRTLIL(); diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index 89ccee788..8a3734302 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -386,7 +386,7 @@ and|nand|or|nor|xor|xnor|not|buf|bufif0|bufif1|notif0|notif1 { supply0 { return TOK_SUPPLY0; } supply1 { return TOK_SUPPLY1; } -"$"(display[bho]?|write[bho]?|strobe|monitor|time|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { +"$"(display[bho]?|write[bho]?|strobe|monitor|time|realtime|stop|finish|dumpfile|dumpvars|dumpon|dumpoff|dumpall) { yylval->string = new std::string(yytext); return TOK_ID; } diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 074ec08ca..d72d60ba8 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -51,8 +51,11 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { part = {}; } + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + size_t arg_size = 0; - for (++i; i < fmt.size(); i++) { + for (; i < fmt.size(); i++) { if (fmt[i] >= '0' && fmt[i] <= '9') { arg_size *= 10; arg_size += fmt[i] - '0'; @@ -106,6 +109,11 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { part.base = 16; } else if (fmt[i] == 'c') { part.type = FmtPart::CHARACTER; + } else if (fmt[i] == 't') { + part.type = FmtPart::TIME; + } else if (fmt[i] == 'r') { + part.type = FmtPart::TIME; + part.realtime = true; } else { log_assert(false && "Unexpected character in format substitution"); } @@ -170,6 +178,9 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { } break; + case FmtPart::TIME: + log_assert(part.sig.size() == 0); + YS_FALLTHROUGH case FmtPart::CHARACTER: log_assert(part.sig.size() % 8 == 0); YS_FALLTHROUGH @@ -202,6 +213,11 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { fmt += part.signed_ ? 's' : 'u'; } else if (part.type == FmtPart::CHARACTER) { fmt += 'c'; + } else if (part.type == FmtPart::TIME) { + if (part.realtime) + fmt += 'r'; + else + fmt += 't'; } else log_abort(); fmt += '}'; break; @@ -339,6 +355,15 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.sig.extend_u0((part.sig.size() + 7) / 8 * 8); // %10s and %010s not fully defined in IEEE 1800-2017 and do the same thing in iverilog part.padding = ' '; + } else if (fmt[i] == 't' || fmt[i] == 'T') { + if (arg->type == VerilogFmtArg::TIME) { + part.type = FmtPart::TIME; + part.realtime = arg->realtime; + if (!has_width && !has_leading_zero) + part.width = 20; + } else { + log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with format character `%c' in argument %zu, but the argument is not $time or $realtime.\n", task_name.c_str(), fmt[i], fmtarg - args.begin() + 1); + } } else { log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with unrecognized format character `%c' in argument %zu.\n", task_name.c_str(), fmt[i], fmtarg - args.begin() + 1); } @@ -458,6 +483,28 @@ std::vector Fmt::emit_verilog() const } break; } + + case FmtPart::TIME: { + VerilogFmtArg arg; + arg.type = VerilogFmtArg::TIME; + if (part.realtime) + arg.realtime = true; + args.push_back(arg); + + fmt.str += '%'; + if (part.plus) + fmt.str += '+'; + if (part.justify == FmtPart::LEFT) + fmt.str += '-'; + log_assert(part.padding == ' ' || part.padding == '0'); + if (part.padding == '0' && part.width > 0) + fmt.str += '0'; + fmt.str += std::to_string(part.width); + fmt.str += 't'; + break; + } + + default: log_abort(); } } @@ -522,6 +569,25 @@ void Fmt::emit_cxxrtl(std::ostream &f, std::function("; + f << "value<64>{steps}"; + f << ", " << (part.type == FmtPart::CHARACTER); + f << ", " << (part.justify == FmtPart::LEFT); + f << ", (char)" << (int)part.padding; + f << ", " << part.width; + f << ", " << part.base; + f << ", " << part.signed_; + f << ", " << part.lzero; + f << ", " << part.plus; + f << ')'; + break; + } + + default: log_abort(); } } } @@ -636,6 +702,12 @@ std::string Fmt::render() const str += std::string(part.width - buf.size(), part.padding); break; } + + case FmtPart::TIME: { + // We only render() during initial, so time is always zero. + str += "0"; + break; + } } } diff --git a/kernel/fmt.h b/kernel/fmt.h index 647fe3a81..0d7300743 100644 --- a/kernel/fmt.h +++ b/kernel/fmt.h @@ -30,6 +30,7 @@ struct VerilogFmtArg { enum { STRING = 0, INTEGER = 1, + TIME = 2, } type; // All types @@ -42,6 +43,9 @@ struct VerilogFmtArg { // INTEGER type RTLIL::SigSpec sig; bool signed_ = false; + + // TIME type + bool realtime = false; }; // RTLIL format part, such as the substitutions in: @@ -51,24 +55,31 @@ struct FmtPart { STRING = 0, INTEGER = 1, CHARACTER = 2, + TIME = 3, } type; // STRING type std::string str; - // INTEGER/CHARACTER type + // INTEGER/CHARACTER types RTLIL::SigSpec sig; + + // INTEGER/CHARACTER/TIME types enum { RIGHT = 0, LEFT = 1, } justify = RIGHT; char padding = '\0'; size_t width = 0; + // INTEGER type unsigned base = 10; bool signed_ = false; bool lzero = false; bool plus = false; + + // TIME type + bool realtime = false; }; struct Fmt { diff --git a/tests/fmt/always_full.v b/tests/fmt/always_full.v index 5e3b17e9c..372781354 100644 --- a/tests/fmt/always_full.v +++ b/tests/fmt/always_full.v @@ -202,34 +202,44 @@ module always_full(input clk, output reg fin); if (counter == 176) $display(":%020b:", 16'shaaaa); if (counter == 177) $display(":%-020b:", 16'shaaaa); - if (counter == 178) $display("===> %%s"); - if (counter == 179) $display(":%10s:", "foo"); - if (counter == 180) $display(":%010s:", "foo"); - if (counter == 181) $display(":%-10s:", "foo"); - if (counter == 182) $display(":%-010s:", "foo"); + if (counter == 178) $display("==> time %%t"); + if (counter == 179) $display(":%t:", $time); + if (counter == 180) $display(":%-t:", $time); + if (counter == 181) $display(":%0t:", $time); + if (counter == 182) $display(":%-0t:", $time); + if (counter == 183) $display(":%10t:", $time); + if (counter == 184) $display(":%-10t:", $time); + if (counter == 185) $display(":%015t:", $time); + if (counter == 186) $display(":%-015t:", $time); - if (counter == 183) $display("===> %%c"); - if (counter == 184) $display(":%10c:", "foo"); - if (counter == 185) $display(":%010c:", "foo"); - if (counter == 186) $display(":%-10c:", "foo"); - if (counter == 187) $display(":%-010c:", "foo"); + if (counter == 187) $display("===> %%s"); + if (counter == 188) $display(":%10s:", "foo"); + if (counter == 189) $display(":%010s:", "foo"); + if (counter == 190) $display(":%-10s:", "foo"); + if (counter == 191) $display(":%-010s:", "foo"); - if (counter == 188) $display("==> aliases"); - if (counter == 189) $display(":%x:", 16'shaa); - if (counter == 190) $display(":%X:", 16'shaa); - if (counter == 191) $display(":%H:", 16'shaa); - if (counter == 192) $display(":%O:", 16'shaa); - if (counter == 193) $display(":%B:", 16'shaa); + if (counter == 192) $display("===> %%c"); + if (counter == 193) $display(":%10c:", "foo"); + if (counter == 194) $display(":%010c:", "foo"); + if (counter == 195) $display(":%-10c:", "foo"); + if (counter == 196) $display(":%-010c:", "foo"); - if (counter == 194) $display("==> default base"); - if (counter == 195) $displayh(16'haa); - if (counter == 196) $displayo(16'haa); - if (counter == 197) $displayb(16'haa); + if (counter == 197) $display("==> aliases"); + if (counter == 198) $display(":%x:", 16'shaa); + if (counter == 199) $display(":%X:", 16'shaa); + if (counter == 200) $display(":%H:", 16'shaa); + if (counter == 201) $display(":%O:", 16'shaa); + if (counter == 202) $display(":%B:", 16'shaa); - if (counter == 198) $display("==> write/format"); - if (counter == 199) $display("%d", 1, "%d", 1); + if (counter == 203) $display("==> default base"); + if (counter == 204) $displayh(16'haa); + if (counter == 205) $displayo(16'haa); + if (counter == 206) $displayb(16'haa); - if (counter == 200) begin + if (counter == 207) $display("==> write/format"); + if (counter == 208) $display("%d", 1, "%d", 1); + + if (counter == 209) begin $display("<<>>"); fin <= 1; end