加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
elab_lval.cc 51.47 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503
/*
* Copyright (c) 2000-2022 Stephen Williams (steve@icarus.com)
* Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com)
*
* 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
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
# include "config.h"
# include "PExpr.h"
# include "PPackage.h"
# include "netlist.h"
# include "netmisc.h"
# include "netstruct.h"
# include "netclass.h"
# include "netdarray.h"
# include "netparray.h"
# include "netvector.h"
# include "netenum.h"
# include "compiler.h"
# include <cstdlib>
# include <iostream>
# include <climits>
# include "ivl_assert.h"
using namespace std;
/*
* These methods generate a NetAssign_ object for the l-value of the
* assignment. This is common code for the = and <= statements.
*
* What gets generated depends on the structure of the l-value. If the
* l-value is a simple name (i.e., foo <= <value>) then the NetAssign_
* is created the width of the foo reg and connected to all the
* bits.
*
* If there is a part select (i.e., foo[3:1] <= <value>) the NetAssign_
* is made only as wide as it needs to be (3 bits in this example) and
* connected to the correct bits of foo. A constant bit select is a
* special case of the part select.
*
* If the bit-select is non-constant (i.e., foo[<expr>] = <value>) the
* NetAssign_ is made wide enough to connect to all the bits of foo,
* then the mux expression is elaborated and attached to the
* NetAssign_ node as a b_mux value. The target must interpret the
* presence of a bmux value as taking a single bit and assigning it to
* the bit selected by the bmux expression.
*
* If the l-value expression is non-trivial, but can be fully
* evaluated at compile time (meaning any bit selects are constant)
* then elaboration will make a single NetAssign_ that connects to a
* synthetic reg that in turn connects to all the proper pins of the
* l-value.
*
* This last case can turn up in statements like: {a, b[1]} = c;
* rather than create a NetAssign_ for each item in the concatenation,
* elaboration makes a single NetAssign_ and connects it up properly.
*/
/*
* The default interpretation of an l-value to a procedural assignment
* is to try to make a net elaboration, and see if the result is
* suitable for assignment.
*/
NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool, bool) const
{
cerr << get_fileline() << ": Assignment l-value too complex." << endl;
return 0;
}
/*
* Concatenation expressions can appear as l-values. Handle them here.
*
* If adjacent l-values in the concatenation are not bit selects, then
* merge them into a single NetAssign_ object. This can happen is code
* like ``{ ...a, b, ...}''. As long as "a" and "b" do not have bit
* selects (or the bit selects are constant) we can merge the
* NetAssign_ objects.
*
* Be careful to get the bit order right. In the expression ``{a, b}''
* a is the MSB and b the LSB. Connect the LSB to the low pins of the
* NetAssign_ object.
*/
NetAssign_* PEConcat::elaborate_lval(Design*des,
NetScope*scope,
bool is_cassign,
bool is_force) const
{
if (repeat_) {
cerr << get_fileline() << ": error: Repeat concatenations make "
"no sense in l-value expressions. I refuse." << endl;
des->errors += 1;
return 0;
}
NetAssign_*res = 0;
for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) {
if (parms_[idx] == 0) {
cerr << get_fileline() << ": error: Empty expressions "
<< "not allowed in concatenations." << endl;
des->errors += 1;
continue;
}
NetAssign_*tmp = parms_[idx]->elaborate_lval(des, scope,
is_cassign, is_force);
/* If the l-value doesn't elaborate, the error was
already detected and printed. We just skip it and let
the compiler catch more errors. */
if (tmp == 0) continue;
if (tmp->expr_type() == IVL_VT_REAL) {
cerr << parms_[idx]->get_fileline() << ": error: "
<< "concatenation operand can not be real: "
<< *parms_[idx] << endl;
des->errors += 1;
continue;
}
/* A concatenation is always unsigned. */
tmp->set_signed(false);
/* Link the new l-value to the previous one. */
NetAssign_*last = tmp;
while (last->more)
last = last->more;
last->more = res;
res = tmp;
}
return res;
}
/*
* Handle the ident as an l-value. This includes bit and part selects
* of that ident.
*/
NetAssign_* PEIdent::elaborate_lval(Design*des,
NetScope*scope,
bool is_cassign,
bool is_force) const
{
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval: "
<< "Elaborate l-value ident expression: " << *this << endl;
}
symbol_search_results sr;
symbol_search(this, des, scope, path_, &sr);
NetNet *reg = sr.net;
pform_name_t &member_path = sr.path_tail;
/* The l-value must be a variable. If not, then give up and
print a useful error message. */
if (reg == 0) {
if (scope->type()==NetScope::FUNC
&& scope->func_def()->is_void()
&& scope->basename()==peek_tail_name(path_)) {
cerr << get_fileline() << ": error: "
<< "Cannot assign to " << path_
<< " because function " << scope_path(scope)
<< " is void." << endl;
} else {
cerr << get_fileline() << ": error: Could not find variable ``"
<< path_ << "'' in ``" << scope_path(scope) <<
"''" << endl;
}
des->errors += 1;
return 0;
}
ivl_assert(*this, reg);
if (debug_elaborate) {
cerr << get_fileline() << ": " << __func__ << ": "
<< "Found l-value path_=" << path_
<< " as reg=" << reg->name() << endl;
cerr << get_fileline() << ": " << __func__ << ": "
<< "reg->type()=" << reg->type()
<< ", reg->unpacked_dimensions()=" << reg->unpacked_dimensions()
<< endl;
if (reg->net_type())
cerr << get_fileline() << ": " << __func__ << ": "
<< "reg->net_type()=" << *reg->net_type() << endl;
else
cerr << get_fileline() << ": " << __func__ << ": "
<< "reg->net_type()=<nil>" << endl;
const pform_name_t &base_path = sr.path_head;
cerr << get_fileline() << ": " << __func__ << ": "
<< " base_path=" << base_path
<< ", member_path=" << member_path
<< endl;
}
return elaborate_lval_var_(des, scope, is_force, is_cassign, reg,
sr.type, member_path);
}
NetAssign_*PEIdent::elaborate_lval_var_(Design *des, NetScope *scope,
bool is_force, bool is_cassign,
NetNet *reg, ivl_type_t data_type,
pform_name_t tail_path) const
{
// We are processing the tail of a string of names. For
// example, the Verilog may be "a.b.c", so we are processing
// "c" at this point.
const name_component_t&name_tail = path_.back();
// Use the last index to determine what kind of select
// (bit/part/etc) we are processing. For example, the Verilog
// may be "a.b.c[1][2][<index>]". All but the last index must
// be simple expressions, only the <index> may be a part
// select etc., so look at it to determine how we will be
// proceeding.
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
if (!name_tail.index.empty())
use_sel = name_tail.index.back().sel;
// Special case: The l-value is an entire memory, or array
// slice. Detect the situation by noting if the index count
// is less than the array dimensions (unpacked).
if (reg->unpacked_dimensions() > name_tail.index.size()) {
if (gn_system_verilog()) {
cerr << get_fileline() << ": sorry: Assignment to an entire"
" array or to an array slice is not yet supported."
<< endl;
} else {
cerr << get_fileline() << ": error: Assignment to an entire"
" array or to an array slice requires SystemVerilog."
<< endl;
}
des->errors += 1;
return 0;
}
/* Get the signal referenced by the identifier, and make sure
it is a register. Wires are not allowed in this context,
unless this is the l-value of a force. */
if ((reg->type() != NetNet::REG)
&& (reg->type() != NetNet::UNRESOLVED_WIRE)
&& !is_force) {
cerr << get_fileline() << ": error: " << path_ <<
" is not a valid l-value in " << scope_path(scope) <<
"." << endl;
cerr << reg->get_fileline() << ": : " << path_ <<
" is declared here as " << reg->type() << "." << endl;
des->errors += 1;
return 0;
}
// If we find that the matched variable is a packed struct,
// then we can handled it with the net_packed_member_ method.
if (reg->struct_type() && !tail_path.empty()) {
NetAssign_*lv = new NetAssign_(reg);
elaborate_lval_net_packed_member_(des, scope, lv, tail_path);
return lv;
}
// If the variable is a class object, then handle it with the
// net_class_member_ method.
const netclass_t *class_type = dynamic_cast<const netclass_t *>(data_type);
if (class_type && !tail_path.empty() && gn_system_verilog())
return elaborate_lval_net_class_member_(des, scope, class_type, reg, tail_path);
// Past this point, we should have taken care of the cases
// where the name is a member/method of a struct/class.
// XXXX ivl_assert(*this, method_name.nil());
ivl_assert(*this, tail_path.empty());
bool need_const_idx = is_cassign || is_force || (reg->type()==NetNet::UNRESOLVED_WIRE);
if (reg->unpacked_dimensions() > 0)
return elaborate_lval_net_word_(des, scope, reg, need_const_idx);
// This must be after the array word elaboration above!
if (reg->get_scalar() &&
use_sel != index_component_t::SEL_NONE) {
cerr << get_fileline() << ": error: can not select part of ";
if (reg->data_type() == IVL_VT_REAL) cerr << "real: ";
else cerr << "scalar: ";
cerr << reg->name() << endl;
des->errors += 1;
return 0;
}
if (use_sel == index_component_t::SEL_PART) {
NetAssign_*lv = new NetAssign_(reg);
elaborate_lval_net_part_(des, scope, lv);
return lv;
}
if (use_sel == index_component_t::SEL_IDX_UP ||
use_sel == index_component_t::SEL_IDX_DO) {
NetAssign_*lv = new NetAssign_(reg);
elaborate_lval_net_idx_(des, scope, lv, use_sel, need_const_idx);
return lv;
}
if (use_sel == index_component_t::SEL_BIT) {
if (reg->darray_type()) {
NetAssign_*lv = new NetAssign_(reg);
elaborate_lval_darray_bit_(des, scope, lv);
return lv;
} else {
NetAssign_*lv = new NetAssign_(reg);
elaborate_lval_net_bit_(des, scope, lv, need_const_idx);
return lv;
}
}
ivl_assert(*this, use_sel == index_component_t::SEL_NONE);
if (reg->type()==NetNet::UNRESOLVED_WIRE && !is_force) {
cerr << get_fileline() << ": error: "
<< path_ << " Unable to assign to unresolved wires."
<< endl;
des->errors += 1;
return 0;
}
/* No select expressions. */
NetAssign_*lv = new NetAssign_(reg);
lv->set_signed(reg->get_signed());
return lv;
}
NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des,
NetScope*scope,
NetNet*reg,
bool need_const_idx) const
{
const name_component_t&name_tail = path_.back();
ivl_assert(*this, !name_tail.index.empty());
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_word_: "
<< "Handle as n-dimensional array." << endl;
}
if (name_tail.index.size() < reg->unpacked_dimensions()) {
cerr << get_fileline() << ": error: Array " << reg->name()
<< " needs " << reg->unpacked_dimensions() << " indices,"
<< " but got only " << name_tail.index.size() << "." << endl;
des->errors += 1;
return 0;
}
// Make sure there are enough indices to address an array element.
const index_component_t&index_head = name_tail.index.front();
if (index_head.sel == index_component_t::SEL_PART) {
cerr << get_fileline() << ": error: cannot perform a part "
<< "select on array " << reg->name() << "." << endl;
des->errors += 1;
return 0;
}
// Evaluate all the index expressions into an
// "unpacked_indices" array.
list<NetExpr*>unpacked_indices;
list<long> unpacked_indices_const;
indices_flags flags;
indices_to_expressions(des, scope, this,
name_tail.index, reg->unpacked_dimensions(),
false,
flags,
unpacked_indices,
unpacked_indices_const);
NetExpr*canon_index = 0;
if (flags.invalid) {
// Nothing to do.
} else if (flags.undefined) {
cerr << get_fileline() << ": warning: "
<< "ignoring undefined l-value array access "
<< reg->name() << as_indices(unpacked_indices)
<< "." << endl;
} else if (flags.variable) {
if (need_const_idx) {
cerr << get_fileline() << ": error: array '" << reg->name()
<< "' index must be a constant in this context." << endl;
des->errors += 1;
return 0;
}
ivl_assert(*this, unpacked_indices.size() == reg->unpacked_dimensions());
canon_index = normalize_variable_unpacked(reg, unpacked_indices);
} else {
ivl_assert(*this, unpacked_indices_const.size() == reg->unpacked_dimensions());
canon_index = normalize_variable_unpacked(reg, unpacked_indices_const);
if (canon_index == 0) {
cerr << get_fileline() << ": warning: "
<< "ignoring out of bounds l-value array access "
<< reg->name() << as_indices(unpacked_indices_const)
<< "." << endl;
}
}
// Ensure invalid array accesses are ignored.
if (canon_index == 0)
canon_index = new NetEConst(verinum(verinum::Vx));
canon_index->set_line(*this);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_word_: "
<< "canon_index=" << *canon_index << endl;
}
if (reg->type()==NetNet::UNRESOLVED_WIRE) {
cerr << get_fileline() << ": error: "
<< "Unable to assign words of unresolved wire array." << endl;
des->errors += 1;
return 0;
}
NetAssign_*lv = new NetAssign_(reg);
lv->set_word(canon_index);
if (debug_elaborate)
cerr << get_fileline() << ": debug: Set array word=" << *canon_index << endl;
/* An array word may also have part selects applied to them. */
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
if (name_tail.index.size() > reg->unpacked_dimensions())
use_sel = name_tail.index.back().sel;
if (reg->get_scalar() &&
use_sel != index_component_t::SEL_NONE) {
cerr << get_fileline() << ": error: can not select part of ";
if (reg->data_type() == IVL_VT_REAL) cerr << "real";
else cerr << "scalar";
cerr << " array word: " << reg->name()
<< as_indices(unpacked_indices) << endl;
des->errors += 1;
return 0;
}
if (use_sel == index_component_t::SEL_BIT)
elaborate_lval_net_bit_(des, scope, lv, need_const_idx);
if (use_sel == index_component_t::SEL_PART)
elaborate_lval_net_part_(des, scope, lv);
if (use_sel == index_component_t::SEL_IDX_UP ||
use_sel == index_component_t::SEL_IDX_DO)
elaborate_lval_net_idx_(des, scope, lv, use_sel, need_const_idx);
return lv;
}
bool PEIdent::elaborate_lval_net_bit_(Design*des,
NetScope*scope,
NetAssign_*lv,
bool need_const_idx) const
{
list<long>prefix_indices;
bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices);
if (!rc) return false;
const name_component_t&name_tail = path_.back();
ivl_assert(*this, !name_tail.index.empty());
const index_component_t&index_tail = name_tail.index.back();
ivl_assert(*this, index_tail.msb != 0);
ivl_assert(*this, index_tail.lsb == 0);
NetNet*reg = lv->sig();
ivl_assert(*this, reg);
// Bit selects have a single select expression. Evaluate the
// constant value and treat it as a part select with a bit
// width of 1.
NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1);
long lsb = 0;
if (NetEConst*index_con = dynamic_cast<NetEConst*> (mux)) {
// The index has a constant defined value.
if (index_con->value().is_defined()) {
lsb = index_con->value().as_long();
mux = 0;
// The index is undefined and this is a packed array.
} else if (prefix_indices.size()+2 <= reg->packed_dims().size()) {
long loff;
unsigned long lwid;
bool rcl = reg->sb_to_slice(prefix_indices, lsb, loff, lwid);
ivl_assert(*this, rcl);
if (warn_ob_select) {
cerr << get_fileline()
<< ": warning: L-value packed array select of "
<< reg->name();
if (reg->unpacked_dimensions() > 0) cerr << "[]";
cerr << " has an undefined index." << endl;
}
lv->set_part(new NetEConst(verinum(verinum::Vx)), lwid);
return true;
// The index is undefined and this is a bit select.
} else {
if (warn_ob_select) {
cerr << get_fileline()
<< ": warning: L-value bit select of "
<< reg->name();
if (reg->unpacked_dimensions() > 0) cerr << "[]";
cerr << " has an undefined index." << endl;
}
lv->set_part(new NetEConst(verinum(verinum::Vx)), 1);
return true;
}
}
if (debug_elaborate && (reg->type()==NetNet::UNRESOLVED_WIRE)) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_bit_: "
<< "Try to assign bits of unresolved wire."
<< endl;
}
// Notice that we might be assigning to an unresolved wire. This
// can happen if we are actually assigning to a variable that
// has a partial continuous assignment to it. If that is the
// case, then the bit select must be constant.
ivl_assert(*this, need_const_idx || (reg->type()!=NetNet::UNRESOLVED_WIRE));
if (prefix_indices.size()+2 <= reg->packed_dims().size()) {
// Special case: this is a slice of a multi-dimensional
// packed array. For example:
// reg [3:0][7:0] x;
// x[2] = ...
// This shows up as the prefix_indices being too short
// for the packed dimensions of the vector. What we do
// here is convert to a "slice" of the vector.
if (mux == 0) {
long loff;
unsigned long lwid;
bool rcl = reg->sb_to_slice(prefix_indices, lsb, loff, lwid);
ivl_assert(*this, rcl);
if (reg->type()==NetNet::UNRESOLVED_WIRE) {
bool rct = reg->test_and_set_part_driver(loff+lwid-1, loff);
if (rct) {
cerr << get_fileline() << ": error: "
<< "These bits are already driven." << endl;
des->errors += 1;
}
}
lv->set_part(new NetEConst(verinum(loff)), lwid);
} else {
ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE);
unsigned long lwid;
mux = normalize_variable_slice_base(prefix_indices, mux,
reg, lwid);
lv->set_part(mux, lwid);
}
} else if (reg->data_type() == IVL_VT_STRING) {
ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE);
// Special case: This is a select of a string
// variable. The target of the assignment is a character
// select of a string. Force the r-value to be an 8bit
// vector and set the "part" to be the character select
// expression. The code generator knows what to do with
// this.
if (debug_elaborate) {
cerr << get_fileline() << ": debug: "
<< "Bit select of string becomes character select." << endl;
}
if (!mux)
mux = new NetEConst(verinum(lsb));
lv->set_part(mux, &netvector_t::atom2s8);
} else if (mux) {
ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE);
// Non-constant bit mux. Correct the mux for the range
// of the vector, then set the l-value part select
// expression.
if (need_const_idx) {
cerr << get_fileline() << ": error: '" << reg->name()
<< "' bit select must be a constant in this context."
<< endl;
des->errors += 1;
return false;
}
mux = normalize_variable_bit_base(prefix_indices, mux, reg);
lv->set_part(mux, 1);
} else if (reg->vector_width() == 1 && reg->sb_is_valid(prefix_indices,lsb)) {
// Constant bit mux that happens to select the only bit
// of the l-value. Don't bother with any select at all.
// NOTE: Don't know what to do about unresolved wires
// here, but they are probably wrong.
ivl_assert(*this, reg->type()!=NetNet::UNRESOLVED_WIRE);
} else {
// Constant bit select that does something useful.
long loff = reg->sb_to_idx(prefix_indices,lsb);
if (warn_ob_select && (loff < 0 || loff >= (long)reg->vector_width())) {
cerr << get_fileline() << ": warning: bit select "
<< reg->name() << "[" <<lsb<<"]"
<< " is out of range." << endl;
}
if (reg->type()==NetNet::UNRESOLVED_WIRE) {
bool rct = reg->test_and_set_part_driver(loff, loff);
if (rct) {
cerr << get_fileline() << ": error: "
<< "Bit " << loff << " is already driven." << endl;
des->errors += 1;
}
}
lv->set_part(new NetEConst(verinum(loff)), 1);
}
return true;
}
bool PEIdent::elaborate_lval_darray_bit_(Design*des, NetScope*scope, NetAssign_*lv)const
{
const name_component_t&name_tail = path_.back();
ivl_assert(*this, !name_tail.index.empty());
// For now, only support single-dimension dynamic arrays.
ivl_assert(*this, name_tail.index.size() == 1);
if (lv->sig()->type()==NetNet::UNRESOLVED_WIRE) {
cerr << get_fileline() << ": error: "
<< path_ << " Unable to darray word select unresolved wires."
<< endl;
des->errors += 1;
return false;
}
const index_component_t&index_tail = name_tail.index.back();
ivl_assert(*this, index_tail.msb != 0);
ivl_assert(*this, index_tail.lsb == 0);
// Evaluate the select expression...
NetExpr*mux = elab_and_eval(des, scope, index_tail.msb, -1);
lv->set_word(mux);
return true;
}
bool PEIdent::elaborate_lval_net_part_(Design*des,
NetScope*scope,
NetAssign_*lv) const
{
list<long> prefix_indices;
bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices);
ivl_assert(*this, rc);
// The range expressions of a part select must be
// constant. The calculate_parts_ function calculates the
// values into msb and lsb.
long msb, lsb;
bool parts_defined_flag;
bool flag = calculate_parts_(des, scope, msb, lsb, parts_defined_flag);
if (!flag) return false;
NetNet*reg = lv->sig();
ivl_assert(*this, reg);
if (! parts_defined_flag) {
if (warn_ob_select) {
cerr << get_fileline()
<< ": warning: L-value part select of "
<< reg->name();
if (reg->unpacked_dimensions() > 0) cerr << "[]";
cerr << " has an undefined index." << endl;
}
// Use a width of two here so we can distinguish between an
// undefined bit or part select.
lv->set_part(new NetEConst(verinum(verinum::Vx)), 2);
return true;
}
if (reg->type()==NetNet::UNRESOLVED_WIRE) {
bool rct = reg->test_and_set_part_driver(msb, lsb);
if (rct) {
cerr << get_fileline() << ": error: "
<< path_ << "Part select is double-driving unresolved wire."
<< endl;
des->errors += 1;
return false;
}
}
const vector<netrange_t>&packed = reg->packed_dims();
long loff, moff;
long wid;
if (prefix_indices.size()+1 < packed.size()) {
// If there are fewer indices then there are packed
// dimensions, then this is a range of slices. Calculate
// it into a big slice.
bool lrc, mrc;
unsigned long tmp_lwid, tmp_mwid;
lrc = reg->sb_to_slice(prefix_indices, lsb, loff, tmp_lwid);
mrc = reg->sb_to_slice(prefix_indices, msb, moff, tmp_mwid);
if (!mrc || !lrc) {
cerr << get_fileline() << ": error: ";
cerr << "Part-select [" << msb << ":" << lsb;
cerr << "] exceeds the declared bounds for ";
cerr << reg->name();
if (reg->unpacked_dimensions() > 0) cerr << "[]";
cerr << "." << endl;
des->errors += 1;
return 0;
}
if (loff < moff) {
moff = moff + tmp_mwid - 1;
} else {
long ltmp = moff;
moff = loff + tmp_lwid - 1;
loff = ltmp;
}
wid = moff - loff + 1;
} else {
loff = reg->sb_to_idx(prefix_indices,lsb);
moff = reg->sb_to_idx(prefix_indices,msb);
wid = moff - loff + 1;
if (moff < loff) {
cerr << get_fileline() << ": error: part select "
<< reg->name() << "[" << msb<<":"<<lsb<<"]"
<< " is reversed." << endl;
des->errors += 1;
return false;
}
}
// Special case: The range winds up selecting the entire
// vector. Treat this as no part select at all.
if (loff == 0 && moff == (long)(reg->vector_width()-1)) {
return true;
}
/* If the part select extends beyond the extremes of the
variable, then output a warning. Note that loff is
converted to normalized form so is relative the
variable pins. */
if (warn_ob_select && (loff < 0 || moff >= (long)reg->vector_width())) {
cerr << get_fileline() << ": warning: Part select "
<< reg->name() << "[" << msb<<":"<<lsb<<"]"
<< " is out of range." << endl;
}
lv->set_part(new NetEConst(verinum(loff)), wid);
return true;
}
bool PEIdent::elaborate_lval_net_idx_(Design*des,
NetScope*scope,
NetAssign_*lv,
index_component_t::ctype_t use_sel,
bool need_const_idx) const
{
list<long>prefix_indices;
bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices);
ivl_assert(*this, rc);
const name_component_t&name_tail = path_.back();;
ivl_assert(*this, !name_tail.index.empty());
const index_component_t&index_tail = name_tail.index.back();
ivl_assert(*this, index_tail.msb != 0);
ivl_assert(*this, index_tail.lsb != 0);
NetNet*reg = lv->sig();
assert(reg);
unsigned long wid;
calculate_up_do_width_(des, scope, wid);
NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1);
ivl_select_type_t sel_type = IVL_SEL_OTHER;
// Handle the special case that the base is constant. For this
// case we can reduce the expression.
if (NetEConst*base_c = dynamic_cast<NetEConst*> (base)) {
// For the undefined case just let the constant pass and
// we will handle it in the code generator.
if (base_c->value().is_defined()) {
long lsv = base_c->value().as_long();
long rel_base = 0;
// Get the signal range.
const vector<netrange_t>&packed = reg->packed_dims();
if (prefix_indices.size()+1 < reg->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;
bool lrc, mrc;
mrc = reg->sb_to_slice(prefix_indices, lsv, moff, mwid);
if (use_sel == index_component_t::SEL_IDX_UP)
lrc = reg->sb_to_slice(prefix_indices, lsv+wid-1, loff, lwid);
else
lrc = reg->sb_to_slice(prefix_indices, lsv-wid+1, loff, lwid);
if (!mrc || !lrc) {
cerr << get_fileline() << ": error: ";
cerr << "Part-select [" << lsv;
if (index_tail.sel == index_component_t::SEL_IDX_UP) {
cerr << "+:";
} else {
cerr << "-:";
}
cerr << wid << "] exceeds the declared bounds for ";
cerr << reg->name();
if (reg->unpacked_dimensions() > 0) cerr << "[]";
cerr << "." << endl;
des->errors += 1;
return 0;
}
ivl_assert(*this, lwid == mwid);
if (moff > loff) {
rel_base = loff;
wid = moff + mwid - loff;
} else {
rel_base = moff;
wid = loff + lwid - moff;
}
} else {
long offset = 0;
// We want the last range, which is where we work.
const netrange_t&rng = packed.back();
if (((rng.get_msb() < rng.get_lsb()) &&
use_sel == index_component_t::SEL_IDX_UP) ||
((rng.get_msb() > rng.get_lsb()) &&
use_sel == index_component_t::SEL_IDX_DO)) {
offset = -wid + 1;
}
rel_base = reg->sb_to_idx(prefix_indices,lsv) + offset;
}
delete base;
/* If we cover the entire lvalue just skip the select. */
if (rel_base == 0 && wid == reg->vector_width()) return true;
base = new NetEConst(verinum(rel_base));
if (warn_ob_select) {
if (rel_base < 0) {
cerr << get_fileline() << ": warning: " << reg->name();
if (reg->unpacked_dimensions() > 0) cerr << "[]";
cerr << "[" << lsv;
if (use_sel == index_component_t::SEL_IDX_UP) {
cerr << "+:";
} else {
cerr << "-:";
}
cerr << wid << "] is selecting before vector." << endl;
}
if (rel_base + wid > reg->vector_width()) {
cerr << get_fileline() << ": warning: " << reg->name();
if (reg->unpacked_dimensions() > 0) cerr << "[]";
cerr << "[" << lsv;
if (use_sel == index_component_t::SEL_IDX_UP) {
cerr << "+:";
} else {
cerr << "-:";
}
cerr << wid << "] is selecting after vector." << endl;
}
}
} else if (warn_ob_select) {
cerr << get_fileline() << ": warning: L-value indexed part "
<< "select of " << reg->name();
if (reg->unpacked_dimensions() > 0) cerr << "[]";
cerr << " has an undefined base." << endl;
}
} else {
if (need_const_idx) {
cerr << get_fileline() << ": error: '" << reg->name()
<< "' base index must be a constant in this context."
<< endl;
des->errors += 1;
return false;
}
ivl_assert(*this, prefix_indices.size()+1 == reg->packed_dims().size());
/* Correct the mux for the range of the vector. */
if (use_sel == index_component_t::SEL_IDX_UP) {
base = normalize_variable_part_base(prefix_indices, base,
reg, wid, true);
sel_type = IVL_SEL_IDX_UP;
} else {
// This is assumed to be a SEL_IDX_DO.
base = normalize_variable_part_base(prefix_indices, base,
reg, wid, false);
sel_type = IVL_SEL_IDX_DOWN;
}
}
if (debug_elaborate)
cerr << get_fileline() << ": debug: Set part select width="
<< wid << ", base=" << *base << endl;
lv->set_part(base, wid, sel_type);
return true;
}
/*
* When the l-value turns out to be a class object, this method is
* called with the bound variable, and the method path. For example,
* if path_=a.b.c and a.b binds to the variable, then sig is b, and
* member_path=c. if path_=obj.base.x, and base_path=obj, then sig is
* obj, and member_path=base.x.
*/
NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope,
const netclass_t *class_type, NetNet*sig,
pform_name_t member_path) const
{
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: "
<< "l-value is property " << member_path
<< " of " << sig->name() << "." << endl;
}
ivl_assert(*this, class_type);
// Iterate over the member_path. This handles nested class
// object, by generating nested NetAssign_ object. We start
// with lv==0, so the front of the member_path is the member
// of the outermost class. This generates an lv from sig. Then
// iterate over the remaining of the member_path, replacing
// the outer lv with an lv that nests the lv from the previous
// iteration.
NetAssign_*lv = 0;
do {
// Start with the first component of the member path...
perm_string method_name = peek_head_name(member_path);
// Pull that component from the member_path. We need to
// know the current member being worked on, and will
// need to know if there are more members to be worked on.
name_component_t member_cur = member_path.front();
member_path.pop_front();
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: "
<< "Processing member_cur=" << member_cur
<< endl;
}
// Make sure the property is really present in the class. If
// not, then generate an error message and return an error.
int pidx = class_type->property_idx_from_name(method_name);
if (pidx < 0) {
cerr << get_fileline() << ": error: Class " << class_type->get_name()
<< " does not have a property " << method_name << "." << endl;
des->errors += 1;
return 0;
}
property_qualifier_t qual = class_type->get_prop_qual(pidx);
if (qual.test_local() && ! class_type->test_scope_is_method(scope)) {
cerr << get_fileline() << ": error: "
<< "Local property " << class_type->get_prop_name(pidx)
<< " is not accessible (l-value) in this context."
<< " (scope=" << scope_path(scope) << ")" << endl;
des->errors += 1;
} else if (qual.test_static()) {
// Special case: this is a static property. Ignore the
// "this" sig and use the property itself, which is not
// part of the sig, as the l-value.
NetNet*psig = class_type->find_static_property(method_name);
ivl_assert(*this, psig);
lv = new NetAssign_(psig);
return lv;
} else if (qual.test_const()) {
if (class_type->get_prop_initialized(pidx)) {
cerr << get_fileline() << ": error: "
<< "Property " << class_type->get_prop_name(pidx)
<< " is constant in this method."
<< " (scope=" << scope_path(scope) << ")" << endl;
des->errors++;
} else if (scope->basename() != "new" && scope->basename() != "new@") {
cerr << get_fileline() << ": error: "
<< "Property " << class_type->get_prop_name(pidx)
<< " is constant in this method."
<< " (scope=" << scope_path(scope) << ")" << endl;
des->errors++;
} else {
// Mark this property as initialized. This is used
// to know that we have initialized the constant
// object so the next assignment will be marked as
// illegal.
class_type->set_prop_initialized(pidx);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Found initializers for property " << class_type->get_prop_name(pidx) << endl;
}
}
}
lv = lv? new NetAssign_(lv) : new NetAssign_(sig);
lv->set_property(method_name, pidx);
// Now get the type of the property.
ivl_type_t ptype = class_type->get_prop_type(pidx);
const netdarray_t*mtype = dynamic_cast<const netdarray_t*> (ptype);
if (mtype) {
if (! member_cur.index.empty()) {
cerr << get_fileline() << ": sorry: "
<< "Array index of array properties not supported."
<< endl;
des->errors += 1;
}
}
NetExpr *canon_index = nullptr;
if (!member_cur.index.empty()) {
if (const netsarray_t *stype = dynamic_cast<const netsarray_t*>(ptype)) {
canon_index = make_canonical_index(des, scope, this,
member_cur.index, stype, false);
} else {
cerr << get_fileline() << ": error: "
<< "Index expressions don't apply to this type of property." << endl;
des->errors++;
}
}
if (const netuarray_t *tmp_ua = dynamic_cast<const netuarray_t*>(ptype)) {
const std::vector<netrange_t> &dims = tmp_ua->static_dimensions();
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Property " << class_type->get_prop_name(pidx)
<< " has " << dims.size() << " dimensions, "
<< " got " << member_cur.index.size() << " indices." << endl;
if (canon_index) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_method_class_member_: "
<< "Canonical index is:" << *canon_index << endl;
}
}
if (dims.size() != member_cur.index.size()) {
cerr << get_fileline() << ": error: "
<< "Got " << member_cur.index.size() << " indices, "
<< "expecting " << dims.size()
<< " to index the property " << class_type->get_prop_name(pidx) << "." << endl;
des->errors++;
}
}
if (canon_index)
lv->set_word(canon_index);
// If the current member is a class object, then get the
// type. We may wind up iterating, and need the proper
// class type.
class_type = dynamic_cast<const netclass_t*>(ptype);
} while (!member_path.empty());
return lv;
}
/*
* This method is caled to handle l-value identifiers that are packed
* structs. The lv is already attached to the variable, so this method
* calculates the part select that is defined by the member_path. For
* example, if the path_ is main.sub.sub_local, and the variable is
* main, then we know at this point that main is a packed struct, and
* lv contains the reference to the bound variable (main). In this
* case member_path==sub.sub_local, and it is up to this method to
* work out the part select that the member_path represents.
*/
bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
NetAssign_*lv,
pform_name_t member_path) const
{
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "path_=" << path_
<< " member_path=" << member_path
<< endl;
}
NetNet*reg = lv->sig();
ivl_assert(*this, reg);
const netstruct_t*struct_type = reg->struct_type();
ivl_assert(*this, struct_type);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "Type=" << *struct_type
<< endl;
}
if (! struct_type->packed()) {
cerr << get_fileline() << ": sorry: Only packed structures "
<< "are supported in l-value." << endl;
des->errors += 1;
return false;
}
// Looking for the base name. We need that to know about
// indices we may need to apply. This is to handle the case
// that the base is an array of structs, and not just a
// struct.
pform_name_t::const_reverse_iterator name_idx = path_.name.rbegin();
for (size_t idx = 1 ; idx < member_path.size() ; idx += 1)
++ name_idx;
if (name_idx->name != peek_head_name(member_path)) {
cerr << get_fileline() << ": internal error: "
<< "name_idx=" << name_idx->name
<< ", expecting member_name=" << peek_head_name(member_path)
<< endl;
des->errors += 1;
return false;
}
ivl_assert(*this, name_idx->name == peek_head_name(member_path));
++ name_idx;
const name_component_t&name_base = *name_idx;
// Shouldn't be seeing unpacked arrays of packed structs...
ivl_assert(*this, reg->unpacked_dimensions() == 0);
// These make up the "part" select that is the equivilent of
// following the member path through the nested structs. To
// start with, the off[set] is zero, and use_width is the
// width of the entire variable. The first member_comp is at
// some offset within the variable, and will have a reduced
// width. As we step through the member_path the off
// increases, and use_width shrinks.
unsigned long off = 0;
unsigned long use_width = struct_type->packed_width();
ivl_type_t member_type;
pform_name_t completed_path;
do {
const name_component_t member_comp = member_path.front();
const perm_string&member_name = member_comp.name;
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "Processing member_comp=" << member_comp
<< " (completed_path=" << completed_path << ")"
<< endl;
}
// This is a packed member, so the name is of the form
// "a.b[...].c[...]" which means that the path_ must have at
// least 2 components. We are processing "c[...]" at that
// point (otherwise known as member_name) and we have a
// reference to it in member_comp.
// The member_path is the members we want to follow for the
// variable. For example, main[N].a.b may have main[N] as the
// base_name, and member_path=a.b. The member_name is the
// start of the member_path, and is "a". The member_name
// should be a member of the struct_type type.
// Calculate the offset within the packed structure of the
// member, and any indices. We will add in the offset of the
// struct into the packed array later. Note that this works
// for packed unions as well (although the offset will be 0
// for union members).
unsigned long tmp_off;
const netstruct_t::member_t* member = struct_type->packed_member(member_name, tmp_off);
if (member == 0) {
cerr << get_fileline() << ": error: Member " << member_name
<< " is not a member of struct type of "
<< reg->name()
<< "." << completed_path << endl;
des->errors += 1;
return false;
}
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "Member type: " << *(member->net_type)
<< endl;
}
off += tmp_off;
ivl_assert(*this, use_width >= (unsigned long)member->net_type->packed_width());
use_width = member->net_type->packed_width();
// At this point, off and use_width are the part select
// expressed by the member_comp, which is a member of the
// struct. We can further refine the part select with any
// indices that might be present.
// Get the index component type. At this point, we only
// support bit select or none.
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
if (!member_comp.index.empty())
use_sel = member_comp.index.back().sel;
if (use_sel != index_component_t::SEL_NONE
&& use_sel != index_component_t::SEL_BIT
&& use_sel != index_component_t::SEL_PART) {
cerr << get_fileline() << ": sorry: Assignments to part selects of "
"a struct member are not yet supported." << endl;
des->errors += 1;
return false;
}
member_type = member->net_type;
if (const netvector_t*mem_vec = dynamic_cast<const netvector_t*>(member->net_type)) {
// If the member type is a netvector_t, then it is a
// vector of atom or scaler objects. For example, if the
// l-value expression is "foo.member[1][2]",
// then the member should be something like:
// ... logic [h:l][m:n] member;
// There should be index expressions index the vector
// down, but there doesn't need to be all of them. We
// can, for example, be selecting a part of the vector.
// We only need to process this if there are any
// index expressions. If not, then the packed
// vector can be handled atomically.
// In any case, this should be the tail of the
// member_path, because the array element of this
// kind of array cannot be a struct.
if (!member_comp.index.empty()) {
// These are the dimensions defined by the type
const vector<netrange_t>&mem_packed_dims = mem_vec->packed_dims();
if (member_comp.index.size() > mem_packed_dims.size()) {
cerr << get_fileline() << ": error: "
<< "Too many index expressions for member." << endl;
des->errors += 1;
return false;
}
// Evaluate all but the last index expression, into prefix_indices.
list<long>prefix_indices;
bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index);
ivl_assert(*this, rc);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "prefix_indices.size()==" << prefix_indices.size()
<< ", mem_packed_dims.size()==" << mem_packed_dims.size()
<< " (netvector_t context)"
<< endl;
}
long tail_off = 0;
unsigned long tail_wid = 0;
rc = calculate_part(this, des, scope, member_comp.index.back(), tail_off, tail_wid);
ivl_assert(*this, rc);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "calculate_part for tail returns tail_off=" << tail_off
<< ", tail_wid=" << tail_wid
<< endl;
}
// Now use the prefix_to_slice function to calculate the
// offset and width of the addressed slice of the member.
long loff;
unsigned long lwid;
prefix_to_slice(mem_packed_dims, prefix_indices, tail_off, loff, lwid);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "Calculate loff=" << loff << " lwid=" << lwid
<< " tail_off=" << tail_off << " tail_wid=" << tail_wid
<< " off=" << off << " use_width=" << use_width
<< endl;
}
off += loff;
use_width = lwid * tail_wid;
member_type = nullptr;
}
// The netvector_t only has atom elements, to
// there is no next struct type.
struct_type = 0;
} else if (const netparray_t*array = dynamic_cast<const netparray_t*> (member->net_type)) {
// If the member is a parray, then the elements
// are themselves packed object, including
// possibly a struct. Handle this by taking the
// part select of the current part of the
// variable, then stepping to the element type to
// possibly iterate through more of the member_path.
ivl_assert(*this, array->packed());
ivl_assert(*this, !member_comp.index.empty());
// These are the dimensions defined by the type
const vector<netrange_t>&mem_packed_dims = array->static_dimensions();
if (member_comp.index.size() != mem_packed_dims.size()) {
cerr << get_fileline() << ": error: "
<< "Incorrect number of index expressions for member "
<< member_name << "." << endl;
des->errors += 1;
return false;
}
// Evaluate all but the last index expression, into prefix_indices.
list<long>prefix_indices;
bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index);
ivl_assert(*this, rc);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "prefix_indices.size()==" << prefix_indices.size()
<< ", mem_packed_dims.size()==" << mem_packed_dims.size()
<< " (netparray_t context)"
<< endl;
}
// Evaluate the last index expression into a constant long.
NetExpr*texpr = elab_and_eval(des, scope, member_comp.index.back().msb, -1, true);
long tmp;
if (texpr == 0 || !eval_as_long(tmp, texpr)) {
cerr << get_fileline() << ": error: "
<< "Array index expressions for member " << member_name
<< " must be constant here." << endl;
des->errors += 1;
return false;
}
delete texpr;
// Now use the prefix_to_slice function to calculate the
// offset and width of the addressed slice of the member.
long loff;
unsigned long lwid;
prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid);
ivl_type_t element_type = array->element_type();
long element_width = element_type->packed_width();
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "parray subselection loff=" << loff
<< ", lwid=" << lwid
<< ", element_width=" << element_width
<< endl;
}
// The width and offset calculated from the
// indices is actually in elements, and not
// bits. In fact, in this context, the lwid should
// come down to 1 (one element).
off += loff * element_width;
ivl_assert(*this, lwid==1);
use_width = element_width;
// To move on to the next component in the member
// path, get the element type. For example, for
// the path a.b[1].c, we are processing b[1] here,
// and the element type should be a netstruct_t
// that will wind up containing the member c.
struct_type = dynamic_cast<const netstruct_t*> (element_type);
} else if (const netstruct_t*tmp_struct = dynamic_cast<const netstruct_t*> (member->net_type)) {
// If the member is itself a struct, then get
// ready to go on to the next iteration.
struct_type = tmp_struct;
} else if (const netenum_t*tmp_enum = dynamic_cast<const netenum_t*> (member->net_type)) {
// If the element is an enum, then we don't have
// anything special to do.
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "Tail element is an enum: " << *tmp_enum
<< endl;
}
struct_type = 0;
} else {
// Unknown type?
cerr << get_fileline() << ": internal error: "
<< "Unexpected member type? " << *(member->net_type)
<< endl;
des->errors += 1;
return false;
}
// Complete this component of the path, mark it
// completed, and set up for the next component.
completed_path .push_back(member_comp);
member_path.pop_front();
} while (!member_path.empty() && struct_type != 0);
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: "
<< "After processing member_path, "
<< "off=" << off
<< ", use_width=" << use_width
<< ", completed_path=" << completed_path
<< ", member_path=" << member_path
<< endl;
}
// The dimensions in the expression must match the packed
// dimensions that are declared for the variable. For example,
// if foo is a packed array of struct, then this expression
// must be "b[n][m]" with the right number of dimensions to
// match the declaration of "b".
// Note that one of the packed dimensions is the packed struct
// itself.
ivl_assert(*this, name_base.index.size()+1 == reg->packed_dimensions());
// Generate an expression that takes the input array of
// expressions and generates a canonical offset into the
// packed array.
NetExpr*packed_base = 0;
if (reg->packed_dimensions() > 1) {
list<index_component_t>tmp_index = name_base.index;
index_component_t member_select;
member_select.sel = index_component_t::SEL_BIT;
member_select.msb = new PENumber(new verinum(off));
tmp_index.push_back(member_select);
packed_base = collapse_array_indices(des, scope, reg, tmp_index);
}
long tmp;
if (packed_base && eval_as_long(tmp, packed_base)) {
off = tmp;
delete packed_base;
packed_base = 0;
}
if (reg->type()==NetNet::UNRESOLVED_WIRE) {
cerr << get_fileline() << ": error: "
<< path_ << " Unable to member-select unresolved wires."
<< endl;
des->errors += 1;
return false;
}
if (packed_base == 0) {
NetExpr *base = new NetEConst(verinum(off));
if (member_type)
lv->set_part(base, member_type);
else
lv->set_part(base, use_width);
return true;
}
// Oops, packed_base is not fully evaluated, so I don't know
// yet what to do with it.
cerr << get_fileline() << ": internal error: "
<< "I don't know how to handle this index expression? " << *packed_base << endl;
ivl_assert(*this, 0);
return false;
}
NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool, bool) const
{
cerr << get_fileline() << ": error: Constant values not allowed "
<< "in l-value expressions." << endl;
des->errors += 1;
return 0;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化