Support streaming operators, bug649.

Signed-off-by: Wilson Snyder <wsnyder@wsnyder.org>
This commit is contained in:
Glen Gibb 2014-04-09 20:29:35 -04:00 committed by Wilson Snyder
parent d04eb977c2
commit d34275150c
16 changed files with 871 additions and 8 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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