Support unpacked struct stream (#7767)
This commit is contained in:
parent
e03fa6c783
commit
87d2610674
|
|
@ -170,6 +170,10 @@ public:
|
|||
void generic(bool flag) { m_generic = flag; }
|
||||
std::pair<uint32_t, uint32_t> dimensions(bool includeBasic) const;
|
||||
uint32_t arrayUnpackedElements() const; // 1, or total multiplication of all dimensions
|
||||
// Fixed aggregate streaming properties
|
||||
bool isStreamableFixedAggregate() const;
|
||||
bool containsUnpackedStruct() const;
|
||||
int widthStream() const;
|
||||
static int uniqueNumInc() { return ++s_uniqueNum; }
|
||||
const char* charIQWN() const {
|
||||
return (isString() ? "N" : isWide() ? "W" : isDouble() ? "D" : isQuad() ? "Q" : "I");
|
||||
|
|
|
|||
|
|
@ -1259,6 +1259,47 @@ uint32_t AstNodeDType::arrayUnpackedElements() const {
|
|||
return entries;
|
||||
}
|
||||
|
||||
bool AstNodeDType::isStreamableFixedAggregate() const {
|
||||
const AstNodeDType* const dtypep = skipRefp();
|
||||
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
return adtypep->subDTypep()->isStreamableFixedAggregate();
|
||||
} else if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||
if (sdtypep->packed()) return true;
|
||||
if (!VN_IS(sdtypep, StructDType)) return false;
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (!itemp->dtypep()->isStreamableFixedAggregate()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return dtypep->isIntegralOrPacked() || dtypep->isDouble();
|
||||
}
|
||||
|
||||
bool AstNodeDType::containsUnpackedStruct() const {
|
||||
const AstNodeDType* const dtypep = skipRefp();
|
||||
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
return adtypep->subDTypep()->containsUnpackedStruct();
|
||||
}
|
||||
const AstStructDType* const sdtypep = VN_CAST(dtypep, StructDType);
|
||||
return sdtypep && !sdtypep->packed();
|
||||
}
|
||||
|
||||
int AstNodeDType::widthStream() const {
|
||||
const AstNodeDType* const dtypep = skipRefp();
|
||||
if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
return adtypep->subDTypep()->widthStream() * adtypep->elementsConst();
|
||||
} else if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||
if (!VN_IS(sdtypep, StructDType) || sdtypep->packed()) return width();
|
||||
int width = 0;
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
width += itemp->dtypep()->widthStream();
|
||||
}
|
||||
return width;
|
||||
}
|
||||
return dtypep->width();
|
||||
}
|
||||
|
||||
std::pair<uint32_t, uint32_t> AstNodeDType::dimensions(bool includeBasic) const {
|
||||
// How many array dimensions (packed,unpacked) does this Var have?
|
||||
uint32_t packed = 0;
|
||||
|
|
|
|||
138
src/V3Const.cpp
138
src/V3Const.cpp
|
|
@ -973,6 +973,96 @@ class ConstVisitor final : public VNVisitor {
|
|||
return !numv.isNumber() ? numv : V3Number{nodep, nodep->width(), numv};
|
||||
}
|
||||
|
||||
static bool lowerAsFixedAggregate(const AstNodeDType* const dtypep) {
|
||||
return dtypep->isStreamableFixedAggregate() && dtypep->containsUnpackedStruct();
|
||||
}
|
||||
|
||||
AstStructSel* newStructSel(AstNodeExpr* const fromp, const AstMemberDType* const itemp) {
|
||||
AstStructSel* const selp = new AstStructSel{fromp->fileline(), fromp, itemp->name()};
|
||||
selp->dtypeFrom(itemp->dtypep());
|
||||
return selp;
|
||||
}
|
||||
|
||||
void collectFixedAggregateTerms(AstNodeExpr* const fromp, std::vector<AstNodeExpr*>& termps,
|
||||
const bool packReal) {
|
||||
const AstNodeDType* const dtypep = fromp->dtypep()->skipRefp();
|
||||
if (const AstUnpackArrayDType* const unpackDtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
const int left = unpackDtypep->left();
|
||||
const int right = unpackDtypep->right();
|
||||
const int step = left <= right ? 1 : -1;
|
||||
for (int idx = left;; idx += step) {
|
||||
AstArraySel* const selp
|
||||
= new AstArraySel{fromp->fileline(), fromp->cloneTreePure(false), idx};
|
||||
collectFixedAggregateTerms(selp, termps, packReal);
|
||||
if (idx == right) break;
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(fromp), fromp);
|
||||
} else if (const AstNodeUOrStructDType* const sdtypep
|
||||
= VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||
if (sdtypep->packed()) {
|
||||
termps.push_back(fromp);
|
||||
return;
|
||||
}
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
collectFixedAggregateTerms(newStructSel(fromp->cloneTreePure(false), itemp),
|
||||
termps, packReal);
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(fromp), fromp);
|
||||
} else if (packReal && dtypep->isDouble()) {
|
||||
termps.push_back(new AstRealToBits{fromp->fileline(), fromp});
|
||||
} else {
|
||||
termps.push_back(fromp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeExpr* packFixedAggregate(AstNodeExpr* const fromp) {
|
||||
std::vector<AstNodeExpr*> termps;
|
||||
collectFixedAggregateTerms(fromp, termps, true);
|
||||
UASSERT(!termps.empty(), "No stream terms");
|
||||
AstNodeExpr* resultp = termps[0];
|
||||
for (size_t i = 1; i < termps.size(); ++i) {
|
||||
resultp = new AstConcat{resultp->fileline(), resultp, termps[i]};
|
||||
}
|
||||
return resultp;
|
||||
}
|
||||
|
||||
void replaceAssignToFixedAggregate(AstNodeAssign* const nodep, AstNodeExpr* const dstp,
|
||||
AstNodeExpr* srcp) {
|
||||
const int dstWidth = dstp->dtypep()->widthStream();
|
||||
std::vector<AstNodeExpr*> termps;
|
||||
collectFixedAggregateTerms(dstp, termps, false);
|
||||
int srcWidth = srcp->width();
|
||||
if (srcWidth < dstWidth) {
|
||||
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
|
||||
extendp->dtypeSetLogicSized(dstWidth, VSigning::UNSIGNED);
|
||||
srcp = new AstShiftL{
|
||||
extendp->fileline(), extendp,
|
||||
new AstConst{extendp->fileline(), static_cast<uint32_t>(dstWidth - srcWidth)},
|
||||
dstWidth};
|
||||
srcWidth = dstWidth;
|
||||
}
|
||||
AstNodeAssign* newp = nullptr;
|
||||
int offset = 0;
|
||||
for (size_t i = 0; i < termps.size(); ++i) {
|
||||
AstNodeExpr* const termp = termps[i];
|
||||
const int width = termp->dtypep()->widthStream();
|
||||
const int lsb = srcWidth - offset - width;
|
||||
offset += width;
|
||||
AstNodeExpr* rhsp
|
||||
= new AstSel{srcp->fileline(), srcp->cloneTreePure(false), lsb, width};
|
||||
if (termp->dtypep()->skipRefp()->isDouble()) {
|
||||
rhsp = new AstBitsToRealD{rhsp->fileline(), rhsp};
|
||||
}
|
||||
AstNodeAssign* const assignp = nodep->cloneType(termp, rhsp);
|
||||
assignp->dtypeFrom(termp);
|
||||
newp = AstNode::addNext(newp, assignp);
|
||||
}
|
||||
nodep->addNextHere(newp);
|
||||
VL_DO_DANGLING(pushDeletep(srcp), srcp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
|
||||
bool operandConst(const AstNode* nodep) { return VN_IS(nodep, Const); }
|
||||
bool operandAsvConst(const AstNode* nodep) {
|
||||
// BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...)
|
||||
|
|
@ -2393,6 +2483,25 @@ 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(), CvtPackedToArray)
|
||||
&& lowerAsFixedAggregate(nodep->lhsp()->dtypep())) {
|
||||
AstCvtPackedToArray* const cvtp
|
||||
= VN_AS(nodep->rhsp(), CvtPackedToArray)->unlinkFrBack();
|
||||
AstNodeExpr* srcp = cvtp->fromp()->unlinkFrBack();
|
||||
if (lowerAsFixedAggregate(srcp->dtypep())) {
|
||||
srcp = packFixedAggregate(srcp);
|
||||
} else if (AstNodeStream* const streamp = VN_CAST(srcp, NodeStream)) {
|
||||
AstNodeExpr* const streamSrcp = streamp->lhsp();
|
||||
if (lowerAsFixedAggregate(streamSrcp->dtypep())) {
|
||||
AstNodeExpr* const packedp = packFixedAggregate(streamSrcp->unlinkFrBack());
|
||||
streamp->lhsp(packedp);
|
||||
streamp->dtypeSetLogicUnsized(packedp->width(), packedp->widthMin(),
|
||||
VSigning::UNSIGNED);
|
||||
}
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(cvtp), cvtp);
|
||||
replaceAssignToFixedAggregate(nodep, nodep->lhsp()->unlinkFrBack(), srcp);
|
||||
return true;
|
||||
} 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
|
||||
|
|
@ -2402,7 +2511,9 @@ class ConstVisitor final : public VNVisitor {
|
|||
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)) {
|
||||
if (lowerAsFixedAggregate(srcDTypep)) {
|
||||
srcp = packFixedAggregate(srcp);
|
||||
} else if (VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)) {
|
||||
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
||||
int srcElementBits = 0;
|
||||
if (const AstNodeDType* const elemDtp = srcDTypep->subDTypep()) {
|
||||
|
|
@ -2429,6 +2540,19 @@ class ConstVisitor final : public VNVisitor {
|
|||
srcp = new AstShiftL{srcp->fileline(), srcp,
|
||||
new AstConst{srcp->fileline(), offset}, packedBits};
|
||||
}
|
||||
if (!VN_IS(dstDTypep, UnpackArrayDType) && !VN_IS(dstDTypep, QueueDType)
|
||||
&& !VN_IS(dstDTypep, DynArrayDType)) {
|
||||
const int sWidth = srcp->width();
|
||||
const int dWidth = nodep->lhsp()->width();
|
||||
if (sWidth < dWidth) {
|
||||
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};
|
||||
}
|
||||
}
|
||||
nodep->rhsp(srcp);
|
||||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
|
|
@ -2438,7 +2562,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
AstNodeExpr* streamp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const dstp = VN_AS(streamp, StreamL)->lhsp()->unlinkFrBack();
|
||||
AstNodeDType* const dstDTypep = dstp->dtypep()->skipRefp();
|
||||
AstNodeExpr* const srcp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||
// Handle unpacked/queue/dynarray source -> queue/dynarray dest via
|
||||
// CvtArrayToArray (StreamL reverses, so reverse=true)
|
||||
|
|
@ -2466,6 +2590,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(pushDeletep(streamp), streamp);
|
||||
return true;
|
||||
}
|
||||
if (lowerAsFixedAggregate(srcDTypep)) srcp = packFixedAggregate(srcp);
|
||||
const int sWidth = srcp->width();
|
||||
const int dWidth = dstp->width();
|
||||
// Connect the rhs to the stream operator and update its width
|
||||
|
|
@ -2556,7 +2681,7 @@ class ConstVisitor final : public VNVisitor {
|
|||
} else {
|
||||
// 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).
|
||||
// LSBs are zero-filled (IEEE 1800-2023 11.4.14.3).
|
||||
if (!VN_IS(srcp->dtypep()->skipRefp(), QueueDType)) {
|
||||
AstExtend* const extendp = new AstExtend{srcp->fileline(), srcp};
|
||||
extendp->dtypeSetLogicSized(dWidth, VSigning::UNSIGNED);
|
||||
|
|
@ -2580,6 +2705,13 @@ class ConstVisitor final : public VNVisitor {
|
|||
AstNodeExpr* srcp = streamp->lhsp();
|
||||
const AstNodeDType* const srcDTypep = srcp->dtypep()->skipRefp();
|
||||
AstNodeDType* const dstDTypep = nodep->lhsp()->dtypep()->skipRefp();
|
||||
if (lowerAsFixedAggregate(srcDTypep)) {
|
||||
AstNodeExpr* const packedp = packFixedAggregate(srcp->unlinkFrBack());
|
||||
streamp->lhsp(packedp);
|
||||
streamp->dtypeSetLogicUnsized(packedp->width(), packedp->widthMin(),
|
||||
VSigning::UNSIGNED);
|
||||
srcp = packedp;
|
||||
}
|
||||
if ((VN_IS(srcDTypep, QueueDType) || VN_IS(srcDTypep, DynArrayDType)
|
||||
|| VN_IS(srcDTypep, UnpackArrayDType))) {
|
||||
if (VN_IS(dstDTypep, QueueDType) || VN_IS(dstDTypep, DynArrayDType)) {
|
||||
|
|
|
|||
|
|
@ -255,13 +255,6 @@ class WidthVisitor final : public VNVisitor {
|
|||
EXTEND_OFF // No extension
|
||||
};
|
||||
|
||||
int widthUnpacked(const AstNodeDType* const dtypep) {
|
||||
if (const AstUnpackArrayDType* const arrDtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
return arrDtypep->subDTypep()->width() * arrDtypep->arrayUnpackedElements();
|
||||
}
|
||||
return dtypep->width();
|
||||
}
|
||||
|
||||
static void packIfUnpacked(AstNodeExpr* const nodep) {
|
||||
if (AstUnpackArrayDType* const unpackDTypep = VN_CAST(nodep->dtypep(), UnpackArrayDType)) {
|
||||
const int elementsNum = unpackDTypep->arrayUnpackedElements();
|
||||
|
|
@ -274,6 +267,9 @@ class WidthVisitor final : public VNVisitor {
|
|||
nodep->findLogicDType(unpackBits, unpackMinBits, VSigning::UNSIGNED)});
|
||||
}
|
||||
}
|
||||
static bool lowerAsFixedAggregate(const AstNodeDType* const dtypep) {
|
||||
return dtypep->isStreamableFixedAggregate() && dtypep->containsUnpackedStruct();
|
||||
}
|
||||
// When fromp() is a DType (e.g. unlinked RefDType), resolve through
|
||||
// the ref chain; when it's an expression, dtypep() is already resolved.
|
||||
static AstNodeDType* fromDTypep(AstNode* fromp) {
|
||||
|
|
@ -979,7 +975,10 @@ class WidthVisitor final : public VNVisitor {
|
|||
}
|
||||
const AstNodeDType* const lhsDtypep = nodep->lhsp()->dtypep()->skipRefToEnump();
|
||||
if (VN_IS(lhsDtypep, DynArrayDType) || VN_IS(lhsDtypep, QueueDType)
|
||||
|| VN_IS(lhsDtypep, UnpackArrayDType)) {
|
||||
|| (VN_IS(lhsDtypep, UnpackArrayDType) && lhsDtypep->isStreamableFixedAggregate())
|
||||
|| (VN_IS(lhsDtypep, NodeUOrStructDType)
|
||||
&& !VN_AS(lhsDtypep, NodeUOrStructDType)->packed()
|
||||
&& lhsDtypep->isStreamableFixedAggregate())) {
|
||||
nodep->dtypeSetStream();
|
||||
} else if (lhsDtypep->isCompound()) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
|
|
@ -6295,14 +6294,14 @@ class WidthVisitor final : public VNVisitor {
|
|||
userIterateAndNext(nodep->rhsp(), WidthVP{nodep->dtypep(), PRELIM}.p());
|
||||
//
|
||||
// UINFOTREE(1, nodep, "", "assign");
|
||||
AstNodeDType* const lhsDTypep
|
||||
AstNodeDType* lhsDTypep
|
||||
= nodep->lhsp()->dtypep(); // Note we use rhsp for context determined
|
||||
|
||||
// Check width of stream and wrap if needed
|
||||
if (AstNodeStream* const streamp = VN_CAST(nodep->rhsp(), NodeStream)) {
|
||||
AstNodeDType* const lhsDTypeSkippedRefp = lhsDTypep->skipRefp();
|
||||
const int lwidth = widthUnpacked(lhsDTypeSkippedRefp);
|
||||
const int rwidth = widthUnpacked(streamp->lhsp()->dtypep()->skipRefp());
|
||||
const int lwidth = lhsDTypeSkippedRefp->widthStream();
|
||||
const int rwidth = streamp->lhsp()->dtypep()->skipRefp()->widthStream();
|
||||
if (lwidth != 0 && lwidth < rwidth) {
|
||||
nodep->v3widthWarn(lwidth, rwidth,
|
||||
"Target fixed size variable ("
|
||||
|
|
@ -6314,16 +6313,18 @@ class WidthVisitor final : public VNVisitor {
|
|||
const int queueElementSize = streamp->lhsp()->dtypep()->subDTypep()->width();
|
||||
UASSERT_OBJ(queueElementSize <= lwidth, nodep, "LHS < RHS");
|
||||
}
|
||||
if (VN_IS(lhsDTypeSkippedRefp, UnpackArrayDType)) {
|
||||
if (VN_IS(lhsDTypeSkippedRefp, UnpackArrayDType)
|
||||
|| lowerAsFixedAggregate(lhsDTypeSkippedRefp)) {
|
||||
streamp->unlinkFrBack();
|
||||
nodep->rhsp(new AstCvtPackedToArray{streamp->fileline(), streamp,
|
||||
lhsDTypeSkippedRefp});
|
||||
}
|
||||
}
|
||||
if (const AstNodeStream* const streamp = VN_CAST(nodep->lhsp(), NodeStream)) {
|
||||
if (AstNodeStream* const streamp = VN_CAST(nodep->lhsp(), NodeStream)) {
|
||||
const AstNodeDType* const rhsDTypep = nodep->rhsp()->dtypep()->skipRefp();
|
||||
const int lwidth = widthUnpacked(streamp->lhsp()->dtypep()->skipRefp());
|
||||
const int rwidth = widthUnpacked(rhsDTypep);
|
||||
AstNodeDType* const lhsStreamDTypep = streamp->lhsp()->dtypep()->skipRefp();
|
||||
const int lwidth = lhsStreamDTypep->widthStream();
|
||||
const int rwidth = rhsDTypep->widthStream();
|
||||
if (rwidth != 0 && rwidth < lwidth) {
|
||||
nodep->v3widthWarn(lwidth, rwidth,
|
||||
"Stream target requires "
|
||||
|
|
@ -6331,7 +6332,27 @@ class WidthVisitor final : public VNVisitor {
|
|||
<< " bits, but source expression only provides "
|
||||
<< rwidth << " bits (IEEE 1800-2023 11.4.14.3)");
|
||||
}
|
||||
if (VN_IS(rhsDTypep, UnpackArrayDType)) {
|
||||
if (lowerAsFixedAggregate(lhsStreamDTypep)) {
|
||||
AstNodeExpr* const streamExprp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const dstp = streamp->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
if (VN_IS(streamp, StreamL)) {
|
||||
streamp->lhsp(srcp);
|
||||
streamp->dtypeSetLogicUnsized(srcp->width(), srcp->widthMin(),
|
||||
VSigning::UNSIGNED);
|
||||
srcp = streamExprp;
|
||||
} else {
|
||||
if (srcp->width() > lwidth) {
|
||||
srcp = new AstSel{streamp->fileline(), srcp, srcp->width() - lwidth,
|
||||
lwidth};
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(streamExprp), streamExprp);
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(new AstCvtPackedToArray{srcp->fileline(), srcp, lhsStreamDTypep});
|
||||
nodep->dtypeFrom(dstp);
|
||||
lhsDTypep = nodep->lhsp()->dtypep();
|
||||
} else if (VN_IS(rhsDTypep, UnpackArrayDType)) {
|
||||
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
nodep->rhsp(
|
||||
new AstCvtArrayToPacked{rhsp->fileline(), rhsp, streamp->dtypep()});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/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: 2026 Wilson Snyder
|
||||
# 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,347 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
// Ref. to IEEE 1800-2023 11.4.14, A.8.1
|
||||
|
||||
module t;
|
||||
|
||||
`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);
|
||||
|
||||
typedef struct packed {
|
||||
logic [3:0] hi;
|
||||
logic [3:0] lo;
|
||||
} packed_pair_t;
|
||||
|
||||
typedef union packed {
|
||||
logic [7:0] byte_data;
|
||||
packed_pair_t pair;
|
||||
} packed_union_t;
|
||||
|
||||
typedef struct {
|
||||
byte a;
|
||||
logic [3:0] b;
|
||||
packed_pair_t p;
|
||||
} simple_t;
|
||||
|
||||
typedef struct {
|
||||
byte prefix;
|
||||
byte data[2];
|
||||
packed_pair_t p;
|
||||
} array_t;
|
||||
|
||||
typedef struct {
|
||||
simple_t inner;
|
||||
byte tail[2];
|
||||
} nested_t;
|
||||
|
||||
typedef struct {
|
||||
byte prefix;
|
||||
simple_t items[2];
|
||||
byte suffix;
|
||||
} struct_array_t;
|
||||
|
||||
typedef struct {
|
||||
byte data[1:0];
|
||||
} descending_array_t;
|
||||
|
||||
typedef struct {
|
||||
byte matrix[2][3];
|
||||
} matrix_array_t;
|
||||
|
||||
typedef struct {
|
||||
logic [1:0][3:0] data[2];
|
||||
} mixed_array_t;
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
E0 = 3'd0,
|
||||
E5 = 3'd5
|
||||
} enum_t;
|
||||
|
||||
typedef struct {
|
||||
enum_t e;
|
||||
logic [4:0] x;
|
||||
} enum_struct_t;
|
||||
|
||||
typedef struct {
|
||||
byte tag;
|
||||
real r;
|
||||
realtime rt;
|
||||
real ra[2];
|
||||
} real_struct_t;
|
||||
|
||||
typedef struct {
|
||||
packed_union_t u;
|
||||
byte tail;
|
||||
} packed_union_struct_t;
|
||||
|
||||
simple_t simple;
|
||||
simple_t simple_out;
|
||||
simple_t simple_cont_out;
|
||||
array_t arrayed;
|
||||
array_t arrayed_out;
|
||||
nested_t nested;
|
||||
nested_t nested_out;
|
||||
struct_array_t struct_array;
|
||||
struct_array_t struct_array_out;
|
||||
descending_array_t descending;
|
||||
descending_array_t descending_out;
|
||||
matrix_array_t matrix_array;
|
||||
matrix_array_t matrix_array_out;
|
||||
mixed_array_t mixed_array;
|
||||
mixed_array_t mixed_array_out;
|
||||
simple_t simple_array[2];
|
||||
simple_t simple_array_out[2];
|
||||
enum_struct_t enum_struct;
|
||||
enum_struct_t enum_struct_out;
|
||||
real_struct_t real_struct;
|
||||
real_struct_t real_struct_out;
|
||||
packed_union_struct_t packed_union_struct;
|
||||
packed_union_struct_t packed_union_struct_out;
|
||||
|
||||
logic [19:0] simple_bits;
|
||||
logic [31:0] wide_simple_bits;
|
||||
logic [31:0] array_bits;
|
||||
logic [35:0] nested_bits;
|
||||
logic [55:0] struct_array_bits;
|
||||
logic [15:0] descending_bits;
|
||||
logic [47:0] matrix_array_bits;
|
||||
logic [15:0] mixed_array_bits;
|
||||
logic [39:0] simple_array_bits;
|
||||
logic [31:0] byteswapped_bits;
|
||||
logic [7:0] enum_bits;
|
||||
logic [263:0] real_bits;
|
||||
logic [15:0] packed_union_bits;
|
||||
logic [11:0] narrow_bits;
|
||||
logic [19:0] simple_streaml_src;
|
||||
byte byte_array_out[2];
|
||||
|
||||
assign {>>{simple_cont_out}} = 20'habcde;
|
||||
|
||||
initial begin
|
||||
byteswapped_bits = {<<8{32'h11223344}};
|
||||
`checkh(byteswapped_bits, 32'h44332211);
|
||||
byteswapped_bits = {<<byte{32'h11223344}};
|
||||
`checkh(byteswapped_bits, 32'h44332211);
|
||||
|
||||
{>>{simple_bits}} = 20'h13579;
|
||||
`checkh(simple_bits, 20'h13579);
|
||||
{<<4{simple_bits}} = 20'h12345;
|
||||
`checkh(simple_bits, 20'h54321);
|
||||
|
||||
simple = '{8'h12, 4'ha, '{4'hb, 4'hc}};
|
||||
simple_bits = {>>{simple}};
|
||||
`checkh(simple_bits, 20'h12abc);
|
||||
/* verilator lint_off WIDTHEXPAND */
|
||||
wide_simple_bits = {>>{simple}};
|
||||
/* verilator lint_on WIDTHEXPAND */
|
||||
`checkh(wide_simple_bits, 32'h12abc000);
|
||||
simple_bits = {<<4{simple}};
|
||||
`checkh(simple_bits, 20'hcba21);
|
||||
|
||||
{>>{simple_out}} = 20'h345de;
|
||||
`checkh(simple_out.a, 8'h34);
|
||||
`checkh(simple_out.b, 4'h5);
|
||||
`checkh(simple_out.p, 8'hde);
|
||||
`checkh(simple_cont_out.a, 8'hab);
|
||||
`checkh(simple_cont_out.b, 4'hc);
|
||||
`checkh(simple_cont_out.p, 8'hde);
|
||||
|
||||
{<<4{simple_out}} = 20'h789ab;
|
||||
`checkh(simple_out.a, 8'hba);
|
||||
`checkh(simple_out.b, 4'h9);
|
||||
`checkh(simple_out.p, 8'h87);
|
||||
|
||||
simple_out = {>>{20'hfedcb}};
|
||||
`checkh(simple_out.a, 8'hfe);
|
||||
`checkh(simple_out.b, 4'hd);
|
||||
`checkh(simple_out.p, 8'hcb);
|
||||
|
||||
simple_out = {>>{simple}};
|
||||
`checkh(simple_out.a, 8'h12);
|
||||
`checkh(simple_out.b, 4'ha);
|
||||
`checkh(simple_out.p, 8'hbc);
|
||||
|
||||
{>>{simple_out}} = simple;
|
||||
`checkh(simple_out.a, 8'h12);
|
||||
`checkh(simple_out.b, 4'ha);
|
||||
`checkh(simple_out.p, 8'hbc);
|
||||
|
||||
if ($test$plusargs("t_stream_unpacked_struct_alt")) begin
|
||||
narrow_bits = 12'h123;
|
||||
end else begin
|
||||
narrow_bits = 12'habd;
|
||||
end
|
||||
/* verilator lint_off WIDTHEXPAND */
|
||||
simple_bits = {>>{narrow_bits}};
|
||||
/* verilator lint_on WIDTHEXPAND */
|
||||
`checkh(simple_bits, {narrow_bits, 8'h00});
|
||||
|
||||
simple_out = {>>{narrow_bits}};
|
||||
`checkh(simple_out.a, narrow_bits[11:4]);
|
||||
`checkh(simple_out.b, narrow_bits[3:0]);
|
||||
`checkh(simple_out.p, 8'h00);
|
||||
|
||||
{>>{simple_out}} = 24'habcdef;
|
||||
`checkh(simple_out.a, 8'hab);
|
||||
`checkh(simple_out.b, 4'hc);
|
||||
`checkh(simple_out.p, 8'hde);
|
||||
|
||||
simple_out = {<<4{20'h13579}};
|
||||
`checkh(simple_out.a, 8'h97);
|
||||
`checkh(simple_out.b, 4'h5);
|
||||
`checkh(simple_out.p, 8'h31);
|
||||
|
||||
simple_streaml_src = 20'h2468a;
|
||||
simple_out = {<<4{simple_streaml_src}};
|
||||
`checkh(simple_out.a, 8'ha8);
|
||||
`checkh(simple_out.b, 4'h6);
|
||||
`checkh(simple_out.p, 8'h42);
|
||||
|
||||
{<<4{simple_out}} = simple_streaml_src;
|
||||
`checkh(simple_out.a, 8'ha8);
|
||||
`checkh(simple_out.b, 4'h6);
|
||||
`checkh(simple_out.p, 8'h42);
|
||||
|
||||
{<<8{byte_array_out}} = 16'h1234;
|
||||
`checkh(byte_array_out[0], 8'h34);
|
||||
`checkh(byte_array_out[1], 8'h12);
|
||||
|
||||
arrayed = '{8'h11, '{8'h22, 8'h33}, '{4'h4, 4'h5}};
|
||||
array_bits = {>>{arrayed}};
|
||||
`checkh(array_bits, 32'h11223345);
|
||||
array_bits = {<<8{arrayed}};
|
||||
`checkh(array_bits, 32'h45332211);
|
||||
|
||||
{>>{arrayed_out}} = 32'haabbccdd;
|
||||
`checkh(arrayed_out.prefix, 8'haa);
|
||||
`checkh(arrayed_out.data[0], 8'hbb);
|
||||
`checkh(arrayed_out.data[1], 8'hcc);
|
||||
`checkh(arrayed_out.p, 8'hdd);
|
||||
|
||||
nested = '{'{8'h12, 4'ha, '{4'hb, 4'hc}}, '{8'h34, 8'h56}};
|
||||
nested_bits = {>>{nested}};
|
||||
`checkh(nested_bits, 36'h12abc3456);
|
||||
|
||||
{>>{nested_out}} = 36'h6543210fe;
|
||||
`checkh(nested_out.inner.a, 8'h65);
|
||||
`checkh(nested_out.inner.b, 4'h4);
|
||||
`checkh(nested_out.inner.p, 8'h32);
|
||||
`checkh(nested_out.tail[0], 8'h10);
|
||||
`checkh(nested_out.tail[1], 8'hfe);
|
||||
|
||||
struct_array.prefix = 8'haa;
|
||||
struct_array.items[0] = '{8'h12, 4'h3, '{4'h4, 4'h5}};
|
||||
struct_array.items[1] = '{8'h67, 4'h8, '{4'h9, 4'ha}};
|
||||
struct_array.suffix = 8'hbb;
|
||||
struct_array_bits = {>>{struct_array}};
|
||||
`checkh(struct_array_bits, 56'haa123456789abb);
|
||||
|
||||
{>>{struct_array_out}} = 56'hccdef0123456dd;
|
||||
`checkh(struct_array_out.prefix, 8'hcc);
|
||||
`checkh(struct_array_out.items[0].a, 8'hde);
|
||||
`checkh(struct_array_out.items[0].b, 4'hf);
|
||||
`checkh(struct_array_out.items[0].p, 8'h01);
|
||||
`checkh(struct_array_out.items[1].a, 8'h23);
|
||||
`checkh(struct_array_out.items[1].b, 4'h4);
|
||||
`checkh(struct_array_out.items[1].p, 8'h56);
|
||||
`checkh(struct_array_out.suffix, 8'hdd);
|
||||
|
||||
descending = '{data: '{8'hcc, 8'hdd}};
|
||||
descending_bits = {>>{descending}};
|
||||
`checkh(descending_bits, 16'hccdd);
|
||||
|
||||
{>>{descending_out}} = 16'h1234;
|
||||
`checkh(descending_out.data[1], 8'h12);
|
||||
`checkh(descending_out.data[0], 8'h34);
|
||||
|
||||
matrix_array.matrix[0][0] = 8'h11;
|
||||
matrix_array.matrix[0][1] = 8'h22;
|
||||
matrix_array.matrix[0][2] = 8'h33;
|
||||
matrix_array.matrix[1][0] = 8'h44;
|
||||
matrix_array.matrix[1][1] = 8'h55;
|
||||
matrix_array.matrix[1][2] = 8'h66;
|
||||
matrix_array_bits = {>>{matrix_array}};
|
||||
`checkh(matrix_array_bits, 48'h112233445566);
|
||||
|
||||
{>>{matrix_array_out}} = 48'haabbccddeeff;
|
||||
`checkh(matrix_array_out.matrix[0][0], 8'haa);
|
||||
`checkh(matrix_array_out.matrix[0][1], 8'hbb);
|
||||
`checkh(matrix_array_out.matrix[0][2], 8'hcc);
|
||||
`checkh(matrix_array_out.matrix[1][0], 8'hdd);
|
||||
`checkh(matrix_array_out.matrix[1][1], 8'hee);
|
||||
`checkh(matrix_array_out.matrix[1][2], 8'hff);
|
||||
|
||||
mixed_array.data[0] = 8'hab;
|
||||
mixed_array.data[1] = 8'hcd;
|
||||
mixed_array_bits = {>>{mixed_array}};
|
||||
`checkh(mixed_array_bits, 16'habcd);
|
||||
|
||||
{>>{mixed_array_out}} = 16'h1234;
|
||||
`checkh(mixed_array_out.data[0], 8'h12);
|
||||
`checkh(mixed_array_out.data[0][1], 4'h1);
|
||||
`checkh(mixed_array_out.data[0][0], 4'h2);
|
||||
`checkh(mixed_array_out.data[1], 8'h34);
|
||||
`checkh(mixed_array_out.data[1][1], 4'h3);
|
||||
`checkh(mixed_array_out.data[1][0], 4'h4);
|
||||
|
||||
simple_array[0] = '{8'h12, 4'ha, '{4'hb, 4'hc}};
|
||||
simple_array[1] = '{8'h34, 4'hd, '{4'he, 4'hf}};
|
||||
simple_array_bits = {>>{simple_array}};
|
||||
`checkh(simple_array_bits, 40'h12abc34def);
|
||||
|
||||
{>>{simple_array_out}} = 40'h567899abcd;
|
||||
`checkh(simple_array_out[0].a, 8'h56);
|
||||
`checkh(simple_array_out[0].b, 4'h7);
|
||||
`checkh(simple_array_out[0].p, 8'h89);
|
||||
`checkh(simple_array_out[1].a, 8'h9a);
|
||||
`checkh(simple_array_out[1].b, 4'hb);
|
||||
`checkh(simple_array_out[1].p, 8'hcd);
|
||||
|
||||
enum_struct = '{E5, 5'ha};
|
||||
enum_bits = {>>{enum_struct}};
|
||||
`checkh(enum_bits, 8'haa);
|
||||
|
||||
{>>{enum_struct_out}} = 8'h71;
|
||||
`checkh(enum_struct_out.e, 3'h3);
|
||||
`checkh(enum_struct_out.x, 5'h11);
|
||||
|
||||
real_struct.tag = 8'h42;
|
||||
real_struct.r = 1.0;
|
||||
real_struct.rt = 3.0;
|
||||
real_struct.ra[0] = 4.0;
|
||||
real_struct.ra[1] = 5.0;
|
||||
real_bits = {>>{real_struct}};
|
||||
`checkh(real_bits,
|
||||
{8'h42, $realtobits(1.0), $realtobits(3.0), $realtobits(4.0), $realtobits(5.0)});
|
||||
|
||||
{>>{real_struct_out}}
|
||||
= {8'h99, $realtobits(2.0), $realtobits(6.0), $realtobits(7.0), $realtobits(8.0)};
|
||||
`checkh(real_struct_out.tag, 8'h99);
|
||||
`checkh($realtobits(real_struct_out.r), $realtobits(2.0));
|
||||
`checkh($realtobits(real_struct_out.rt), $realtobits(6.0));
|
||||
`checkh($realtobits(real_struct_out.ra[0]), $realtobits(7.0));
|
||||
`checkh($realtobits(real_struct_out.ra[1]), $realtobits(8.0));
|
||||
|
||||
packed_union_struct.u.byte_data = 8'hbe;
|
||||
packed_union_struct.tail = 8'hef;
|
||||
packed_union_bits = {>>{packed_union_struct}};
|
||||
`checkh(packed_union_bits, 16'hbeef);
|
||||
|
||||
{>>{packed_union_struct_out}} = 16'hcafe;
|
||||
`checkh(packed_union_struct_out.u.byte_data, 8'hca);
|
||||
`checkh(packed_union_struct_out.tail, 8'hfe);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
%Error-UNSUPPORTED: t/t_stream_unpacked_struct_bad.v:54:12: Unsupported: Stream operation on a variable of a type 'struct{}t.queue_struct_t'
|
||||
: ... note: In instance 't'
|
||||
54 | out = {>>{queue_struct}};
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_stream_unpacked_struct_bad.v:55:12: Unsupported: Stream operation on a variable of a type 'struct{}t.dynamic_struct_t'
|
||||
: ... note: In instance 't'
|
||||
55 | out = {>>{dynamic_struct}};
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_stream_unpacked_struct_bad.v:56:12: Unsupported: Stream operation on a variable of a type 'struct{}t.associative_struct_t'
|
||||
: ... note: In instance 't'
|
||||
56 | out = {>>{associative_struct}};
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_stream_unpacked_struct_bad.v:57:12: Unsupported: Stream operation on a variable of a type 'struct{}t.union_struct_t'
|
||||
: ... note: In instance 't'
|
||||
57 | out = {>>{union_struct}};
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_stream_unpacked_struct_bad.v:58:12: Unsupported: Stream operation on a variable of a type 'struct{}t.string_struct_t'
|
||||
: ... note: In instance 't'
|
||||
58 | out = {>>{string_struct}};
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_stream_unpacked_struct_bad.v:59:12: Unsupported: Stream operation on a variable of a type 'struct{}t.chandle_struct_t'
|
||||
: ... note: In instance 't'
|
||||
59 | out = {>>{chandle_struct}};
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_stream_unpacked_struct_bad.v:60:12: Unsupported: Stream operation on a variable of a type 'struct{}t.event_struct_t'
|
||||
: ... note: In instance 't'
|
||||
60 | out = {>>{event_struct}};
|
||||
| ^~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/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: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('linter')
|
||||
|
||||
test.lint(fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify it
|
||||
// under the terms of either the GNU Lesser General Public License Version 3
|
||||
// or the Perl Artistic License Version 2.0.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
module t;
|
||||
|
||||
typedef struct {
|
||||
byte bytes[$];
|
||||
} queue_struct_t;
|
||||
|
||||
typedef struct {
|
||||
byte bytes[];
|
||||
} dynamic_struct_t;
|
||||
|
||||
typedef struct {
|
||||
byte bytes[int];
|
||||
} associative_struct_t;
|
||||
|
||||
typedef union {
|
||||
byte a;
|
||||
logic [15:0] b;
|
||||
} unpacked_union_t;
|
||||
|
||||
typedef struct {
|
||||
unpacked_union_t u;
|
||||
} union_struct_t;
|
||||
|
||||
typedef struct {
|
||||
string s;
|
||||
} string_struct_t;
|
||||
|
||||
typedef struct {
|
||||
chandle c;
|
||||
} chandle_struct_t;
|
||||
|
||||
typedef struct {
|
||||
event e;
|
||||
} event_struct_t;
|
||||
|
||||
logic [255:0] out;
|
||||
queue_struct_t queue_struct;
|
||||
dynamic_struct_t dynamic_struct;
|
||||
associative_struct_t associative_struct;
|
||||
union_struct_t union_struct;
|
||||
string_struct_t string_struct;
|
||||
chandle_struct_t chandle_struct;
|
||||
event_struct_t event_struct;
|
||||
|
||||
initial begin
|
||||
out = {>>{queue_struct}};
|
||||
out = {>>{dynamic_struct}};
|
||||
out = {>>{associative_struct}};
|
||||
out = {>>{union_struct}};
|
||||
out = {>>{string_struct}};
|
||||
out = {>>{chandle_struct}};
|
||||
out = {>>{event_struct}};
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue