forked from github/verilator
Support streaming operators, bug649.
Signed-off-by: Wilson Snyder <wsnyder@wsnyder.org>
This commit is contained in:
parent
d04eb977c2
commit
d34275150c
2
Changes
2
Changes
@ -9,6 +9,8 @@ indicates the contributor was also the author of the fix; Thanks!
|
||||
|
||||
** Support '{} assignment pattern on arrays, bug355.
|
||||
|
||||
** Support streaming operators, bug649. [Glen Gibb]
|
||||
|
||||
*** Add --no-trace-params.
|
||||
|
||||
*** Add assertions on 'unique if', bug725. [Jeff Bush]
|
||||
|
@ -1358,6 +1358,122 @@ static inline WDataOutP VL_REPLICATE_WWI(int obits, int lbits, int, WDataOutP ow
|
||||
return(owp);
|
||||
}
|
||||
|
||||
// Left stream operator. Output will always be clean. LHS and RHS must be clean.
|
||||
// Special "fast" versions for slice sizes that are a power of 2. These use
|
||||
// shifts and masks to execute faster than the slower for-loop approach where a
|
||||
// subset of bits is copied in during each iteration.
|
||||
static inline IData VL_STREAML_FAST_III(int, int lbits, int, IData ld, IData rd_log2) {
|
||||
// Pre-shift bits in most-significant slice:
|
||||
//
|
||||
// If lbits is not a multiple of the slice size (i.e., lbits % rd != 0),
|
||||
// then we end up with a "gap" in our reversed result. For example, if we
|
||||
// have a 5-bit Verlilog signal (lbits=5) in an 8-bit C data type:
|
||||
//
|
||||
// ld = ---43210
|
||||
//
|
||||
// (where numbers are the Verilog signal bit numbers and '-' is an unused bit).
|
||||
// Executing the switch statement below with a slice size of two (rd=2,
|
||||
// rd_log2=1) produces:
|
||||
//
|
||||
// ret = 1032-400
|
||||
//
|
||||
// Pre-shifting the bits in the most-significant slice allows us to avoid
|
||||
// this gap in the shuffled data:
|
||||
//
|
||||
// ld_adjusted = --4-3210
|
||||
// ret = 10324---
|
||||
IData ret = ld;
|
||||
if (rd_log2) {
|
||||
vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits
|
||||
vluint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS)
|
||||
IData msbMask = VL_MASK_I(lbitsRem) << lbitsFloor; // mask to sel only bits in MSS
|
||||
ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem));
|
||||
}
|
||||
switch (rd_log2) {
|
||||
case 0:
|
||||
ret = ((ret >> 1) & VL_UL(0x55555555)) | ((ret & VL_UL(0x55555555)) << 1); // FALLTHRU
|
||||
case 1:
|
||||
ret = ((ret >> 2) & VL_UL(0x33333333)) | ((ret & VL_UL(0x33333333)) << 2); // FALLTHRU
|
||||
case 2:
|
||||
ret = ((ret >> 4) & VL_UL(0x0f0f0f0f)) | ((ret & VL_UL(0x0f0f0f0f)) << 4); // FALLTHRU
|
||||
case 3:
|
||||
ret = ((ret >> 8) & VL_UL(0x00ff00ff)) | ((ret & VL_UL(0x00ff00ff)) << 8); // FALLTHRU
|
||||
case 4:
|
||||
ret = ((ret >> 16) | (ret << 16));
|
||||
}
|
||||
return ret >> (VL_WORDSIZE - lbits);
|
||||
}
|
||||
|
||||
static inline QData VL_STREAML_FAST_QQI(int, int lbits, int, QData ld, IData rd_log2) {
|
||||
// Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III)
|
||||
QData ret = ld;
|
||||
if (rd_log2) {
|
||||
vluint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
|
||||
vluint32_t lbitsRem = lbits - lbitsFloor;
|
||||
QData msbMask = VL_MASK_Q(lbitsRem) << lbitsFloor;
|
||||
ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_ULL(1) << rd_log2) - lbitsRem));
|
||||
}
|
||||
switch (rd_log2) {
|
||||
case 0:
|
||||
ret = ((ret >> 1) & VL_ULL(0x5555555555555555)) | ((ret & VL_ULL(0x5555555555555555)) << 1); // FALLTHRU
|
||||
case 1:
|
||||
ret = ((ret >> 2) & VL_ULL(0x3333333333333333)) | ((ret & VL_ULL(0x3333333333333333)) << 2); // FALLTHRU
|
||||
case 2:
|
||||
ret = ((ret >> 4) & VL_ULL(0x0f0f0f0f0f0f0f0f)) | ((ret & VL_ULL(0x0f0f0f0f0f0f0f0f)) << 4); // FALLTHRU
|
||||
case 3:
|
||||
ret = ((ret >> 8) & VL_ULL(0x00ff00ff00ff00ff)) | ((ret & VL_ULL(0x00ff00ff00ff00ff)) << 8); // FALLTHRU
|
||||
case 4:
|
||||
ret = ((ret >> 16) & VL_ULL(0x0000ffff0000ffff)) | ((ret & VL_ULL(0x0000ffff0000ffff)) << 16); // FALLTHRU
|
||||
case 5:
|
||||
ret = ((ret >> 32) | (ret << 32));
|
||||
}
|
||||
return ret >> (VL_QUADSIZE - lbits);
|
||||
}
|
||||
|
||||
// Regular "slow" streaming operators
|
||||
static inline IData VL_STREAML_III(int, int lbits, int, IData ld, IData rd) {
|
||||
IData ret = 0;
|
||||
// Slice size should never exceed the lhs width
|
||||
int ssize = ((int)rd < lbits) ? ((int)rd) : lbits;
|
||||
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;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline QData VL_STREAML_QQI(int, int lbits, int, QData ld, IData rd) {
|
||||
QData ret = 0;
|
||||
// Slice size should never exceed the lhs width
|
||||
int ssize = ((int)rd < lbits) ? ((int)rd) : lbits;
|
||||
QData mask = VL_MASK_Q(rd);
|
||||
for (int istart=0; istart<lbits; istart+=rd) {
|
||||
int ostart=lbits-rd-istart;
|
||||
ostart = ostart > 0 ? ostart : 0;
|
||||
ret |= ((ld >> istart) & mask) << ostart;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline WDataOutP VL_STREAML_WWI(int, int lbits, int, WDataOutP owp, WDataInP lwp, IData rd) {
|
||||
VL_ZERO_RESET_W(lbits, owp);
|
||||
// Slice size should never exceed the lhs width
|
||||
int ssize = ((int)rd < lbits) ? ((int)rd) : 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++) {
|
||||
// Extract a single bit from lwp and shift it to the correct
|
||||
// location for owp.
|
||||
WData bit= ((lwp[VL_BITWORD_I(istart+sbit)] >> VL_BITBIT_I(istart+sbit)) & 1) << VL_BITBIT_I(ostart+sbit);
|
||||
owp[VL_BITWORD_I(ostart+sbit)] |= bit;
|
||||
}
|
||||
}
|
||||
return owp;
|
||||
}
|
||||
|
||||
// Because concats are common and wide, it's valuable to always have a clean output.
|
||||
// Thus we specify inputs must be clean, so we don't need to clean the output.
|
||||
// Note the bit shifts are always constants, so the adds in these constify out.
|
||||
|
10
src/V3Ast.h
10
src/V3Ast.h
@ -1676,6 +1676,16 @@ struct AstNodeSel : public AstNodeBiop {
|
||||
virtual bool hasDType() const { return true; }
|
||||
};
|
||||
|
||||
struct AstNodeStream : public AstNodeBiop {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
AstNodeStream(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
if (lhsp->dtypep()) {
|
||||
dtypeSetLogicSized(lhsp->dtypep()->width(), lhsp->dtypep()->width(), AstNumeric::UNSIGNED);
|
||||
}
|
||||
}
|
||||
ASTNODE_BASE_FUNCS(NodeStream)
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// Tasks/functions common handling
|
||||
|
||||
|
@ -4175,6 +4175,30 @@ struct AstReplicate : public AstNodeBiop {
|
||||
virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*2; }
|
||||
};
|
||||
struct AstStreamL : public AstNodeStream {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
AstStreamL(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeStream(fl, lhsp, rhsp) {}
|
||||
ASTNODE_NODE_FUNCS(StreamL, STREAML)
|
||||
virtual string emitVerilog() { return "%f{ << %r %k{%l} }"; }
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opStreamL(lhs,rhs); }
|
||||
virtual string emitC() { return "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)"; }
|
||||
virtual bool cleanOut() {return true;}
|
||||
virtual bool cleanLhs() {return true;} virtual bool cleanRhs() {return true;}
|
||||
virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*2; }
|
||||
};
|
||||
struct AstStreamR : public AstNodeStream {
|
||||
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
|
||||
AstStreamR(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeStream(fl, lhsp, rhsp) {}
|
||||
ASTNODE_NODE_FUNCS(StreamR, STREAMR)
|
||||
virtual string emitVerilog() { return "%f{ >> %r %k{%l} }"; }
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opAssign(lhs); }
|
||||
virtual string emitC() { return isWide() ? "VL_ASSIGN_W(%nw, %P, %li)" : "%li"; }
|
||||
virtual bool cleanOut() {return false;}
|
||||
virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;}
|
||||
virtual bool sizeMattersLhs() {return true;} virtual bool sizeMattersRhs() {return false;}
|
||||
virtual int instrCount() const { return widthInstrs()*2; }
|
||||
};
|
||||
struct AstBufIf1 : public AstNodeBiop {
|
||||
// lhs is enable, rhs is data to drive
|
||||
// Note unlike the Verilog bufif1() UDP, this allows any width; each lhsp bit enables respective rhsp bit
|
||||
|
@ -869,10 +869,10 @@ private:
|
||||
}
|
||||
if (debug()>=9) nodep->dumpTree(cout," Ass_old: ");
|
||||
// Unlink the stuff
|
||||
AstNode* lc1p = nodep->lhsp()->castConcat()->lhsp()->unlinkFrBack();
|
||||
AstNode* lc2p = nodep->lhsp()->castConcat()->rhsp()->unlinkFrBack();
|
||||
AstNode* conp = nodep->lhsp()->castConcat()->unlinkFrBack();
|
||||
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNode* lc1p = nodep->lhsp()->castConcat()->lhsp()->unlinkFrBack();
|
||||
AstNode* lc2p = nodep->lhsp()->castConcat()->rhsp()->unlinkFrBack();
|
||||
AstNode* conp = nodep->lhsp()->castConcat()->unlinkFrBack();
|
||||
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
||||
AstNode* rhs2p = rhsp->cloneTree(false);
|
||||
// Calc widths
|
||||
int lsb2 = 0;
|
||||
@ -941,6 +941,64 @@ private:
|
||||
// Further reduce, either node may have more reductions.
|
||||
return true;
|
||||
}
|
||||
else if (m_doV && nodep->rhsp()->castStreamR()) {
|
||||
// The right-streaming operator on rhs of assignment does not
|
||||
// change the order of bits. Eliminate stream but keep its lhsp
|
||||
// Unlink the stuff
|
||||
AstNode* srcp = nodep->rhsp()->castStreamR()->lhsp()->unlinkFrBack();
|
||||
AstNode* sizep = nodep->rhsp()->castStreamR()->rhsp()->unlinkFrBack();
|
||||
AstNode* streamp = nodep->rhsp()->castStreamR()->unlinkFrBack();
|
||||
nodep->rhsp(srcp);
|
||||
// Cleanup
|
||||
sizep->deleteTree(); sizep=NULL;
|
||||
streamp->deleteTree(); streamp=NULL;
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
}
|
||||
else if (m_doV && nodep->lhsp()->castStreamL()) {
|
||||
// Push the stream operator to the rhs of the assignment statement
|
||||
int dWidth = nodep->lhsp()->castStreamL()->lhsp()->width();
|
||||
int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNode* dstp = nodep->lhsp()->castStreamL()->lhsp()->unlinkFrBack();
|
||||
AstNode* streamp = nodep->lhsp()->castStreamL()->unlinkFrBack();
|
||||
AstNode* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
// Connect the rhs to the stream operator and update its width
|
||||
streamp->castStreamL()->lhsp(srcp);
|
||||
streamp->dtypeSetLogicSized((srcp->width()),
|
||||
(srcp->widthMin()),
|
||||
AstNumeric::UNSIGNED);
|
||||
// Shrink the RHS if necessary
|
||||
if (sWidth > dWidth) {
|
||||
streamp = new AstSel(streamp->fileline(), streamp, sWidth-dWidth, dWidth);
|
||||
}
|
||||
// Link the nodes back in
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(streamp);
|
||||
return true;
|
||||
}
|
||||
else if (m_doV && nodep->lhsp()->castStreamR()) {
|
||||
// The right stream operator on lhs of assignment statement does
|
||||
// not reorder bits. However, if the rhs is wider than the lhs,
|
||||
// then we select bits from the left-most, not the right-most.
|
||||
int dWidth = nodep->lhsp()->castStreamR()->lhsp()->width();
|
||||
int sWidth = nodep->rhsp()->width();
|
||||
// Unlink the stuff
|
||||
AstNode* dstp = nodep->lhsp()->castStreamR()->lhsp()->unlinkFrBack();
|
||||
AstNode* sizep = nodep->lhsp()->castStreamR()->rhsp()->unlinkFrBack();
|
||||
AstNode* streamp = nodep->lhsp()->castStreamR()->unlinkFrBack();
|
||||
AstNode* srcp = nodep->rhsp()->unlinkFrBack();
|
||||
if (sWidth > dWidth) {
|
||||
srcp = new AstSel(streamp->fileline(), srcp, sWidth-dWidth, dWidth);
|
||||
}
|
||||
nodep->lhsp(dstp);
|
||||
nodep->rhsp(srcp);
|
||||
// Cleanup
|
||||
sizep->deleteTree(); sizep=NULL;
|
||||
streamp->deleteTree(); streamp=NULL;
|
||||
// Further reduce, any of the nodes may have more reductions.
|
||||
return true;
|
||||
}
|
||||
else if (replaceAssignMultiSel(nodep)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "V3String.h"
|
||||
#include "V3EmitC.h"
|
||||
#include "V3EmitCBase.h"
|
||||
#include "V3Number.h"
|
||||
|
||||
#define VL_VALUE_STRING_MAX_WIDTH 8192 // We use a static char array in VL_VALUE_STRING
|
||||
|
||||
@ -552,6 +553,28 @@ public:
|
||||
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstStreamL* nodep, AstNUser*) {
|
||||
// Attempt to use a "fast" stream function for slice size = power of 2
|
||||
if (!nodep->isWide()) {
|
||||
uint32_t isPow2 = nodep->rhsp()->castConst()->num().countOnes() == 1;
|
||||
uint32_t sliceSize = nodep->rhsp()->castConst()->toUInt();
|
||||
if (isPow2 && sliceSize <= (nodep->isQuad() ? sizeof(uint64_t) : sizeof(uint32_t))) {
|
||||
puts("VL_STREAML_FAST_");
|
||||
emitIQW(nodep);
|
||||
emitIQW(nodep->lhsp());
|
||||
puts("I(");
|
||||
puts(cvtToStr(nodep->widthMin()));
|
||||
puts(","+cvtToStr(nodep->lhsp()->widthMin()));
|
||||
puts(","+cvtToStr(nodep->rhsp()->widthMin()));
|
||||
puts(",");
|
||||
nodep->lhsp()->iterateAndNext(*this); puts(", ");
|
||||
uint32_t rd_log2 = V3Number::log2b(nodep->rhsp()->castConst()->toUInt());
|
||||
puts(cvtToStr(rd_log2)+")");
|
||||
return;
|
||||
}
|
||||
}
|
||||
emitOpName(nodep, "VL_STREAML_%nq%lq%rq(%nw,%lw,%rw, %P, %li, %ri)", nodep->lhsp(), nodep->rhsp(), NULL);
|
||||
}
|
||||
// Terminals
|
||||
virtual void visit(AstVarRef* nodep, AstNUser*) {
|
||||
puts(nodep->hiername());
|
||||
|
@ -902,6 +902,23 @@ V3Number& V3Number::opRepl (const V3Number& lhs, uint32_t rhsval) { // rhs is #
|
||||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opStreamL (const V3Number& lhs, const V3Number& rhs) {
|
||||
setZero();
|
||||
// See also error in V3Width
|
||||
if (!lhs.sized()) {
|
||||
m_fileline->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in streams.");
|
||||
}
|
||||
// Slice size should never exceed the lhs width
|
||||
int ssize=min(rhs.toUInt(), (unsigned)lhs.width());
|
||||
for (int istart=0; istart<lhs.width(); istart+=ssize) {
|
||||
int ostart=max(0, lhs.width()-ssize-istart);
|
||||
for (int bit=0; bit<ssize && bit<lhs.width()-istart; bit++) {
|
||||
setBit(ostart+bit, lhs.bitIs(istart+bit));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::opLogAnd (const V3Number& lhs, const V3Number& rhs) {
|
||||
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation
|
||||
char loutc = 0;
|
||||
|
@ -214,6 +214,7 @@ public:
|
||||
V3Number& opConcat (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opRepl (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opRepl (const V3Number& lhs, uint32_t rhs);
|
||||
V3Number& opStreamL (const V3Number& lhs, const V3Number& rhs);
|
||||
V3Number& opSel (const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||
V3Number& opSel (const V3Number& lhs, uint32_t rhs, uint32_t ths);
|
||||
V3Number& opCond (const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
|
||||
|
@ -128,7 +128,8 @@ private:
|
||||
enum ExtendRule {
|
||||
EXTEND_EXP, // Extend if expect sign and node signed, e.g. where node=x/y in "x + y"
|
||||
EXTEND_ZERO, // Extend with zeros. e.g. node=x/y in "$signed(x) == $unsigned(y)"
|
||||
EXTEND_LHS // Extend with sign if node signed. e.g. node=x/y in "$signed(x) == $signed(y)"
|
||||
EXTEND_LHS, // Extend with sign if node signed. e.g. node=x/y in "$signed(x) == $signed(y)"
|
||||
EXTEND_OFF // No extension
|
||||
};
|
||||
|
||||
// CLASSES
|
||||
@ -366,6 +367,35 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeStream* nodep, AstNUser* vup) {
|
||||
if (vup->c()->prelim()) {
|
||||
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
||||
checkCvtUS(nodep->lhsp());
|
||||
checkCvtUS(nodep->rhsp());
|
||||
V3Const::constifyParamsEdit(nodep->rhsp()); // rhsp may change
|
||||
AstConst* constp = nodep->rhsp()->castConst();
|
||||
AstBasicDType* basicp = nodep->rhsp()->castBasicDType();
|
||||
if (!constp && !basicp) { nodep->v3error("Slice size isn't a constant or basic data type."); return; }
|
||||
if (basicp) { // Convert data type to a constant size
|
||||
AstConst* newp = new AstConst(basicp->fileline(), basicp->width());
|
||||
nodep->rhsp()->replaceWith(newp);
|
||||
pushDeletep(basicp);
|
||||
} else {
|
||||
uint32_t sliceSize = constp->toUInt();
|
||||
if (!sliceSize) { nodep->v3error("Slice size cannot be zero."); return; }
|
||||
}
|
||||
nodep->dtypeSetLogicSized((nodep->lhsp()->width()),
|
||||
(nodep->lhsp()->widthMin()),
|
||||
AstNumeric::UNSIGNED);
|
||||
}
|
||||
if (vup->c()->final()) {
|
||||
if (!nodep->dtypep()->widthSized()) {
|
||||
// See also error in V3Number
|
||||
nodep->v3warn(WIDTHCONCAT,"Unsized numbers/parameters not allowed in streams.");
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void visit(AstRange* nodep, AstNUser* vup) {
|
||||
// Real: Not allowed
|
||||
// Signed: unsigned output, input either
|
||||
@ -1548,7 +1578,8 @@ private:
|
||||
//UINFO(0,"aw "<<awidth<<" w"<<nodep->rhsp()->width()<<" m"<<nodep->rhsp()->widthMin()<<endl);
|
||||
AstNodeDType* subDTypep = nodep->dtypep();
|
||||
// Note assignments do not look at the LHS's sign, extend based on right only
|
||||
widthCheck(nodep,"Assign RHS",nodep->rhsp(),subDTypep,EXTEND_LHS);
|
||||
int lhsStream = nodep->lhsp()->castNodeStream() != NULL;
|
||||
widthCheck(nodep,"Assign RHS",nodep->rhsp(),subDTypep,lhsStream?EXTEND_OFF:EXTEND_LHS);
|
||||
//if (debug()) nodep->dumpTree(cout," AssignOut: ");
|
||||
}
|
||||
}
|
||||
@ -2329,6 +2360,7 @@ private:
|
||||
// It is reasonable to have sign extension with unsigned output,
|
||||
// for example $unsigned(a)+$signed(b), the SIGNED(B) will be unsigned dtype out
|
||||
UINFO(4," widthExtend_(r="<<extendRule<<") old: "<<nodep<<endl);
|
||||
if (extendRule == EXTEND_OFF) return;
|
||||
AstConst* constp = nodep->castConst();
|
||||
int expWidth = expDTypep->width();
|
||||
if (constp && !nodep->isSigned()) {
|
||||
@ -2467,7 +2499,13 @@ private:
|
||||
<<" bits.");
|
||||
}
|
||||
if (bad || underp->width()!=expWidth) {
|
||||
fixWidthExtend(underp, expDTypep, extendRule); underp=NULL;//Changed
|
||||
// If we're in an NodeAssign, don't truncate the RHS if the LHS is
|
||||
// a NodeStream. The streaming operator changes the rules regarding
|
||||
// which bits to truncate.
|
||||
AstNodeAssign* assignp = nodep->castNodeAssign();
|
||||
if (!assignp || !assignp->lhsp()->castNodeStream()) {
|
||||
fixWidthExtend(underp, expDTypep, extendRule); underp=NULL;//Changed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3010,7 +3010,7 @@ exprOkLvalue<nodep>: // expression that's also OK to use as a variable_lvalue
|
||||
| data_type assignment_pattern { $$ = $2; $2->childDTypep($1); }
|
||||
| assignment_pattern { $$ = $1; }
|
||||
//
|
||||
//UNSUP streaming_concatenation { UNSUP }
|
||||
| streaming_concatenation { $$ = $1; }
|
||||
;
|
||||
|
||||
fexprOkLvalue<nodep>: // exprOkLValue, For use as first part of statement (disambiguates <=)
|
||||
@ -3112,6 +3112,32 @@ argsDotted<nodep>: // IEEE: part of list_of_arguments
|
||||
| '.' idAny '(' expr ')' { $$ = new AstArg($1,*$2,$4); }
|
||||
;
|
||||
|
||||
streaming_concatenation<nodep>: // ==IEEE: streaming_concatenation
|
||||
// // Need to disambiguate {<< expr-{ ... expr-} stream_concat }
|
||||
// // From {<< stream-{ ... stream-} }
|
||||
// // Likewise simple_type's idScoped from constExpr's idScope
|
||||
// // Thus we allow always any two operations. Sorry
|
||||
// // IEEE: "'{' yP_SL/R stream_concatenation '}'"
|
||||
// // IEEE: "'{' yP_SL/R simple_type stream_concatenation '}'"
|
||||
// // IEEE: "'{' yP_SL/R constExpr stream_concatenation '}'"
|
||||
'{' yP_SLEFT stream_concOrExprOrType '}' { $$ = new AstStreamL($1, $3, new AstConst($1,1)); }
|
||||
| '{' yP_SRIGHT stream_concOrExprOrType '}' { $$ = new AstStreamR($1, $3, new AstConst($1,1)); }
|
||||
| '{' yP_SLEFT stream_concOrExprOrType stream_concatenation '}' { $$ = new AstStreamL($1, $4, $3); }
|
||||
| '{' yP_SRIGHT stream_concOrExprOrType stream_concatenation '}' { $$ = new AstStreamR($1, $4, $3); }
|
||||
;
|
||||
|
||||
stream_concOrExprOrType<nodep>: // IEEE: stream_concatenation | slice_size:simple_type | slice_size:constExpr
|
||||
cateList { $$ = $1; }
|
||||
| simple_type { $$ = $1; }
|
||||
// // stream_concatenation found via cateList:stream_expr:'{-normal-concat'
|
||||
// // simple_typeRef found via cateList:stream_expr:expr:id
|
||||
// // constant_expression found via cateList:stream_expr:expr
|
||||
;
|
||||
|
||||
stream_concatenation<nodep>: // ==IEEE: stream_concatenation
|
||||
'{' cateList '}' { $$ = $2; }
|
||||
;
|
||||
|
||||
stream_expression<nodep>: // ==IEEE: stream_expression
|
||||
// // IEEE: array_range_expression expanded below
|
||||
expr { $$ = $1; }
|
||||
@ -3381,6 +3407,7 @@ variable_lvalue<nodep>: // IEEE: variable_lvalue or net_lvalue
|
||||
//UNSUP idClassSel yP_TICKBRA variable_lvalueList '}' { UNSUP }
|
||||
//UNSUP /**/ yP_TICKBRA variable_lvalueList '}' { UNSUP }
|
||||
//UNSUP streaming_concatenation { UNSUP }
|
||||
| streaming_concatenation { $$ = $1; }
|
||||
;
|
||||
|
||||
variable_lvalueConcList<nodep>: // IEEE: part of variable_lvalue: '{' variable_lvalue { ',' variable_lvalue } '}'
|
||||
|
18
test_regress/t/t_stream.pl
Executable file
18
test_regress/t/t_stream.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
311
test_regress/t/t_stream.v
Normal file
311
test_regress/t/t_stream.v
Normal file
@ -0,0 +1,311 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2014 by Glen Gibb.
|
||||
|
||||
//module t;
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
integer cyc; initial cyc=1;
|
||||
|
||||
|
||||
// The 'initial' code block below tests compilation-time
|
||||
// evaluation/optimization of the stream operator. All occurences of the stream
|
||||
// operator within this block are replaced prior to generation of C code.
|
||||
logic [3:0] dout;
|
||||
logic [31:0] dout32;
|
||||
logic [10:0] dout11;
|
||||
|
||||
initial begin
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: rhs of assignment
|
||||
//
|
||||
// Test slice sizes from 1 - 5
|
||||
dout = { << {4'b0001}}; if (dout != 4'b1000) $stop;
|
||||
dout = { << 2 {4'b0001}}; if (dout != 4'b0100) $stop;
|
||||
dout = { << 3 {4'b0001}}; if (dout != 4'b0010) $stop;
|
||||
dout = { << 4 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
dout = { << 5 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
|
||||
// Stream operator: >>
|
||||
// Location: rhs of assignment
|
||||
//
|
||||
// Right-streaming operator on RHS does not reorder bits
|
||||
dout = { >> {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
dout = { >> 2 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
dout = { >> 3 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
dout = { >> 4 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
dout = { >> 5 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: lhs of assignment
|
||||
{ << {dout}} = 4'b0001; if (dout != 4'b1000) $stop;
|
||||
{ << 2 {dout}} = 4'b0001; if (dout != 4'b0100) $stop;
|
||||
{ << 3 {dout}} = 4'b0001; if (dout != 4'b0010) $stop;
|
||||
{ << 4 {dout}} = 4'b0001; if (dout != 4'b0001) $stop;
|
||||
{ << 5 {dout}} = 4'b0001; if (dout != 4'b0001) $stop;
|
||||
|
||||
// Stream operator: >>
|
||||
// Location: lhs of assignment
|
||||
{ >> {dout}} = 4'b0001; if (dout != 4'b0001) $stop;
|
||||
{ >> 2 {dout}} = 4'b0001; if (dout != 4'b0001) $stop;
|
||||
{ >> 3 {dout}} = 4'b0001; if (dout != 4'b0001) $stop;
|
||||
{ >> 4 {dout}} = 4'b0001; if (dout != 4'b0001) $stop;
|
||||
{ >> 5 {dout}} = 4'b0001; if (dout != 4'b0001) $stop;
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: lhs of assignment
|
||||
// RHS is *wider* than LHS
|
||||
/* verilator lint_off WIDTH */
|
||||
{ << {dout}} = 5'b00001; if (dout != 4'b1000) $stop;
|
||||
{ << 2 {dout}} = 5'b00001; if (dout != 4'b0100) $stop;
|
||||
{ << 3 {dout}} = 5'b00001; if (dout != 4'b0010) $stop;
|
||||
{ << 4 {dout}} = 5'b00001; if (dout != 4'b0001) $stop;
|
||||
{ << 5 {dout}} = 5'b01101; if (dout != 4'b0110) $stop;
|
||||
/* verilator lint_on WIDTH */
|
||||
|
||||
// Stream operator: >>
|
||||
// Location: lhs of assignment
|
||||
// RHS is *wider* than LHS
|
||||
/* verilator lint_off WIDTH */
|
||||
{ >> {dout}} = 5'b01101; if (dout != 4'b0110) $stop;
|
||||
{ >> 2 {dout}} = 5'b01101; if (dout != 4'b0110) $stop;
|
||||
{ >> 3 {dout}} = 5'b01101; if (dout != 4'b0110) $stop;
|
||||
{ >> 4 {dout}} = 5'b01101; if (dout != 4'b0110) $stop;
|
||||
{ >> 5 {dout}} = 5'b01101; if (dout != 4'b0110) $stop;
|
||||
/* verilator lint_on WIDTH */
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: both sides of assignment
|
||||
{ << {dout}} = { << {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
{ << 2 {dout}} = { << 2 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
{ << 3 {dout}} = { << 3 {4'b0001}}; if (dout != 4'b0100) $stop;
|
||||
{ << 4 {dout}} = { << 4 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
{ << 5 {dout}} = { << 5 {4'b0001}}; if (dout != 4'b0001) $stop;
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: as an operand within a statement
|
||||
//
|
||||
// Test slice sizes from 1 - 5
|
||||
if (4'({ << {4'b0001}}) != 4'b1000) $stop;
|
||||
if (4'({ << 2 {4'b0001}}) != 4'b0100) $stop;
|
||||
if (4'({ << 3 {4'b0001}}) != 4'b0010) $stop;
|
||||
if (4'({ << 4 {4'b0001}}) != 4'b0001) $stop;
|
||||
if (4'({ << 5 {4'b0001}}) != 4'b0001) $stop;
|
||||
|
||||
// case
|
||||
dout32 = { << 3 { 32'b11010111000010100100010010010111 }}; if (dout32 != 32'he92910eb) $stop;
|
||||
dout11 = { << 4 { 11'b10010010111 }}; if (dout11 != 11'h3cc) $stop;
|
||||
end
|
||||
|
||||
|
||||
// The two always blocks below test run-time evaluation of the stream
|
||||
// operator in generated C code.
|
||||
//
|
||||
// Various stream operators are optimized away. Here's a brief summary:
|
||||
//
|
||||
// Stream op on RHS of assign
|
||||
// --------------------------
|
||||
// X = { << a { Y } } --- C function evaluates stream operator
|
||||
// -- if log2(a) == int --> "fast" eval func
|
||||
// -- if log2(a) != int --> "slow" eval func
|
||||
// X = { >> a { Y } } --- stream operator is optimized away
|
||||
//
|
||||
// Stream op on LHS of assign
|
||||
// --------------------------
|
||||
// Note: if Y.width() > X.width, then the MSBs of Y are used, not the LSBs!
|
||||
// { << a { X } } = Y --- stream operator is moved to RHS, eval as above
|
||||
// { >> a { X } } = Y --- stream operator is optimized away
|
||||
|
||||
logic [31:0] din_i;
|
||||
logic [63:0] din_q;
|
||||
logic [95:0] din_w;
|
||||
|
||||
// Stream op on RHS, left-stream operator
|
||||
logic [31:0] dout_rhs_ls_i;
|
||||
logic [63:0] dout_rhs_ls_q;
|
||||
logic [95:0] dout_rhs_ls_w;
|
||||
|
||||
// Stream op on RHS, right-stream operator
|
||||
logic [31:0] dout_rhs_rs_i;
|
||||
logic [63:0] dout_rhs_rs_q;
|
||||
logic [95:0] dout_rhs_rs_w;
|
||||
|
||||
// Stream op on both sides, left-stream operator
|
||||
logic [31:0] dout_bhs_ls_i;
|
||||
logic [63:0] dout_bhs_ls_q;
|
||||
logic [95:0] dout_bhs_ls_w;
|
||||
|
||||
// Stream op on both sides, right-stream operator
|
||||
logic [31:0] dout_bhs_rs_i;
|
||||
logic [63:0] dout_bhs_rs_q;
|
||||
logic [95:0] dout_bhs_rs_w;
|
||||
|
||||
// Stream operator on LHS (with concatenation on LHS)
|
||||
logic [3:0] din_lhs;
|
||||
logic [1:0] dout_lhs_ls_a, dout_lhs_ls_b;
|
||||
logic [1:0] dout_lhs_rs_a, dout_lhs_rs_b;
|
||||
|
||||
// Addition operator on LHS, right-shift tests:
|
||||
// Testing various shift sizes to exercise fast + slow funcs
|
||||
logic [22:0] dout_rhs_ls_i_23_3;
|
||||
logic [22:0] dout_rhs_ls_i_23_4;
|
||||
|
||||
logic [36:0] dout_rhs_ls_q_37_3;
|
||||
logic [36:0] dout_rhs_ls_q_37_4;
|
||||
|
||||
always @*
|
||||
begin
|
||||
// Stream operator: <<
|
||||
// Location: rhs of assignment
|
||||
//
|
||||
// Test each data type (I, Q, W)
|
||||
dout_rhs_ls_i = { << {din_i}};
|
||||
dout_rhs_ls_q = { << {din_q}};
|
||||
dout_rhs_ls_w = { << {din_w}};
|
||||
|
||||
// Stream operator: >>
|
||||
// Location: rhs of assignment
|
||||
dout_rhs_rs_i = { >> {din_i}};
|
||||
dout_rhs_rs_q = { >> {din_q}};
|
||||
dout_rhs_rs_w = { >> {din_w}};
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: lhs of assignment
|
||||
{ << 2 {dout_lhs_ls_a, dout_lhs_ls_b}} = din_lhs;
|
||||
|
||||
// Stream operator: >>
|
||||
// Location: lhs of assignment
|
||||
{ >> 2 {dout_lhs_rs_a, dout_lhs_rs_b}} = din_lhs;
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: both sides of assignment
|
||||
{ << 5 {dout_bhs_ls_i}} = { << 5 {din_i}};
|
||||
{ << 5 {dout_bhs_ls_q}} = { << 5 {din_q}};
|
||||
{ << 5 {dout_bhs_ls_w}} = { << 5 {din_w}};
|
||||
|
||||
// Stream operator: >>
|
||||
// Location: both sides of assignment
|
||||
{ >> 5 {dout_bhs_rs_i}} = { >> 5 {din_i}};
|
||||
{ >> 5 {dout_bhs_rs_q}} = { >> 5 {din_q}};
|
||||
{ >> 5 {dout_bhs_rs_w}} = { >> 5 {din_w}};
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: both sides of assignment
|
||||
{ << 5 {dout_bhs_ls_i}} = { << 5 {din_i}};
|
||||
{ << 5 {dout_bhs_ls_q}} = { << 5 {din_q}};
|
||||
{ << 5 {dout_bhs_ls_w}} = { << 5 {din_w}};
|
||||
|
||||
// Stream operator: <<
|
||||
// Location: rhs of assignment
|
||||
//
|
||||
// Verify both fast and slow paths (fast: sliceSize = power of 2)
|
||||
dout_rhs_ls_i_23_3 = { << 3 {din_i[22:0]}}; // SLOW
|
||||
dout_rhs_ls_i_23_4 = { << 4 {din_i[22:0]}}; // FAST
|
||||
|
||||
dout_rhs_ls_q_37_3 = { << 3 {din_q[36:0]}}; // SLOW
|
||||
dout_rhs_ls_q_37_4 = { << 4 {din_q[36:0]}}; // FAST
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (cyc != 0) begin
|
||||
cyc <= cyc + 1;
|
||||
|
||||
if (cyc == 1) begin
|
||||
din_i <= 32'h_00_00_00_01;
|
||||
din_q <= 64'h_00_00_00_00_00_00_00_01;
|
||||
din_w <= 96'h_00_00_00_00_00_00_00_00_00_00_00_01;
|
||||
|
||||
din_lhs <= 4'b_00_01;
|
||||
end
|
||||
if (cyc == 2) begin
|
||||
din_i <= 32'h_04_03_02_01;
|
||||
din_q <= 64'h_08_07_06_05_04_03_02_01;
|
||||
din_w <= 96'h_0c_0b_0a_09_08_07_06_05_04_03_02_01;
|
||||
|
||||
din_lhs <= 4'b_01_11;
|
||||
|
||||
if (dout_rhs_ls_i != 32'h_80_00_00_00) $stop;
|
||||
if (dout_rhs_ls_q != 64'h_80_00_00_00_00_00_00_00) $stop;
|
||||
if (dout_rhs_ls_w != 96'h_80_00_00_00_00_00_00_00_00_00_00_00) $stop;
|
||||
|
||||
if (dout_rhs_rs_i != 32'h_00_00_00_01) $stop;
|
||||
if (dout_rhs_rs_q != 64'h_00_00_00_00_00_00_00_01) $stop;
|
||||
if (dout_rhs_rs_w != 96'h_00_00_00_00_00_00_00_00_00_00_00_01) $stop;
|
||||
|
||||
if (dout_lhs_ls_a != 2'b_01) $stop;
|
||||
if (dout_lhs_ls_b != 2'b_00) $stop;
|
||||
|
||||
if (dout_lhs_rs_a != 2'b_00) $stop;
|
||||
if (dout_lhs_rs_b != 2'b_01) $stop;
|
||||
|
||||
if (dout_bhs_rs_i != 32'h_00_00_00_01) $stop;
|
||||
if (dout_bhs_rs_q != 64'h_00_00_00_00_00_00_00_01) $stop;
|
||||
if (dout_bhs_rs_w != 96'h_00_00_00_00_00_00_00_00_00_00_00_01) $stop;
|
||||
|
||||
if (dout_bhs_ls_i != 32'h_00_00_00_10) $stop;
|
||||
if (dout_bhs_ls_q != 64'h_00_00_00_00_00_00_01_00) $stop;
|
||||
if (dout_bhs_ls_w != 96'h_00_00_00_00_00_00_00_00_00_00_00_04) $stop;
|
||||
|
||||
if (dout_rhs_ls_i_23_3 != 23'h_10_00_00) $stop;
|
||||
if (dout_rhs_ls_i_23_4 != 23'h_08_00_00) $stop;
|
||||
|
||||
if (dout_rhs_ls_q_37_3 != 37'h_04_00_00_00_00) $stop;
|
||||
if (dout_rhs_ls_q_37_4 != 37'h_02_00_00_00_00) $stop;
|
||||
end
|
||||
if (cyc == 3) begin
|
||||
// The values below test the strange shift-merge done at the end of
|
||||
// the fast stream operators.
|
||||
// All-1s in the bits being streamed should end up as all-1s.
|
||||
din_i <= 32'h_00_7f_ff_ff;
|
||||
din_q <= 64'h_00_00_00_1f_ff_ff_ff_ff;
|
||||
|
||||
if (dout_rhs_ls_i != 32'h_80_40_c0_20) $stop;
|
||||
if (dout_rhs_ls_q != 64'h_80_40_c0_20_a0_60_e0_10) $stop;
|
||||
if (dout_rhs_ls_w != 96'h_80_40_c0_20_a0_60_e0_10_90_50_d0_30) $stop;
|
||||
|
||||
if (dout_rhs_rs_i != 32'h_04_03_02_01) $stop;
|
||||
if (dout_rhs_rs_q != 64'h_08_07_06_05_04_03_02_01) $stop;
|
||||
if (dout_rhs_rs_w != 96'h_0c_0b_0a_09_08_07_06_05_04_03_02_01) $stop;
|
||||
|
||||
if (dout_bhs_ls_i != 32'h_40_30_00_18) $stop;
|
||||
if (dout_bhs_ls_q != 64'h_06_00_c1_81_41_00_c1_80) $stop;
|
||||
if (dout_bhs_ls_w != 96'h_30_2c_28_20_01_1c_1a_04_14_0c_00_06) $stop;
|
||||
|
||||
if (dout_bhs_rs_i != 32'h_04_03_02_01) $stop;
|
||||
if (dout_bhs_rs_q != 64'h_08_07_06_05_04_03_02_01) $stop;
|
||||
if (dout_bhs_rs_w != 96'h_0c_0b_0a_09_08_07_06_05_04_03_02_01) $stop;
|
||||
|
||||
if (dout_lhs_ls_a != 2'b_11) $stop;
|
||||
if (dout_lhs_ls_b != 2'b_01) $stop;
|
||||
|
||||
if (dout_lhs_rs_a != 2'b_01) $stop;
|
||||
if (dout_lhs_rs_b != 2'b_11) $stop;
|
||||
|
||||
if (dout_rhs_ls_i_23_3 != 23'h_10_08_c0) $stop;
|
||||
if (dout_rhs_ls_i_23_4 != 23'h_08_10_18) $stop;
|
||||
|
||||
if (dout_rhs_ls_q_37_3 != 37'h_04_02_30_10_44) $stop;
|
||||
if (dout_rhs_ls_q_37_4 != 37'h_02_04_06_08_0a) $stop;
|
||||
end
|
||||
if (cyc == 4) begin
|
||||
if (dout_rhs_ls_i_23_3 != 23'h_7f_ff_ff) $stop;
|
||||
if (dout_rhs_ls_i_23_4 != 23'h_7f_ff_ff) $stop;
|
||||
|
||||
if (dout_rhs_ls_q_37_3 != 37'h_1f_ff_ff_ff_ff) $stop;
|
||||
if (dout_rhs_ls_q_37_4 != 37'h_1f_ff_ff_ff_ff) $stop;
|
||||
end
|
||||
if (cyc == 9) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
18
test_regress/t/t_stream2.pl
Executable file
18
test_regress/t/t_stream2.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
83
test_regress/t/t_stream2.v
Normal file
83
test_regress/t/t_stream2.v
Normal file
@ -0,0 +1,83 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2014 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
integer cyc=0;
|
||||
reg [63:0] crc;
|
||||
reg [63:0] sum;
|
||||
/*AUTOWIRE*/
|
||||
// Beginning of automatic wires (for undeclared instantiated-module outputs)
|
||||
wire [67:0] left; // From test of Test.v
|
||||
wire [67:0] right; // From test of Test.v
|
||||
// End of automatics
|
||||
|
||||
wire [6:0] amt = crc[6:0];
|
||||
wire [67:0] in = {crc[3:0], crc[63:0]};
|
||||
|
||||
Test test (/*AUTOINST*/
|
||||
// Outputs
|
||||
.left (left[67:0]),
|
||||
.right (right[67:0]),
|
||||
// Inputs
|
||||
.amt (amt[6:0]),
|
||||
.in (in[67:0]));
|
||||
|
||||
wire [63:0] result = (left[63:0] ^ {60'h0, left[67:64]}
|
||||
^ right[63:0] ^ {60'h0, right[67:64]});
|
||||
|
||||
// Test loop
|
||||
always @ (posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d crc=%x result=%x amt=%x left=%x right=%x\n",
|
||||
$time, cyc, crc, result, amt, left, right);
|
||||
`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
|
||||
// Setup
|
||||
crc <= 64'h5aef0c8d_d70a4497;
|
||||
sum <= 64'h0;
|
||||
end
|
||||
else if (cyc<10) begin
|
||||
sum <= 64'h0;
|
||||
end
|
||||
else if (cyc<90) begin
|
||||
end
|
||||
else if (cyc==99) begin
|
||||
$write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum);
|
||||
if (crc !== 64'hc77bb9b3784ea091) $stop;
|
||||
// What checksum will we end up with (above print should match)
|
||||
`define EXPECTED_SUM 64'h0da01049b480c38a
|
||||
if (sum !== `EXPECTED_SUM) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module Test (/*AUTOARG*/
|
||||
// Outputs
|
||||
left, right,
|
||||
// Inputs
|
||||
amt, in
|
||||
);
|
||||
|
||||
input [6:0] amt;
|
||||
input [67:0] in;
|
||||
|
||||
// amt must be constant
|
||||
output wire [67:0] left;
|
||||
output wire [67:0] right;
|
||||
assign right = { << 33 {in}};
|
||||
assign left = { >> 33 {in}};
|
||||
|
||||
endmodule
|
18
test_regress/t/t_stream3.pl
Executable file
18
test_regress/t/t_stream3.pl
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003 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.
|
||||
|
||||
compile (
|
||||
);
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
99
test_regress/t/t_stream3.v
Normal file
99
test_regress/t/t_stream3.v
Normal file
@ -0,0 +1,99 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2014 by Wilson Snyder.
|
||||
|
||||
`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)
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
integer cyc=0;
|
||||
reg [63:0] crc;
|
||||
reg [63:0] sum;
|
||||
/*AUTOWIRE*/
|
||||
|
||||
generate
|
||||
for (genvar width=1; width<=16; width++) begin
|
||||
for (genvar amt=1; amt<=width; amt++) begin
|
||||
Test #(.WIDTH(width),
|
||||
.AMT(amt))
|
||||
test (.ins(crc[width-1:0]));
|
||||
end
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// Test loop
|
||||
always @ (posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d crc=%x\n",
|
||||
$time, cyc, crc);
|
||||
`endif
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||
if (cyc==0) begin
|
||||
// Setup
|
||||
crc <= 64'h5aef0c8d_d70a4497;
|
||||
sum <= 64'h0;
|
||||
end
|
||||
else if (cyc<10) begin
|
||||
sum <= 64'h0;
|
||||
end
|
||||
else if (cyc<90) begin
|
||||
end
|
||||
else if (cyc==99) begin
|
||||
$write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum);
|
||||
if (crc !== 64'hc77bb9b3784ea091) $stop;
|
||||
// What checksum will we end up with (above print should match)
|
||||
`define EXPECTED_SUM 64'h0
|
||||
if (sum !== `EXPECTED_SUM) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module Test (/*AUTOARG*/
|
||||
// Inputs
|
||||
ins
|
||||
);
|
||||
|
||||
parameter WIDTH = 1;
|
||||
parameter AMT = 1;
|
||||
|
||||
input [WIDTH-1:0] ins;
|
||||
reg [WIDTH-1:0] got;
|
||||
reg [WIDTH-1:0] expec;
|
||||
int istart;
|
||||
int bitn;
|
||||
int ostart;
|
||||
|
||||
always @* begin
|
||||
got = { << AMT {ins}};
|
||||
|
||||
// Note always starts with right-most bit
|
||||
expec = 0;
|
||||
for (istart=0; istart<WIDTH; istart+=AMT) begin
|
||||
ostart = WIDTH - AMT - istart;
|
||||
if (ostart<0) ostart = 0;
|
||||
for (bitn=0; bitn<AMT; bitn++) begin
|
||||
if ((istart+bitn) < WIDTH
|
||||
&& (istart+bitn) >= 0
|
||||
&& (ostart+bitn) < WIDTH
|
||||
&& (ostart+bitn) >= 0) begin
|
||||
expec[ostart+bitn] = ins[istart+bitn];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] exp %0d'b%b got %0d'b%b = { << %0d { %0d'b%b }}\n", $time, WIDTH, expec, WIDTH, got, AMT, WIDTH, ins);
|
||||
`endif
|
||||
`checkh(got, expec);
|
||||
end
|
||||
|
||||
endmodule
|
Loading…
Reference in New Issue
Block a user