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 Unknown assigns
|
|
|
|
//
|
2019-11-08 03:33:59 +00:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
//
|
2020-03-21 15:24:24 +00:00
|
|
|
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
|
|
|
// can 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.
|
2020-03-21 15:24:24 +00:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
// V3Unknown'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
|
|
|
// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel
|
|
|
|
// Need __en in changed list if a signal is on the LHS of a assign
|
|
|
|
// Constants:
|
|
|
|
// RHS, Replace 5'bx_1_x with a module global we init to a random value
|
|
|
|
// CONST(5'bx_1_x) -> VARREF(_{numberedtemp})
|
|
|
|
// -> VAR(_{numberedtemp})
|
|
|
|
// -> INITIAL(VARREF(_{numberedtemp}),
|
|
|
|
// OR(5'bx_1_x,AND(random,5'b0_1_x))
|
|
|
|
// OPTIMIZE: Must not collapse this initial back into the equation.
|
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 "V3Unknown.h"
|
|
|
|
#include "V3Ast.h"
|
|
|
|
#include "V3Const.h"
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
2018-10-14 17:43:24 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
class UnknownVisitor : public AstNVisitor {
|
|
|
|
private:
|
|
|
|
// NODE STATE
|
|
|
|
// Cleared on Netlist
|
2019-05-19 20:13:13 +00:00
|
|
|
// AstSel::user() -> bool. Set true if already processed
|
|
|
|
// AstArraySel::user() -> bool. Set true if already processed
|
|
|
|
// AstNode::user2p() -> AstIf* Inserted if assignment for conditional
|
2020-04-15 11:58:34 +00:00
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
AstUser2InUse m_inuser2;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
// STATE
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNodeModule* m_modp; // Current module
|
|
|
|
bool m_constXCvt; // Convert X's
|
|
|
|
VDouble0 m_statUnkVars; // Statistic tracking
|
|
|
|
AstAssignW* m_assignwp; // Current assignment
|
|
|
|
AstAssignDly* m_assigndlyp; // Current assignment
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2009-01-21 21:56:50 +00:00
|
|
|
// METHODS
|
2018-05-14 10:50:47 +00:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2009-10-09 00:42:45 +00:00
|
|
|
void replaceBoundLvalue(AstNode* nodep, AstNode* condp) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Spec says a out-of-range LHS SEL results in a NOP.
|
|
|
|
// This is a PITA. We could:
|
|
|
|
// 1. IF(...) around an ASSIGN,
|
|
|
|
// but that would break a "foo[TOO_BIG]=$fopen(...)".
|
|
|
|
// 2. Hack to extend the size of the output structure
|
|
|
|
// by one bit, and write to that temporary, but never read it.
|
|
|
|
// That makes there be two widths() and is likely a bug farm.
|
|
|
|
// 3. Make a special SEL to choose between the real lvalue
|
|
|
|
// and a temporary NOP register.
|
|
|
|
// 4. Assign to a temp, then IF that assignment.
|
|
|
|
// This is suspected to be nicest to later optimizations.
|
|
|
|
// 4 seems best but breaks later optimizations. 3 was tried,
|
|
|
|
// but makes a mess in the emitter as lvalue switching is needed. So 4.
|
|
|
|
// SEL(...) -> temp
|
|
|
|
// if (COND(LTE(bit<=maxlsb))) ASSIGN(SEL(...)),temp)
|
|
|
|
if (m_assignwp) {
|
|
|
|
// Wire assigns must become always statements to deal with insertion
|
|
|
|
// of multiple statements. Perhaps someday make all wassigns into always's?
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(5, " IM_WireRep " << m_assignwp << endl);
|
2020-01-18 15:29:49 +00:00
|
|
|
m_assignwp->convertToAlways();
|
|
|
|
VL_DO_CLEAR(pushDeletep(m_assignwp), m_assignwp = NULL);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
bool needDly = (m_assigndlyp != NULL);
|
|
|
|
if (m_assigndlyp) {
|
|
|
|
// Delayed assignments become normal assignments,
|
|
|
|
// then the temp created becomes the delayed assignment
|
|
|
|
AstNode* newp = new AstAssign(m_assigndlyp->fileline(),
|
|
|
|
m_assigndlyp->lhsp()->unlinkFrBackWithNext(),
|
|
|
|
m_assigndlyp->rhsp()->unlinkFrBackWithNext());
|
2020-01-18 15:29:49 +00:00
|
|
|
m_assigndlyp->replaceWith(newp);
|
|
|
|
VL_DO_CLEAR(pushDeletep(m_assigndlyp), m_assigndlyp = NULL);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
AstNode* prep = nodep;
|
2009-10-09 00:42:45 +00:00
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
// Scan back to put the condlvalue above all selects (IE top of the lvalue)
|
2020-04-15 11:58:34 +00:00
|
|
|
while (VN_IS(prep->backp(), NodeSel) || VN_IS(prep->backp(), Sel)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
prep = prep->backp();
|
|
|
|
}
|
|
|
|
FileLine* fl = nodep->fileline();
|
|
|
|
VL_DANGLING(nodep); // Zap it so we don't use it by mistake - use prep
|
2009-10-09 00:42:45 +00:00
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
// Already exists; rather than IF(a,... IF(b... optimize to IF(a&&b,
|
|
|
|
// Saves us teaching V3Const how to optimize, and it won't be needed again.
|
2018-02-02 02:32:58 +00:00
|
|
|
if (AstIf* ifp = VN_CAST(prep->user2p(), If)) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(!needDly, prep, "Should have already converted to non-delay");
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNRelinker replaceHandle;
|
|
|
|
AstNode* earliercondp = ifp->condp()->unlinkFrBack(&replaceHandle);
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* newp = new AstLogAnd(condp->fileline(), condp, earliercondp);
|
|
|
|
UINFO(4, "Edit BOUNDLVALUE " << newp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
replaceHandle.relink(newp);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else {
|
|
|
|
string name = (string("__Vlvbound") + cvtToStr(m_modp->varNumGetInc()));
|
|
|
|
AstVar* varp = new AstVar(fl, AstVarType::MODULETEMP, name, prep->dtypep());
|
2019-05-19 20:13:13 +00:00
|
|
|
m_modp->addStmtp(varp);
|
2009-10-09 00:42:45 +00:00
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNode* abovep = prep->backp(); // Grab above point before lose it w/ next replace
|
|
|
|
prep->replaceWith(new AstVarRef(fl, varp, true));
|
2020-04-28 22:47:59 +00:00
|
|
|
AstIf* newp
|
|
|
|
= new AstIf(fl, condp,
|
|
|
|
(needDly ? static_cast<AstNode*>(
|
|
|
|
new AstAssignDly(fl, prep, new AstVarRef(fl, varp, false)))
|
|
|
|
: static_cast<AstNode*>(
|
|
|
|
new AstAssign(fl, prep, new AstVarRef(fl, varp, false)))),
|
|
|
|
NULL);
|
2019-10-05 11:54:14 +00:00
|
|
|
newp->branchPred(VBranchPred::BP_LIKELY);
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) newp->dumpTree(cout, " _new: ");
|
2019-01-06 22:38:27 +00:00
|
|
|
abovep->addNextStmt(newp, abovep);
|
|
|
|
prep->user2p(newp); // Save so we may LogAnd it next time
|
|
|
|
}
|
2009-10-09 00:42:45 +00:00
|
|
|
}
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
// VISITORS
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(4, " MOD " << nodep << endl);
|
2020-01-20 18:27:27 +00:00
|
|
|
AstNodeModule* origModp = m_modp;
|
|
|
|
{
|
|
|
|
m_modp = nodep;
|
|
|
|
m_constXCvt = true;
|
|
|
|
iterateChildren(nodep);
|
|
|
|
}
|
|
|
|
m_modp = origModp;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstAssignDly* nodep) VL_OVERRIDE {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_assigndlyp = nodep;
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(iterateChildren(nodep), nodep); // May delete nodep.
|
2019-05-19 20:13:13 +00:00
|
|
|
m_assigndlyp = NULL;
|
2009-10-09 00:42:45 +00:00
|
|
|
}
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstAssignW* nodep) VL_OVERRIDE {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_assignwp = nodep;
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(iterateChildren(nodep), nodep); // May delete nodep.
|
2019-05-19 20:13:13 +00:00
|
|
|
m_assignwp = NULL;
|
2009-10-09 00:42:45 +00:00
|
|
|
}
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstCaseItem* nodep) VL_OVERRIDE {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_constXCvt = false; // Avoid losing the X's in casex
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateAndNextNull(nodep->condsp());
|
2019-05-19 20:13:13 +00:00
|
|
|
m_constXCvt = true;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateAndNextNull(nodep->bodysp());
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstNodeDType* nodep) VL_OVERRIDE {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_constXCvt = false; // Avoid losing the X's in casex
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
m_constXCvt = true;
|
2013-02-02 17:43:28 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
void visitEqNeqCase(AstNodeBiop* nodep) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(4, " N/EQCASE->EQ " << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
V3Const::constifyEdit(nodep->lhsp()); // lhsp may change
|
|
|
|
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
2018-02-02 02:32:58 +00:00
|
|
|
if (VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Both sides are constant, node can be constant
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(V3Const::constifyEdit(nodep), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
AstNode* newp;
|
|
|
|
// If we got ==1'bx it can never be true (but 1'bx==1'bx can be!)
|
2018-02-02 02:32:58 +00:00
|
|
|
if (((VN_IS(lhsp, Const) && VN_CAST(lhsp, Const)->num().isFourState())
|
|
|
|
|| (VN_IS(rhsp, Const) && VN_CAST(rhsp, Const)->num().isFourState()))) {
|
2020-04-15 11:58:34 +00:00
|
|
|
newp = new AstConst(nodep->fileline(), AstConst::WidthedValue(), 1,
|
|
|
|
(VN_IS(nodep, EqCase) ? 0 : 1));
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
|
|
|
|
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
2018-10-14 22:39:33 +00:00
|
|
|
if (VN_IS(nodep, EqCase)) {
|
|
|
|
newp = new AstEq(nodep->fileline(), lhsp, rhsp);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else {
|
|
|
|
newp = new AstNeq(nodep->fileline(), lhsp, rhsp);
|
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
// Iterate tree now that we may have gotten rid of Xs
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(newp);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2007-07-18 15:01:39 +00:00
|
|
|
void visitEqNeqWild(AstNodeBiop* nodep) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(4, " N/EQWILD->EQ " << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
V3Const::constifyEdit(nodep->lhsp()); // lhsp may change
|
|
|
|
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
2018-02-02 02:32:58 +00:00
|
|
|
if (VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Both sides are constant, node can be constant
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(V3Const::constifyEdit(nodep), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
AstNode* lhsp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
AstNode* rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
AstNode* newp;
|
2018-02-02 02:32:58 +00:00
|
|
|
if (!VN_IS(rhsp, Const)) {
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->v3error("Unsupported: RHS of ==? or !=? must be "
|
|
|
|
"constant to be synthesizable"); // Says spec.
|
2019-05-19 20:13:13 +00:00
|
|
|
// Replace with anything that won't cause more errors
|
2018-08-25 13:52:45 +00:00
|
|
|
newp = new AstEq(nodep->fileline(), lhsp, rhsp);
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
|
|
|
// X or Z's become mask, ala case statements.
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number nummask(rhsp, rhsp->width());
|
2018-02-02 02:32:58 +00:00
|
|
|
nummask.opBitsNonX(VN_CAST(rhsp, Const)->num());
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number numval(rhsp, rhsp->width());
|
2018-08-25 13:52:45 +00:00
|
|
|
numval.opBitsOne(VN_CAST(rhsp, Const)->num());
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNode* and1p = new AstAnd(nodep->fileline(), lhsp,
|
|
|
|
new AstConst(nodep->fileline(), nummask));
|
|
|
|
AstNode* and2p = new AstConst(nodep->fileline(), numval);
|
2018-10-14 22:39:33 +00:00
|
|
|
if (VN_IS(nodep, EqWild)) {
|
2020-04-15 11:58:34 +00:00
|
|
|
newp = new AstEq(nodep->fileline(), and1p, and2p);
|
|
|
|
} else {
|
|
|
|
newp = new AstNeq(nodep->fileline(), and1p, and2p);
|
|
|
|
}
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
// Iterate tree now that we may have gotten rid of the compare
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(newp);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2007-07-18 15:01:39 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2020-04-15 11:58:34 +00:00
|
|
|
virtual void visit(AstEqCase* nodep) VL_OVERRIDE { visitEqNeqCase(nodep); }
|
|
|
|
virtual void visit(AstNeqCase* nodep) VL_OVERRIDE { visitEqNeqCase(nodep); }
|
|
|
|
virtual void visit(AstEqWild* nodep) VL_OVERRIDE { visitEqNeqWild(nodep); }
|
|
|
|
virtual void visit(AstNeqWild* nodep) VL_OVERRIDE { visitEqNeqWild(nodep); }
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstIsUnknown* nodep) VL_OVERRIDE {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
// Ahh, we're two state, so this is easy
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(4, " ISUNKNOWN->0 " << nodep << endl);
|
2019-05-30 03:18:47 +00:00
|
|
|
AstConst* newp = new AstConst(nodep->fileline(), AstConst::LogicFalse());
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstConst* nodep) VL_OVERRIDE {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (m_constXCvt && nodep->num().isFourState()) {
|
|
|
|
UINFO(4, " CONST4 " << nodep << endl);
|
|
|
|
if (debug() >= 9) nodep->dumpTree(cout, " Const_old: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
// CONST(num) -> VARREF(newvarp)
|
|
|
|
// -> VAR(newvarp)
|
|
|
|
// -> INITIAL(VARREF(newvarp, OR(num_No_Xs,AND(random,num_1s_Where_X))
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number numb1(nodep, nodep->width());
|
2019-05-10 00:03:19 +00:00
|
|
|
numb1.opBitsOne(nodep->num());
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number numbx(nodep, nodep->width());
|
2019-05-10 00:03:19 +00:00
|
|
|
numbx.opBitsXZ(nodep->num());
|
2020-04-15 11:58:34 +00:00
|
|
|
if (v3Global.opt.xAssign() != "unique") {
|
2019-05-10 00:03:19 +00:00
|
|
|
// All X bits just become 0; fastest simulation, but not nice
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number numnew(nodep, numb1.width());
|
|
|
|
if (v3Global.opt.xAssign() == "1") {
|
2019-05-19 20:13:13 +00:00
|
|
|
numnew.opOr(numb1, numbx);
|
|
|
|
} else {
|
|
|
|
numnew.opAssign(numb1);
|
|
|
|
}
|
|
|
|
AstConst* newp = new AstConst(nodep->fileline(), numnew);
|
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(4, " -> " << newp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
|
|
|
// Make a Vxrand variable
|
|
|
|
// We use the special XTEMP type so it doesn't break pure functions
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_modp, nodep, "X number not under module");
|
2020-04-15 11:58:34 +00:00
|
|
|
string newvarname = (string("__Vxrand") + cvtToStr(m_modp->varNumGetInc()));
|
|
|
|
AstVar* newvarp = new AstVar(nodep->fileline(), AstVarType::XTEMP, newvarname,
|
|
|
|
VFlagLogicPacked(), nodep->width());
|
2019-05-19 20:13:13 +00:00
|
|
|
++m_statUnkVars;
|
|
|
|
AstNRelinker replaceHandle;
|
|
|
|
nodep->unlinkFrBack(&replaceHandle);
|
|
|
|
AstNodeVarRef* newref1p = new AstVarRef(nodep->fileline(), newvarp, false);
|
|
|
|
replaceHandle.relink(newref1p); // Replace const with varref
|
2020-04-15 11:58:34 +00:00
|
|
|
AstInitial* newinitp = new AstInitial(
|
|
|
|
nodep->fileline(),
|
|
|
|
new AstAssign(
|
|
|
|
nodep->fileline(), new AstVarRef(nodep->fileline(), newvarp, true),
|
|
|
|
new AstOr(
|
|
|
|
nodep->fileline(), new AstConst(nodep->fileline(), numb1),
|
|
|
|
new AstAnd(nodep->fileline(), new AstConst(nodep->fileline(), numbx),
|
|
|
|
new AstRand(nodep->fileline(), nodep->dtypep(), true)))));
|
2019-05-19 20:13:13 +00:00
|
|
|
// Add inits in front of other statement.
|
|
|
|
// In the future, we should stuff the initp into the module's constructor.
|
|
|
|
AstNode* afterp = m_modp->stmtsp()->unlinkFrBackWithNext();
|
|
|
|
m_modp->addStmtp(newvarp);
|
|
|
|
m_modp->addStmtp(newinitp);
|
|
|
|
m_modp->addStmtp(afterp);
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) newref1p->dumpTree(cout, " _new: ");
|
|
|
|
if (debug() >= 9) newvarp->dumpTree(cout, " _new: ");
|
|
|
|
if (debug() >= 9) newinitp->dumpTree(cout, " _new: ");
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstSel* nodep) VL_OVERRIDE {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!nodep->user1SetOnce()) {
|
|
|
|
// Guard against reading/writing past end of bit vector array
|
|
|
|
AstNode* basefromp = AstArraySel::baseFromp(nodep);
|
|
|
|
bool lvalue = false;
|
2018-02-02 02:32:58 +00:00
|
|
|
if (const AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
lvalue = varrefp->lvalue();
|
|
|
|
}
|
|
|
|
// Find range of dtype we are selecting from
|
|
|
|
// Similar code in V3Const::warnSelect
|
2020-04-15 11:58:34 +00:00
|
|
|
int maxmsb = nodep->fromp()->dtypep()->width() - 1;
|
|
|
|
if (debug() >= 9) nodep->dumpTree(cout, "sel_old: ");
|
2009-10-06 21:19:38 +00:00
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
// If (maxmsb >= selected), we're in bound
|
2018-08-25 13:52:45 +00:00
|
|
|
AstNode* condp = new AstGte(nodep->fileline(),
|
2020-04-15 11:58:34 +00:00
|
|
|
new AstConst(nodep->fileline(), AstConst::WidthedValue(),
|
2019-05-30 03:18:47 +00:00
|
|
|
nodep->lsbp()->width(), maxmsb),
|
2018-08-25 13:52:45 +00:00
|
|
|
nodep->lsbp()->cloneTree(false));
|
2019-05-19 20:13:13 +00:00
|
|
|
// See if the condition is constant true (e.g. always in bound due to constant select)
|
|
|
|
// Note below has null backp(); the Edit function knows how to deal with that.
|
|
|
|
condp = V3Const::constifyEdit(condp);
|
|
|
|
if (condp->isOne()) {
|
|
|
|
// We don't need to add a conditional; we know the existing expression is ok
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(condp->deleteTree(), condp);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (!lvalue) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// SEL(...) -> COND(LTE(bit<=maxmsb), ARRAYSEL(...), {width{1'bx}})
|
|
|
|
AstNRelinker replaceHandle;
|
2019-05-10 00:03:19 +00:00
|
|
|
nodep->unlinkFrBack(&replaceHandle);
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number xnum(nodep, nodep->width());
|
2019-05-10 00:03:19 +00:00
|
|
|
xnum.setAllBitsX();
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* newp = new AstCondBound(nodep->fileline(), condp, nodep,
|
2018-08-25 13:52:45 +00:00
|
|
|
new AstConst(nodep->fileline(), xnum));
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) newp->dumpTree(cout, " _new: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
// Link in conditional
|
|
|
|
replaceHandle.relink(newp);
|
|
|
|
// Added X's, tristate them too
|
2018-05-11 00:55:37 +00:00
|
|
|
iterate(newp);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else { // lvalue
|
2019-05-19 20:13:13 +00:00
|
|
|
replaceBoundLvalue(nodep, condp);
|
|
|
|
}
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
2017-11-23 19:55:32 +00:00
|
|
|
// visit(AstSliceSel) not needed as its bounds are constant and checked
|
|
|
|
// in V3Width.
|
|
|
|
|
2020-01-21 22:35:56 +00:00
|
|
|
virtual void visit(AstArraySel* nodep) VL_OVERRIDE {
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!nodep->user1SetOnce()) {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() == 9) nodep->dumpTree(cout, "-in: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
// Guard against reading/writing past end of arrays
|
|
|
|
AstNode* basefromp = AstArraySel::baseFromp(nodep->fromp());
|
|
|
|
bool lvalue = false;
|
2018-02-02 02:32:58 +00:00
|
|
|
if (const AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
lvalue = varrefp->lvalue();
|
2018-02-02 02:32:58 +00:00
|
|
|
} else if (VN_IS(basefromp, Const)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// If it's a PARAMETER[bit], then basefromp may be a constant instead of a varrefp
|
|
|
|
} else {
|
|
|
|
nodep->v3fatalSrc("No VarRef or Const under ArraySel");
|
|
|
|
}
|
|
|
|
// Find range of dtype we are selecting from
|
|
|
|
int declElements = -1;
|
|
|
|
AstNodeDType* dtypep = nodep->fromp()->dtypep()->skipRefp();
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(dtypep, nodep, "Select of non-selectable type");
|
2018-02-02 02:32:58 +00:00
|
|
|
if (const AstNodeArrayDType* adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
declElements = adtypep->elementsConst();
|
|
|
|
} else {
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->v3error("Select from non-array " << dtypep->prettyTypeName());
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) nodep->dumpTree(cout, "arraysel_old: ");
|
2009-10-06 21:19:38 +00:00
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
// See if the condition is constant true
|
2018-08-25 13:52:45 +00:00
|
|
|
AstNode* condp = new AstGte(nodep->fileline(),
|
2019-05-30 03:18:47 +00:00
|
|
|
new AstConst(nodep->fileline(), AstConst::WidthedValue(),
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->bitp()->width(), declElements - 1),
|
2018-08-25 13:52:45 +00:00
|
|
|
nodep->bitp()->cloneTree(false));
|
2019-05-19 20:13:13 +00:00
|
|
|
// Note below has null backp(); the Edit function knows how to deal with that.
|
|
|
|
condp = V3Const::constifyEdit(condp);
|
|
|
|
if (condp->isOne()) {
|
|
|
|
// We don't need to add a conditional; we know the existing expression is ok
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(condp->deleteTree(), condp);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (!lvalue
|
|
|
|
// Making a scalar would break if we're making an array
|
|
|
|
&& !VN_IS(nodep->dtypep()->skipRefp(), NodeArrayDType)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// ARRAYSEL(...) -> COND(LT(bit<maxbit), ARRAYSEL(...), {width{1'bx}})
|
|
|
|
AstNRelinker replaceHandle;
|
|
|
|
nodep->unlinkFrBack(&replaceHandle);
|
2020-04-15 11:58:34 +00:00
|
|
|
V3Number xnum(nodep, nodep->width());
|
2019-05-10 00:03:19 +00:00
|
|
|
if (nodep->isString()) {
|
|
|
|
xnum = V3Number(V3Number::String(), nodep, "");
|
2019-05-19 20:13:13 +00:00
|
|
|
} else {
|
|
|
|
xnum.setAllBitsX();
|
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* newp = new AstCondBound(nodep->fileline(), condp, nodep,
|
2018-08-25 13:52:45 +00:00
|
|
|
new AstConst(nodep->fileline(), xnum));
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) newp->dumpTree(cout, " _new: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
// Link in conditional, can blow away temp xor
|
|
|
|
replaceHandle.relink(newp);
|
|
|
|
// Added X's, tristate them too
|
2018-05-11 00:55:37 +00:00
|
|
|
iterate(newp);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (!lvalue) { // Mid-multidimension read, just use zero
|
2019-05-19 20:13:13 +00:00
|
|
|
// ARRAYSEL(...) -> ARRAYSEL(COND(LT(bit<maxbit), bit, 0))
|
|
|
|
AstNRelinker replaceHandle;
|
2019-05-10 00:03:19 +00:00
|
|
|
AstNode* bitp = nodep->bitp()->unlinkFrBack(&replaceHandle);
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* newp = new AstCondBound(
|
|
|
|
bitp->fileline(), condp, bitp,
|
|
|
|
new AstConst(bitp->fileline(), AstConst::WidthedValue(), bitp->width(), 0));
|
2019-05-19 20:13:13 +00:00
|
|
|
// Added X's, tristate them too
|
2020-04-15 11:58:34 +00:00
|
|
|
if (debug() >= 9) newp->dumpTree(cout, " _new: ");
|
2019-05-19 20:13:13 +00:00
|
|
|
replaceHandle.relink(newp);
|
2018-05-11 00:55:37 +00:00
|
|
|
iterate(newp);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else { // lvalue
|
2019-05-19 20:13:13 +00:00
|
|
|
replaceBoundLvalue(nodep, condp);
|
|
|
|
}
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
//--------------------
|
2020-04-04 12:31:14 +00:00
|
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(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 UnknownVisitor(AstNetlist* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_modp = NULL;
|
|
|
|
m_assigndlyp = NULL;
|
|
|
|
m_assignwp = NULL;
|
|
|
|
m_constXCvt = false;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterate(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
virtual ~UnknownVisitor() { //
|
2019-05-19 20:13:13 +00:00
|
|
|
V3Stats::addStat("Unknowns, variables created", m_statUnkVars);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
// Unknown class functions
|
|
|
|
|
|
|
|
void V3Unknown::unknownAll(AstNetlist* nodep) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
{ UnknownVisitor visitor(nodep); } // Destruct before checking
|
2017-09-18 02:52:57 +00:00
|
|
|
V3Global::dumpCheckGlobalTree("unknown", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|