2006-08-26 11:35:28 +00:00
|
|
|
|
// $Id$
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Expression width calculations
|
|
|
|
|
//
|
|
|
|
|
// Code available from: http://www.veripool.com/verilator
|
|
|
|
|
//
|
|
|
|
|
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2007-01-02 22:06:40 +00:00
|
|
|
|
// Copyright 2003-2007 by Wilson Snyder. This program is free software; you can
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
|
|
|
|
// General Public License or the Perl Artistic License.
|
|
|
|
|
//
|
|
|
|
|
// Verilator is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Width's Transformations:
|
|
|
|
|
// Top down traversal:
|
|
|
|
|
// Determine width of sub-expressions
|
|
|
|
|
// width() = # bits upper expression wants, 0 for anything-goes
|
|
|
|
|
// widthUnsized() = # bits for unsized constant, or 0 if it's sized
|
|
|
|
|
// widthMin() = Alternative acceptable width for linting, or width() if sized
|
|
|
|
|
// Determine this subop's width, can be either:
|
|
|
|
|
// Fixed width X
|
|
|
|
|
// Unsized, min width X ('d5 is unsized, min 3 bits.)
|
|
|
|
|
// Pass up:
|
|
|
|
|
// width() = # bits this expression generates
|
|
|
|
|
// widthSized() = true if all constants sized, else false
|
|
|
|
|
// Compute size of this expression
|
|
|
|
|
// Lint warn about mismatches
|
|
|
|
|
// If expr size != subop fixed, bad
|
|
|
|
|
// If expr size < subop unsized minimum, bad
|
|
|
|
|
// If expr size != subop, edit netlist
|
|
|
|
|
// For == and similar ops, if multibit underneath, add a REDOR
|
|
|
|
|
// If subop larger, add a EXTRACT
|
|
|
|
|
// If subop smaller, add a EXTEND
|
|
|
|
|
// Pass size to sub-expressions if required (+/-* etc)
|
|
|
|
|
// FINAL = true.
|
|
|
|
|
// Subexpressions lint and extend as needed
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2006-12-18 19:20:45 +00:00
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Width.h"
|
|
|
|
|
#include "V3Number.h"
|
|
|
|
|
#include "V3Const.h"
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Width state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
enum Stage { PRELIM=1,FINAL=2,BOTH=3 };
|
|
|
|
|
|
|
|
|
|
class WidthVP : public AstNUser {
|
|
|
|
|
// Parameters to pass down hierarchy with visit functions.
|
|
|
|
|
int m_width; // Expression width, for (2+2), it's 32 bits
|
|
|
|
|
int m_minWidth; // Minimum width, for (2+2), it's 2 bits, for 32'2+32'2 it's 32 bits
|
|
|
|
|
Stage m_stage; // If true, report errors
|
|
|
|
|
public:
|
|
|
|
|
WidthVP(int width, int minWidth, Stage stage) : m_width(width), m_minWidth(minWidth), m_stage(stage) {};
|
|
|
|
|
int width() const { return m_width; }
|
|
|
|
|
int widthMin() const { return m_minWidth?m_minWidth:m_width; }
|
|
|
|
|
bool prelim() const { return m_stage&1; }
|
|
|
|
|
bool final() const { return m_stage&2; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WidthVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
|
|
|
|
int m_taskDepth; // Recursion check
|
|
|
|
|
bool m_paramsOnly; // Computing parameter value; limit operation
|
|
|
|
|
AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations
|
|
|
|
|
AstNodeCase* m_casep; // Current case statement CaseItem is under
|
|
|
|
|
|
|
|
|
|
//int debug() { return 9; }
|
|
|
|
|
|
|
|
|
|
// CLASSES
|
|
|
|
|
#define ANYSIZE 0
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
// Naming: width_{output size rule}_{lhs rule}_{rhs rule}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
// Widths: 1 bit out, lhs 1 bit
|
|
|
|
|
void width_O1_L1(AstNode* nodep, AstNUser* vup);
|
|
|
|
|
virtual void visit(AstLogNot* nodep, AstNUser* vup) { width_O1_L1(nodep,vup); }
|
|
|
|
|
virtual void visit(AstPslBool* nodep, AstNUser* vup) { width_O1_L1(nodep,vup); }
|
|
|
|
|
|
|
|
|
|
// Widths: 1 bit out, lhs 1 bit, rhs 1 bit
|
|
|
|
|
void width_O1_L1_R1(AstNode* nodep, AstNUser* vup);
|
|
|
|
|
virtual void visit(AstLogAnd* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); }
|
|
|
|
|
virtual void visit(AstLogOr* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); }
|
|
|
|
|
virtual void visit(AstLogIf* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); }
|
|
|
|
|
virtual void visit(AstLogIff* nodep, AstNUser* vup) { width_O1_L1_R1(nodep,vup); }
|
|
|
|
|
|
|
|
|
|
// Widths: 1 bit out, Any width lhs
|
|
|
|
|
void width_O1_L(AstNode* nodep, AstNUser* vup);
|
|
|
|
|
virtual void visit(AstRedAnd* nodep, AstNUser* vup) { width_O1_L(nodep,vup); }
|
|
|
|
|
virtual void visit(AstRedOr* nodep, AstNUser* vup) { width_O1_L(nodep,vup); }
|
|
|
|
|
virtual void visit(AstRedXnor* nodep, AstNUser* vup){ width_O1_L(nodep,vup); }
|
|
|
|
|
virtual void visit(AstRedXor* nodep,AstNUser* vup) { width_O1_L(nodep,vup); }
|
|
|
|
|
virtual void visit(AstIsUnknown* nodep,AstNUser* vup) { width_O1_L(nodep,vup); }
|
|
|
|
|
virtual void visit(AstOneHot* nodep,AstNUser* vup) { width_O1_L(nodep,vup); }
|
|
|
|
|
virtual void visit(AstOneHot0* nodep,AstNUser* vup) { width_O1_L(nodep,vup); }
|
|
|
|
|
|
|
|
|
|
// Widths: 1 bit out, lhs width == rhs width
|
|
|
|
|
void width_O1_L_Rlhs(AstNode* nodep, AstNUser* vup);
|
|
|
|
|
virtual void visit(AstEq* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstEqCase* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstGt* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstGtS* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstGte* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstGteS* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstLt* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstLtS* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstLte* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstLteS* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstNeq* nodep, AstNUser* vup) { width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstNeqCase* nodep, AstNUser* vup){ width_O1_L_Rlhs(nodep,vup); }
|
|
|
|
|
|
|
|
|
|
// Widths: out width = lhs width = rhs width
|
|
|
|
|
void width_Omax_L_Rlhs(AstNode* nodep, AstNUser* vup);
|
|
|
|
|
virtual void visit(AstAnd* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstOr* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstXnor* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstXor* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
// Multiple possible reasonable division width conversions. Just keep our code simple, they aren't common.
|
|
|
|
|
virtual void visit(AstModDiv* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstModDivS* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstDiv* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstDivS* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
// Special warning suppression rules
|
|
|
|
|
virtual void visit(AstSub* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstAdd* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstMul* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
virtual void visit(AstMulS* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); }
|
|
|
|
|
|
2006-10-25 21:41:32 +00:00
|
|
|
|
// Widths: out width = lhs width, but upper matters
|
|
|
|
|
void width_Olhs_L(AstNodeUniop* nodep, AstNUser* vup);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual void visit(AstNot* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); }
|
|
|
|
|
virtual void visit(AstUnaryMin* nodep, AstNUser* vup) { width_Olhs_L(nodep,vup); }
|
2006-10-25 21:41:32 +00:00
|
|
|
|
|
|
|
|
|
// Widths: out width = lhs width, upper doesn't matter
|
|
|
|
|
void width_Olhs_Lforce(AstNodeUniop* nodep, AstNUser* vup);
|
|
|
|
|
virtual void visit(AstSigned* nodep, AstNUser* vup) { width_Olhs_Lforce(nodep,vup); }
|
|
|
|
|
virtual void visit(AstUnsigned* nodep, AstNUser* vup) { width_Olhs_Lforce(nodep,vup); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// Widths: Output width from lhs, rhs<33 bits
|
|
|
|
|
void width_Olhs_L_R32(AstNode* nodep, AstNUser* vup);
|
|
|
|
|
virtual void visit(AstPow* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); }
|
|
|
|
|
virtual void visit(AstPowS* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); }
|
|
|
|
|
virtual void visit(AstShiftL* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); }
|
|
|
|
|
virtual void visit(AstShiftR* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); }
|
|
|
|
|
virtual void visit(AstShiftRS* nodep, AstNUser* vup) { width_Olhs_L_R32(nodep,vup); }
|
|
|
|
|
|
|
|
|
|
// Special cases. So many....
|
|
|
|
|
virtual void visit(AstNodeCond* nodep, AstNUser* vup) {
|
|
|
|
|
// op=cond?expr1:expr2 is a Good large example of the propagation mess
|
|
|
|
|
if (vup->c()->prelim()) { // First stage evaluation
|
|
|
|
|
// Just once, do the conditional, expect one bit out.
|
|
|
|
|
nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
|
|
|
|
// Determine sub expression widths only relying on what's in the subops
|
|
|
|
|
nodep->expr1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
nodep->expr2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
}
|
|
|
|
|
// Calculate width of this expression.
|
|
|
|
|
// First call (prelim()) vup->c()->width() is probably zero, so we'll return
|
|
|
|
|
// the size of this subexpression only.
|
|
|
|
|
// Second call (final()) vup->c()->width() is probably the expression size, so
|
|
|
|
|
// the expression includes the size of the output too.
|
|
|
|
|
int width = max(vup->c()->width(), max(nodep->expr1p()->width(), nodep->expr2p()->width()));
|
|
|
|
|
int mwidth = max(vup->c()->widthMin(), max(nodep->expr1p()->widthMin(), nodep->expr2p()->widthMin()));
|
|
|
|
|
nodep->width(width,mwidth);
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
// Final width known, so make sure children recompute & check their sizes
|
|
|
|
|
nodep->expr1p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p());
|
|
|
|
|
nodep->expr2p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p());
|
|
|
|
|
// Error report and change sizes for suboperands of this node.
|
|
|
|
|
widthCheckReduce(nodep,"Conditional Expression",nodep->condp(),1,0);
|
|
|
|
|
widthCheck(nodep,"Conditional True",nodep->expr1p(),width,mwidth);
|
|
|
|
|
widthCheck(nodep,"Conditional False",nodep->expr2p(),width,mwidth);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstConcat* 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());
|
|
|
|
|
nodep->width(nodep->lhsp()->width() + nodep->rhsp()->width(),
|
|
|
|
|
nodep->lhsp()->widthMin() + nodep->rhsp()->widthMin());
|
|
|
|
|
}
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
if (!nodep->widthSized()) nodep->v3error("Concat argument must have specific size. (No unsized numbers/parameters).");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstReplicate* 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());
|
|
|
|
|
V3Const::constifyParam(nodep->rhsp());
|
|
|
|
|
AstConst* constp = nodep->rhsp()->castConst();
|
|
|
|
|
if (!constp) { nodep->v3error("Replication value isn't a constant."); return; }
|
|
|
|
|
uint32_t times = constp->asInt();
|
|
|
|
|
if (times==0) { nodep->v3error("Replication value is 0."); times=1; }
|
|
|
|
|
nodep->width((nodep->lhsp()->width() * times),
|
|
|
|
|
(nodep->lhsp()->widthMin() * times));
|
|
|
|
|
}
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
if (!nodep->widthSized()) nodep->v3error("Replication expression must have specific size.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstRange* nodep, AstNUser* vup) {
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->msbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
V3Const::constifyParam(nodep->msbp());
|
|
|
|
|
V3Const::constifyParam(nodep->lsbp());
|
|
|
|
|
AstConst* msbConstp = nodep->msbp()->castConst();
|
|
|
|
|
AstConst* lsbConstp = nodep->lsbp()->castConst();
|
|
|
|
|
if (!msbConstp || !lsbConstp) {
|
|
|
|
|
if (!msbConstp) nodep->v3error("MSB of bit range isn't a constant");
|
|
|
|
|
if (!lsbConstp) nodep->v3error("LSB of bit range isn't a constant");
|
|
|
|
|
nodep->width(1,1); return;
|
|
|
|
|
}
|
|
|
|
|
uint32_t msb = msbConstp->asInt();
|
|
|
|
|
uint32_t lsb = lsbConstp->asInt();
|
|
|
|
|
if (msb > (1UL<<28)) nodep->v3error("MSB of bit range is huge; vector of over 1billion bits: 0x"<<hex<<msb);
|
|
|
|
|
if (msb<lsb) {
|
|
|
|
|
// If it's a array, ok to have either ordering, we'll just correct
|
|
|
|
|
// So, see if we're sitting under a variable's arrayp.
|
|
|
|
|
AstNode* huntbackp = nodep;
|
|
|
|
|
while (huntbackp->backp()->castRange()) huntbackp=huntbackp->backp();
|
|
|
|
|
if (huntbackp->backp()->castVar()
|
|
|
|
|
&& huntbackp->backp()->castVar()->arraysp()==huntbackp) {
|
|
|
|
|
AstNRelinker msbHandle;
|
|
|
|
|
AstNRelinker lsbHandle;
|
|
|
|
|
msbConstp->unlinkFrBack(&msbHandle);
|
|
|
|
|
lsbConstp->unlinkFrBack(&lsbHandle);
|
|
|
|
|
msbHandle.relink(lsbConstp);
|
|
|
|
|
lsbHandle.relink(msbConstp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3error("Unsupported: MSB < LSB of bit range: "<<msb<<"<"<<lsb);
|
|
|
|
|
}
|
|
|
|
|
int x=msb; msb=lsb; lsb=x;
|
|
|
|
|
}
|
|
|
|
|
int width = msb-lsb+1;
|
|
|
|
|
nodep->width(width,width);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstSel* nodep, AstNUser* vup) {
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->widthp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
V3Const::constifyParam(nodep->widthp());
|
|
|
|
|
AstConst* widthConstp = nodep->widthp()->castConst();
|
|
|
|
|
if (!widthConstp) {
|
|
|
|
|
nodep->v3error("Width of bit extract isn't a constant");
|
|
|
|
|
nodep->width(1,1); return;
|
|
|
|
|
}
|
|
|
|
|
int width = nodep->widthConst();
|
|
|
|
|
nodep->width(width,width);
|
|
|
|
|
if (nodep->lsbp()->castConst()
|
|
|
|
|
&& nodep->msbConst() < nodep->lsbConst()) {
|
|
|
|
|
nodep->v3error("Unsupported: MSB < LSB of bit extract: "
|
|
|
|
|
<<nodep->msbConst()<<"<"<<nodep->lsbConst());
|
|
|
|
|
nodep->lsbp()->replaceWith(new AstConst(nodep->lsbp()->fileline(), 0));
|
|
|
|
|
}
|
|
|
|
|
// We're extracting, so just make sure the expression is at least wide enough.
|
|
|
|
|
if (nodep->fromp()->width() < width) {
|
|
|
|
|
nodep->v3error("Extracting "<<width
|
|
|
|
|
<<" bits from only "<<nodep->fromp()->width()<<" bit number");
|
|
|
|
|
// Extend it.
|
|
|
|
|
widthCheck(nodep,"errorless...",nodep->fromp(),width,width,true/*noerror*/);
|
|
|
|
|
}
|
|
|
|
|
// Check bit indexes.
|
|
|
|
|
// What is the MSB? We want the true MSB, not one starting at 0,
|
|
|
|
|
// because a 4 bit index is required to look at a one-bit variable[15:15]
|
|
|
|
|
int frommsb = nodep->fromp()->width() - 1;
|
|
|
|
|
int fromlsb = 0;
|
|
|
|
|
AstNodeVarRef* varrp = nodep->fromp()->castNodeVarRef();
|
|
|
|
|
if (varrp && varrp->varp()->rangep()) { // Selecting a bit from a multibit register
|
|
|
|
|
frommsb = varrp->varp()->msb();
|
|
|
|
|
fromlsb = varrp->varp()->lsb();
|
|
|
|
|
}
|
|
|
|
|
int selwidth = V3Number::log2b(frommsb+1-1)+1; // Width to address a bit
|
|
|
|
|
nodep->fromp()->iterateAndNext(*this,WidthVP(selwidth,selwidth,BOTH).p());
|
|
|
|
|
if (widthBad(nodep->lsbp(),selwidth,selwidth)
|
|
|
|
|
&& nodep->lsbp()->width()!=32)
|
|
|
|
|
nodep->v3warn(WIDTH,"Bit extraction of var["<<frommsb<<":"<<fromlsb<<"] requires "
|
|
|
|
|
<<selwidth<<" bit index, not "
|
|
|
|
|
<<nodep->lsbp()->width()
|
|
|
|
|
<<(nodep->lsbp()->width()!=nodep->lsbp()->widthMin()
|
|
|
|
|
?" or "+cvtToStr(nodep->lsbp()->widthMin()):"")
|
|
|
|
|
<<" bits.");
|
|
|
|
|
widthCheck(nodep,"Extract Range",nodep->lsbp(),selwidth,selwidth,true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstArraySel* nodep, AstNUser* vup) {
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->bitp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp());
|
|
|
|
|
int dimension = AstArraySel::dimension(nodep->fromp());
|
|
|
|
|
AstNodeVarRef* varrp = basefromp->castNodeVarRef();
|
|
|
|
|
if (!varrp) nodep->v3fatalSrc("No VarRef found under ArraySel(s)");
|
|
|
|
|
//
|
|
|
|
|
int frommsb;
|
|
|
|
|
int fromlsb;
|
|
|
|
|
if (!varrp->varp()->arrayp(dimension)) {
|
|
|
|
|
nodep->v3fatalSrc("Array reference exceeds dimension of array");
|
|
|
|
|
}
|
|
|
|
|
if (1) { // ARRAY slice extraction
|
|
|
|
|
int outwidth = varrp->width(); // Width of variable
|
|
|
|
|
frommsb = varrp->varp()->arrayp(dimension)->msbConst();
|
|
|
|
|
fromlsb = varrp->varp()->arrayp(dimension)->lsbConst();
|
|
|
|
|
if (fromlsb>frommsb) {int t=frommsb; frommsb=fromlsb; fromlsb=t; }
|
|
|
|
|
nodep->width(outwidth,outwidth); // Width out = width of array
|
|
|
|
|
}
|
|
|
|
|
int selwidth = V3Number::log2b(frommsb+1-1)+1; // Width to address a bit
|
|
|
|
|
nodep->fromp()->iterateAndNext(*this,WidthVP(selwidth,selwidth,BOTH).p());
|
|
|
|
|
if (widthBad(nodep->bitp(),selwidth,selwidth)
|
|
|
|
|
&& nodep->bitp()->width()!=32)
|
|
|
|
|
nodep->v3warn(WIDTH,"Bit extraction of array["<<frommsb<<":"<<fromlsb<<"] requires "
|
|
|
|
|
<<selwidth<<" bit index, not "
|
|
|
|
|
<<nodep->bitp()->width()
|
|
|
|
|
<<(nodep->bitp()->width()!=nodep->bitp()->widthMin()
|
|
|
|
|
?" or "+cvtToStr(nodep->bitp()->widthMin()):"")
|
|
|
|
|
<<" bits.");
|
|
|
|
|
widthCheck(nodep,"Extract Range",nodep->bitp(),selwidth,selwidth,true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstExtend* nodep, AstNUser* vup) {
|
|
|
|
|
// Only created by this process, so we know width from here down it is correct.
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstExtendS* nodep, AstNUser* vup) {
|
|
|
|
|
// Only created by signing process, so we know width from here down it is correct.
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstConst* nodep, AstNUser* vup) {
|
|
|
|
|
if (vup && vup->c()->prelim()) {
|
|
|
|
|
if (nodep->num().sized()) {
|
|
|
|
|
nodep->width(nodep->num().width(), nodep->num().width());
|
|
|
|
|
} else {
|
|
|
|
|
nodep->width(nodep->num().width(), nodep->num().minWidth());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstTime* nodep, AstNUser*) {
|
|
|
|
|
nodep->width(64,64);
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstUCFunc* nodep, AstNUser* vup) {
|
|
|
|
|
// Give it the size the user wants.
|
|
|
|
|
if (vup && vup->c()->prelim()) {
|
|
|
|
|
nodep->width(32,1); // We don't care
|
|
|
|
|
}
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
nodep->width(vup->c()->width(),vup->c()->widthMin()); // We don't care
|
|
|
|
|
if (nodep->width()>64) nodep->v3error("Unsupported: $c can't generate wider then 64 bits");
|
|
|
|
|
}
|
|
|
|
|
// Just let all arguments seek their natural sizes
|
|
|
|
|
nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstCountOnes* nodep, AstNUser* vup) {
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
// If it's a 32 bit number, we need a 6 bit number as we need to return '32'.
|
|
|
|
|
int selwidth = V3Number::log2b(nodep->lhsp()->width())+1;
|
|
|
|
|
nodep->width(selwidth,selwidth);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstAttrOf* nodep, AstNUser*) {
|
|
|
|
|
nodep->fromp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->width(32,1); // Approximation, unsized 32
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstText* nodep, AstNUser* vup) {
|
|
|
|
|
// Only used in CStmts which don't care....
|
|
|
|
|
}
|
2007-06-14 16:41:32 +00:00
|
|
|
|
virtual void visit(AstScopeName* nodep, AstNUser* vup) {
|
|
|
|
|
// Only used in Displays which don't care....
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual void visit(AstVar* nodep, AstNUser* vup) {
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout," InitPre: ");
|
|
|
|
|
// Must have deterministic constant width
|
|
|
|
|
int width=1; int mwidth=1;
|
|
|
|
|
nodep->arraysp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
if (nodep->rangep()) {
|
|
|
|
|
nodep->rangep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
width = mwidth = nodep->rangep()->width();
|
|
|
|
|
}
|
|
|
|
|
else if (nodep->isInteger() || nodep->isGenVar()) {
|
|
|
|
|
width = 32;
|
|
|
|
|
mwidth = 1; // Consider it unsized, as we want x[int] to work, and also want {int} to fail.
|
|
|
|
|
}
|
|
|
|
|
else if (nodep->isParam()) { // unranged
|
|
|
|
|
width = mwidth = 0; // But see below later.
|
|
|
|
|
}
|
|
|
|
|
if (nodep->initp()) {
|
|
|
|
|
nodep->initp()->iterateAndNext(*this,WidthVP(width,0,BOTH).p());
|
|
|
|
|
if (nodep->isParam() && !nodep->rangep()) {
|
|
|
|
|
if (nodep->initp()->widthSized()) {
|
|
|
|
|
width = mwidth = nodep->initp()->width();
|
|
|
|
|
} else {
|
|
|
|
|
if (nodep->initp()->width()>32) nodep->initp()->v3warn(WIDTH,"Assigning >32 bit to unranged parameter (defaults to 32 bits)\n");
|
|
|
|
|
width = 32;
|
|
|
|
|
mwidth = nodep->initp()->widthMin();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout," final: ");
|
|
|
|
|
}
|
|
|
|
|
if (nodep->isParam() && !nodep->rangep()) {
|
|
|
|
|
// Parameter sizes can come from the thing they get assigned from
|
|
|
|
|
// They then "stick" to that width.
|
|
|
|
|
if (!width) width=32; // Or, if nothing, they're 32 bits.
|
|
|
|
|
nodep->rangep(new AstRange(nodep->fileline(),width-1,0));
|
|
|
|
|
nodep->rangep()->width(width,width);
|
|
|
|
|
}
|
|
|
|
|
nodep->width(width,mwidth); // No need to check; varrefs are the "checkers"
|
|
|
|
|
if (nodep->initp()) widthCheck(nodep,"Initial value",nodep->initp(),width,mwidth);
|
|
|
|
|
UINFO(4,"varWidthed "<<nodep<<endl);
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout," InitPos: ");
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeVarRef* nodep, AstNUser* vup) {
|
|
|
|
|
if (nodep->varp()->width()==0) {
|
|
|
|
|
// Var hasn't been widthed, so make it so.
|
|
|
|
|
nodep->varp()->iterate(*this);
|
|
|
|
|
}
|
|
|
|
|
if ((nodep->varp()->isInteger() || nodep->varp()->isGenVar())
|
|
|
|
|
&& nodep->backp()->castNodeAssign()) { // On LHS
|
|
|
|
|
// Consider Integers on LHS to sized (else would be unsized.)
|
|
|
|
|
nodep->width(32,32);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->width(nodep->varp()->width(), nodep->varp()->widthMin());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstPslClocked* nodep, AstNUser*) {
|
|
|
|
|
nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
|
|
|
|
nodep->sensesp()->iterate(*this);
|
|
|
|
|
widthCheckReduce(nodep,"Property",nodep->propp(),1,1); // it's like a if() condition.
|
|
|
|
|
nodep->width(1,1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Top levels
|
|
|
|
|
|
|
|
|
|
virtual void visit(AstNodeCase* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
AstNodeCase* lastCasep = m_casep;
|
|
|
|
|
m_casep = nodep;
|
|
|
|
|
nodep->exprp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
|
|
|
|
itemp->iterate(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
}
|
|
|
|
|
int width = nodep->exprp()->width();
|
|
|
|
|
int mwidth = nodep->exprp()->widthMin();
|
|
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
|
|
|
|
width = max(width,itemp->width());
|
|
|
|
|
mwidth = max(mwidth,itemp->widthMin());
|
|
|
|
|
}
|
|
|
|
|
nodep->exprp()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p());
|
|
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=itemp->nextp()->castCaseItem()) {
|
|
|
|
|
itemp->iterate(*this,WidthVP(width,mwidth,FINAL).p());
|
|
|
|
|
}
|
|
|
|
|
widthCheck(nodep,"Case expression",nodep->exprp(),width,mwidth);
|
|
|
|
|
m_casep = lastCasep;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstCaseItem* nodep, AstNUser* vup) {
|
|
|
|
|
// Same for both prelim() and final()
|
|
|
|
|
if (!m_casep->castGenCase()) nodep->bodysp()->iterateAndNext(*this);
|
|
|
|
|
if (!nodep->condsp()) {
|
|
|
|
|
// Else "default:" of the case, just return benign value
|
|
|
|
|
nodep->width(vup->c()->width(),vup->c()->widthMin());
|
|
|
|
|
} else {
|
|
|
|
|
// Need to look across multiple case values for one set of statements
|
|
|
|
|
int width = nodep->condsp()->width();
|
|
|
|
|
int mwidth = nodep->condsp()->widthMin();
|
|
|
|
|
for (AstNode* condp = nodep->condsp(); condp; condp=condp->nextp()) {
|
|
|
|
|
condp->iterate(*this,vup);
|
|
|
|
|
width = max(width,condp->width());
|
|
|
|
|
mwidth = max(mwidth,condp->widthMin());
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
widthCheck(nodep,"Case Item",condp,vup->c()->width(),vup->c()->widthMin());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nodep->width(width,mwidth);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeFor* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
nodep->initsp()->iterateAndNext(*this);
|
|
|
|
|
nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
|
|
|
|
if (!nodep->castGenFor()) nodep->bodysp()->iterateAndNext(*this);
|
2006-09-05 20:06:23 +00:00
|
|
|
|
nodep->incsp()->iterateAndNext(*this);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like a if() condition.
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstWhile* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
nodep->precondsp()->iterateAndNext(*this);
|
|
|
|
|
nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
|
|
|
|
nodep->bodysp()->iterateAndNext(*this);
|
|
|
|
|
widthCheckReduce(nodep,"For Test Condition",nodep->condp(),1,1); // it's like a if() condition.
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeIf* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout," IfPre: ");
|
|
|
|
|
if (!nodep->castGenIf()) {
|
|
|
|
|
nodep->ifsp()->iterateAndNext(*this);
|
|
|
|
|
nodep->elsesp()->iterateAndNext(*this);
|
|
|
|
|
}
|
|
|
|
|
nodep->condp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
|
|
|
|
widthCheckReduce(nodep,"If",nodep->condp(),1,1); // it's like a if() condition.
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout," IfPos: ");
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeAssign* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout," AssignPre: ");
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->rhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
if(!nodep->lhsp()->widthSized()) nodep->v3fatalSrc("How can LHS be unsized?");
|
|
|
|
|
int awidth = nodep->lhsp()->width();
|
|
|
|
|
if (awidth==0) {
|
|
|
|
|
awidth = nodep->rhsp()->width(); // Parameters can propagate by unsized assignment
|
|
|
|
|
}
|
|
|
|
|
nodep->rhsp()->iterateAndNext(*this,WidthVP(awidth,awidth,FINAL).p());
|
|
|
|
|
nodep->width(awidth,awidth); // We know the assign will truncate, so rather
|
|
|
|
|
//UINFO(0,"aw "<<awidth<<" w"<<nodep->rhsp()->width()<<" m"<<nodep->rhsp()->widthMin()<<endl);
|
|
|
|
|
// then using "width" and have the optimizer truncate the result, we do
|
|
|
|
|
// it using the normal width reduction checks.
|
|
|
|
|
widthCheck(nodep,"Assign RHS",nodep->rhsp(),awidth,awidth);
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout," AssignPos: ");
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodePli* nodep, AstNUser*) {
|
|
|
|
|
// Excludes NodeDisplay, see below
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
// Just let all arguments seek their natural sizes
|
|
|
|
|
nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstDisplay* nodep, AstNUser*) {
|
|
|
|
|
if (nodep->filep()) nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
|
|
|
|
// Just let all arguments seek their natural sizes
|
|
|
|
|
nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstFOpen* nodep, AstNUser*) {
|
|
|
|
|
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
|
|
|
|
nodep->filenamep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->modep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstFClose* nodep, AstNUser*) {
|
|
|
|
|
nodep->filep()->iterateAndNext(*this,WidthVP(64,64,BOTH).p());
|
|
|
|
|
}
|
2006-12-19 14:09:57 +00:00
|
|
|
|
virtual void visit(AstReadMem* nodep, AstNUser*) {
|
|
|
|
|
nodep->filenamep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->memp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->lsbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
nodep->msbp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual void visit(AstUCStmt* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
// Just let all arguments seek their natural sizes
|
|
|
|
|
nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstPslCover* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
|
|
|
|
widthCheckReduce(nodep,"Property",nodep->propp(),1,1); // it's like a if() condition.
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstPslAssert* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
|
|
|
|
widthCheckReduce(nodep,"Property",nodep->propp(),1,1); // it's like a if() condition.
|
|
|
|
|
}
|
2007-03-06 21:43:38 +00:00
|
|
|
|
virtual void visit(AstVAssert* nodep, AstNUser*) {
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
nodep->propp()->iterateAndNext(*this,WidthVP(1,1,BOTH).p());
|
|
|
|
|
nodep->passsp()->iterateAndNext(*this);
|
|
|
|
|
nodep->failsp()->iterateAndNext(*this);
|
|
|
|
|
widthCheckReduce(nodep,"Property",nodep->propp(),1,1); // it's like a if() condition.
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
virtual void visit(AstPin* nodep, AstNUser*) {
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout,"- PinPre: ");
|
|
|
|
|
// TOP LEVEL NODE
|
|
|
|
|
if (nodep->modVarp() && nodep->modVarp()->isGParam()) {
|
|
|
|
|
// Widthing handled as special init() case
|
|
|
|
|
nodep->iterateChildren(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
} else if (!m_paramsOnly){
|
|
|
|
|
if (nodep->modVarp()->width()==0) {
|
|
|
|
|
// Var hasn't been widthed, so make it so.
|
|
|
|
|
nodep->modVarp()->iterate(*this);
|
|
|
|
|
}
|
|
|
|
|
// Very much like like an assignment, but which side is LH/RHS
|
|
|
|
|
// depends on pin being a in/output/inout.
|
|
|
|
|
nodep->exprp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
int pinwidth = nodep->modVarp()->width();
|
|
|
|
|
int expwidth = nodep->exprp()->width();
|
|
|
|
|
bool inputPin = nodep->modVarp()->isInput();
|
|
|
|
|
int awidth;
|
|
|
|
|
if (m_cellRangep) {
|
|
|
|
|
int numInsts = m_cellRangep->width();
|
|
|
|
|
if (expwidth == pinwidth) {
|
|
|
|
|
awidth = pinwidth; // Arrayed instants: widths match so connect to each instance
|
|
|
|
|
} else if (expwidth == pinwidth*numInsts) {
|
|
|
|
|
awidth = pinwidth; // Arrayed instants: one bit for each of the instants (each assign is 1 pinwidth wide)
|
|
|
|
|
} else {
|
|
|
|
|
// Must be a error according to spec
|
|
|
|
|
// (Because we need to know if to connect to one or all instants)
|
|
|
|
|
nodep->v3error("Port connection "<<nodep->prettyName()<<" as part of a module instance array "
|
|
|
|
|
<<" requires "<<pinwidth<<" or "<<pinwidth*numInsts
|
|
|
|
|
<<" bits, but connection's "<<nodep->exprp()->typeName()
|
|
|
|
|
<<" generates "<<expwidth<<" bits.");
|
|
|
|
|
awidth = expwidth;
|
|
|
|
|
}
|
|
|
|
|
nodep->width(awidth,awidth);
|
|
|
|
|
} else {
|
|
|
|
|
if (nodep->modVarp()->isTristate()) nodep->v3error("Unsupported: inouts: Verilator is a 2 state simulator\n");
|
|
|
|
|
// if support tristates need some mess to force both sides to proper size
|
|
|
|
|
if (inputPin) {
|
|
|
|
|
// input pin is lhs, expr is rhs; resize expr to match
|
|
|
|
|
awidth = pinwidth;
|
|
|
|
|
} else {
|
|
|
|
|
// output pin is rhs, expr is lhs
|
|
|
|
|
// We can't make the RANGE/EXTEND until V3Inst phase, as need RHS of assignment
|
|
|
|
|
awidth = expwidth;
|
|
|
|
|
}
|
|
|
|
|
nodep->width(awidth,awidth);
|
|
|
|
|
widthCheckPin(nodep, nodep->exprp(), pinwidth, inputPin);
|
|
|
|
|
}
|
|
|
|
|
nodep->exprp()->iterateAndNext(*this,WidthVP(awidth,awidth,FINAL).p());
|
|
|
|
|
}
|
|
|
|
|
//if (debug()) nodep->dumpTree(cout,"- PinPos: ");
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstCell* nodep, AstNUser*) {
|
|
|
|
|
if (!m_paramsOnly) {
|
|
|
|
|
if (nodep->rangep()) {
|
|
|
|
|
m_cellRangep = nodep->rangep();
|
|
|
|
|
nodep->rangep()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
nodep->pinsp()->iterateAndNext(*this);
|
|
|
|
|
}
|
|
|
|
|
nodep->paramsp()->iterateAndNext(*this);
|
|
|
|
|
m_cellRangep = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstFunc* nodep, AstNUser* vup) {
|
|
|
|
|
// Grab width from the output variable
|
|
|
|
|
UINFO(5," FUNC "<<nodep<<endl);
|
|
|
|
|
if (nodep->width()==0) {
|
|
|
|
|
// Function hasn't been widthed, so make it so.
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
nodep->width(nodep->fvarp()->width(), nodep->fvarp()->width());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstFuncRef* nodep, AstNUser* vup) {
|
|
|
|
|
visit(nodep->castNodeFTaskRef(), vup);
|
|
|
|
|
nodep->widthSignedFrom(nodep->taskp());
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNodeFTaskRef* nodep, AstNUser* vup) {
|
|
|
|
|
// Function hasn't been widthed, so make it so.
|
2006-08-29 19:10:55 +00:00
|
|
|
|
if (!nodep->taskp()) nodep->v3fatalSrc("Unlinked");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (nodep->taskp()->width()==0) {
|
|
|
|
|
if (m_taskDepth > 100) {
|
|
|
|
|
nodep->v3error("Unsupported: Recursive function or task call\n");
|
|
|
|
|
nodep->width(1,1);
|
|
|
|
|
nodep->taskp()->width(1,1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_taskDepth++;
|
|
|
|
|
nodep->taskp()->iterate(*this);
|
|
|
|
|
m_taskDepth--;
|
|
|
|
|
}
|
|
|
|
|
// And do the arguments to the task/function too
|
|
|
|
|
AstNode* pinp = nodep->pinsp();
|
|
|
|
|
AstNode* stmt_nextp; // List may change, so need to keep pointer
|
|
|
|
|
for (AstNode* stmtp = nodep->taskp()->stmtsp(); stmtp; stmtp=stmt_nextp) {
|
|
|
|
|
stmt_nextp = stmtp->nextp();
|
|
|
|
|
if (AstVar* portp = stmtp->castVar()) {
|
|
|
|
|
if (portp->isIO()
|
|
|
|
|
&& pinp!=NULL) { // Else argument error we'll find later
|
|
|
|
|
AstNode* pin_nextp = pinp->nextp(); // List may change, so remember nextp
|
|
|
|
|
int width = portp->width();
|
|
|
|
|
int ewidth = portp->widthMin();
|
|
|
|
|
pinp->accept(*this,WidthVP(width,ewidth,BOTH).p());
|
|
|
|
|
widthCheck(nodep,"Function Argument",pinp,width,ewidth);
|
|
|
|
|
pinp = pin_nextp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstNetlist* nodep, AstNUser*) {
|
|
|
|
|
// Iterate modules backwards, in bottom-up order. That's faster
|
|
|
|
|
nodep->iterateChildrenBackwards(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser* vup) {
|
|
|
|
|
// Default: Just iterate
|
|
|
|
|
if (vup) nodep->v3fatalSrc("Visit function missing? Widthed expectation for this node: "<<nodep);
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
bool widthBad (AstNode* nodep, int expWidth, int expWidthMin);
|
|
|
|
|
void widthCheck (AstNode* nodep, const char* side,
|
|
|
|
|
AstNode* underp, int expWidth, int expWidthMin,
|
|
|
|
|
bool ignoreWarn=false);
|
|
|
|
|
void widthCheckReduce (AstNode* nodep, const char* side,
|
|
|
|
|
AstNode* underp, int expWidth, int expWidthMin,
|
|
|
|
|
bool ignoreWarn=false);
|
|
|
|
|
void widthCheckPin (AstNode* nodep, AstNode* underp, int expWidth, bool inputPin);
|
|
|
|
|
bool fixAutoExtend (AstNode*& nodepr, int expWidth);
|
|
|
|
|
void fixWidthExtend (AstNode* nodep, int expWidth);
|
|
|
|
|
void fixWidthReduce (AstNode* nodep, int expWidth);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
WidthVisitor(AstNode* nodep, bool paramsOnly) {
|
|
|
|
|
m_paramsOnly = paramsOnly;
|
|
|
|
|
m_taskDepth = 0;
|
|
|
|
|
m_cellRangep = NULL;
|
|
|
|
|
m_casep = NULL;
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~WidthVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
// METHODs
|
|
|
|
|
|
|
|
|
|
bool WidthVisitor::widthBad (AstNode* nodep, int expWidth, int expWidthMin) {
|
|
|
|
|
if (nodep->width()==0) nodep->v3fatalSrc("Under node has no expected width?? Missing Visitor func?");
|
|
|
|
|
if (expWidth==0) nodep->v3fatalSrc("Node has no expected width?? Missing Visitor func?");
|
|
|
|
|
if (expWidthMin==0) expWidthMin = expWidth;
|
|
|
|
|
if (nodep->widthSized() && nodep->width() != expWidthMin) return true;
|
|
|
|
|
if (!nodep->widthSized() && nodep->widthMin() > expWidthMin) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::fixWidthExtend (AstNode* nodep, int expWidth) {
|
|
|
|
|
// Fix the width mismatch by extending or truncating bits
|
|
|
|
|
// Truncation is rarer, but can occur: parameter [3:0] FOO = 64'h12312;
|
|
|
|
|
// A(CONSTwide)+B becomes A(CONSTwidened)+B
|
|
|
|
|
// A(somewide)+B becomes A(TRUNC(somewide,width))+B
|
|
|
|
|
// or A(EXTRACT(somewide,width,0))+B
|
|
|
|
|
UINFO(4," widthExtend_old: "<<nodep<<endl);
|
|
|
|
|
AstConst* constp = nodep->castConst();
|
|
|
|
|
if (constp && !nodep->isSigned()) {
|
|
|
|
|
// Save later constant propagation work, just right-size it.
|
|
|
|
|
V3Number num (nodep->fileline(), expWidth);
|
|
|
|
|
num.opAssign(constp->num());
|
|
|
|
|
AstNode* newp = new AstConst(nodep->fileline(), num);
|
|
|
|
|
newp->signedFrom(constp);
|
|
|
|
|
constp->replaceWith(newp);
|
|
|
|
|
nodep=newp;
|
|
|
|
|
} else if (expWidth<nodep->width()) {
|
|
|
|
|
// Trunc - Extract
|
|
|
|
|
AstNRelinker linker;
|
|
|
|
|
nodep->unlinkFrBack(&linker);
|
|
|
|
|
AstNode* newp = new AstSel(nodep->fileline(), nodep, 0, expWidth);
|
|
|
|
|
linker.relink(newp);
|
|
|
|
|
nodep=newp;
|
|
|
|
|
} else {
|
|
|
|
|
// Extend
|
|
|
|
|
AstNRelinker linker;
|
|
|
|
|
nodep->unlinkFrBack(&linker);
|
|
|
|
|
AstNode* newp = (nodep->isSigned()
|
|
|
|
|
? (new AstExtendS(nodep->fileline(), nodep))->castNode()
|
|
|
|
|
: (new AstExtend (nodep->fileline(), nodep))->castNode());
|
|
|
|
|
linker.relink(newp);
|
|
|
|
|
nodep=newp;
|
|
|
|
|
}
|
|
|
|
|
nodep->width(expWidth,expWidth);
|
|
|
|
|
UINFO(4," _new: "<<nodep<<endl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::fixWidthReduce (AstNode* nodep, int expWidth) {
|
|
|
|
|
// Fix the width mismatch by adding a reduction OR operator
|
|
|
|
|
// IF (A(CONSTwide)) becomes IF (A(CONSTreduced))
|
|
|
|
|
// IF (A(somewide)) becomes IF (A(REDOR(somewide)))
|
|
|
|
|
// Attempt to fix it quietly
|
|
|
|
|
UINFO(4," widthReduce_old: "<<nodep<<endl);
|
|
|
|
|
AstConst* constp = nodep->castConst();
|
|
|
|
|
if (constp) {
|
|
|
|
|
V3Number num (nodep->fileline(), expWidth);
|
|
|
|
|
num.opRedOr(constp->num());
|
|
|
|
|
AstNode* newp = new AstConst(nodep->fileline(), num);
|
|
|
|
|
newp->signedFrom(constp);
|
|
|
|
|
constp->replaceWith(newp);
|
|
|
|
|
nodep=newp;
|
|
|
|
|
} else {
|
|
|
|
|
AstNRelinker linker;
|
|
|
|
|
nodep->unlinkFrBack(&linker);
|
|
|
|
|
AstNode* newp = new AstRedOr(nodep->fileline(), nodep);
|
|
|
|
|
linker.relink(newp);
|
|
|
|
|
nodep=newp;
|
|
|
|
|
}
|
|
|
|
|
nodep->width(expWidth,expWidth);
|
|
|
|
|
UINFO(4," _new: "<<nodep<<endl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool WidthVisitor::fixAutoExtend (AstNode*& nodepr, int expWidth) {
|
|
|
|
|
// For SystemVerilog '0,'1,'x,'z, autoextend and don't warn
|
|
|
|
|
if (AstConst* constp = nodepr->castConst()) {
|
|
|
|
|
if (constp->num().autoExtend() && !constp->num().sized() && constp->width()==1) {
|
|
|
|
|
// Make it the proper size. Careful of proper extension of 0's/1's
|
|
|
|
|
V3Number num (constp->fileline(), expWidth);
|
|
|
|
|
num.opRepl(constp->num(), expWidth); // {width{'1}}
|
|
|
|
|
AstNode* newp = new AstConst(constp->fileline(), num);
|
|
|
|
|
// Spec says always unsigned with proper width
|
|
|
|
|
if (debug()>4) constp->dumpTree(cout," fixAutoExtend_old: ");
|
|
|
|
|
if (debug()>4) newp->dumpTree(cout," _new: ");
|
|
|
|
|
constp->replaceWith(newp);
|
|
|
|
|
constp->deleteTree(); constp=NULL;
|
|
|
|
|
// Tell caller the new constp, and that we changed it.
|
|
|
|
|
nodepr = newp;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false; // No change
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::widthCheck (AstNode* nodep, const char* side,
|
|
|
|
|
AstNode* underp, int expWidth, int expWidthMin,
|
|
|
|
|
bool ignoreWarn) {
|
|
|
|
|
//UINFO(0,"wchk "<<nodep<<endl<<" "<<underp<<endl<<" e"<<expWidth<<" m"<<expWidthMin<<" i"<<ignoreWarn<<endl);
|
|
|
|
|
if (expWidthMin==0) expWidthMin = expWidth;
|
|
|
|
|
bool bad = widthBad(underp,expWidth,expWidthMin);
|
|
|
|
|
if (bad && fixAutoExtend(underp/*ref*/,expWidth)) bad=false; // Changes underp
|
|
|
|
|
if (bad && !ignoreWarn) {
|
|
|
|
|
if (debug()>4) nodep->backp()->dumpTree(cout," back: ");
|
|
|
|
|
nodep->v3warn(WIDTH,"Operator "<<nodep->typeName()
|
|
|
|
|
<<" expects "<<expWidth
|
|
|
|
|
<<(expWidth!=expWidthMin?" or "+cvtToStr(expWidthMin):"")
|
|
|
|
|
<<" bits on the "<<side<<", but "<<side<<"'s "
|
|
|
|
|
<<underp->typeName()<<" generates "<<underp->width()
|
|
|
|
|
<<(underp->width()!=underp->widthMin()
|
|
|
|
|
?" or "+cvtToStr(underp->widthMin()):"")
|
|
|
|
|
<<" bits.");
|
|
|
|
|
}
|
|
|
|
|
if (bad || underp->width()!=expWidth) {
|
|
|
|
|
fixWidthExtend(underp, expWidth); underp=NULL;//Changed
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::widthCheckReduce (AstNode* nodep, const char* side,
|
|
|
|
|
AstNode* underp, int expWidth, int expWidthMin,
|
|
|
|
|
bool ignoreWarn) {
|
|
|
|
|
if (expWidthMin==0) expWidthMin = expWidth;
|
|
|
|
|
if (expWidth!=1) nodep->v3fatalSrc("Only for binary functions");
|
|
|
|
|
bool bad = widthBad(underp,expWidth,expWidthMin);
|
|
|
|
|
if (bad) {
|
|
|
|
|
if (!ignoreWarn) {
|
|
|
|
|
if (debug()>4) nodep->backp()->dumpTree(cout," back: ");
|
|
|
|
|
nodep->v3warn(WIDTH,"Logical Operator "<<nodep->typeName()
|
|
|
|
|
<<" expects 1 bit on the "<<side<<", but "<<side<<"'s "
|
|
|
|
|
<<underp->typeName()<<" generates "<<underp->width()
|
|
|
|
|
<<(underp->width()!=underp->widthMin()
|
|
|
|
|
?" or "+cvtToStr(underp->widthMin()):"")
|
|
|
|
|
<<" bits.");
|
|
|
|
|
}
|
|
|
|
|
fixWidthReduce(underp, expWidth); underp=NULL;//Changed
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::widthCheckPin (AstNode* nodep, AstNode* underp, int expWidth, bool inputPin) {
|
|
|
|
|
bool bad = widthBad(underp,expWidth,expWidth);
|
|
|
|
|
if (bad && fixAutoExtend(underp/*ref*/,expWidth)) bad=false; // Changes underp
|
|
|
|
|
if (bad) {
|
|
|
|
|
nodep->v3warn(WIDTH,(inputPin?"Input":"Output")
|
|
|
|
|
<<" port connection "<<nodep->prettyName()
|
|
|
|
|
<<" expects "<<expWidth
|
|
|
|
|
<<" bits but connection's "
|
|
|
|
|
<<underp->typeName()<<" generates "<<underp->width()
|
|
|
|
|
<<(underp->width()!=underp->widthMin()
|
|
|
|
|
?" or "+cvtToStr(underp->widthMin()):"")
|
|
|
|
|
<<" bits.");
|
|
|
|
|
}
|
|
|
|
|
// We only fix input mismatches
|
|
|
|
|
if (bad && inputPin) {
|
|
|
|
|
fixWidthExtend(underp, expWidth); underp=NULL;//Changed
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::width_O1_L1(AstNode* nodep, AstNUser* vup) {
|
|
|
|
|
// Widths: 1 bit out, lhs 1 bit
|
|
|
|
|
// We calculate the width of the UNDER expression.
|
|
|
|
|
// We then check its width to see if it's legal, and edit if not
|
|
|
|
|
// We finally set the width of our output
|
|
|
|
|
if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!");
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
nodep->width(1,1);
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::width_O1_L1_R1(AstNode* nodep, AstNUser* vup) {
|
|
|
|
|
// Widths: 1 bit out, lhs 1 bit, rhs 1 bit
|
|
|
|
|
if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!");
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p());
|
|
|
|
|
nodep->op2p()->iterateAndNext(*this,WidthVP(1,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
nodep->width(1,1);
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
widthCheckReduce(nodep,"LHS",nodep->op1p(),1,1);
|
|
|
|
|
widthCheckReduce(nodep,"RHS",nodep->op2p(),1,1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::width_O1_L(AstNode* nodep, AstNUser* vup) {
|
|
|
|
|
// Widths: 1 bit out, Any width lhs
|
|
|
|
|
if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!");
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
nodep->width(1,1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::width_O1_L_Rlhs(AstNode* nodep, AstNUser* vup) {
|
|
|
|
|
// Widths: 1 bit out, lhs width == rhs width
|
|
|
|
|
if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!");
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
}
|
|
|
|
|
int width = max(nodep->op1p()->width(), nodep->op2p()->width());
|
|
|
|
|
int ewidth = max(nodep->op1p()->widthMin(), nodep->op2p()->widthMin());
|
|
|
|
|
nodep->width(1,1);
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p());
|
|
|
|
|
nodep->op2p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p());
|
|
|
|
|
widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth);
|
|
|
|
|
widthCheck(nodep,"RHS",nodep->op2p(),width,ewidth);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-25 21:41:32 +00:00
|
|
|
|
void WidthVisitor::width_Olhs_L(AstNodeUniop* nodep, AstNUser* vup) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Widths: out width = lhs width
|
|
|
|
|
// "Interim results shall take the max of operands, including LHS of assignments"
|
|
|
|
|
if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!");
|
|
|
|
|
if (vup->c()->prelim()) {
|
2006-10-25 21:41:32 +00:00
|
|
|
|
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2006-10-25 21:41:32 +00:00
|
|
|
|
int width = max(vup->c()->width(), nodep->lhsp()->width());
|
|
|
|
|
int ewidth = max(vup->c()->widthMin(), nodep->lhsp()->widthMin());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->width(width,ewidth);
|
|
|
|
|
if (vup->c()->final()) {
|
2006-10-25 21:41:32 +00:00
|
|
|
|
nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p());
|
|
|
|
|
widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::width_Olhs_Lforce(AstNodeUniop* nodep, AstNUser* vup) {
|
|
|
|
|
// Widths: out width = lhs width
|
|
|
|
|
// It always comes exactly from LHS; ignores any upper operand
|
|
|
|
|
if (nodep->op2p()) nodep->v3fatalSrc("For unary ops only!");
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
}
|
|
|
|
|
int width = nodep->lhsp()->width();
|
|
|
|
|
int ewidth = nodep->lhsp()->width(); // Not minWidth; force it.
|
|
|
|
|
nodep->width(width,ewidth);
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
// Final call, so make sure children check their sizes
|
|
|
|
|
nodep->lhsp()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p());
|
|
|
|
|
widthCheck(nodep,"LHS",nodep->lhsp(),width,ewidth);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::width_Olhs_L_R32(AstNode* nodep, AstNUser* vup) {
|
|
|
|
|
// Widths: Output width from lhs, rhs<33 bits
|
|
|
|
|
if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!");
|
|
|
|
|
if (vup->c()->prelim()) {
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,BOTH).p());
|
|
|
|
|
}
|
|
|
|
|
int width = max(vup->c()->width(), nodep->op1p()->width());
|
|
|
|
|
int ewidth = max(vup->c()->widthMin(), nodep->op1p()->widthMin());
|
|
|
|
|
nodep->width(width,ewidth);
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(width,ewidth,FINAL).p());
|
|
|
|
|
widthCheck(nodep,"LHS",nodep->op1p(),width,ewidth);
|
|
|
|
|
if (nodep->op2p()->width()>32)
|
|
|
|
|
nodep->op2p()->v3error("Unsupported: Shifting of by a over 32 bit number isn't supported."
|
|
|
|
|
<<" (This isn't a shift of 32 bits, but a shift of 2^32, or 4 billion!)\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WidthVisitor::width_Omax_L_Rlhs(AstNode* nodep, AstNUser* vup) {
|
|
|
|
|
// Widths: out width = lhs width = rhs width
|
|
|
|
|
if (!nodep->op2p()) nodep->v3fatalSrc("For binary ops only!");
|
|
|
|
|
// If errors are off, we need to follow the spec; thus we really need to do the max()
|
|
|
|
|
// because the rhs could be larger, and we need to have proper editing to get the widths
|
|
|
|
|
// to be the same for our operations.
|
|
|
|
|
if (vup->c()->prelim()) { // First stage evaluation
|
|
|
|
|
// Determine expression widths only relying on what's in the subops
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
nodep->op2p()->iterateAndNext(*this,WidthVP(ANYSIZE,0,PRELIM).p());
|
|
|
|
|
}
|
|
|
|
|
int width = max(vup->c()->width(), max(nodep->op1p()->width(), nodep->op2p()->width()));
|
|
|
|
|
int mwidth = max(vup->c()->widthMin(), max(nodep->op1p()->widthMin(), nodep->op2p()->widthMin()));
|
|
|
|
|
nodep->width(width,mwidth);
|
|
|
|
|
if (vup->c()->final()) {
|
|
|
|
|
// Final call, so make sure children check their sizes
|
|
|
|
|
nodep->op1p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p());
|
|
|
|
|
nodep->op2p()->iterateAndNext(*this,WidthVP(width,mwidth,FINAL).p());
|
|
|
|
|
// Some warning suppressions
|
|
|
|
|
bool lhsOk=false; bool rhsOk = false;
|
|
|
|
|
if (nodep->castAdd() || nodep->castSub()) {
|
|
|
|
|
lhsOk = (mwidth == (nodep->op1p()->widthMin()+1)); // Ok if user wants extra bit from carry
|
|
|
|
|
rhsOk = (mwidth == (nodep->op2p()->widthMin()+1)); // Ok if user wants extra bit from carry
|
|
|
|
|
} else if (nodep->castMul() || nodep->castMulS()) {
|
|
|
|
|
lhsOk = (mwidth >= (nodep->op1p()->widthMin()));
|
|
|
|
|
rhsOk = (mwidth >= (nodep->op2p()->widthMin()));
|
|
|
|
|
}
|
|
|
|
|
// Error report and change sizes for suboperands of this node.
|
|
|
|
|
widthCheck(nodep,"LHS",nodep->op1p(),width,mwidth,lhsOk);
|
|
|
|
|
widthCheck(nodep,"RHS",nodep->op2p(),width,mwidth,rhsOk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class WidthCommitVisitor : public AstNVisitor {
|
|
|
|
|
// Now that all widthing is complete,
|
|
|
|
|
// Copy all width() to widthMin(). V3Const expects this
|
|
|
|
|
private:
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->width(nodep->width(),nodep->width());
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
WidthCommitVisitor(AstNode* nodep) {
|
|
|
|
|
nodep->iterateAndNext(*this, NULL);
|
|
|
|
|
}
|
|
|
|
|
virtual ~WidthCommitVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Width class functions
|
|
|
|
|
|
|
|
|
|
void V3Width::width(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
// We should do it in bottom-up module order, but it works in any order.
|
|
|
|
|
WidthVisitor visitor (nodep, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Width::widthParams(AstNode* nodep) {
|
|
|
|
|
UINFO(4,__FUNCTION__<<": "<<endl);
|
|
|
|
|
// We should do it in bottom-up module order, but it works in any order.
|
|
|
|
|
WidthVisitor visitor (nodep, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Width::widthCommit(AstNode* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
WidthCommitVisitor visitor (nodep);
|
|
|
|
|
}
|
|
|
|
|
|