Support streaming on queues (#7597)

This commit is contained in:
Benjamin Collier 2026-05-20 17:14:02 -06:00 committed by GitHub
parent c5798f902b
commit 69b3c5f6d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 1142 additions and 24 deletions

View File

@ -34,6 +34,7 @@ Artur Bieniek
AUDIY
Aylon Chaim Porat
Bartłomiej Chmiel
Benjamin Collier
Brian Li
Cameron Kirk
Cameron Waite
@ -251,6 +252,7 @@ Ryszard Rozak
Samuel Riedel
Sean Cross
Sebastien Van Cauwenberghe
Secturion
Sergey Fedorov
Sergi Granell
Seth Pellegrino

View File

@ -779,7 +779,7 @@ static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP const lwp, IDa
EData r = 0;
IData wordLbits = 32;
for (int i = 0; i < words; ++i) {
if (i == words - 1) wordLbits = lbits % 32;
if (i == words - 1) wordLbits = VL_BITBIT_I(lbits);
r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2);
}
return r;
@ -904,6 +904,7 @@ static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP const lwp) V
// EMIT_RULE: VL_GTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits;
// EMIT_RULE: VL_LTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits;
#define VL_NEQ_W(words, lwp, rwp) (!VL_EQ_W(words, lwp, rwp))
#define VL_NEQ_R(words, q, rwp) (!VL_EQ_R(words, q, rwp))
#define VL_LT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) < 0)
#define VL_LTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) <= 0)
#define VL_GT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) > 0)
@ -916,6 +917,73 @@ static inline IData VL_EQ_W(int words, WDataInP const lwp, WDataInP const rwp) V
return (nequal == 0);
}
template <std::size_t N_Words>
static inline IData VL_EQ_W(int words, WDataInP const rwp,
const VlQueue<VlWide<N_Words>>& q) VL_PURE {
return VL_EQ_R(words, q, rwp);
}
template <typename T>
static inline IData VL_EQ_W(int words, WDataInP const rwp, VlQueue<T> q) VL_PURE {
return VL_EQ_R(words, q, rwp);
}
template <typename T>
static inline IData VL_EQ_R(int words, VlQueue<T> q, WDataInP const rwp) VL_PURE {
EData nequal = 0;
const int wordsInQ = q.size() * sizeof(T) / sizeof(IData) - 1;
if (wordsInQ + 1 != words) return false;
if (sizeof(T) == 1) {
IData temp = 0;
for (int i = 0; (i < wordsInQ + 1); ++i) {
temp |= static_cast<EData>(q.at((wordsInQ - i) * sizeof(IData) + 3));
temp |= static_cast<EData>(q.at((wordsInQ - i) * sizeof(IData) + 2)) << 8;
temp |= static_cast<EData>(q.at((wordsInQ - i) * sizeof(IData) + 1)) << 16;
temp |= static_cast<EData>(q.at((wordsInQ - i) * sizeof(IData))) << 24;
nequal |= (temp ^ rwp[i]);
temp = 0;
}
} else if (sizeof(T) == 2) {
IData temp = 0;
for (int i = 0; (i < wordsInQ + 1); ++i) {
temp |= q.at((wordsInQ - i) * sizeof(SData) + 1);
temp |= q.at((wordsInQ - i) * sizeof(SData)) << 16;
nequal |= (temp ^ rwp[i]);
temp = 0;
}
} else if (sizeof(T) == 4) {
for (int i = 0; (i < wordsInQ + 1); ++i) { nequal |= (q.at(wordsInQ - i) ^ rwp[i]); }
} else if (sizeof(T) == 8) {
QData temp = 0;
int qSize = q.size() - 1;
for (int i = 0; (i < qSize); i += 2) {
temp = q.at(qSize - i);
nequal |= (static_cast<QData>(q.at(qSize - i)) >> 32 ^ rwp[i + 1]);
temp = rwp[i + 1];
nequal |= (static_cast<QData>(q.at(qSize - i)) ^ rwp[i]);
temp = rwp[i];
}
}
return (nequal == 0);
}
template <std::size_t N_Words>
static inline IData VL_EQ_R(int words, const VlQueue<VlWide<N_Words>>& q,
WDataInP const rwp) VL_PURE {
EData nequal = 0;
const int wordsInQ = q.size() * N_Words;
if ((q.size() * N_Words) != words) { return false; }
int count = 0;
for (int qIndex = q.size() - 1; qIndex >= 0; qIndex--) {
for (int wordInElement = 0; wordInElement < N_Words; wordInElement++) {
nequal |= (q.at(qIndex).at(wordInElement) ^ rwp[count]);
count++;
}
}
return (nequal == 0);
}
// Internal usage
static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE {
for (int i = words - 1; i >= 0; --i) {
@ -1558,6 +1626,300 @@ static inline QData VL_STREAML_FAST_QQI(int lbits, QData ld, IData rd_log2) VL_P
return ret >> (VL_QUADSIZE - lbits);
}
template <typename T>
static inline void VL_STREAML_FAST_RQI(int lbits, VlQueue<T>& q, QData ld, IData rd_log2) VL_PURE {
const QData ret = VL_STREAML_FAST_QQI(lbits, ld, rd_log2);
q.clear();
const int numQData = 8 / sizeof(T);
const bool needsMask = sizeof(T) < 8;
for (int ii = numQData - 1; ii >= 0; ii--) {
if VL_CONSTEXPR_CXX17 (needsMask) {
VL_CONSTEXPR_CXX17 uint64_t mask = VL_MASK_Q(sizeof(T) * 8);
q.push_back(static_cast<T>(ret >> (ii * sizeof(T) * 8)) & mask);
} else {
q.push_back(static_cast<T>(ret));
}
}
}
template <std::size_t N_Words>
static inline void VL_STREAML_FAST_RQI(int lbits, VlQueue<VlWide<N_Words>>& q, QData ld,
IData rd_log2) VL_PURE {
const QData ret = VL_STREAML_FAST_QQI(lbits, ld, rd_log2);
q.clear();
VlWide<N_Words> value;
value[N_Words - 1] = static_cast<EData>(ret >> 32);
value[N_Words - 2] = static_cast<EData>(ret);
for (int i = N_Words - 3; i >= 0; i--) value[i] = 0;
q.push_back(value);
}
template <typename T>
static inline void VL_STREAMR_RII(int lbits, VlQueue<T>& q, IData ld, IData rd_log2) VL_PURE {
q.clear();
VL_CONSTEXPR_CXX17 int valueSize = sizeof(T);
if VL_CONSTEXPR_CXX17 (valueSize < 4) {
VL_CONSTEXPR_CXX17 int mask = VL_MASK_I(valueSize * 8);
// Push all bytes of the 32-bit integer, MSB first (Big-Endian)
VL_CONSTEXPR_CXX17 int qElementsPerWord = 4 / valueSize;
for (int i = 0; i < qElementsPerWord; i++) {
q.push_back(
static_cast<T>(((ld >> (qElementsPerWord - i - 1) * 8 * valueSize)) & mask));
}
} else {
q.push_back(static_cast<T>(ld));
}
}
template <std::size_t N_Words>
static inline void VL_STREAMR_RII(int lbits, VlQueue<VlWide<N_Words>>& q, IData ld,
IData rd_log2) VL_PURE {
q.clear();
VlWide<N_Words> value;
VL_SET_WI(value, ld);
q.push_back(value);
}
template <typename T>
static inline void VL_STREAMR_RQI(int lbits, VlQueue<T>& q, QData ld, IData rd_log2) VL_PURE {
q.clear(); // Empty the queue first
// If this is a queue of bytes (unsigned char)
if VL_CONSTEXPR_CXX17 (sizeof(T) == 1) {
// Push all 8 bytes of the 64-bit integer, MSB first (Big-Endian)
q.push_back(static_cast<T>((ld >> 56) & 0xFF));
q.push_back(static_cast<T>((ld >> 48) & 0xFF));
q.push_back(static_cast<T>((ld >> 40) & 0xFF));
q.push_back(static_cast<T>((ld >> 32) & 0xFF));
q.push_back(static_cast<T>((ld >> 24) & 0xFF));
q.push_back(static_cast<T>((ld >> 16) & 0xFF));
q.push_back(static_cast<T>((ld >> 8) & 0xFF));
q.push_back(static_cast<T>(ld & 0xFF));
} else {
const int numQData = 8 / sizeof(T);
for (int ii = numQData - 1; ii >= 0; ii--) {
q.push_back(static_cast<T>(ld >> (ii * sizeof(T) * 8)));
}
}
}
template <typename T>
static inline IData VL_STREAMR_IRI(int lbits, VlQueue<T>& q, IData rd_log2) VL_PURE {
IData value = 0; // Starts at 0. Out-of-range bits will remain 0.
const size_t len = q.size();
if VL_CONSTEXPR_CXX17 (sizeof(T) == 1) { // If it is a queue of bytes
if (len > 0) value |= static_cast<IData>(q.at(0)) << 24;
if (len > 1) value |= static_cast<IData>(q.at(1)) << 16;
if (len > 2) value |= static_cast<IData>(q.at(2)) << 8;
if (len > 3) value |= static_cast<IData>(q.at(3));
} else if VL_CONSTEXPR_CXX17 (sizeof(T) == 2) {
if (len > 0) value |= static_cast<IData>(q.at(0)) << 16;
if (len > 1) value |= static_cast<IData>(q.at(1));
} else if VL_CONSTEXPR_CXX17 (sizeof(T) == 8) {
if (len > 0) value = static_cast<IData>(q.at(0));
} else { // If it is a queue of larger types (e.g. ints)
VL_CONSTEXPR_CXX17 int shiftAmt = sizeof(T) > 4 ? 32 : 0;
if (len > 0) value = static_cast<IData>(q.at(0) >> shiftAmt);
}
return value;
}
template <typename T>
static inline IData VL_STREAMR_QRI(int lbits, VlQueue<T>& q, IData rd_log2) VL_PURE {
QData value = 0;
const size_t len = q.size();
if VL_CONSTEXPR_CXX17 (sizeof(T) == 1) {
// Must cast to QData BEFORE shifting to prevent 32-bit overflow!
if (len > 0) value |= static_cast<QData>(q.at(0)) << 56;
if (len > 1) value |= static_cast<QData>(q.at(1)) << 48;
if (len > 2) value |= static_cast<QData>(q.at(2)) << 40;
if (len > 3) value |= static_cast<QData>(q.at(3)) << 32;
if (len > 4) value |= static_cast<QData>(q.at(4)) << 24;
if (len > 5) value |= static_cast<QData>(q.at(5)) << 16;
if (len > 6) value |= static_cast<QData>(q.at(6)) << 8;
if (len > 7) value |= static_cast<QData>(q.at(7));
} else {
// If it is a queue of larger types (e.g. ints/longs)
if (len > 0) value = static_cast<QData>(q.at(0));
}
return value;
}
template <std::size_t N_Words>
static inline void VL_STREAMR_RQI(int lbits, VlQueue<VlWide<N_Words>>& q, QData ld,
IData rd_log2) VL_PURE {
q.clear(); // Empty the queue first
VlWide<N_Words> value;
VL_SET_WQ(value, ld);
q.push_back(value);
}
template <typename T>
static inline void VL_STREAMR_RWI(int lbits, VlQueue<T>& q, WDataInP const lwp,
IData rd_log2) VL_PURE {
q.clear(); // Empty the queue first
const int numWords = VL_BITWORD_E(lbits);
QData qdataValue = 0;
for (int word = numWords - 1; word >= 0; word--) {
VL_CONSTEXPR_CXX17 int valueSize = sizeof(T);
if VL_CONSTEXPR_CXX17 (valueSize < 4) {
VL_CONSTEXPR_CXX17 int mask = VL_MASK_I(valueSize * 8);
// Push all bytes of the 32-bit integer, MSB first (Big-Endian)
VL_CONSTEXPR_CXX17 int qElementsPerWord = 4 / valueSize;
for (int i = 0; i < qElementsPerWord; i++) {
q.push_back(static_cast<T>(
((lwp[word] >> (qElementsPerWord - i - 1) * 8 * valueSize)) & mask));
}
} else if VL_CONSTEXPR_CXX17 (sizeof(T) == 8) {
const int shiftAmt = (word & 0x1) << 5;
qdataValue |= static_cast<QData>(lwp[word]) << shiftAmt;
if ((word & 0x1) == 0) {
q.push_back(qdataValue);
qdataValue = 0;
}
} else {
q.push_back(static_cast<T>(lwp[word]));
}
}
}
template <std::size_t N_Words>
static inline void VL_STREAMR_RWI(int lbits, VlQueue<VlWide<N_Words>>& q, WDataInP const lwp,
IData rd_log2) VL_PURE {
q.clear(); // Empty the queue first
const int numWords = VL_BITWORD_E(lbits);
VlWide<N_Words> value;
for (int ii = 0; ii < N_Words; ii++) { value.at(ii) = 0; }
for (int word = numWords - 1; word >= 0; word--) {
value.at(word) = lwp[word];
if ((word % N_Words) == 0) { q.push_back(value); }
}
}
template <typename T>
static inline VlQueue<T> VL_STREAMR_RRI(int lbits, const VlQueue<T> q, IData rd) VL_MT_SAFE {
return q;
}
static inline VlQueue<std::string> VL_STREAMR_NRI(int lbits, const VlQueue<std::string> q,
IData rd) VL_MT_SAFE {
return q;
}
template <typename T_Value, typename T_Other>
static inline void VL_STREAMR_RRI(int lbits, VlQueue<T_Value>& to_q,
const VlQueue<T_Other>& from_q, IData rd) VL_MT_SAFE {
to_q.clear();
VL_CONSTEXPR_CXX17 size_t otherSize = sizeof(T_Other);
VL_CONSTEXPR_CXX17 size_t sizeOfThis = sizeof(T_Value);
T_Value temp = 0;
if (otherSize > sizeOfThis) {
for (auto val : from_q) {
for (int ii = otherSize / sizeOfThis - 1; ii >= 0; ii--) {
temp = (static_cast<T_Value>(val >> (ii * 8 * sizeOfThis)));
to_q.push_back(temp);
}
}
} else {
// How many of the other element fits in this element.
size_t otherInElement = sizeOfThis / otherSize - 1;
for (auto val : from_q) {
// Shift the element into the correct position and merge
temp |= (static_cast<T_Value>(val) << (otherInElement * 8 * otherSize));
otherInElement--;
if (otherInElement == -1) {
to_q.push_back(temp);
temp = 0;
otherInElement = sizeOfThis - 1;
}
}
// Push any remaining leftover elements (upper bits will remain zero-padded)
if (otherInElement < sizeOfThis - 1) { to_q.push_back(temp); }
}
}
template <typename T_Other, std::size_t N_Words>
static inline void VL_STREAMR_RRI(int lbits, VlQueue<VlWide<N_Words>>& to_q,
const VlQueue<T_Other>& from_q, IData rd) VL_MT_SAFE {
to_q.clear();
VL_CONSTEXPR_CXX17 size_t otherSize = sizeof(T_Other);
VL_CONSTEXPR_CXX17 size_t sizeOfThis = 4 * N_Words;
VL_CONSTEXPR_CXX17 int numOtherInWord = 4 / otherSize;
VlWide<N_Words> temp;
for (int ii = 0; ii < N_Words; ii++) { temp.at(ii) = 0; }
if VL_CONSTEXPR_CXX17 (numOtherInWord > 0) {
size_t elementCount = sizeOfThis - 1;
for (auto val : from_q) {
temp.at((elementCount / numOtherInWord) % N_Words)
|= (static_cast<EData>(val) << (elementCount * 8 * otherSize));
elementCount--;
// If we've collected enough elements for the target type, push and reset
if (elementCount == -1) {
to_q.push_back(temp);
for (int ii = 0; ii < N_Words; ii++) { temp.at(ii) = 0; }
elementCount = sizeOfThis - 1;
}
}
// Push any remaining leftover elements (upper bits will remain zero-padded)
if (elementCount < sizeOfThis - 1) { to_q.push_back(temp); }
} else { //QData
size_t wordCount = N_Words - 1;
for (auto val : from_q) {
temp.at(wordCount % N_Words) = (static_cast<EData>(static_cast<QData>(val) >> 32));
wordCount--;
if (wordCount == -1) {
to_q.push_back(temp);
for (int ii = 0; ii < N_Words; ii++) { temp.at(ii) = 0; }
wordCount = N_Words - 1;
}
temp.at(wordCount % N_Words) = (static_cast<EData>(val));
wordCount--;
if (wordCount == -1) {
to_q.push_back(temp);
for (int ii = 0; ii < N_Words; ii++) { temp.at(ii) = 0; }
wordCount = N_Words - 1;
}
}
// Push any remaining leftover elements (upper bits will remain zero-padded)
if (wordCount < N_Words - 1) { to_q.push_back(temp); }
}
}
template <typename T_Value, std::size_t N_Words>
static inline void VL_STREAMR_RRI(int lbits, VlQueue<T_Value>& to_q,
const VlQueue<VlWide<N_Words>>& from_q, IData rd) VL_MT_SAFE {
to_q.clear();
VL_CONSTEXPR_CXX17 size_t otherSize = 4 * N_Words;
VL_CONSTEXPR_CXX17 size_t sizeOfThis = sizeof(T_Value);
T_Value temp = 0;
for (auto val : from_q) {
if VL_CONSTEXPR_CXX17 (sizeof(T_Value) == 8) {
// iterate backwards because queues are msb first
for (int wordIndex = N_Words - 1; wordIndex >= 0; wordIndex -= 2) {
temp |= (static_cast<T_Value>(val.at(wordIndex)) << 32);
if (wordIndex - 1 >= 0) { temp |= (static_cast<T_Value>(val.at(wordIndex - 1))); }
to_q.push_back(temp);
temp = 0;
}
} else {
//iterate backwards because queues are msb first
for (int wordIndex = N_Words - 1; wordIndex >= 0; wordIndex--) {
for (int elemInWord = sizeof(EData) / sizeOfThis - 1; elemInWord >= 0;
elemInWord--) {
temp
= (static_cast<T_Value>(val.at(wordIndex) >> elemInWord * 8 * sizeOfThis));
to_q.push_back(temp);
}
}
}
}
}
// Regular "slow" streaming operators
static inline IData VL_STREAML_III(int lbits, IData ld, IData rd) VL_PURE {
IData ret = 0;
@ -1571,6 +1933,112 @@ static inline IData VL_STREAML_III(int lbits, IData ld, IData rd) VL_PURE {
return ret;
}
template <typename T>
static inline VlQueue<T> VL_STREAML_RRI(int lbitsIn, const VlQueue<T> q, IData rd) VL_MT_SAFE {
// TODO this function needs to have a temp variable made in verilator and passed in.
// dynamicly make our "temp variable"
// lbitsIn is always 0
VlQueue<T> out_queue;
const int lbits = q.size() * 8 * sizeof(T);
out_queue.renew(q.size());
VL_CONSTEXPR_CXX17 unsigned int moduloMask = 8 * sizeof(T) - 1;
const int ssize = (rd < static_cast<IData>(lbits)) ? rd : (static_cast<IData>(lbits));
for (int istart = 0; istart < lbits; istart += rd) {
int ostart = lbits - rd - istart;
ostart = ostart > 0 ? ostart : 0;
for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) {
const int qIndex = (ostart + sbit) / (8 * sizeof(T));
const int shiftLeft = (istart + sbit) & moduloMask;
const int shiftRight = ((ostart + sbit) & moduloMask);
const T bit = ((q.at(qIndex)) >> shiftRight & 1) << shiftLeft;
const int writeIndx = (istart + sbit) / (8 * sizeof(T));
out_queue.atWrite(writeIndx) |= bit;
}
}
return out_queue;
}
template <std::size_t N_Words>
static inline VlQueue<VlWide<N_Words>>
VL_STREAML_RRI(int lbitsIn, const VlQueue<VlWide<N_Words>> q, IData rd) VL_MT_SAFE {
// TODO this function needs to have a temp variable.
// dynamicly make our "temp variable"
// lbitsIn is always zero
const int lbits = q.size() * 8 * sizeof(IData) * N_Words;
VL_CONSTEXPR_CXX17 int sizeOfElement = 8 * sizeof(IData) * N_Words;
VlQueue<VlWide<N_Words>> out_queue;
out_queue.renew(q.size());
VL_CONSTEXPR_CXX17 unsigned int moduloMask = sizeOfElement - 1;
const int ssize = (rd < static_cast<IData>(lbits)) ? rd : (static_cast<IData>(lbits));
for (int istart = 0; istart < lbits; istart += rd) {
int ostart = lbits - rd - istart;
ostart = ostart > 0 ? ostart : 0;
for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) {
const int qIndex = (ostart + sbit) / (sizeOfElement);
const int shiftLeftTotal = (istart + sbit) & moduloMask;
const int shiftRightTotal = ((ostart + sbit) & moduloMask);
const int shiftRight = VL_MASK_I(shiftRightTotal);
const int wordIn = VL_BITWORD_E(shiftRightTotal);
const int shiftLeft = VL_MASK_I(shiftLeftTotal);
const int wordOut = VL_BITWORD_E(shiftLeftTotal);
const EData bit = ((q.at(qIndex).at(wordIn)) >> shiftRight & 1) << shiftLeft;
const int writeIndx = (istart + sbit) / (sizeOfElement);
out_queue.atWrite(writeIndx).at(wordOut) |= bit;
}
}
return out_queue;
}
template <typename T>
static inline void VL_STREAML_RII(int lbits, int queueBits, VlQueue<T>& q, IData ld,
IData rd) VL_MT_SAFE {
IData ret = 0;
if (lbits < queueBits) { lbits = queueBits; }
// Slice size should never exceed the lhs width
const IData mask = VL_MASK_I(rd);
for (int istart = 0; istart < lbits; istart += rd) {
int ostart = lbits - rd - istart;
ostart = ostart > 0 ? ostart : 0;
ret |= ((ld >> istart) & mask) << ostart;
}
q.clear();
VL_CONSTEXPR_CXX17 int numBitsPerQElem = sizeof(T) * 8;
const bool needsMask = sizeof(T) < 4;
VL_CONSTEXPR_CXX17 int elementMask = VL_MASK_I(numBitsPerQElem * needsMask);
VL_CONSTEXPR_CXX17 int qElementPerWord = numBitsPerQElem < 32 ? 32 / numBitsPerQElem : 1;
for (int i = 0; i < qElementPerWord; i++) {
if VL_CONSTEXPR_CXX17 (needsMask) {
q.push_back(static_cast<T>(((ret >> (qElementPerWord - i - 1) * numBitsPerQElem))
& elementMask));
} else {
q.push_back(static_cast<T>((ret)));
}
}
}
template <std::size_t N_Words>
static inline void VL_STREAML_RII(int lbits, int queueBits, VlQueue<VlWide<N_Words>>& q, IData ld,
IData rd) VL_MT_SAFE {
if (lbits < queueBits) { lbits = queueBits; }
IData ret = 0;
// Slice size should never exceed the lhs width
const IData mask = VL_MASK_I(rd);
for (int istart = 0; istart < lbits; istart += rd) {
int ostart = lbits - rd - istart;
ostart = ostart > 0 ? ostart : 0;
ret |= ((ld >> istart) & mask) << ostart;
}
q.clear();
VlWide<N_Words> value;
value[0] = ret;
q.push_back(value);
}
static inline QData VL_STREAML_QQI(int lbits, QData ld, IData rd) VL_PURE {
QData ret = 0;
// Slice size should never exceed the lhs width
@ -1602,6 +2070,59 @@ static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const
return owp;
}
template <typename T>
static inline void VL_STREAML_RWI(int lbits, int queueBits, VlQueue<T>& q, WDataInP const lwp,
IData rd) VL_MT_SAFE {
const bool needsMask = sizeof(T) < 4;
VL_CONSTEXPR_CXX17 int numBitsInT = 8 * sizeof(T);
VL_CONSTEXPR_CXX17 int mask = VL_MASK_I(numBitsInT * needsMask);
q.renew(lbits / numBitsInT);
const int ssize = (rd < static_cast<IData>(lbits)) ? rd : (static_cast<IData>(lbits));
for (int istart = 0; istart < lbits; istart += rd) {
int ostart = lbits - rd - istart;
ostart = ostart > 0 ? ostart : 0;
for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) {
const EData bit = (VL_BITRSHIFT_W(lwp, (istart + sbit)) & 1)
<< VL_BITBIT_E(ostart + sbit);
int qIndex = istart / numBitsInT;
if VL_CONSTEXPR_CXX17 (needsMask) {
int elementInWord = VL_BITBIT_I(ostart + sbit) / numBitsInT;
elementInWord *= numBitsInT;
q.atWrite(qIndex) |= (bit >> elementInWord) & mask;
} else if VL_CONSTEXPR_CXX17 (sizeof(T) > 4) {
int wordInElement = VL_BITBIT_Q(ostart) > 32;
wordInElement *= 32;
q.atWrite(qIndex) |= static_cast<T>(bit) << wordInElement;
} else {
q.atWrite(qIndex) |= (bit);
}
}
}
}
template <std::size_t N_Words>
static inline void VL_STREAML_RWI(int lbits, int queueBits, VlQueue<VlWide<N_Words>>& q,
WDataInP const lwp, IData rd) VL_MT_SAFE {
VL_CONSTEXPR_CXX17 int numBitsInT = 4 * N_Words * 8;
if (lbits < queueBits) { // this handles the case where the queue is larger than the rhs
lbits = queueBits;
}
const int leftOver = (lbits % numBitsInT) > 0;
q.renew(lbits / numBitsInT + leftOver);
const int ssize = (rd < static_cast<IData>(lbits)) ? rd : (static_cast<IData>(lbits));
for (int istart = 0; istart < lbits; istart += rd) {
int ostart = lbits - rd - istart;
ostart = ostart > 0 ? ostart : 0;
for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) {
const EData bit = (VL_BITRSHIFT_W(lwp, (istart + sbit)) & 1)
<< VL_BITBIT_E(ostart + sbit);
int qIndex = istart / numBitsInT;
int wordInWide = VL_BITWORD_E(ostart % numBitsInT);
q.atWrite(qIndex).at(wordInWide) |= (bit);
}
}
}
static inline IData VL_PACK_I_RI(int /*obits*/, int lbits, const VlQueue<CData>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i)
@ -1696,6 +2217,19 @@ static inline QData VL_PACK_Q_RQ(int /*obits*/, int lbits, const VlQueue<QData>&
return ret;
}
static inline IData VL_PACK_I_RQ(int /*obits*/, int lbits, const VlQueue<QData>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits);
return ret;
}
template <std::size_t N_Words>
static inline IData VL_PACK_I_RW(int /*obits*/, int lbits, const VlQueue<VlWide<N_Words>>& q) {
IData ret = 0;
for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i)[0] << (i * lbits);
return ret;
}
template <std::size_t N_Depth>
static inline QData VL_PACK_Q_UQ(int /*obits*/, int lbits, const VlUnpacked<QData, N_Depth>& q) {
QData ret = 0;
@ -1835,6 +2369,7 @@ static inline WDataOutP VL_CONCAT_WWI(int obits, int lbits, int rbits, WDataOutP
_vl_insert_WW(owp, lwp, rbits + lbits - 1, rbits);
return owp;
}
static inline WDataOutP VL_CONCAT_WIW(int obits, int lbits, int rbits, WDataOutP owp, IData ld,
WDataInP const rwp) VL_MT_SAFE {
const int rwords = VL_WORDS_I(rbits);
@ -2179,6 +2714,43 @@ static inline IData VL_BITSEL_IWII(int lbits, WDataInP const lwp, IData rd) VL_M
#define VL_SEL_QQII(lbits, lhs, lsb, width) ((lhs) >> (lsb))
#define VL_SEL_IQII(lbits, lhs, lsb, width) (static_cast<IData>((lhs) >> (lsb)))
// #define VL_SEL_IRII(lbits, lhs, lsb, width) ((lhs) >> (lsb))
template <typename T>
static inline IData VL_SEL_IRII(int lbits, const VlQueue<T>& lhs, IData lsb,
IData width) VL_MT_SAFE {
IData val = 0;
if (sizeof(T) == 8) {
const int offset = lhs.size() * sizeof(T) / sizeof(IData) - VL_BITWORD_E(lsb) - 1;
const int wordIndex = VL_BITWORD_E(VL_BITBIT_Q(lsb));
const int shiftAmt = wordIndex << 5;
const int index = offset / 2;
val |= static_cast<IData>(lhs.at(index) >> shiftAmt);
return val;
}
const int qElemPerWord = 4 / sizeof(T);
const int shiftAmt = qElemPerWord > 1 ? sizeof(T) * 8 : 0;
for (int ii = 0; ii < qElemPerWord; ii++) {
const int offset = lhs.size() * sizeof(T) / sizeof(IData) - VL_BITWORD_E(lsb) - 1;
const int index = offset * qElemPerWord + (qElemPerWord - 1 - ii);
val |= static_cast<IData>(lhs.at(index)) << (shiftAmt * ii);
}
return val;
}
template <std::size_t N_Words>
static inline IData VL_SEL_IRII(int lbits, const VlQueue<VlWide<N_Words>>& lhs, IData lsb,
IData width) VL_MT_SAFE {
IData val = 0;
const int offset = lhs.size() * N_Words - VL_BITWORD_E(lsb) - 1;
const int wordIndex = VL_BITWORD_E(lsb % (N_Words * 32));
const int shiftAmt = VL_BITBIT_I(lsb);
const int index = offset / N_Words;
val = lhs.at(index).at(wordIndex) >> shiftAmt;
return val;
}
static inline IData VL_SEL_IWII(int lbits, WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE {
const int msb = lsb + width - 1;
if (VL_UNLIKELY(msb >= lbits)) {

View File

@ -68,6 +68,8 @@ def process() -> None:
author += ", Heidelberg University"
if re.search(r'tenstorrent', email):
author += ", Testorrent USA, Inc."
if re.search(r'secturion', email):
author += ", Secturion Systems, Inc."
if re.search(r'github action', author):
author = ""
continue

View File

@ -1388,7 +1388,8 @@ class ConstVisitor final : public VNVisitor {
&& (!VN_AS(nodep->rhsp(), Const)->num().fitsInUInt() // > 2^32 shift
|| (VN_AS(nodep->rhsp(), Const)->toUInt()
>= static_cast<uint32_t>(nodep->lhsp()->width())))
&& nodep->lhsp()->isPure());
&& nodep->lhsp()->isPure()
&& !(VN_IS(nodep->lhsp()->dtypep()->skipRefp(), StreamDType)));
}
bool operandIsTwo(const AstNode* nodep) {
const AstConst* const constp = VN_CAST(nodep, Const);
@ -1831,6 +1832,7 @@ class ConstVisitor final : public VNVisitor {
nodep->replaceWithKeepDType(childp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
}
void replaceWChildBool(AstNode* nodep, AstNodeExpr* childp) {
// NODE(..., CHILD(...)) -> REDOR(CHILD(...))
childp->unlinkFrBack();
@ -2354,7 +2356,8 @@ class ConstVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(conp), conp);
// Further reduce, either node may have more reductions.
return true;
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)) {
} else if (m_doV && VN_IS(nodep->rhsp(), StreamR)
&& !VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) {
// The right-streaming operator on rhs of assignment does not
// change the order of bits. Eliminate stream but keep its lhsp.
// Add a cast if needed.
@ -2517,12 +2520,16 @@ class ConstVisitor final : public VNVisitor {
// Source narrower than destination: left-justify by shifting left.
// The right stream operator packs left-to-right, so remaining
// LSBs are zero-filled (IEEE 1800-2023 11.4.14.2).
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
extendp->dtypeSetLogicSized(dWidth, VSigning::UNSIGNED);
srcp = new AstShiftL{
srcp->fileline(), extendp,
new AstConst{srcp->fileline(), static_cast<uint32_t>(dWidth - sWidth)},
dWidth};
if (!VN_IS(srcp->dtypep()->skipRefp(), QueueDType)) {
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
extendp->dtypeSetLogicSized(dWidth, VSigning::UNSIGNED);
srcp = new AstShiftL{
srcp->fileline(), extendp,
new AstConst{srcp->fileline(), static_cast<uint32_t>(dWidth - sWidth)},
dWidth};
} else { // if it is a queue we dont need do do a shiftL
srcp = new AstExtend{srcp->fileline(), srcp};
}
}
}
nodep->lhsp(dstp);

View File

@ -57,6 +57,7 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
putnbs(nodep, "");
bool needComma = false;
bool usesQueue = false;
string nextComma;
auto commaOut = [&out, &nextComma]() {
if (!nextComma.empty()) {
@ -109,7 +110,7 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
detailp = thsp;
break;
case 'P':
if (nodep->isWide()) {
if (nodep->isWide() && !usesQueue) {
UASSERT_OBJ(m_wideTempRefp, nodep,
"Wide op " << nodep->prettyTypeName()
<< " w/ no temp, perhaps missing op in V3EmitC?");
@ -122,7 +123,13 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
out += m_wideTempRefp->varp()->nameProtect();
m_wideTempRefp = nullptr;
needComma = true;
} else if (usesQueue) {
commaOut();
putOut();
iterateAndNextConstNull(nodep->backp()->op2p());
needComma = true;
}
break;
default: nodep->v3fatalSrc("Unknown emitOperator format code: %" << pos[0]); break;
}
@ -132,7 +139,17 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
switch (pos[0]) {
case 'q':
putOut();
emitIQW(detailp);
// If we are assigning this to a queue we need to get the return type
if (VN_IS(detailp->backp(), Assign)
&& VN_IS(detailp->backp()->op2p()->dtypep()->skipRefp(), QueueDType)) {
puts("R"); // R for queue
usesQueue = true;
} else if (VN_IS(detailp->dtypep()->skipRefp(), QueueDType)
|| VN_IS(detailp->dtypep()->skipRefp(), StreamDType)) {
puts("R"); // R for queue
} else {
emitIQW(detailp);
}
break;
case 'w':
commaOut();
@ -144,6 +161,10 @@ void EmitCFunc::emitOpName(AstNode* nodep, const string& format, AstNode* lhsp,
commaOut();
out += cvtToStr(lhsp->widthWords());
needComma = true;
} else if (VN_IS(lhsp, StreamR)) {
commaOut();
out += cvtToStr(rhsp->widthWords());
needComma = true;
}
break;
case 'i':
@ -324,7 +345,11 @@ void EmitCFunc::displayNode(AstNode* nodep, AstSFormatF* fmtp, // fmtp is nullp
const bool addrof = isScan || formatAttr.isString() || formatAttr.isComplex();
puts(",");
if (addrof) puts("&(");
iterateConst(subargp);
if (VN_IS(subargp, StreamR))
emitStreamR(
VN_CAST(subargp, StreamR),
nodep); // This has to be done here because streamR doesn't know what it returns
else { iterateConst(subargp); }
if (addrof) puts(")");
if (!addrof) emitDatap(argp);
ofp()->indentDec();

View File

@ -627,6 +627,15 @@ public:
puts(cvtToStr(nodep->widthMin()) + ", ");
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
} else if (VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)
&& (VN_IS(nodep->rhsp(), StreamL) || VN_IS(nodep->lhsp(), StreamL)
|| VN_IS(nodep->rhsp(), StreamR) || VN_IS(nodep->lhsp(), StreamR)
|| VN_IS(nodep->rhsp(), StreamR))) {
//if either side is streamL or streamR don't emit lhsp everything will be passed by
//reference
paren = false;
} else {
paren = false;
iterateAndNextConstNull(nodep->lhsp());
@ -1599,6 +1608,33 @@ public:
emitOpName(nodep, nodep->emitC(), nodep->srcp(), nodep->countp(), nullptr);
}
}
void emitStreamR(AstStreamR* nodep, AstNode* parent) {
//TODO: This might need to handle more cases like the visit(AstStreamR) function
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
}
void visit(AstStreamR* nodep) override {
//The parrent node of our AstStreamR will give just enough info for what streamR should
//output if nodep->backp() is not the parent then emitStreamR should have been used. throw
//an error
bool backpIsParent = (nodep->backp()->op1p() == nodep || nodep->backp()->op2p() == nodep);
UASSERT(backpIsParent, "can not find return type for streamR");
if ((VN_IS(nodep->backp()->dtypep()->skipRefp(), QueueDType))) {
emitOpName(nodep, "VL_STREAMR_%nq%lq%rq(%lw, %P, %li, %ri)", nodep->lhsp(),
nodep->rhsp(), nullptr);
} else if (VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) {
if (!((nodep->backp()->op1p() && nodep->backp()->op1p()->isWide())
|| (nodep->backp()->op2p() && nodep->backp()->op2p()->isWide()))) {
//If our lhsp is a queue make sure we streamR and return the correct type.
//If either side is wide or the previous node is string type dont use this case
emitOpName(nodep->backp(), "VL_STREAMR_%nq%lq%rq(%lw, %P, %li, %ri)",
nodep->lhsp(), nodep->rhsp(), nullptr);
} else {
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
}
} else {
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
}
}
void visit(AstStreamL* nodep) override {
// Attempt to use a "fast" stream function for slice size = power of 2
if (!nodep->isWide()) {
@ -1606,11 +1642,26 @@ public:
const uint32_t sliceSize = VN_AS(nodep->rhsp(), Const)->toUInt();
if (isPow2 && sliceSize <= (nodep->isQuad() ? sizeof(uint64_t) : sizeof(uint32_t))) {
putns(nodep, "VL_STREAML_FAST_");
emitIQW(nodep);
bool usesQueue = false;
AstQueueDType* qtypep
= nodep->backp()->op2p()
? VN_CAST(nodep->backp()->op2p()->dtypep()->skipRefp(), QueueDType)
: nullptr;
if (VN_IS(nodep->backp(), Assign)
&& qtypep) { // If we are assigning to a queue then emit the correct symbol
puts("R"); // R for queue
usesQueue = true;
} else {
emitIQW(nodep);
}
emitIQW(nodep->lhsp());
puts("I(");
puts(cvtToStr(nodep->lhsp()->widthMin()));
puts(", ");
if (usesQueue) {
iterateAndNextConstNull(nodep->backp()->op2p());
puts(", ");
}
iterateAndNextConstNull(nodep->lhsp());
puts(", ");
const uint32_t rd_log2 = V3Number::log2b(VN_AS(nodep->rhsp(), Const)->toUInt());
@ -1618,7 +1669,18 @@ public:
return;
}
}
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
if (VN_IS(nodep->backp(), Assign)
&& VN_IS(nodep->backp()->op2p()->dtypep()->skipRefp(), QueueDType)) {
int queueWidth
= nodep->backp()->op2p()->dtypep()->subDTypep()->width(); //We need to know the
//width of both sides
emitOpName(nodep,
"VL_STREAML_%nq%lq%rq(%lw," + std::to_string(queueWidth)
+ ", %P, %li, %ri)",
nodep->lhsp(), nodep->rhsp(), nullptr);
} else {
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nullptr);
}
}
void visit(AstCastDynamic* nodep) override {
putnbs(nodep, "VL_CAST_DYNAMIC(");

View File

@ -508,6 +508,9 @@ class ExpandVisitor final : public VNVisitor {
// Sel is an LHS assignment select
} else if (nodep->isWide()) {
// See under ASSIGN(WIDE)
} else if (VN_IS(nodep->fromp()->dtypep(), StreamDType)
|| VN_IS(nodep->fromp()->dtypep(), QueueDType)) {
//sel stream or queue
} else if (nodep->fromp()->isWide()) {
if (isImpure(nodep)) return;
UINFO(8, " SEL(wide) " << nodep);
@ -962,7 +965,9 @@ class ExpandVisitor final : public VNVisitor {
void visitEqNeq(AstNodeBiop* nodep) {
if (nodep->user1SetOnce()) return; // Process once
iterateChildren(nodep);
if (nodep->lhsp()->isWide()) {
if (nodep->lhsp()->isWide()
&& !(VN_IS(nodep->lhsp()->dtypep()->skipRefp(), StreamDType)
|| VN_IS(nodep->rhsp()->dtypep()->skipRefp(), StreamDType))) {
if (isImpure(nodep)) return;
if (!doExpandWide(nodep->lhsp())) return;
if (!doExpandWide(nodep->rhsp())) return;

View File

@ -264,7 +264,8 @@ class FuncOptVisitor final : public VNVisitor {
if (!VN_IS(rhsp, Concat) && !VN_IS(rhsp, Extend)) return false;
// Will need the LHS
AstNodeExpr* lhsp = nodep->lhsp();
UASSERT_OBJ(lhsp->width() == rhsp->width(), nodep, "Inconsistent assignment");
if (!VN_IS(lhsp->dtypep()->skipRefp(), QueueDType))
UASSERT_OBJ(lhsp->width() == rhsp->width(), nodep, "Inconsistent assignment");
// Only consider pure assignments. Nodes inserted below are safe.
if (!nodep->user1() && (!lhsp->isPure() || !rhsp->isPure())) return false;
// Do not split assignments to SC variables, they cannot be assigned in parts

View File

@ -6229,6 +6229,11 @@ class WidthVisitor final : public VNVisitor {
<< lwidth << " bits) is narrower than the stream ("
<< rwidth << " bits) (IEEE 1800-2023 11.4.14)");
}
if (VN_IS(streamp->lhsp()->dtypep()->skipRefp(), QueueDType)
&& !VN_IS(nodep->lhsp()->dtypep()->skipRefp(), QueueDType)) {
const int queueElementSize = streamp->lhsp()->dtypep()->subDTypep()->width();
UASSERT_OBJ(queueElementSize <= lwidth, nodep, "LHS < RHS");
}
if (VN_IS(lhsDTypeSkippedRefp, UnpackArrayDType)) {
streamp->unlinkFrBack();
nodep->rhsp(new AstCvtPackedToArray{streamp->fileline(), streamp,

View File

@ -11,7 +11,7 @@ import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.compile(verilator_flags2=["--timing"])
test.execute()

View File

@ -4,31 +4,372 @@
// SPDX-FileCopyrightText: 2025 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%h' exp='%h'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
module t;
logic [7:0] i_char;
logic [15:0] i_short;
int i_header;
int i_len;
byte i_data[];
int i_data;
int i_crc;
logic [7:0] o_char;
logic [15:0] o_short;
int o_header;
int o_len;
byte o_data[];
int o_data;
int o_crc;
logic [128:0] wide129;
initial begin
byte pkt[$];
byte byte_pkt[$];
logic [15:0] sdata_pkt[$];
int int_pkt[$];
logic [63:0] qdata_pkt[$];
logic [128:0] vlwide_pkt_129[$];//this is off by one to test edge cases
logic [127:0] vlwide_pkt_128[$];
/* verilator lint_off ASCRANGE */
logic [0:7] byte_pkt_rev[$];
logic [0:15] sdata_pkt_rev[$];
logic [0:31] int_pkt_rev[$];
logic [0:63] qdata_pkt_rev[$];
logic [0:128] vlwide_pkt_129_rev[$];//this is off by one to test edge cases
logic [0:127] vlwide_pkt_128_rev[$];
i_header = 12;
i_len = 5;
i_data = new[5];
i_data = 11;
i_crc = 42;
i_char = 15;
i_short = 16'hFF;
#0; // this forces no-life
//TODO make this work with V3Life
//-------------------- STREAML ------------------------------------
//----------- CData QUEUE --------
byte_pkt = {<<8{i_char}};
o_char = {<<8{byte_pkt}};
`checks(o_char,i_char);
pkt = {<<8{i_header, i_len, i_data, i_crc}};
byte_pkt = {<<8{i_short}};
o_short = {<<8{byte_pkt}};
`checks(o_short,i_short);
{<<8{o_header, o_len, o_data, o_crc}} = pkt;
byte_pkt = {<<8{i_header}};
o_header = {<<8{byte_pkt}};
`checks(o_header,i_header);
byte_pkt = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = byte_pkt;
`checks({i_header,i_len},{o_header,o_len});
byte_pkt = {<<8{i_header,i_len,i_crc,i_data}};
{<<8{o_header,o_len,o_crc,o_data}} = byte_pkt;
`checks({>>{byte_pkt}},{<<8{i_header,i_len,i_crc,i_data}});
`checks({i_header,i_len,i_crc,i_data},{<<8{byte_pkt}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
// //----------- SData QUEUE --------
// sdata_pkt = {<<8{i_char}};
//TODO This should compile
// o_char = {{<<8{sdata_pkt}}}[7:0];
// `checks(o_char,i_char);
sdata_pkt = {<<8{i_short}};
o_short = {<<8{sdata_pkt}};
`checks(o_short,i_short);
sdata_pkt = {<<8{i_header}};
o_header = {<<8{sdata_pkt}};
`checks(o_header,i_header);
//test with QData
sdata_pkt = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = sdata_pkt;
`checks({i_header,i_len},{o_header,o_len});
sdata_pkt = {<<8{i_header,i_len,i_crc,i_data}};
{<<8{o_header,o_len,o_crc,o_data}} = sdata_pkt;
`checks({>>{sdata_pkt}},{<<8{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
//----------- IData QUEUE --------
int_pkt = {<<8{i_header}};
o_header = {<<8{int_pkt}};
`checks(o_header,i_header);
//test with QData
int_pkt = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = int_pkt;
`checks({i_header,i_len},{o_header,o_len});
int_pkt = {<<8{i_header,i_len,i_crc,i_data}};
{<<8{o_header,o_len,o_crc,o_data}} = int_pkt;
`checks({>>{int_pkt}},{<<8{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
//----------- QData QUEUE --------
qdata_pkt = {<<8{i_header}};
// o_header = {<<8{qdata_pkt}};
//TODO This should compile
// o_header = {{<<8{sdata_pkt}}}[32:0];
// `checks(o_header,i_header);
//test with QData
qdata_pkt = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = qdata_pkt;
`checks({i_header,i_len},{o_header,o_len});
qdata_pkt = {<<8{i_header,i_len,i_crc,i_data}};
{<<8{o_header,o_len,o_crc,o_data}} = qdata_pkt;
`checks({>>{qdata_pkt}},{<<8{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
// ----------- VLWide QUEUE --------
// test with QData
vlwide_pkt_129 = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = vlwide_pkt_129; //TODO this shouldn't compile lhs should not be smaller then rhs
// `checks({i_header,i_len},{o_header,o_len});
vlwide_pkt_129 = {<<8{i_header,i_len,i_crc,i_data}};
/* verilator lint_off WIDTHEXPAND */
wide129 = {<<8{i_header,i_len,i_crc,i_data}};
`checks({>>{vlwide_pkt_129}},wide129);
/* verilator lint_on WIDTHEXPAND */
//------------------------------- REVERSE ENDIAN ------------------------------
//----------- CData QUEUE --------
byte_pkt_rev = {<<8{i_char}};
o_char = {<<8{byte_pkt_rev}};
`checks(o_char,i_char);
byte_pkt_rev = {<<8{i_short}};
o_short = {<<8{byte_pkt_rev}};
`checks(o_short,i_short);
byte_pkt_rev = {<<8{i_header}};
o_header = {<<8{byte_pkt_rev}};
`checks(o_header,i_header);
byte_pkt_rev = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = byte_pkt_rev;
`checks({i_header,i_len},{o_header,o_len});
byte_pkt_rev = {<<8{i_header,i_len,i_crc,i_data}};
{<<8{o_header,o_len,o_crc,o_data}} = byte_pkt_rev;
`checks({>>{byte_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}});
`checks({i_header,i_len,i_crc,i_data},{<<8{byte_pkt_rev}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
//----------- SData QUEUE --------
sdata_pkt_rev = {<<8{i_short}};
o_short = {<<8{sdata_pkt_rev}};
`checks(o_short,i_short);
sdata_pkt_rev = {<<8{i_header}};
o_header = {<<8{sdata_pkt_rev}};
`checks(o_header,i_header);
//test with QData
sdata_pkt_rev = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = sdata_pkt_rev;
`checks({i_header,i_len},{o_header,o_len});
sdata_pkt_rev = {<<8{i_header,i_len,i_crc,i_data}};
{<<8{o_header,o_len,o_crc,o_data}} = sdata_pkt_rev;
`checks({>>{sdata_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
//----------- IData QUEUE --------
int_pkt_rev = {<<8{i_header}};
o_header = {<<8{int_pkt_rev}};
`checks(o_header,i_header);
//test with QData
int_pkt_rev = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = int_pkt_rev;
`checks({i_header,i_len},{o_header,o_len});
int_pkt_rev = {<<8{i_header,i_len,i_crc,i_data}};
{<<8{o_header,o_len,o_crc,o_data}} = int_pkt_rev;
`checks({>>{int_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
//----------- QData QUEUE --------
//test with QData
qdata_pkt_rev = {<<8{i_header,i_len}};
{<<8{o_header,o_len}} = qdata_pkt_rev;
`checks({i_header,i_len},{o_header,o_len});
qdata_pkt_rev = {<<8{i_header,i_len,i_crc,i_data}};
{<<8{o_header,o_len,o_crc,o_data}} = qdata_pkt_rev;
`checks({>>{qdata_pkt_rev}},{<<8{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
// ----------- VLWide QUEUE --------
vlwide_pkt_129_rev = {<<8{i_header,i_len,i_crc,i_data}};
/* verilator lint_off WIDTHEXPAND */
wide129 = {<<8{i_header,i_len,i_crc,i_data}};
/* verilator lint_on WIDTHEXPAND */
`checks({>>{vlwide_pkt_129_rev}},wide129);
// // -------------------- STREAMR ------------------------------------
// //----------- CData QUEUE --------
byte_pkt = {>>{i_header}};
o_header = {>>{byte_pkt}};
`checks(o_header,i_header);
byte_pkt = {>>{i_header,i_len}};
{>>{o_header,o_len}} = byte_pkt;
`checks({>>{i_header,i_len}},{>>{o_header,o_len}});
`checks({i_header,i_len},{o_header,o_len});
byte_pkt = {>>{i_header,i_len,i_crc,i_data}};
{>>{o_header,o_len,o_crc,o_data}} = byte_pkt;
`checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
//----------- IData QUEUE --------
int_pkt = {>>{i_header}};
o_header = {>>{int_pkt}};
`checks(o_header,i_header);
`checks(o_header,{>>{int_pkt}});
`checks({>>{o_header}},{>>{int_pkt}});
//test with QData
int_pkt = {>>{i_header,i_len}};
{>>{o_header,o_len}} = int_pkt;
`checks({i_header,i_len},{o_header,o_len});
int_pkt = {>>{i_header,i_len,i_crc,i_data}};
{>>{o_header,o_len,o_crc,o_data}} = int_pkt;
`checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
//----------- QData QUEUE --------
// test with QData
qdata_pkt = {>>{i_header,i_len}};
{>>{o_header,o_len}} = qdata_pkt;
`checks({i_header,i_len},{o_header,o_len});
qdata_pkt = {>>{i_header,i_len,i_crc,i_data}};
{>>{o_header,o_len,o_crc,o_data}} = qdata_pkt;
`checks({>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
// ----------- VLWide QUEUE --------
// test with QData
vlwide_pkt_129 = {>>{i_header,i_len}};
{>>{o_header,o_len}} = vlwide_pkt_129;
`checks({i_header,i_len},{o_header,o_len});
vlwide_pkt_129 = {>>{i_header,i_len,i_crc,i_data}};
{>>{o_header,o_len,o_crc,o_data}} = vlwide_pkt_129;
`checks({>>{vlwide_pkt_129}},{>>{1'b0,i_header,i_len,i_crc,i_data}});
`checks({o_header,o_len,o_crc,o_data} ,{i_header,i_len,i_crc,i_data});
//---------- into other queues ------
int_pkt = {>>{i_header,i_len,i_crc,i_data}};
byte_pkt = {>>{int_pkt}};
`checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}});
byte_pkt = {>>{i_header,i_len,i_crc,i_data}};
int_pkt = {>>{byte_pkt}};
`checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}});
byte_pkt = {>>{i_header,i_len,i_crc,i_data}};
int_pkt = {>>{byte_pkt}};
`checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}});
sdata_pkt = {>>{i_header,i_len,i_crc,i_data}};
byte_pkt = {>>{sdata_pkt}};
`checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}});
byte_pkt = {>>{i_header,i_len,i_crc,i_data}};
sdata_pkt = {>>{byte_pkt}};
`checks({>>{sdata_pkt}},{>>{i_header,i_len,i_crc,i_data}});
byte_pkt = {>>{i_header,i_len,i_crc,i_data}};
qdata_pkt = {>>{byte_pkt}};
`checks({>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}});
qdata_pkt = {>>{i_header,i_len,i_crc,i_data}};
byte_pkt = {>>{qdata_pkt}};
`checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}});
qdata_pkt = {>>{i_header,i_len,i_crc,i_data}};
int_pkt = {>>{qdata_pkt}};
`checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}});
int_pkt = {>>{i_header,i_len,i_crc,i_data}};
qdata_pkt = {>>{int_pkt}};
`checks({>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}});
byte_pkt = {>>{i_header,i_len,i_crc,i_data}};
vlwide_pkt_128 = {>>{byte_pkt}};
`checks({>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}});
vlwide_pkt_128 = {>>{i_header,i_len,i_crc,i_data}};
byte_pkt = {>>{vlwide_pkt_128}};
`checks({i_header,i_len,i_crc,i_data},{>>{byte_pkt}});
`checks({>>{byte_pkt}},{>>{i_header,i_len,i_crc,i_data}});
int_pkt = {>>{i_header,i_len,i_crc,i_data}};
vlwide_pkt_128 = {>>{int_pkt}};
`checks({i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}});
`checks({>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}});
vlwide_pkt_128 = {>>{i_header,i_len,i_crc,i_data}};
int_pkt = {>>{vlwide_pkt_128}};
`checks({i_header,i_len,i_crc,i_data},{>>{int_pkt}});
`checks({>>{int_pkt}},{>>{i_header,i_len,i_crc,i_data}});
qdata_pkt = {>>{i_header,i_len,i_crc,i_data}};
vlwide_pkt_128 = {>>{qdata_pkt}};
`checks({i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}});
`checks({>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data}});
qdata_pkt = {>>{i_header,i_len,i_crc,i_data,i_header,i_len,i_crc,i_data}};
vlwide_pkt_128 = {>>{qdata_pkt}};
`checks({i_header,i_len,i_crc,i_data,i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}});
`checks({>>{vlwide_pkt_128}},{>>{i_header,i_len,i_crc,i_data,i_header,i_len,i_crc,i_data}});
qdata_pkt = {>>{i_header,i_len,i_crc,i_data,i_header,i_len,i_crc}};
vlwide_pkt_128 = {>>{qdata_pkt}};
`checks({32'h0,i_header,i_len,i_crc,i_data,i_header,i_len,i_crc},{>>{vlwide_pkt_128}});
`checks({>>{vlwide_pkt_128}},{>>{32'h0,i_header,i_len,i_crc,i_data,i_header,i_len,i_crc}});
vlwide_pkt_128 = {>>{i_header,i_len,i_crc,i_data}};
qdata_pkt = {>>{vlwide_pkt_128}};
`checks({i_header,i_len,i_crc,i_data},{>>{vlwide_pkt_128}});
`checks({>>{qdata_pkt}},{>>{i_header,i_len,i_crc,i_data}});
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2025 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_stream_queue_interface.sv"
test.compile(verilator_flags2=["--timing -Wno-WIDTHEXPAND"])
test.execute()
test.passes()

View File

@ -0,0 +1,76 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2025 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%h' exp='%h'\n", `__FILE__,`__LINE__, (gotv), (expv)); end while(0);
module t;
logic clk;
int i_header;
int i_len;
int i_data;
int i_crc;
int o_header;
int o_len;
int o_data;
int o_crc;
pkt_if pkt_if_init (clk);
//this will not compile without -fno-life
initial begin
byte byte_pkt[$];
//---------------------- STREAM WITH INTERFACE -------------------
//using this forces verilator to a AstSel Node into a Stream Node
#0 //make sure we dont optimize it all away in v3life
pkt_if_init.s.extra = 8'hd;
byte_pkt = {>>{pkt_if_init.s.extra}};
if(8'hd == {>>{byte_pkt}}) begin
$write("*-* All Finished *-*\n");
$finish();
end
end
endmodule
interface pkt_if (
input wire clk
);
typedef struct packed {
logic [31:0] extra;
logic [31:0] empty;
logic [31:0] data;
logic valid;
logic sop;
logic eop;
} avst_s;
avst_s s;
logic ready;
// Read-Only Helper Signals
logic sop_pulse;
logic eop_pulse;
modport src (
output s,
input ready,
input sop_pulse, eop_pulse
);
modport snoop (
input s,
input ready,
input sop_pulse, eop_pulse
);
modport sink (
input s,
output ready,
input sop_pulse, eop_pulse
);
endinterface

View File

@ -11,7 +11,7 @@ import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=['--cc --trace-vcd'])
test.compile(verilator_flags2=['--cc --trace-vcd -fno-life'])
test.execute()