From 99c26d39944d97398899be3eda35ff7b04d189a0 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 21 Aug 2026 16:35:15 +0200 Subject: [PATCH] Update linfstwriter from upstream (#7193 prep) (#8181) --- include/fstcpp/fstcpp.h | 2 +- include/fstcpp/fstcpp_variable_info.h | 177 ++++++++++++++++++-------- include/fstcpp/fstcpp_writer.cpp | 43 ++++++- 3 files changed, 161 insertions(+), 61 deletions(-) diff --git a/include/fstcpp/fstcpp.h b/include/fstcpp/fstcpp.h index e17b33766..d8d5a9b6e 100644 --- a/include/fstcpp/fstcpp.h +++ b/include/fstcpp/fstcpp.h @@ -90,7 +90,7 @@ static inline constexpr unsigned bitPerEncodedBit(EncodingType type) { [[maybe_unused]] static const char* kEncodedBitToCharTable = ( "01" // Binary - "xzhu" // Verilog + "zxhu" // Verilog "wl-? " // Vhdl (padded with ' ') ); diff --git a/include/fstcpp/fstcpp_variable_info.h b/include/fstcpp/fstcpp_variable_info.h index 0074705b1..c5b4a4f5f 100644 --- a/include/fstcpp/fstcpp_variable_info.h +++ b/include/fstcpp/fstcpp_variable_info.h @@ -423,18 +423,26 @@ public: void emitValueChange(uint64_t current_time_index, const uint32_t *val, EncodingType encoding) { auto wh = emitValueChangeCommonPart(current_time_index, encoding); - for (unsigned i = 0; i < bitPerEncodedBit(encoding); ++i) { - // C++17: replace this with if constexpr - if (sizeof(T) == 8) { + // C++17: replace this with if constexpr + if (sizeof(T) == 8) { + if (encoding == EncodingType::VERILOG) { + uint64_t v = val[2]; // high bits + v <<= 32; + v |= val[0]; // low bits + wh.template write(v); + v = val[3]; // high bits + v <<= 32; + v |= val[1]; // low bits + wh.template write(v); + } else { uint64_t v = val[1]; // high bits v <<= 32; v |= val[0]; // low bits wh.template write(v); - val += 2; - } else { - wh.template write(val[0]); - val += 1; } + } else { + wh.template write(val[0]); + if (encoding == EncodingType::VERILOG) wh.template write(val[1]); } } @@ -517,7 +525,7 @@ public: } else { unsigned val = 0; for (unsigned i = 0; i < num_element; ++i) { - val |= rh.peek(i); + val |= rh.peek(i) << i; } uint64_t delta_time_index = time_index - prev_time_index; prev_time_index = time_index; @@ -525,8 +533,8 @@ public: // clang-format off case 0: delta_time_index = (delta_time_index<<2) | (0<<1) | 0; break; // '0' case 1: delta_time_index = (delta_time_index<<2) | (1<<1) | 0; break; // '1' - case 2: delta_time_index = (delta_time_index<<4) | (0<<1) | 1; break; // 'X' - case 3: delta_time_index = (delta_time_index<<4) | (1<<1) | 1; break; // 'Z' + case 2: delta_time_index = (delta_time_index<<4) | (1<<1) | 1; break; // 'Z' + case 3: delta_time_index = (delta_time_index<<4) | (0<<1) | 1; break; // 'X' // Not supporting VHDL now // LCOV_EXCL_START case 4: delta_time_index = (delta_time_index<<4) | (2<<1) | 1; break; // 'H' @@ -556,13 +564,29 @@ public: if (first) { first = false; } else { - FST_CHECK(enc == EncodingType::BINARY); // TODO - const bool has_non_binary = enc != EncodingType::BINARY; const uint64_t delta_time_index = time_index - prev_time_index; prev_time_index = time_index; - h // - .writeLEB128((delta_time_index << 1) | has_non_binary) - .writeUIntPartialForValueChange(rh.peek(), bitwidth); + switch (enc) { + case EncodingType::BINARY: { + h // + .writeLEB128(delta_time_index << 1) + .writeUIntPartialForValueChange(rh.peek(), bitwidth); + } break; + case EncodingType::VERILOG: { + h.writeLEB128((delta_time_index << 1) | 1); + const T val = rh.peek(); + const T xz = rh.peek(1); + for (int j = bitwidth; j > 0;) { + --j; + h.writeUIntBE( + kEncodedBitToCharTable[(((xz >> j) << 1) & 2) | ((val >> j) & 1)] + ); + } + } break; + [[unlikely]] case EncodingType::VHDL: { + FST_FAIL_STRING("VHDL format is unsupported with wide values"); + } break; + } } rh.skip(num_byte); } @@ -573,16 +597,26 @@ public: class VariableInfoLongInt { VariableInfo &info; unsigned num_words() const { return (info.bitwidth() + 63) / 64; } + unsigned num_words32() const { return (info.bitwidth() + 31) / 32; } public: VariableInfoLongInt(VariableInfo &info_) : info(info_) {} public: + size_t computeBytesNeededNoHeader(EncodingType encoding) const { + switch (encoding) { + case EncodingType::BINARY: + return num_words() * sizeof(uint64_t); + case EncodingType::VERILOG: + return num_words32() * sizeof(uint32_t) * 2; + [[unlikely]] case EncodingType::VHDL: + FST_FAIL_STRING("VHDL format is unsupported with wide values"); + } + FST_UNREACHABLE; + } + size_t computeBytesNeeded(EncodingType encoding) const { - return ( - kEmitTimeIndexAndEncodingSize + - num_words() * sizeof(uint64_t) * bitPerEncodedBit(encoding) - ); + return kEmitTimeIndexAndEncodingSize + computeBytesNeededNoHeader(encoding); } EmitWriterHelper emitValueChangeCommonPart(uint64_t current_time_index, EncodingType encoding) { @@ -600,13 +634,12 @@ public: public: void construct() { - const size_t nw = num_words(); + const size_t nw = num_words32(); info.resize(computeBytesNeeded(EncodingType::VERILOG)); EmitWriterHelper wh(info.data_ptr()); wh // .writeTimeIndexAndEncoding(0, EncodingType::VERILOG) - .fill(uint64_t(0), nw) - .fill(uint64_t(-1), nw); + .fill(static_cast(std::numeric_limits::max()) << 32, nw); } void emitValueChange(uint64_t current_time_index, const uint64_t val) { @@ -616,12 +649,12 @@ public: } void emitValueChange(uint64_t current_time_index, const uint32_t *val, EncodingType encoding) { - const unsigned nw32 = (info.bitwidth() + 31) / 32; - const unsigned bpb = bitPerEncodedBit(encoding); + const unsigned nw32 = num_words32(); auto wh = emitValueChangeCommonPart(current_time_index, encoding); - for (unsigned i = 0; i < bpb; ++i) { + switch (encoding) { + case EncodingType::BINARY: { for (unsigned j = 0; j < nw32 / 2; ++j) { uint64_t v = val[1]; // high bits v <<= 32; @@ -634,13 +667,25 @@ public: wh.write(v); val += 1; } + } break; + case EncodingType::VERILOG: { + for (unsigned j = 0; j < nw32; ++j) { + uint64_t v = val[1]; // high bits + v <<= 32; + v |= val[0]; // low bits + wh.write(v); + val += 2; + } + } break; + [[unlikely]] case EncodingType::VHDL: + FST_FAIL_STRING("VHDL format is unsupported with wide values"); } } void emitValueChange(uint64_t current_time_index, const uint64_t *val, EncodingType encoding) { - const unsigned nw_encoded = num_words() * bitPerEncodedBit(encoding); auto wh = emitValueChangeCommonPart(current_time_index, encoding); - wh.write(val, nw_encoded); + FST_CHECK(encoding == EncodingType::BINARY); + wh.write(val, num_words()); } void dumpInitialBits(std::vector &buf) const { @@ -663,15 +708,16 @@ public: break; } case EncodingType::VERILOG: { - for (unsigned word_index = nw; word_index-- > 0;) { - const uint64_t v0 = rh.peek(nw * 0 + word_index); - const uint64_t v1 = rh.peek(nw * 1 + word_index); + for (unsigned word_index = num_words32(); word_index-- > 0;) { + const uint64_t val = rh.peek(word_index); + const uint32_t aval = static_cast(val); + const uint32_t bval = static_cast(val >> 32); const unsigned num_bit = - (word_index * 64 + 64 > info.bitwidth()) ? (info.bitwidth() % 64) : 64; + (word_index * 32 + 32 > info.bitwidth()) ? (info.bitwidth() % 32) : 32; for (unsigned bit_index = num_bit; bit_index-- > 0;) { - const bool b0 = ((v0 >> bit_index) & uint64_t(1)); - const bool b1 = ((v1 >> bit_index) & uint64_t(1)); - const char c = kEncodedBitToCharTable[(b1 << 1) | b0]; + const bool a = ((aval >> bit_index) & 1); + const bool b = ((bval >> bit_index) & 1); + const char c = kEncodedBitToCharTable[(b << 1) | a]; buf.push_back(c); } } @@ -717,33 +763,58 @@ public: FST_DCHECK_GT(tail, rh.ptr); const auto time_index = rh.read(); const auto enc = rh.read(); - const auto num_element = bitPerEncodedBit(enc); - const auto num_byte = num_element * nw * sizeof(uint64_t); + const auto num_byte = computeBytesNeededNoHeader(enc); if (first) { // Note: [0] is initial value, which is already dumped in dumpInitialBits() first = false; } else { - FST_CHECK(enc == EncodingType::BINARY); // TODO - const bool has_non_binary = enc != EncodingType::BINARY; const uint64_t delta_time_index = time_index - prev_time_index; prev_time_index = time_index; - h.writeLEB128((delta_time_index << 1) | has_non_binary); - if (bitwidth % 64 != 0) { - const unsigned remaining = bitwidth % 64; - uint64_t hi64 = rh.peek(nw - 1); - // write from nw-1 to 1 - for (unsigned j = nw - 1; j > 0; --j) { - uint64_t lo64 = rh.peek(j - 1); - h.writeUIntBE((hi64 << (64 - remaining)) | (lo64 >> remaining)); - hi64 = lo64; + switch (enc) { + case EncodingType::BINARY: { + h.writeLEB128((delta_time_index << 1)); + if (bitwidth % 64 != 0) { + const unsigned remaining = bitwidth % 64; + uint64_t hi64 = rh.peek(nw - 1); + // write from nw-1 to 1 + for (unsigned j = nw - 1; j > 0; --j) { + uint64_t lo64 = rh.peek(j - 1); + h.writeUIntBE((hi64 << (64 - remaining)) | (lo64 >> remaining)); + hi64 = lo64; + } + // write 0 + h.writeUIntPartialForValueChange(hi64, remaining); + } else { + // write from nw-1 to 0 + for (unsigned j = nw; j-- > 0;) { + h.writeUIntBE(rh.peek(j)); + } } - // write 0 - h.writeUIntPartialForValueChange(hi64, remaining); - } else { - // write from nw-1 to 0 - for (unsigned j = nw; j-- > 0;) { - h.writeUIntBE(rh.peek(j)); + } break; + case EncodingType::VERILOG: { + h.writeLEB128((delta_time_index << 1) | 1); + const int fullWords = (bitwidth / 32); + if (int j = bitwidth % 32) { + const uint64_t val = rh.peek(fullWords); + while (j > 0) { + --j; + const uint64_t v = val >> j; + h.writeUIntBE(kEncodedBitToCharTable[((v >> 31) & 2) | (v & 1)]); + } } + for (size_t i = fullWords; i > 0;) { + --i; + const uint64_t val = rh.peek(i); + for (int j = 32; j > 0;) { + --j; + const uint64_t v = val >> j; + h.writeUIntBE(kEncodedBitToCharTable[((v >> 31) & 2) | (v & 1)]); + } + } + } break; + [[unlikely]] case EncodingType::VHDL: { + FST_FAIL_STRING("VHDL format is unsupported with wide values"); + } break; } } rh.skip(num_byte); diff --git a/include/fstcpp/fstcpp_writer.cpp b/include/fstcpp/fstcpp_writer.cpp index 386a76162..4a31c3af2 100644 --- a/include/fstcpp/fstcpp_writer.cpp +++ b/include/fstcpp/fstcpp_writer.cpp @@ -268,26 +268,55 @@ void Writer::emitValueChange(Handle handle, const char *val) { // For normal integer handles, const char* is "01xz..." (1B per bit) const uint32_t bitwidth{var_info.bitwidth()}; + const bool hasXZ = // Detects A-Z and a-z but not 0-9 and NOT `-` `?` + (std::accumulate(val, val + bitwidth, 0, [](int a, char b) { return a | b; }) & (1 << 6)) != + 0; FST_DCHECK_NE(bitwidth, 0); val += bitwidth; const unsigned num_words{(bitwidth + 63) / 64}; - m_packed_value_buffer_.assign(num_words, 0); + m_packed_value_buffer_.assign(num_words << (hasXZ ? 1 : 0), 0); for (unsigned i = 0; i < num_words; ++i) { const char *start{val - std::min((i + 1) * 64, bitwidth)}; const char *end{val - 64 * i}; m_packed_value_buffer_[i] = 0; for (const char *p = start; p < end; ++p) { // No checking for invalid characters, follow original C implementation - m_packed_value_buffer_[i] <<= 1; - m_packed_value_buffer_[i] |= static_cast(*p - '0'); + if (hasXZ) { + const size_t j = i << 1; + m_packed_value_buffer_[j] <<= 1; + m_packed_value_buffer_[j | 1] <<= 1; + switch (*p) { + case '0': + break; + case '1': { + m_packed_value_buffer_[i] |= 1; + } break; + case 'X': + case 'x': { + m_packed_value_buffer_[i] |= 1; + } // FALLTHROUGH + case 'Z': + case 'z': { + m_packed_value_buffer_[j | 1] |= 1; + } break; + [[unlikely]] default: { FST_FAIL_STRING("Unexpected char"); } break; + } + } else { + m_packed_value_buffer_[i] <<= 1; + m_packed_value_buffer_[i] |= static_cast(*p - '0'); + } } } - if (bitwidth <= 64) { + if (bitwidth <= 64 && !hasXZ) { emitValueChange(handle, m_packed_value_buffer_.front()); } else { - emitValueChange(handle, m_packed_value_buffer_.data(), EncodingType::BINARY); + emitValueChange( + handle, + m_packed_value_buffer_.data(), + hasXZ ? EncodingType::VERILOG : EncodingType::BINARY + ); } } @@ -605,7 +634,7 @@ void detail::ValueChangeData::writeEncodedPositions( } // encode as signed (value << 1) | 1 and write as signed LEB128 - h.writeLEB128Signed((value_to_encode << 1) | 1); + h.writeLEB128Signed((static_cast(value_to_encode) << 1) | 1); ++i; } @@ -695,7 +724,7 @@ void Writer::flushValueChangeDataConstPart_( (void)count; return std::make_pair(positions, memory_usage); }(); - const std::vector positions{p_tmp2.first}; + const std::vector positions{std::move(p_tmp2.first)}; const size_t memory_usage{p_tmp2.second}; // 4. Position Section