From e7068369fe3e7f61036ed5468cda180d86b1ec4e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 18 Oct 2022 21:10:35 -0400 Subject: [PATCH] Fix $display of fixed-width numbers (#3565). --- Changes | 1 + include/verilated.cpp | 108 ++++++++---- src/V3Number.cpp | 154 +++++++++--------- test_regress/t/t_display.out | 5 +- test_regress/t/t_display.v | 10 +- test_regress/t/t_extract_static_const.v | 16 +- .../t/t_extract_static_const_multimodule.v | 20 +-- 7 files changed, 188 insertions(+), 126 deletions(-) diff --git a/Changes b/Changes index 263c68d37..f9a79bec6 100644 --- a/Changes +++ b/Changes @@ -37,6 +37,7 @@ Verilator 5.001 devel * Add --dump-tree-dot to enable dumping Ast Tree .dot files (#3636). [Marcel Chang] * Add error on real edge event control. * Fix LSB error on --hierarchical submodules (#3539). [danbone] +* Fix $display of fixed-width numbers (#3565). [Iztok Jeras] * Fix foreach and pre/post increment in functions (#3613). [Nandu Raj] * Fix linker errors in user-facing timing functions (#3657). [Krzysztof Bieganski, Antmicro Ltd] * Fix null access on optimized-out fork statements (#3658). [Krzysztof Bieganski, Antmicro Ltd] diff --git a/include/verilated.cpp b/include/verilated.cpp index 46192052f..506c1e5f7 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -905,15 +905,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA digits = append.length(); } const int needmore = width - digits; - std::string padding; if (needmore > 0) { - if (pctp && pctp[0] && pctp[1] == '0') { // %0 - padding.append(needmore, '0'); // Pre-pad zero - } else { + std::string padding; + if (left) { padding.append(needmore, ' '); // Pre-pad spaces + output += append + padding; + } else { + if (pctp && pctp[0] && pctp[1] == '0') { // %0 + padding.append(needmore, '0'); // Pre-pad zero + } else { + padding.append(needmore, ' '); // Pre-pad spaces + } + output += padding + append; } + } else { + output += append; } - output += left ? (append + padding) : (padding + append); break; } case '#': { // Unsigned decimal @@ -927,15 +934,22 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA digits = append.length(); } const int needmore = width - digits; - std::string padding; if (needmore > 0) { - if (pctp && pctp[0] && pctp[1] == '0') { // %0 - padding.append(needmore, '0'); // Pre-pad zero - } else { + std::string padding; + if (left) { padding.append(needmore, ' '); // Pre-pad spaces + output += append + padding; + } else { + if (pctp && pctp[0] && pctp[1] == '0') { // %0 + padding.append(needmore, '0'); // Pre-pad zero + } else { + padding.append(needmore, ' '); // Pre-pad spaces + } + output += padding + append; } + } else { + output += append; } - output += left ? (append + padding) : (padding + append); break; } case 't': { // Time @@ -944,21 +958,62 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width); break; } - case 'b': - for (; lsb >= 0; --lsb) output += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0'; - break; - case 'o': - for (; lsb >= 0; --lsb) { - lsb = (lsb / 3) * 3; // Next digit - // Octal numbers may span more than one wide word, - // so we need to grab each bit separately and check for overrun - // Octal is rare, so we'll do it a slow simple way - output += static_cast( - '0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0) - + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0) - + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0)); + case 'b': // FALLTHRU + case 'o': // FALLTHRU + case 'x': { + if (widthSet || left) { + lsb = VL_MOSTSETBITP1_W(VL_WORDS_I(lbits), lwp); + lsb = (lsb < 1) ? 0 : (lsb - 1); + } + + std::string append; + int digits; + switch (fmt) { + case 'b': { + digits = lsb + 1; + for (; lsb >= 0; --lsb) append += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0'; + break; + } + case 'o': { + digits = (lsb + 1 + 2) / 3; + for (; lsb >= 0; --lsb) { + lsb = (lsb / 3) * 3; // Next digit + // Octal numbers may span more than one wide word, + // so we need to grab each bit separately and check for overrun + // Octal is rare, so we'll do it a slow simple way + append += static_cast( + '0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0) + + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0) + + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0)); + } + break; + } + default: { // 'x' + digits = (lsb + 1 + 3) / 4; + for (; lsb >= 0; --lsb) { + lsb = (lsb / 4) * 4; // Next digit + const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf; + append += "0123456789abcdef"[charval]; + } + break; + } + } // switch + + const int needmore = width - digits; + if (needmore > 0) { + std::string padding; + if (left) { + padding.append(needmore, ' '); // Pre-pad spaces + output += append + padding; + } else { + padding.append(needmore, '0'); // Pre-pad zero + output += padding + append; + } + } else { + output += append; } break; + } // b / o / x case 'u': case 'z': { // Packed 4-state const bool is_4_state = (fmt == 'z'); @@ -984,13 +1039,6 @@ void _vl_vsformat(std::string& output, const char* formatp, va_list ap) VL_MT_SA } } break; - case 'x': - for (; lsb >= 0; --lsb) { - lsb = (lsb / 4) * 4; // Next digit - const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf; - output += "0123456789abcdef"[charval]; - } - break; default: { // LCOV_EXCL_START const std::string msg = std::string{"Unknown _vl_vsformat code: "} + pos[0]; VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 3b1b2ccc0..4ce1c1423 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -603,86 +603,90 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { string str; const char code = tolower(pos[0]); switch (code) { - case 'b': { - int bit = width() - 1; - if (fmtsize == "0") - while (bit && bitIs0(bit)) bit--; - for (; bit >= 0; bit--) { - if (bitIs0(bit)) { - str += '0'; - } else if (bitIs1(bit)) { - str += '1'; - } else if (bitIsZ(bit)) { - str += 'z'; - } else { - str += 'x'; - } - } - return str; - } - case 'o': { - int bit = width() - 1; - if (fmtsize == "0") - while (bit && bitIs0(bit)) bit--; - while ((bit % 3) != 2) bit++; - for (; bit > 0; bit -= 3) { - const int numX = countX(bit - 2, 3); - const int numZ = countZ(bit - 2, 3); - if (numX == 3 || numX == width() - (bit - 2)) { - str += 'x'; - continue; - } - if (numZ == 3 || numZ == width() - (bit - 2)) { - str += 'z'; - continue; - } - if (numX > 0) { - str += 'X'; - continue; - } - if (numZ > 0) { - str += 'Z'; - continue; - } - const int v = bitsValue(bit - 2, 3); - str += static_cast('0' + v); - } - return str; - } - case 'h': + case 'b': // FALLTHRU + case 'o': // FALLTHRU + case 'h': // FALLTHRU case 'x': { int bit = width() - 1; - if (fmtsize == "0") - while (bit && bitIs0(bit)) bit--; - while ((bit % 4) != 3) bit++; - for (; bit > 0; bit -= 4) { - const int numX = countX(bit - 3, 4); - const int numZ = countZ(bit - 3, 4); - if (numX == 4 || numX == width() - (bit - 3)) { - str += 'x'; - continue; + if (left || !fmtsize.empty()) { + while (bit && bitIs0(bit)) --bit; + } + switch (code) { + case 'b': { + for (; bit >= 0; --bit) { + if (bitIs0(bit)) { + str += '0'; + } else if (bitIs1(bit)) { + str += '1'; + } else if (bitIsZ(bit)) { + str += 'z'; + } else { + str += 'x'; + } } - if (numZ == 4 || numZ == width() - (bit - 3)) { - str += 'z'; - continue; - } - if (numX > 0) { - str += 'X'; - continue; - } - if (numZ > 0) { - str += 'Z'; - continue; - } - const int v = bitsValue(bit - 3, 4); - if (v >= 10) { - str += static_cast('a' + v - 10); - } else { + break; + } + case 'o': { + while ((bit % 3) != 2) ++bit; + for (; bit > 0; bit -= 3) { + const int numX = countX(bit - 2, 3); + const int numZ = countZ(bit - 2, 3); + if (numX == 3 || numX == width() - (bit - 2)) { + str += 'x'; + continue; + } + if (numZ == 3 || numZ == width() - (bit - 2)) { + str += 'z'; + continue; + } + if (numX > 0) { + str += 'X'; + continue; + } + if (numZ > 0) { + str += 'Z'; + continue; + } + const int v = bitsValue(bit - 2, 3); str += static_cast('0' + v); } + break; } + default: { // h/x + while ((bit % 4) != 3) ++bit; + for (; bit > 0; bit -= 4) { + const int numX = countX(bit - 3, 4); + const int numZ = countZ(bit - 3, 4); + if (numX == 4 || numX == width() - (bit - 3)) { + str += 'x'; + continue; + } + if (numZ == 4 || numZ == width() - (bit - 3)) { + str += 'z'; + continue; + } + if (numX > 0) { + str += 'X'; + continue; + } + if (numZ > 0) { + str += 'Z'; + continue; + } + const int v = bitsValue(bit - 3, 4); + if (v >= 10) { + str += static_cast('a' + v - 10); + } else { + str += static_cast('0' + v); + } + } + break; + } + } // switch + const size_t fmtsizen = static_cast(std::atoi(fmtsize.c_str())); + str = displayPad(fmtsizen, (left ? ' ' : '0'), left, str); return str; - } + } // case b/d/x/o case 'c': { if (width() > 8) fl->v3warn(WIDTH, "$display-like format of %c format of > 8 bit value"); const unsigned int v = bitsValue(0, 8); @@ -714,7 +718,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { case 't': // Time case 'd': { // Unsigned decimal const bool issigned = (code == '~'); - if (fmtsize == "") { + if (fmtsize == "" && !left) { const double mantissabits = width() - (issigned ? 1 : 0); // To get the number of digits required, we want to compute // log10(2**mantissabits) and round it up. To be able to handle @@ -753,7 +757,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const { } } } - const bool zeropad = fmtsize.length() > 0 && fmtsize[0] == '0'; + const bool zeropad = fmtsize.length() > 0 && fmtsize[0] == '0' && !left; // fmtsize might have changed since we parsed the %fmtsize const size_t fmtsizen = static_cast(std::atoi(fmtsize.c_str())); str = displayPad(fmtsizen, (zeropad ? '0' : ' '), left, str); diff --git a/test_regress/t/t_display.out b/test_regress/t/t_display.out index 375c5dfc9..0ecc886a1 100644 --- a/test_regress/t/t_display.out +++ b/test_regress/t/t_display.out @@ -37,7 +37,10 @@ [0] %P="sv-str" [0] %u=dcba %0u=dcba [0] %U=dcba %0U=dcba -[0] %D= 12 %d= 12 %01d=12 %06d=000012 %6d= 12 +[0] %D= 12 %d= 12 %01d=12 %06d=000012 %6d= 12 %-06d=12 %-6d=12 +[0] %X=00c %x=00c %01x=c %06x=00000c %6x=00000c %-06x=c %-6x=c +[0] %O=014 %o=014 %01o=14 %06o=000014 %6o=000014 %-06o=14 %-6o=14 +[0] %B=000001100 %b=000001100 %01b=1100 %06b=001100 %6b=001100 %-06b=1100 %-6b=1100 [0] %t= 0 %03t= 0 %0t=0 [0] %s=! %s= what! %s= hmmm!1234 diff --git a/test_regress/t/t_display.v b/test_regress/t/t_display.v index 0da4596e2..24aa8f428 100644 --- a/test_regress/t/t_display.v +++ b/test_regress/t/t_display.v @@ -120,8 +120,14 @@ module t; {"a","b","c","d"}, {"a","b","c","d"}); // Avoid binary output // %z is tested in t_sys_sformat.v - $display("[%0t] %%D=%D %%d=%d %%01d=%01d %%06d=%06d %%6d=%6d", $time, - nine, nine, nine, nine, nine); + $display("[%0t] %%D=%D %%d=%d %%01d=%01d %%06d=%06d %%6d=%6d %%-06d=%-06d %%-6d=%-6d", $time, + nine, nine, nine, nine, nine, nine, nine); + $display("[%0t] %%X=%X %%x=%x %%01x=%01x %%06x=%06x %%6x=%6x %%-06x=%-06x %%-6x=%-6x", $time, + nine, nine, nine, nine, nine, nine, nine); + $display("[%0t] %%O=%O %%o=%o %%01o=%01o %%06o=%06o %%6o=%6o %%-06o=%-06o %%-6o=%-6o", $time, + nine, nine, nine, nine, nine, nine, nine); + $display("[%0t] %%B=%B %%b=%b %%01b=%01b %%06b=%06b %%6b=%6b %%-06b=%-06b %%-6b=%-6b", $time, + nine, nine, nine, nine, nine, nine, nine); $display("[%0t] %%t=%t %%03t=%03t %%0t=%0t", $time, $time, $time, $time); $display; diff --git a/test_regress/t/t_extract_static_const.v b/test_regress/t/t_extract_static_const.v index 2a49caced..5e258dbe9 100755 --- a/test_regress/t/t_extract_static_const.v +++ b/test_regress/t/t_extract_static_const.v @@ -27,14 +27,14 @@ module t (/*AUTOARG*/); initial begin // Note: Base index via $c to prevent optimizatoin by Verilator - $display("0x%32x", C[$c(0*32)+:32]); - $display("0x%32x", D[$c(1*32)+:32]); - $display("0x%32x", C[$c(2*32)+:32]); - $display("0x%32x", D[$c(3*32)+:32]); - $display("0x%32x", C[$c(4*32)+:32]); - $display("0x%32x", D[$c(5*32)+:32]); - $display("0x%32x", C[$c(6*32)+:32]); - $display("0x%32x", D[$c(7*32)+:32]); + $display("0x%8x", C[$c(0*32)+:32]); + $display("0x%8x", D[$c(1*32)+:32]); + $display("0x%8x", C[$c(2*32)+:32]); + $display("0x%8x", D[$c(3*32)+:32]); + $display("0x%8x", C[$c(4*32)+:32]); + $display("0x%8x", D[$c(5*32)+:32]); + $display("0x%8x", C[$c(6*32)+:32]); + $display("0x%8x", D[$c(7*32)+:32]); $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_extract_static_const_multimodule.v b/test_regress/t/t_extract_static_const_multimodule.v index 0f959991a..7885ecb5b 100755 --- a/test_regress/t/t_extract_static_const_multimodule.v +++ b/test_regress/t/t_extract_static_const_multimodule.v @@ -29,11 +29,11 @@ module a( trig_o <= 1'd0; if (trig_i) begin // Note: Base index via $c to prevent optimizatoin by Verilator - $display("0x%32x", C[$c(0*32)+:32]); - $display("0x%32x", C[$c(2*32)+:32]); - $display("0x%32x", C[$c(4*32)+:32]); - $display("0x%32x", C[$c(6*32)+:32]); - $display("0x%256x", C); + $display("0x%8x", C[$c(0*32)+:32]); + $display("0x%8x", C[$c(2*32)+:32]); + $display("0x%8x", C[$c(4*32)+:32]); + $display("0x%8x", C[$c(6*32)+:32]); + $display("0x%32x", C); trig_o <= 1'd1; end end @@ -61,11 +61,11 @@ module b( trig_o <= 1'd0; if (trig_i) begin // Note: Base index via $c to prevent optimizatoin by Verilator - $display("0x%32x", C[$c(1*32)+:32]); - $display("0x%32x", C[$c(3*32)+:32]); - $display("0x%32x", C[$c(5*32)+:32]); - $display("0x%32x", C[$c(7*32)+:32]); - $display("0x%256x", C); + $display("0x%8x", C[$c(1*32)+:32]); + $display("0x%8x", C[$c(3*32)+:32]); + $display("0x%8x", C[$c(5*32)+:32]); + $display("0x%8x", C[$c(7*32)+:32]); + $display("0x%32x", C); trig_o <= 1'd1; end end