diff --git a/include/verilated.cpp b/include/verilated.cpp index 2303927e6..82869fab1 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -815,7 +815,7 @@ double VL_ISTOR_D_W(int lbits, const WDataInP lwp) VL_MT_SAFE { if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp); const int words = VL_WORDS_I(lbits); VL_DEBUG_IFDEF(assert(words <= VL_MULS_MAX_WORDS);); - uint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here + VlWide pos; VL_NEGATE_W(words, pos, lwp); _vl_clean_inplace_w(lbits, pos); return -VL_ITOR_D_W(lbits, pos); @@ -829,9 +829,12 @@ std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE { const int maxdecwidth = (width + 3) * 4 / 3; // Or (maxdecwidth+7)/8], but can't have more than 4 BCD bits per word std::vector bcd(VL_WORDS_I(maxdecwidth)); - VL_ZERO_W(maxdecwidth, bcd.data()); + WDataOutP bcdp = WDataOutP::external(bcd.data()); + VL_ZERO_W(maxdecwidth, bcdp); std::vector tmp(VL_WORDS_I(maxdecwidth)); std::vector tmp2(VL_WORDS_I(maxdecwidth)); + WDataOutP tmpp = WDataOutP::external(tmp.data()); + WDataOutP tmp2p = WDataOutP::external(tmp2.data()); int from_bit = width - 1; // Skip all leading zeros for (; from_bit >= 0 && !(VL_BITRSHIFT_W(lwp, from_bit) & 1); --from_bit) {} @@ -840,15 +843,15 @@ std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE { // Any digits >= 5 need an add 3 (via tmp) for (int nibble_bit = 0; nibble_bit < maxdecwidth; nibble_bit += 4) { if ((VL_BITRSHIFT_W(bcd, nibble_bit) & 0xf) >= 5) { - VL_ZERO_W(maxdecwidth, tmp2.data()); + VL_ZERO_W(maxdecwidth, tmp2p); tmp2[VL_BITWORD_E(nibble_bit)] |= VL_EUL(0x3) << VL_BITBIT_E(nibble_bit); - VL_ASSIGN_W(maxdecwidth, tmp.data(), bcd.data()); - VL_ADD_W(VL_WORDS_I(maxdecwidth), bcd.data(), tmp.data(), tmp2.data()); + VL_ASSIGN_W(maxdecwidth, tmpp, bcdp); + VL_ADD_W(VL_WORDS_I(maxdecwidth), bcdp, tmpp, tmp2p); } } // Shift; bcd = bcd << 1 - VL_ASSIGN_W(maxdecwidth, tmp.data(), bcd.data()); - VL_SHIFTL_WWI(maxdecwidth, maxdecwidth, 32, bcd.data(), tmp.data(), 1); + VL_ASSIGN_W(maxdecwidth, tmpp, bcdp); + VL_SHIFTL_WWI(maxdecwidth, maxdecwidth, 32, bcdp, tmpp, 1); // bcd[0] = lwp[from_bit] if (VL_BITISSET_W(lwp, from_bit)) bcd[0] |= 1; } @@ -951,7 +954,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc, va_list ap) VL_MT_SAFE { // Format a Verilog $write style format into the output list // The format must be pre-processed (and lower cased) by Verilator. - // Arguments are each {"VFormatAttr character, int width, arg-value (or WDataIn* if wide)"} + // Arguments are each {"VFormatAttr character, int width, arg-value (or EData* if wide)"} // // Uses a single buffer internally; presumes only one usage per printf. // Also assumes variables < 64 are not wide, this assumption is @@ -1083,7 +1086,7 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc, void* thingp = nullptr; QData ld = 0; std::vector strwide; - WDataInP lwp = nullptr; + WDataInP lwp{nullptr}; int lsb = 0; double real = 0.0; if (formatAttr == VL_VFORMATATTR_COMPLEX) { // printed as string @@ -1093,8 +1096,9 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc, real = va_arg(ap, double); ld = VL_RTOIROUND_Q_D(real); strwide.resize(2); - VL_SET_WQ(strwide, ld); - lwp = strwide.data(); + WDataOutP strwidep = WDataOutP::external(strwide.data()); + VL_SET_WQ(strwidep, ld); + lwp = strwidep; lbits = 64; // Not changint fmt == 'p' to fmt = 'g', as need fmts correct } else if (formatAttr == VL_VFORMATATTR_STRING) { @@ -1105,10 +1109,11 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc, if (lbits <= VL_QUADSIZE) { ld = VL_VA_ARG_Q_(ap, lbits); strwide.resize(2); - VL_SET_WQ(strwide, ld); - lwp = strwide.data(); + WDataOutP strwidep = WDataOutP::external(strwide.data()); + VL_SET_WQ(strwidep, ld); + lwp = strwidep; } else { - lwp = va_arg(ap, WDataInP); + lwp = WDataInP::external(va_arg(ap, EData*)); ld = lwp[0]; } if (fmt == 'p') { @@ -1194,8 +1199,9 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc, } else { if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) { std::vector neg(VL_WORDS_I(lbits)); - VL_NEGATE_W(VL_WORDS_I(lbits), neg.data(), lwp); - append = "-"s + VL_DECIMAL_NW(lbits, neg.data()); + WDataOutP negp = WDataOutP::external(neg.data()); + VL_NEGATE_W(VL_WORDS_I(lbits), negp, lwp); + append = "-"s + VL_DECIMAL_NW(lbits, negp); } else { append = VL_DECIMAL_NW(lbits, lwp); } @@ -1262,9 +1268,10 @@ void _vl_vsformat(std::string& output, const std::string& format, int argc, if (truncFront < 0) truncFront = 0; lbits = chars * 8; strwide.resize(VL_WORDS_I(lbits)); - lwp = strwide.data(); + WDataOutP strwidep = WDataOutP::external(strwide.data()); + lwp = strwidep; lsb = lbits - 1; - VL_NTOI_W(lbits, strwide.data(), *strp, truncFront); + VL_NTOI_W(lbits, strwidep, *strp, truncFront); } if (widthSet || left) { @@ -1459,7 +1466,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf const std::string& format, int argc, va_list ap) VL_MT_SAFE { // Read a Verilog $sscanf/$fscanf style format into the output list // The format must be pre-processed (and lower cased) by Verilator - // Arguments are in "width, arg-value (or WDataIn* if wide)" form + // Arguments are in "width, arg-value (or EData* if wide)" form static thread_local std::string t_tmp; int floc = fbits - 1; IData got = 0; @@ -1546,7 +1553,8 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf VlWide qowp; VL_SET_WQ(qowp, 0ULL); - WDataOutP owp = (obits <= 64) ? qowp : static_cast(thingp); + WDataOutP owp = WDataOutP::external((obits <= 64) ? qowp.data() + : static_cast(thingp)); for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; t_tmp.clear(); @@ -1649,7 +1657,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf case 'u': { // Read packed 2-value binary data const int bytes = VL_BYTES_I(obits); - char* const out = reinterpret_cast(owp); + char* const out = reinterpret_cast(owp.datap()); if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done; const int last = bytes % 4; if (last != 0 @@ -1659,7 +1667,7 @@ IData _vl_vsscanf(FILE* fp, // If a fscanf } case 'z': { // Read packed 4-value binary data - char* out = reinterpret_cast(owp); + char* out = reinterpret_cast(owp.datap()); int bytes = VL_BYTES_I(obits); while (bytes > 0) { const int abytes = std::min(4, bytes); @@ -1801,7 +1809,7 @@ IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE { IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE { std::string output; const IData ret = VL_FERROR_IN(fpi, output /*ref*/); - _vl_string_to_vint(obits, outwp, output.length(), output.c_str()); + _vl_string_to_vint(obits, outwp.datap(), output.length(), output.c_str()); return ret; } @@ -1866,7 +1874,7 @@ void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc, _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); } -void VL_SFORMAT_NX(int obits, void* destp, const std::string& format, int argc, ...) VL_MT_SAFE { +void VL_SFORMAT_NX(int obits, EData* destp, const std::string& format, int argc, ...) VL_MT_SAFE { static thread_local std::string t_output; // static only for speed t_output = ""; va_list ap; @@ -1928,9 +1936,10 @@ IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_S FILE* const fp = VL_CVT_I_FP(fpi); if (VL_UNLIKELY(!fp)) return ~0U; // -1 + WDataInP fromp{nullptr}; va_list ap; va_start(ap, argc); - const IData got = _vl_vsscanf(fp, 0, nullptr, "", format, argc, ap); + const IData got = _vl_vsscanf(fp, 0, fromp, "", format, argc, ap); va_end(ap); return got; } @@ -1965,10 +1974,11 @@ IData VL_SSCANF_IWNX(int lbits, const WDataInP lwp, const std::string& format, i } IData VL_SSCANF_INNX(int, const std::string& ld, const std::string& format, int argc, ...) VL_MT_SAFE { + WDataInP fromp{nullptr}; va_list ap; va_start(ap, argc); const IData got - = _vl_vsscanf(nullptr, static_cast(ld.length() * 8), nullptr, ld, format, argc, ap); + = _vl_vsscanf(nullptr, static_cast(ld.length() * 8), fromp, ld, format, argc, ap); va_end(ap); return got; } @@ -2052,7 +2062,8 @@ IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi if (shift == start_shift) *datap = 0; *datap |= ((static_cast(c) << static_cast(shift)) & VL_MASK_Q(width)); } else { - WDataOutP datap = &(reinterpret_cast(memp))[entry * VL_WORDS_I(width)]; + const WDataOutP datap = WDataOutP::external( + &(reinterpret_cast(memp))[entry * VL_WORDS_I(width)]); if (shift == start_shift) VL_ZERO_W(width, datap); datap[VL_BITWORD_E(shift)] |= (static_cast(c) << VL_BITBIT_E(shift)); } @@ -2545,7 +2556,7 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) { *datap = ((*datap << static_cast(shift)) + static_cast(value)) & VL_MASK_Q(m_bits); } else { - WDataOutP datap = reinterpret_cast(valuep); + const WDataOutP datap = WDataOutP::external(reinterpret_cast(valuep)); if (!innum) VL_ZERO_W(m_bits, datap); _vl_shiftl_inplace_w(m_bits, datap, static_cast(shift)); datap[0] |= value; @@ -2617,7 +2628,7 @@ void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { fprintf(m_fp, "%s\n", formatBinary(32, lo)); } } else { - const WDataInP datap = reinterpret_cast(valuep); + const WDataInP datap = WDataInP::external(reinterpret_cast(valuep)); // output as a sequence of VL_EDATASIZE'd words // from MSB to LSB. Mask off the MSB word which could // contain junk above the top of valid data. @@ -2685,8 +2696,8 @@ void VL_READMEM_N(bool hex, // Hex format, else binary QData* const datap = &(reinterpret_cast(memp))[entry]; rmem.setData(datap, value); } else { - WDataOutP datap - = &(reinterpret_cast(memp))[entry * VL_WORDS_I(bits)]; + EData* const datap + = &(reinterpret_cast(memp))[entry * VL_WORDS_I(bits)]; rmem.setData(datap, value); } } @@ -2728,8 +2739,8 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary const QData* const datap = &(reinterpret_cast(memp))[row_offset]; wmem.print(addr, false, datap); } else { - const WDataInP memDatap = reinterpret_cast(memp); - const WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)]; + const EData* const datap + = &(reinterpret_cast(memp))[row_offset * VL_WORDS_I(bits)]; wmem.print(addr, false, datap); } } diff --git a/include/verilated.h b/include/verilated.h index b90f80bc5..dcb5144cd 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -120,8 +120,7 @@ using CData = uint8_t; ///< Data representing 'bit' of 1-8 packed bits using SData = uint16_t; ///< Data representing 'bit' of 9-16 packed bits using IData = uint32_t; ///< Data representing 'bit' of 17-32 packed bits using QData = uint64_t; ///< Data representing 'bit' of 33-64 packed bits -using EData = uint32_t; ///< Data representing one element of WData array -using WData = EData; ///< Data representing >64 packed bits (used as pointer) +using EData = uint32_t; ///< Data representing one element of VlWide // F = float; // No typedef needed; Verilator uses float // D = double; // No typedef needed; Verilator uses double // N = std::string; // No typedef needed; Verilator uses string @@ -129,9 +128,6 @@ using WData = EData; ///< Data representing >64 packed bits (used as poin // R = VlQueue; // clang-format on -using WDataInP = const WData*; ///< 'bit' of >64 packed bits as array input to a function -using WDataOutP = WData*; ///< 'bit' of >64 packed bits as array output from a function - enum VerilatedVarType : uint8_t { VLVT_UNKNOWN = 0, VLVT_PTR, // Pointer to something @@ -139,7 +135,7 @@ enum VerilatedVarType : uint8_t { VLVT_UINT16, // AKA SData VLVT_UINT32, // AKA IData VLVT_UINT64, // AKA QData - VLVT_WDATA, // AKA WData + VLVT_WDATA, // AKA VlWide VLVT_STRING, // C++ string VLVT_REAL // AKA double }; diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index c97c56640..23abb4594 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -70,7 +70,9 @@ svLogic svGetBitselLogic(const svLogicVecVal* sp, int bit) { | (((sp[VL_BITWORD_I(bit)].bval >> VL_BITBIT_I(bit)) & 1) << 1)); } -void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { VL_ASSIGNBIT_WI(bit, dp, s); } +void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { + VL_ASSIGNBIT_WI(bit, WDataOutP::external(dp), s); +} void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) { // Verilator doesn't support X/Z so only aval dp[VL_BITWORD_I(bit)].aval = ((dp[VL_BITWORD_I(bit)].aval & ~(VL_UL(1) << VL_BITBIT_I(bit))) @@ -290,7 +292,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, break; } case VLVT_WDATA: { - WDataInP wdatap = (reinterpret_cast(datap)); + WDataInP wdatap = WDataInP::external(reinterpret_cast(datap)); for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) d[i] = wdatap[i]; return; } @@ -329,7 +331,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl break; } case VLVT_WDATA: { - WDataInP wdatap = (reinterpret_cast(datap)); + WDataInP wdatap = WDataInP::external(reinterpret_cast(datap)); for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) { d[i].aval = wdatap[i]; d[i].bval = 0; @@ -355,7 +357,7 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV case VLVT_UINT32: *(reinterpret_cast(datap)) = s[0]; return; case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1], s[0]); break; case VLVT_WDATA: { - WDataOutP wdatap = (reinterpret_cast(datap)); + WDataOutP wdatap = WDataOutP::external(reinterpret_cast(datap)); for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i]; return; } @@ -377,7 +379,7 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic case VLVT_UINT32: *(reinterpret_cast(datap)) = s[0].aval; return; case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break; case VLVT_WDATA: { - WDataOutP wdatap = (reinterpret_cast(datap)); + WDataOutP wdatap = WDataOutP::external(reinterpret_cast(datap)); for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i].aval; return; } diff --git a/include/verilated_dpi.h b/include/verilated_dpi.h index cb6efff68..fd8f177ee 100644 --- a/include/verilated_dpi.h +++ b/include/verilated_dpi.h @@ -63,7 +63,7 @@ static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp } static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; } static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE { - VL_SET_WQ(owp, ld); + VL_SET_WQ(WDataOutP::external(owp), ld); } // Convert svLogicVecVal to Verilator internal data diff --git a/include/verilated_force.h b/include/verilated_force.h index 8160fdffb..d50d27c18 100644 --- a/include/verilated_force.h +++ b/include/verilated_force.h @@ -145,7 +145,7 @@ private: return (rhsVal >> rhsLsb) & mask; } - const EData* const rhswp = static_cast(entry.m_rhsDatap); + WDataInP rhswp = WDataInP::external(static_cast(entry.m_rhsDatap)); return VL_SEL_QWII(rhsWidth, rhswp, rhsLsb, width) & mask; } @@ -198,7 +198,7 @@ private: template typename std::enable_if::value>::type applyEntries(T& val) const { for (const auto& entry : m_entries) { - applyEntry(val.data(), entry, entry.m_lsb, entry.m_msb, 0); + applyEntry(val, entry, entry.m_lsb, entry.m_msb, 0); } } @@ -262,14 +262,14 @@ public: IData readSelI(int lbits, WDataInP valp, int lsb, int width) const { IData result; - readSel(lbits, valp, &result, lsb, width); + readSel(lbits, valp, WDataOutP::external(reinterpret_cast(&result)), lsb, width); result &= VL_MASK_I(width); return result; } QData readSelQ(int lbits, WDataInP valp, int lsb, int width) const { QData result; - readSel(lbits, valp, reinterpret_cast(&result), lsb, width); + readSel(lbits, valp, WDataOutP::external(reinterpret_cast(&result)), lsb, width); result &= VL_MASK_Q(width); return result; } @@ -277,9 +277,8 @@ public: template VlWide readSelW(int lbits, WDataInP valp, int lsb, int width) const { VlWide result; - WDataOutP const reswp = result.data(); - readSel(lbits, valp, reswp, lsb, width); - reswp[N_Words - 1] &= VL_MASK_E(width); + readSel(lbits, valp, result, lsb, width); + result[N_Words - 1] &= VL_MASK_E(width); return result; } diff --git a/include/verilated_fst_c.cpp b/include/verilated_fst_c.cpp index 5bf3db12b..0e3706725 100644 --- a/include/verilated_fst_c.cpp +++ b/include/verilated_fst_c.cpp @@ -412,11 +412,10 @@ void VerilatedFstBuffer::emitQData(uint32_t code, QData newval, int) { } VL_ATTR_ALWINLINE -void VerilatedFstBuffer::emitWData(uint32_t code, const WData* newvalp, int) { +void VerilatedFstBuffer::emitWData(uint32_t code, WDataInP newval, int) { VL_DEBUG_IFDEF(assert(m_symbolp[code]);); // LCOV_EXCL_BR_LINE m_owner.emitTimeChangeMaybe(); - // call emitValueChange(handle, uint32_t*) - m_fst->emitValueChange(m_symbolp[code], newvalp); + m_fst->emitValueChange(m_symbolp[code], newval.datap()); } VL_ATTR_ALWINLINE diff --git a/include/verilated_fst_c.h b/include/verilated_fst_c.h index caf185bce..de1b0eeca 100644 --- a/include/verilated_fst_c.h +++ b/include/verilated_fst_c.h @@ -232,7 +232,7 @@ class VerilatedFstBuffer VL_NOT_FINAL { VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int); VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int); VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int); - VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int); + VL_ATTR_ALWINLINE void emitWData(uint32_t code, WDataInP newval, int); VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval); }; diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 44a658c37..2bb4ee4e1 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -169,7 +169,7 @@ extern void VL_SFORMAT_NX(int obits, IData& destr, const std::string& format, in ...) VL_MT_SAFE; extern void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc, ...) VL_MT_SAFE; -extern void VL_SFORMAT_NX(int obits, void* destp, const std::string& format, int argc, +extern void VL_SFORMAT_NX(int obits, EData* destp, const std::string& format, int argc, ...) VL_MT_SAFE; extern void VL_STACKTRACE() VL_MT_SAFE; @@ -194,20 +194,20 @@ extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIi #define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit)) // Create two 32-bit words from quadword -// WData is always at least 2 words; does not clean upper bits -#define VL_SET_WQ(owp, data) \ - do { \ - (owp)[0] = static_cast(data); \ - (owp)[1] = static_cast((data) >> VL_EDATASIZE); \ - } while (false) -#define VL_SET_WI(owp, data) \ - do { \ - (owp)[0] = static_cast(data); \ - (owp)[1] = 0; \ - } while (false) -#define VL_SET_QW(lwp) \ - ((static_cast((lwp)[0])) \ - | (static_cast((lwp)[1]) << (static_cast(VL_EDATASIZE)))) +// VLWide is always at least 2 words; does not clean upper bits +static inline WDataOutP VL_SET_WQ(WDataOutP owp, QData data) VL_PURE { + owp[0] = static_cast(data); + owp[1] = static_cast(data >> VL_EDATASIZE); + return owp; +} +static inline WDataOutP VL_SET_WI(WDataOutP owp, IData data) VL_PURE { + owp[0] = static_cast(data); + owp[1] = 0; + return owp; +} +static inline QData VL_SET_QW(WDataInP lwp) VL_PURE { + return (static_cast(lwp[1]) << VL_EDATASIZE) | static_cast(lwp[0]); +} #define VL_SET_QII(ld, rd) ((static_cast(ld) << 32ULL) | static_cast(rd)) // Return FILE* from IData @@ -376,15 +376,18 @@ std::string vl_timescaled_double(double value, const char* format = "%0.0f%s") V VL_ATTR_ALWINLINE static WDataOutP VL_MEMSET_ZERO_W(WDataOutP owp, int words) VL_MT_SAFE { - return static_cast(std::memset(owp, 0, words * sizeof(EData))); + for (size_t i = 0; i < words; ++i) owp[i] = 0; + return owp; } VL_ATTR_ALWINLINE static WDataOutP VL_MEMSET_ONES_W(WDataOutP owp, int words) VL_MT_SAFE { - return static_cast(std::memset(owp, 0xff, words * sizeof(EData))); + for (size_t i = 0; i < words; ++i) owp[i] = ~static_cast(0); + return owp; } VL_ATTR_ALWINLINE static WDataOutP VL_MEMCPY_W(WDataOutP owp, WDataInP const iwp, int words) VL_MT_SAFE { - return static_cast(std::memcpy(owp, iwp, words * sizeof(EData))); + for (size_t i = 0; i < words; ++i) owp[i] = iwp[i]; + return owp; } // Output clean @@ -502,12 +505,12 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE { while (lsb < (obits) - BITS_PER_DIGIT) { \ const uint32_t data = *chunkp; \ ++chunkp; \ - _vl_insert_WI((owp).data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \ + _vl_insert_WI(owp, data, lsb + BITS_PER_DIGIT - 1, lsb); \ lsb += BITS_PER_DIGIT; \ } \ if (lsb < (obits)) { \ const uint32_t msb_data = *chunkp; \ - _vl_insert_WI((owp).data(), msb_data, (obits) - 1, lsb); \ + _vl_insert_WI(owp, msb_data, (obits) - 1, lsb); \ } \ (owp)[words - 1] &= VL_MASK_E(obits); \ } @@ -558,16 +561,16 @@ static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE { while (lsb + BITS_PER_DIGIT < (obits)) { \ static_assert(std::is_same::value, "IData and EData mismatch"); \ const uint32_t data \ - = VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, (rwp).data(), lsb, BITS_PER_DIGIT); \ + = VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, rwp, lsb, BITS_PER_DIGIT); \ *chunkp = data & VL_MASK_E(BITS_PER_DIGIT); \ ++chunkp; \ lsb += BITS_PER_DIGIT; \ } \ if (lsb < (obits)) { \ - const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits) - lsb); \ + const uint32_t msb_data = VL_SEL_IWII((obits) + 1, rwp, lsb, (obits) - lsb); \ *chunkp = msb_data & VL_MASK_E((obits) - lsb); \ } \ - _butemp.set(0, *(rwp).data() & 1); /* force update the sign */ \ + _butemp.set(0, rwp[0] & 1); /* force update the sign */ \ (svar).write(_butemp); \ } @@ -596,7 +599,7 @@ static inline WDataOutP VL_EXTEND_WQ(int obits, int, WDataOutP owp, QData ld) VL static inline WDataOutP VL_EXTEND_WW(int obits, int lbits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { const int lwords = VL_WORDS_I(lbits); - VL_PREFETCH_RD(lwp); + VL_PREFETCH_RD(lwp.datap()); VL_MEMSET_ZERO_W(owp + lwords, VL_WORDS_I(obits) - lwords); return VL_MEMCPY_W(owp, lwp, lwords); } @@ -636,7 +639,7 @@ static inline WDataOutP VL_EXTENDS_WQ(int obits, int lbits, WDataOutP owp, QData static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { const int lwords = VL_WORDS_I(lbits); - VL_PREFETCH_RD(lwp); + VL_PREFETCH_RD(lwp.datap()); owp[lwords - 1] = lwp[lwords - 1]; if (VL_SIGN_E(lbits, lwp[lwords - 1])) { owp[lwords - 1] |= ~VL_MASK_E(lbits); @@ -1152,29 +1155,25 @@ static inline QData VL_MULS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE { return lhs_signed * rhs_signed; } -static inline WDataOutP VL_MULS_WWW(int lbits, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { +static inline WDataOutP VL_MULS_WWW(int lbits, WDataOutP owp, WDataInP lwp, + WDataInP rwp) VL_MT_SAFE { const int words = VL_WORDS_I(lbits); VL_DEBUG_IFDEF(assert(words <= VL_MULS_MAX_WORDS);); - // cppcheck-suppress variableScope - WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here - // cppcheck-suppress variableScope - WData rwstore[VL_MULS_MAX_WORDS]; - WDataInP lwusp = lwp; - WDataInP rwusp = rwp; + VlWide lwstore; + VlWide rwstore; const EData lneg = VL_SIGN_E(lbits, lwp[words - 1]); if (lneg) { // Negate lhs - lwusp = lwstore; VL_NEGATE_W(words, lwstore, lwp); lwstore[words - 1] &= VL_MASK_E(lbits); // Clean it + lwp = lwstore; } const EData rneg = VL_SIGN_E(lbits, rwp[words - 1]); if (rneg) { // Negate rhs - rwusp = rwstore; VL_NEGATE_W(words, rwstore, rwp); rwstore[words - 1] &= VL_MASK_E(lbits); // Clean it + rwp = rwstore; } - VL_MUL_W(words, owp, lwusp, rwusp); + VL_MUL_W(words, owp, lwp, rwp); owp[words - 1] &= VL_MASK_E( lbits); // Clean. Note it's ok for the multiply to overflow into the sign bit if ((lneg ^ rneg) & 1) { // Negate output (not using NEGATE, as owp==lwp) @@ -1222,49 +1221,41 @@ static inline QData VL_MODDIVS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE { return lhs_signed % rhs_signed; } -static inline WDataOutP VL_DIVS_WWW(int lbits, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { +static inline WDataOutP VL_DIVS_WWW(int lbits, WDataOutP owp, WDataInP lwp, + WDataInP rwp) VL_MT_SAFE { const int lwords = VL_WORDS_I(lbits); const EData lsign = VL_SIGN_E(lbits, lwp[lwords - 1]); const EData rsign = VL_SIGN_E(lbits, rwp[lwords - 1]); VL_DEBUG_IFDEF(assert(lwords <= VL_MULS_MAX_WORDS);); - // cppcheck-suppress variableScope - WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here - // cppcheck-suppress variableScope - WData rwstore[VL_MULS_MAX_WORDS]; - WDataInP ltup = lwp; - WDataInP rtup = rwp; - if (lsign) ltup = _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, lwstore, lwp)); - if (rsign) rtup = _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, rwstore, rwp)); + VlWide lwstore; + VlWide rwstore; + if (lsign) lwp = _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, lwstore, lwp)); + if (rsign) rwp = _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, rwstore, rwp)); if ((lsign && !rsign) || (!lsign && rsign)) { - WData qNoSign[VL_MULS_MAX_WORDS]; - VL_DIV_WWW(lbits, qNoSign, ltup, rtup); + VlWide qNoSign; + VL_DIV_WWW(lbits, qNoSign, lwp, rwp); _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, owp, qNoSign)); return owp; } - return VL_DIV_WWW(lbits, owp, ltup, rtup); + return VL_DIV_WWW(lbits, owp, lwp, rwp); } -static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP const lwp, - WDataInP const rwp) VL_MT_SAFE { +static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP lwp, + WDataInP rwp) VL_MT_SAFE { const int lwords = VL_WORDS_I(lbits); const EData lsign = VL_SIGN_E(lbits, lwp[lwords - 1]); const EData rsign = VL_SIGN_E(lbits, rwp[lwords - 1]); VL_DEBUG_IFDEF(assert(lwords <= VL_MULS_MAX_WORDS);); - // cppcheck-suppress variableScope - WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here - // cppcheck-suppress variableScope - WData rwstore[VL_MULS_MAX_WORDS]; - WDataInP ltup = lwp; - WDataInP rtup = rwp; - if (lsign) ltup = _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, lwstore, lwp)); - if (rsign) rtup = _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, rwstore, rwp)); + VlWide lwstore; + VlWide rwstore; + if (lsign) lwp = _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, lwstore, lwp)); + if (rsign) rwp = _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, rwstore, rwp)); if (lsign) { // Only dividend sign matters for modulus - WData qNoSign[VL_MULS_MAX_WORDS]; - VL_MODDIV_WWW(lbits, qNoSign, ltup, rtup); + VlWide qNoSign; + VL_MODDIV_WWW(lbits, qNoSign, lwp, rwp); _vl_clean_inplace_w(lbits, VL_NEGATE_W(lwords, owp, qNoSign)); return owp; } - return VL_MODDIV_WWW(lbits, owp, ltup, rtup); + return VL_MODDIV_WWW(lbits, owp, lwp, rwp); } #define VL_POW_IIQ(obits, lbits, rbits, lhs, rhs) VL_POW_QQQ(obits, lbits, rbits, lhs, rhs) @@ -2890,9 +2881,9 @@ static inline void VL_SET_QUEUE_BIT(VlQueue>& queue, int dstElem VlWide& element = queue.atWrite(elemIdx); if (value) { - VL_ASSIGNBIT_WO(actualBitPos, element.data()); + VL_ASSIGNBIT_WO(actualBitPos, element); } else { - VL_ASSIGNBIT_WI(actualBitPos, element.data(), 0); + VL_ASSIGNBIT_WI(actualBitPos, element, 0); } } @@ -3384,7 +3375,8 @@ static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataIn if (w == obits) return; obits -= w; } - _vl_insert_WW(iowp, rwp + wordoff + (lsb != 0), upperbits + obits - 1, upperbits, rbits); + _vl_insert_WW(iowp, rwp + static_cast(wordoff + (lsb != 0)), upperbits + obits - 1, + upperbits, rbits); } //====================================================================== diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index 19817b8d0..b35769d83 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -349,7 +349,7 @@ void VlRandomVar::emitConcreteValue(std::ostream& s) const { } else if (w <= VL_QUADSIZE) { bit = (*static_cast(dp) >> i) & 1; } else { - const EData* const wp = static_cast(dp); + const WDataInP wp = WDataInP::external(static_cast(dp)); bit = (wp[VL_BITWORD_E(i)] >> VL_BITBIT_E(i)) & 1; } s << (bit ? '1' : '0'); @@ -381,7 +381,7 @@ bool VlRandomVar::set(const std::string& idx, const std::string& val) const { VL_SET_WQ(qiwp, 0ULL); if (!idx.empty() && !parseSMTNum(64, qiwp, idx)) return false; const int nidx = qiwp[0]; - if (obits > VL_QUADSIZE) owp = reinterpret_cast(datap(nidx)); + if (obits > VL_QUADSIZE) owp = WDataOutP::external(reinterpret_cast(datap(nidx))); if (!parseSMTNum(obits, owp, val)) return false; if (obits <= VL_BYTESIZE) { diff --git a/include/verilated_saif_c.cpp b/include/verilated_saif_c.cpp index d6632dca7..18f579ec2 100644 --- a/include/verilated_saif_c.cpp +++ b/include/verilated_saif_c.cpp @@ -117,7 +117,7 @@ public: updateLastTime(time); } - VL_ATTR_ALWINLINE void emitWData(uint64_t time, const WData* newvalp, uint32_t bits); + VL_ATTR_ALWINLINE void emitWData(uint64_t time, WDataInP newval, uint32_t bits); VL_ATTR_ALWINLINE void updateLastTime(uint64_t val) { m_lastTime = val; } // ACCESSORS @@ -229,13 +229,13 @@ void VerilatedSaifActivityVar::emitBit(const uint64_t time, const CData newval) } VL_ATTR_ALWINLINE -void VerilatedSaifActivityVar::emitWData(const uint64_t time, const WData* newvalp, +void VerilatedSaifActivityVar::emitWData(const uint64_t time, WDataInP newval, const uint32_t bits) { assert(m_lastTime <= time); const uint64_t dt = time - m_lastTime; for (std::size_t i = 0; i < std::min(m_width, bits); ++i) { const size_t wordIndex = i / VL_EDATASIZE; - m_bits[i].aggregateVal(dt, (newvalp[wordIndex] >> VL_BITBIT_E(i)) & 1); + m_bits[i].aggregateVal(dt, (newval[wordIndex] >> VL_BITBIT_E(i)) & 1); } updateLastTime(time); @@ -670,12 +670,12 @@ void VerilatedSaifBuffer::emitQData(const uint32_t code, const QData newval, con } VL_ATTR_ALWINLINE -void VerilatedSaifBuffer::emitWData(const uint32_t code, const WData* newvalp, const int bits) { +void VerilatedSaifBuffer::emitWData(const uint32_t code, WDataInP newval, const int bits) { assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code) && "Activity must be declared earlier"); VerilatedSaifActivityVar& activity = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code); - activity.emitWData(m_owner.currentTime(), newvalp, bits); + activity.emitWData(m_owner.currentTime(), newval, bits); } VL_ATTR_ALWINLINE diff --git a/include/verilated_saif_c.h b/include/verilated_saif_c.h index 3ad4e630c..63cc717c0 100644 --- a/include/verilated_saif_c.h +++ b/include/verilated_saif_c.h @@ -247,7 +247,7 @@ class VerilatedSaifBuffer VL_NOT_FINAL { VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits); VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits); VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits); - VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits); + VL_ATTR_ALWINLINE void emitWData(uint32_t code, WDataInP newval, int bits); VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval); }; diff --git a/include/verilated_trace.h b/include/verilated_trace.h index 748e45f7e..de8f89946 100644 --- a/include/verilated_trace.h +++ b/include/verilated_trace.h @@ -378,7 +378,7 @@ public: // duck-typed void emitSData(uint32_t code, SData newval, int bits) = 0; // duck-typed void emitIData(uint32_t code, IData newval, int bits) = 0; // duck-typed void emitQData(uint32_t code, QData newval, int bits) = 0; - // duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0; + // duck-typed void emitWData(uint32_t code, WDataInP newval, int bits) = 0; // duck-typed void emitDouble(uint32_t code, double newval) = 0; VL_ATTR_ALWINLINE uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; } @@ -389,7 +389,7 @@ public: void fullSData(uint32_t* oldp, SData newval, int bits); void fullIData(uint32_t* oldp, IData newval, int bits); void fullQData(uint32_t* oldp, QData newval, int bits); - void fullWData(uint32_t* oldp, const WData* newvalp, int bits); + void fullWData(uint32_t* oldp, WDataInP newval, int bits); void fullDouble(uint32_t* oldp, double newval); void fullEvent(uint32_t* oldp, const VlEventBase* newvalp); void fullEventTriggered(uint32_t* oldp); @@ -417,10 +417,10 @@ public: const uint64_t diff = old ^ newval; if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits); } - VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) { + VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, WDataInP newval, int bits) { for (int i = 0; i < (bits + 31) / 32; ++i) { - if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) { - fullWData(oldp, newvalp, bits); + if (VL_UNLIKELY(oldp[i] ^ newval[i])) { + fullWData(oldp, newval, bits); return; } } diff --git a/include/verilated_trace_imp.h b/include/verilated_trace_imp.h index 1916e5e9a..0843f1843 100644 --- a/include/verilated_trace_imp.h +++ b/include/verilated_trace_imp.h @@ -602,11 +602,11 @@ void VerilatedTraceBuffer::fullQData(uint32_t* oldp, QData newval, int } template <> -void VerilatedTraceBuffer::fullWData(uint32_t* oldp, const WData* newvalp, int bits) { +void VerilatedTraceBuffer::fullWData(uint32_t* oldp, WDataInP newval, int bits) { const uint32_t code = oldp - m_sigs_oldvalp; - for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i]; + for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newval[i]; if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return; - emitWData(code, newvalp, bits); + emitWData(code, newval, bits); } template <> diff --git a/include/verilated_types.h b/include/verilated_types.h index 6e7833ae3..08a6b93bb 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -64,16 +64,166 @@ class VlUnpacked; } while (false) #endif +//=================================================================== +/// Verilog wide packed bit container. +/// Similar to std::array, but lighter weight, only methods needed +/// by Verilator, to help compile time. +/// +/// A 'struct' as we want this to be an aggregate type that allows +/// static aggregate initialization. Consider data members private. +/// +/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32 +/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be +/// zero in memory, but during intermediate operations in the Verilated +/// internals is unpredictable. + +template +struct VlWide final { + static constexpr size_t Words = N_Words; + + // MEMBERS + // This should be the only data member, otherwise generated static initializers need updating + EData m_storage[N_Words]; // Contents of the packed array + + // CONSTRUCTORS + // Default constructors and destructor are used. Note however that C++20 requires that + // aggregate types do not have a user declared constructor, not even an explicitly defaulted + // one. + + // OPERATOR METHODS + // Default copy assignment operators are used. + bool operator==(const VlWide& that) const VL_PURE { + for (size_t i = 0; i < N_Words; ++i) { + if (m_storage[i] != that.m_storage[i]) return false; + } + return true; + } + bool operator!=(const VlWide& that) const VL_PURE { return !(*this == that); } + operator bool() const VL_PURE { + for (size_t i = 0; i < N_Words; ++i) { + if (m_storage[i]) return true; + } + return false; + } + EData& operator[](size_t index) VL_MT_SAFE { return m_storage[index]; } + const EData& operator[](size_t index) const VL_MT_SAFE { return m_storage[index]; } + + // METHODS + EData& at(size_t index) VL_MT_SAFE { return m_storage[index]; } + const EData& at(size_t index) const VL_MT_SAFE { return m_storage[index]; } + size_t size() const VL_PURE { return N_Words; } + EData* data() VL_MT_SAFE { return &m_storage[0]; } + const EData* data() const VL_MT_SAFE { return &m_storage[0]; } + inline bool operator<(const VlWide& rhs) const VL_PURE; +}; + +// Type trait to check if a type is VlWide +template +struct VlIsVlWide : public std::false_type {}; +template +struct VlIsVlWide> : public std::true_type {}; + +// Opaque handles to backing store of a VlWide, These are used to pass the +// backing store to runtime functions without having to template all the +// runtime functions on the VlWide template patameters. They created via +// implicit constructors from any VlWide, so a VlWide can be passed directly +// to functions that take one of these handles. The handles are 'opaque' in +// the sense that they are not primitive C++ types. They have necessary +// operators defined to allow them to behave like pointers to the extent +// it is necessary to implement the runtime functions. They are still only +// a single pointer underneath, so they will be passed in registers. + +// Read-Write VlWide handle +class WDataOutP final { + EData* m_datap; // The backing store - non const as the handle is assignable + + // Use WDataOutP::external() to create a handle from a raw pointer + explicit WDataOutP(EData* datap) VL_PURE : m_datap{datap} {} + +public: + // FACTORY METHODS + // Create a handle from a raw pointer - only to be used in verilated*.h + static WDataOutP external(EData* datap) VL_PURE { return WDataOutP{datap}; } + + // CONSTRUCTORS + // Implicit conversion from 'VlWide' + template + // cppcheck-suppress noExplicitConstructor + /* implicit */ WDataOutP(VlWide& vlWide) VL_PURE : m_datap{vlWide.data()} {} + WDataOutP(const WDataOutP& other) VL_PURE = default; + WDataOutP(WDataOutP&& other) VL_PURE = default; + WDataOutP& operator=(const WDataOutP& other) VL_PURE = default; + + // METHODS + EData* datap() const VL_PURE { return m_datap; } + operator bool() const VL_PURE { return m_datap; } + EData& operator[](size_t index) const VL_PURE { return m_datap[index]; } + WDataOutP operator+(size_t index) const VL_PURE { return WDataOutP(m_datap + index); } + WDataOutP operator+(int index) const VL_PURE { return WDataOutP(m_datap + index); } +}; +static_assert(sizeof(WDataOutP) == sizeof(EData*), "WDataOutP should be a single pointer"); + +// Read-Only VlWide handle +class WDataInP final { + const EData* m_datap; // The backing store - non const as the handle is assignable + + // Use WDataInP::external() to create a handle from a raw pointer + explicit WDataInP(const EData* datap) VL_PURE : m_datap{datap} {} + +public: + // FACTORY METHODS + // Create a handle from a raw pointer - only to be used in verilated*.h + static WDataInP external(const EData* datap) VL_PURE { return WDataInP{datap}; } + + // CONSTRUCTORS + // Implicit conversion from 'VlWide' + template + // cppcheck-suppress noExplicitConstructor + /* implicit */ WDataInP(VlWide& vlWide) VL_PURE : m_datap{vlWide.data()} {} + // Implicit conversion from 'const VlWide' + template + // cppcheck-suppress noExplicitConstructor + /* implicit */ WDataInP(const VlWide& vlWide) VL_PURE : m_datap{vlWide.data()} {} + // Implicit conversion from 'WDataOutP' + // cppcheck-suppress noExplicitConstructor + /* implicit */ WDataInP(const WDataOutP& owp) VL_PURE : m_datap{owp.datap()} {} + // Initialize with 'nullptr' + explicit WDataInP(std::nullptr_t) VL_PURE : m_datap{nullptr} {} + WDataInP(const WDataInP& other) VL_PURE = default; + WDataInP(WDataInP&& other) VL_PURE = default; + WDataInP& operator=(const WDataInP& other) VL_PURE = default; + + // METHODS + const EData* datap() const VL_PURE { return m_datap; } + operator bool() const VL_PURE { return m_datap; } + const EData& operator[](size_t index) const VL_PURE { return m_datap[index]; } + WDataInP operator+(size_t index) const VL_PURE { return WDataInP(m_datap + index); } + WDataInP operator+(int index) const VL_PURE { return WDataInP(m_datap + index); } +}; +static_assert(sizeof(WDataInP) == sizeof(EData*), "WDataInP should be a single pointer"); + +static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE; + +template +bool VlWide::operator<(const VlWide& rhs) const VL_PURE { + return _vl_cmp_w(N_Words, *this, rhs) < 0; +} + //=================================================================== // String formatters (required by below containers) +extern std::string VL_TO_STRING_W(int words, const WDataInP obj); + extern std::string VL_TO_STRING(CData lhs); extern std::string VL_TO_STRING(SData lhs); extern std::string VL_TO_STRING(IData lhs); extern std::string VL_TO_STRING(QData lhs); extern std::string VL_TO_STRING(double lhs); inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; } -extern std::string VL_TO_STRING_W(int words, const WDataInP obj); +template +inline std::string VL_TO_STRING(const VlWide& obj) { + return VL_TO_STRING_W(N_Words, obj); +} //========================================================================= // Declare net data types @@ -383,79 +533,6 @@ public: void print(QData addr, bool addrstamp, const void* valuep); }; -//=================================================================== -/// Verilog wide packed bit container. -/// Similar to std::array, but lighter weight, only methods needed -/// by Verilator, to help compile time. -/// -/// A 'struct' as we want this to be an aggregate type that allows -/// static aggregate initialization. Consider data members private. -/// -/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32 -/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be -/// zero in memory, but during intermediate operations in the Verilated -/// internals is unpredictable. - -static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE; - -template -struct VlWide; - -// Type trait to check if a type is VlWide -template -struct VlIsVlWide : public std::false_type {}; - -template -struct VlIsVlWide> : public std::true_type {}; - -template -struct VlWide final { - static constexpr size_t Words = N_Words; - - // MEMBERS - // This should be the only data member, otherwise generated static initializers need updating - EData m_storage[N_Words]; // Contents of the packed array - - // CONSTRUCTORS - // Default constructors and destructor are used. Note however that C++20 requires that - // aggregate types do not have a user declared constructor, not even an explicitly defaulted - // one. - - // OPERATOR METHODS - // Default copy assignment operators are used. - operator WDataOutP() VL_PURE { return &m_storage[0]; } // This also allows [] - operator WDataInP() const VL_PURE { return &m_storage[0]; } // This also allows [] - bool operator==(const VlWide& that) const VL_PURE { - for (size_t i = 0; i < N_Words; ++i) { - if (m_storage[i] != that.m_storage[i]) return false; - } - return true; - } - bool operator!=(const VlWide& that) const VL_PURE { return !(*this == that); } - - // METHODS - const EData& at(size_t index) const { return m_storage[index]; } - EData& at(size_t index) { return m_storage[index]; } - size_t size() const { return N_Words; } - WData* data() { return &m_storage[0]; } - const WData* data() const { return &m_storage[0]; } - bool operator<(const VlWide& rhs) const { - return _vl_cmp_w(N_Words, data(), rhs.data()) < 0; - } -}; - -// Convert a C array to std::array reference by pointer magic, without copy. -// Data type (second argument) is so the function template can automatically generate. -template -VlWide& VL_CVT_W_A(const WDataInP inp, const VlWide&) { - return *((VlWide*)inp); -} - -template -std::string VL_TO_STRING(const VlWide& obj) { - return VL_TO_STRING_W(N_Words, obj.data()); -} - template class VlClassRef; @@ -1356,8 +1433,8 @@ public: // METHODS // Raw access - WData* data() { return &m_storage[0]; } - const WData* data() const { return &m_storage[0]; } + T_Value* data() { return &m_storage[0]; } + const T_Value* data() const { return &m_storage[0]; } constexpr std::size_t size() const { return N_Depth; } diff --git a/include/verilated_vcd_c.cpp b/include/verilated_vcd_c.cpp index 1ee7443f0..c0ca9281b 100644 --- a/include/verilated_vcd_c.cpp +++ b/include/verilated_vcd_c.cpp @@ -668,17 +668,17 @@ void VerilatedVcdBuffer::emitQData(uint32_t code, QData newval, int bits) { } VL_ATTR_ALWINLINE -void VerilatedVcdBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) { +void VerilatedVcdBuffer::emitWData(uint32_t code, WDataInP newval, int bits) { int words = VL_WORDS_I(bits); char* wp = m_writep; *wp++ = 'b'; // Handle the most significant word const int bitsInMSW = VL_BITBIT_E(bits) ? VL_BITBIT_E(bits) : VL_EDATASIZE; - cvtEDataToStr(wp, newvalp[--words] << (VL_EDATASIZE - bitsInMSW)); + cvtEDataToStr(wp, newval[--words] << (VL_EDATASIZE - bitsInMSW)); wp += bitsInMSW; // Handle the remaining words while (words > 0) { - cvtEDataToStr(wp, newvalp[--words]); + cvtEDataToStr(wp, newval[--words]); wp += VL_EDATASIZE; } finishLine(code, wp); diff --git a/include/verilated_vcd_c.h b/include/verilated_vcd_c.h index f71e2513c..931035bed 100644 --- a/include/verilated_vcd_c.h +++ b/include/verilated_vcd_c.h @@ -251,7 +251,7 @@ class VerilatedVcdBuffer VL_NOT_FINAL { VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits); VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits); VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits); - VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits); + VL_ATTR_ALWINLINE void emitWData(uint32_t code, WDataInP newval, int bits); VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval); }; diff --git a/include/verilatedos.h b/include/verilatedos.h index b93909309..03cdd8bdb 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -438,8 +438,8 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read() // Constants for VL_SFORMATF; see V3Number.h VFormatAttr // Character codes are upper case so harder to confuse with format %codes. // (...) indicates what is passed as arguments in emitted code -#define VL_VFORMATATTR_UNSIGNED '#' // (int widthMin, IData/WData/etc) Use standard format -#define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/WData/etc) Signed number; for %d showing sign +#define VL_VFORMATATTR_UNSIGNED '#' // (int widthMin, IData/VlWide/etc) Use standard format +#define VL_VFORMATATTR_SIGNED '~' // (int widthMin, IData/VlWide/etc) Signed number; for %d showing sign #define VL_VFORMATATTR_COMPLEX '!' // (std::string*); for non-POD; e.g. struct, requires %p typically #define VL_VFORMATATTR_DOUBLE 'D' // (double); promote %p to %f #define VL_VFORMATATTR_SCOPE 'M' // (char* name, char* scope); for scopes @@ -462,7 +462,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read() #define VL_SHORTSIZE 16 ///< Bits in a SData / short #define VL_IDATASIZE 32 ///< Bits in an IData / word #define VL_QUADSIZE 64 ///< Bits in a QData / quadword -#define VL_EDATASIZE 32 ///< Bits in an EData (WData entry) +#define VL_EDATASIZE 32 ///< Bits in an EData (VlWide entry) #define VL_EDATASIZE_LOG2 5 ///< log2(VL_EDATASIZE) #define VL_CACHE_LINE_BYTES 64 ///< Bytes in a cache line (for alignment) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index ed5a3da98..46d4ac746 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4873,7 +4873,7 @@ class AstWordSel final : public AstNodeSel { public: AstWordSel(FileLine* fl, AstNodeExpr* fromp, AstNodeExpr* bitp) : ASTGEN_SUPER_WordSel(fl, fromp, bitp) { - dtypeSetUInt32(); // Always used on WData arrays so returns edata size + dtypeSetUInt32(); // Always used on VlWide arrays so returns EData size } ASTGEN_MEMBERS_AstWordSel; void numberOperate(V3Number&, const V3Number&, const V3Number&) override { V3ERROR_NA; } diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index 97d8decc7..5b1d2444c 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -227,6 +227,7 @@ bool EmitCFunc::displayEmitHeader(AstNode* nodep) { puts(cvtToStr(dispp->lhsp()->widthMin())); putbs(","); iterateConst(dispp->lhsp()); + emitDatap(dispp->lhsp()); putbs(","); } else if (VN_IS(nodep, SFormatF)) { isStmt = false; @@ -285,6 +286,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstSFormatF* fmtp, // fmtp is nullp if (exprFormat) { UASSERT_OBJ(exprsp, nodep, "Missing format expression"); iterateConst(exprsp); + emitDatap(exprsp); exprsp = exprsp->nextp(); } else { ofp()->putsQuoted(vformat); @@ -430,15 +432,6 @@ void EmitCFunc::emitCvtPackStr(AstNode* nodep) { } } -void EmitCFunc::emitCvtWideArray(AstNode* nodep, AstNode* fromp) { - putnbs(nodep, "VL_CVT_W_A("); - iterateConst(nodep); - puts(", "); - iterateConst(fromp); - putbs(".atDefault()"); // Not accessed; only to get the proper type of values - puts(")"); -} - void EmitCFunc::emitConstant(AstConst* nodep) { // Put out constant set to the specified variable, or given variable in a string const V3Number& num = nodep->num(); @@ -536,18 +529,16 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing, // Returns string to do resetting, empty to do nothing (which caller should handle) if (AstAssocArrayDType* const adtypep = VN_CAST(dtypep, AssocArrayDType)) { // Access std::array as C array - const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n"; return pre + emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(), - depth + 1, suffix + ".atDefault()" + cvtarray, nullptr); + depth + 1, suffix + ".atDefault()", nullptr); } else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) { // Access std::array as C array - const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n"; return pre + emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(), - depth + 1, suffix + ".atDefault()" + cvtarray, nullptr); + depth + 1, suffix + ".atDefault()", nullptr); } else if (VN_IS(dtypep, CDType)) { return ""; // Constructor does it } else if (VN_IS(dtypep, ClassRefDType)) { @@ -555,19 +546,15 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing, } else if (VN_IS(dtypep, IfaceRefDType)) { return varNameProtected + suffix + " = nullptr;\n"; } else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) { - // Access std::array as C array - const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n"; return pre + emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(), - depth + 1, suffix + ".atDefault()" + cvtarray, nullptr); + depth + 1, suffix + ".atDefault()", nullptr); } else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) { - // Access std::array as C array - const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : ""); const string pre = constructing ? "" : varNameProtected + suffix + ".clear();\n"; return pre + emitVarResetRecurse(varp, constructing, varNameProtected, adtypep->subDTypep(), - depth + 1, suffix + ".atDefault()" + cvtarray, nullptr); + depth + 1, suffix + ".atDefault()", nullptr); } else if (VN_IS(dtypep, SampleQueueDType)) { return ""; } else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 3c05a6055..81552f724 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -187,7 +187,6 @@ public: void emitDereference(AstNode* nodep, const string& pointer); std::string dereferenceString(const std::string& pointer) const; void emitCvtPackStr(AstNode* nodep); - void emitCvtWideArray(AstNode* nodep, AstNode* fromp); void emitConstant(AstConst* nodep); void emitConstantString(const AstConst* nodep); void emitSetVarConstant(const string& assignString, AstConst* constp); @@ -741,10 +740,7 @@ public: int argNum = 0; for (AstNode* subnodep = nodep->pinsp(); subnodep; subnodep = subnodep->nextp()) { if (comma) puts(", "); - // handle wide arguments to the queues - if (VN_IS(nodep->fromp()->dtypep(), QueueDType) && subnodep->dtypep()->isWide()) { - emitCvtWideArray(subnodep, nodep->fromp()); - } else if (nodep->method() == VCMethod::RANDOMIZER_HARD && argNum == 1) { + if (nodep->method() == VCMethod::RANDOMIZER_HARD && argNum == 1) { // For RANDOMIZER_HARD's filename argument (2nd arg after constraint), // apply protect() similar to VL_STOP to handle --protected flag if (const AstCExpr* const cexprp = VN_CAST(subnodep, CExpr)) { diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 7947bb070..2ab7cffe7 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -455,31 +455,29 @@ class EmitCHeader final : public EmitCConstInit { puts("}\n"); } else if (VN_IS(dtypep, NodeUOrStructDType)) { const std::string tmp = m_names.get("__Vtmp"); - const std::string suffixName = dtypep->isWide() ? tmp + ".data()" : tmp; if (getfunc) { // Emit `get` func; // auto __tmp = field.get(); puts("auto " + tmp + " = " + fieldname + ".get();\n"); // VL_ASSIGNSEL_XX(rbits, obits, lsb, lhsdata, rhsdata); - emitVlAssign(parentDtypep, dtypep, offset, retOrArg, suffixName, getfunc); + emitVlAssign(parentDtypep, dtypep, offset, retOrArg, tmp, getfunc); } else { // Emit `set` func const std::string tmptype = AstCDType::typeToHold(dtypep->width()); // type tmp; puts(tmptype + " " + tmp + ";\n"); // VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, roffset); - emitVlAssign(dtypep, parentDtypep, offset, suffixName, retOrArg, getfunc); + emitVlAssign(dtypep, parentDtypep, offset, tmp, retOrArg, getfunc); // field.set(__tmp); puts(fieldname + ".set(" + tmp + ");\n"); } } else { UASSERT_OBJ(VN_IS(dtypep, EnumDType) || VN_IS(dtypep, BasicDType), dtypep, "Unsupported type in packed struct or union"); - const std::string suffixName = dtypep->isWide() ? fieldname + ".data()" : fieldname; if (getfunc) { // Emit `get` func; // VL_ASSIGNSEL_XX(rbits, obits, lsb, lhsdata, rhsdata); - emitVlAssign(parentDtypep, dtypep, offset, retOrArg, suffixName, getfunc); + emitVlAssign(parentDtypep, dtypep, offset, retOrArg, fieldname, getfunc); } else { // Emit `set` func // VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, roffset); - emitVlAssign(dtypep, parentDtypep, offset, suffixName, retOrArg, getfunc); + emitVlAssign(dtypep, parentDtypep, offset, fieldname, retOrArg, getfunc); } } } @@ -501,7 +499,6 @@ class EmitCHeader final : public EmitCConstInit { } const std::string retArgName = m_names.get("__v"); - const std::string suffixName = sdtypep->isWide() ? retArgName + ".data()" : retArgName; const std::string retArgType = AstCDType::typeToHold(sdtypep->width()); // Emit `get` member function @@ -510,12 +507,12 @@ class EmitCHeader final : public EmitCConstInit { if (VN_IS(sdtypep, StructDType)) { for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) { emitPackedMember(sdtypep, itemp->dtypep(), itemp->nameProtect(), - std::to_string(itemp->lsb()), /*getfunc=*/true, suffixName); + std::to_string(itemp->lsb()), /*getfunc=*/true, retArgName); } } else { // We only need to fill the widest field of union emitPackedMember(sdtypep, witemp->dtypep(), witemp->nameProtect(), - std::to_string(witemp->lsb()), /*getfunc=*/true, suffixName); + std::to_string(witemp->lsb()), /*getfunc=*/true, retArgName); } puts("return " + retArgName + ";\n"); puts("}\n"); @@ -525,12 +522,12 @@ class EmitCHeader final : public EmitCConstInit { if (VN_IS(sdtypep, StructDType)) { for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) { emitPackedMember(sdtypep, itemp->dtypep(), itemp->nameProtect(), - std::to_string(itemp->lsb()), /*getfunc=*/false, suffixName); + std::to_string(itemp->lsb()), /*getfunc=*/false, retArgName); } } else { // We only need to fill the widest field of union emitPackedMember(sdtypep, witemp->dtypep(), witemp->nameProtect(), - std::to_string(witemp->lsb()), /*getfunc=*/false, suffixName); + std::to_string(witemp->lsb()), /*getfunc=*/false, retArgName); } puts("}\n"); diff --git a/test_regress/t/t_func_public.v b/test_regress/t/t_func_public.v index 059126c6f..301cd4e47 100644 --- a/test_regress/t/t_func_public.v +++ b/test_regress/t/t_func_public.v @@ -120,7 +120,7 @@ module tpub ( if (24'h11bca != got_long) $stop; $c("{ uint64_t qq; this->publicGetQuad(qq); this->got_quad=qq; }"); if (60'haaaa_bbbb_cccc != got_quad) $stop; - $c("{ WData gw[3]; this->publicGetWide(gw); VL_ASSIGN_W(72,this->got_wide,gw); }"); + $c("{ EData gw[3]; this->publicGetWide(gw); VL_ASSIGN_W(72,this->got_wide,WDataInP::external(gw)); }"); if (72'hac_abca_aaaa_bbbb_1234 != got_wide) $stop; //Below doesn't work, because we're calling it inside the loop that sets var_flop // if (12'h321 != var_flop) $stop; diff --git a/test_regress/t/t_var_sc_bv.cpp b/test_regress/t/t_var_sc_bv.cpp index 116ac6aa5..fe9fc213b 100644 --- a/test_regress/t/t_var_sc_bv.cpp +++ b/test_regress/t/t_var_sc_bv.cpp @@ -180,35 +180,35 @@ int main() //////////////////////////////// VL_ASSIGN_WSB(29, out_var, o_29_s); - compareWls(29, input_var.data(), out_var.data()); + compareWls(29, input_var, out_var); VL_ASSIGN_WSB(30, out_var, o_30_s); - compareWls(30, input_var.data(), out_var.data()); + compareWls(30, input_var, out_var); VL_ASSIGN_WSB(31, out_var, o_31_s); - compareWls(31, input_var.data(), out_var.data()); + compareWls(31, input_var, out_var); VL_ASSIGN_WSB(32, out_var, o_32_s); - compareWls(32, input_var.data(), out_var.data()); + compareWls(32, input_var, out_var); VL_ASSIGN_WSB(59, out_var, o_59_s); - compareWls(59, input_var.data(), out_var.data()); + compareWls(59, input_var, out_var); VL_ASSIGN_WSB(60, out_var, o_60_s); - compareWls(60, input_var.data(), out_var.data()); + compareWls(60, input_var, out_var); VL_ASSIGN_WSB(62, out_var, o_62_s); - compareWls(62, input_var.data(), out_var.data()); + compareWls(62, input_var, out_var); VL_ASSIGN_WSB(64, out_var, o_64_s); - compareWls(64, input_var.data(), out_var.data()); + compareWls(64, input_var, out_var); VL_ASSIGN_WSB(119, out_var, o_119_s); - compareWls(119, input_var.data(), out_var.data()); + compareWls(119, input_var, out_var); VL_ASSIGN_WSB(120, out_var, o_120_s); - compareWls(120, input_var.data(), out_var.data()); + compareWls(120, input_var, out_var); VL_ASSIGN_WSB(121, out_var, o_121_s); - compareWls(121, input_var.data(), out_var.data()); + compareWls(121, input_var, out_var); VL_ASSIGN_WSB(127, out_var, o_127_s); - compareWls(127, input_var.data(), out_var.data()); + compareWls(127, input_var, out_var); VL_ASSIGN_WSB(128, out_var, o_128_s); - compareWls(128, input_var.data(), out_var.data()); + compareWls(128, input_var, out_var); VL_ASSIGN_WSB(255, out_var, o_255_s); - compareWls(255, input_var.data(), out_var.data()); + compareWls(255, input_var, out_var); VL_ASSIGN_WSB(256, out_var, o_256_s); - compareWls(256, input_var.data(), out_var.data()); + compareWls(256, input_var, out_var); tb->final(); VL_DO_DANGLING(delete tb, tb);