Fix erroneous implicit conversions of VlWide (#7642)

Change WDataInP/WDataOutP to be opaque handles types instead of aliases
to raw pointers. This subsequently eliminates needing an implicit cast
operator in VlWide, which is replaced with implicit constructors of
WDataInP/WDataOutP that can create a handle from a VlWide. This
eliminates some unsafe conversions that the previous implicit cast
operator unintentionally enabled (e.g. #7618). It also eliminates
having to insert ".data()" in various places int he generated code, which
simplifies internals (the only place ".data()" should be needed is in
calls to variadic functions where the expected type of the argument is
not WDataInP/WDataOutP).

The handles otherwise behave like pointers, implementing the minimal
amount of operators required to code the runtime. The handle is still
only a single pointer, and will be passed in registers as before, so
this patch should be performance neutral.

As part of this removed WData, which used to be an alias for EData.
All uses are now either EData*, WDataInP, WDataOutP, or VlWide directly.
This commit is contained in:
Geza Lore 2026-05-22 20:05:08 +01:00 committed by GitHub
parent ef72b2fabb
commit c99aa8ede5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 328 additions and 272 deletions

View File

@ -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<VL_MULS_MAX_WORDS + 1> 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<EData> bcd(VL_WORDS_I(maxdecwidth));
VL_ZERO_W(maxdecwidth, bcd.data());
WDataOutP bcdp = WDataOutP::external(bcd.data());
VL_ZERO_W(maxdecwidth, bcdp);
std::vector<EData> tmp(VL_WORDS_I(maxdecwidth));
std::vector<EData> 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<EData> 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<EData> 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<VL_WQ_WORDS_E> qowp;
VL_SET_WQ(qowp, 0ULL);
WDataOutP owp = (obits <= 64) ? qowp : static_cast<WDataOutP>(thingp);
WDataOutP owp = WDataOutP::external((obits <= 64) ? qowp.data()
: static_cast<EData*>(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<char*>(owp);
char* const out = reinterpret_cast<char*>(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<char*>(owp);
char* out = reinterpret_cast<char*>(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<int>(ld.length() * 8), nullptr, ld, format, argc, ap);
= _vl_vsscanf(nullptr, static_cast<int>(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<QData>(c) << static_cast<QData>(shift)) & VL_MASK_Q(width));
} else {
WDataOutP datap = &(reinterpret_cast<WDataOutP>(memp))[entry * VL_WORDS_I(width)];
const WDataOutP datap = WDataOutP::external(
&(reinterpret_cast<EData*>(memp))[entry * VL_WORDS_I(width)]);
if (shift == start_shift) VL_ZERO_W(width, datap);
datap[VL_BITWORD_E(shift)] |= (static_cast<EData>(c) << VL_BITBIT_E(shift));
}
@ -2545,7 +2556,7 @@ void VlReadMem::setData(void* valuep, const std::string& rhs) {
*datap = ((*datap << static_cast<QData>(shift)) + static_cast<QData>(value))
& VL_MASK_Q(m_bits);
} else {
WDataOutP datap = reinterpret_cast<WDataOutP>(valuep);
const WDataOutP datap = WDataOutP::external(reinterpret_cast<EData*>(valuep));
if (!innum) VL_ZERO_W(m_bits, datap);
_vl_shiftl_inplace_w(m_bits, datap, static_cast<IData>(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<WDataInP>(valuep);
const WDataInP datap = WDataInP::external(reinterpret_cast<const EData*>(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<QData*>(memp))[entry];
rmem.setData(datap, value);
} else {
WDataOutP datap
= &(reinterpret_cast<WDataOutP>(memp))[entry * VL_WORDS_I(bits)];
EData* const datap
= &(reinterpret_cast<EData*>(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<const QData*>(memp))[row_offset];
wmem.print(addr, false, datap);
} else {
const WDataInP memDatap = reinterpret_cast<WDataInP>(memp);
const WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)];
const EData* const datap
= &(reinterpret_cast<const EData*>(memp))[row_offset * VL_WORDS_I(bits)];
wmem.print(addr, false, datap);
}
}

View File

@ -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
};

View File

@ -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<WDataInP>(datap));
WDataInP wdatap = WDataInP::external(reinterpret_cast<EData*>(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<WDataInP>(datap));
WDataInP wdatap = WDataInP::external(reinterpret_cast<EData*>(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<IData*>(datap)) = s[0]; return;
case VLVT_UINT64: *(reinterpret_cast<QData*>(datap)) = VL_SET_QII(s[1], s[0]); break;
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
WDataOutP wdatap = WDataOutP::external(reinterpret_cast<EData*>(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<IData*>(datap)) = s[0].aval; return;
case VLVT_UINT64: *(reinterpret_cast<QData*>(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break;
case VLVT_WDATA: {
WDataOutP wdatap = (reinterpret_cast<WDataOutP>(datap));
WDataOutP wdatap = WDataOutP::external(reinterpret_cast<EData*>(datap));
for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i].aval;
return;
}

View File

@ -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

View File

@ -145,7 +145,7 @@ private:
return (rhsVal >> rhsLsb) & mask;
}
const EData* const rhswp = static_cast<const EData*>(entry.m_rhsDatap);
WDataInP rhswp = WDataInP::external(static_cast<const EData*>(entry.m_rhsDatap));
return VL_SEL_QWII(rhsWidth, rhswp, rhsLsb, width) & mask;
}
@ -198,7 +198,7 @@ private:
template <typename T>
typename std::enable_if<VlIsVlWide<T>::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<EData*>(&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<WDataOutP>(&result), lsb, width);
readSel(lbits, valp, WDataOutP::external(reinterpret_cast<EData*>(&result)), lsb, width);
result &= VL_MASK_Q(width);
return result;
}
@ -277,9 +277,8 @@ public:
template <std::size_t N_Words>
VlWide<N_Words> readSelW(int lbits, WDataInP valp, int lsb, int width) const {
VlWide<N_Words> 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;
}

View File

@ -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

View File

@ -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);
};

View File

@ -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<IData>(data); \
(owp)[1] = static_cast<IData>((data) >> VL_EDATASIZE); \
} while (false)
#define VL_SET_WI(owp, data) \
do { \
(owp)[0] = static_cast<IData>(data); \
(owp)[1] = 0; \
} while (false)
#define VL_SET_QW(lwp) \
((static_cast<QData>((lwp)[0])) \
| (static_cast<QData>((lwp)[1]) << (static_cast<QData>(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<IData>(data);
owp[1] = static_cast<IData>(data >> VL_EDATASIZE);
return owp;
}
static inline WDataOutP VL_SET_WI(WDataOutP owp, IData data) VL_PURE {
owp[0] = static_cast<IData>(data);
owp[1] = 0;
return owp;
}
static inline QData VL_SET_QW(WDataInP lwp) VL_PURE {
return (static_cast<QData>(lwp[1]) << VL_EDATASIZE) | static_cast<QData>(lwp[0]);
}
#define VL_SET_QII(ld, rd) ((static_cast<QData>(ld) << 32ULL) | static_cast<QData>(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<WDataOutP>(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<WDataOutP>(std::memset(owp, 0xff, words * sizeof(EData)));
for (size_t i = 0; i < words; ++i) owp[i] = ~static_cast<EData>(0);
return owp;
}
VL_ATTR_ALWINLINE
static WDataOutP VL_MEMCPY_W(WDataOutP owp, WDataInP const iwp, int words) VL_MT_SAFE {
return static_cast<WDataOutP>(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<IData, EData>::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<VL_MULS_MAX_WORDS> lwstore;
VlWide<VL_MULS_MAX_WORDS> 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<VL_MULS_MAX_WORDS> lwstore;
VlWide<VL_MULS_MAX_WORDS> 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<VL_MULS_MAX_WORDS> 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<VL_MULS_MAX_WORDS> lwstore;
VlWide<VL_MULS_MAX_WORDS> 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<VL_MULS_MAX_WORDS> 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<VlWide<N_Words>>& queue, int dstElem
VlWide<N_Words>& 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<size_t>(wordoff + (lsb != 0)), upperbits + obits - 1,
upperbits, rbits);
}
//======================================================================

View File

@ -349,7 +349,7 @@ void VlRandomVar::emitConcreteValue(std::ostream& s) const {
} else if (w <= VL_QUADSIZE) {
bit = (*static_cast<const QData*>(dp) >> i) & 1;
} else {
const EData* const wp = static_cast<const EData*>(dp);
const WDataInP wp = WDataInP::external(static_cast<const EData*>(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<WDataOutP>(datap(nidx));
if (obits > VL_QUADSIZE) owp = WDataOutP::external(reinterpret_cast<EData*>(datap(nidx)));
if (!parseSMTNum(obits, owp, val)) return false;
if (obits <= VL_BYTESIZE) {

View File

@ -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

View File

@ -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);
};

View File

@ -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;
}
}

View File

@ -602,11 +602,11 @@ void VerilatedTraceBuffer<VL_BUF_T>::fullQData(uint32_t* oldp, QData newval, int
}
template <>
void VerilatedTraceBuffer<VL_BUF_T>::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
void VerilatedTraceBuffer<VL_BUF_T>::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 <>

View File

@ -64,16 +64,166 @@ class VlUnpacked;
} while (false)
#endif
//===================================================================
/// Verilog wide packed bit container.
/// Similar to std::array<EData, N>, 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 <std::size_t N_Words>
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<N_Words>& 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<N_Words>& 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<N_Words>& rhs) const VL_PURE;
};
// Type trait to check if a type is VlWide
template <typename>
struct VlIsVlWide : public std::false_type {};
template <std::size_t N_Words>
struct VlIsVlWide<VlWide<N_Words>> : 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 <std::size_t N_Words>
// cppcheck-suppress noExplicitConstructor
/* implicit */ WDataOutP(VlWide<N_Words>& 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 <std::size_t N_Words>
// cppcheck-suppress noExplicitConstructor
/* implicit */ WDataInP(VlWide<N_Words>& vlWide) VL_PURE : m_datap{vlWide.data()} {}
// Implicit conversion from 'const VlWide'
template <std::size_t N_Words>
// cppcheck-suppress noExplicitConstructor
/* implicit */ WDataInP(const VlWide<N_Words>& 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 <std::size_t N_Words>
bool VlWide<N_Words>::operator<(const VlWide<N_Words>& 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 <std::size_t N_Words>
inline std::string VL_TO_STRING(const VlWide<N_Words>& 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<WData, N>, 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 <std::size_t N_Words>
struct VlWide;
// Type trait to check if a type is VlWide
template <typename>
struct VlIsVlWide : public std::false_type {};
template <std::size_t N_Words>
struct VlIsVlWide<VlWide<N_Words>> : public std::true_type {};
template <std::size_t N_Words>
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<N_Words>& 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<N_Words>& 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<N_Words>& 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 <std::size_t N_Words>
VlWide<N_Words>& VL_CVT_W_A(const WDataInP inp, const VlWide<N_Words>&) {
return *((VlWide<N_Words>*)inp);
}
template <std::size_t N_Words>
std::string VL_TO_STRING(const VlWide<N_Words>& obj) {
return VL_TO_STRING_W(N_Words, obj.data());
}
template <typename T_Class>
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; }

View File

@ -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);

View File

@ -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);
};

View File

@ -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)

View File

@ -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; }

View File

@ -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)) {

View File

@ -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)) {

View File

@ -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");

View File

@ -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;

View File

@ -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);