diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 1ee7443f0..74531da2d 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -635,50 +635,118 @@ void VerilatedVcdBuffer::emitBit(uint32_t code, CData newval) { finishLine(code, wp); } +static inline int vlVcdClz32(uint32_t value) { + VL_DEBUG_IFDEF(assert(value);); +#if defined(__GNUC__) || defined(__clang__) + return __builtin_clz(value); +#else + int zeros = 0; + uint32_t bit = 1U << 31; + while (!(value & bit)) { + ++zeros; + bit >>= 1; + } + return zeros; +#endif +} + +static inline int vlVcdClz64(uint64_t value) { + VL_DEBUG_IFDEF(assert(value);); +#if defined(__GNUC__) || defined(__clang__) + return __builtin_clzll(value); +#else + const uint32_t upper = static_cast(value >> 32); + return upper ? vlVcdClz32(upper) : 32 + vlVcdClz32(static_cast(value)); +#endif +} + VL_ATTR_ALWINLINE void VerilatedVcdBuffer::emitCData(uint32_t code, CData newval, int bits) { char* wp = m_writep; *wp++ = 'b'; - cvtCDataToStr(wp, newval << (VL_BYTESIZE - bits)); - finishLine(code, wp + bits); + CData value = newval << (VL_BYTESIZE - bits); + if (VL_UNLIKELY(!value)) { + *wp++ = '0'; + finishLine(code, wp); + return; + } + const int skip = vlVcdClz32(value) - (VL_IDATASIZE - VL_BYTESIZE); + cvtCDataToStr(wp, value << skip); + finishLine(code, wp + bits - skip); } VL_ATTR_ALWINLINE void VerilatedVcdBuffer::emitSData(uint32_t code, SData newval, int bits) { char* wp = m_writep; *wp++ = 'b'; - cvtSDataToStr(wp, newval << (VL_SHORTSIZE - bits)); - finishLine(code, wp + bits); + SData value = newval << (VL_SHORTSIZE - bits); + if (VL_UNLIKELY(!value)) { + *wp++ = '0'; + finishLine(code, wp); + return; + } + const int skip = vlVcdClz32(value) - (VL_IDATASIZE - VL_SHORTSIZE); + cvtSDataToStr(wp, value << skip); + finishLine(code, wp + bits - skip); } VL_ATTR_ALWINLINE void VerilatedVcdBuffer::emitIData(uint32_t code, IData newval, int bits) { char* wp = m_writep; *wp++ = 'b'; - cvtIDataToStr(wp, newval << (VL_IDATASIZE - bits)); - finishLine(code, wp + bits); + IData value = newval << (VL_IDATASIZE - bits); + if (VL_UNLIKELY(!value)) { + *wp++ = '0'; + finishLine(code, wp); + return; + } + const int skip = vlVcdClz32(value); + cvtIDataToStr(wp, value << skip); + finishLine(code, wp + bits - skip); } VL_ATTR_ALWINLINE void VerilatedVcdBuffer::emitQData(uint32_t code, QData newval, int bits) { char* wp = m_writep; *wp++ = 'b'; - cvtQDataToStr(wp, newval << (VL_QUADSIZE - bits)); - finishLine(code, wp + bits); + QData value = newval << (VL_QUADSIZE - bits); + if (VL_UNLIKELY(!value)) { + *wp++ = '0'; + finishLine(code, wp); + return; + } + const int skip = vlVcdClz64(value); + cvtQDataToStr(wp, value << skip); + finishLine(code, wp + bits - skip); } VL_ATTR_ALWINLINE void VerilatedVcdBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) { - int words = VL_WORDS_I(bits); char* wp = m_writep; *wp++ = 'b'; - // Handle the most significant word + + int word = VL_WORDS_I(bits) - 1; const int bitsInMSW = VL_BITBIT_E(bits) ? VL_BITBIT_E(bits) : VL_EDATASIZE; - cvtEDataToStr(wp, newvalp[--words] << (VL_EDATASIZE - bitsInMSW)); - wp += bitsInMSW; - // Handle the remaining words - while (words > 0) { - cvtEDataToStr(wp, newvalp[--words]); + int bitsInWord = bitsInMSW; + EData value = newvalp[word] & VL_MASK_E(bitsInMSW); + + while (!value && word > 0) { + value = newvalp[--word]; + bitsInWord = VL_EDATASIZE; + } + + if (VL_UNLIKELY(!value)) { + *wp++ = '0'; + finishLine(code, wp); + return; + } + + const int skip = vlVcdClz32(value) - (VL_EDATASIZE - bitsInWord); + cvtEDataToStr(wp, value << (VL_EDATASIZE - bitsInWord + skip)); + wp += bitsInWord - skip; + + while (word > 0) { + cvtEDataToStr(wp, newvalp[--word]); wp += VL_EDATASIZE; } finishLine(code, wp); diff --git a/test_regress/t/t_trace_vcd_shortened.py b/test_regress/t/t_trace_vcd_shortened.py new file mode 100755 index 000000000..7066e447a --- /dev/null +++ b/test_regress/t/t_trace_vcd_shortened.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +import os +import platform + +test.scenarios('vlt') + +if platform.machine().lower() not in ("amd64", "x86_64"): + test.skip("Test requires x86-64 AVX2/LZCNT") + +if os.path.exists("/proc/cpuinfo"): + with open("/proc/cpuinfo", encoding="utf-8") as fh: + cpuinfo = " " + fh.read().lower() + " " + if " avx2 " not in cpuinfo or (" lzcnt " not in cpuinfo and " abm " not in cpuinfo): + test.skip("Test requires x86-64 AVX2/LZCNT") + +test.compile(verilator_flags2=[ + "--binary", "--trace-vcd", "-CFLAGS", "\"-mavx2 -mlzcnt\"" +]) + +test.execute() + +code = r"[!-~]+" + +test.file_grep(test.trace_filename, rf"^b0 {code}$") +test.file_grep(test.trace_filename, rf"^b10 {code}$") +test.file_grep(test.trace_filename, rf"^b1 {code}$") +test.file_grep(test.trace_filename, rf"^b1000000000000000 {code}$") +test.file_grep(test.trace_filename, rf"^b10100101 {code}$") +test.file_grep(test.trace_filename, rf"^b1{'0' * 63} {code}$") +test.file_grep(test.trace_filename, rf"^b1{'0' * 32} {code}$") +test.file_grep(test.trace_filename, rf"^b1{'0' * 65} {code}$") + +test.file_grep_not(test.trace_filename, rf"^b0[01]+ {code}$") + +test.passes() diff --git a/test_regress/t/t_trace_vcd_shortened.v b/test_regress/t/t_trace_vcd_shortened.v new file mode 100644 index 000000000..1f334fca6 --- /dev/null +++ b/test_regress/t/t_trace_vcd_shortened.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +`define STRINGIFY(x) `"x`" + +module t; + logic [3:0] zero4 = 4'b0; + logic [3:0] small4 = 4'b0010; + logic [7:0] small8 = 8'h01; + logic [15:0] msb16 = 16'h8000; + logic [31:0] mid32 = 32'h0000_00a5; + logic [63:0] msb64 = 64'h8000_0000_0000_0000; + logic [99:0] wide_skip = 100'h1_0000_0000; + logic [67:0] wide_partial = 68'h2_0000_0000_0000_0000; + + initial begin + $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); + $dumpvars(0, t); + #1; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_trace_vcd_shortened_portable.py b/test_regress/t/t_trace_vcd_shortened_portable.py new file mode 100755 index 000000000..4c82e7cef --- /dev/null +++ b/test_regress/t/t_trace_vcd_shortened_portable.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of either the GNU Lesser General Public License Version 3 +# or the Perl Artistic License Version 2.0. +# SPDX-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') +test.top_filename = "t/t_trace_vcd_shortened.v" + +test.compile(verilator_flags2=[ + "--binary", "--trace-vcd", "-CFLAGS", "-DVL_PORTABLE_ONLY" +]) + +test.execute() + +code = r"[!-~]+" + +test.file_grep(test.trace_filename, rf"^b0 {code}$") +test.file_grep(test.trace_filename, rf"^b10 {code}$") +test.file_grep(test.trace_filename, rf"^b1 {code}$") +test.file_grep(test.trace_filename, rf"^b1000000000000000 {code}$") +test.file_grep(test.trace_filename, rf"^b10100101 {code}$") +test.file_grep(test.trace_filename, rf"^b1{'0' * 63} {code}$") +test.file_grep(test.trace_filename, rf"^b1{'0' * 32} {code}$") +test.file_grep(test.trace_filename, rf"^b1{'0' * 65} {code}$") + +test.file_grep_not(test.trace_filename, rf"^b0[01]+ {code}$") + +test.passes()