3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-23 09:05:32 +00:00

cxxrtl: refactor the formatter and use a closure.

This commit achieves three roughly equally important goals:
1. To bring the rendering code in kernel/fmt.cc and in cxxrtl.h as close
   together as possible, with an ideal of only having the bigint library
   as the difference between the render functions.
2. To make the treatment of `$time` and `$realtime` in CXXRTL closer to
   the Verilog semantics, at least in the formatting code.
3. To change the code generator so that all of the `$print`-to-`string`
   conversion code is contained inside of a closure.

There are two reasons to aim for goal (3):
a. Because output redirection through definition of a global ostream
   object is neither convenient nor useful for environments where
   the output is consumed by other code rather than being printed on
   a terminal.
b. Because it may be desirable to, in some cases, ignore the `$print`
   cells that are present in the netlist based on a runtime decision.
   This is doubly true for an upcoming `$check` cell implementing
   assertions, since failing a `$check` would by default cause a crash.
This commit is contained in:
Catherine 2024-01-16 09:08:07 +00:00
parent bf1a99da09
commit a33acb7cd9
5 changed files with 192 additions and 173 deletions

View file

@ -569,82 +569,60 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
return args;
}
void Fmt::emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &)> emit_sig) const
std::string escape_cxx_string(const std::string &input)
{
for (auto &part : parts) {
switch (part.type) {
case FmtPart::STRING:
f << " << \"";
for (char c : part.str) {
switch (c) {
case '\\':
YS_FALLTHROUGH
case '"':
f << '\\' << c;
break;
case '\a':
f << "\\a";
break;
case '\b':
f << "\\b";
break;
case '\f':
f << "\\f";
break;
case '\n':
f << "\\n";
break;
case '\r':
f << "\\r";
break;
case '\t':
f << "\\t";
break;
case '\v':
f << "\\v";
break;
default:
f << c;
break;
}
}
f << '"';
break;
case FmtPart::INTEGER:
case FmtPart::CHARACTER: {
f << " << value_formatted<" << part.sig.size() << ">(";
emit_sig(part.sig);
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.plus;
f << ')';
break;
}
case FmtPart::TIME: {
// CXXRTL only records steps taken, so there's no difference between
// the values taken by $time and $realtime.
f << " << value_formatted<64>(";
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.plus;
f << ')';
break;
}
default: log_abort();
std::string output = "\"";
for (auto c : input) {
if (::isprint(c)) {
if (c == '\\')
output.push_back('\\');
output.push_back(c);
} else {
char l = c & 0xf, h = (c >> 4) & 0xf;
output.append("\\x");
output.push_back((h < 10 ? '0' + h : 'a' + h - 10));
output.push_back((l < 10 ? '0' + l : 'a' + l - 10));
}
}
output.push_back('"');
if (output.find('\0') != std::string::npos) {
output.insert(0, "std::string {");
output.append(stringf(", %zu}", input.size()));
}
return output;
}
void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(const RTLIL::SigSpec &)> emit_sig) const
{
os << indent << "std::string buf;\n";
for (auto &part : parts) {
os << indent << "buf += fmt_part { ";
os << "fmt_part::";
switch (part.type) {
case FmtPart::STRING: os << "STRING"; break;
case FmtPart::INTEGER: os << "INTEGER"; break;
case FmtPart::CHARACTER: os << "CHARACTER"; break;
case FmtPart::TIME: os << "TIME"; break;
}
os << ", ";
os << escape_cxx_string(part.str) << ", ";
os << "fmt_part::";
switch (part.justify) {
case FmtPart::LEFT: os << "LEFT"; break;
case FmtPart::RIGHT: os << "RIGHT"; break;
}
os << ", ";
os << "(char)" << (int)part.padding << ", ";
os << part.width << ", ";
os << part.base << ", ";
os << part.signed_ << ", ";
os << part.plus << ", ";
os << part.realtime;
os << " }.render(";
emit_sig(part.sig);
os << ", itime, ftime);\n";
}
os << indent << "return buf;\n";
}
std::string Fmt::render() const

View file

@ -50,6 +50,7 @@ struct VerilogFmtArg {
// RTLIL format part, such as the substitutions in:
// "foo {4:> 4du} bar {2:<01hs}"
// Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h!
struct FmtPart {
enum {
STRING = 0,
@ -71,7 +72,7 @@ struct FmtPart {
} justify = RIGHT;
char padding = '\0';
size_t width = 0;
// INTEGER type
unsigned base = 10;
bool signed_ = false;
@ -93,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);
std::vector<VerilogFmtArg> emit_verilog() const;
void emit_cxxrtl(std::ostream &f, 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 render() const;