1 Star 0 Fork 0

theta405/iverilog

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
netmisc.cc 56.68 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827
/*
* Copyright (c) 2001-2021 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 <cstdlib>
# include <climits>
# include "netlist.h"
# include "netparray.h"
# include "netvector.h"
# include "netmisc.h"
# include "PExpr.h"
# include "pform_types.h"
# include "compiler.h"
# include "ivl_assert.h"
using namespace std;
NetNet* sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig)
{
netvector_t*zero_vec = new netvector_t(sig->data_type(),
sig->vector_width()-1, 0);
NetNet*zero_net = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, zero_vec);
zero_net->set_line(*sig);
zero_net->local_flag(true);
if (sig->data_type() == IVL_VT_REAL) {
verireal zero (val);
NetLiteral*zero_obj = new NetLiteral(scope, scope->local_symbol(), zero);
zero_obj->set_line(*sig);
des->add_node(zero_obj);
connect(zero_net->pin(0), zero_obj->pin(0));
} else {
verinum zero ((int64_t)val);
zero = cast_to_width(zero, sig->vector_width());
zero.has_sign(sig->get_signed());
NetConst*zero_obj = new NetConst(scope, scope->local_symbol(), zero);
zero_obj->set_line(*sig);
des->add_node(zero_obj);
connect(zero_net->pin(0), zero_obj->pin(0));
}
NetAddSub*adder = new NetAddSub(scope, scope->local_symbol(), sig->vector_width());
adder->set_line(*sig);
des->add_node(adder);
adder->attribute(perm_string::literal("LPM_Direction"), verinum("SUB"));
connect(zero_net->pin(0), adder->pin_DataA());
connect(adder->pin_DataB(), sig->pin(0));
netvector_t*tmp_vec = new netvector_t(sig->data_type(),
sig->vector_width()-1, 0);
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, tmp_vec);
tmp->set_line(*sig);
tmp->local_flag(true);
connect(adder->pin_Result(), tmp->pin(0));
return tmp;
}
NetNet* cast_to_int2(Design*des, NetScope*scope, NetNet*src, unsigned wid)
{
if (src->data_type() == IVL_VT_BOOL)
return src;
netvector_t*tmp_vec = new netvector_t(IVL_VT_BOOL, wid-1, 0,
src->get_signed());
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec);
tmp->set_line(*src);
tmp->local_flag(true);
NetCastInt2*cast = new NetCastInt2(scope, scope->local_symbol(), wid);
cast->set_line(*src);
des->add_node(cast);
connect(cast->pin(0), tmp->pin(0));
connect(cast->pin(1), src->pin(0));
return tmp;
}
NetNet* cast_to_int4(Design*des, NetScope*scope, NetNet*src, unsigned wid)
{
if (src->data_type() != IVL_VT_REAL)
return src;
netvector_t*tmp_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0);
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec);
tmp->set_line(*src);
tmp->local_flag(true);
NetCastInt4*cast = new NetCastInt4(scope, scope->local_symbol(), wid);
cast->set_line(*src);
des->add_node(cast);
connect(cast->pin(0), tmp->pin(0));
connect(cast->pin(1), src->pin(0));
return tmp;
}
NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src)
{
if (src->data_type() == IVL_VT_REAL)
return src;
netvector_t*tmp_vec = new netvector_t(IVL_VT_REAL);
NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, tmp_vec);
tmp->set_line(*src);
tmp->local_flag(true);
NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed());
cast->set_line(*src);
des->add_node(cast);
connect(cast->pin(0), tmp->pin(0));
connect(cast->pin(1), src->pin(0));
return tmp;
}
NetExpr* cast_to_int2(NetExpr*expr, unsigned width)
{
// Special case: The expression is already BOOL
if (expr->expr_type() == IVL_VT_BOOL)
return expr;
if (debug_elaborate)
cerr << expr->get_fileline() << ": debug: "
<< "Cast expression to int2, width=" << width << "." << endl;
NetECast*cast = new NetECast('2', expr, width, expr->has_sign());
cast->set_line(*expr);
return cast;
}
NetExpr* cast_to_int4(NetExpr*expr, unsigned width)
{
// Special case: The expression is already LOGIC or BOOL
if (expr->expr_type() == IVL_VT_LOGIC || expr->expr_type() == IVL_VT_BOOL)
return expr;
if (debug_elaborate)
cerr << expr->get_fileline() << ": debug: "
<< "Cast expression to int4, width=" << width << "." << endl;
NetECast*cast = new NetECast('v', expr, width, expr->has_sign());
cast->set_line(*expr);
return cast;
}
NetExpr* cast_to_real(NetExpr*expr)
{
if (expr->expr_type() == IVL_VT_REAL)
return expr;
if (debug_elaborate)
cerr << expr->get_fileline() << ": debug: "
<< "Cast expression to real." << endl;
NetECast*cast = new NetECast('r', expr, 1, true);
cast->set_line(*expr);
return cast;
}
/*
* Add a signed constant to an existing expression. Generate a new
* NetEBAdd node that has the input expression and an expression made
* from the constant value.
*/
static NetExpr* make_add_expr(NetExpr*expr, long val)
{
if (val == 0)
return expr;
// If the value to be added is <0, then instead generate a
// SUBTRACT node and turn the value positive.
char add_op = '+';
if (val < 0) {
add_op = '-';
val = -val;
}
verinum val_v (val, expr->expr_width());
val_v.has_sign(expr->has_sign());
NetEConst*val_c = new NetEConst(val_v);
val_c->set_line(*expr);
NetEBAdd*res = new NetEBAdd(add_op, expr, val_c, expr->expr_width(),
expr->has_sign());
res->set_line(*expr);
return res;
}
static NetExpr* make_add_expr(const LineInfo*loc, NetExpr*expr1, NetExpr*expr2)
{
bool use_signed = expr1->has_sign() && expr2->has_sign();
unsigned use_wid = expr1->expr_width();
if (expr2->expr_width() > use_wid)
use_wid = expr2->expr_width();
expr1 = pad_to_width(expr1, use_wid, *loc);
expr2 = pad_to_width(expr2, use_wid, *loc);
NetEBAdd*tmp = new NetEBAdd('+', expr1, expr2, use_wid, use_signed);
return tmp;
}
/*
* Subtract an existing expression from a signed constant.
*/
static NetExpr* make_sub_expr(long val, NetExpr*expr)
{
verinum val_v (val, expr->expr_width());
val_v.has_sign(expr->has_sign());
NetEConst*val_c = new NetEConst(val_v);
val_c->set_line(*expr);
NetEBAdd*res = new NetEBAdd('-', val_c, expr, expr->expr_width(),
expr->has_sign());
res->set_line(*expr);
return res;
}
/*
* Subtract a signed constant from an existing expression.
*/
static NetExpr* make_sub_expr(NetExpr*expr, long val)
{
verinum val_v (val, expr->expr_width());
val_v.has_sign(expr->has_sign());
NetEConst*val_c = new NetEConst(val_v);
val_c->set_line(*expr);
NetEBAdd*res = new NetEBAdd('-', expr, val_c, expr->expr_width(),
expr->has_sign());
res->set_line(*expr);
return res;
}
/*
* Multiply an existing expression by a signed positive number.
* This does a lossless multiply, so the arguments will need to be
* sized to match the output size.
*/
static NetExpr* make_mult_expr(NetExpr*expr, unsigned long val)
{
const unsigned val_wid = ceil(log2((double)val)) ;
unsigned use_wid = expr->expr_width() + val_wid;
verinum val_v (val, use_wid);
val_v.has_sign(expr->has_sign());
NetEConst*val_c = new NetEConst(val_v);
val_c->set_line(*expr);
// We know by definitions that the expr argument needs to be
// padded to be the right argument width for this lossless multiply.
expr = pad_to_width(expr, use_wid, *expr);
NetEBMult*res = new NetEBMult('*', expr, val_c, use_wid, expr->has_sign());
res->set_line(*expr);
return res;
}
/*
* This routine is used to calculate the number of bits needed to
* contain the given number.
*/
static unsigned num_bits(long arg)
{
unsigned res = 0;
/* For a negative value we have room for one extra value, but
* we have a signed result so we need an extra bit for this. */
if (arg < 0) {
arg = -arg - 1;
res += 1;
}
/* Calculate the number of bits needed here. */
while (arg) {
res += 1;
arg >>= 1;
}
return res;
}
/*
* This routine generates the normalization expression needed for a variable
* bit select or a variable base expression for an indexed part
* select. This function doesn't actually look at the variable
* dimensions, it just does the final calculation using msb/lsb of the
* last slice, and the off of the slice in the variable.
*/
NetExpr *normalize_variable_base(NetExpr *base, long msb, long lsb,
unsigned long wid, bool is_up, long soff)
{
long offset = lsb;
if (msb < lsb) {
/* Correct the offset if needed. */
if (is_up) offset -= wid - 1;
/* Calculate the space needed for the offset. */
unsigned min_wid = num_bits(offset);
if (num_bits(soff) > min_wid)
min_wid = num_bits(soff);
/* We need enough space for the larger of the offset or the
* base expression. */
if (min_wid < base->expr_width()) min_wid = base->expr_width();
/* Now that we have the minimum needed width increase it by
* one to make room for the normalization calculation. */
min_wid += 2;
/* Pad the base expression to the correct width. */
base = pad_to_width(base, min_wid, *base);
/* If the base expression is unsigned and either the lsb
* is negative or it does not fill the width of the base
* expression then we could generate negative normalized
* values so cast the expression to signed to get the
* math correct. */
if ((lsb < 0 || num_bits(lsb+1) <= base->expr_width()) &&
! base->has_sign()) {
/* We need this extra select to hide the signed
* property from the padding above. It will be
* removed automatically during code generation. */
NetESelect *tmp = new NetESelect(base, 0 , min_wid);
tmp->set_line(*base);
tmp->cast_signed(true);
base = tmp;
}
/* Normalize the expression. */
base = make_sub_expr(offset+soff, base);
} else {
/* Correct the offset if needed. */
if (!is_up) offset += wid - 1;
/* If the offset is zero then just return the base (index)
* expression. */
if ((soff-offset) == 0) return base;
/* Calculate the space needed for the offset. */
unsigned min_wid = num_bits(-offset);
if (num_bits(soff) > min_wid)
min_wid = num_bits(soff);
/* We need enough space for the larger of the offset or the
* base expression. */
if (min_wid < base->expr_width()) min_wid = base->expr_width();
/* Now that we have the minimum needed width increase it by
* one to make room for the normalization calculation. */
min_wid += 2;
/* Pad the base expression to the correct width. */
base = pad_to_width(base, min_wid, *base);
/* If the offset is greater than zero then we need to do
* signed math to get the location value correct. */
if (offset > 0 && ! base->has_sign()) {
/* We need this extra select to hide the signed
* property from the padding above. It will be
* removed automatically during code generation. */
NetESelect *tmp = new NetESelect(base, 0 , min_wid);
tmp->set_line(*base);
tmp->cast_signed(true);
base = tmp;
}
/* Normalize the expression. */
base = make_add_expr(base, soff-offset);
}
return base;
}
/*
* This method is how indices should work except that the base should
* be a vector of expressions that matches the size of the dims list,
* so that we can generate an expression based on the entire packed
* vector. For now, we assert that there is only one set of dimensions.
*/
NetExpr *normalize_variable_base(NetExpr *base,
const list<netrange_t>&dims,
unsigned long wid, bool is_up)
{
ivl_assert(*base, dims.size() == 1);
const netrange_t&rng = dims.back();
return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), wid, is_up);
}
NetExpr *normalize_variable_bit_base(const list<long>&indices, NetExpr*base,
const NetNet*reg)
{
const vector<netrange_t>&packed_dims = reg->packed_dims();
ivl_assert(*base, indices.size()+1 == packed_dims.size());
// Get the canonical offset of the slice within which we are
// addressing. We need that address as a slice offset to
// calculate the proper complete address
const netrange_t&rng = packed_dims.back();
long slice_off = reg->sb_to_idx(indices, rng.get_lsb());
return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), 1, true, slice_off);
}
NetExpr *normalize_variable_part_base(const list<long>&indices, NetExpr*base,
const NetNet*reg,
unsigned long wid, bool is_up)
{
const vector<netrange_t>&packed_dims = reg->packed_dims();
ivl_assert(*base, indices.size()+1 == packed_dims.size());
// Get the canonical offset of the slice within which we are
// addressing. We need that address as a slice offset to
// calculate the proper complete address
const netrange_t&rng = packed_dims.back();
long slice_off = reg->sb_to_idx(indices, rng.get_lsb());
return normalize_variable_base(base, rng.get_msb(), rng.get_lsb(), wid, is_up, slice_off);
}
NetExpr *normalize_variable_slice_base(const list<long>&indices, NetExpr*base,
const NetNet*reg, unsigned long&lwid)
{
const vector<netrange_t>&packed_dims = reg->packed_dims();
ivl_assert(*base, indices.size() < packed_dims.size());
vector<netrange_t>::const_iterator pcur = packed_dims.end();
for (size_t idx = indices.size() ; idx < packed_dims.size(); idx += 1) {
-- pcur;
}
long sb = min(pcur->get_lsb(), pcur->get_msb());
long loff;
reg->sb_to_slice(indices, sb, loff, lwid);
unsigned min_wid = base->expr_width();
if ((sb < 0) && !base->has_sign()) min_wid += 1;
if (min_wid < num_bits(pcur->get_lsb())) min_wid = pcur->get_lsb();
if (min_wid < num_bits(pcur->get_msb())) min_wid = pcur->get_msb();
base = pad_to_width(base, min_wid, *base);
if ((sb < 0) && !base->has_sign()) {
NetESelect *tmp = new NetESelect(base, 0 , min_wid);
tmp->set_line(*base);
tmp->cast_signed(true);
base = tmp;
}
if (pcur->get_msb() >= pcur->get_lsb()) {
if (pcur->get_lsb() != 0)
base = make_sub_expr(base, pcur->get_lsb());
base = make_mult_expr(base, lwid);
min_wid = base->expr_width();
if (min_wid < num_bits(loff)) min_wid = num_bits(loff);
if (loff != 0) min_wid += 1;
base = pad_to_width(base, min_wid, *base);
base = make_add_expr(base, loff);
} else {
if (pcur->get_msb() != 0)
base = make_sub_expr(base, pcur->get_msb());
base = make_mult_expr(base, lwid);
min_wid = base->expr_width();
if (min_wid < num_bits(loff)) min_wid = num_bits(loff);
if (loff != 0) min_wid += 1;
base = pad_to_width(base, min_wid, *base);
base = make_sub_expr(loff, base);
}
return base;
}
ostream& operator << (ostream&o, __IndicesManip<long> val)
{
for (list<long>::const_iterator cur = val.val.begin()
; cur != val.val.end() ; ++cur) {
o << "[" << *cur << "]";
}
return o;
}
ostream& operator << (ostream&o, __IndicesManip<NetExpr*> val)
{
for (list<NetExpr*>::const_iterator cur = val.val.begin()
; cur != val.val.end() ; ++cur) {
o << "[" << *(*cur) << "]";
}
return o;
}
/*
* The src is the input index expression list from the expression, and
* the count is the number that are to be elaborated into the indices
* list. At the same time, create a indices_const list that contains
* the evaluated values for the expression, if they can be evaluated.
*/
void indices_to_expressions(Design*des, NetScope*scope,
// loc is for error messages.
const LineInfo*loc,
// src is the index list, and count is
// the number of items in the list to use.
const list<index_component_t>&src, unsigned count,
// True if the expression MUST be constant.
bool need_const,
// These are the outputs.
indices_flags&flags,
list<NetExpr*>&indices, list<long>&indices_const)
{
ivl_assert(*loc, count <= src.size());
flags.invalid = false;
flags.variable = false;
flags.undefined = false;
for (list<index_component_t>::const_iterator cur = src.begin()
; count > 0 ; ++cur, --count) {
ivl_assert(*loc, cur->sel != index_component_t::SEL_NONE);
if (cur->sel != index_component_t::SEL_BIT) {
cerr << loc->get_fileline() << ": error: "
<< "Array cannot be indexed by a range." << endl;
des->errors += 1;
}
ivl_assert(*loc, cur->msb);
NetExpr*word_index = elab_and_eval(des, scope, cur->msb, -1, need_const);
if (word_index == 0)
flags.invalid = true;
// Track if we detect any non-constant expressions
// here. This may allow for a special case.
NetEConst*word_const = dynamic_cast<NetEConst*> (word_index);
if (word_const == 0)
flags.variable = true;
else if (!word_const->value().is_defined())
flags.undefined = true;
else if (!flags.variable && !flags.undefined)
indices_const.push_back(word_const->value().as_long());
indices.push_back(word_index);
}
}
static void make_strides(const vector<netrange_t>&dims,
vector<long>&stride)
{
stride[dims.size()-1] = 1;
for (size_t idx = stride.size()-1 ; idx > 0 ; --idx) {
long tmp = dims[idx].width();
if (idx < stride.size())
tmp *= stride[idx];
stride[idx-1] = tmp;
}
}
/*
* Take in a vector of constant indices and convert them to a single
* number that is the canonical address (zero based, 1-d) of the
* word. If any of the indices are out of bounds, return nil instead
* of an expression.
*/
static NetExpr* normalize_variable_unpacked(const vector<netrange_t>&dims, list<long>&indices)
{
// Make strides for each index. The stride is the distance (in
// words) to the next element in the canonical array.
vector<long> stride (dims.size());
make_strides(dims, stride);
int64_t canonical_addr = 0;
int idx = 0;
for (list<long>::const_iterator cur = indices.begin()
; cur != indices.end() ; ++cur, ++idx) {
long tmp = *cur;
if (dims[idx].get_lsb() <= dims[idx].get_msb())
tmp -= dims[idx].get_lsb();
else
tmp -= dims[idx].get_msb();
// Notice of this index is out of range.
if (tmp < 0 || tmp >= (long)dims[idx].width()) {
return 0;
}
canonical_addr += tmp * stride[idx];
}
NetEConst*canonical_expr = new NetEConst(verinum(canonical_addr));
return canonical_expr;
}
NetExpr* normalize_variable_unpacked(const NetNet*net, list<long>&indices)
{
const vector<netrange_t>&dims = net->unpacked_dims();
return normalize_variable_unpacked(dims, indices);
}
NetExpr* normalize_variable_unpacked(const netsarray_t*stype, list<long>&indices)
{
const vector<netrange_t>&dims = stype->static_dimensions();
return normalize_variable_unpacked(dims, indices);
}
NetExpr* normalize_variable_unpacked(const LineInfo&loc, const vector<netrange_t>&dims, list<NetExpr*>&indices)
{
// Make strides for each index. The stride is the distance (in
// words) to the next element in the canonical array.
vector<long> stride (dims.size());
make_strides(dims, stride);
NetExpr*canonical_expr = 0;
int idx = 0;
for (list<NetExpr*>::const_iterator cur = indices.begin()
; cur != indices.end() ; ++cur, ++idx) {
NetExpr*tmp = *cur;
// If the expression elaboration generated errors, then
// give up. Presumably, the error during expression
// elaboration already generated the error message.
if (tmp == 0)
return 0;
int64_t use_base;
if (! dims[idx].defined())
use_base = 0;
else if (dims[idx].get_lsb() <= dims[idx].get_msb())
use_base = dims[idx].get_lsb();
else
use_base = dims[idx].get_msb();
int64_t use_stride = stride[idx];
// Account for that we are doing arithmetic and should
// have a proper width to make sure there are no
// losses. So calculate a min_wid width.
unsigned tmp_wid;
unsigned min_wid = tmp->expr_width();
if (use_base != 0 && ((tmp_wid = num_bits(use_base)) >= min_wid))
min_wid = tmp_wid + 1;
if ((tmp_wid = num_bits(dims[idx].width()+1)) >= min_wid)
min_wid = tmp_wid + 1;
if (use_stride != 1)
min_wid += num_bits(use_stride);
tmp = pad_to_width(tmp, min_wid, loc);
// Now generate the math to calculate the canonical address.
NetExpr*tmp_scaled = 0;
if (NetEConst*tmp_const = dynamic_cast<NetEConst*> (tmp)) {
// Special case: the index is constant, so this
// iteration can be replaced with a constant
// expression.
int64_t val = tmp_const->value().as_long();
val -= use_base;
val *= use_stride;
// Very special case: the index is zero, so we can
// skip this iteration
if (val == 0)
continue;
tmp_scaled = new NetEConst(verinum(val));
} else {
tmp_scaled = tmp;
if (use_base != 0)
tmp_scaled = make_add_expr(tmp_scaled, -use_base);
if (use_stride != 1)
tmp_scaled = make_mult_expr(tmp_scaled, use_stride);
}
if (canonical_expr == 0) {
canonical_expr = tmp_scaled;
} else {
bool expr_has_sign = canonical_expr->has_sign() &&
tmp_scaled->has_sign();
canonical_expr = new NetEBAdd('+', canonical_expr, tmp_scaled,
canonical_expr->expr_width()+1,
expr_has_sign);
}
}
// If we don't have an expression at this point, all the indices were
// constant zero. But this variant of normalize_variable_unpacked()
// is only used when at least one index is not a constant.
ivl_assert(loc, canonical_expr);
return canonical_expr;
}
NetExpr* normalize_variable_unpacked(const NetNet*net, list<NetExpr*>&indices)
{
const vector<netrange_t>&dims = net->unpacked_dims();
return normalize_variable_unpacked(*net, dims, indices);
}
NetExpr* normalize_variable_unpacked(const LineInfo&loc, const netsarray_t*stype, list<NetExpr*>&indices)
{
const vector<netrange_t>&dims = stype->static_dimensions();
return normalize_variable_unpacked(loc, dims, indices);
}
NetExpr* make_canonical_index(Design*des, NetScope*scope,
const LineInfo*loc,
const std::list<index_component_t>&src,
const netsarray_t*stype,
bool need_const)
{
NetExpr*canon_index = 0;
list<long> indices_const;
list<NetExpr*> indices_expr;
indices_flags flags;
indices_to_expressions(des, scope, loc,
src, src.size(),
need_const,
flags,
indices_expr, indices_const);
if (flags.undefined) {
cerr << loc->get_fileline() << ": warning: "
<< "ignoring undefined value array access." << endl;
} else if (flags.variable) {
canon_index = normalize_variable_unpacked(*loc, stype, indices_expr);
} else {
canon_index = normalize_variable_unpacked(stype, indices_const);
}
return canon_index;
}
NetEConst* make_const_x(unsigned long wid)
{
verinum xxx (verinum::Vx, wid);
NetEConst*resx = new NetEConst(xxx);
return resx;
}
NetEConst* make_const_0(unsigned long wid)
{
verinum xxx (verinum::V0, wid);
NetEConst*resx = new NetEConst(xxx);
return resx;
}
NetEConst* make_const_val(unsigned long value)
{
verinum tmp (value, integer_width);
NetEConst*res = new NetEConst(tmp);
return res;
}
NetEConst* make_const_val_s(long value)
{
verinum tmp (value, integer_width);
tmp.has_sign(true);
NetEConst*res = new NetEConst(tmp);
return res;
}
NetNet* make_const_x(Design*des, NetScope*scope, unsigned long wid)
{
verinum xxx (verinum::Vx, wid);
NetConst*res = new NetConst(scope, scope->local_symbol(), xxx);
des->add_node(res);
netvector_t*sig_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0);
NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, sig_vec);
sig->local_flag(true);
connect(sig->pin(0), res->pin(0));
return sig;
}
NetNet* make_const_z(Design*des, NetScope*scope, unsigned long wid)
{
verinum xxx (verinum::Vz, wid);
NetConst*res = new NetConst(scope, scope->local_symbol(), xxx);
des->add_node(res);
netvector_t*sig_vec = new netvector_t(IVL_VT_LOGIC, wid-1, 0);
NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, sig_vec);
sig->local_flag(true);
connect(sig->pin(0), res->pin(0));
return sig;
}
NetExpr* condition_reduce(NetExpr*expr)
{
if (expr->expr_type() == IVL_VT_REAL) {
if (NetECReal *tmp = dynamic_cast<NetECReal*>(expr)) {
verinum::V res;
if (tmp->value().as_double() == 0.0) res = verinum::V0;
else res = verinum::V1;
verinum vres (res, 1, true);
NetExpr *rtn = new NetEConst(vres);
rtn->set_line(*expr);
delete expr;
return rtn;
}
NetExpr *rtn = new NetEBComp('n', expr,
new NetECReal(verireal(0.0)));
rtn->set_line(*expr);
return rtn;
}
if (expr->expr_width() == 1)
return expr;
verinum zero (verinum::V0, expr->expr_width());
zero.has_sign(expr->has_sign());
NetEConst*ezero = new NetEConst(zero);
ezero->set_line(*expr);
NetEBComp*cmp = new NetEBComp('n', expr, ezero);
cmp->set_line(*expr);
cmp->cast_signed(false);
return cmp;
}
NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
int context_width, bool need_const, bool annotatable,
ivl_variable_type_t cast_type, bool force_unsigned)
{
PExpr::width_mode_t mode = PExpr::SIZED;
if ((context_width == -2) && !gn_strict_expr_width_flag)
mode = PExpr::EXPAND;
pe->test_width(des, scope, mode);
if (pe->expr_type() == IVL_VT_CLASS) {
cerr << pe->get_fileline() << ": Error: "
<< "Class/null r-value not allowed in this context." << endl;
des->errors += 1;
return 0;
}
// Get the final expression width. If the expression is unsized,
// this may be different from the value returned by test_width().
unsigned expr_width = pe->expr_width();
// If context_width is positive, this is the RHS of an assignment,
// so the LHS width must also be included in the width calculation.
unsigned pos_context_width = context_width > 0 ? context_width : 0;
if ((pe->expr_type() != IVL_VT_REAL) && (expr_width < pos_context_width))
expr_width = pos_context_width;
// If this is the RHS of a compressed assignment, the LHS also
// affects the expression type (signed/unsigned).
if (force_unsigned)
pe->cast_signed(false);
if (debug_elaborate) {
cerr << pe->get_fileline() << ": elab_and_eval: test_width of "
<< *pe << endl;
cerr << pe->get_fileline() << ": : "
<< "returns type=" << pe->expr_type()
<< ", context_width=" << context_width
<< ", signed=" << pe->has_sign()
<< ", expr_width=" << expr_width
<< ", mode=" << PExpr::width_mode_name(mode) << endl;
cerr << pe->get_fileline() << ": : "
<< "cast_type=" << cast_type << endl;
}
// If we can get the same result using a smaller expression
// width, do so.
unsigned min_width = pe->min_width();
if ((min_width != UINT_MAX) && (pe->expr_type() != IVL_VT_REAL)
&& (pos_context_width > 0) && (expr_width > pos_context_width)) {
expr_width = max(min_width, pos_context_width);
if (debug_elaborate) {
cerr << pe->get_fileline() << ": : "
<< "pruned to width=" << expr_width << endl;
}
}
if ((mode >= PExpr::LOSSLESS) && (expr_width > width_cap)
&& (expr_width > pos_context_width)) {
cerr << pe->get_fileline() << ": warning: excessive unsized "
<< "expression width detected." << endl;
cerr << pe->get_fileline() << ": : The expression width "
<< "is capped at " << width_cap << " bits." << endl;
expr_width = width_cap;
}
unsigned flags = PExpr::NO_FLAGS;
if (need_const)
flags |= PExpr::NEED_CONST;
if (annotatable)
flags |= PExpr::ANNOTATABLE;
if (debug_elaborate) {
cerr << pe->get_fileline() << ": elab_and_eval: "
<< "Calculated width is " << expr_width << "." << endl;
}
NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, flags);
if (tmp == 0) return 0;
if ((cast_type != IVL_VT_NO_TYPE) && (cast_type != tmp->expr_type())) {
switch (tmp->expr_type()) {
case IVL_VT_BOOL:
case IVL_VT_LOGIC:
case IVL_VT_REAL:
break;
default:
cerr << tmp->get_fileline() << ": error: "
"The expression '" << *pe << "' cannot be implicitly "
"cast to the target type." << endl;
des->errors += 1;
delete tmp;
return 0;
}
switch (cast_type) {
case IVL_VT_REAL:
tmp = cast_to_real(tmp);
break;
case IVL_VT_BOOL:
tmp = cast_to_int2(tmp, pos_context_width);
break;
case IVL_VT_LOGIC:
tmp = cast_to_int4(tmp, pos_context_width);
break;
default:
break;
}
}
eval_expr(tmp, context_width);
if (NetEConst*ce = dynamic_cast<NetEConst*>(tmp)) {
if ((mode >= PExpr::LOSSLESS) && (context_width < 0))
ce->trim();
}
return tmp;
}
NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
ivl_type_t lv_net_type, bool need_const)
{
if (debug_elaborate) {
cerr << pe->get_fileline() << ": " << __func__ << ": "
<< "pe=" << *pe
<< ", lv_net_type=" << *lv_net_type << endl;
}
// Elaborate the expression using the more general
// elaborate_expr method.
unsigned flags = PExpr::NO_FLAGS;
if (need_const)
flags |= PExpr::NEED_CONST;
NetExpr*tmp = pe->elaborate_expr(des, scope, lv_net_type, flags);
if (tmp == 0) return 0;
ivl_variable_type_t cast_type = ivl_type_base(lv_net_type);
if ((cast_type != IVL_VT_NO_TYPE) && (cast_type != tmp->expr_type())) {
// Catch some special cases.
switch (cast_type) {
case IVL_VT_DARRAY:
case IVL_VT_QUEUE:
if (NetESignal*net = dynamic_cast<NetESignal*>(tmp)) {
ivl_variable_type_t type = net->expr_type();
if ((type == IVL_VT_DARRAY) || (type == IVL_VT_QUEUE))
return tmp;
}
if (dynamic_cast<PEAssignPattern*>(pe))
return tmp;
// fall through
case IVL_VT_STRING:
if (dynamic_cast<PEConcat*>(pe))
return tmp;
break;
case IVL_VT_CLASS:
if (dynamic_cast<PENull*>(pe))
return tmp;
break;
default:
break;
}
cerr << tmp->get_fileline() << ": error: "
"The expression '" << *pe << "' cannot be implicitly "
"cast to the target type." << endl;
des->errors += 1;
delete tmp;
return 0;
}
return tmp;
}
NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name,
unsigned arg_idx, PExpr*pe, bool need_const)
{
PExpr::width_mode_t mode = PExpr::SIZED;
pe->test_width(des, scope, mode);
if (debug_elaborate) {
cerr << pe->get_fileline() << ": " << __func__ << ": "
<< "test_width of " << name
<< " argument " << (arg_idx+1) << " " << *pe << endl;
cerr << pe->get_fileline() << ": "
<< "returns type=" << pe->expr_type()
<< ", width=" << pe->expr_width()
<< ", signed=" << pe->has_sign()
<< ", mode=" << PExpr::width_mode_name(mode) << endl;
}
unsigned flags = PExpr::SYS_TASK_ARG;
if (need_const)
flags |= PExpr::NEED_CONST;
NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), flags);
if (tmp == 0) return 0;
eval_expr(tmp, -1);
if (NetEConst*ce = dynamic_cast<NetEConst*>(tmp)) {
// For lossless/unsized constant expressions, we can now
// determine the exact width required to hold the result.
// But leave literal numbers exactly as the user supplied
// them.
if ((mode >= PExpr::LOSSLESS) && !dynamic_cast<PENumber*>(pe) && tmp->expr_width()>32)
ce->trim();
}
return tmp;
}
bool evaluate_range(Design*des, NetScope*scope, const LineInfo*li,
const pform_range_t&range, long&index_l, long&index_r)
{
bool dimension_ok = true;
// Unsized and queue dimensions should be handled before calling
// this function. If we find them here, we are in a context where
// they are not allowed.
if (range.first == 0) {
cerr << li->get_fileline() << ": error: "
"An unsized dimension is not allowed here." << endl;
dimension_ok = false;
des->errors += 1;
} else if (dynamic_cast<PENull*>(range.first)) {
cerr << li->get_fileline() << ": error: "
"A queue dimension is not allowed here." << endl;
dimension_ok = false;
des->errors += 1;
} else {
NetExpr*texpr = elab_and_eval(des, scope, range.first, -1, true);
if (! eval_as_long(index_l, texpr)) {
cerr << range.first->get_fileline() << ": error: "
"Dimensions must be constant." << endl;
cerr << range.first->get_fileline() << " : "
<< (range.second ? "This MSB" : "This size")
<< " expression violates the rule: "
<< *range.first << endl;
dimension_ok = false;
des->errors += 1;
}
delete texpr;
if (range.second == 0) {
// This is a SystemVerilog [size] dimension. The IEEE
// standard does not allow this in a packed dimension,
// but we do. At least one commercial simulator does too.
if (!dimension_ok) {
// bail out
} else if (index_l > 0) {
index_l = index_l - 1;
index_r = 0;
} else {
cerr << range.first->get_fileline() << ": error: "
"Dimension size must be greater than zero." << endl;
cerr << range.first->get_fileline() << " : "
"This size expression violates the rule: "
<< *range.first << endl;
dimension_ok = false;
des->errors += 1;
}
} else {
texpr = elab_and_eval(des, scope, range.second, -1, true);
if (! eval_as_long(index_r, texpr)) {
cerr << range.second->get_fileline() << ": error: "
"Dimensions must be constant." << endl;
cerr << range.second->get_fileline() << " : "
"This LSB expression violates the rule: "
<< *range.second << endl;
dimension_ok = false;
des->errors += 1;
}
delete texpr;
}
}
/* Error recovery */
if (!dimension_ok) {
index_l = 0;
index_r = 0;
}
return dimension_ok;
}
bool evaluate_ranges(Design*des, NetScope*scope, const LineInfo*li,
vector<netrange_t>&llist,
const list<pform_range_t>&rlist)
{
bool dimensions_ok = true;
for (list<pform_range_t>::const_iterator cur = rlist.begin()
; cur != rlist.end() ; ++cur) {
long index_l, index_r;
dimensions_ok &= evaluate_range(des, scope, li, *cur, index_l, index_r);
llist.push_back(netrange_t(index_l, index_r));
}
return dimensions_ok;
}
void eval_expr(NetExpr*&expr, int context_width)
{
assert(expr);
if (dynamic_cast<NetECReal*>(expr)) return;
NetExpr*tmp = expr->eval_tree();
if (tmp != 0) {
tmp->set_line(*expr);
delete expr;
expr = tmp;
}
if (context_width <= 0) return;
NetEConst *ce = dynamic_cast<NetEConst*>(expr);
if (ce == 0) return;
// The expression is a constant, so resize it if needed.
if (ce->expr_width() < (unsigned)context_width) {
expr = pad_to_width(expr, context_width, *expr);
} else if (ce->expr_width() > (unsigned)context_width) {
verinum value(ce->value(), context_width);
ce = new NetEConst(value);
ce->set_line(*expr);
delete expr;
expr = ce;
}
}
bool eval_as_long(long&value, const NetExpr*expr)
{
if (const NetEConst*tmp = dynamic_cast<const NetEConst*>(expr) ) {
value = tmp->value().as_long();
return true;
}
if (const NetECReal*rtmp = dynamic_cast<const NetECReal*>(expr)) {
value = rtmp->value().as_long();
return true;
}
return false;
}
bool eval_as_double(double&value, NetExpr*expr)
{
if (NetEConst*tmp = dynamic_cast<NetEConst*>(expr) ) {
value = tmp->value().as_double();
return true;
}
if (NetECReal*rtmp = dynamic_cast<NetECReal*>(expr)) {
value = rtmp->value().as_double();
return true;
}
return false;
}
/*
* At the parser level, a name component is a name with a collection
* of expressions. For example foo[N] is the name "foo" and the index
* expression "N". This function takes as input the name component and
* returns the path component name. It will evaluate the index
* expression if it is present.
*/
hname_t eval_path_component(Design*des, NetScope*scope,
const name_component_t&comp,
bool&error_flag)
{
// No index expression, so the path component is an undecorated
// name, for example "foo".
if (comp.index.empty())
return hname_t(comp.name);
vector<int> index_values;
for (list<index_component_t>::const_iterator cur = comp.index.begin()
; cur != comp.index.end() ; ++cur) {
const index_component_t&index = *cur;
if (index.sel != index_component_t::SEL_BIT) {
cerr << index.msb->get_fileline() << ": error: "
<< "Part select is not valid for this kind of object." << endl;
des->errors += 1;
return hname_t(comp.name, 0);
}
// The parser will assure that path components will have only
// bit select index expressions. For example, "foo[n]" is OK,
// but "foo[n:m]" is not.
assert(index.sel == index_component_t::SEL_BIT);
// Evaluate the bit select to get a number.
NetExpr*tmp = elab_and_eval(des, scope, index.msb, -1);
ivl_assert(*index.msb, tmp);
if (NetEConst*ctmp = dynamic_cast<NetEConst*>(tmp)) {
index_values.push_back(ctmp->value().as_long());
delete ctmp;
continue;
}
#if 1
// Darn, the expression doesn't evaluate to a constant. That's
// an error to be reported. And make up a fake index value to
// return to the caller.
cerr << index.msb->get_fileline() << ": error: "
<< "Scope index expression is not constant: "
<< *index.msb << endl;
des->errors += 1;
#endif
error_flag = true;
delete tmp;
}
return hname_t(comp.name, index_values);
}
std::list<hname_t> eval_scope_path(Design*des, NetScope*scope,
const pform_name_t&path)
{
bool path_error_flag = false;
list<hname_t> res;
typedef pform_name_t::const_iterator pform_path_it;
for (pform_path_it cur = path.begin() ; cur != path.end(); ++ cur ) {
const name_component_t&comp = *cur;
res.push_back( eval_path_component(des,scope,comp,path_error_flag) );
}
#if 0
if (path_error_flag) {
cerr << "XXXXX: Errors evaluating path " << path << endl;
}
#endif
return res;
}
/*
* Human readable version of op. Used in elaboration error messages.
*/
const char *human_readable_op(const char op, bool unary)
{
const char *type;
switch (op) {
case '~': type = "~"; break; // Negation
case '+': type = "+"; break;
case '-': type = "-"; break;
case '*': type = "*"; break;
case '/': type = "/"; break;
case '%': type = "%"; break;
case '<': type = "<"; break;
case '>': type = ">"; break;
case 'L': type = "<="; break;
case 'G': type = ">="; break;
case '^': type = "^"; break; // XOR
case 'X': type = "~^"; break; // XNOR
case '&': type = "&"; break; // Bitwise AND
case 'A': type = "~&"; break; // NAND (~&)
case '|': type = "|"; break; // Bitwise OR
case 'O': type = "~|"; break; // NOR
case '!': type = "!"; break; // Logical NOT
case 'a': type = "&&"; break; // Logical AND
case 'o': type = "||"; break; // Logical OR
case 'q': type = "->"; break; // Logical implication
case 'Q': type = "<->"; break; // Logical equivalence
case 'e': type = "=="; break;
case 'n': type = "!="; break;
case 'E': type = "==="; break; // Case equality
case 'N':
if (unary) type = "~|"; // NOR
else type = "!=="; // Case inequality
break;
case 'w': type = "==?"; break; // Wild equality
case 'W': type = "!=?"; break; // Wild inequality
case 'l': type = "<<(<)"; break; // Left shifts
case 'r': type = ">>"; break; // Logical right shift
case 'R': type = ">>>"; break; // Arithmetic right shift
case 'p': type = "**"; break; // Power
case 'i':
case 'I': type = "++"; break; /* increment */
case 'd':
case 'D': type = "--"; break; /* decrement */
default:
type = "???";
assert(0);
}
return type;
}
const_bool const_logical(const NetExpr*expr)
{
switch (expr->expr_type()) {
case IVL_VT_REAL: {
const NetECReal*val = dynamic_cast<const NetECReal*> (expr);
if (val == 0) return C_NON;
if (val->value().as_double() == 0.0) return C_0;
else return C_1;
}
case IVL_VT_BOOL:
case IVL_VT_LOGIC: {
const NetEConst*val = dynamic_cast<const NetEConst*> (expr);
if (val == 0) return C_NON;
verinum cval = val->value();
const_bool res = C_0;
for (unsigned idx = 0; idx < cval.len(); idx += 1) {
switch (cval.get(idx)) {
case verinum::V1:
return C_1;
break;
case verinum::V0:
break;
default:
if (res == C_0) res = C_X;
break;
}
}
return res;
}
default:
break;
}
return C_NON;
}
uint64_t get_scaled_time_from_real(Design*des, NetScope*scope, NetECReal*val)
{
verireal fn = val->value();
int shift = scope->time_unit() - scope->time_precision();
assert(shift >= 0);
int64_t delay = fn.as_long64(shift);
shift = scope->time_precision() - des->get_precision();
assert(shift >= 0);
for (int lp = 0; lp < shift; lp += 1) delay *= 10;
return delay;
}
/*
* This function looks at the NetNet signal to see if there are any
* NetPartSelect::PV nodes driving this signal. If so, See if they can
* be collapsed into a single concatenation.
*/
void collapse_partselect_pv_to_concat(Design*des, NetNet*sig)
{
NetScope*scope = sig->scope();
vector<NetPartSelect*> ps_map (sig->vector_width());
Nexus*nex = sig->pin(0).nexus();
for (Link*cur = nex->first_nlink(); cur ; cur = cur->next_nlink()) {
NetPins*obj;
unsigned obj_pin;
cur->cur_link(obj, obj_pin);
// Look for NetPartSelect devices, where this signal is
// connected to pin 1 of a NetPartSelect::PV.
NetPartSelect*ps_obj = dynamic_cast<NetPartSelect*> (obj);
if (ps_obj == 0)
continue;
if (ps_obj->dir() != NetPartSelect::PV)
continue;
if (obj_pin != 1)
continue;
// Don't support overrun selects here.
if (ps_obj->base()+ps_obj->width() > ps_map.size())
continue;
ivl_assert(*ps_obj, ps_obj->base() < ps_map.size());
ps_map[ps_obj->base()] = ps_obj;
}
// Check the collected NetPartSelect::PV objects to see if
// they cover the vector.
unsigned idx = 0;
unsigned device_count = 0;
while (idx < ps_map.size()) {
NetPartSelect*ps_obj = ps_map[idx];
if (ps_obj == 0)
return;
idx += ps_obj->width();
device_count += 1;
}
ivl_assert(*sig, idx == ps_map.size());
/* The vlog95 and possibly other code generators do not want
* to have a group of part selects turned into a transparent
* concatenation. */
if (disable_concatz_generation) {
// HERE: If the part selects have matching strengths then we can use
// a normal concat with a buf-Z after if the strengths are not
// both strong. We would ideally delete any buf-Z driving the
// concat, but that is not required for the vlog95 generator.
return;
}
// Ah HAH! The NetPartSelect::PV objects exactly cover the
// target signal. We can replace all of them with a single
// concatenation.
if (debug_elaborate) {
cerr << sig->get_fileline() << ": debug: "
<< "Collapse " << device_count
<< " NetPartSelect::PV devices into a concatenation." << endl;
}
NetConcat*cat = new NetConcat(scope, scope->local_symbol(),
ps_map.size(), device_count,
true);
des->add_node(cat);
cat->set_line(*sig);
connect(cat->pin(0), sig->pin(0));
idx = 0;
unsigned concat_position = 1;
while (idx < ps_map.size()) {
assert(ps_map[idx]);
NetPartSelect*ps_obj = ps_map[idx];
connect(cat->pin(concat_position), ps_obj->pin(0));
concat_position += 1;
idx += ps_obj->width();
delete ps_obj;
}
}
/*
* Evaluate the prefix indices. All but the final index in a
* chain of indices must be a single value and must evaluate
* to constants at compile time. For example:
* [x] - OK
* [1][2][x] - OK
* [1][x:y] - OK
* [2:0][x] - BAD
* [y][x] - BAD
* Leave the last index for special handling.
*/
bool evaluate_index_prefix(Design*des, NetScope*scope,
list<long>&prefix_indices,
const list<index_component_t>&indices)
{
list<index_component_t>::const_iterator icur = indices.begin();
for (size_t idx = 0 ; (idx+1) < indices.size() ; idx += 1, ++icur) {
assert(icur != indices.end());
if (icur->sel != index_component_t::SEL_BIT) {
cerr << icur->msb->get_fileline() << ": error: "
"All but the final index in a chain of indices must be "
"a single value, not a range." << endl;
des->errors += 1;
return false;
}
NetExpr*texpr = elab_and_eval(des, scope, icur->msb, -1, true);
long tmp;
if (texpr == 0 || !eval_as_long(tmp, texpr)) {
cerr << icur->msb->get_fileline() << ": error: "
"Array index expressions must be constant here." << endl;
des->errors += 1;
return false;
}
prefix_indices.push_back(tmp);
delete texpr;
}
return true;
}
/*
* Evaluate the indices. The chain of indices are applied to the
* packed indices of a NetNet to generate a canonical expression to
* replace the exprs.
*/
NetExpr*collapse_array_exprs(Design*des, NetScope*scope,
const LineInfo*loc, NetNet*net,
const list<index_component_t>&indices)
{
// First elaborate all the expressions as far as possible.
list<NetExpr*> exprs;
list<long> exprs_const;
indices_flags flags;
indices_to_expressions(des, scope, loc, indices,
net->packed_dimensions(),
false, flags, exprs, exprs_const);
ivl_assert(*loc, exprs.size() == net->packed_dimensions());
// Special Case: there is only 1 packed dimension, so the
// single expression should already be naturally canonical.
if (net->slice_width(1) == 1) {
return *exprs.begin();
}
const std::vector<netrange_t>&pdims = net->packed_dims();
std::vector<netrange_t>::const_iterator pcur = pdims.begin();
list<NetExpr*>::iterator ecur = exprs.begin();
NetExpr* base = 0;
for (size_t idx = 0 ; idx < net->packed_dimensions() ; idx += 1, ++pcur, ++ecur) {
unsigned cur_slice_width = net->slice_width(idx+1);
long lsb = pcur->get_lsb();
long msb = pcur->get_msb();
// This normalizes the expression of this index based on
// the msb/lsb values.
NetExpr*tmp = normalize_variable_base(*ecur, msb, lsb,
cur_slice_width, msb > lsb);
// If this slice has width, then scale it.
if (net->slice_width(idx+1) != 1) {
unsigned min_wid = tmp->expr_width();
if (num_bits(cur_slice_width) >= min_wid) {
min_wid = num_bits(cur_slice_width)+1;
tmp = pad_to_width(tmp, min_wid, *loc);
}
tmp = make_mult_expr(tmp, cur_slice_width);
}
// Now add it to the position we've accumulated so far.
if (base) {
base = make_add_expr(loc, base, tmp);
} else {
base = tmp;
}
}
return base;
}
/*
* Given a list of indices, treat them as packed indices and convert
* them to an expression that normalizes the list to a single index
* expression over a canonical equivalent 1-dimensional array.
*/
NetExpr*collapse_array_indices(Design*des, NetScope*scope, NetNet*net,
const list<index_component_t>&indices)
{
list<long>prefix_indices;
bool rc = evaluate_index_prefix(des, scope, prefix_indices, indices);
assert(rc);
const index_component_t&back_index = indices.back();
assert(back_index.sel == index_component_t::SEL_BIT);
assert(back_index.msb && !back_index.lsb);
NetExpr*base = elab_and_eval(des, scope, back_index.msb, -1, true);
NetExpr*res = normalize_variable_bit_base(prefix_indices, base, net);
eval_expr(res, -1);
return res;
}
void assign_unpacked_with_bufz(Design*des, NetScope*scope,
const LineInfo*loc,
NetNet*lval, NetNet*rval)
{
ivl_assert(*loc, lval->pin_count()==rval->pin_count());
for (unsigned idx = 0 ; idx < lval->pin_count() ; idx += 1) {
NetBUFZ*driver = new NetBUFZ(scope, scope->local_symbol(),
lval->vector_width(), false);
driver->set_line(*loc);
des->add_node(driver);
connect(lval->pin(idx), driver->pin(0));
connect(driver->pin(1), rval->pin(idx));
}
}
/*
* synthesis sometimes needs to unpack assignment to a part
* select. That looks like this:
*
* foo[N] <= <expr> ;
*
* The NetAssignBase::synth_async() method will turn that into a
* netlist like this:
*
* NetAssignBase(PV) --> base()==<N>
* (0) (1)
* | |
* v v
* <expr> foo
*
* This search will return a pointer to the NetAssignBase(PV) object,
* but only if it matches this pattern.
*/
NetPartSelect* detect_partselect_lval(Link&pin)
{
NetPartSelect*found_ps = 0;
Nexus*nex = pin.nexus();
for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) {
NetPins*obj;
unsigned obj_pin;
cur->cur_link(obj, obj_pin);
// Skip NexusSet objects.
if (obj == 0)
continue;
// NetNet pins have no effect on this search.
if (dynamic_cast<NetNet*> (obj))
continue;
if (NetPartSelect*ps = dynamic_cast<NetPartSelect*> (obj)) {
// If this is the input side of a NetPartSelect, skip.
if (ps->pin(obj_pin).get_dir()==Link::INPUT)
continue;
// Oops, driven by the wrong size of a
// NetPartSelect, so this is not going to work out.
if (ps->dir()==NetPartSelect::VP)
return 0;
// So now we know this is a NetPartSelect::PV. It
// is a candidate for our part-select assign. If
// we already have a candidate, then give up.
if (found_ps)
return 0;
// This is our candidate. Carry on.
found_ps = ps;
continue;
}
// If this is a driver to the Nexus that is not a
// NetPartSelect device. This cannot happen to
// part selected lval nets, so quit now.
if (obj->pin(obj_pin).get_dir() == Link::OUTPUT)
return 0;
}
return found_ps;
}
const netclass_t* find_class_containing_scope(const LineInfo&loc, const NetScope*scope)
{
while (scope && scope->type() != NetScope::CLASS)
scope = scope->parent();
if (scope == 0)
return 0;
const netclass_t*found_in = scope->class_def();
ivl_assert(loc, found_in);
return found_in;
}
/*
* Find the scope that contains this scope, that is the method for a
* class scope. Look for the scope whose PARENT is the scope for a
* class. This is going to be a method.
*/
NetScope* find_method_containing_scope(const LineInfo&, NetScope*scope)
{
NetScope*up = scope->parent();
while (up && up->type() != NetScope::CLASS) {
scope = up;
up = up->parent();
}
if (up == 0) return 0;
// Should I check if this scope is a TASK or FUNC?
return scope;
}
/*
* Print a warning if we find a mixture of default and explicit timescale
* based delays in the design, since this is likely an error.
*/
void check_for_inconsistent_delays(NetScope*scope)
{
static bool used_implicit_timescale = false;
static bool used_explicit_timescale = false;
static bool display_ts_dly_warning = true;
if (scope->time_from_timescale())
used_explicit_timescale = true;
else
used_implicit_timescale = true;
if (display_ts_dly_warning &&
used_explicit_timescale &&
used_implicit_timescale) {
if (gn_system_verilog()) {
cerr << "warning: Found both default and explicit "
"timescale based delays. Use" << endl;
cerr << " : -Wtimescale to find the design "
"element(s) with no explicit" << endl;
cerr << " : timescale." << endl;
} else {
cerr << "warning: Found both default and "
"`timescale based delays. Use" << endl;
cerr << " : -Wtimescale to find the "
"module(s) with no `timescale." << endl;
}
display_ts_dly_warning = false;
}
}
/*
* Calculate the bit vector range for a parameter, from the type of the
* parameter. This is expecting that the type is a vector type. The parameter
* is presumably declared something like this:
*
* parameter [4:1] foo = <value>;
*
* In this case, the par_type is a netvector with a single dimension. The
* par_msv gets 4, and par_lsv get 1. The caller uses these values to
* interpret things like bit selects.
*/
bool calculate_param_range(const LineInfo&line, ivl_type_t par_type,
long&par_msv, long&par_lsv, long length)
{
const netvector_t*vector_type = dynamic_cast<const netvector_t*> (par_type);
if (vector_type == 0) {
// If the parameter doesn't have an explicit range, then
// just return range values of [length-1:0].
par_msv = length-1;
par_lsv = 0;
return true;
}
ivl_assert(line, vector_type->packed());
const std::vector<netrange_t>& packed_dims = vector_type->packed_dims();
// This is a netvector_t with 0 dimensions, then the parameter was
// declared with a statement like this:
//
// parameter signed foo = <value>;
//
// The netvector_t is just here to carry the signed-ness, which we don't
// even need here. So act like the type is defined by the r-value
// length.
if (packed_dims.size() == 0) {
par_msv = length-1;
par_lsv = 0;
return true;
}
ivl_assert(line, packed_dims.size() == 1);
netrange_t use_range = packed_dims[0];
par_msv = use_range.get_msb();
par_lsv = use_range.get_lsb();
return true;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
VHDL
1
https://gitee.com/theta405/iverilog.git
git@gitee.com:theta405/iverilog.git
theta405
iverilog
iverilog
master

搜索帮助