diff --git a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h index b834cd12019..bc26f2b2e03 100644 --- a/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/runtime/cxxrtl/cxxrtl.h @@ -1010,22 +1010,24 @@ struct observer { // Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. struct fmt_part { enum { - STRING = 0, + LITERAL = 0, INTEGER = 1, - CHARACTER = 2, - VLOG_TIME = 3, + STRING = 2, + UNICHAR = 3, + VLOG_TIME = 4, } type; - // STRING type + // LITERAL type std::string str; - // INTEGER/CHARACTER types + // INTEGER/STRING/UNICHAR types // + value val; - // INTEGER/CHARACTER/VLOG_TIME types + // INTEGER/STRING/VLOG_TIME types enum { RIGHT = 0, LEFT = 1, + NUMERIC = 2, } justify; // = RIGHT; char padding; // = '\0'; size_t width; // = 0; @@ -1033,7 +1035,14 @@ struct fmt_part { // INTEGER type unsigned base; // = 10; bool signed_; // = false; - bool plus; // = false; + enum { + MINUS = 0, + PLUS_MINUS = 1, + SPACE_MINUS = 2, + } sign; // = MINUS; + bool hex_upper; // = false; + bool show_base; // = false; + bool group; // = false; // VLOG_TIME type bool realtime; // = false; @@ -1049,11 +1058,12 @@ struct fmt_part { // We might want to replace some of these bit() calls with direct // chunk access if it turns out to be slow enough to matter. std::string buf; + std::string prefix; switch (type) { - case STRING: + case LITERAL: return str; - case CHARACTER: { + case STRING: { buf.reserve(Bits/8); for (int i = 0; i < Bits; i += 8) { char ch = 0; @@ -1067,35 +1077,76 @@ struct fmt_part { break; } + case UNICHAR: { + uint32_t codepoint = val.template get(); + if (codepoint >= 0x10000) + buf += (char)(0xf0 | (codepoint >> 18)); + else if (codepoint >= 0x800) + buf += (char)(0xe0 | (codepoint >> 12)); + else if (codepoint >= 0x80) + buf += (char)(0xc0 | (codepoint >> 6)); + else + buf += (char)codepoint; + if (codepoint >= 0x10000) + buf += (char)(0x80 | ((codepoint >> 12) & 0x3f)); + if (codepoint >= 0x800) + buf += (char)(0x80 | ((codepoint >> 6) & 0x3f)); + if (codepoint >= 0x80) + buf += (char)(0x80 | ((codepoint >> 0) & 0x3f)); + break; + } + case INTEGER: { - size_t width = Bits; + bool negative = signed_ && val.is_neg(); + if (negative) { + prefix = "-"; + val = val.neg(); + } else { + switch (sign) { + case MINUS: break; + case PLUS_MINUS: prefix = "+"; break; + case SPACE_MINUS: prefix = " "; break; + } + } + + size_t val_width = Bits; if (base != 10) { - width = 0; + val_width = 1; for (size_t index = 0; index < Bits; index++) if (val.bit(index)) - width = index + 1; + val_width = index + 1; } if (base == 2) { - for (size_t i = width; i > 0; i--) - buf += (val.bit(i - 1) ? '1' : '0'); + if (show_base) + prefix += "0b"; + for (size_t index = 0; index < val_width; index++) { + if (group && index > 0 && index % 4 == 0) + buf += '_'; + buf += (val.bit(index) ? '1' : '0'); + } } else if (base == 8 || base == 16) { + if (show_base) + prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o"; size_t step = (base == 16) ? 4 : 3; - for (size_t index = 0; index < width; index += step) { + for (size_t index = 0; index < val_width; index += step) { + if (group && index > 0 && index % (4 * step) == 0) + buf += '_'; uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); if (step == 4) value |= val.bit(index + 3) << 3; - buf += "0123456789abcdef"[value]; + buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value]; } - std::reverse(buf.begin(), buf.end()); } else if (base == 10) { - bool negative = signed_ && val.is_neg(); - if (negative) - val = val.neg(); + if (show_base) + prefix += "0d"; if (val.is_zero()) buf += '0'; value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); + size_t index = 0; while (!xval.is_zero()) { + if (group && index > 0 && index % 3 == 0) + buf += '_'; value<(Bits > 4 ? Bits : 4)> quotient, remainder; if (Bits >= 4) std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); @@ -1103,11 +1154,18 @@ struct fmt_part { std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); buf += '0' + remainder.template trunc<4>().template get(); xval = quotient; + index++; } - if (negative || plus) - buf += negative ? '-' : '+'; - std::reverse(buf.begin(), buf.end()); } else assert(false && "Unsupported base for fmt_part"); + if (justify == NUMERIC && group && padding == '0') { + int group_size = base == 10 ? 3 : 4; + while (prefix.size() + buf.size() < width) { + if (buf.size() % (group_size + 1) == group_size) + buf += '_'; + buf += '0'; + } + } + std::reverse(buf.begin(), buf.end()); break; } @@ -1123,17 +1181,29 @@ struct fmt_part { std::string str; assert(width == 0 || padding != '\0'); - if (justify == RIGHT && buf.size() < width) { - size_t pad_width = width - buf.size(); - if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { - str += buf.front(); - buf.erase(0, 1); - } - str += std::string(pad_width, padding); + if (prefix.size() + buf.size() < width) { + size_t pad_width = width - prefix.size() - buf.size(); + switch (justify) { + case LEFT: + str += prefix; + str += buf; + str += std::string(pad_width, padding); + break; + case RIGHT: + str += std::string(pad_width, padding); + str += prefix; + str += buf; + break; + case NUMERIC: + str += prefix; + str += std::string(pad_width, padding); + str += buf; + break; + } + } else { + str += prefix; + str += buf; } - str += buf; - if (justify == LEFT && buf.size() < width) - str += std::string(width - buf.size(), padding); return str; } }; diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index fe67f00c692..bc7f1ddd1d6 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -790,7 +790,7 @@ struct AST_INTERNAL::ProcessGenerator Fmt fmt; fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name); if (ast->str.substr(0, 8) == "$display") - fmt.append_string("\n"); + fmt.append_literal("\n"); fmt.emit_rtlil(cell); } else if (!ast->str.empty()) { log_file_error(ast->filename, ast->location.first_line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str()); diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 43a4e03a29d..3d8478ef160 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -1079,7 +1079,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin // when $display()/$write() functions are used in an initial block, print them during synthesis Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true); if (str.substr(0, 8) == "$display") - fmt.append_string("\n"); + fmt.append_literal("\n"); log("%s", fmt.render().c_str()); } diff --git a/kernel/fmt.cc b/kernel/fmt.cc index 18eb7cb7193..cbf2d12e9ac 100644 --- a/kernel/fmt.cc +++ b/kernel/fmt.cc @@ -22,9 +22,9 @@ USING_YOSYS_NAMESPACE -void Fmt::append_string(const std::string &str) { +void Fmt::append_literal(const std::string &str) { FmtPart part = {}; - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; part.str = str; parts.push_back(part); } @@ -42,11 +42,11 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } else if (fmt.substr(i, 2) == "{{") { part.str += '{'; ++i; - } else if (fmt[i] == '}') + } else if (fmt[i] == '}') { log_assert(false && "Unexpected '}' in format string"); - else if (fmt[i] == '{') { + } else if (fmt[i] == '{') { if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); part = {}; } @@ -74,19 +74,24 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { part.sig = args.extract(0, arg_size); args.remove(0, arg_size); + if (fmt[i] == 'U') { + part.type = FmtPart::UNICHAR; + ++i; + goto success; + } + if (fmt[i] == '>') part.justify = FmtPart::RIGHT; else if (fmt[i] == '<') part.justify = FmtPart::LEFT; + else if (fmt[i] == '=') + part.justify = FmtPart::NUMERIC; else log_assert(false && "Unexpected justification in format substitution"); if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); - if (fmt[i] == '0' || fmt[i] == ' ') - part.padding = fmt[i]; - else - log_assert(false && "Unexpected padding in format substitution"); + part.padding = fmt[i]; if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); @@ -107,8 +112,12 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } else if (fmt[i] == 'h') { part.type = FmtPart::INTEGER; part.base = 16; + } else if (fmt[i] == 'H') { + part.type = FmtPart::INTEGER; + part.base = 16; + part.hex_upper = true; } else if (fmt[i] == 'c') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; } else if (fmt[i] == 't') { part.type = FmtPart::VLOG_TIME; } else if (fmt[i] == 'r') { @@ -124,10 +133,29 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { log_assert(false && "Unexpected end in format substitution"); if (part.type == FmtPart::INTEGER) { - if (fmt[i] == '+') { - part.plus = true; + if (fmt[i] == '-') { + part.sign = FmtPart::MINUS; + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + } else if (fmt[i] == '+') { + part.sign = FmtPart::PLUS_MINUS; + if (++i == fmt.size()) + log_assert(false && "Unexpected end in format substitution"); + } else if (fmt[i] == ' ') { + part.sign = FmtPart::SPACE_MINUS; if (++i == fmt.size()) log_assert(false && "Unexpected end in format substitution"); + } else { + // also accept no sign character and treat like MINUS for compatibility + } + + if (fmt[i] == '#') { + part.show_base = true; + ++i; + } + if (fmt[i] == '_') { + part.group = true; + ++i; } if (fmt[i] == 'u') @@ -140,6 +168,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { log_assert(false && "Unexpected end in format substitution"); } + success: if (fmt[i] != '}') log_assert(false && "Expected '}' after format substitution"); @@ -150,7 +179,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) { } } if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); } } @@ -161,7 +190,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { for (auto &part : parts) { switch (part.type) { - case FmtPart::STRING: + case FmtPart::LITERAL: for (char c : part.str) { if (c == '{') fmt += "{{"; @@ -172,10 +201,15 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { } break; + case FmtPart::UNICHAR: + log_assert(part.sig.size() <= 32); + fmt += "{U}"; + break; + case FmtPart::VLOG_TIME: log_assert(part.sig.size() == 0); YS_FALLTHROUGH - case FmtPart::CHARACTER: + case FmtPart::STRING: log_assert(part.sig.size() % 8 == 0); YS_FALLTHROUGH case FmtPart::INTEGER: @@ -187,6 +221,8 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { fmt += '>'; else if (part.justify == FmtPart::LEFT) fmt += '<'; + else if (part.justify == FmtPart::NUMERIC) + fmt += '='; else log_abort(); log_assert(part.width == 0 || part.padding != '\0'); fmt += part.padding != '\0' ? part.padding : ' '; @@ -197,13 +233,18 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const { case 2: fmt += 'b'; break; case 8: fmt += 'o'; break; case 10: fmt += 'd'; break; - case 16: fmt += 'h'; break; + case 16: fmt += part.hex_upper ? 'H' : 'h'; break; default: log_abort(); } - if (part.plus) - fmt += '+'; + switch (part.sign) { + case FmtPart::MINUS: fmt += '-'; break; + case FmtPart::PLUS_MINUS: fmt += '+'; break; + case FmtPart::SPACE_MINUS: fmt += ' '; break; + } + fmt += part.show_base ? "#" : ""; + fmt += part.group ? "_" : ""; fmt += part.signed_ ? 's' : 'u'; - } else if (part.type == FmtPart::CHARACTER) { + } else if (part.type == FmtPart::STRING) { fmt += 'c'; } else if (part.type == FmtPart::VLOG_TIME) { if (part.realtime) @@ -299,12 +340,12 @@ void Fmt::apply_verilog_automatic_sizing_and_add(FmtPart &part) part.width = places; if (part.justify == FmtPart::RIGHT) { - append_string(gap); + append_literal(gap); parts.push_back(part); } else { part.justify = FmtPart::RIGHT; parts.push_back(part); - append_string(gap); + append_literal(gap); } } } @@ -355,7 +396,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.str += module_name.str(); } else { if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); part = {}; } @@ -375,7 +416,7 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.justify = FmtPart::LEFT; } else if (fmt[i] == '+') { // always show sign; not in IEEE 1800-2017 or verilator but iverilog has it - part.plus = true; + part.sign = FmtPart::PLUS_MINUS; } else break; } if (i == fmt.size()) { @@ -408,11 +449,11 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik part.type = FmtPart::INTEGER; part.base = 16; } else if (fmt[i] == 'c' || fmt[i] == 'C') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; part.sig.extend_u0(8); // %10c and %010c not fully defined in IEEE 1800-2017 and do different things in iverilog } else if (fmt[i] == 's' || fmt[i] == 'S') { - part.type = FmtPart::CHARACTER; + part.type = FmtPart::STRING; if ((part.sig.size() % 8) != 0) 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 @@ -435,12 +476,20 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); } - if (part.padding == '\0') - part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' '; + if (part.padding == '\0') { + if (has_leading_zero && part.justify == FmtPart::RIGHT) { + part.padding = '0'; + part.justify = FmtPart::NUMERIC; + } else { + part.padding = ' '; + } + } - if (part.type == FmtPart::INTEGER && part.base != 10 && part.plus) + if (part.type == FmtPart::INTEGER && part.base != 10 && part.sign != FmtPart::MINUS) log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); + if (part.base != 10) + part.signed_ = false; if (part.type == FmtPart::INTEGER && !has_leading_zero) apply_verilog_automatic_sizing_and_add(part); else @@ -449,12 +498,12 @@ void Fmt::parse_verilog(const std::vector &args, bool sformat_lik } } if (!part.str.empty()) { - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; parts.push_back(part); } } else { FmtPart part = {}; - part.type = FmtPart::STRING; + part.type = FmtPart::LITERAL; part.str = arg->str; parts.push_back(part); } @@ -474,7 +523,7 @@ std::vector Fmt::emit_verilog() const for (auto &part : parts) { switch (part.type) { - case FmtPart::STRING: + case FmtPart::LITERAL: for (char c : part.str) { if (c == '%') fmt.str += "%%"; @@ -491,14 +540,13 @@ std::vector Fmt::emit_verilog() const args.push_back(arg); fmt.str += '%'; - if (part.plus) - fmt.str += '+'; + if (part.sign == FmtPart::PLUS_MINUS || part.sign == FmtPart::SPACE_MINUS) + fmt.str += '+'; // treat space/minus as plus/minus if (part.justify == FmtPart::LEFT) fmt.str += '-'; if (part.width == 0) { fmt.str += '0'; } else if (part.width > 0) { - log_assert(part.padding == ' ' || part.padding == '0'); if (part.base != 10 || part.padding == '0') fmt.str += '0'; fmt.str += std::to_string(part.width); @@ -507,13 +555,13 @@ std::vector Fmt::emit_verilog() const case 2: fmt.str += 'b'; break; case 8: fmt.str += 'o'; break; case 10: fmt.str += 'd'; break; - case 16: fmt.str += 'h'; break; + case 16: fmt.str += 'h'; break; // treat uppercase hex as lowercase default: log_abort(); } break; } - case FmtPart::CHARACTER: { + case FmtPart::STRING: { VerilogFmtArg arg; arg.type = VerilogFmtArg::INTEGER; arg.sig = part.sig; @@ -524,7 +572,6 @@ std::vector Fmt::emit_verilog() const fmt.str += '-'; if (part.sig.size() == 8) { if (part.width > 0) { - log_assert(part.padding == '0' || part.padding == ' '); if (part.padding == '0') fmt.str += part.padding; fmt.str += std::to_string(part.width); @@ -533,7 +580,6 @@ std::vector Fmt::emit_verilog() const } else { log_assert(part.sig.size() % 8 == 0); if (part.width > 0) { - log_assert(part.padding == ' '); // no zero padding fmt.str += std::to_string(part.width); } fmt.str += 's'; @@ -541,6 +587,16 @@ std::vector Fmt::emit_verilog() const break; } + case FmtPart::UNICHAR: { + VerilogFmtArg arg; + arg.type = VerilogFmtArg::INTEGER; + arg.sig = part.sig.extract(0, 7); // only ASCII + args.push_back(arg); + + fmt.str += "%c"; + break; + } + case FmtPart::VLOG_TIME: { VerilogFmtArg arg; arg.type = VerilogFmtArg::TIME; @@ -549,11 +605,11 @@ std::vector Fmt::emit_verilog() const args.push_back(arg); fmt.str += '%'; - if (part.plus) + log_assert(part.sign == FmtPart::MINUS || part.sign == FmtPart::PLUS_MINUS); + if (part.sign == FmtPart::PLUS_MINUS) 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); @@ -599,24 +655,35 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function= 0x10000) + str += (char)(0xf0 | (codepoint >> 18)); + else if (codepoint >= 0x800) + str += (char)(0xe0 | (codepoint >> 12)); + else if (codepoint >= 0x80) + str += (char)(0xc0 | (codepoint >> 6)); + else + str += (char)codepoint; + if (codepoint >= 0x10000) + str += (char)(0x80 | ((codepoint >> 12) & 0x3f)); + if (codepoint >= 0x800) + str += (char)(0x80 | ((codepoint >> 6) & 0x3f)); + if (codepoint >= 0x80) + str += (char)(0x80 | ((codepoint >> 0) & 0x3f)); + break; + } + case FmtPart::INTEGER: - case FmtPart::CHARACTER: + case FmtPart::STRING: case FmtPart::VLOG_TIME: { std::string buf; + std::string prefix; if (part.type == FmtPart::INTEGER) { RTLIL::Const value = part.sig.as_const(); + bool has_x = false, all_x = true, has_z = false, all_z = true; + for (State bit : value) { + if (bit == State::Sx) + has_x = true; + else + all_x = false; + if (bit == State::Sz) + has_z = true; + else + all_z = false; + } + + if (!has_z && !has_x && part.signed_ && value[value.size() - 1]) { + prefix = "-"; + value = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1); + } else { + switch (part.sign) { + case FmtPart::MINUS: break; + case FmtPart::PLUS_MINUS: prefix = "+"; break; + case FmtPart::SPACE_MINUS: prefix = " "; break; + } + } if (part.base != 10) { - size_t minimum_size = 0; + size_t minimum_size = 1; for (size_t index = 0; index < (size_t)value.size(); index++) if (value[index] != State::S0) minimum_size = index + 1; @@ -651,10 +761,28 @@ std::string Fmt::render() const } if (part.base == 2) { - buf = value.as_string(); + if (part.show_base) + prefix += "0b"; + for (size_t index = 0; index < (size_t)value.size(); index++) { + if (part.group && index > 0 && index % 4 == 0) + buf += '_'; + RTLIL::State bit = value[index]; + if (bit == State::Sx) + buf += 'x'; + else if (bit == State::Sz) + buf += 'z'; + else if (bit == State::S1) + buf += '1'; + else /* if (bit == State::S0) */ + buf += '0'; + } } else if (part.base == 8 || part.base == 16) { + if (part.show_base) + prefix += (part.base == 16) ? (part.hex_upper ? "0X" : "0x") : "0o"; size_t step = (part.base == 16) ? 4 : 3; for (size_t index = 0; index < (size_t)value.size(); index += step) { + if (part.group && index > 0 && index % (4 * step) == 0) + buf += '_'; RTLIL::Const subvalue = value.extract(index, min(step, value.size() - index)); bool has_x = false, all_x = true, has_z = false, all_z = true; for (State bit : subvalue) { @@ -676,21 +804,11 @@ std::string Fmt::render() const else if (has_z) buf += 'Z'; else - buf += "0123456789abcdef"[subvalue.as_int()]; + buf += (part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[subvalue.as_int()]; } - std::reverse(buf.begin(), buf.end()); } else if (part.base == 10) { - bool has_x = false, all_x = true, has_z = false, all_z = true; - for (State bit : value) { - if (bit == State::Sx) - has_x = true; - else - all_x = false; - if (bit == State::Sz) - has_z = true; - else - all_z = false; - } + if (part.show_base) + prefix += "0d"; if (all_x) buf += 'x'; else if (all_z) @@ -700,25 +818,29 @@ std::string Fmt::render() const else if (has_z) buf += 'Z'; else { - bool negative = part.signed_ && value[value.size() - 1]; - RTLIL::Const absvalue; - if (negative) - absvalue = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1); - else - absvalue = value; - log_assert(absvalue.is_fully_def()); - if (absvalue.is_fully_zero()) + log_assert(value.is_fully_def()); + if (value.is_fully_zero()) buf += '0'; - while (!absvalue.is_fully_zero()) { - buf += '0' + RTLIL::const_mod(absvalue, 10, false, false, 4).as_int(); - absvalue = RTLIL::const_div(absvalue, 10, false, false, absvalue.size()); + size_t index = 0; + while (!value.is_fully_zero()) { + if (part.group && index > 0 && index % 3 == 0) + buf += '_'; + buf += '0' + RTLIL::const_mod(value, 10, false, false, 4).as_int(); + value = RTLIL::const_div(value, 10, false, false, value.size()); + index++; } - if (negative || part.plus) - buf += negative ? '-' : '+'; - std::reverse(buf.begin(), buf.end()); } } else log_abort(); - } else if (part.type == FmtPart::CHARACTER) { + if (part.justify == FmtPart::NUMERIC && part.group && part.padding == '0') { + int group_size = part.base == 10 ? 3 : 4; + while (prefix.size() + buf.size() < part.width) { + if (buf.size() % (group_size + 1) == group_size) + buf += '_'; + buf += '0'; + } + } + std::reverse(buf.begin(), buf.end()); + } else if (part.type == FmtPart::STRING) { buf = part.sig.as_const().decode_string(); } else if (part.type == FmtPart::VLOG_TIME) { // We only render() during initial, so time is always zero. @@ -726,17 +848,29 @@ std::string Fmt::render() const } log_assert(part.width == 0 || part.padding != '\0'); - if (part.justify == FmtPart::RIGHT && buf.size() < part.width) { - size_t pad_width = part.width - buf.size(); - if (part.padding == '0' && (!buf.empty() && (buf.front() == '+' || buf.front() == '-'))) { - str += buf.front(); - buf.erase(0, 1); + if (prefix.size() + buf.size() < part.width) { + size_t pad_width = part.width - prefix.size() - buf.size(); + switch (part.justify) { + case FmtPart::LEFT: + str += prefix; + str += buf; + str += std::string(pad_width, part.padding); + break; + case FmtPart::RIGHT: + str += std::string(pad_width, part.padding); + str += prefix; + str += buf; + break; + case FmtPart::NUMERIC: + str += prefix; + str += std::string(pad_width, part.padding); + str += buf; + break; } - str += std::string(pad_width, part.padding); + } else { + str += prefix; + str += buf; } - str += buf; - if (part.justify == FmtPart::LEFT && buf.size() < part.width) - str += std::string(part.width - buf.size(), part.padding); break; } } diff --git a/kernel/fmt.h b/kernel/fmt.h index 3bedb786ec4..2d4b24979b2 100644 --- a/kernel/fmt.h +++ b/kernel/fmt.h @@ -53,22 +53,24 @@ struct VerilogFmtArg { // Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h! struct FmtPart { enum { - STRING = 0, + LITERAL = 0, INTEGER = 1, - CHARACTER = 2, - VLOG_TIME = 3, + STRING = 2, + UNICHAR = 3, + VLOG_TIME = 4, } type; - // STRING type + // LITERAL type std::string str; - // INTEGER/CHARACTER types + // INTEGER/STRING/UNICHAR types RTLIL::SigSpec sig; - // INTEGER/CHARACTER/VLOG_TIME types + // INTEGER/STRING/VLOG_TIME types enum { RIGHT = 0, LEFT = 1, + NUMERIC = 2, } justify = RIGHT; char padding = '\0'; size_t width = 0; @@ -76,7 +78,14 @@ struct FmtPart { // INTEGER type unsigned base = 10; bool signed_ = false; - bool plus = false; + enum { + MINUS = 0, + PLUS_MINUS = 1, + SPACE_MINUS = 2, + } sign = MINUS; + bool hex_upper = false; + bool show_base = false; + bool group = false; // VLOG_TIME type bool realtime = false; @@ -86,7 +95,7 @@ struct Fmt { public: std::vector parts; - void append_string(const std::string &str); + void append_literal(const std::string &str); void parse_rtlil(const RTLIL::Cell *cell); void emit_rtlil(RTLIL::Cell *cell) const;