2012-04-13 01:08:20 +00:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
//*************************************************************************
|
|
|
|
// DESCRIPTION: Verilator: Add temporaries, such as for clean nodes
|
|
|
|
//
|
2019-11-08 03:33:59 +00:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
//
|
2019-01-04 00:17:22 +00:00
|
|
|
// Copyright 2003-2019 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
|
2009-05-04 21:07:57 +00:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
// Version 2.0.
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
// V3Clean's Transformations:
|
2008-06-10 01:25:10 +00:00
|
|
|
//
|
2006-08-26 11:35:28 +00:00
|
|
|
// Each module:
|
2019-05-19 20:13:13 +00:00
|
|
|
// For each math operator, if it requires a clean operand,
|
|
|
|
// and the operand is dirty, insert a CLEAN node.
|
|
|
|
// Resize operands to C++ 32/64/wide types.
|
|
|
|
// Copy all width() values to widthMin() so RANGE, etc can still see orig widths
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
2019-10-05 00:17:11 +00:00
|
|
|
|
2006-12-18 19:20:45 +00:00
|
|
|
#include "config_build.h"
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
#include "V3Clean.h"
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
2018-10-14 17:43:24 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cstdarg>
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
//######################################################################
|
|
|
|
// Clean state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
class CleanVisitor : public AstNVisitor {
|
|
|
|
private:
|
|
|
|
// NODE STATE
|
|
|
|
// Entire netlist:
|
2019-05-19 20:13:13 +00:00
|
|
|
// AstNode::user() -> CleanState. For this node, 0==UNKNOWN
|
|
|
|
// AstNode::user2() -> bool. True indicates widthMin has been propagated
|
|
|
|
// AstNodeDType::user3() -> AstNodeDType*. Alternative node with C size
|
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
AstUser2InUse m_inuser2;
|
|
|
|
AstUser3InUse m_inuser3;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2009-01-21 21:56:50 +00:00
|
|
|
// TYPES
|
2010-02-02 01:15:48 +00:00
|
|
|
enum CleanState { CS_UNKNOWN, CS_CLEAN, CS_DIRTY };
|
2009-01-21 21:56:50 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
// STATE
|
2009-11-07 11:20:20 +00:00
|
|
|
AstNodeModule* m_modp;
|
2008-06-10 01:25:10 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
// METHODS
|
2018-05-14 10:50:47 +00:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2009-01-21 21:56:50 +00:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
// Width resetting
|
|
|
|
int cppWidth(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (nodep->width()<=VL_WORDSIZE) return VL_WORDSIZE;
|
|
|
|
else if (nodep->width()<=VL_QUADSIZE) return VL_QUADSIZE;
|
|
|
|
else return nodep->widthWords()*VL_WORDSIZE;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2018-08-25 13:52:45 +00:00
|
|
|
void setCppWidth(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->user2(true); // Don't resize it again
|
|
|
|
AstNodeDType* old_dtypep = nodep->dtypep();
|
|
|
|
int width = cppWidth(nodep); // widthMin is unchanged
|
|
|
|
if (old_dtypep->width() != width) {
|
|
|
|
// Since any given dtype's cppWidth() is the same, we can just
|
2019-09-09 11:50:21 +00:00
|
|
|
// remember one conversion for each, and reuse it
|
2018-02-02 02:32:58 +00:00
|
|
|
if (AstNodeDType* new_dtypep = VN_CAST(old_dtypep->user3p(), NodeDType)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->dtypep(new_dtypep);
|
|
|
|
} else {
|
|
|
|
nodep->dtypeChgWidth(width, nodep->widthMin());
|
|
|
|
AstNodeDType* new_dtypep2 = nodep->dtypep();
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(new_dtypep2 != old_dtypep, nodep,
|
|
|
|
"Dtype didn't change when width changed");
|
2019-05-19 20:13:13 +00:00
|
|
|
old_dtypep->user3p(new_dtypep2); // Remember for next time
|
|
|
|
}
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2018-08-25 13:52:45 +00:00
|
|
|
void computeCppWidth(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!nodep->user2() && nodep->hasDType()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
if (VN_IS(nodep, Var) || VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|
|
|
|
|| VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) { // Or arrays
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
|
|
|
setCppWidth(nodep);
|
|
|
|
}
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Store the clean state in the userp on each node
|
|
|
|
void setCleanState(AstNode* nodep, CleanState clean) {
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->user1(clean);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
CleanState getCleanState(AstNode* nodep) {
|
2018-10-14 22:39:33 +00:00
|
|
|
return static_cast<CleanState>(nodep->user1());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
bool isClean(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
CleanState clstate = getCleanState(nodep);
|
|
|
|
if (clstate==CS_CLEAN) return true;
|
|
|
|
if (clstate==CS_DIRTY) return false;
|
|
|
|
nodep->v3fatalSrc("Unknown clean state on node: "+nodep->prettyTypeName());
|
|
|
|
return false;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
void setClean(AstNode* nodep, bool isClean) {
|
2018-08-25 13:52:45 +00:00
|
|
|
computeCppWidth(nodep); // Just to be sure it's in widthMin
|
2019-05-19 20:13:13 +00:00
|
|
|
bool wholeUint = ((nodep->widthMin() % VL_WORDSIZE) == 0); // 32,64,...
|
|
|
|
setCleanState(nodep, ((isClean||wholeUint) ? CS_CLEAN:CS_DIRTY));
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Operate on nodes
|
|
|
|
void insertClean(AstNode* nodep) { // We'll insert ABOVE passed node
|
2019-05-19 20:13:13 +00:00
|
|
|
UINFO(4," NeedClean "<<nodep<<endl);
|
|
|
|
AstNRelinker relinkHandle;
|
|
|
|
nodep->unlinkFrBack(&relinkHandle);
|
|
|
|
//
|
2019-05-10 00:03:19 +00:00
|
|
|
computeCppWidth(nodep);
|
|
|
|
V3Number mask (nodep, cppWidth(nodep));
|
|
|
|
mask.setMask(nodep->widthMin());
|
2018-08-25 13:52:45 +00:00
|
|
|
AstNode* cleanp = new AstAnd(nodep->fileline(),
|
|
|
|
new AstConst(nodep->fileline(), mask),
|
|
|
|
nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
cleanp->dtypeFrom(nodep); // Otherwise the AND normally picks LHS
|
|
|
|
relinkHandle.relink(cleanp);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
void insureClean(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
computeCppWidth(nodep);
|
|
|
|
if (!isClean(nodep)) insertClean(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
void insureCleanAndNext(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Editing list, careful looping!
|
|
|
|
for (AstNode* exprp = nodep; exprp; ) {
|
|
|
|
AstNode* nextp = exprp->nextp();
|
|
|
|
insureClean(exprp);
|
|
|
|
exprp = nextp;
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Base type handling methods
|
|
|
|
void operandBiop(AstNodeBiop* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
computeCppWidth(nodep);
|
|
|
|
if (nodep->cleanLhs()) {
|
|
|
|
insureClean(nodep->lhsp());
|
|
|
|
}
|
|
|
|
if (nodep->cleanRhs()) {
|
|
|
|
insureClean(nodep->rhsp());
|
|
|
|
}
|
|
|
|
//no setClean.. must do it in each user routine.
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
void operandTriop(AstNodeTriop* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
computeCppWidth(nodep);
|
|
|
|
if (nodep->cleanLhs()) {
|
|
|
|
insureClean(nodep->lhsp());
|
|
|
|
}
|
|
|
|
if (nodep->cleanRhs()) {
|
|
|
|
insureClean(nodep->rhsp());
|
|
|
|
}
|
|
|
|
if (nodep->cleanThs()) {
|
|
|
|
insureClean(nodep->thsp());
|
|
|
|
}
|
|
|
|
//no setClean.. must do it in each user routine.
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// VISITORS
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstNodeModule* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_modp = nodep;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
m_modp = NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstNodeUniop* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
computeCppWidth(nodep);
|
|
|
|
if (nodep->cleanLhs()) {
|
|
|
|
insureClean(nodep->lhsp());
|
|
|
|
}
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, nodep->cleanOut());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstNodeBiop* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
operandBiop(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, nodep->cleanOut());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstAnd* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
operandBiop(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, isClean(nodep->lhsp()) || isClean(nodep->rhsp()));
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstXor* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
operandBiop(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstOr* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
operandBiop(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, isClean(nodep->lhsp()) && isClean(nodep->rhsp()));
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstNodeMath* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
computeCppWidth(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, nodep->cleanOut());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstNodeAssign* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
computeCppWidth(nodep);
|
|
|
|
if (nodep->cleanRhs()) {
|
|
|
|
insureClean(nodep->rhsp());
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstText* nodep) {
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, true);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstScopeName* nodep) {
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, true);
|
2007-06-14 16:41:32 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstSel* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
operandTriop(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, nodep->cleanOut());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstUCFunc* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
computeCppWidth(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
setClean(nodep, false);
|
2019-05-19 20:13:13 +00:00
|
|
|
// We always clean, as we don't trust those pesky users.
|
2018-02-02 02:32:58 +00:00
|
|
|
if (!VN_IS(nodep->backp(), And)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
insertClean(nodep);
|
|
|
|
}
|
2018-08-25 13:52:45 +00:00
|
|
|
insureCleanAndNext(nodep->bodysp());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2019-05-01 23:18:45 +00:00
|
|
|
virtual void visit(AstTraceDecl* nodep) {
|
|
|
|
// No cleaning, or would loose pointer to enum
|
|
|
|
iterateChildren(nodep);
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstTraceInc* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
insureCleanAndNext(nodep->valuep());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstTypedef* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// No cleaning, or would loose pointer to enum
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2014-11-07 12:50:11 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstParamTypeDType* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// No cleaning, or would loose pointer to enum
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2016-03-15 01:51:31 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
// Control flow operators
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstNodeCond* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
insureClean(nodep->condp());
|
|
|
|
setClean(nodep, isClean(nodep->expr1p()) && isClean(nodep->expr2p()));
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstWhile* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
insureClean(nodep->condp());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstNodeIf* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
insureClean(nodep->condp());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstSFormatF* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
insureCleanAndNext(nodep->exprsp());
|
2019-05-19 20:13:13 +00:00
|
|
|
setClean(nodep, true); // generates a string, so not relevant
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstUCStmt* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
insureCleanAndNext(nodep->bodysp());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstCCall* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2018-08-25 13:52:45 +00:00
|
|
|
insureCleanAndNext(nodep->argsp());
|
|
|
|
setClean(nodep, true);
|
2006-10-11 15:41:42 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
//--------------------
|
|
|
|
// Default: Just iterate
|
2016-11-27 13:11:38 +00:00
|
|
|
virtual void visit(AstNode* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
computeCppWidth(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2019-09-12 11:22:22 +00:00
|
|
|
// CONSTRUCTORS
|
2015-10-04 02:33:06 +00:00
|
|
|
explicit CleanVisitor(AstNetlist* nodep) {
|
2018-06-14 23:04:52 +00:00
|
|
|
m_modp = NULL;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterate(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2009-10-02 02:33:11 +00:00
|
|
|
virtual ~CleanVisitor() {}
|
2006-08-26 11:35:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
// Clean class functions
|
|
|
|
|
|
|
|
void V3Clean::cleanAll(AstNetlist* nodep) {
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
2018-03-10 17:57:50 +00:00
|
|
|
{
|
|
|
|
CleanVisitor visitor (nodep);
|
|
|
|
} // Destruct before checking
|
2017-09-18 02:52:57 +00:00
|
|
|
V3Global::dumpCheckGlobalTree("clean", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|