// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // // Code available from: https://verilator.org // // Copyright 2026-2026 by Wilson Snyder. 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 // //************************************************************************* /// /// \file /// \brief Verilator: Runtime support for force/release statements /// /// This file provides runtime data structures for efficient dynamic /// resolution of force/release statements. A sorted list of active /// forces is maintained that can be efficiently queried and modified /// at runtime. /// //************************************************************************* #ifndef VERILATOR_VERILATED_FORCE_H_ #define VERILATOR_VERILATED_FORCE_H_ #include "verilatedos.h" #include #include #include #include #include template using VlForceBaseType = typename std::remove_cv::type>::type; // VlForceRead - Helper functions to read a forced value // // These functions combine original value with forced values based on // VlForceVec entries. // This achieves O(k) complexity where k = number of active forces. template struct VlForceTypeInfo final { using Type = VlForceBaseType; static constexpr bool bitwise = std::is_integral::value || std::is_enum::value || VlIsVlWide::value; static constexpr bool unpackedArray = false; }; template struct VlForceArrayIndexer final { static constexpr std::size_t size = 1; static T& elem(T& value, std::size_t) { return value; } }; template struct VlForceArrayIndexer> final { static constexpr std::size_t size = N * VlForceArrayIndexer::size; static auto& elem(VlUnpacked& array, std::size_t index) { constexpr std::size_t subSize = VlForceArrayIndexer::size; return VlForceArrayIndexer::elem(array[index / subSize], index % subSize); } }; template struct VlForceTypeInfo> final { using Type = VlUnpacked; static constexpr bool bitwise = false; static constexpr bool unpackedArray = true; }; template ::value> struct VlForceStorageTypeOf final { using type = typename std::make_unsigned::type; }; template struct VlForceStorageTypeOf final { using type = typename std::make_unsigned::type>::type; }; template using VlForceStorageType = typename VlForceStorageTypeOf>::type; //============================================================================= // VlForceVec - Vector of active force entries for a signal // // This class maintains a sorted vector of non-overlapping force entries. // When a new force is added, it removes or trims existing entries that // overlap with the new range. // // The generated code will: // 1. Use addForce/release to update the active forces // 2. Call a generated read function that iterates entries and evaluates RHS class VlForceVec final { private: struct Entry final { int m_lsb; // Inclusive lower bit int m_msb; // Inclusive upper bit int m_rhsLsb; // Destination index that maps to RHS index 0 const void* m_rhsDatap; // Pointer to RHS storage bool operator<(const Entry& other) const { return m_msb < other.m_msb; } }; std::vector m_entries; // Sorted by msb, non-overlapping std::vector::iterator trimEntries(int lsb, int msb) { auto it = std::lower_bound(m_entries.begin(), m_entries.end(), lsb, [](const Entry& e, int bit) { return e.m_msb < bit; }); while (it != m_entries.end() && it->m_lsb <= msb) { if (it->m_lsb < lsb && it->m_msb > msb) { const Entry right{msb + 1, it->m_msb, it->m_rhsLsb, it->m_rhsDatap}; it->m_msb = lsb - 1; return m_entries.insert(++it, right); } if (it->m_lsb < lsb) { it->m_msb = lsb - 1; ++it; continue; } if (it->m_msb > msb) { it->m_lsb = msb + 1; return it; } it = m_entries.erase(it); } return it; } static QData extractRhsChunk(const Entry& entry, int rhsLsb, int width) { assert(width > 0 && width <= VL_QUADSIZE); assert(rhsLsb >= 0); const QData mask = static_cast(VL_MASK_Q(width)); const int rhsWidth = entry.m_msb - entry.m_rhsLsb + 1; if (rhsWidth <= VL_QUADSIZE) { const QData rhsVal = static_cast(*static_cast(entry.m_rhsDatap)); return (rhsVal >> rhsLsb) & mask; } const EData* const rhswp = static_cast(entry.m_rhsDatap); return VL_SEL_QWII(rhsWidth, rhswp, rhsLsb, width) & mask; } template static T applyBits(T cur, const Entry& entry, int lsb, int width, int rhsLsb) { const T lowMask = static_cast(VL_MASK_Q(width)); const T mask = static_cast(lowMask << lsb); const T rhsBits = static_cast( (static_cast(extractRhsChunk(entry, rhsLsb, width)) & lowMask) << lsb); return static_cast((cur & ~mask) | (rhsBits & mask)); } template static typename std::enable_if::value, T>::type applyEntry(T result, const Entry& entry) { EData* const reswp = result.data(); const int lword = VL_BITWORD_E(entry.m_lsb); const int hword = VL_BITWORD_E(entry.m_msb); for (int word = lword; word <= hword; ++word) { const int wordLsb = word * VL_EDATASIZE; const int segLsb = std::max(entry.m_lsb, wordLsb); const int segMsb = std::min(entry.m_msb, wordLsb + VL_EDATASIZE - 1); const int segWidth = segMsb - segLsb + 1; const int bitOffset = segLsb - wordLsb; const int rhsLsb = segLsb - entry.m_rhsLsb; reswp[word] = applyBits(reswp[word], entry, bitOffset, segWidth, rhsLsb); } return result; } template static typename std::enable_if::value && VlForceTypeInfo::bitwise, T>::type applyEntry(T result, const Entry& entry) { using U = VlForceStorageType; const int width = entry.m_msb - entry.m_lsb + 1; const int bits = static_cast(sizeof(U) * 8); const int rhsLsb = entry.m_lsb - entry.m_rhsLsb; const QData rhsChunk = extractRhsChunk(entry, rhsLsb, width); if (width >= bits) return static_cast(static_cast(rhsChunk)); return static_cast( applyBits(static_cast(result), entry, entry.m_lsb, width, rhsLsb)); } template static typename std::enable_if::bitwise, T>::type applyEntry(T result, const Entry& entry) { static_cast(result); return *static_cast*>(entry.m_rhsDatap); } public: VlForceVec() = default; template T read(T val) const { if VL_CONSTEXPR_CXX17 (VlForceTypeInfo::unpackedArray) { // Handling the case of a nested flattened array using recursion using ElemRef = decltype(VlForceArrayIndexer::elem(val, static_cast(0))); using Elem = VlForceBaseType; const int total = static_cast(VlForceArrayIndexer::size); for (const auto& entry : m_entries) { const Elem* const rhsBasep = static_cast(entry.m_rhsDatap); const int startIdx = entry.m_lsb; const int endIdx = entry.m_msb; for (int idx = startIdx; idx <= endIdx; idx++) { const int rhsIndex = idx - entry.m_rhsLsb; const std::size_t uidx = static_cast(idx); VlForceArrayIndexer::elem(val, uidx) = rhsBasep[rhsIndex]; } } return val; } for (const auto& entry : m_entries) { val = applyEntry(val, entry); } return val; } template T readIndex(T origVal, int index) const { if (m_entries.empty()) return origVal; const auto it = std::lower_bound(m_entries.begin(), m_entries.end(), index, [](const Entry& e, int idx) { return e.m_msb < idx; }); if (it != m_entries.end() && it->m_lsb <= index) { const int rhsIndex = index - it->m_rhsLsb; const T* const rhsBasep = static_cast(it->m_rhsDatap); return rhsBasep[rhsIndex]; } return origVal; } void addForce(int lsb, int msb, const void* rhsDatap, int rhsLsb) { assert(lsb <= msb); assert(rhsDatap); assert(rhsLsb <= lsb); auto it = trimEntries(lsb, msb); m_entries.insert(it, {lsb, msb, rhsLsb, rhsDatap}); } void release(int lsb, int msb) { assert(lsb <= msb); trimEntries(lsb, msb); } void touch() {} }; #endif // guard