mirror of
https://github.com/YosysHQ/yosys
synced 2025-04-23 09:05:32 +00:00
fmt: remove lzero by lowering during Verilog parse
See https://github.com/YosysHQ/yosys/pull/3721#issuecomment-1502037466 -- this reduces logic within the cell, and makes the rules that apply much more clear.
This commit is contained in:
parent
eb0fb4d662
commit
7f7c61c9f0
4 changed files with 82 additions and 41 deletions
100
kernel/fmt.cc
100
kernel/fmt.cc
|
@ -130,12 +130,6 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
|
|||
log_assert(false && "Unexpected end in format substitution");
|
||||
}
|
||||
|
||||
if (fmt[i] == '0') {
|
||||
part.lzero = true;
|
||||
if (++i == fmt.size())
|
||||
log_assert(false && "Unexpected end in format substitution");
|
||||
}
|
||||
|
||||
if (fmt[i] == 'u')
|
||||
part.signed_ = false;
|
||||
else if (fmt[i] == 's')
|
||||
|
@ -208,8 +202,6 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
|
|||
}
|
||||
if (part.plus)
|
||||
fmt += '+';
|
||||
if (part.lzero)
|
||||
fmt += '0';
|
||||
fmt += part.signed_ ? 's' : 'u';
|
||||
} else if (part.type == FmtPart::CHARACTER) {
|
||||
fmt += 'c';
|
||||
|
@ -248,14 +240,72 @@ static size_t compute_required_decimal_places(size_t size, bool signed_)
|
|||
return places;
|
||||
}
|
||||
|
||||
static void apply_verilog_automatic_sizing(FmtPart &part)
|
||||
static size_t compute_required_nondecimal_places(size_t size, unsigned base)
|
||||
{
|
||||
log_assert(base != 10);
|
||||
BigUnsigned max;
|
||||
max.setBit(size - 1, true);
|
||||
size_t places = 0;
|
||||
while (!max.isZero()) {
|
||||
places++;
|
||||
max /= base;
|
||||
}
|
||||
return places;
|
||||
}
|
||||
|
||||
// Only called for integers, either when:
|
||||
//
|
||||
// (a) passed without a format string (e.g. "$display(a);"), or
|
||||
//
|
||||
// (b) the corresponding format specifier has no leading zero, e.g. "%b",
|
||||
// "%20h", "%-10d".
|
||||
//
|
||||
// In these cases, for binary/octal/hex, we always zero-pad to the size of the
|
||||
// signal; i.e. whether "%h" or "%10h" or "%-20h" is used, if the corresponding
|
||||
// signal is 32'h1234, "00001234" will always be a substring of the output.
|
||||
//
|
||||
// For case (a), we have no specified width, so there is nothing more to do.
|
||||
//
|
||||
// For case (b), because we are only called with no leading zero on the
|
||||
// specifier, any specified width beyond the signal size is therefore space
|
||||
// padding, whatever the justification.
|
||||
//
|
||||
// For decimal, we do no zero-padding, instead space-padding to the size
|
||||
// required for the signal's largest value. This is per other Verilog
|
||||
// implementations, and intuitively makes sense as decimal representations lack
|
||||
// a discrete mapping of digits to bit groups. Decimals may also show sign and
|
||||
// must accommodate this, whereas other representations do not.
|
||||
void Fmt::apply_verilog_automatic_sizing_and_add(FmtPart &part)
|
||||
{
|
||||
if (part.base == 10) {
|
||||
size_t places = compute_required_decimal_places(part.sig.size(), part.signed_);
|
||||
part.padding = ' ';
|
||||
part.width = std::max(part.width, places);
|
||||
} else {
|
||||
part.lzero = true;
|
||||
parts.push_back(part);
|
||||
return;
|
||||
}
|
||||
|
||||
part.padding = '0';
|
||||
|
||||
size_t places = compute_required_nondecimal_places(part.sig.size(), part.base);
|
||||
if (part.width < places) {
|
||||
part.justify = FmtPart::RIGHT;
|
||||
part.width = places;
|
||||
parts.push_back(part);
|
||||
} else if (part.width == places) {
|
||||
parts.push_back(part);
|
||||
} else if (part.width > places) {
|
||||
auto gap = std::string(part.width - places, ' ');
|
||||
part.width = places;
|
||||
|
||||
if (part.justify == FmtPart::RIGHT) {
|
||||
append_string(gap);
|
||||
parts.push_back(part);
|
||||
} else {
|
||||
part.justify = FmtPart::RIGHT;
|
||||
parts.push_back(part);
|
||||
append_string(gap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,8 +322,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
|
|||
part.sig = arg->sig;
|
||||
part.base = default_base;
|
||||
part.signed_ = arg->signed_;
|
||||
apply_verilog_automatic_sizing(part);
|
||||
parts.push_back(part);
|
||||
apply_verilog_automatic_sizing_and_add(part);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -379,15 +428,13 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
|
|||
if (part.padding == '\0')
|
||||
part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' ';
|
||||
|
||||
if (part.type == FmtPart::INTEGER) {
|
||||
if (part.base != 10 && part.plus) {
|
||||
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 (!has_leading_zero)
|
||||
apply_verilog_automatic_sizing(part);
|
||||
}
|
||||
if (part.type == FmtPart::INTEGER && part.base != 10 && part.plus)
|
||||
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);
|
||||
|
||||
parts.push_back(part);
|
||||
if (part.type == FmtPart::INTEGER && !has_leading_zero)
|
||||
apply_verilog_automatic_sizing_and_add(part);
|
||||
else
|
||||
parts.push_back(part);
|
||||
part = {};
|
||||
}
|
||||
}
|
||||
|
@ -439,13 +486,10 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
|
|||
if (part.justify == FmtPart::LEFT)
|
||||
fmt.str += '-';
|
||||
if (part.width == 0) {
|
||||
if (!part.lzero)
|
||||
fmt.str += '0';
|
||||
fmt.str += '0';
|
||||
} else if (part.width > 0) {
|
||||
log_assert(part.padding == ' ' || part.padding == '0');
|
||||
if (!part.lzero && part.base != 10)
|
||||
fmt.str += '0';
|
||||
else if (part.padding == '0')
|
||||
if (part.base != 10 || part.padding == '0')
|
||||
fmt.str += '0';
|
||||
fmt.str += std::to_string(part.width);
|
||||
}
|
||||
|
@ -567,7 +611,6 @@ void Fmt::emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &
|
|||
f << ", " << part.width;
|
||||
f << ", " << part.base;
|
||||
f << ", " << part.signed_;
|
||||
f << ", " << part.lzero;
|
||||
f << ", " << part.plus;
|
||||
f << ')';
|
||||
break;
|
||||
|
@ -584,7 +627,6 @@ void Fmt::emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &
|
|||
f << ", " << part.width;
|
||||
f << ", " << part.base;
|
||||
f << ", " << part.signed_;
|
||||
f << ", " << part.lzero;
|
||||
f << ", " << part.plus;
|
||||
f << ')';
|
||||
break;
|
||||
|
@ -612,7 +654,7 @@ std::string Fmt::render() const
|
|||
if (part.type == FmtPart::INTEGER) {
|
||||
RTLIL::Const value = part.sig.as_const();
|
||||
|
||||
if (!part.lzero && part.base != 10) {
|
||||
if (part.base != 10) {
|
||||
size_t minimum_size = 0;
|
||||
for (size_t index = 0; index < (size_t)value.size(); index++)
|
||||
if (value[index] != State::S0)
|
||||
|
|
|
@ -75,7 +75,6 @@ struct FmtPart {
|
|||
// INTEGER type
|
||||
unsigned base = 10;
|
||||
bool signed_ = false;
|
||||
bool lzero = false;
|
||||
bool plus = false;
|
||||
|
||||
// TIME type
|
||||
|
@ -83,6 +82,7 @@ struct FmtPart {
|
|||
};
|
||||
|
||||
struct Fmt {
|
||||
public:
|
||||
std::vector<FmtPart> parts;
|
||||
|
||||
void append_string(const std::string &str);
|
||||
|
@ -96,6 +96,9 @@ struct Fmt {
|
|||
void emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &)> emit_sig) const;
|
||||
|
||||
std::string render() const;
|
||||
|
||||
private:
|
||||
void apply_verilog_automatic_sizing_and_add(FmtPart &part);
|
||||
};
|
||||
|
||||
YOSYS_NAMESPACE_END
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue