1999-10-31 21:08:24 +01:00
|
|
|
/*
|
2025-07-01 08:47:38 +02:00
|
|
|
* Copyright (c) 1999-2025 Stephen Williams (steve@icarus.com)
|
2012-09-04 00:56:40 +02:00
|
|
|
* Copyright CERN 2012 / Stephen Williams (steve@icarus.com)
|
1999-10-31 21:08:24 +01:00
|
|
|
*
|
|
|
|
|
* This source code is free software; you can redistribute it
|
|
|
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
|
|
|
* General Public License as published by the Free Software
|
|
|
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program 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.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
2012-08-29 03:41:23 +02:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
1999-10-31 21:08:24 +01:00
|
|
|
*/
|
|
|
|
|
|
2001-07-25 05:10:48 +02:00
|
|
|
# include "config.h"
|
|
|
|
|
|
1999-10-31 21:08:24 +01:00
|
|
|
# include "PExpr.h"
|
2022-12-29 22:36:08 +01:00
|
|
|
# include "PPackage.h"
|
1999-10-31 21:08:24 +01:00
|
|
|
# include "netlist.h"
|
2000-02-16 04:58:27 +01:00
|
|
|
# include "netmisc.h"
|
2012-01-01 02:27:30 +01:00
|
|
|
# include "netstruct.h"
|
2012-09-15 19:27:43 +02:00
|
|
|
# include "netvector.h"
|
2000-03-17 22:50:25 +01:00
|
|
|
# include "compiler.h"
|
1999-10-31 21:08:24 +01:00
|
|
|
|
2008-01-05 00:23:47 +01:00
|
|
|
# include <cstdlib>
|
|
|
|
|
# include <cstring>
|
2001-07-25 05:10:48 +02:00
|
|
|
# include <iostream>
|
2007-01-31 05:21:10 +01:00
|
|
|
# include "ivl_assert.h"
|
2001-07-25 05:10:48 +02:00
|
|
|
|
2021-11-04 17:12:04 +01:00
|
|
|
using namespace std;
|
|
|
|
|
|
2002-11-09 20:20:48 +01:00
|
|
|
/*
|
|
|
|
|
* The concatenation is also OK an an l-value. This method elaborates
|
2004-12-11 03:31:25 +01:00
|
|
|
* it as a structural l-value. The return values is the *input* net of
|
|
|
|
|
* the l-value, which may feed via part selects to the final
|
|
|
|
|
* destination. The caller can connect gate outputs to this signal to
|
|
|
|
|
* make the l-value connections.
|
2002-11-09 20:20:48 +01:00
|
|
|
*/
|
2006-04-28 06:28:35 +02:00
|
|
|
NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope,
|
2025-07-01 08:47:38 +02:00
|
|
|
bool bidirectional_flag,
|
|
|
|
|
bool var_allowed_in_sv) const
|
2002-11-09 20:20:48 +01:00
|
|
|
{
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*this, scope);
|
2002-11-09 20:20:48 +01:00
|
|
|
|
2022-02-17 10:48:43 +01:00
|
|
|
std::vector<NetNet*> nets(parms_.size());
|
2004-12-11 03:31:25 +01:00
|
|
|
unsigned width = 0;
|
2002-11-09 20:20:48 +01:00
|
|
|
unsigned errors = 0;
|
|
|
|
|
|
|
|
|
|
if (repeat_) {
|
2024-11-10 02:08:59 +01:00
|
|
|
cerr << repeat_->get_fileline() << ": error: "
|
|
|
|
|
<< "repeat concatenations are not allowed in net l-values."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
2002-11-09 20:20:48 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Elaborate the operands of the concatenation. */
|
2022-02-17 10:48:43 +01:00
|
|
|
for (unsigned idx = 0 ; idx < nets.size() ; idx += 1) {
|
2003-01-19 01:35:39 +01:00
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
if (debug_elaborate) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Elaborate subexpression "
|
2022-02-17 10:48:43 +01:00
|
|
|
<< idx << " of " << nets.size() << " l-values: "
|
2004-12-11 03:31:25 +01:00
|
|
|
<< *parms_[idx] << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-19 01:35:39 +01:00
|
|
|
if (parms_[idx] == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: Empty expressions "
|
2003-01-19 01:35:39 +01:00
|
|
|
<< "not allowed in concatenations." << endl;
|
|
|
|
|
errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-28 06:28:35 +02:00
|
|
|
if (bidirectional_flag) {
|
2025-07-01 08:47:38 +02:00
|
|
|
nets[idx] = parms_[idx]->elaborate_bi_net(des, scope, var_allowed_in_sv);
|
2006-04-28 06:28:35 +02:00
|
|
|
} else {
|
2025-07-01 08:47:38 +02:00
|
|
|
nets[idx] = parms_[idx]->elaborate_lnet(des, scope, var_allowed_in_sv);
|
2006-04-28 06:28:35 +02:00
|
|
|
}
|
2009-04-02 19:03:07 +02:00
|
|
|
|
2011-03-13 14:29:42 +01:00
|
|
|
if (nets[idx] == 0) {
|
|
|
|
|
errors += 1;
|
|
|
|
|
} else if (nets[idx]->data_type() == IVL_VT_REAL) {
|
2009-04-02 19:03:07 +02:00
|
|
|
cerr << parms_[idx]->get_fileline() << ": error: "
|
|
|
|
|
<< "concatenation operand can no be real: "
|
|
|
|
|
<< *parms_[idx] << endl;
|
2002-11-09 20:20:48 +01:00
|
|
|
errors += 1;
|
2009-04-02 19:03:07 +02:00
|
|
|
continue;
|
2011-03-13 14:29:42 +01:00
|
|
|
} else {
|
|
|
|
|
width += nets[idx]->vector_width();
|
|
|
|
|
}
|
2002-11-09 20:20:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (errors) {
|
2008-02-22 23:51:53 +01:00
|
|
|
des->errors += errors;
|
2002-11-09 20:20:48 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the temporary signal that connects to all the
|
|
|
|
|
operands, and connect it up. Scan the operands of the
|
2004-12-11 03:31:25 +01:00
|
|
|
concat operator from most significant to least significant,
|
|
|
|
|
which is the order they are given in the concat list. */
|
|
|
|
|
|
2025-10-21 07:45:05 +02:00
|
|
|
const netvector_t*tmp2_vec = new netvector_t(nets[0]->data_type(),width-1,0);
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::IMPLICIT, tmp2_vec);
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2006-04-30 07:17:48 +02:00
|
|
|
/* Assume that the data types of the nets are all the same, so
|
|
|
|
|
we can take the data type of any, the first will do. */
|
2007-02-05 02:42:31 +01:00
|
|
|
osig->local_flag(true);
|
|
|
|
|
osig->set_line(*this);
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2008-06-03 20:16:25 +02:00
|
|
|
if (bidirectional_flag) {
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": debug: Generating tran(VP) "
|
|
|
|
|
<< "to connect input l-value to subexpressions."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
2002-11-09 20:20:48 +01:00
|
|
|
|
2022-02-17 10:48:43 +01:00
|
|
|
for (unsigned idx = 0 ; idx < nets.size() ; idx += 1) {
|
2008-06-03 20:16:25 +02:00
|
|
|
unsigned wid = nets[idx]->vector_width();
|
|
|
|
|
unsigned off = width - wid;
|
|
|
|
|
NetTran*ps = new NetTran(scope, scope->local_symbol(),
|
|
|
|
|
osig->vector_width(), wid, off);
|
|
|
|
|
des->add_node(ps);
|
|
|
|
|
ps->set_line(*this);
|
2006-04-28 06:28:35 +02:00
|
|
|
|
2008-06-03 20:16:25 +02:00
|
|
|
connect(ps->pin(0), osig->pin(0));
|
|
|
|
|
connect(ps->pin(1), nets[idx]->pin(0));
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, wid <= width);
|
|
|
|
|
width -= wid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": debug: Generating part selects "
|
|
|
|
|
<< "to connect input l-value to subexpressions."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetPartSelect::dir_t part_dir = NetPartSelect::VP;
|
|
|
|
|
|
2022-02-17 10:48:43 +01:00
|
|
|
for (unsigned idx = 0 ; idx < nets.size() ; idx += 1) {
|
2008-06-03 20:16:25 +02:00
|
|
|
unsigned wid = nets[idx]->vector_width();
|
|
|
|
|
unsigned off = width - wid;
|
|
|
|
|
NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir);
|
|
|
|
|
des->add_node(ps);
|
|
|
|
|
ps->set_line(*this);
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2008-06-03 20:16:25 +02:00
|
|
|
connect(ps->pin(1), osig->pin(0));
|
|
|
|
|
connect(ps->pin(0), nets[idx]->pin(0));
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*this, wid <= width);
|
2008-06-03 20:16:25 +02:00
|
|
|
width -= wid;
|
|
|
|
|
}
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*this, width == 0);
|
2004-12-11 03:31:25 +01:00
|
|
|
}
|
|
|
|
|
|
2002-11-09 20:20:48 +01:00
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-01 08:47:38 +02:00
|
|
|
NetNet* PEConcat::elaborate_lnet(Design*des, NetScope*scope,
|
|
|
|
|
bool var_allowed_in_sv) const
|
2006-04-28 06:28:35 +02:00
|
|
|
{
|
2025-07-01 08:47:38 +02:00
|
|
|
return elaborate_lnet_common_(des, scope, false, var_allowed_in_sv);
|
2006-04-28 06:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-01 08:47:38 +02:00
|
|
|
NetNet* PEConcat::elaborate_bi_net(Design*des, NetScope*scope,
|
|
|
|
|
bool var_allowed_in_sv) const
|
2006-04-28 06:28:35 +02:00
|
|
|
{
|
2025-07-01 08:47:38 +02:00
|
|
|
return elaborate_lnet_common_(des, scope, true, var_allowed_in_sv);
|
2006-04-28 06:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
2020-12-01 07:44:54 +01:00
|
|
|
bool PEConcat::is_collapsible_net(Design*des, NetScope*scope,
|
|
|
|
|
NetNet::PortType port_type) const
|
2011-03-13 14:29:42 +01:00
|
|
|
{
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*this, scope);
|
2011-03-13 14:29:42 +01:00
|
|
|
|
|
|
|
|
// Repeat concatenations are not currently supported.
|
|
|
|
|
if (repeat_)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// Test the operands of the concatenation.
|
|
|
|
|
for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) {
|
|
|
|
|
|
|
|
|
|
// Empty expressions are not allowed in concatenations
|
|
|
|
|
if (parms_[idx] == 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-12-01 07:44:54 +01:00
|
|
|
if (!parms_[idx]->is_collapsible_net(des, scope, port_type))
|
2011-03-13 14:29:42 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/*
|
|
|
|
|
* This private method evaluates the part selects (if any) for the
|
|
|
|
|
* signal. The sig argument is the NetNet already located for the
|
|
|
|
|
* PEIdent name. The midx and lidx arguments are loaded with the
|
|
|
|
|
* results, which may be the whole vector, or a single bit, or
|
|
|
|
|
* anything in between. The values are in canonical indices.
|
|
|
|
|
*/
|
2025-10-23 18:58:49 +02:00
|
|
|
bool PEIdent::eval_part_select_(Design*des, NetScope*scope, const NetNet*sig,
|
2008-05-10 02:42:37 +02:00
|
|
|
long&midx, long&lidx) const
|
2005-01-09 21:16:00 +01:00
|
|
|
{
|
2012-02-11 02:17:59 +01:00
|
|
|
list<long> prefix_indices;
|
|
|
|
|
bool rc = calculate_packed_indices_(des, scope, sig, prefix_indices);
|
|
|
|
|
ivl_assert(*this, rc);
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
// Only treat as part/bit selects any index that is beyond the
|
|
|
|
|
// word selects for an array. This is not an array, then
|
|
|
|
|
// dimensions==0 and any index is treated as a select.
|
2012-05-26 00:58:29 +02:00
|
|
|
if (name_tail.index.size() <= sig->unpacked_dimensions()) {
|
2007-05-24 06:07:11 +02:00
|
|
|
midx = sig->vector_width()-1;
|
|
|
|
|
lidx = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
|
|
|
|
|
switch (index_tail.sel) {
|
2006-04-24 07:15:07 +02:00
|
|
|
default:
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
2007-05-24 06:07:11 +02:00
|
|
|
<< "Unexpected sel_ value = " << index_tail.sel << endl;
|
|
|
|
|
ivl_assert(*this, 0);
|
2006-04-24 07:15:07 +02:00
|
|
|
break;
|
2005-01-09 21:16:00 +01:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
case index_component_t::SEL_IDX_DO:
|
|
|
|
|
case index_component_t::SEL_IDX_UP: {
|
2011-03-27 12:08:33 +02:00
|
|
|
NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1, true);
|
2025-10-21 07:45:05 +02:00
|
|
|
const NetEConst*tmp = dynamic_cast<NetEConst*>(tmp_ex);
|
2007-11-07 20:26:56 +01:00
|
|
|
if (!tmp) {
|
2023-07-09 21:24:39 +02:00
|
|
|
cerr << get_fileline() << ": error: Indexed part select "
|
|
|
|
|
"base expression must be a constant integral value "
|
|
|
|
|
"in this context." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : This expression "
|
|
|
|
|
"violates that rule: " << *index_tail.msb << endl;
|
2007-11-07 20:26:56 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2005-01-09 21:16:00 +01:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
/* The width (a constant) is calculated here. */
|
|
|
|
|
unsigned long wid = 0;
|
|
|
|
|
bool flag = calculate_up_do_width_(des, scope, wid);
|
2009-08-29 01:50:59 +02:00
|
|
|
if (! flag) return false;
|
|
|
|
|
|
|
|
|
|
/* We have an undefined index and that is out of range. */
|
|
|
|
|
if (! tmp->value().is_defined()) {
|
2024-03-05 16:43:03 +01:00
|
|
|
delete tmp_ex;
|
2009-08-29 01:50:59 +02:00
|
|
|
if (warn_ob_select) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< sig->name();
|
2012-05-26 00:58:29 +02:00
|
|
|
if (sig->unpacked_dimensions() > 0) cerr << "[]";
|
2009-08-29 01:50:59 +02:00
|
|
|
cerr << "['bx";
|
|
|
|
|
if (index_tail.sel ==
|
|
|
|
|
index_component_t::SEL_IDX_UP) {
|
|
|
|
|
cerr << "+:";
|
|
|
|
|
} else {
|
|
|
|
|
cerr << "-:";
|
|
|
|
|
}
|
2013-05-21 22:44:31 +02:00
|
|
|
cerr << wid << "] is always outside the vector."
|
2009-08-29 01:50:59 +02:00
|
|
|
<< endl;
|
|
|
|
|
}
|
2007-07-03 05:53:02 +02:00
|
|
|
return false;
|
2009-08-29 01:50:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long midx_val = tmp->value().as_long();
|
2024-03-05 16:43:03 +01:00
|
|
|
|
|
|
|
|
// Check whether an unsigned base fits in a 32 bit int.
|
|
|
|
|
// This ensures correct results for the vlog95 target, and
|
|
|
|
|
// for the vvp target on LLP64 platforms (Microsoft Windows).
|
|
|
|
|
if (!tmp->has_sign() && (int32_t)midx_val < 0) {
|
|
|
|
|
// The base is wrapped around.
|
|
|
|
|
delete tmp_ex;
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
cerr << get_fileline() << ": warning: " << sig->name();
|
|
|
|
|
cerr << "[" << (unsigned long)midx_val
|
|
|
|
|
<< (index_tail.sel == index_component_t::SEL_IDX_UP ? "+:" : "-:")
|
|
|
|
|
<< wid << "] is always outside vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-29 01:50:59 +02:00
|
|
|
delete tmp_ex;
|
2024-03-05 16:43:03 +01:00
|
|
|
|
2021-04-20 22:29:00 +02:00
|
|
|
if (prefix_indices.size()+1 < sig->packed_dims().size()) {
|
|
|
|
|
// Here we are selecting one or more sub-arrays.
|
|
|
|
|
// Make this work by finding the indexed sub-arrays and
|
|
|
|
|
// creating a generated slice that spans the whole range.
|
|
|
|
|
long loff, moff;
|
|
|
|
|
unsigned long lwid, mwid;
|
2021-04-20 23:58:40 +02:00
|
|
|
bool mrc, lrc;
|
|
|
|
|
mrc = sig->sb_to_slice(prefix_indices, midx_val, moff, mwid);
|
2021-04-20 22:29:00 +02:00
|
|
|
if (index_tail.sel == index_component_t::SEL_IDX_UP)
|
|
|
|
|
lrc = sig->sb_to_slice(prefix_indices, midx_val+wid-1, loff, lwid);
|
|
|
|
|
else
|
|
|
|
|
lrc = sig->sb_to_slice(prefix_indices, midx_val-wid+1, loff, lwid);
|
2021-04-20 23:58:40 +02:00
|
|
|
if (!mrc || !lrc) {
|
|
|
|
|
cerr << get_fileline() << ": error: ";
|
|
|
|
|
cerr << "Part-select [" << midx_val;
|
|
|
|
|
if (index_tail.sel == index_component_t::SEL_IDX_UP) {
|
|
|
|
|
cerr << "+:";
|
|
|
|
|
} else {
|
|
|
|
|
cerr << "-:";
|
|
|
|
|
}
|
|
|
|
|
cerr << wid << "] exceeds the declared bounds for ";
|
|
|
|
|
cerr << sig->name();
|
|
|
|
|
if (sig->unpacked_dimensions() > 0) cerr << "[]";
|
|
|
|
|
cerr << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-04-20 22:29:00 +02:00
|
|
|
ivl_assert(*this, lwid == mwid);
|
|
|
|
|
|
|
|
|
|
if (moff > loff) {
|
|
|
|
|
lidx = loff;
|
|
|
|
|
midx = moff + mwid - 1;
|
2009-08-29 01:50:59 +02:00
|
|
|
} else {
|
2021-04-20 22:29:00 +02:00
|
|
|
lidx = moff;
|
|
|
|
|
midx = loff + lwid - 1;
|
2009-08-29 01:50:59 +02:00
|
|
|
}
|
2021-04-20 22:29:00 +02:00
|
|
|
} else {
|
|
|
|
|
midx = sig->sb_to_idx(prefix_indices, midx_val);
|
|
|
|
|
|
|
|
|
|
if (index_tail.sel == index_component_t::SEL_IDX_UP)
|
|
|
|
|
lidx = sig->sb_to_idx(prefix_indices, midx_val+wid-1);
|
|
|
|
|
else
|
|
|
|
|
lidx = sig->sb_to_idx(prefix_indices, midx_val-wid+1);
|
|
|
|
|
|
|
|
|
|
if (midx < lidx) {
|
|
|
|
|
long tmpx = midx;
|
|
|
|
|
midx = lidx;
|
|
|
|
|
lidx = tmpx;
|
2008-10-15 02:03:50 +02:00
|
|
|
}
|
2021-04-20 22:29:00 +02:00
|
|
|
|
|
|
|
|
/* Warn about an indexed part select that is out of range. */
|
|
|
|
|
if (warn_ob_select && (lidx < 0)) {
|
|
|
|
|
cerr << get_fileline() << ": warning: " << sig->name();
|
|
|
|
|
if (sig->unpacked_dimensions() > 0) cerr << "[]";
|
|
|
|
|
cerr << "[" << midx_val;
|
|
|
|
|
if (index_tail.sel == index_component_t::SEL_IDX_UP) {
|
|
|
|
|
cerr << "+:";
|
|
|
|
|
} else {
|
|
|
|
|
cerr << "-:";
|
|
|
|
|
}
|
|
|
|
|
cerr << wid << "] is selecting before vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
if (warn_ob_select && (midx >= (long)sig->vector_width())) {
|
|
|
|
|
cerr << get_fileline() << ": warning: " << sig->name();
|
|
|
|
|
if (sig->unpacked_dimensions() > 0) {
|
|
|
|
|
cerr << "[]";
|
|
|
|
|
}
|
|
|
|
|
cerr << "[" << midx_val;
|
|
|
|
|
if (index_tail.sel == index_component_t::SEL_IDX_UP) {
|
|
|
|
|
cerr << "+:";
|
|
|
|
|
} else {
|
|
|
|
|
cerr << "-:";
|
|
|
|
|
}
|
|
|
|
|
cerr << wid << "] is selecting after vector." << endl;
|
2008-10-15 02:03:50 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-20 22:29:00 +02:00
|
|
|
/* This is completely out side the signal so just skip it. */
|
|
|
|
|
if (lidx >= (long)sig->vector_width() || midx < 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2008-10-15 02:03:50 +02:00
|
|
|
}
|
|
|
|
|
|
2006-04-24 07:15:07 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
case index_component_t::SEL_PART: {
|
2006-04-24 07:15:07 +02:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
long msb, lsb;
|
2009-01-02 01:20:41 +01:00
|
|
|
bool part_defined_flag;
|
|
|
|
|
/* bool flag = */ calculate_parts_(des, scope, msb, lsb, part_defined_flag);
|
2013-05-21 22:44:31 +02:00
|
|
|
|
|
|
|
|
/* We have an undefined index and that is out of range. */
|
|
|
|
|
if (!part_defined_flag) {
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< sig->name();
|
|
|
|
|
if (sig->unpacked_dimensions() > 0) cerr << "[]";
|
|
|
|
|
cerr << "['bx] is always outside the vector."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-04-24 07:15:07 +02:00
|
|
|
|
2016-02-15 15:12:40 +01:00
|
|
|
if (prefix_indices.size()+1 < sig->packed_dims().size()) {
|
|
|
|
|
// Here we have a slice that doesn't have enough indices
|
|
|
|
|
// to get to a single slice. For example:
|
|
|
|
|
// wire [9:0][5:1] foo
|
|
|
|
|
// ... foo[4:3] ...
|
|
|
|
|
// Make this work by finding the indexed slices and
|
|
|
|
|
// creating a generated slice that spans the whole
|
|
|
|
|
// range.
|
|
|
|
|
unsigned long lwid, mwid;
|
2021-04-20 23:58:40 +02:00
|
|
|
bool lrc, mrc;
|
2023-12-28 07:15:39 +01:00
|
|
|
lrc = sig->sb_to_slice(prefix_indices, lsb, lidx, lwid);
|
|
|
|
|
mrc = sig->sb_to_slice(prefix_indices, msb, midx, mwid);
|
2021-04-20 23:58:40 +02:00
|
|
|
if (!mrc || !lrc) {
|
|
|
|
|
cerr << get_fileline() << ": error: ";
|
|
|
|
|
cerr << "Part-select [" << msb << ":" << lsb;
|
|
|
|
|
cerr << "] exceeds the declared bounds for ";
|
|
|
|
|
cerr << sig->name();
|
|
|
|
|
if (sig->unpacked_dimensions() > 0) cerr << "[]";
|
|
|
|
|
cerr << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2016-02-15 15:12:40 +01:00
|
|
|
ivl_assert(*this, lwid == mwid);
|
2023-12-28 07:15:39 +01:00
|
|
|
midx += mwid - 1;
|
2016-02-15 15:12:40 +01:00
|
|
|
} else {
|
2023-12-28 07:15:39 +01:00
|
|
|
lidx = sig->sb_to_idx(prefix_indices, lsb);
|
|
|
|
|
midx = sig->sb_to_idx(prefix_indices, msb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Detect reversed indices of a part select. */
|
|
|
|
|
if (lidx > midx) {
|
|
|
|
|
cerr << get_fileline() << ": error: Part select "
|
|
|
|
|
<< sig->name() << "[" << msb << ":"
|
|
|
|
|
<< lsb << "] indices reversed." << endl;
|
|
|
|
|
cerr << get_fileline() << ": : Did you mean "
|
|
|
|
|
<< sig->name() << "[" << lsb << ":"
|
|
|
|
|
<< msb << "]?" << endl;
|
|
|
|
|
des->errors += 1;
|
2016-02-15 15:12:40 +01:00
|
|
|
|
2023-12-28 07:15:39 +01:00
|
|
|
std::swap(lidx, midx);
|
2006-04-24 07:15:07 +02:00
|
|
|
}
|
2023-12-28 07:15:39 +01:00
|
|
|
|
|
|
|
|
/* Warn about a part select that is out of range. */
|
|
|
|
|
if (midx >= (long)sig->vector_width() || lidx < 0) {
|
|
|
|
|
cerr << get_fileline() << ": warning: Part select "
|
|
|
|
|
<< sig->name();
|
|
|
|
|
if (sig->unpacked_dimensions() > 0) {
|
|
|
|
|
cerr << "[]";
|
2016-02-15 15:12:40 +01:00
|
|
|
}
|
2023-12-28 07:15:39 +01:00
|
|
|
cerr << "[" << msb << ":" << lsb
|
|
|
|
|
<< "] is out of range." << endl;
|
|
|
|
|
}
|
2008-05-10 02:42:37 +02:00
|
|
|
|
2023-12-28 07:15:39 +01:00
|
|
|
/* This is completely out side the signal so just skip it. */
|
|
|
|
|
if (lidx >= (long)sig->vector_width() || midx < 0) {
|
|
|
|
|
return false;
|
2016-02-15 15:12:40 +01:00
|
|
|
}
|
2006-04-24 07:15:07 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
case index_component_t::SEL_BIT:
|
2012-05-26 00:58:29 +02:00
|
|
|
if (name_tail.index.size() > sig->unpacked_dimensions()) {
|
2012-04-30 20:48:33 +02:00
|
|
|
long msb;
|
|
|
|
|
bool bit_defined_flag;
|
|
|
|
|
/* bool flag = */ calculate_bits_(des, scope, msb, bit_defined_flag);
|
2013-05-21 22:44:31 +02:00
|
|
|
|
|
|
|
|
/* We have an undefined index and that is out of range. */
|
|
|
|
|
if (!bit_defined_flag) {
|
|
|
|
|
if (warn_ob_select) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< sig->name();
|
|
|
|
|
if (sig->unpacked_dimensions() > 0) cerr << "[]";
|
|
|
|
|
cerr << "['bx] is always outside the vector."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 04:08:22 +02:00
|
|
|
|
|
|
|
|
if (prefix_indices.size()+2 <= sig->packed_dims().size()) {
|
|
|
|
|
long tmp_loff;
|
|
|
|
|
unsigned long tmp_lwid;
|
|
|
|
|
bool rcl = sig->sb_to_slice(prefix_indices, msb,
|
|
|
|
|
tmp_loff, tmp_lwid);
|
2022-12-30 19:36:38 +01:00
|
|
|
if(rcl) {
|
|
|
|
|
midx = tmp_loff + tmp_lwid - 1;
|
|
|
|
|
lidx = tmp_loff;
|
|
|
|
|
} else {
|
|
|
|
|
cerr << get_fileline() << ": error: Index " << sig->name()
|
|
|
|
|
<< "[" << msb << "] is out of range."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
midx = 0;
|
|
|
|
|
lidx = 0;
|
|
|
|
|
}
|
2012-04-23 04:08:22 +02:00
|
|
|
} else {
|
|
|
|
|
midx = sig->sb_to_idx(prefix_indices, msb);
|
|
|
|
|
if (midx >= (long)sig->vector_width()) {
|
|
|
|
|
cerr << get_fileline() << ": error: Index " << sig->name()
|
|
|
|
|
<< "[" << msb << "] is out of range."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
midx = 0;
|
|
|
|
|
}
|
|
|
|
|
lidx = midx;
|
2006-04-24 07:15:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: "
|
2007-05-24 06:07:11 +02:00
|
|
|
<< "Bit select " << path_ << endl;
|
|
|
|
|
ivl_assert(*this, 0);
|
2006-04-24 07:15:07 +02:00
|
|
|
midx = sig->vector_width() - 1;
|
|
|
|
|
lidx = 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2005-01-09 21:16:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-21 01:13:08 +01:00
|
|
|
/*
|
2005-08-06 19:58:16 +02:00
|
|
|
* This is the common code for l-value nets and bi-directional
|
|
|
|
|
* nets. There is very little that is different between the two cases,
|
|
|
|
|
* so most of the work for both is done here.
|
1999-11-21 01:13:08 +01:00
|
|
|
*/
|
2005-08-06 19:58:16 +02:00
|
|
|
NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
2025-07-01 08:47:38 +02:00
|
|
|
bool bidirectional_flag,
|
|
|
|
|
bool var_allowed_in_sv) const
|
1999-11-21 01:13:08 +01:00
|
|
|
{
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*this, scope);
|
2003-09-19 05:50:12 +02:00
|
|
|
|
2024-02-18 10:36:23 +01:00
|
|
|
symbol_search_results sr;
|
2024-02-19 11:02:40 +01:00
|
|
|
symbol_search(this, des, scope, path_.name, lexical_pos_, &sr);
|
2003-09-19 05:50:12 +02:00
|
|
|
|
2024-02-18 10:36:23 +01:00
|
|
|
if (sr.eve != 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: named events (" << path_
|
2003-09-19 05:50:12 +02:00
|
|
|
<< ") cannot be l-values in continuous "
|
|
|
|
|
<< "assignments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-18 10:36:23 +01:00
|
|
|
NetNet*sig = sr.net;
|
|
|
|
|
pform_name_t base_path = sr.path_head;
|
|
|
|
|
pform_name_t member_path = sr.path_tail;
|
2012-01-01 02:27:30 +01:00
|
|
|
|
1999-11-21 01:13:08 +01:00
|
|
|
if (sig == 0) {
|
2008-03-19 04:50:40 +01:00
|
|
|
cerr << get_fileline() << ": error: Net " << path_
|
|
|
|
|
<< " is not defined in this context." << endl;
|
2025-07-21 23:46:01 +02:00
|
|
|
if (sr.is_found()) {
|
|
|
|
|
cerr << sr.scope->get_fileline() << ": : Found a "
|
|
|
|
|
<< sr.result_type() << " with this name here." << endl;
|
|
|
|
|
}
|
2024-02-25 17:12:31 +01:00
|
|
|
if (sr.decl_after_use) {
|
|
|
|
|
cerr << sr.decl_after_use->get_fileline() << ": : "
|
|
|
|
|
"A symbol with that name was declared here. "
|
|
|
|
|
"Check for declaration after use." << endl;
|
|
|
|
|
}
|
2008-03-19 04:50:40 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
1999-11-21 01:13:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": " << __func__ << ": "
|
|
|
|
|
<< "Found l-value path_=" << path_
|
|
|
|
|
<< " as sig=" << sig->name()
|
|
|
|
|
<< " base_path=" << base_path
|
|
|
|
|
<< " member_path=" << member_path
|
|
|
|
|
<< " unpacked_dimensions()=" << sig->unpacked_dimensions()
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-03 21:24:23 +01:00
|
|
|
if (sig->get_const()) {
|
|
|
|
|
cerr << get_fileline() << ": error: Continuous assignment to const"
|
|
|
|
|
<< " signal `" << sig->name() << "` is not allowed." << endl;
|
|
|
|
|
des->errors++;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
// If this is SystemVerilog and the variable is not yet
|
|
|
|
|
// assigned by anything, then convert it to an unresolved
|
|
|
|
|
// wire.
|
2025-07-01 08:47:38 +02:00
|
|
|
if (gn_var_can_be_uwire() && var_allowed_in_sv
|
2010-10-08 05:13:11 +02:00
|
|
|
&& (sig->type() == NetNet::REG)
|
2012-04-16 03:04:09 +02:00
|
|
|
&& (sig->peek_lref() == 0) ) {
|
2010-10-08 05:13:11 +02:00
|
|
|
sig->type(NetNet::UNRESOLVED_WIRE);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
// Don't allow registers as assign l-values.
|
1999-11-21 01:13:08 +01:00
|
|
|
if (sig->type() == NetNet::REG) {
|
2025-07-17 07:37:04 +02:00
|
|
|
cerr << get_fileline() << ": error: Variable '" << sig->name()
|
|
|
|
|
<< "' cannot be driven by a ";
|
|
|
|
|
if (var_allowed_in_sv) cerr << "continuous assignment/module";
|
|
|
|
|
else cerr << "primitive";
|
|
|
|
|
if (gn_var_can_be_uwire()) {
|
|
|
|
|
cerr << " or continuous assignment with non-default strength." << endl;
|
|
|
|
|
} else {
|
|
|
|
|
cerr << "." << endl;
|
|
|
|
|
if (var_allowed_in_sv) {
|
|
|
|
|
cerr << get_fileline() << ": : "
|
|
|
|
|
<< "This is allowed when SystemVerilog is enabled."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
2025-07-01 08:47:38 +02:00
|
|
|
}
|
2003-08-05 05:01:58 +02:00
|
|
|
des->errors += 1;
|
2025-07-01 08:47:38 +02:00
|
|
|
return nullptr;
|
1999-11-21 01:13:08 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
// Some parts below need the tail component. This is a convenient
|
|
|
|
|
// reference to it.
|
|
|
|
|
const name_component_t&path_tail = path_.back();
|
|
|
|
|
|
|
|
|
|
// Default part select is the entire word.
|
2007-01-16 06:44:14 +01:00
|
|
|
unsigned midx = sig->vector_width()-1, lidx = 0;
|
2020-11-30 03:18:55 +01:00
|
|
|
// The default word select is the first.
|
2009-07-04 00:37:44 +02:00
|
|
|
long widx = 0;
|
2020-11-30 03:18:55 +01:00
|
|
|
// Set this to true if we calculate the word index. This is
|
|
|
|
|
// used to distinguish between unpacked array assignment and
|
|
|
|
|
// array word assignment.
|
2014-03-17 01:08:38 +01:00
|
|
|
bool widx_flag = false;
|
2007-01-16 06:44:14 +01:00
|
|
|
|
2012-05-26 00:58:29 +02:00
|
|
|
list<long> unpacked_indices_const;
|
2007-05-24 06:07:11 +02:00
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
// Detect the net is a structure and there was a method path
|
|
|
|
|
// detected. We have already broken the path_ into the path to
|
|
|
|
|
// the net, and the path of member names. For example, if the
|
|
|
|
|
// path_ is a.b.x.y, we have determined that a.b is a reference
|
|
|
|
|
// to the net, and that x.y are the member_path. So in this case
|
|
|
|
|
// we handle the member_path.
|
2012-09-30 00:13:45 +02:00
|
|
|
const netstruct_t*struct_type = 0;
|
2020-11-30 03:18:55 +01:00
|
|
|
if ((struct_type = sig->struct_type()) && !member_path.empty()) {
|
2012-01-01 02:27:30 +01:00
|
|
|
|
2012-12-18 19:43:07 +01:00
|
|
|
if (debug_elaborate) {
|
2020-11-30 03:18:55 +01:00
|
|
|
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
|
|
|
|
<< "Signal " << sig->name() << " is a structure, "
|
|
|
|
|
<< "try to match member path " << member_path << endl;
|
2012-12-18 19:43:07 +01:00
|
|
|
}
|
|
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
unsigned long member_off = 0;
|
|
|
|
|
unsigned long member_width = sig->vector_width();
|
|
|
|
|
|
|
|
|
|
// Might be an array of structs, like a.b[N].x.y. (A packed
|
|
|
|
|
// array.) Handle that here by taking a part select that
|
|
|
|
|
// reflects the array index.
|
2012-09-04 00:56:40 +02:00
|
|
|
if (sig->packed_dimensions() > 1) {
|
2020-11-30 03:18:55 +01:00
|
|
|
list<index_component_t>tmp_index = base_path.back().index;
|
2012-09-04 00:56:40 +02:00
|
|
|
index_component_t member_select;
|
|
|
|
|
member_select.sel = index_component_t::SEL_BIT;
|
|
|
|
|
member_select.msb = new PENumber(new verinum(member_off));
|
|
|
|
|
tmp_index.push_back(member_select);
|
2020-11-30 03:18:55 +01:00
|
|
|
NetExpr*packed_base = collapse_array_indices(des, scope, sig, tmp_index);
|
2012-09-04 00:56:40 +02:00
|
|
|
if (debug_elaborate) {
|
2020-11-30 03:18:55 +01:00
|
|
|
cerr << get_fileline() << ": " << __func__ << ": "
|
2014-03-29 19:23:16 +01:00
|
|
|
<< "packed_base=" << *packed_base
|
2020-11-30 03:18:55 +01:00
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long tmp;
|
|
|
|
|
if (packed_base && eval_as_long(tmp, packed_base)) {
|
|
|
|
|
member_off = tmp;
|
|
|
|
|
member_width = struct_type->packed_width();
|
|
|
|
|
delete packed_base;
|
|
|
|
|
packed_base = 0;
|
2012-09-04 00:56:40 +02:00
|
|
|
}
|
|
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
// Only support constant dimensions here.
|
|
|
|
|
ivl_assert(*this, packed_base == 0);
|
2012-09-04 00:56:40 +02:00
|
|
|
}
|
|
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
// Now run through the member names, possibly nested, to take
|
|
|
|
|
// further part selects reflected by the member name. So for
|
|
|
|
|
// example, (.x.y) member x has an offset and width within the
|
|
|
|
|
// containing vector, and member y an offset and width within
|
|
|
|
|
// that.
|
|
|
|
|
pform_name_t use_path = member_path;
|
2022-12-30 20:12:29 +01:00
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
while (! use_path.empty()) {
|
|
|
|
|
const name_component_t member_comp = use_path.front();
|
|
|
|
|
const perm_string&member_name = member_comp.name;
|
|
|
|
|
|
|
|
|
|
unsigned long tmp_off;
|
2022-12-30 20:12:29 +01:00
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
const struct netstruct_t::member_t*member = struct_type->packed_member(member_name, tmp_off);
|
2022-12-30 20:12:29 +01:00
|
|
|
|
|
|
|
|
if(!member) {
|
|
|
|
|
cerr << get_fileline() << ": error: missing element " << path() << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
member_off += tmp_off;
|
|
|
|
|
member_width = member->net_type->packed_width();
|
|
|
|
|
|
|
|
|
|
if (const netstruct_t*tmp_struct = dynamic_cast<const netstruct_t*> (member->net_type)) {
|
|
|
|
|
struct_type = tmp_struct;
|
|
|
|
|
} else {
|
|
|
|
|
struct_type = 0;
|
|
|
|
|
}
|
2012-09-04 00:56:40 +02:00
|
|
|
|
2020-11-30 03:18:55 +01:00
|
|
|
use_path.pop_front();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Look for part selects on the final member. For example if
|
|
|
|
|
// the path is a.b.x.y[3:0], the end of the member_path will
|
|
|
|
|
// have an index that needs to be handled.
|
|
|
|
|
// For now, assume there is unly a single part/bit select, and
|
|
|
|
|
// assume it's constant.
|
|
|
|
|
if (member_path.back().index.size() > 0) {
|
|
|
|
|
list<index_component_t>tmp_index = member_path.back().index;
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": " << __func__ << ": "
|
|
|
|
|
<< "Process trailing bit/part select. "
|
|
|
|
|
<< "index.size()=" << tmp_index.size()
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
ivl_assert(*this, tmp_index.size() == 1);
|
|
|
|
|
const index_component_t&tail_sel = tmp_index.back();
|
|
|
|
|
ivl_assert(*this, tail_sel.sel == index_component_t::SEL_PART || tail_sel.sel == index_component_t::SEL_BIT);
|
2012-12-18 19:43:07 +01:00
|
|
|
long tmp_off;
|
|
|
|
|
unsigned long tmp_wid;
|
|
|
|
|
bool rc = calculate_part(this, des, scope, tail_sel, tmp_off, tmp_wid);
|
|
|
|
|
ivl_assert(*this, rc);
|
2020-11-30 03:18:55 +01:00
|
|
|
member_off += tmp_off;
|
|
|
|
|
member_width = tmp_wid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": " << __func__ << ": "
|
|
|
|
|
<< "Final, calculated member " << member_path
|
|
|
|
|
<< " offset=" << member_off
|
|
|
|
|
<< " width=" << member_width
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rewrite a member select of a packed structure as a
|
|
|
|
|
// part select of the base variable.
|
|
|
|
|
lidx = member_off;
|
|
|
|
|
midx = lidx + member_width - 1;
|
|
|
|
|
|
|
|
|
|
// Elaborate an expression from the packed indices and
|
|
|
|
|
// the member offset (into the structure) to get a
|
|
|
|
|
// canonical expression into the packed signal vector.
|
|
|
|
|
if (sig->packed_dimensions() > 1) {
|
|
|
|
|
list<index_component_t>tmp_index = base_path.back().index;
|
|
|
|
|
index_component_t member_select;
|
|
|
|
|
member_select.sel = index_component_t::SEL_BIT;
|
|
|
|
|
member_select.msb = new PENumber(new verinum(member_off));
|
|
|
|
|
tmp_index.push_back(member_select);
|
2025-10-21 07:45:05 +02:00
|
|
|
const NetExpr*packed_base = collapse_array_indices(des, scope, sig, tmp_index);
|
2020-11-30 03:18:55 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
2012-12-18 19:43:07 +01:00
|
|
|
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
2020-11-30 03:18:55 +01:00
|
|
|
<< "packed_base=" << *packed_base
|
|
|
|
|
<< ", member_off=" << member_off << endl;
|
|
|
|
|
}
|
2012-12-18 19:43:07 +01:00
|
|
|
}
|
|
|
|
|
|
2014-06-30 05:38:23 +02:00
|
|
|
} else if (gn_system_verilog() && sig->unpacked_dimensions() > 0 && path_tail.index.empty()) {
|
2014-03-17 01:08:38 +01:00
|
|
|
|
|
|
|
|
// In this case, we are doing a continuous assignment to
|
|
|
|
|
// an unpacked array. The NetNet representation is a
|
|
|
|
|
// NetNet with a pin for each array element, so there is
|
|
|
|
|
// nothing more needed here.
|
|
|
|
|
//
|
|
|
|
|
// This can come up from code like this:
|
|
|
|
|
// logic [...] data [0:3];
|
|
|
|
|
// assign data = ...;
|
|
|
|
|
// In this case, "sig" is "data", and sig->pin_count()
|
|
|
|
|
// is 4 to account for the unpacked size.
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
|
|
|
|
<< "Net assign to unpacked array \"" << sig->name()
|
|
|
|
|
<< "\" with " << sig->pin_count() << " elements." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-26 00:58:29 +02:00
|
|
|
} else if (sig->unpacked_dimensions() > 0) {
|
2007-05-24 06:07:11 +02:00
|
|
|
|
2012-05-26 00:58:29 +02:00
|
|
|
// Make sure there are enough indices to address an array element.
|
2012-09-04 00:56:40 +02:00
|
|
|
if (path_tail.index.size() < sig->unpacked_dimensions()) {
|
2012-05-26 00:58:29 +02:00
|
|
|
cerr << get_fileline() << ": error: Array " << path()
|
|
|
|
|
<< " needs " << sig->unpacked_dimensions() << " indices,"
|
2014-03-17 01:08:38 +01:00
|
|
|
<< " but got only " << path_tail.index.size() << ". (net)" << endl;
|
2025-07-01 08:47:38 +02:00
|
|
|
cerr << get_fileline() << ": : Assignment to a whole array requires SystemVerilog."
|
|
|
|
|
<< endl;
|
2007-11-06 22:01:11 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2007-05-24 06:07:11 +02:00
|
|
|
|
2012-05-26 00:58:29 +02:00
|
|
|
// Evaluate all the index expressions into an
|
|
|
|
|
// "unpacked_indices" array.
|
|
|
|
|
list<NetExpr*>unpacked_indices;
|
2013-05-18 20:21:37 +02:00
|
|
|
indices_flags flags;
|
|
|
|
|
indices_to_expressions(des, scope, this,
|
|
|
|
|
path_tail.index, sig->unpacked_dimensions(),
|
2015-07-31 06:00:59 +02:00
|
|
|
true,
|
2013-05-18 20:21:37 +02:00
|
|
|
flags,
|
|
|
|
|
unpacked_indices,
|
|
|
|
|
unpacked_indices_const);
|
|
|
|
|
|
|
|
|
|
if (flags.invalid) {
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
} else if (flags.variable) {
|
|
|
|
|
cerr << get_fileline() << ": error: array '" << sig->name()
|
|
|
|
|
<< "' index must be a constant in this context." << endl;
|
2007-11-07 03:15:08 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
2007-01-16 06:44:14 +01:00
|
|
|
|
2013-05-18 20:21:37 +02:00
|
|
|
} else if (flags.undefined) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< "ignoring undefined l-value array access "
|
|
|
|
|
<< sig->name() << as_indices(unpacked_indices)
|
|
|
|
|
<< "." << endl;
|
2009-08-06 20:59:34 +02:00
|
|
|
widx = -1;
|
2014-03-17 01:08:38 +01:00
|
|
|
widx_flag = true;
|
2012-05-26 00:58:29 +02:00
|
|
|
|
|
|
|
|
} else {
|
2013-05-18 20:21:37 +02:00
|
|
|
NetExpr*canon_index = 0;
|
|
|
|
|
ivl_assert(*this, unpacked_indices_const.size() == sig->unpacked_dimensions());
|
|
|
|
|
canon_index = normalize_variable_unpacked(sig, unpacked_indices_const);
|
|
|
|
|
|
|
|
|
|
if (canon_index == 0) {
|
|
|
|
|
cerr << get_fileline() << ": warning: "
|
|
|
|
|
<< "ignoring out of bounds l-value array access "
|
|
|
|
|
<< sig->name() << as_indices(unpacked_indices_const)
|
|
|
|
|
<< "." << endl;
|
|
|
|
|
widx = -1;
|
2014-03-17 01:08:38 +01:00
|
|
|
widx_flag = true;
|
2012-05-26 00:58:29 +02:00
|
|
|
|
2013-05-18 20:21:37 +02:00
|
|
|
} else {
|
2025-10-21 07:45:05 +02:00
|
|
|
const NetEConst*canon_const = dynamic_cast<NetEConst*>(canon_index);
|
2013-05-18 20:21:37 +02:00
|
|
|
ivl_assert(*this, canon_const);
|
|
|
|
|
|
|
|
|
|
widx = canon_const->value().as_long();
|
2014-03-17 01:08:38 +01:00
|
|
|
widx_flag = true;
|
2013-05-18 20:21:37 +02:00
|
|
|
delete canon_index;
|
|
|
|
|
}
|
2012-05-26 00:58:29 +02:00
|
|
|
}
|
2007-01-16 06:44:14 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: Use [" << widx << "]"
|
2007-01-16 06:44:14 +01:00
|
|
|
<< " to index l-value array." << endl;
|
|
|
|
|
|
2007-11-07 03:15:08 +01:00
|
|
|
/* The array has a part/bit select at the end. */
|
2012-09-04 00:56:40 +02:00
|
|
|
if (path_tail.index.size() > sig->unpacked_dimensions()) {
|
2009-04-15 01:08:27 +02:00
|
|
|
if (sig->get_scalar()) {
|
2010-10-02 20:02:27 +02:00
|
|
|
cerr << get_fileline() << ": error: "
|
2009-04-15 01:08:27 +02:00
|
|
|
<< "can not select part of ";
|
|
|
|
|
if (sig->data_type() == IVL_VT_REAL) cerr << "real";
|
|
|
|
|
else cerr << "scalar";
|
|
|
|
|
cerr << " array word: " << sig->name()
|
2012-05-26 00:58:29 +02:00
|
|
|
<< as_indices(unpacked_indices_const) << endl;
|
2009-04-02 03:31:29 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-10 02:42:37 +02:00
|
|
|
long midx_tmp, lidx_tmp;
|
|
|
|
|
if (! eval_part_select_(des, scope, sig, midx_tmp, lidx_tmp))
|
2007-11-07 03:15:08 +01:00
|
|
|
return 0;
|
2008-10-15 02:03:50 +02:00
|
|
|
|
|
|
|
|
if (lidx_tmp < 0) {
|
2010-10-02 20:02:27 +02:00
|
|
|
cerr << get_fileline() << ": sorry: part selects "
|
2008-10-15 02:03:50 +02:00
|
|
|
"straddling the start of signal (" << path_
|
|
|
|
|
<< ") are not currently supported." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-05-10 02:42:37 +02:00
|
|
|
midx = midx_tmp;
|
|
|
|
|
lidx = lidx_tmp;
|
2007-11-07 03:15:08 +01:00
|
|
|
}
|
2012-05-26 00:58:29 +02:00
|
|
|
|
2012-09-04 00:56:40 +02:00
|
|
|
} else if (!path_tail.index.empty()) {
|
2014-04-06 05:57:22 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PEIdent::elaborate_lnet_common_: "
|
|
|
|
|
<< "path_tail.index.size()=" << path_tail.index.size()
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There are index expressions on the name, so this is a
|
|
|
|
|
// bit/slice select of the name. Calculate a canonical
|
|
|
|
|
// part select.
|
|
|
|
|
|
2009-04-15 01:08:27 +02:00
|
|
|
if (sig->get_scalar()) {
|
2010-10-02 20:02:27 +02:00
|
|
|
cerr << get_fileline() << ": error: "
|
2009-04-15 01:08:27 +02:00
|
|
|
<< "can not select part of ";
|
|
|
|
|
if (sig->data_type() == IVL_VT_REAL) cerr << "real: ";
|
|
|
|
|
else cerr << "scalar: ";
|
|
|
|
|
cerr << sig->name() << endl;
|
2009-04-02 03:31:29 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-10 02:42:37 +02:00
|
|
|
long midx_tmp, lidx_tmp;
|
|
|
|
|
if (! eval_part_select_(des, scope, sig, midx_tmp, lidx_tmp))
|
2007-01-16 06:44:14 +01:00
|
|
|
return 0;
|
2008-05-10 02:42:37 +02:00
|
|
|
|
2008-10-15 02:03:50 +02:00
|
|
|
if (lidx_tmp < 0) {
|
2010-10-02 20:02:27 +02:00
|
|
|
cerr << get_fileline() << ": sorry: part selects "
|
2008-10-15 02:03:50 +02:00
|
|
|
"straddling the start of signal (" << path_
|
|
|
|
|
<< ") are not currently supported." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-05-10 02:42:37 +02:00
|
|
|
midx = midx_tmp;
|
|
|
|
|
lidx = lidx_tmp;
|
2007-01-16 06:44:14 +01:00
|
|
|
}
|
2000-12-01 03:55:37 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
unsigned subnet_wid = midx-lidx+1;
|
|
|
|
|
|
2012-01-28 19:20:09 +01:00
|
|
|
/* Check if the l-value bits are double-driven. */
|
2024-02-03 14:50:06 +01:00
|
|
|
|
|
|
|
|
if (sig->type() == NetNet::UNRESOLVED_WIRE) {
|
|
|
|
|
ivl_assert(*this, widx_flag || (widx == 0));
|
|
|
|
|
long wcount = widx_flag ? 1 : sig->pin_count();
|
|
|
|
|
for (long idx = 0; idx < wcount; idx += 1) {
|
|
|
|
|
if (sig->test_and_set_part_driver(midx, lidx, widx + idx)) {
|
2024-02-03 15:06:40 +01:00
|
|
|
cerr << get_fileline() << ": error: ";
|
|
|
|
|
if (sig->coerced_to_uwire())
|
|
|
|
|
cerr << "Variable '";
|
|
|
|
|
else
|
|
|
|
|
cerr << "Unresolved wire '";
|
|
|
|
|
cerr << sig->name() << "' cannot have multiple drivers." << endl;
|
2024-02-03 14:50:06 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": : Overlap in "
|
|
|
|
|
<< "[" << midx << ":" << lidx << "] (canonical)"
|
|
|
|
|
<< ", widx=" << (widx_flag? widx : 0)
|
|
|
|
|
<< ", vector width=" << sig->vector_width()
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-04-06 05:57:22 +02:00
|
|
|
}
|
2012-01-28 19:20:09 +01:00
|
|
|
}
|
|
|
|
|
|
2014-03-17 01:08:38 +01:00
|
|
|
if (sig->pin_count() > 1 && widx_flag) {
|
2013-05-18 20:21:37 +02:00
|
|
|
if (widx < 0 || widx >= (long) sig->pin_count())
|
2009-07-04 00:37:44 +02:00
|
|
|
return 0;
|
2007-01-16 06:44:14 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2023-02-05 06:15:53 +01:00
|
|
|
sig->type(), sig->net_type());
|
2007-01-16 06:44:14 +01:00
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
connect(sig->pin(widx), tmp->pin(0));
|
|
|
|
|
sig = tmp;
|
2014-03-17 01:08:38 +01:00
|
|
|
|
|
|
|
|
} else if (sig->pin_count() > 1) {
|
|
|
|
|
|
|
|
|
|
// If this turns out to be an l-value unpacked array,
|
|
|
|
|
// then let the caller handle it. It will probably be
|
|
|
|
|
// converted into an array of assignments.
|
|
|
|
|
return sig;
|
2007-01-16 06:44:14 +01:00
|
|
|
}
|
|
|
|
|
|
2011-03-11 20:27:54 +01:00
|
|
|
/* If the desired l-value vector is narrower than the
|
2005-01-09 21:16:00 +01:00
|
|
|
signal itself, then use a NetPartSelect node to
|
|
|
|
|
arrange for connection to the desired bits. All this
|
2011-03-11 20:27:54 +01:00
|
|
|
can be skipped if the desired width matches the
|
2005-01-09 21:16:00 +01:00
|
|
|
original vector. */
|
|
|
|
|
|
|
|
|
|
if (subnet_wid != sig->vector_width()) {
|
2005-08-06 19:58:16 +02:00
|
|
|
/* If we are processing a tran or inout, then the
|
|
|
|
|
partselect is bi-directional. Otherwise, it is a
|
|
|
|
|
Part-to-Vector select. */
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
if (debug_elaborate)
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": debug: "
|
2005-01-09 21:16:00 +01:00
|
|
|
<< "Elaborate lnet part select "
|
|
|
|
|
<< sig->name()
|
|
|
|
|
<< "[base=" << lidx
|
|
|
|
|
<< " wid=" << subnet_wid <<"]"
|
|
|
|
|
<< endl;
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2025-10-21 07:45:05 +02:00
|
|
|
const netvector_t*tmp2_vec = new netvector_t(sig->data_type(),
|
|
|
|
|
subnet_wid-1,0);
|
2005-01-09 21:16:00 +01:00
|
|
|
NetNet*subsig = new NetNet(sig->scope(),
|
|
|
|
|
sig->scope()->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, tmp2_vec);
|
2007-02-05 02:42:31 +01:00
|
|
|
subsig->local_flag(true);
|
|
|
|
|
subsig->set_line(*this);
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2008-06-03 20:16:25 +02:00
|
|
|
if (bidirectional_flag) {
|
|
|
|
|
// Make a tran(VP)
|
|
|
|
|
NetTran*sub = new NetTran(scope, scope->local_symbol(),
|
|
|
|
|
sig->vector_width(),
|
|
|
|
|
subnet_wid, lidx);
|
|
|
|
|
sub->set_line(*this);
|
|
|
|
|
des->add_node(sub);
|
|
|
|
|
connect(sub->pin(0), sig->pin(0));
|
|
|
|
|
connect(sub->pin(1), subsig->pin(0));
|
2002-06-19 06:20:03 +02:00
|
|
|
|
2008-06-03 20:16:25 +02:00
|
|
|
} else {
|
|
|
|
|
NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid,
|
|
|
|
|
NetPartSelect::PV);
|
|
|
|
|
des->add_node(sub);
|
|
|
|
|
sub->set_line(*this);
|
|
|
|
|
connect(sub->pin(0), subsig->pin(0));
|
2011-04-04 02:21:43 +02:00
|
|
|
collapse_partselect_pv_to_concat(des, sig);
|
2008-06-03 20:16:25 +02:00
|
|
|
}
|
2005-01-09 21:16:00 +01:00
|
|
|
sig = subsig;
|
1999-11-21 01:13:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
/*
|
|
|
|
|
* Identifiers in continuous assignment l-values are limited to wires
|
|
|
|
|
* and that ilk. Detect registers and memories here and report errors.
|
|
|
|
|
*/
|
2025-07-01 08:47:38 +02:00
|
|
|
NetNet* PEIdent::elaborate_lnet(Design*des, NetScope*scope,
|
|
|
|
|
bool var_allowed_in_sv) const
|
2005-08-06 19:58:16 +02:00
|
|
|
{
|
2025-07-01 08:47:38 +02:00
|
|
|
return elaborate_lnet_common_(des, scope, false, var_allowed_in_sv);
|
2005-08-06 19:58:16 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-01 08:47:38 +02:00
|
|
|
NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope,
|
|
|
|
|
bool var_allowed_in_sv) const
|
2005-08-06 19:58:16 +02:00
|
|
|
{
|
2025-07-01 08:47:38 +02:00
|
|
|
return elaborate_lnet_common_(des, scope, true, var_allowed_in_sv);
|
2005-08-06 19:58:16 +02:00
|
|
|
}
|
|
|
|
|
|
2000-05-16 06:05:15 +02:00
|
|
|
/*
|
|
|
|
|
* This method is used to elaborate identifiers that are ports to a
|
|
|
|
|
* scope. The scope is presumed to be that of the module that has the
|
2005-01-09 21:16:00 +01:00
|
|
|
* port. This elaboration is done inside the module, and is only done
|
|
|
|
|
* to PEIdent objects. This method is used by elaboration of a module
|
|
|
|
|
* instantiation (PGModule::elaborate_mod_) to get NetNet objects for
|
|
|
|
|
* the port.
|
2000-05-16 06:05:15 +02:00
|
|
|
*/
|
2012-06-04 21:43:33 +02:00
|
|
|
NetNet* PEIdent::elaborate_subport(Design*des, NetScope*scope) const
|
2000-05-16 06:05:15 +02:00
|
|
|
{
|
2012-05-14 02:48:47 +02:00
|
|
|
ivl_assert(*this, scope->type() == NetScope::MODULE);
|
2022-09-25 10:39:41 +02:00
|
|
|
NetNet*sig = des->find_signal(scope, path_.name);
|
2000-05-16 06:05:15 +02:00
|
|
|
if (sig == 0) {
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: no wire/reg " << path_
|
2007-06-02 05:42:12 +02:00
|
|
|
<< " in module " << scope_path(scope) << "." << endl;
|
2000-05-16 06:05:15 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/* Check the port_type of the signal to make sure it is really
|
|
|
|
|
a port, and its direction is resolved. */
|
2000-08-18 06:38:57 +02:00
|
|
|
switch (sig->port_type()) {
|
|
|
|
|
case NetNet::PINPUT:
|
|
|
|
|
case NetNet::POUTPUT:
|
|
|
|
|
case NetNet::PINOUT:
|
2012-02-25 19:19:48 +01:00
|
|
|
case NetNet::PREF:
|
2000-08-18 06:38:57 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* If the name matches, but the signal is not a port,
|
|
|
|
|
then the user declared the object but there is no
|
|
|
|
|
matching input/output/inout declaration. */
|
|
|
|
|
|
|
|
|
|
case NetNet::NOT_A_PORT:
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": error: signal " << path_ << " in"
|
2007-06-02 05:42:12 +02:00
|
|
|
<< " module " << scope_path(scope) << " is not a port." << endl;
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": : Are you missing an input/"
|
2000-08-18 06:38:57 +02:00
|
|
|
<< "output/inout declaration?" << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* This should not happen. A PWire can only become
|
2008-01-15 19:54:04 +01:00
|
|
|
PIMPLICIT if this is a UDP reg port, and the make_udp
|
2000-08-18 06:38:57 +02:00
|
|
|
function should turn it into an output.... I think. */
|
|
|
|
|
|
|
|
|
|
case NetNet::PIMPLICIT:
|
2007-12-20 18:31:01 +01:00
|
|
|
cerr << get_fileline() << ": internal error: signal " << path_
|
2007-06-02 05:42:12 +02:00
|
|
|
<< " in module " << scope_path(scope) << " is left as "
|
2000-08-18 06:38:57 +02:00
|
|
|
<< "port type PIMPLICIT." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-10 02:42:37 +02:00
|
|
|
long midx;
|
|
|
|
|
long lidx;
|
2000-05-16 06:05:15 +02:00
|
|
|
|
2014-03-17 01:08:38 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PEIdent::elaborate_subport: "
|
|
|
|
|
<< "path_ = \"" << path_
|
|
|
|
|
<< "\", unpacked_dimensions=" << sig->unpacked_dimensions()
|
|
|
|
|
<< ", port_type()=" << sig->port_type() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-23 04:50:47 +01:00
|
|
|
if (sig->unpacked_dimensions() && !gn_system_verilog()) {
|
|
|
|
|
cerr << get_fileline() << ": error: "
|
|
|
|
|
<< "Ports cannot be unpacked arrays. Try enabling SystemVerilog support." << endl;
|
2014-03-17 01:08:38 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-23 04:50:47 +01:00
|
|
|
// There cannot be parts to an unpacked array, so process this
|
|
|
|
|
// simply as an unpacked array.
|
|
|
|
|
if (sig->unpacked_dimensions()) {
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_fileline() << ": PEIdent::elaborate_subport: "
|
|
|
|
|
<< "path_=\"" << path_
|
|
|
|
|
<< "\" is an unpacked array with " << sig->pin_count()
|
|
|
|
|
<< " elements." << endl;
|
|
|
|
|
}
|
|
|
|
|
scope->add_module_port_net(sig);
|
|
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/* Evaluate the part/bit select expressions, to get the part
|
|
|
|
|
select of the signal that attaches to the port. Also handle
|
|
|
|
|
range and direction checking here. */
|
2000-05-16 06:05:15 +02:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
if (! eval_part_select_(des, scope, sig, midx, lidx))
|
|
|
|
|
return 0;
|
2002-06-19 06:20:03 +02:00
|
|
|
|
2008-12-12 06:35:28 +01:00
|
|
|
/* If this is a part select of the entire signal (or no part
|
|
|
|
|
select at all) then we're done. */
|
2011-03-07 20:18:23 +01:00
|
|
|
if ((lidx == 0) && (midx == (long)sig->vector_width()-1)) {
|
2012-06-04 21:43:33 +02:00
|
|
|
scope->add_module_port_net(sig);
|
2008-12-12 06:35:28 +01:00
|
|
|
return sig;
|
2011-03-07 20:18:23 +01:00
|
|
|
}
|
2008-12-12 06:35:28 +01:00
|
|
|
|
|
|
|
|
unsigned swid = abs(midx - lidx) + 1;
|
|
|
|
|
ivl_assert(*this, swid > 0 && swid < sig->vector_width());
|
|
|
|
|
|
2025-10-21 07:45:05 +02:00
|
|
|
const netvector_t*tmp2_vec = new netvector_t(sig->data_type(),swid-1,0);
|
2008-12-12 06:35:28 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2012-09-15 19:27:43 +02:00
|
|
|
NetNet::WIRE, tmp2_vec);
|
2008-12-12 06:35:28 +01:00
|
|
|
tmp->port_type(sig->port_type());
|
|
|
|
|
tmp->set_line(*this);
|
2011-03-07 20:18:23 +01:00
|
|
|
tmp->local_flag(true);
|
2008-12-12 06:35:28 +01:00
|
|
|
NetNode*ps = 0;
|
|
|
|
|
switch (sig->port_type()) {
|
|
|
|
|
|
|
|
|
|
case NetNet::PINPUT:
|
2011-03-03 03:14:57 +01:00
|
|
|
ps = new NetPartSelect(sig, lidx, swid, NetPartSelect::PV);
|
2008-12-12 06:35:28 +01:00
|
|
|
connect(tmp->pin(0), ps->pin(0));
|
|
|
|
|
sig = tmp;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NetNet::POUTPUT:
|
2011-03-03 03:14:57 +01:00
|
|
|
ps = new NetPartSelect(sig, lidx, swid, NetPartSelect::VP);
|
2008-12-12 06:35:28 +01:00
|
|
|
connect(tmp->pin(0), ps->pin(0));
|
|
|
|
|
sig = tmp;
|
|
|
|
|
break;
|
2000-05-16 06:05:15 +02:00
|
|
|
|
2012-02-25 19:19:48 +01:00
|
|
|
case NetNet::PREF:
|
|
|
|
|
// For the purposes of module ports, treat ref ports
|
|
|
|
|
// just like inout ports.
|
2008-12-12 06:35:28 +01:00
|
|
|
case NetNet::PINOUT:
|
|
|
|
|
ps = new NetTran(scope, scope->local_symbol(), sig->vector_width(),
|
2011-03-03 03:14:57 +01:00
|
|
|
swid, lidx);
|
2008-12-12 06:35:28 +01:00
|
|
|
connect(sig->pin(0), ps->pin(0));
|
|
|
|
|
connect(tmp->pin(0), ps->pin(1));
|
|
|
|
|
sig = tmp;
|
|
|
|
|
break;
|
2000-05-16 06:05:15 +02:00
|
|
|
|
2008-12-12 06:35:28 +01:00
|
|
|
default:
|
|
|
|
|
ivl_assert(*this, 0);
|
|
|
|
|
break;
|
2000-05-16 06:05:15 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-12 06:35:28 +01:00
|
|
|
ps->set_line(*this);
|
|
|
|
|
des->add_node(ps);
|
|
|
|
|
|
2012-06-04 21:43:33 +02:00
|
|
|
scope->add_module_port_net(sig);
|
2000-05-16 06:05:15 +02:00
|
|
|
return sig;
|
|
|
|
|
}
|
2011-03-13 14:29:42 +01:00
|
|
|
|
2014-03-17 01:08:38 +01:00
|
|
|
NetNet*PEIdent::elaborate_unpacked_net(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
2022-12-29 22:36:08 +01:00
|
|
|
symbol_search_results sr;
|
2024-02-19 11:02:40 +01:00
|
|
|
symbol_search(this, des, scope, path_, lexical_pos_, &sr);
|
2022-12-29 22:36:08 +01:00
|
|
|
if (!sr.net) {
|
2022-09-12 22:13:25 +02:00
|
|
|
cerr << get_fileline() << ": error: Net " << path_
|
|
|
|
|
<< " is not defined in this context." << endl;
|
2025-07-21 23:46:01 +02:00
|
|
|
if (sr.is_found()) {
|
|
|
|
|
cerr << sr.scope->get_fileline() << ": : Found a "
|
|
|
|
|
<< sr.result_type() << " with this name here." << endl;
|
|
|
|
|
}
|
2024-02-25 17:12:31 +01:00
|
|
|
if (sr.decl_after_use) {
|
|
|
|
|
cerr << sr.decl_after_use->get_fileline() << ": : "
|
|
|
|
|
"A symbol with that name was declared here. "
|
|
|
|
|
"Check for declaration after use." << endl;
|
|
|
|
|
}
|
2022-09-12 22:13:25 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2014-03-17 01:08:38 +01:00
|
|
|
|
2022-09-12 22:13:25 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
2022-12-28 08:59:39 +01:00
|
|
|
if (!name_tail.index.empty()) {
|
2022-09-12 22:13:25 +02:00
|
|
|
cerr << get_fileline() << ": sorry: Array slices are not yet "
|
|
|
|
|
<< "supported for continuous assignment." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2014-03-17 01:08:38 +01:00
|
|
|
|
2022-12-29 22:36:08 +01:00
|
|
|
return sr.net;
|
2014-03-17 01:08:38 +01:00
|
|
|
}
|
|
|
|
|
|
2020-12-01 07:44:54 +01:00
|
|
|
bool PEIdent::is_collapsible_net(Design*des, NetScope*scope,
|
|
|
|
|
NetNet::PortType port_type) const
|
2011-03-13 14:29:42 +01:00
|
|
|
{
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*this, scope);
|
2011-03-13 14:29:42 +01:00
|
|
|
|
2024-02-18 10:36:23 +01:00
|
|
|
symbol_search_results sr;
|
2024-02-19 11:02:40 +01:00
|
|
|
symbol_search(this, des, scope, path_.name, lexical_pos_, &sr);
|
2011-03-13 14:29:42 +01:00
|
|
|
|
2024-02-18 10:36:23 +01:00
|
|
|
if (sr.eve != 0)
|
2011-03-13 14:29:42 +01:00
|
|
|
return false;
|
|
|
|
|
|
2024-02-18 10:36:23 +01:00
|
|
|
NetNet*sig = sr.net;
|
|
|
|
|
|
2011-03-13 14:29:42 +01:00
|
|
|
if (sig == 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
2023-04-13 22:00:34 +02:00
|
|
|
ivl_assert(*this, sig);
|
2011-03-13 14:29:42 +01:00
|
|
|
|
|
|
|
|
/* If this is SystemVerilog and the variable is not yet
|
|
|
|
|
assigned by anything, then convert it to an unresolved
|
|
|
|
|
wire. */
|
2020-12-01 07:44:54 +01:00
|
|
|
if (gn_var_can_be_uwire() &&
|
|
|
|
|
(sig->type() == NetNet::REG) &&
|
|
|
|
|
(sig->peek_eref() == 0) &&
|
|
|
|
|
(port_type == NetNet::POUTPUT)) {
|
2011-03-13 14:29:42 +01:00
|
|
|
sig->type(NetNet::UNRESOLVED_WIRE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sig->type() == NetNet::UNRESOLVED_WIRE && sig->pin(0).is_linked())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (sig->type() == NetNet::REG)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|