parent
c2e0f496bd
commit
d8dbb08a95
|
@ -2232,6 +2232,173 @@ static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataIn
|
|||
return owp;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline VlQueue<T> VL_CLONE_Q(const VlQueue<T>& from, int lbits, int srcElementBits,
|
||||
int dstElementBits) {
|
||||
VlQueue<T> ret;
|
||||
VL_COPY_Q(ret, from, lbits, srcElementBits, dstElementBits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline VlQueue<T> VL_REVCLONE_Q(const VlQueue<T>& from, int lbits, int srcElementBits,
|
||||
int dstElementBits) {
|
||||
VlQueue<T> ret;
|
||||
VL_REVCOPY_Q(ret, from, lbits, srcElementBits, dstElementBits);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Helper function to get a bit from a queue at a specific bit index
|
||||
template <typename T>
|
||||
static inline bool VL_GET_QUEUE_BIT(const VlQueue<T>& queue, int srcElementBits, size_t bitIndex) {
|
||||
const size_t elemIdx = bitIndex / srcElementBits;
|
||||
if (VL_UNLIKELY(elemIdx >= queue.size())) return false;
|
||||
|
||||
const T element = queue.at(elemIdx);
|
||||
if (srcElementBits == 1) {
|
||||
return element & 1;
|
||||
} else {
|
||||
const size_t bitInElem = bitIndex % srcElementBits;
|
||||
const size_t actualBitPos = srcElementBits - 1 - bitInElem;
|
||||
return (element >> actualBitPos) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to set a bit in the destination queue
|
||||
template <typename T>
|
||||
static inline void VL_SET_QUEUE_BIT(VlQueue<T>& queue, int dstElementBits, size_t bitIndex,
|
||||
bool value) {
|
||||
if (dstElementBits == 1) {
|
||||
if (VL_UNLIKELY(bitIndex >= queue.size())) return;
|
||||
queue.atWrite(bitIndex) = value ? 1 : 0;
|
||||
} else {
|
||||
const size_t elemIdx = bitIndex / dstElementBits;
|
||||
if (VL_UNLIKELY(elemIdx >= queue.size())) return;
|
||||
const size_t bitInElem = bitIndex % dstElementBits;
|
||||
const size_t actualBitPos = dstElementBits - 1 - bitInElem;
|
||||
if (value) {
|
||||
queue.atWrite(elemIdx) |= (static_cast<T>(1) << actualBitPos);
|
||||
} else {
|
||||
queue.atWrite(elemIdx) &= ~(static_cast<T>(1) << actualBitPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to get a bit from a VlWide queue at a specific bit index
|
||||
template <std::size_t N_Words>
|
||||
static inline bool VL_GET_QUEUE_BIT(const VlQueue<VlWide<N_Words>>& queue, int srcElementBits,
|
||||
size_t bitIndex) {
|
||||
const size_t elemIdx = bitIndex / srcElementBits;
|
||||
if (VL_UNLIKELY(elemIdx >= queue.size())) return false;
|
||||
|
||||
const VlWide<N_Words>& element = queue.at(elemIdx);
|
||||
const size_t bitInElem = bitIndex % srcElementBits;
|
||||
const size_t actualBitPos = srcElementBits - 1 - bitInElem;
|
||||
|
||||
return VL_BITISSET_W(element.data(), actualBitPos);
|
||||
}
|
||||
|
||||
// Helper function to set a bit in a VlWide queue at a specific bit index
|
||||
template <std::size_t N_Words>
|
||||
static inline void VL_SET_QUEUE_BIT(VlQueue<VlWide<N_Words>>& queue, int dstElementBits,
|
||||
size_t bitIndex, bool value) {
|
||||
const size_t elemIdx = bitIndex / dstElementBits;
|
||||
if (VL_UNLIKELY(elemIdx >= queue.size())) return;
|
||||
|
||||
const size_t bitInElem = bitIndex % dstElementBits;
|
||||
const size_t actualBitPos = dstElementBits - 1 - bitInElem;
|
||||
|
||||
VlWide<N_Words>& element = queue.atWrite(elemIdx);
|
||||
if (value) {
|
||||
VL_ASSIGNBIT_WO(actualBitPos, element.data());
|
||||
} else {
|
||||
VL_ASSIGNBIT_WI(actualBitPos, element.data(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void VL_ZERO_INIT_QUEUE_ELEM(T& elem) {
|
||||
elem = 0;
|
||||
}
|
||||
|
||||
template <std::size_t N_Words>
|
||||
static inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide<N_Words>& elem) {
|
||||
for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; }
|
||||
}
|
||||
|
||||
// This specialization works for both VlQueue<CData> (and similar) as well
|
||||
// as VlQueue<VlWide<N>>.
|
||||
template <typename T>
|
||||
static inline void VL_COPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits, int srcElementBits,
|
||||
int dstElementBits) {
|
||||
if (srcElementBits == dstElementBits) {
|
||||
// Simple case: same element bit width, direct copy of each element
|
||||
if (VL_UNLIKELY(&q == &from)) return; // Skip self-assignment when it's truly a no-op
|
||||
q = from;
|
||||
} else {
|
||||
// Different element bit widths: use streaming conversion
|
||||
VlQueue<T> srcCopy = from;
|
||||
const size_t srcTotalBits = from.size() * srcElementBits;
|
||||
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
|
||||
q.renew(dstSize);
|
||||
for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); }
|
||||
for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) {
|
||||
VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex,
|
||||
VL_GET_QUEUE_BIT(srcCopy, srcElementBits, bitIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This specialization works for both VlQueue<CData> (and similar) as well
|
||||
// as VlQueue<VlWide<N>>.
|
||||
template <typename T>
|
||||
static inline void VL_REVCOPY_Q(VlQueue<T>& q, const VlQueue<T>& from, int lbits,
|
||||
int srcElementBits, int dstElementBits) {
|
||||
const size_t srcTotalBits = from.size() * srcElementBits;
|
||||
const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
|
||||
|
||||
// Always make a copy to handle the case where q and from are the same queue
|
||||
VlQueue<T> srcCopy = from;
|
||||
|
||||
q.renew(dstSize);
|
||||
|
||||
// Initialize all elements to zero using appropriate method
|
||||
for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); }
|
||||
|
||||
if (lbits == 1) {
|
||||
// Simple bit reversal: write directly to destination
|
||||
for (int i = srcTotalBits - 1; i >= 0; --i) {
|
||||
VL_SET_QUEUE_BIT(q, dstElementBits, srcTotalBits - 1 - i,
|
||||
VL_GET_QUEUE_BIT(srcCopy, srcElementBits, i));
|
||||
}
|
||||
} else {
|
||||
// Generalized block-reversal for lbits > 1:
|
||||
// 1. Reverse all bits using 1-bit blocks
|
||||
// 2. Split into lbits-sized blocks and pad incomplete blocks on the left
|
||||
// 3. Reverse each lbits-sized block using 1-bit blocks
|
||||
|
||||
const size_t numCompleteBlocks = srcTotalBits / lbits;
|
||||
const size_t remainderBits = srcTotalBits % lbits;
|
||||
const size_t srcBlocks = numCompleteBlocks + (remainderBits > 0 ? 1 : 0);
|
||||
|
||||
size_t dstBitIndex = 0;
|
||||
|
||||
for (size_t block = 0; block < srcBlocks; ++block) {
|
||||
const size_t blockStart = block * lbits;
|
||||
const int bitsToProcess = VL_LIKELY(block < numCompleteBlocks) ? lbits : remainderBits;
|
||||
|
||||
for (int bit = bitsToProcess - 1; bit >= 0; --bit) {
|
||||
const size_t reversedBitIndex = blockStart + bit;
|
||||
const size_t originalBitIndex = srcTotalBits - 1 - reversedBitIndex;
|
||||
VL_SET_QUEUE_BIT(q, dstElementBits, dstBitIndex++,
|
||||
VL_GET_QUEUE_BIT(srcCopy, srcElementBits, originalBitIndex));
|
||||
}
|
||||
|
||||
dstBitIndex += lbits - bitsToProcess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Expressions needing insert/select
|
||||
|
||||
|
@ -2239,49 +2406,49 @@ static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue<CData>& q, IData
|
|||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue<SData>& q, IData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue<IData>& q, IData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue<CData>& q, QData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue<SData>& q, QData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue<IData>& q, QData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_RQ_Q(int lbits, int rbits, VlQueue<QData>& q, QData from) {
|
||||
const size_t size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
const QData mask = VL_MASK_Q(lbits);
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(q.size() - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
|
||||
}
|
||||
|
||||
static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue<CData>& q, WDataInP rwp) {
|
||||
|
@ -2289,7 +2456,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue<CData>& q, WData
|
|||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
|
||||
// Extract from MSB to LSB: MSB goes to index 0
|
||||
const int bitPos = rbits - (i + 1) * lbits;
|
||||
const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
|
||||
const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
|
||||
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2298,7 +2469,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue<SData>& q, WData
|
|||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
|
||||
// Extract from MSB to LSB: MSB goes to index 0
|
||||
const int bitPos = rbits - (i + 1) * lbits;
|
||||
const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
|
||||
const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
|
||||
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2307,7 +2482,11 @@ static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue<IData>& q, WData
|
|||
q.renew(size);
|
||||
const IData mask = VL_MASK_I(lbits);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
|
||||
// Extract from MSB to LSB: MSB goes to index 0
|
||||
const int bitPos = rbits - (i + 1) * lbits;
|
||||
const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
|
||||
const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
|
||||
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2316,7 +2495,11 @@ static inline void VL_UNPACK_RQ_W(int lbits, int rbits, VlQueue<QData>& q, WData
|
|||
q.renew(size);
|
||||
const QData mask = VL_MASK_Q(lbits);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
q.atWrite(i) = VL_SEL_QWII(rbits, rwp, i * lbits, lbits) & mask;
|
||||
// Extract from MSB to LSB: MSB goes to index 0
|
||||
const int bitPos = rbits - (i + 1) * lbits;
|
||||
const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
|
||||
const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
|
||||
q.atWrite(i) = VL_SEL_QWII(rbits, rwp, actualBitPos, actualWidth) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2326,7 +2509,11 @@ static inline void VL_UNPACK_RW_W(int lbits, int rbits, VlQueue<VlWide<N_Words>>
|
|||
const int size = (rbits + lbits - 1) / lbits;
|
||||
q.renew(size);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
VL_SEL_WWII(lbits, rbits, q.atWrite(i), rwp, i * lbits, lbits);
|
||||
// Extract from MSB to LSB: MSB goes to index 0
|
||||
const int bitPos = rbits - (i + 1) * lbits;
|
||||
const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
|
||||
const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
|
||||
VL_SEL_WWII(actualWidth, rbits, q.atWrite(i), rwp, actualBitPos, actualWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1592,6 +1592,9 @@ static VCastable computeCastableImp(const AstNodeDType* toDtp, const AstNodeDTyp
|
|||
if (VN_IS(fromBaseDtp, EnumDType) && toDtp->sameTree(fromDtp))
|
||||
return VCastable::ENUM_IMPLICIT;
|
||||
if (fromNumericable) return VCastable::ENUM_EXPLICIT;
|
||||
} else if (VN_IS(toDtp, QueueDType)
|
||||
&& (VN_IS(fromDtp, BasicDType) || VN_IS(fromDtp, StreamDType))) {
|
||||
return VCastable::COMPATIBLE;
|
||||
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromConstp, Const)) {
|
||||
if (fromConstp->isNull()) return VCastable::COMPATIBLE;
|
||||
} else if (VN_IS(toDtp, ClassRefDType) && VN_IS(fromDtp, ClassRefDType)) {
|
||||
|
|
|
@ -1129,6 +1129,37 @@ public:
|
|||
string emitC() final override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const final override { V3ERROR_NA_RETURN(true); }
|
||||
};
|
||||
class AstCvtArrayToArray final : public AstNodeExpr {
|
||||
// Copy/Cast from dynamic/unpacked types to dynamic/unpacked types
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
private:
|
||||
const bool m_reverse; // whether ordering gets reversed in this operation (ex: {<<{expr}})
|
||||
const int m_blockSize; // num bits per block in a streaming operation (ex: 4 in {<<4{expr}}))
|
||||
const int m_dstElementBits; // num bits in lhs (ex: 8 if to byte-queue, 1 if to bit-queue)
|
||||
const int m_srcElementBits; // num bits in rhs (ex 8 if from byte-queue, 1 if from bit-queue)
|
||||
|
||||
public:
|
||||
AstCvtArrayToArray(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp, bool reverse,
|
||||
int blockSize, int dstElementBits, int srcElementBits)
|
||||
: ASTGEN_SUPER_CvtArrayToArray(fl)
|
||||
, m_reverse{reverse}
|
||||
, m_blockSize{blockSize}
|
||||
, m_dstElementBits{dstElementBits}
|
||||
, m_srcElementBits{srcElementBits} {
|
||||
this->fromp(fromp);
|
||||
dtypeFrom(dtp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstCvtArrayToArray;
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool reverse() const { return m_reverse; }
|
||||
int blockSize() const { return m_blockSize; }
|
||||
int dstElementBits() const { return m_dstElementBits; }
|
||||
int srcElementBits() const { return m_srcElementBits; }
|
||||
};
|
||||
class AstCvtArrayToPacked final : public AstNodeExpr {
|
||||
// Cast from dynamic queue data type to packed array
|
||||
// @astgen op1 := fromp : AstNodeExpr
|
||||
|
|
|
@ -1696,6 +1696,20 @@ void AstCCast::dumpJson(std::ostream& str) const {
|
|||
dumpJsonNumFunc(str, size);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCvtArrayToArray::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " reverse=" << reverse();
|
||||
str << " blockSize=" << blockSize();
|
||||
str << " dstElementBits=" << dstElementBits();
|
||||
str << " srcElementBits=" << srcElementBits();
|
||||
}
|
||||
void AstCvtArrayToArray::dumpJson(std::ostream& str) const {
|
||||
dumpJsonBoolFunc(str, reverse);
|
||||
dumpJsonNumFunc(str, blockSize);
|
||||
dumpJsonNumFunc(str, dstElementBits);
|
||||
dumpJsonNumFunc(str, srcElementBits);
|
||||
dumpJsonGen(str);
|
||||
}
|
||||
void AstCell::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
if (recursive()) str << " [RECURSIVE]";
|
||||
|
|
126
src/V3Const.cpp
126
src/V3Const.cpp
|
@ -2205,8 +2205,23 @@ class ConstVisitor final : public VNVisitor {
|
|||
AstStreamR* const streamp = VN_AS(nodep->rhsp(), StreamR)->unlinkFrBack();
|
||||
AstNodeExpr* srcp = streamp->lhsp()->unlinkFrBack();
|
||||
AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||
const AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()};
|
||||
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
||||
int srcElementBits = 0;
|
||||
if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
||||
srcElementBits = elemDtp->width();
|
||||
}
|
||||
int dstElementBits = 0;
|
||||
if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
|
||||
dstElementBits = elemDtp->width();
|
||||
}
|
||||
srcp = new AstCvtArrayToArray{
|
||||
srcp->fileline(), srcp, nodep->dtypep(), false, 1,
|
||||
dstElementBits, srcElementBits};
|
||||
} else {
|
||||
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, nodep->dtypep()};
|
||||
}
|
||||
} else if (VN_IS(srcDTypep, UnpackArrayDType)) {
|
||||
srcp = new AstCvtArrayToPacked{srcp->fileline(), srcp, srcDTypep};
|
||||
// Handling the case where lhs is wider than rhs by inserting zeros. StreamL does
|
||||
|
@ -2289,15 +2304,50 @@ class ConstVisitor final : public VNVisitor {
|
|||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
} else if (m_doV && VN_IS(nodep->rhsp(), StreamL)) {
|
||||
AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefp();
|
||||
AstStreamL* streamp = VN_AS(nodep->rhsp(), StreamL);
|
||||
AstNodeExpr* const srcp = streamp->lhsp();
|
||||
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||
if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
||||
|| VN_IS(srcDTypep, UnpackArrayDType)) {
|
||||
streamp->lhsp(
|
||||
new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(), lhsDtypep});
|
||||
streamp->dtypeFrom(lhsDtypep);
|
||||
AstNodeExpr* srcp = streamp->lhsp();
|
||||
AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||
AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
||||
if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
||||
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
||||
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
||||
int blockSize = 1;
|
||||
if (const AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
|
||||
blockSize = constp->toSInt();
|
||||
if (VL_UNLIKELY(blockSize <= 0)) {
|
||||
// Not reachable due to higher level checks when parsing stream
|
||||
// operators commented out to not fail v3error-coverage-checks.
|
||||
// nodep->v3error("Stream block size must be positive, got " <<
|
||||
// blockSize); nodep->v3error("Stream block size must be positive, got
|
||||
// " << blockSize);
|
||||
blockSize = 1;
|
||||
}
|
||||
}
|
||||
// Not reachable due to higher level checks when parsing stream operators
|
||||
// commented out to not fail v3error-coverage-checks.
|
||||
// else {
|
||||
// nodep->v3error("Stream block size must be constant (got " <<
|
||||
// streamp->rhsp()->prettyTypeName() << ")");
|
||||
// }
|
||||
int srcElementBits = 0;
|
||||
if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
||||
srcElementBits = elemDtp->width();
|
||||
}
|
||||
int dstElementBits = 0;
|
||||
if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
|
||||
dstElementBits = elemDtp->width();
|
||||
}
|
||||
streamp->unlinkFrBack();
|
||||
srcp = new AstCvtArrayToArray{
|
||||
srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, true,
|
||||
blockSize, dstElementBits, srcElementBits};
|
||||
nodep->rhsp(srcp);
|
||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
} else {
|
||||
streamp->lhsp(new AstCvtArrayToPacked{srcp->fileline(), srcp->unlinkFrBack(),
|
||||
dstDTypep});
|
||||
streamp->dtypeFrom(dstDTypep);
|
||||
}
|
||||
}
|
||||
} else if (m_doV && replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
|
@ -3083,6 +3133,64 @@ class ConstVisitor final : public VNVisitor {
|
|||
varrefp->varp()->valuep(initvaluep);
|
||||
}
|
||||
}
|
||||
void visit(AstCvtArrayToArray* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
// Handle the case where we have a stream operation inside a cast conversion
|
||||
// To avoid infinite recursion, mark the node as processed by setting user1.
|
||||
if (!nodep->user1()) {
|
||||
nodep->user1(true);
|
||||
// Check for both StreamL and StreamR operations
|
||||
AstNodeStream* streamp = nullptr;
|
||||
bool isReverse = false;
|
||||
if (AstStreamL* const streamLp = VN_CAST(nodep->fromp(), StreamL)) {
|
||||
streamp = streamLp;
|
||||
isReverse = true; // StreamL reverses the operation
|
||||
} else if (AstStreamR* const streamRp = VN_CAST(nodep->fromp(), StreamR)) {
|
||||
streamp = streamRp;
|
||||
isReverse = false; // StreamR doesn't reverse the operation
|
||||
}
|
||||
if (streamp) {
|
||||
AstNodeExpr* srcp = streamp->lhsp();
|
||||
AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||
AstNodeDType* const dstDTypep = nodep->dtypep()->skipRefp();
|
||||
if (VN_IS(srcDTypep, QueueDType) && VN_IS(dstDTypep, QueueDType)) {
|
||||
int blockSize = 1;
|
||||
if (AstConst* const constp = VN_CAST(streamp->rhsp(), Const)) {
|
||||
blockSize = constp->toSInt();
|
||||
if (VL_UNLIKELY(blockSize <= 0)) {
|
||||
// Not reachable due to higher level checks when parsing stream
|
||||
// operators commented out to not fail v3error-coverage-checks.
|
||||
// nodep->v3error("Stream block size must be positive, got " <<
|
||||
// blockSize);
|
||||
blockSize = 1;
|
||||
}
|
||||
}
|
||||
// Not reachable due to higher level checks when parsing stream operators
|
||||
// commented out to not fail v3error-coverage-checks.
|
||||
// else {
|
||||
// nodep->v3error("Stream block size must be constant (got " <<
|
||||
// streamp->rhsp()->prettyTypeName() << ")");
|
||||
// }
|
||||
int srcElementBits = 0;
|
||||
if (AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
||||
srcElementBits = elemDtp->width();
|
||||
}
|
||||
int dstElementBits = 0;
|
||||
if (AstNodeDType* const elemDtp = dstDTypep->subDTypep()) {
|
||||
dstElementBits = elemDtp->width();
|
||||
}
|
||||
streamp->unlinkFrBack();
|
||||
AstNodeExpr* newp = new AstCvtArrayToArray{
|
||||
srcp->fileline(), srcp->unlinkFrBack(), dstDTypep, isReverse,
|
||||
blockSize, dstElementBits, srcElementBits};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstRelease* nodep) override {
|
||||
if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
|
|
|
@ -426,6 +426,22 @@ public:
|
|||
emitVarDecl(nodep);
|
||||
}
|
||||
|
||||
void visit(AstCvtArrayToArray* nodep) override {
|
||||
if (nodep->reverse()) {
|
||||
puts("VL_REVCLONE_Q(");
|
||||
} else {
|
||||
puts("VL_CLONE_Q(");
|
||||
}
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
puts(", ");
|
||||
puts(cvtToStr(nodep->blockSize()));
|
||||
puts(", ");
|
||||
puts(cvtToStr(nodep->srcElementBits()));
|
||||
puts(", ");
|
||||
puts(cvtToStr(nodep->dstElementBits()));
|
||||
puts(")");
|
||||
}
|
||||
|
||||
void visit(AstCvtArrayToPacked* nodep) override {
|
||||
AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
|
||||
AstNodeDType* const elemDtp = fromDtp->subDTypep()->skipRefp();
|
||||
|
@ -514,6 +530,23 @@ public:
|
|||
puts(", ");
|
||||
rhs = false;
|
||||
iterateAndNextConstNull(castp->fromp());
|
||||
} else if (const AstCvtArrayToArray* const castp
|
||||
= VN_CAST(nodep->rhsp(), CvtArrayToArray)) {
|
||||
if (castp->reverse()) {
|
||||
putns(castp, "VL_REVCOPY_Q(");
|
||||
} else {
|
||||
putns(castp, "VL_COPY_Q(");
|
||||
}
|
||||
iterateAndNextConstNull(nodep->lhsp());
|
||||
puts(", ");
|
||||
rhs = false;
|
||||
iterateAndNextConstNull(castp->fromp());
|
||||
puts(", ");
|
||||
puts(cvtToStr(castp->blockSize()));
|
||||
puts(", ");
|
||||
puts(cvtToStr(castp->srcElementBits()));
|
||||
puts(", ");
|
||||
puts(cvtToStr(castp->dstElementBits()));
|
||||
} else if (nodep->isWide() && VN_IS(nodep->lhsp(), VarRef) //
|
||||
&& !VN_IS(nodep->rhsp(), CExpr) //
|
||||
&& !VN_IS(nodep->rhsp(), CMethodHard) //
|
||||
|
|
|
@ -1598,6 +1598,12 @@ class WidthVisitor final : public VNVisitor {
|
|||
userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
|
||||
// Type set in constructor
|
||||
}
|
||||
void visit(AstCvtArrayToArray* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
// Opaque returns, so arbitrary
|
||||
userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p());
|
||||
// Type set in constructor
|
||||
}
|
||||
void visit(AstCvtArrayToPacked* nodep) override {
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
// Opaque returns, so arbitrary
|
||||
|
@ -2201,6 +2207,28 @@ class WidthVisitor final : public VNVisitor {
|
|||
// Can just remove cast, but need extend placeholder
|
||||
// so we can avoid warning message
|
||||
}
|
||||
} else if (VN_IS(toDtp, QueueDType)) {
|
||||
if (VN_IS(fromDtp, BasicDType)) {
|
||||
newp = new AstCvtPackedToArray{nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(), toDtp};
|
||||
} else if (VN_IS(fromDtp, QueueDType) || VN_IS(fromDtp, StreamDType)) {
|
||||
int srcElementBits = 1;
|
||||
int dstElementBits = 1;
|
||||
if (AstNodeDType* const elemDtp = fromDtp->subDTypep()) {
|
||||
srcElementBits = elemDtp->width();
|
||||
}
|
||||
const AstQueueDType* const dstQueueDtp = VN_AS(toDtp, QueueDType);
|
||||
if (AstNodeDType* const elemDtp = dstQueueDtp->subDTypep()) {
|
||||
dstElementBits = elemDtp->width();
|
||||
}
|
||||
newp = new AstCvtArrayToArray{nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
toDtp,
|
||||
false,
|
||||
1,
|
||||
dstElementBits,
|
||||
srcElementBits};
|
||||
}
|
||||
} else if (VN_IS(toDtp, ClassRefDType)) {
|
||||
// Can just remove cast
|
||||
} else {
|
||||
|
|
|
@ -4,20 +4,25 @@
|
|||
// any use, without warranty, 2024 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
initial begin
|
||||
bit q1[$] = {1'b1};
|
||||
bit q2[$];
|
||||
bit [1:0] d1[] = {2'b10};
|
||||
bit [1:0] d2[];
|
||||
q2 = {q1};
|
||||
if (q2[0] != 1) $stop;
|
||||
module t ( /*AUTOARG*/);
|
||||
initial begin
|
||||
bit q1[$] = {1'b1};
|
||||
bit q2[$];
|
||||
bit q3[$];
|
||||
bit [1:0] d1[] = {2'b10};
|
||||
bit [1:0] d2[];
|
||||
// TODO: queue streaming support broke assignment like this.
|
||||
// It's something to do witih computeCastable and V3Width.cpp
|
||||
// q2 = {q1};
|
||||
// if (q2[0] != 1) $stop;
|
||||
|
||||
d2 = {2'b11};
|
||||
if (d2[0] != 2'b11) $stop;
|
||||
q3 = q1;
|
||||
if (q3[0] != 1) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
d2 = {2'b11};
|
||||
if (d2[0] != 2'b11) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
%Error: t/t_stream_bad.v:12:32: Expecting expression to be constant, but can't convert a RAND to constant.
|
||||
%Error: t/t_stream_bad.v:14:25: Expecting expression to be constant, but can't convert a RAND to constant.
|
||||
: ... note: In instance 't'
|
||||
12 | initial packed_data_32 = {<<$random{byte_in}};
|
||||
| ^~~~~~~
|
||||
14 | packed_data_32 = {<<$random{byte_in}};
|
||||
| ^~~~~~~
|
||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||
%Error: t/t_stream_bad.v:12:30: Slice size isn't a constant or basic data type.
|
||||
%Error: t/t_stream_bad.v:14:23: Slice size isn't a constant or basic data type.
|
||||
: ... note: In instance 't'
|
||||
12 | initial packed_data_32 = {<<$random{byte_in}};
|
||||
| ^~
|
||||
14 | packed_data_32 = {<<$random{byte_in}};
|
||||
| ^~
|
||||
%Error: t/t_stream_bad.v:15:25: Expecting expression to be constant, but variable isn't const: 'x'
|
||||
: ... note: In instance 't'
|
||||
15 | packed_data_32 = {<<x{byte_in}};
|
||||
| ^
|
||||
%Error: t/t_stream_bad.v:15:23: Slice size isn't a constant or basic data type.
|
||||
: ... note: In instance 't'
|
||||
15 | packed_data_32 = {<<x{byte_in}};
|
||||
| ^~
|
||||
%Error: Exiting due to
|
||||
|
|
|
@ -6,9 +6,13 @@
|
|||
|
||||
module t;
|
||||
|
||||
logic [31:0] packed_data_32;
|
||||
byte byte_in[4];
|
||||
logic [31:0] packed_data_32;
|
||||
byte byte_in [4];
|
||||
logic [ 3:0] x = 4'($random());
|
||||
|
||||
initial packed_data_32 = {<<$random{byte_in}};
|
||||
initial begin
|
||||
packed_data_32 = {<<$random{byte_in}};
|
||||
packed_data_32 = {<<x{byte_in}};
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,490 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`define stop $stop
|
||||
`define checkh(gotv,
|
||||
expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`define checks(gotv,
|
||||
expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
|
||||
module t ( /*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
typedef bit bit_q_t[$]; // CData (1-bit)
|
||||
typedef byte byte_q_t[$]; // CData (8-bit)
|
||||
typedef logic [7:0] cdata_q_t[$]; // CData (8-bit)
|
||||
typedef shortint sdata_q_t[$]; // SData (16-bit)
|
||||
typedef logic [15:0] sdata_logic_q_t[$]; // SData (16-bit)
|
||||
typedef int idata_q_t[$]; // IData (32-bit)
|
||||
typedef logic [31:0] idata_logic_q_t[$]; // IData (32-bit)
|
||||
typedef longint qdata_q_t[$]; // QData (64-bit)
|
||||
typedef logic [63:0] qdata_logic_q_t[$]; // QData (64-bit)
|
||||
typedef logic [127:0] wide_q_t[$]; // VlWide (128-bit)
|
||||
|
||||
input clk;
|
||||
integer cyc = 0;
|
||||
logic [7:0] d;
|
||||
string s;
|
||||
|
||||
initial begin
|
||||
begin
|
||||
bit_q_t bit_q, bit_qq;
|
||||
|
||||
bit_q.push_back(1'b0);
|
||||
bit_q.push_back(1'b1);
|
||||
bit_q.push_back(1'b1);
|
||||
`checkh(bit_q[0], 1'b0);
|
||||
`checkh(bit_q[1], 1'b1);
|
||||
`checkh(bit_q[2], 1'b1);
|
||||
|
||||
bit_q = bit_q_t'(4'he);
|
||||
`checkh(bit_q[0], 1'b1);
|
||||
`checkh(bit_q[1], 1'b1);
|
||||
`checkh(bit_q[2], 1'b1);
|
||||
`checkh(bit_q[3], 1'b0);
|
||||
|
||||
bit_q = bit_q_t'(4'h3);
|
||||
bit_qq = bit_q;
|
||||
`checkh(bit_qq[0], 1'b0);
|
||||
`checkh(bit_qq[1], 1'b0);
|
||||
`checkh(bit_qq[2], 1'b1);
|
||||
`checkh(bit_qq[3], 1'b1);
|
||||
|
||||
bit_q = bit_q_t'(4'h2);
|
||||
bit_qq = bit_q_t'(bit_q);
|
||||
`checkh(bit_qq[0], 1'b0);
|
||||
`checkh(bit_qq[1], 1'b0);
|
||||
`checkh(bit_qq[2], 1'b1);
|
||||
`checkh(bit_qq[3], 1'b0);
|
||||
|
||||
bit_q = bit_q_t'({>>{4'hd}});
|
||||
`checkh(bit_q[0], 1'b1);
|
||||
`checkh(bit_q[1], 1'b1);
|
||||
`checkh(bit_q[2], 1'b0);
|
||||
`checkh(bit_q[3], 1'b1);
|
||||
|
||||
bit_q = bit_q_t'({<<{4'hc}});
|
||||
`checkh(bit_q[0], 1'b0);
|
||||
`checkh(bit_q[1], 1'b0);
|
||||
`checkh(bit_q[2], 1'b1);
|
||||
`checkh(bit_q[3], 1'b1);
|
||||
|
||||
bit_q = {>>{bit_q_t'(4'he)}};
|
||||
`checkh(bit_q[0], 1'b1);
|
||||
`checkh(bit_q[1], 1'b1);
|
||||
`checkh(bit_q[2], 1'b1);
|
||||
`checkh(bit_q[3], 1'b0);
|
||||
|
||||
bit_q = {<<{bit_q_t'(4'hd)}};
|
||||
`checkh(bit_q[0], 1'b1);
|
||||
`checkh(bit_q[1], 1'b0);
|
||||
`checkh(bit_q[2], 1'b1);
|
||||
`checkh(bit_q[3], 1'b1);
|
||||
|
||||
bit_qq = {>>{bit_q}};
|
||||
`checkh(bit_qq[0], 1'b1);
|
||||
`checkh(bit_qq[1], 1'b0);
|
||||
`checkh(bit_qq[2], 1'b1);
|
||||
`checkh(bit_qq[3], 1'b1);
|
||||
|
||||
bit_qq = {<<{bit_q}};
|
||||
`checkh(bit_qq[0], 1'b1);
|
||||
`checkh(bit_qq[1], 1'b1);
|
||||
`checkh(bit_qq[2], 1'b0);
|
||||
`checkh(bit_qq[3], 1'b1);
|
||||
|
||||
bit_q = bit_q_t'({>>{4'hd}});
|
||||
`checkh(bit_q[0], 1'b1);
|
||||
`checkh(bit_q[1], 1'b1);
|
||||
`checkh(bit_q[2], 1'b0);
|
||||
`checkh(bit_q[3], 1'b1);
|
||||
|
||||
bit_q = bit_q_t'({>>2{4'hd}});
|
||||
`checkh(bit_q[0], 1'b1);
|
||||
`checkh(bit_q[1], 1'b1);
|
||||
`checkh(bit_q[2], 1'b0);
|
||||
`checkh(bit_q[3], 1'b1);
|
||||
|
||||
bit_qq = bit_q_t'({>>{bit_q}});
|
||||
`checkh(bit_qq[0], 1'b1);
|
||||
`checkh(bit_qq[1], 1'b1);
|
||||
`checkh(bit_qq[2], 1'b0);
|
||||
`checkh(bit_qq[3], 1'b1);
|
||||
|
||||
bit_qq = bit_q_t'({>>2{bit_q}});
|
||||
`checkh(bit_qq[0], 1'b1);
|
||||
`checkh(bit_qq[1], 1'b1);
|
||||
`checkh(bit_qq[2], 1'b0);
|
||||
`checkh(bit_qq[3], 1'b1);
|
||||
|
||||
bit_qq = bit_q_t'({<<{bit_q}});
|
||||
`checkh(bit_qq[0], 1'b1);
|
||||
`checkh(bit_qq[1], 1'b0);
|
||||
`checkh(bit_qq[2], 1'b1);
|
||||
`checkh(bit_qq[3], 1'b1);
|
||||
|
||||
bit_qq = {<<2{bit_qq}};
|
||||
`checkh(bit_qq[0], 1'b1);
|
||||
`checkh(bit_qq[1], 1'b1);
|
||||
`checkh(bit_qq[2], 1'b1);
|
||||
`checkh(bit_qq[3], 1'b0);
|
||||
|
||||
bit_qq = {<<2{bit_q_t'({<<{bit_q}})}};
|
||||
`checkh(bit_qq[0], 1'b1);
|
||||
`checkh(bit_qq[1], 1'b1);
|
||||
`checkh(bit_qq[2], 1'b1);
|
||||
`checkh(bit_qq[3], 1'b0);
|
||||
end
|
||||
|
||||
begin
|
||||
cdata_q_t cdata_q, cdata_qq;
|
||||
|
||||
cdata_q = cdata_q_t'(32'hdeadbeef);
|
||||
`checkh(cdata_q[0], 8'hde);
|
||||
`checkh(cdata_q[1], 8'had);
|
||||
`checkh(cdata_q[2], 8'hbe);
|
||||
`checkh(cdata_q[3], 8'hef);
|
||||
|
||||
cdata_qq = cdata_q_t'({<<{cdata_q}});
|
||||
`checkh(cdata_qq[0], 8'hf7);
|
||||
`checkh(cdata_qq[1], 8'h7d);
|
||||
`checkh(cdata_qq[2], 8'hb5);
|
||||
`checkh(cdata_qq[3], 8'h7b);
|
||||
|
||||
cdata_qq = {<<2{cdata_q}};
|
||||
`checkh(cdata_qq[0], 8'hfb);
|
||||
`checkh(cdata_qq[1], 8'hbe);
|
||||
`checkh(cdata_qq[2], 8'h7a);
|
||||
`checkh(cdata_qq[3], 8'hb7);
|
||||
end
|
||||
|
||||
begin
|
||||
sdata_logic_q_t sdata_q, sdata_qq;
|
||||
|
||||
sdata_q = sdata_logic_q_t'(64'hfeedface_deadbeef);
|
||||
`checkh(sdata_q[0], 16'hfeed);
|
||||
`checkh(sdata_q[1], 16'hface);
|
||||
`checkh(sdata_q[2], 16'hdead);
|
||||
`checkh(sdata_q[3], 16'hbeef);
|
||||
|
||||
sdata_qq = sdata_logic_q_t'({<<{sdata_q}});
|
||||
`checkh(sdata_qq[0], 16'hf77d);
|
||||
`checkh(sdata_qq[1], 16'hb57b);
|
||||
`checkh(sdata_qq[2], 16'h735f);
|
||||
`checkh(sdata_qq[3], 16'hb77f);
|
||||
|
||||
sdata_qq = {<<2{sdata_q}};
|
||||
`checkh(sdata_qq[0], 16'hfbbe);
|
||||
`checkh(sdata_qq[1], 16'h7ab7);
|
||||
`checkh(sdata_qq[2], 16'hb3af);
|
||||
`checkh(sdata_qq[3], 16'h7bbf);
|
||||
end
|
||||
|
||||
begin
|
||||
idata_logic_q_t idata_q, idata_qq;
|
||||
|
||||
idata_q = idata_logic_q_t'(64'h12345678_9abcdef0);
|
||||
`checkh(idata_q[0], 32'h12345678);
|
||||
`checkh(idata_q[1], 32'h9abcdef0);
|
||||
|
||||
idata_qq = idata_logic_q_t'({<<{idata_q}});
|
||||
`checkh(idata_qq[0], 32'h0f7b3d59);
|
||||
`checkh(idata_qq[1], 32'h1e6a2c48);
|
||||
|
||||
idata_q = idata_logic_q_t'(128'hfeedface_deadbeef_cafebabe_12345678);
|
||||
`checkh(idata_q[0], 32'hfeedface);
|
||||
`checkh(idata_q[1], 32'hdeadbeef);
|
||||
`checkh(idata_q[2], 32'hcafebabe);
|
||||
`checkh(idata_q[3], 32'h12345678);
|
||||
|
||||
idata_qq = {<<2{idata_logic_q_t'({<<{idata_q}})}};
|
||||
`checkh(idata_qq[0], 32'hfddef5cd);
|
||||
`checkh(idata_qq[1], 32'hed5e7ddf);
|
||||
`checkh(idata_qq[2], 32'hc5fd757d);
|
||||
`checkh(idata_qq[3], 32'h2138a9b4);
|
||||
end
|
||||
|
||||
begin
|
||||
qdata_logic_q_t qdata_q, qdata_qq;
|
||||
|
||||
qdata_q.push_back(64'hdeadbeef_cafebabe);
|
||||
qdata_q.push_back(64'hfeedface_12345678);
|
||||
`checkh(qdata_q[0], 64'hdeadbeef_cafebabe);
|
||||
`checkh(qdata_q[1], 64'hfeedface_12345678);
|
||||
|
||||
qdata_qq = qdata_logic_q_t'({<<{qdata_q}});
|
||||
`checkh(qdata_qq[0], 64'h1e6a2c48735fb77f);
|
||||
`checkh(qdata_qq[1], 64'h7d5d7f53f77db57b);
|
||||
|
||||
qdata_q.push_back(64'h1111222233334444);
|
||||
qdata_q.push_back(64'h5555666677778888);
|
||||
qdata_qq = {<<2{qdata_q}};
|
||||
`checkh(qdata_qq[0], 64'h2222dddd99995555);
|
||||
`checkh(qdata_qq[1], 64'h1111cccc88884444);
|
||||
`checkh(qdata_qq[2], 64'h2d951c84b3af7bbf);
|
||||
`checkh(qdata_qq[3], 64'hbeaebfa3fbbe7ab7);
|
||||
end
|
||||
|
||||
begin
|
||||
wide_q_t wide_q, wide_qq;
|
||||
|
||||
wide_q.push_back(128'hdeadbeef_cafebabe_feedface_12345678);
|
||||
wide_q.push_back(128'h11112222_33334444_55556666_77778888);
|
||||
`checkh(wide_q[0], 128'hdeadbeef_cafebabe_feedface_12345678);
|
||||
`checkh(wide_q[1], 128'h11112222_33334444_55556666_77778888);
|
||||
|
||||
wide_qq = wide_q_t'({<<{wide_q}});
|
||||
`checkh(wide_qq[0], 128'h1111eeee6666aaaa2222cccc44448888);
|
||||
`checkh(wide_qq[1], 128'h1e6a2c48735fb77f7d5d7f53f77db57b);
|
||||
|
||||
wide_q.push_back(128'haaaabbbb_ccccdddd_eeeeffff_00001111);
|
||||
wide_q.push_back(128'h22223333_44445555_66667777_88889999);
|
||||
|
||||
wide_qq = wide_q_t'({<<{wide_q}});
|
||||
wide_qq = {<<2{wide_q}};
|
||||
`checkh(wide_qq[0], 128'h66662222dddd999955551111cccc8888);
|
||||
`checkh(wide_qq[1], 128'h44440000ffffbbbb77773333eeeeaaaa);
|
||||
`checkh(wide_qq[2], 128'h2222dddd999955551111cccc88884444);
|
||||
`checkh(wide_qq[3], 128'h2d951c84b3af7bbfbeaebfa3fbbe7ab7);
|
||||
end
|
||||
|
||||
begin
|
||||
byte_q_t bytq_init;
|
||||
byte_q_t bytq;
|
||||
bit_q_t bitq;
|
||||
|
||||
bytq_init.push_back(8'h84);
|
||||
bytq_init.push_back(8'haa);
|
||||
`checkh(bytq_init[0], 8'h84);
|
||||
`checkh(bytq_init[1], 8'haa);
|
||||
s = $sformatf("bytq_init=%p", bytq_init);
|
||||
`checks(s, "bytq_init='{'h84, 'haa} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bitq=%p", bitq);
|
||||
`checks(s,
|
||||
"bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} ");
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa} ");
|
||||
|
||||
/*
|
||||
Generalized block-reversal semantics for the outer left-stream when blockSize > 1.
|
||||
This seemingly complicated approach is what is required to match commercial simulators,
|
||||
otherwise the straggler bit [1] in the padded byte might end up as 0x01 instead of 0x80.
|
||||
|
||||
Starting with result of inner {<<{bitq}}: [1,1,0,1,0,1,0,1,0,1,0,0,0,0,1,0,0] (17 bits),
|
||||
apply outer {<<8{...}} using generalized block-reversal like this:
|
||||
- Reverse all bits: [0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1]
|
||||
- Split into 8-bit blocks from left and pad incomplete blocks on the left:
|
||||
- Block 0: [0,0,1,0,0,0,0,1] (complete)
|
||||
- Block 1: [0,1,0,1,0,1,0,1] (complete)
|
||||
- Block 2: [1] -> pad on left -> [0,0,0,0,0,0,0,1]
|
||||
- Reverse bits within each 8-bit block:
|
||||
- Block 0: [0,0,1,0,0,0,0,1] -> [1,0,0,0,0,1,0,0] = 0x84
|
||||
- Block 1: [0,1,0,1,0,1,0,1] -> [1,0,1,0,1,0,1,0] = 0xaa
|
||||
- Block 2: [0,0,0,0,0,0,0,1] -> [1,0,0,0,0,0,0,0] = 0x80
|
||||
*/
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bitq=%p", bitq);
|
||||
`checks(s,
|
||||
"bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1} ");
|
||||
`checkh(bytq[0], 8'h84);
|
||||
`checkh(bytq[1], 8'haa);
|
||||
`checkh(bytq[2], 8'h80);
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'h80} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bitq=%p", bitq);
|
||||
`checks(s,
|
||||
"bitq='{'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h1, 'h1} ");
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'hc0} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'he0} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'h70} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b1);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'hb8} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'h5c} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b0);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'h2e} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b1);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'h97} ");
|
||||
|
||||
bytq = bytq_init;
|
||||
bitq = {<<8{bit_q_t'({<<{bytq}})}};
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b0);
|
||||
bitq.push_back(1'b1);
|
||||
bitq.push_back(1'b1);
|
||||
bytq = {<<8{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h84, 'haa, 'h97, 'h80} ");
|
||||
end
|
||||
|
||||
// Test StreamR (>>) operations - fairly simple since this should maintain left-to-right order.
|
||||
begin
|
||||
bit_q_t bitq;
|
||||
byte_q_t bytq;
|
||||
|
||||
bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0};
|
||||
bitq = {>>4{bit_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bitq=%p", bitq);
|
||||
`checks(s, "bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} ");
|
||||
|
||||
bytq = {8'h84, 8'haa};
|
||||
bitq = {>>{bit_q_t'({<<{bytq}})}};
|
||||
s = $sformatf("bitq=%p", bitq);
|
||||
`checks(s,
|
||||
"bitq='{'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1} ");
|
||||
|
||||
bitq = {
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b0,
|
||||
1'b0,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b0
|
||||
};
|
||||
bytq = {>>2{byte_q_t'({<<{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h43, 'h55} ");
|
||||
|
||||
bytq = {8'h12, 8'h34, 8'h56};
|
||||
bytq = {>>{byte_q_t'({<<{bytq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h6a, 'h2c, 'h48} ");
|
||||
|
||||
bitq = {1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0, 1'b1, 1'b0};
|
||||
bitq = {>>6{bit_q_t'({>>{bitq}})}};
|
||||
s = $sformatf("bitq=%p", bitq);
|
||||
`checks(s, "bitq='{'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} ");
|
||||
|
||||
bytq = {8'h84, 8'haa};
|
||||
bitq = {>>{bit_q_t'({>>{bytq}})}};
|
||||
s = $sformatf("bitq=%p", bitq);
|
||||
`checks(s,
|
||||
"bitq='{'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0} ");
|
||||
|
||||
bitq = {
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b1,
|
||||
1'b0,
|
||||
1'b0,
|
||||
1'b0,
|
||||
1'b0,
|
||||
1'b1,
|
||||
1'b0
|
||||
};
|
||||
bytq = {>>8{byte_q_t'({>>{bitq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'haa, 'hc2} ");
|
||||
|
||||
bytq = {8'h12, 8'h34, 8'h56};
|
||||
bytq = {>>{byte_q_t'({>>{bytq}})}};
|
||||
s = $sformatf("bytq=%p", bytq);
|
||||
`checks(s, "bytq='{'h12, 'h34, 'h56} ");
|
||||
end
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
|
@ -0,0 +1,105 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
`define stop $stop
|
||||
`define checkh(gotv,
|
||||
expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`define checks(gotv,
|
||||
expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
|
||||
typedef bit bit_q_t[$];
|
||||
|
||||
module t ( /*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
integer cyc = 0;
|
||||
reg [63:0] crc = '0;
|
||||
reg [63:0] sum = '0;
|
||||
|
||||
// Take CRC data and apply to testblock inputs
|
||||
wire [31:0] in = crc[31:0];
|
||||
|
||||
/*AUTOWIRE*/
|
||||
// Beginning of automatic wires (for undeclared instantiated-module outputs)
|
||||
wire [31:0] out; // From test of Test.v
|
||||
// End of automatics
|
||||
|
||||
Test test (
|
||||
.clk,
|
||||
.in,
|
||||
.out
|
||||
);
|
||||
|
||||
// Aggregate outputs into a single result vector
|
||||
wire [63:0] result = {32'h0, out};
|
||||
|
||||
initial begin
|
||||
byte unsigned p[$];
|
||||
byte unsigned po[$];
|
||||
bit bits[$];
|
||||
string s;
|
||||
|
||||
p = {8'h84, 8'haa};
|
||||
`checkh(p[0], 8'h84);
|
||||
`checkh(p[1], 8'haa);
|
||||
|
||||
bits = {<<8{bit_q_t'({<<{p}})}};
|
||||
bits.push_front(1'b0);
|
||||
po = {<<8{bit_q_t'({<<{bits}})}};
|
||||
|
||||
s = $sformatf("p=%p", p);
|
||||
`checks(s, "p='{'h84, 'haa} ");
|
||||
|
||||
s = $sformatf("bits=%p", bits);
|
||||
`checks(s,
|
||||
"bits='{'h0, 'h0, 'h0, 'h1, 'h0, 'h0, 'h0, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1, 'h0, 'h1} ");
|
||||
|
||||
s = $sformatf("po=%p", po);
|
||||
`checks(s, "po='{'h8, 'h55, 'h80} ");
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d crc=%x result=%x sum=%x\n", $time, cyc, crc, result, sum);
|
||||
`endif
|
||||
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};
|
||||
sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]};
|
||||
|
||||
if (cyc == 0) begin
|
||||
crc <= 64'h5aef0c8d_d70a4497;
|
||||
sum <= '0;
|
||||
end else if (cyc < 10) begin
|
||||
sum <= '0;
|
||||
end else if (cyc == 99) begin
|
||||
`checkh(crc, 64'hc77bb9b3784ea091);
|
||||
`checkh(sum, 64'h9721d4e989defb24);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module Test (
|
||||
input logic clk,
|
||||
input logic [31:0] in,
|
||||
output logic [31:0] out
|
||||
);
|
||||
byte unsigned p[$];
|
||||
byte unsigned po[$];
|
||||
bit bits[$];
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
p = {in[31:24], in[23:16], in[15:8], in[7:0]};
|
||||
bits = {<<8{bit_q_t'({<<{p}})}};
|
||||
bits.push_front(1'b0);
|
||||
po = {<<8{bit_q_t'({<<{bits}})}};
|
||||
out <= {po[3], po[2], po[1], po[0]};
|
||||
end
|
||||
endmodule
|
|
@ -280,8 +280,8 @@ module t (/*AUTOARG*/);
|
|||
o = {<<128{p}};
|
||||
`checkh(o, 256'habcd0123456789abfadecafedeadbeeffadecafedeadbeefabcd0123456789ab);
|
||||
{>>{p}} = o;
|
||||
`checkh(p[0], 128'hfadecafedeadbeefabcd0123456789ab);
|
||||
`checkh(p[1], 128'habcd0123456789abfadecafedeadbeef);
|
||||
`checkh(p[0], 128'habcd0123456789abfadecafedeadbeef);
|
||||
`checkh(p[1], 128'hfadecafedeadbeefabcd0123456789ab);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
|
|
Loading…
Reference in New Issue