2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Add temporaries, such as for inst nodes
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Inst's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Each module:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Pins:
|
|
|
|
|
// Create a wire assign to interconnect to submodule
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Inst.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2015-11-25 03:28:04 +01:00
|
|
|
#include "V3Const.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Inst state, as a visitor of each AstNode
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class InstVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared each Cell:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstPin::user1p() -> bool. True if created assignment already
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstCell* m_cellp = nullptr; // Current cell
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " CELL " << nodep);
|
2025-02-28 03:18:27 +01:00
|
|
|
VL_RESTORER(m_cellp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_cellp = nodep;
|
2020-04-15 13:58:34 +02:00
|
|
|
// VV***** We reset user1p() on each cell!!!
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode::user1ClearTree();
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPin* nodep) override {
|
2018-10-27 23:29:00 +02:00
|
|
|
// PIN(p,expr) -> ASSIGNW(VARXREF(p),expr) (if sub's input)
|
|
|
|
|
// or ASSIGNW(expr,VARXREF(p)) (if sub's output)
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " PIN " << nodep);
|
2020-04-10 05:26:03 +02:00
|
|
|
if (!nodep->user1()) {
|
|
|
|
|
// Simplify it
|
|
|
|
|
V3Inst::pinReconnectSimple(nodep, m_cellp, false);
|
|
|
|
|
}
|
2018-10-27 23:29:00 +02:00
|
|
|
if (!nodep->exprp()) return; // No-connect
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "Pin_oldb");
|
2019-02-28 03:06:07 +01:00
|
|
|
V3Inst::checkOutputShort(nodep);
|
2018-10-27 23:29:00 +02:00
|
|
|
// Use user1p on the PIN to indicate we created an assign for this pin
|
|
|
|
|
if (!nodep->user1SetOnce()) {
|
2018-03-16 04:19:43 +01:00
|
|
|
// Make an ASSIGNW (expr, pin)
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const exprp = VN_AS(nodep->exprp(), NodeExpr)->cloneTree(false);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(exprp->width() == nodep->modVarp()->width(), nodep,
|
|
|
|
|
"Width mismatch, should have been handled in pinReconnectSimple");
|
2024-11-26 00:25:36 +01:00
|
|
|
if (nodep->modVarp()->isInout()) {
|
2018-10-27 23:29:00 +02:00
|
|
|
nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator");
|
|
|
|
|
} else if (nodep->modVarp()->isWritable()) {
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* const rhsp = new AstVarXRef{exprp->fileline(), nodep->modVarp(),
|
|
|
|
|
m_cellp->name(), VAccess::READ};
|
|
|
|
|
AstAssignW* const assp = new AstAssignW{exprp->fileline(), exprp, rhsp};
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
m_cellp->addNextHere(new AstAlways{assp});
|
2018-10-27 23:29:00 +02:00
|
|
|
} else if (nodep->modVarp()->isNonOutput()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't bother moving constants now,
|
|
|
|
|
// we'll be pushing the const down to the cell soon enough.
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const assp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstAssignW{exprp->fileline(),
|
|
|
|
|
new AstVarXRef{exprp->fileline(), nodep->modVarp(),
|
|
|
|
|
m_cellp->name(), VAccess::WRITE},
|
|
|
|
|
exprp};
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
m_cellp->addNextHere(new AstAlways{assp});
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, assp, "", "_new");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (nodep->modVarp()->isIfaceRef()
|
2025-06-05 03:43:46 +02:00
|
|
|
|| (VN_IS(nodep->modVarp()->dtypep()->skipRefp(), UnpackArrayDType)
|
|
|
|
|
&& VN_IS(VN_AS(nodep->modVarp()->dtypep()->skipRefp(), UnpackArrayDType)
|
|
|
|
|
->subDTypep()
|
|
|
|
|
->skipRefp(),
|
|
|
|
|
IfaceRefDType))) {
|
2025-09-30 07:40:17 +02:00
|
|
|
// Create an AstAliasScope for Vars to Cells so we can
|
2019-05-19 22:13:13 +02:00
|
|
|
// link with their scope later
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* const lhsp = new AstVarXRef{exprp->fileline(), nodep->modVarp(),
|
|
|
|
|
m_cellp->name(), VAccess::READ};
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstVarRef* const refp = VN_CAST(exprp, VarRef);
|
|
|
|
|
const AstVarXRef* const xrefp = VN_CAST(exprp, VarXRef);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(refp || xrefp, exprp,
|
|
|
|
|
"Interfaces: Pin is not connected to a VarRef or VarXRef");
|
2025-09-30 07:40:17 +02:00
|
|
|
m_cellp->addNextHere(new AstAliasScope{exprp->fileline(), lhsp, exprp});
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
nodep->v3error("Assigned pin is neither input nor output");
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// We're done with the pin
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save some time
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstNodeExpr*) override {}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeAssign*) override {}
|
|
|
|
|
void visit(AstAlways*) override {}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit InstVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~InstVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2023-03-18 17:17:25 +01:00
|
|
|
class InstDeModVarVisitor final : public VNVisitorConst {
|
2017-04-29 02:03:38 +02:00
|
|
|
// Expand all module variables, and save names for later reference
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
2021-03-12 23:26:53 +01:00
|
|
|
std::map<const std::string, AstVar*> m_modVarNameMap; // Per module, name of cloned variables
|
2017-04-29 02:03:38 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2025-06-05 03:43:46 +02:00
|
|
|
if (VN_IS(nodep->dtypep()->skipRefp(), IfaceRefDType)) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " dm-1-VAR " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
insert(nodep);
|
|
|
|
|
}
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2017-04-29 02:03:38 +02:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstNodeExpr*) override {} // Accelerate
|
2023-03-18 17:17:25 +01:00
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2017-04-29 02:03:38 +02:00
|
|
|
public:
|
|
|
|
|
// METHODS
|
|
|
|
|
void insert(AstVar* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " dmINSERT " << nodep);
|
2020-12-19 00:24:47 +01:00
|
|
|
m_modVarNameMap.emplace(nodep->name(), nodep);
|
2017-04-29 02:03:38 +02:00
|
|
|
}
|
|
|
|
|
AstVar* find(const string& name) {
|
2020-08-16 17:43:49 +02:00
|
|
|
const auto it = m_modVarNameMap.find(name);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (it != m_modVarNameMap.end()) {
|
|
|
|
|
return it->second;
|
|
|
|
|
} else {
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2017-04-29 02:03:38 +02:00
|
|
|
}
|
|
|
|
|
void dump() {
|
2020-11-11 04:10:38 +01:00
|
|
|
for (const auto& itr : m_modVarNameMap) {
|
|
|
|
|
cout << "-namemap: " << itr.first << " -> " << itr.second << endl;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2017-04-29 02:03:38 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-11-17 01:56:16 +01:00
|
|
|
InstDeModVarVisitor() = default;
|
2022-09-16 12:22:11 +02:00
|
|
|
~InstDeModVarVisitor() override = default;
|
2017-09-16 17:06:35 +02:00
|
|
|
void main(AstNodeModule* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " dmMODULE " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modVarNameMap.clear();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep);
|
2017-04-29 02:03:38 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class InstDeVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Find all cells with arrays, and convert to non-arrayed
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
2020-11-14 03:12:18 +01:00
|
|
|
// Range for arrayed instantiations, nullptr for normal instantiations
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstRange* m_cellRangep = nullptr;
|
2020-08-16 15:55:36 +02:00
|
|
|
int m_instSelNum = 0; // Current instantiation count 0..N-1
|
2019-05-19 22:13:13 +02:00
|
|
|
InstDeModVarVisitor m_deModVars; // State of variables for current cell module
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2025-08-21 10:43:37 +02:00
|
|
|
// cppcheck-suppress constVariablePointer
|
2025-06-05 03:43:46 +02:00
|
|
|
AstNode* const dtp = nodep->dtypep()->skipRefp();
|
|
|
|
|
if (VN_IS(dtp, UnpackArrayDType)
|
|
|
|
|
&& VN_IS(VN_AS(dtp, UnpackArrayDType)->subDTypep()->skipRefp(), IfaceRefDType)) {
|
|
|
|
|
if (VN_AS(VN_AS(dtp, UnpackArrayDType)->subDTypep()->skipRefp(), IfaceRefDType)
|
2025-03-21 22:00:49 +01:00
|
|
|
->isVirtual())
|
|
|
|
|
return;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " dv-vec-VAR " << nodep);
|
2025-06-05 03:43:46 +02:00
|
|
|
AstUnpackArrayDType* const arrdtype = VN_AS(dtp, UnpackArrayDType);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstNode* prevp = nullptr;
|
2020-12-07 03:13:56 +01:00
|
|
|
for (int i = arrdtype->lo(); i <= arrdtype->hi(); ++i) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__";
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, "VAR name insert " << varNewName << " " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!m_deModVars.find(varNewName)) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstIfaceRefDType* const ifaceRefp
|
2025-06-05 03:43:46 +02:00
|
|
|
= VN_AS(arrdtype->subDTypep()->skipRefp(), IfaceRefDType)
|
|
|
|
|
->cloneTree(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
arrdtype->addNextHere(ifaceRefp);
|
2020-08-15 16:12:55 +02:00
|
|
|
ifaceRefp->cellp(nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const varNewp = nodep->cloneTree(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
varNewp->name(varNewName);
|
|
|
|
|
varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__");
|
|
|
|
|
varNewp->dtypep(ifaceRefp);
|
|
|
|
|
m_deModVars.insert(varNewp);
|
|
|
|
|
if (!prevp) {
|
|
|
|
|
prevp = varNewp;
|
|
|
|
|
} else {
|
|
|
|
|
prevp->addNextHere(varNewp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (prevp) nodep->addNextHere(prevp);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (prevp && debug() == 9) {
|
2022-11-27 14:31:22 +01:00
|
|
|
prevp->dumpTree("- newintf: ");
|
2020-04-15 13:58:34 +02:00
|
|
|
cout << endl;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2017-03-16 01:03:53 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " CELL " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Find submodule vars
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(nodep->modp(), nodep, "Unlinked");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_deModVars.main(nodep->modp());
|
|
|
|
|
//
|
|
|
|
|
if (nodep->rangep()) {
|
|
|
|
|
m_cellRangep = nodep->rangep();
|
2015-10-24 05:06:24 +02:00
|
|
|
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const ifaceVarp = VN_CAST(nodep->nextp(), Var);
|
2025-08-21 10:43:37 +02:00
|
|
|
// cppcheck-suppress constVariablePointer
|
2025-06-05 03:43:46 +02:00
|
|
|
AstNodeDType* const ifaceVarDtp
|
|
|
|
|
= ifaceVarp ? ifaceVarp->dtypep()->skipRefp() : nullptr;
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool isIface
|
2025-06-05 03:43:46 +02:00
|
|
|
= ifaceVarp && VN_IS(ifaceVarDtp, UnpackArrayDType)
|
|
|
|
|
&& VN_IS(VN_AS(ifaceVarDtp, UnpackArrayDType)->subDTypep()->skipRefp(),
|
2025-03-21 22:00:49 +01:00
|
|
|
IfaceRefDType)
|
2025-06-05 03:43:46 +02:00
|
|
|
&& !VN_AS(VN_AS(ifaceVarDtp, UnpackArrayDType)->subDTypep()->skipRefp(),
|
2025-03-21 22:00:49 +01:00
|
|
|
IfaceRefDType)
|
|
|
|
|
->isVirtual();
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// Make all of the required clones
|
|
|
|
|
for (int i = 0; i < m_cellRangep->elementsConst(); i++) {
|
2020-04-15 13:58:34 +02:00
|
|
|
m_instSelNum
|
2023-03-21 01:44:11 +01:00
|
|
|
= m_cellRangep->ascending() ? (m_cellRangep->elementsConst() - 1 - i) : i;
|
2021-06-21 00:32:57 +02:00
|
|
|
const int instNum = m_cellRangep->loConst() + i;
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2021-11-13 19:50:44 +01:00
|
|
|
AstCell* const newp = nodep->cloneTree(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->addNextHere(newp);
|
|
|
|
|
// Remove ranging and fix name
|
|
|
|
|
newp->rangep()->unlinkFrBack()->deleteTree();
|
2019-09-09 13:50:21 +02:00
|
|
|
// Somewhat illogically, we need to rename the original name of the cell too.
|
2019-05-19 22:13:13 +02:00
|
|
|
// as that is the name users expect for dotting
|
|
|
|
|
// The spec says we add [x], but that won't work in C...
|
2020-04-15 13:58:34 +02:00
|
|
|
newp->name(newp->name() + "__BRA__" + cvtToStr(instNum) + "__KET__");
|
|
|
|
|
newp->origName(newp->origName() + "__BRA__" + cvtToStr(instNum) + "__KET__");
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, " CELL loop " << newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// If this AstCell is actually an interface instantiation, also clone the IfaceRef
|
|
|
|
|
// within the same parent module as the cell
|
|
|
|
|
if (isIface) {
|
2025-06-05 03:43:46 +02:00
|
|
|
AstUnpackArrayDType* const arrdtype = VN_AS(ifaceVarDtp, UnpackArrayDType);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstIfaceRefDType* const origIfaceRefp
|
2025-06-05 03:43:46 +02:00
|
|
|
= VN_AS(arrdtype->subDTypep()->skipRefp(), IfaceRefDType);
|
2020-08-15 16:12:55 +02:00
|
|
|
origIfaceRefp->cellp(nullptr);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const varNewp = ifaceVarp->cloneTree(false);
|
|
|
|
|
AstIfaceRefDType* const ifaceRefp = origIfaceRefp->cloneTree(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
arrdtype->addNextHere(ifaceRefp);
|
|
|
|
|
ifaceRefp->cellp(newp);
|
|
|
|
|
ifaceRefp->cellName(newp->name());
|
|
|
|
|
varNewp->name(varNewp->name() + "__BRA__" + cvtToStr(instNum) + "__KET__");
|
2020-04-15 13:58:34 +02:00
|
|
|
varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(instNum)
|
|
|
|
|
+ "__KET__");
|
2019-05-19 22:13:13 +02:00
|
|
|
varNewp->dtypep(ifaceRefp);
|
|
|
|
|
newp->addNextHere(varNewp);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (debug() == 9) {
|
2022-11-27 14:31:22 +01:00
|
|
|
varNewp->dumpTree("- newintf: ");
|
2020-04-15 13:58:34 +02:00
|
|
|
cout << endl;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Fixup pins
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(newp->pinsp());
|
2020-04-15 13:58:34 +02:00
|
|
|
if (debug() == 9) {
|
2022-11-27 14:31:22 +01:00
|
|
|
newp->dumpTree("- newcell: ");
|
2020-04-15 13:58:34 +02:00
|
|
|
cout << endl;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Done. Delete original
|
2020-08-15 16:12:55 +02:00
|
|
|
m_cellRangep = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (isIface) {
|
2020-01-17 02:17:11 +01:00
|
|
|
ifaceVarp->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(ifaceVarp), ifaceVarp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-08-15 16:12:55 +02:00
|
|
|
m_cellRangep = nullptr;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2015-10-24 05:06:24 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPin* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Any non-direct pins need reconnection with a part-select
|
|
|
|
|
if (!nodep->exprp()) return; // No-connect
|
2025-06-05 03:43:46 +02:00
|
|
|
const AstNodeDType* expDtp = nodep->exprp()->dtypep()->skipRefp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_cellRangep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " PIN " << nodep);
|
2022-01-20 01:14:09 +01:00
|
|
|
const int modwidth = nodep->modVarp()->width();
|
2021-06-21 00:32:57 +02:00
|
|
|
const int expwidth = nodep->exprp()->width();
|
2021-11-26 23:55:36 +01:00
|
|
|
const std::pair<uint32_t, uint32_t> pinDim
|
2025-06-05 03:43:46 +02:00
|
|
|
= nodep->modVarp()->dtypep()->skipRefp()->dimensions(false);
|
|
|
|
|
const std::pair<uint32_t, uint32_t> expDim = expDtp->dimensions(false);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, " PINVAR " << nodep->modVarp());
|
|
|
|
|
UINFO(4, " EXP " << nodep->exprp());
|
2023-10-14 21:20:22 +02:00
|
|
|
UINFO(4, " expwidth=" << expwidth << " modwidth=" << modwidth
|
|
|
|
|
<< " expDim(p,u)=" << expDim.first << "," << expDim.second
|
2025-05-23 02:29:32 +02:00
|
|
|
<< " pinDim(p,u)=" << pinDim.first << "," << pinDim.second);
|
2023-10-14 21:19:19 +02:00
|
|
|
if (expDim.second == pinDim.second + 1) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Connection to array, where array dimensions match the instant dimension
|
2025-06-05 03:43:46 +02:00
|
|
|
const AstRange* const rangep = VN_AS(expDtp, UnpackArrayDType)->rangep();
|
2023-03-21 01:44:11 +01:00
|
|
|
const int arraySelNum = rangep->ascending()
|
2021-06-21 00:32:57 +02:00
|
|
|
? (rangep->elementsConst() - 1 - m_instSelNum)
|
|
|
|
|
: m_instSelNum;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* exprp = VN_AS(nodep->exprp(), NodeExpr)->unlinkFrBack();
|
2022-11-20 23:40:38 +01:00
|
|
|
exprp = new AstArraySel{exprp->fileline(), exprp, arraySelNum};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->exprp(exprp);
|
2022-01-20 01:14:09 +01:00
|
|
|
} else if (expwidth == modwidth) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// NOP: Arrayed instants: widths match so connect to each instance
|
2022-01-20 01:14:09 +01:00
|
|
|
} else if (expwidth == modwidth * m_cellRangep->elementsConst()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Arrayed instants: one bit for each of the instants (each
|
2022-01-20 01:14:09 +01:00
|
|
|
// assign is 1 modwidth wide)
|
2023-03-21 01:44:11 +01:00
|
|
|
if (m_cellRangep->ascending()) {
|
|
|
|
|
nodep->exprp()->v3warn(ASCRANGE, "Ascending instance range connecting to "
|
|
|
|
|
"vector: left < right of instance range: ["
|
|
|
|
|
<< m_cellRangep->leftConst() << ":"
|
|
|
|
|
<< m_cellRangep->rightConst() << "]");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* exprp = VN_AS(nodep->exprp(), NodeExpr)->unlinkFrBack();
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool inputPin = nodep->modVarp()->isNonOutput();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!inputPin
|
|
|
|
|
&& !VN_IS(exprp, VarRef)
|
|
|
|
|
// V3Const will collapse the SEL with the one we're about to make
|
2020-12-06 19:49:44 +01:00
|
|
|
&& !VN_IS(exprp, Concat) && !VN_IS(exprp, Replicate) && !VN_IS(exprp, Sel)) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Per-bit array instantiations "
|
|
|
|
|
"with output connections to non-wires.");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Note spec allows more complicated matches such as slices and such
|
|
|
|
|
}
|
2022-11-20 23:40:38 +01:00
|
|
|
exprp = new AstSel{exprp->fileline(), exprp, modwidth * m_instSelNum, modwidth};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->exprp(exprp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("Width mismatch; V3Width should have errored out.");
|
|
|
|
|
}
|
2020-04-18 01:30:53 +02:00
|
|
|
} // end expanding ranged cell
|
2021-11-26 23:55:36 +01:00
|
|
|
else if (AstArraySel* const arrselp = VN_CAST(nodep->exprp(), ArraySel)) {
|
|
|
|
|
if (const AstUnpackArrayDType* const arrp
|
2025-06-05 03:43:46 +02:00
|
|
|
= VN_CAST(arrselp->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) {
|
|
|
|
|
if (!VN_IS(arrp->subDTypep()->skipRefp(), IfaceRefDType)) return;
|
|
|
|
|
if (VN_AS(arrp->subDTypep()->skipRefp(), IfaceRefDType)->isVirtual()) return;
|
2020-04-18 01:30:53 +02:00
|
|
|
// Interface pin attaches to one element of arrayed interface
|
2023-11-12 19:30:48 +01:00
|
|
|
V3Const::constifyParamsEdit(arrselp->bitp());
|
|
|
|
|
const AstConst* const constp = VN_CAST(arrselp->bitp(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!constp) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(
|
|
|
|
|
E_UNSUPPORTED,
|
2020-04-15 13:58:34 +02:00
|
|
|
"Unsupported: Non-constant index when passing interface to module");
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2025-06-28 15:43:02 +02:00
|
|
|
const string index = AstNode::encodeNumber(constp->toSInt() + arrp->lo());
|
2023-11-12 19:30:48 +01:00
|
|
|
if (VN_IS(arrselp->fromp(), SliceSel))
|
2025-09-20 14:19:42 +02:00
|
|
|
arrselp->fromp()->v3warn(E_UNSUPPORTED, "Unsupported: interface slices");
|
2023-11-12 19:30:48 +01:00
|
|
|
const AstVarRef* const varrefp = VN_CAST(arrselp->fromp(), VarRef);
|
2020-11-09 04:43:32 +01:00
|
|
|
UASSERT_OBJ(varrefp, arrselp, "No interface varref under array");
|
2022-11-20 23:40:38 +01:00
|
|
|
AstVarXRef* const newp = new AstVarXRef{
|
2021-11-13 19:50:44 +01:00
|
|
|
nodep->fileline(), varrefp->name() + "__BRA__" + index + "__KET__", "",
|
2022-11-20 23:40:38 +01:00
|
|
|
VAccess::WRITE};
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->dtypep(nodep->modVarp()->dtypep());
|
2020-11-25 03:56:03 +01:00
|
|
|
newp->classOrPackagep(varrefp->classOrPackagep());
|
2019-05-19 22:13:13 +02:00
|
|
|
arrselp->addNextHere(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(arrselp->unlinkFrBack()->deleteTree(), arrselp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const pinVarp = nodep->modVarp();
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstUnpackArrayDType* const pinArrp
|
2025-06-05 03:43:46 +02:00
|
|
|
= VN_CAST(pinVarp->dtypep()->skipRefp(), UnpackArrayDType);
|
|
|
|
|
if (!pinArrp || !VN_IS(pinArrp->subDTypep()->skipRefp(), IfaceRefDType)) return;
|
|
|
|
|
if (VN_AS(pinArrp->subDTypep()->skipRefp(), IfaceRefDType)->isVirtual()) return;
|
2020-04-18 01:30:53 +02:00
|
|
|
// Arrayed pin/var attaches to arrayed submodule lower port/var, expand it
|
2020-08-15 16:12:55 +02:00
|
|
|
AstNode* prevp = nullptr;
|
|
|
|
|
AstNode* prevPinp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
// Clone the var referenced by the pin, and clone each var referenced by the varref
|
|
|
|
|
// Clone pin varp:
|
2021-03-12 01:22:19 +01:00
|
|
|
for (int in = 0; in < pinArrp->elementsConst(); ++in) { // 0 = leftmost
|
2021-11-26 23:55:36 +01:00
|
|
|
const int i = pinArrp->left() + in * pinArrp->declRange().leftToRightInc();
|
2021-06-21 00:32:57 +02:00
|
|
|
const string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__";
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVar* varNewp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
// Only clone the var once for all usages of a given child module
|
|
|
|
|
if (!pinVarp->backp()) {
|
|
|
|
|
varNewp = m_deModVars.find(varNewName);
|
|
|
|
|
} else {
|
2025-06-05 03:43:46 +02:00
|
|
|
AstIfaceRefDType* const ifaceRefp
|
|
|
|
|
= VN_AS(pinArrp->subDTypep()->skipRefp(), IfaceRefDType);
|
2020-08-15 16:12:55 +02:00
|
|
|
ifaceRefp->cellp(nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
varNewp = pinVarp->cloneTree(false);
|
|
|
|
|
varNewp->name(varNewName);
|
|
|
|
|
varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__");
|
|
|
|
|
varNewp->dtypep(ifaceRefp);
|
|
|
|
|
m_deModVars.insert(varNewp);
|
|
|
|
|
if (!prevp) {
|
|
|
|
|
prevp = varNewp;
|
|
|
|
|
} else {
|
|
|
|
|
prevp->addNextHere(varNewp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!varNewp) {
|
2020-05-23 16:34:58 +02:00
|
|
|
if (debug() >= 9) m_deModVars.dump(); // LCOV_EXCL_LINE
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->v3fatalSrc("Module dearray failed for "
|
2020-04-15 13:58:34 +02:00
|
|
|
<< AstNode::prettyNameQ(varNewName));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// But clone the pin for each module instance
|
|
|
|
|
// Now also clone the pin itself and update its varref
|
2021-11-13 19:50:44 +01:00
|
|
|
AstPin* const newp = nodep->cloneTree(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
newp->modVarp(varNewp);
|
|
|
|
|
newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__");
|
|
|
|
|
// And replace exprp with a new varxref
|
2021-03-12 01:22:19 +01:00
|
|
|
const AstVarRef* varrefp = VN_CAST(newp->exprp(), VarRef); // Maybe null
|
|
|
|
|
int expr_i = i;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstSliceSel* const slicep = VN_CAST(newp->exprp(), SliceSel)) {
|
2021-10-22 14:56:48 +02:00
|
|
|
varrefp = VN_AS(slicep->fromp(), VarRef);
|
2022-07-13 00:02:45 +02:00
|
|
|
UASSERT_OBJ(VN_IS(slicep->rhsp(), Const), slicep, "Slices should be constant");
|
2021-11-26 23:55:36 +01:00
|
|
|
const int slice_index
|
2021-03-12 01:22:19 +01:00
|
|
|
= slicep->declRange().left() + in * slicep->declRange().leftToRightInc();
|
2025-06-05 03:43:46 +02:00
|
|
|
const auto* const exprArrp
|
|
|
|
|
= VN_AS(varrefp->dtypep()->skipRefp(), UnpackArrayDType);
|
2021-03-12 01:22:19 +01:00
|
|
|
UASSERT_OBJ(exprArrp, slicep, "Slice of non-array");
|
|
|
|
|
expr_i = slice_index + exprArrp->lo();
|
|
|
|
|
} else if (!varrefp) {
|
|
|
|
|
newp->exprp()->v3error("Unexpected connection to arrayed port");
|
2021-11-26 23:55:36 +01:00
|
|
|
} else if (const auto* const exprArrp
|
2025-06-05 03:43:46 +02:00
|
|
|
= VN_CAST(varrefp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
2021-03-12 01:22:19 +01:00
|
|
|
expr_i = exprArrp->left() + in * exprArrp->declRange().leftToRightInc();
|
2020-04-18 01:30:53 +02:00
|
|
|
}
|
2021-03-12 01:22:19 +01:00
|
|
|
|
2021-06-21 00:32:57 +02:00
|
|
|
const string newname = varrefp->name() + "__BRA__" + cvtToStr(expr_i) + "__KET__";
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarXRef* const newVarXRefp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstVarXRef{nodep->fileline(), newname, "", VAccess::WRITE};
|
2019-05-19 22:13:13 +02:00
|
|
|
newVarXRefp->varp(newp->modVarp());
|
|
|
|
|
newp->exprp()->unlinkFrBack()->deleteTree();
|
|
|
|
|
newp->exprp(newVarXRefp);
|
|
|
|
|
if (!prevPinp) {
|
|
|
|
|
prevPinp = newp;
|
|
|
|
|
} else {
|
|
|
|
|
prevPinp->addNextHere(newp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (prevp) {
|
|
|
|
|
pinVarp->replaceWith(prevp);
|
|
|
|
|
pushDeletep(pinVarp);
|
|
|
|
|
} // else pinVarp already unlinked when another instance did this step
|
|
|
|
|
nodep->replaceWith(prevPinp);
|
2024-05-08 14:36:24 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2025-05-30 04:11:47 +02:00
|
|
|
void visit(AstArraySel* nodep) override {
|
|
|
|
|
if (const AstUnpackArrayDType* const arrp
|
|
|
|
|
= VN_CAST(nodep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) {
|
|
|
|
|
if (!VN_IS(arrp->subDTypep()->skipRefp(), IfaceRefDType)) return;
|
|
|
|
|
if (VN_AS(arrp->subDTypep()->skipRefp(), IfaceRefDType)->isVirtual()) return;
|
|
|
|
|
V3Const::constifyParamsEdit(nodep->bitp());
|
|
|
|
|
const AstConst* const constp = VN_CAST(nodep->bitp(), Const);
|
|
|
|
|
if (!constp) {
|
|
|
|
|
nodep->bitp()->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Non-constant index in RHS interface array selection");
|
|
|
|
|
return;
|
2025-03-21 22:00:49 +01:00
|
|
|
}
|
2025-06-28 15:43:02 +02:00
|
|
|
const string index = AstNode::encodeNumber(constp->toSInt() + arrp->lo());
|
2025-05-30 04:11:47 +02:00
|
|
|
const AstVarRef* const varrefp = VN_CAST(nodep->fromp(), VarRef);
|
|
|
|
|
UASSERT_OBJ(varrefp, nodep, "No interface varref under array");
|
|
|
|
|
AstVarXRef* const newp = new AstVarXRef{
|
|
|
|
|
nodep->fileline(), varrefp->name() + "__BRA__" + index + "__KET__", "",
|
|
|
|
|
VAccess::READ};
|
|
|
|
|
newp->dtypep(arrp->subDTypep());
|
|
|
|
|
newp->classOrPackagep(varrefp->classOrPackagep());
|
|
|
|
|
nodep->addNextHere(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNodeAssign* nodep) override {
|
|
|
|
|
if (AstSliceSel* const arrslicep = VN_CAST(nodep->rhsp(), SliceSel)) {
|
2025-03-21 22:00:49 +01:00
|
|
|
if (const AstUnpackArrayDType* const arrp
|
2025-06-05 03:43:46 +02:00
|
|
|
= VN_CAST(arrslicep->fromp()->dtypep()->skipRefp(), UnpackArrayDType)) {
|
|
|
|
|
if (!VN_IS(arrp->subDTypep()->skipRefp(), IfaceRefDType)) return;
|
|
|
|
|
if (VN_AS(arrp->subDTypep()->skipRefp(), IfaceRefDType)->isVirtual()) return;
|
2025-03-21 22:00:49 +01:00
|
|
|
arrslicep->v3warn(E_UNSUPPORTED, "Interface slices unsupported");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2025-05-30 04:11:47 +02:00
|
|
|
if (const AstUnpackArrayDType* const rhsarrp
|
|
|
|
|
= VN_CAST(nodep->rhsp()->dtypep()->skipRefp(), UnpackArrayDType)) {
|
|
|
|
|
if (const AstUnpackArrayDType* const lhsarrp
|
|
|
|
|
= VN_CAST(nodep->lhsp()->dtypep()->skipRefp(), UnpackArrayDType)) {
|
2025-03-21 22:00:49 +01:00
|
|
|
// copy between arrays
|
|
|
|
|
if (!VN_IS(lhsarrp->subDTypep()->skipRefp(), IfaceRefDType)) return;
|
|
|
|
|
if (!VN_IS(rhsarrp->subDTypep()->skipRefp(), IfaceRefDType)) return;
|
|
|
|
|
if (VN_AS(rhsarrp->subDTypep()->skipRefp(), IfaceRefDType)->isVirtual())
|
|
|
|
|
return;
|
2025-05-30 04:11:47 +02:00
|
|
|
if (!VN_AS(lhsarrp->subDTypep()->skipRefp(), IfaceRefDType)->isVirtual()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unexpected target of interface assignment ["
|
|
|
|
|
<< rhsarrp->prettyDTypeNameQ() << "]");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-03-21 22:00:49 +01:00
|
|
|
if (lhsarrp->elementsConst() != rhsarrp->elementsConst()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Array size mismatch in interface assignment");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < lhsarrp->elementsConst(); ++i) {
|
|
|
|
|
const string index = AstNode::encodeNumber(i);
|
|
|
|
|
AstNodeExpr* lhsp = nullptr;
|
|
|
|
|
if (AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef)) {
|
2025-05-30 04:11:47 +02:00
|
|
|
AstVarRef* const newvarp = varrefp->cloneTree(false);
|
|
|
|
|
AstArraySel* newarrselp = new AstArraySel{
|
|
|
|
|
nodep->fileline(), newvarp,
|
|
|
|
|
new AstConst{nodep->fileline(), static_cast<uint32_t>(i)}};
|
|
|
|
|
lhsp = newarrselp;
|
2025-03-21 22:00:49 +01:00
|
|
|
} else if (AstMemberSel* const prevselp
|
|
|
|
|
= VN_CAST(nodep->lhsp(), MemberSel)) {
|
|
|
|
|
AstMemberSel* membselp = prevselp->cloneTree(false);
|
2025-05-30 04:11:47 +02:00
|
|
|
AstArraySel* newarrselp = new AstArraySel{
|
2025-03-21 22:00:49 +01:00
|
|
|
nodep->fileline(), membselp,
|
2025-05-30 04:11:47 +02:00
|
|
|
new AstConst{nodep->fileline(), static_cast<uint32_t>(i)}};
|
2025-03-21 22:00:49 +01:00
|
|
|
lhsp = newarrselp;
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
2025-10-14 02:30:47 +02:00
|
|
|
"Unsupported LHS node type in array assignment");
|
2025-03-21 22:00:49 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const AstVarRef* const rhsrefp = VN_CAST(nodep->rhsp(), VarRef);
|
|
|
|
|
AstVarXRef* const rhsp = new AstVarXRef{
|
|
|
|
|
nodep->fileline(), rhsrefp->name() + "__BRA__" + index + "__KET__", "",
|
|
|
|
|
VAccess::READ};
|
|
|
|
|
rhsp->dtypep(rhsarrp->subDTypep()->skipRefp());
|
|
|
|
|
rhsp->classOrPackagep(rhsrefp->classOrPackagep());
|
2025-08-09 00:21:12 +02:00
|
|
|
AstAssign* const assignp = new AstAssign{nodep->fileline(), lhsp, rhsp};
|
2025-03-21 22:00:49 +01:00
|
|
|
nodep->addNextHere(assignp);
|
|
|
|
|
}
|
2025-05-30 04:11:47 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
|
|
|
|
return;
|
2025-03-21 22:00:49 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-30 04:11:47 +02:00
|
|
|
iterateChildren(nodep);
|
2025-03-21 22:00:49 +01:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2025-05-30 04:11:47 +02:00
|
|
|
void visit(AstNew* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstMethodCall* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
void visit(AstArg* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit InstDeVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~InstDeVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2015-05-14 02:56:16 +02:00
|
|
|
// Inst static function
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class InstStatic final {
|
2020-11-17 01:56:16 +01:00
|
|
|
InstStatic() = default; // Static class
|
2015-05-14 02:56:16 +02:00
|
|
|
|
2025-08-21 10:43:37 +02:00
|
|
|
static AstNodeExpr* extendOrSel(FileLine* fl, AstNodeExpr* rhsp, const AstNode* cmpWidthp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (cmpWidthp->width() > rhsp->width()) {
|
2022-11-13 21:33:11 +01:00
|
|
|
rhsp = (rhsp->isSigned() ? static_cast<AstNodeExpr*>(new AstExtendS{fl, rhsp})
|
|
|
|
|
: static_cast<AstNodeExpr*>(new AstExtend{fl, rhsp}));
|
2020-04-15 13:58:34 +02:00
|
|
|
// Need proper widthMin, which may differ from AstSel created above
|
|
|
|
|
rhsp->dtypeFrom(cmpWidthp);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (cmpWidthp->width() < rhsp->width()) {
|
2021-07-12 00:42:01 +02:00
|
|
|
rhsp = new AstSel{fl, rhsp, 0, cmpWidthp->width()};
|
2020-04-15 13:58:34 +02:00
|
|
|
// Need proper widthMin, which may differ from AstSel created above
|
|
|
|
|
rhsp->dtypeFrom(cmpWidthp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// else don't change dtype, as might be e.g. array of something
|
|
|
|
|
return rhsp;
|
2015-05-14 02:56:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2020-04-15 13:58:34 +02:00
|
|
|
static AstAssignW* pinReconnectSimple(AstPin* pinp, AstCell* cellp, bool forTristate,
|
|
|
|
|
bool alwaysCvt) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// If a pin connection is "simple" leave it as-is
|
|
|
|
|
// Else create a intermediate wire to perform the interconnect
|
|
|
|
|
// Return the new assignment, if one was made
|
2022-12-03 00:46:38 +01:00
|
|
|
// Note this module calls cloneTree() via new AstVar
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const pinVarp = pinp->modVarp();
|
2020-04-10 05:26:03 +02:00
|
|
|
if (!pinp->exprp()) {
|
|
|
|
|
// No-connect, perhaps promote based on `unconnected_drive,
|
|
|
|
|
// otherwise done
|
|
|
|
|
if (pinVarp->direction() == VDirection::INPUT
|
|
|
|
|
&& cellp->modp()->unconnectedDrive().isSetTrue()) {
|
2023-05-06 04:36:51 +02:00
|
|
|
pinp->exprp(new AstConst{pinp->fileline(), AstConst::All1{}});
|
2020-04-10 05:26:03 +02:00
|
|
|
} else if (pinVarp->direction() == VDirection::INPUT
|
|
|
|
|
&& cellp->modp()->unconnectedDrive().isSetFalse()) {
|
2023-05-06 04:36:51 +02:00
|
|
|
pinp->exprp(new AstConst{pinp->fileline(), AstConst::All0{}});
|
2020-04-10 05:26:03 +02:00
|
|
|
} else {
|
2020-08-15 16:12:55 +02:00
|
|
|
return nullptr;
|
2020-04-10 05:26:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarRef* const connectRefp = VN_CAST(pinp->exprp(), VarRef);
|
|
|
|
|
const AstVarXRef* const connectXRefp = VN_CAST(pinp->exprp(), VarXRef);
|
2024-02-22 19:33:23 +01:00
|
|
|
const AstNodeDType* const pinDTypep = pinVarp->dtypep()->skipRefp();
|
|
|
|
|
const AstBasicDType* const pinBasicp = VN_CAST(pinDTypep, BasicDType);
|
|
|
|
|
const AstNodeDType* const connDTypep
|
|
|
|
|
= connectRefp ? connectRefp->varp()->dtypep()->skipRefp() : nullptr;
|
|
|
|
|
const AstBasicDType* const connBasicp = VN_CAST(connDTypep, BasicDType);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstAssignW* assignp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
2024-02-22 19:33:23 +01:00
|
|
|
if (!alwaysCvt && connectRefp && connDTypep->sameTree(pinDTypep)
|
2019-05-19 22:13:13 +02:00
|
|
|
&& !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types
|
|
|
|
|
// Done. Same data type
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (!alwaysCvt && connectRefp && connectRefp->varp()->isIfaceRef()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Done. Interface
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (!alwaysCvt && connectXRefp && connectXRefp->varp()
|
2019-05-19 22:13:13 +02:00
|
|
|
&& connectXRefp->varp()->isIfaceRef()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (!alwaysCvt && connBasicp && pinBasicp
|
2019-05-19 22:13:13 +02:00
|
|
|
&& connBasicp->width() == pinBasicp->width()
|
2020-12-07 03:13:56 +01:00
|
|
|
&& connBasicp->lo() == pinBasicp->lo()
|
2020-04-15 13:58:34 +02:00
|
|
|
&& !connectRefp->varp()
|
|
|
|
|
->isSc() // Need the signal as a 'shell' to convert types
|
2019-05-19 22:13:13 +02:00
|
|
|
&& connBasicp->width() == pinVarp->width()) {
|
|
|
|
|
// Done. One to one interconnect won't need a temporary variable.
|
2018-02-02 03:32:58 +01:00
|
|
|
} else if (!alwaysCvt && !forTristate && VN_IS(pinp->exprp(), Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Done. Constant.
|
|
|
|
|
} else {
|
|
|
|
|
// Make a new temp wire
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(9, pinp, "", "in_pin");
|
2019-02-28 03:06:07 +01:00
|
|
|
V3Inst::checkOutputShort(pinp);
|
2025-08-18 10:55:54 +02:00
|
|
|
// Simplify, so stuff like '{a[0], b[0]}[1]' produced during
|
|
|
|
|
// instance array expansion are brought to normal 'a[0]'
|
|
|
|
|
AstNodeExpr* const pinexprp
|
|
|
|
|
= V3Const::constifyEdit(VN_AS(pinp->exprp(), NodeExpr)->unlinkFrBack());
|
2021-11-26 23:55:36 +01:00
|
|
|
const string newvarname
|
2023-04-07 03:00:10 +02:00
|
|
|
= (string{pinVarp->isWritable() ? "__Vcellout" : "__Vcellinp"}
|
2020-04-15 13:58:34 +02:00
|
|
|
// Prevent name conflict if both tri & non-tri add signals
|
|
|
|
|
+ (forTristate ? "t" : "") + "__" + cellp->name() + "__" + pinp->name());
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const newvarp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstVar{pinVarp->fileline(), VVarType::MODULETEMP, newvarname, pinVarp};
|
2018-10-27 23:29:00 +02:00
|
|
|
// Important to add statement next to cell, in case there is a
|
|
|
|
|
// generate with same named cell
|
|
|
|
|
cellp->addNextHere(newvarp);
|
2024-11-26 00:25:36 +01:00
|
|
|
if (pinVarp->isInout()) {
|
2018-03-10 22:32:04 +01:00
|
|
|
pinVarp->v3fatalSrc("Unsupported: Inout connections to pins must be"
|
|
|
|
|
" direct one-to-one connection (without any expression)");
|
2018-10-27 23:29:00 +02:00
|
|
|
} else if (pinVarp->isWritable()) {
|
|
|
|
|
// See also V3Inst
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* rhsp = new AstVarRef{pinp->fileline(), newvarp, VAccess::READ};
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "pinRecon width " << pinVarp->width() << " >? " << rhsp->width() << " >? "
|
2025-05-23 02:29:32 +02:00
|
|
|
<< pinexprp->width());
|
2018-08-25 15:52:45 +02:00
|
|
|
rhsp = extendOrSel(pinp->fileline(), rhsp, pinVarp);
|
2022-11-20 23:40:38 +01:00
|
|
|
pinp->exprp(new AstVarRef{newvarp->fileline(), newvarp, VAccess::WRITE});
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const rhsSelp = extendOrSel(pinp->fileline(), rhsp, pinexprp);
|
2022-11-20 23:40:38 +01:00
|
|
|
assignp = new AstAssignW{pinp->fileline(), pinexprp, rhsSelp};
|
2018-10-27 23:29:00 +02:00
|
|
|
} else {
|
|
|
|
|
// V3 width should have range/extended to make the widths correct
|
2022-11-20 23:40:38 +01:00
|
|
|
assignp = new AstAssignW{pinp->fileline(),
|
|
|
|
|
new AstVarRef{pinp->fileline(), newvarp, VAccess::WRITE},
|
|
|
|
|
pinexprp};
|
|
|
|
|
pinp->exprp(new AstVarRef{pinexprp->fileline(), newvarp, VAccess::READ});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
if (assignp) cellp->addNextHere(new AstAlways{assignp});
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, pinp, "", "out");
|
|
|
|
|
// UINFOTREE(1, assignp, "", "aout");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return assignp;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2015-05-14 02:56:16 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Inst class functions
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, bool forTristate,
|
|
|
|
|
bool alwaysCvt) {
|
2018-03-16 04:19:43 +01:00
|
|
|
return InstStatic::pinReconnectSimple(pinp, cellp, forTristate, alwaysCvt);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-22 22:43:49 +02:00
|
|
|
void V3Inst::checkOutputShort(const AstPin* nodep) {
|
2019-02-28 03:06:07 +01:00
|
|
|
if (nodep->modVarp()->direction() == VDirection::OUTPUT) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (VN_IS(nodep->exprp(), Const) || VN_IS(nodep->exprp(), Extend)
|
2019-02-28 03:06:07 +01:00
|
|
|
|| (VN_IS(nodep->exprp(), Concat)
|
2021-10-22 14:56:48 +02:00
|
|
|
&& (VN_IS(VN_AS(nodep->exprp(), Concat)->lhsp(), Const)))) {
|
2019-02-28 03:06:07 +01:00
|
|
|
// Uses v3warn for error, as might be found multiple times
|
|
|
|
|
nodep->v3warn(E_PORTSHORT, "Output port is connected to a constant pin,"
|
2020-04-15 13:58:34 +02:00
|
|
|
" electrical short");
|
2019-02-28 03:06:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Inst class visitor
|
|
|
|
|
|
|
|
|
|
void V3Inst::instAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ InstVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("inst", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Inst::dearrayAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ InstDeVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("dearray", 0, dumpTreeEitherLevel() >= 6);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|