1999-10-31 21:08:24 +01:00
|
|
|
/*
|
2007-01-16 06:44:14 +01:00
|
|
|
* Copyright (c) 1999-2007 Stephen Williams (steve@icarus.com)
|
1999-10-31 21:08:24 +01:00
|
|
|
*
|
|
|
|
|
* This source code is free software; you can redistribute it
|
|
|
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
|
|
|
* General Public License as published by the Free Software
|
|
|
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
|
|
|
* any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
|
|
|
*/
|
2002-08-12 03:34:58 +02:00
|
|
|
#ifdef HAVE_CVS_IDENT
|
2007-06-12 06:05:45 +02:00
|
|
|
#ident "$Id: elab_net.cc,v 1.207 2007/06/12 04:05:45 steve Exp $"
|
1999-10-31 21:08:24 +01:00
|
|
|
#endif
|
|
|
|
|
|
2001-07-25 05:10:48 +02:00
|
|
|
# include "config.h"
|
|
|
|
|
|
1999-10-31 21:08:24 +01:00
|
|
|
# include "PExpr.h"
|
|
|
|
|
# include "netlist.h"
|
2000-02-16 04:58:27 +01:00
|
|
|
# include "netmisc.h"
|
2000-03-17 22:50:25 +01:00
|
|
|
# include "compiler.h"
|
1999-10-31 21:08:24 +01:00
|
|
|
|
2001-07-25 05:10:48 +02:00
|
|
|
# include <iostream>
|
2007-01-31 05:21:10 +01:00
|
|
|
# include "ivl_assert.h"
|
2001-07-25 05:10:48 +02:00
|
|
|
|
2003-09-23 05:31:28 +02:00
|
|
|
/*
|
|
|
|
|
* This is a state flag that determines whether an elaborate_net must
|
|
|
|
|
* report an error when it encounters an unsized number. Normally, it
|
|
|
|
|
* is fine to make an unsized number as small as it can be, but there
|
|
|
|
|
* are a few cases where the size must be fully self-determined. For
|
|
|
|
|
* example, within a {...} (concatenation) operator.
|
|
|
|
|
*/
|
|
|
|
|
static bool must_be_self_determined_flag = false;
|
|
|
|
|
|
2001-12-30 22:32:03 +01:00
|
|
|
NetNet* PExpr::elaborate_net(Design*des, NetScope*scope, unsigned,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr*,
|
|
|
|
|
const NetExpr*,
|
|
|
|
|
const NetExpr*,
|
2001-12-30 22:32:03 +01:00
|
|
|
Link::strength_t,
|
|
|
|
|
Link::strength_t) const
|
|
|
|
|
{
|
|
|
|
|
cerr << get_line() << ": error: Unable to elaborate `"
|
|
|
|
|
<< *this << "' as gates." << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-31 21:08:24 +01:00
|
|
|
/*
|
|
|
|
|
* Elaborating binary operations generally involves elaborating the
|
|
|
|
|
* left and right expressions, then making an output wire and
|
|
|
|
|
* connecting the lot together with the right kind of gate.
|
|
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net(Design*des, NetScope*scope,
|
1999-10-31 21:08:24 +01:00
|
|
|
unsigned width,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2000-05-07 06:37:55 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
1999-10-31 21:08:24 +01:00
|
|
|
{
|
|
|
|
|
switch (op_) {
|
2000-01-13 04:35:35 +01:00
|
|
|
case '*':
|
2001-11-08 06:15:50 +01:00
|
|
|
return elaborate_net_mul_(des, scope, width, rise, fall, decay);
|
2000-09-17 23:26:15 +02:00
|
|
|
case '%':
|
2001-11-08 06:15:50 +01:00
|
|
|
return elaborate_net_mod_(des, scope, width, rise, fall, decay);
|
2000-04-01 23:40:22 +02:00
|
|
|
case '/':
|
2001-11-08 06:15:50 +01:00
|
|
|
return elaborate_net_div_(des, scope, width, rise, fall, decay);
|
1999-10-31 21:08:24 +01:00
|
|
|
case '+':
|
|
|
|
|
case '-':
|
2001-11-08 06:15:50 +01:00
|
|
|
return elaborate_net_add_(des, scope, width, rise, fall, decay);
|
2000-02-16 04:58:27 +01:00
|
|
|
case '|': // Bitwise OR
|
|
|
|
|
case '&':
|
|
|
|
|
case '^':
|
2002-09-12 17:49:43 +02:00
|
|
|
case 'A': // Bitwise NAND (~&)
|
2002-09-18 06:29:55 +02:00
|
|
|
case 'O': // Bitwise NOR (~|)
|
2003-01-27 06:09:17 +01:00
|
|
|
case 'X': // Exclusive NOR
|
2001-11-08 06:15:50 +01:00
|
|
|
return elaborate_net_bit_(des, scope, width, rise, fall, decay);
|
2001-07-07 06:37:18 +02:00
|
|
|
case 'E': // === (case equals)
|
|
|
|
|
case 'e': // ==
|
|
|
|
|
case 'N': // !== (case not-equals)
|
|
|
|
|
case 'n': // !=
|
1999-11-15 00:43:45 +01:00
|
|
|
case '<':
|
|
|
|
|
case '>':
|
|
|
|
|
case 'L': // <=
|
|
|
|
|
case 'G': // >=
|
2001-11-08 06:15:50 +01:00
|
|
|
return elaborate_net_cmp_(des, scope, width, rise, fall, decay);
|
1999-12-16 04:46:39 +01:00
|
|
|
case 'a': // && (logical and)
|
|
|
|
|
case 'o': // || (logical or)
|
2001-11-08 06:15:50 +01:00
|
|
|
return elaborate_net_log_(des, scope, width, rise, fall, decay);
|
1999-11-14 21:24:28 +01:00
|
|
|
case 'l': // <<
|
|
|
|
|
case 'r': // >>
|
2004-06-30 04:16:26 +02:00
|
|
|
case 'R': // >>>
|
2001-11-08 06:15:50 +01:00
|
|
|
return elaborate_net_shift_(des, scope, width, rise, fall, decay);
|
1999-10-31 21:08:24 +01:00
|
|
|
}
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*lsig = left_->elaborate_net(des, scope, width, 0, 0, 0),
|
|
|
|
|
*rsig = right_->elaborate_net(des, scope, width, 0, 0, 0);
|
1999-10-31 21:08:24 +01:00
|
|
|
if (lsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
left_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (rsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
right_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet*osig;
|
|
|
|
|
|
|
|
|
|
switch (op_) {
|
|
|
|
|
case '^': // XOR
|
2000-01-18 05:53:40 +01:00
|
|
|
case 'X': // XNOR
|
1999-10-31 21:08:24 +01:00
|
|
|
case '&': // AND
|
|
|
|
|
case '|': // Bitwise OR
|
2000-02-16 04:58:27 +01:00
|
|
|
assert(0);
|
1999-10-31 21:08:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E': // === (Case equals)
|
|
|
|
|
case 'e': // ==
|
|
|
|
|
case 'n': // !=
|
1999-11-15 00:43:45 +01:00
|
|
|
case '<':
|
|
|
|
|
case '>':
|
|
|
|
|
case 'G': // >=
|
|
|
|
|
case 'L': // <=
|
1999-11-05 22:45:19 +01:00
|
|
|
assert(0);
|
1999-10-31 21:08:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '+':
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'l':
|
|
|
|
|
case 'r':
|
2004-06-30 04:16:26 +02:00
|
|
|
case 'R':
|
1999-11-14 21:24:28 +01:00
|
|
|
assert(0);
|
1999-10-31 21:08:24 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
cerr << get_line() << ": internal error: unsupported"
|
|
|
|
|
" combinational operator (" << op_ << ")." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
osig = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Elaborate the structural +/- as an AddSub object. Connect DataA and
|
|
|
|
|
* DataB to the parameters, and connect the output signal to the
|
|
|
|
|
* Result. In this context, the device is a combinational adder with
|
|
|
|
|
* fixed direction, so leave Add_Sub unconnected and set the
|
|
|
|
|
* LPM_Direction property.
|
|
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net_add_(Design*des, NetScope*scope,
|
1999-10-31 21:08:24 +01:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay) const
|
1999-10-31 21:08:24 +01:00
|
|
|
{
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0),
|
|
|
|
|
*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
1999-10-31 21:08:24 +01:00
|
|
|
if (lsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
left_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (rsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
right_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet*osig;
|
|
|
|
|
|
2005-07-07 18:22:49 +02:00
|
|
|
unsigned width = lsig->vector_width();
|
|
|
|
|
if (rsig->vector_width() > lsig->vector_width())
|
|
|
|
|
width = rsig->vector_width();
|
1999-10-31 21:08:24 +01:00
|
|
|
|
2001-02-08 02:10:30 +01:00
|
|
|
|
2001-01-24 03:52:30 +01:00
|
|
|
/* The owidth is the output width of the lpm_add_sub
|
2003-08-28 06:11:17 +02:00
|
|
|
device. If the desired width is greater then the width of
|
2001-01-24 03:52:30 +01:00
|
|
|
the operands, then widen the adder and let code below pad
|
2004-12-11 03:31:25 +01:00
|
|
|
the operands. */
|
2001-01-24 03:52:30 +01:00
|
|
|
unsigned owidth = width;
|
|
|
|
|
switch (op_) {
|
|
|
|
|
case '+':
|
|
|
|
|
if (lwidth > owidth) {
|
|
|
|
|
owidth = lwidth;
|
2004-12-11 03:31:25 +01:00
|
|
|
width = lwidth;
|
2001-01-24 03:52:30 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '-':
|
|
|
|
|
if (lwidth > owidth) {
|
|
|
|
|
owidth = lwidth;
|
|
|
|
|
width = lwidth;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
2001-02-08 02:10:30 +01:00
|
|
|
|
2006-06-20 07:06:47 +02:00
|
|
|
bool expr_signed = lsig->get_signed() && rsig->get_signed();
|
1999-12-16 03:42:14 +01:00
|
|
|
|
2000-04-28 23:00:28 +02:00
|
|
|
// Pad out the operands, if necessary, the match the width of
|
|
|
|
|
// the adder device.
|
2005-07-07 18:22:49 +02:00
|
|
|
if (lsig->vector_width() < width)
|
2006-06-20 07:06:47 +02:00
|
|
|
if (expr_signed)
|
|
|
|
|
lsig = pad_to_width_signed(des, lsig, width);
|
|
|
|
|
else
|
|
|
|
|
lsig = pad_to_width(des, lsig, width);
|
2000-04-28 23:00:28 +02:00
|
|
|
|
2005-07-07 18:22:49 +02:00
|
|
|
if (rsig->vector_width() < width)
|
2006-06-20 07:06:47 +02:00
|
|
|
if (expr_signed)
|
|
|
|
|
rsig = pad_to_width_signed(des, rsig, width);
|
|
|
|
|
else
|
|
|
|
|
rsig = pad_to_width(des, rsig, width);
|
2000-04-28 23:00:28 +02:00
|
|
|
|
2005-07-07 18:22:49 +02:00
|
|
|
// Check that the argument types match.
|
|
|
|
|
if (lsig->data_type() != rsig->data_type()) {
|
|
|
|
|
cerr << get_line() << ": error: Arguments of add/sub "
|
|
|
|
|
<< "have different data types." << endl;
|
|
|
|
|
cerr << get_line() << ": : Left argument is "
|
|
|
|
|
<< lsig->data_type() << ", right argument is "
|
|
|
|
|
<< rsig->data_type() << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
1999-10-31 21:08:24 +01:00
|
|
|
// Make the adder as wide as the widest operand
|
2003-03-06 01:28:41 +01:00
|
|
|
osig = new NetNet(scope, scope->local_symbol(),
|
2001-01-24 03:52:30 +01:00
|
|
|
NetNet::WIRE, owidth);
|
2005-07-07 18:22:49 +02:00
|
|
|
osig->data_type(lsig->data_type());
|
2006-06-20 07:06:47 +02:00
|
|
|
osig->set_signed(expr_signed);
|
2001-01-24 03:52:30 +01:00
|
|
|
osig->local_flag(true);
|
2004-12-11 03:31:25 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate NetAddSub "
|
|
|
|
|
<< "width=" << width << " lwidth=" << lwidth
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
2003-03-06 01:28:41 +01:00
|
|
|
NetAddSub*adder = new NetAddSub(scope, scope->local_symbol(), width);
|
1999-10-31 21:08:24 +01:00
|
|
|
|
|
|
|
|
// Connect the adder to the various parts.
|
2004-12-11 03:31:25 +01:00
|
|
|
connect(lsig->pin(0), adder->pin_DataA());
|
|
|
|
|
connect(rsig->pin(0), adder->pin_DataB());
|
|
|
|
|
connect(osig->pin(0), adder->pin_Result());
|
|
|
|
|
#ifdef XXXX
|
1999-12-16 03:42:14 +01:00
|
|
|
if (owidth > width)
|
|
|
|
|
connect(osig->pin(width), adder->pin_Cout());
|
2004-12-11 03:31:25 +01:00
|
|
|
#endif
|
2003-06-21 03:21:42 +02:00
|
|
|
NetNode*gate = adder;
|
1999-10-31 21:08:24 +01:00
|
|
|
gate->rise_time(rise);
|
|
|
|
|
gate->fall_time(fall);
|
|
|
|
|
gate->decay_time(decay);
|
|
|
|
|
des->add_node(gate);
|
|
|
|
|
|
2004-06-24 17:22:23 +02:00
|
|
|
gate->attribute(perm_string::literal("LPM_Direction"),
|
|
|
|
|
verinum(op_ == '+' ? "ADD" : "SUB"));
|
1999-10-31 21:08:24 +01:00
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-16 04:58:27 +01:00
|
|
|
/*
|
|
|
|
|
* Elaborate various bitwise logic operators. These are all similar in
|
|
|
|
|
* that they take operants of equal width, and each bit does not
|
|
|
|
|
* affect any other bits. Also common about all this is how bit widths
|
|
|
|
|
* of the operands are handled, when they do not match.
|
|
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net_bit_(Design*des, NetScope*scope,
|
2000-02-16 04:58:27 +01:00
|
|
|
unsigned width,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay) const
|
2000-02-16 04:58:27 +01:00
|
|
|
{
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*lsig = left_->elaborate_net(des, scope, width, 0, 0, 0),
|
|
|
|
|
*rsig = right_->elaborate_net(des, scope, width, 0, 0, 0);
|
2000-02-16 04:58:27 +01:00
|
|
|
if (lsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
left_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (rsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
right_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-29 19:46:18 +01:00
|
|
|
if (lsig->vector_width() < rsig->vector_width())
|
|
|
|
|
lsig = pad_to_width(des, lsig, rsig->vector_width());
|
|
|
|
|
if (rsig->vector_width() < lsig->vector_width())
|
|
|
|
|
rsig = pad_to_width(des, rsig, lsig->vector_width());
|
2000-02-16 04:58:27 +01:00
|
|
|
|
2005-09-01 06:10:47 +02:00
|
|
|
if (lsig->data_type() != rsig->data_type()) {
|
2005-09-14 17:15:44 +02:00
|
|
|
cerr << get_line() << ": error: Types of "
|
|
|
|
|
<< "operands of " << op_ << " do not match: "
|
|
|
|
|
<< lsig->data_type() << " vs. " << rsig->data_type()
|
2005-09-01 06:10:47 +02:00
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-29 19:46:18 +01:00
|
|
|
if (lsig->vector_width() != rsig->vector_width()) {
|
|
|
|
|
cerr << get_line() << ": internal error: lsig width ("
|
|
|
|
|
<< lsig->vector_width() << ") != rsig pin width ("
|
|
|
|
|
<< rsig->vector_width() << ")." << endl;
|
2000-02-16 04:58:27 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-29 19:46:18 +01:00
|
|
|
assert(lsig->vector_width() == rsig->vector_width());
|
2000-02-16 04:58:27 +01:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE,
|
2005-01-29 19:46:18 +01:00
|
|
|
lsig->vector_width());
|
2000-02-16 04:58:27 +01:00
|
|
|
osig->local_flag(true);
|
2005-09-01 06:10:47 +02:00
|
|
|
osig->data_type( lsig->data_type() );
|
2000-02-16 04:58:27 +01:00
|
|
|
|
2004-06-24 17:22:23 +02:00
|
|
|
NetLogic::TYPE gtype=NetLogic::AND;
|
2000-02-16 04:58:27 +01:00
|
|
|
switch (op_) {
|
2004-06-24 17:22:23 +02:00
|
|
|
case '^': gtype = NetLogic::XOR; break; // XOR
|
|
|
|
|
case 'X': gtype = NetLogic::XNOR; break; // XNOR
|
|
|
|
|
case '&': gtype = NetLogic::AND; break; // AND
|
|
|
|
|
case 'A': gtype = NetLogic::NAND; break; // NAND (~&)
|
|
|
|
|
case '|': gtype = NetLogic::OR; break; // Bitwise OR
|
|
|
|
|
case 'O': gtype = NetLogic::NOR; break; // Bitwise NOR
|
|
|
|
|
default: assert(0);
|
2000-02-16 04:58:27 +01:00
|
|
|
}
|
|
|
|
|
|
2005-01-29 19:46:18 +01:00
|
|
|
NetLogic*gate = new NetLogic(scope, scope->local_symbol(),
|
|
|
|
|
3, gtype, osig->vector_width());
|
|
|
|
|
gate->set_line(*this);
|
|
|
|
|
connect(gate->pin(0), osig->pin(0));
|
|
|
|
|
connect(gate->pin(1), lsig->pin(0));
|
|
|
|
|
connect(gate->pin(2), rsig->pin(0));
|
|
|
|
|
gate->rise_time(rise);
|
|
|
|
|
gate->fall_time(fall);
|
|
|
|
|
gate->decay_time(decay);
|
|
|
|
|
des->add_node(gate);
|
2000-02-16 04:58:27 +01:00
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-17 01:32:58 +02:00
|
|
|
/*
|
|
|
|
|
* This function attempts to handle the special case of == or !=
|
|
|
|
|
* compare to a constant value. The caller has determined already that
|
|
|
|
|
* one of the operands is a NetEConst, and has already elaborated the
|
|
|
|
|
* other.
|
|
|
|
|
*/
|
|
|
|
|
static NetNet* compare_eq_constant(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*lsig, NetEConst*rexp,
|
|
|
|
|
char op_code,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay)
|
2004-06-17 01:32:58 +02:00
|
|
|
{
|
|
|
|
|
if (op_code != 'e' && op_code != 'n')
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
verinum val = rexp->value();
|
|
|
|
|
|
|
|
|
|
/* Abandon special case if there are x or z bits in the
|
|
|
|
|
constant. We can't get the right behavior out of
|
|
|
|
|
OR/NOR in this case. */
|
|
|
|
|
if (! val.is_defined())
|
|
|
|
|
return 0;
|
|
|
|
|
|
2005-01-13 01:23:10 +01:00
|
|
|
if (val.len() < lsig->vector_width())
|
|
|
|
|
val = verinum(val, lsig->vector_width());
|
2004-06-17 01:32:58 +02:00
|
|
|
|
2004-06-18 18:38:22 +02:00
|
|
|
/* Look for the very special case that we know the compare
|
|
|
|
|
results a priori due to different high bits, that are
|
|
|
|
|
constant pad in the signal. */
|
2005-01-13 01:23:10 +01:00
|
|
|
if (val.len() > lsig->vector_width()) {
|
|
|
|
|
unsigned idx = lsig->vector_width();
|
2004-06-18 18:38:22 +02:00
|
|
|
verinum::V lpad = verinum::V0;
|
|
|
|
|
|
|
|
|
|
while (idx < val.len()) {
|
|
|
|
|
if (val.get(idx) != lpad) {
|
|
|
|
|
verinum oval (op_code == 'e'
|
|
|
|
|
? verinum::V0
|
|
|
|
|
: verinum::V1,
|
|
|
|
|
1);
|
|
|
|
|
NetEConst*ogate = new NetEConst(oval);
|
|
|
|
|
NetNet*osig = ogate->synthesize(des);
|
2006-04-30 07:17:48 +02:00
|
|
|
osig->data_type(lsig->data_type());
|
2004-06-18 18:38:22 +02:00
|
|
|
delete ogate;
|
2005-01-13 01:23:10 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << lsig->get_line() << ": debug: "
|
|
|
|
|
<< "Equality replaced with "
|
|
|
|
|
<< oval << " due to high pad mismatch"
|
|
|
|
|
<< endl;
|
|
|
|
|
|
2004-06-18 18:38:22 +02:00
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idx +=1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-06-17 01:32:58 +02:00
|
|
|
|
|
|
|
|
unsigned zeros = 0;
|
|
|
|
|
unsigned ones = 0;
|
2005-01-13 01:23:10 +01:00
|
|
|
for (unsigned idx = 0 ; idx < lsig->vector_width() ; idx += 1) {
|
2004-06-17 01:32:58 +02:00
|
|
|
if (val.get(idx) == verinum::V0)
|
|
|
|
|
zeros += 1;
|
|
|
|
|
if (val.get(idx) == verinum::V1)
|
|
|
|
|
ones += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-10 07:10:40 +02:00
|
|
|
/* Handle the special case that the gate is a compare that can
|
|
|
|
|
be replaces with a reduction AND or NOR. */
|
|
|
|
|
|
|
|
|
|
if (ones == 0 || zeros == 0) {
|
|
|
|
|
NetUReduce::TYPE type;
|
|
|
|
|
|
|
|
|
|
if (zeros > 0) {
|
|
|
|
|
type = op_code == 'e'? NetUReduce::NOR : NetUReduce::OR;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << lsig->get_line() << ": debug: "
|
|
|
|
|
<< "Replace net==" << val << " equality with "
|
|
|
|
|
<< zeros << "-input reduction [N]OR gate." << endl;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
type = op_code == 'e'? NetUReduce::AND : NetUReduce::NAND;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << lsig->get_line() << ": debug: "
|
|
|
|
|
<< "Replace net==" << val << " equality with "
|
|
|
|
|
<< ones << "-input reduction AND gate." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetUReduce*red = new NetUReduce(scope, scope->local_symbol(),
|
|
|
|
|
type, zeros+ones);
|
|
|
|
|
des->add_node(red);
|
|
|
|
|
red->set_line(*lsig);
|
2005-01-13 01:23:10 +01:00
|
|
|
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, 0, 0);
|
2006-04-30 07:17:48 +02:00
|
|
|
tmp->data_type(lsig->data_type());
|
2005-01-13 01:23:10 +01:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->set_line(*lsig);
|
2004-06-17 01:32:58 +02:00
|
|
|
|
2005-05-10 07:10:40 +02:00
|
|
|
connect(red->pin(1), lsig->pin(0));
|
|
|
|
|
connect(red->pin(0), tmp->pin(0));
|
|
|
|
|
return tmp;
|
2004-06-17 01:32:58 +02:00
|
|
|
}
|
|
|
|
|
|
2005-05-10 07:10:40 +02:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << lsig->get_line() << ": debug: "
|
|
|
|
|
<< "Give up trying to replace net==" << val
|
|
|
|
|
<< " equality with "
|
|
|
|
|
<< ones << "-input AND and "
|
|
|
|
|
<< zeros << "-input NOR gates." << endl;
|
|
|
|
|
|
|
|
|
|
return 0;
|
2004-06-17 01:32:58 +02:00
|
|
|
}
|
|
|
|
|
|
1999-11-05 22:45:19 +01:00
|
|
|
/*
|
2000-01-11 05:20:57 +01:00
|
|
|
* Elaborate the various binary comparison operators. The comparison
|
|
|
|
|
* operators return a single bit result, no matter what, so the left
|
|
|
|
|
* and right values can have their own size. The only restriction is
|
|
|
|
|
* that they have the same size.
|
1999-11-05 22:45:19 +01:00
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net_cmp_(Design*des, NetScope*scope,
|
1999-11-05 22:45:19 +01:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay) const
|
1999-11-05 22:45:19 +01:00
|
|
|
{
|
2005-05-06 02:25:13 +02:00
|
|
|
|
2004-02-15 05:23:48 +01:00
|
|
|
/* Elaborate the operands of the compare first as expressions
|
|
|
|
|
(so that the eval_tree method can reduce constant
|
|
|
|
|
expressions, including parameters) then turn those results
|
|
|
|
|
into synthesized nets. */
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*lexp = elab_and_eval(des, scope, left_, lwidth);
|
2004-02-15 05:23:48 +01:00
|
|
|
if (lexp == 0) {
|
1999-11-05 22:45:19 +01:00
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
left_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2004-02-15 05:23:48 +01:00
|
|
|
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*rexp = elab_and_eval(des, scope, right_, lwidth);
|
2004-02-15 05:23:48 +01:00
|
|
|
if (rexp == 0) {
|
1999-11-05 22:45:19 +01:00
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
right_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-19 05:51:38 +02:00
|
|
|
/* Choose the operand width to be the width of the widest
|
|
|
|
|
self-determined operand. */
|
|
|
|
|
unsigned operand_width = lexp->expr_width();
|
|
|
|
|
if (rexp->expr_width() > operand_width)
|
|
|
|
|
operand_width = rexp->expr_width();
|
|
|
|
|
|
|
|
|
|
lexp->set_width(operand_width);
|
|
|
|
|
lexp = pad_to_width(lexp, operand_width);
|
|
|
|
|
rexp->set_width(operand_width);
|
|
|
|
|
rexp = pad_to_width(rexp, operand_width);
|
2004-02-15 05:23:48 +01:00
|
|
|
|
2004-06-17 01:32:58 +02:00
|
|
|
NetNet*lsig = 0;
|
|
|
|
|
NetNet*rsig = 0;
|
|
|
|
|
|
|
|
|
|
/* Handle the special case that the right or left
|
|
|
|
|
sub-expression is a constant value. The compare_eq_constant
|
|
|
|
|
function will return an elaborated result if it can make
|
|
|
|
|
use of the situation, or 0 if it cannot. */
|
|
|
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*>(rexp)) {
|
|
|
|
|
|
|
|
|
|
lsig = lexp->synthesize(des);
|
2004-10-04 05:09:38 +02:00
|
|
|
if (lsig == 0) {
|
|
|
|
|
cerr << get_line() << ": internal error: "
|
|
|
|
|
"Cannot elaborate net for " << *lexp << endl;
|
2005-08-31 07:07:31 +02:00
|
|
|
return 0;
|
2004-10-04 05:09:38 +02:00
|
|
|
}
|
2004-06-17 01:32:58 +02:00
|
|
|
assert(lsig);
|
|
|
|
|
delete lexp;
|
|
|
|
|
lexp = 0;
|
|
|
|
|
|
|
|
|
|
NetNet*osig = compare_eq_constant(des, scope,
|
|
|
|
|
lsig, tmp, op_,
|
|
|
|
|
rise, fall, decay);
|
|
|
|
|
if (osig != 0) {
|
|
|
|
|
delete rexp;
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NetEConst*tmp = dynamic_cast<NetEConst*>(lexp)) {
|
|
|
|
|
|
|
|
|
|
rsig = rexp->synthesize(des);
|
|
|
|
|
assert(rsig);
|
|
|
|
|
delete rexp;
|
|
|
|
|
|
|
|
|
|
NetNet*osig = compare_eq_constant(des, scope,
|
|
|
|
|
rsig, tmp, op_,
|
|
|
|
|
rise, fall, decay);
|
|
|
|
|
if (osig != 0) {
|
|
|
|
|
delete lexp;
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lsig == 0) {
|
|
|
|
|
lsig = lexp->synthesize(des);
|
|
|
|
|
assert(lsig);
|
|
|
|
|
delete lexp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rsig == 0) {
|
|
|
|
|
rsig = rexp->synthesize(des);
|
|
|
|
|
assert(rsig);
|
|
|
|
|
delete rexp;
|
|
|
|
|
}
|
2004-02-15 05:23:48 +01:00
|
|
|
|
2005-01-16 05:20:32 +01:00
|
|
|
unsigned dwidth = lsig->vector_width();
|
|
|
|
|
if (rsig->vector_width() > dwidth) dwidth = rsig->vector_width();
|
2000-01-11 05:20:57 +01:00
|
|
|
|
2003-04-11 07:18:08 +02:00
|
|
|
/* Operands of binary compare need to be padded to equal
|
|
|
|
|
size. Figure the pad bit needed to extend the narrowest
|
|
|
|
|
vector. */
|
2005-01-16 05:20:32 +01:00
|
|
|
if (lsig->vector_width() < dwidth)
|
|
|
|
|
lsig = pad_to_width(des, lsig, dwidth);
|
|
|
|
|
if (rsig->vector_width() < dwidth)
|
|
|
|
|
rsig = pad_to_width(des, rsig, dwidth);
|
2003-04-11 07:18:08 +02:00
|
|
|
|
1999-11-05 22:45:19 +01:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE);
|
2005-07-07 18:22:49 +02:00
|
|
|
osig->data_type(IVL_VT_LOGIC);
|
2005-01-16 05:20:32 +01:00
|
|
|
osig->set_line(*this);
|
1999-11-05 22:45:19 +01:00
|
|
|
osig->local_flag(true);
|
|
|
|
|
|
|
|
|
|
NetNode*gate;
|
|
|
|
|
|
|
|
|
|
switch (op_) {
|
1999-11-15 00:43:45 +01:00
|
|
|
case '<':
|
|
|
|
|
case '>':
|
|
|
|
|
case 'L':
|
|
|
|
|
case 'G': {
|
2000-01-11 05:20:57 +01:00
|
|
|
NetCompare*cmp = new
|
2003-02-26 02:29:24 +01:00
|
|
|
NetCompare(scope, scope->local_symbol(), dwidth);
|
2005-01-16 05:20:32 +01:00
|
|
|
connect(cmp->pin_DataA(), lsig->pin(0));
|
|
|
|
|
connect(cmp->pin_DataB(), rsig->pin(0));
|
2000-01-11 05:20:57 +01:00
|
|
|
|
1999-11-15 00:43:45 +01:00
|
|
|
switch (op_) {
|
|
|
|
|
case '<':
|
|
|
|
|
connect(cmp->pin_ALB(), osig->pin(0));
|
|
|
|
|
break;
|
|
|
|
|
case '>':
|
|
|
|
|
connect(cmp->pin_AGB(), osig->pin(0));
|
|
|
|
|
break;
|
|
|
|
|
case 'L':
|
|
|
|
|
connect(cmp->pin_ALEB(), osig->pin(0));
|
|
|
|
|
break;
|
|
|
|
|
case 'G':
|
|
|
|
|
connect(cmp->pin_AGEB(), osig->pin(0));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2003-04-11 07:18:08 +02:00
|
|
|
/* If both operands are signed, then do a signed
|
|
|
|
|
compare. */
|
|
|
|
|
if (lsig->get_signed() && rsig->get_signed())
|
|
|
|
|
cmp->set_signed(true);
|
|
|
|
|
|
1999-11-15 00:43:45 +01:00
|
|
|
gate = cmp;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-05 22:45:19 +01:00
|
|
|
case 'E': // Case equals (===)
|
2005-03-09 06:52:03 +01:00
|
|
|
gate = new NetCaseCmp(scope, scope->local_symbol(), dwidth, true);
|
1999-11-05 22:45:19 +01:00
|
|
|
connect(gate->pin(0), osig->pin(0));
|
2005-01-22 02:06:55 +01:00
|
|
|
connect(gate->pin(1), lsig->pin(0));
|
|
|
|
|
connect(gate->pin(2), rsig->pin(0));
|
1999-11-05 22:45:19 +01:00
|
|
|
break;
|
|
|
|
|
|
2005-01-22 02:06:55 +01:00
|
|
|
case 'N': // Case equals (!==)
|
2005-03-09 06:52:03 +01:00
|
|
|
gate = new NetCaseCmp(scope, scope->local_symbol(), dwidth, false);
|
|
|
|
|
connect(gate->pin(0), osig->pin(0));
|
|
|
|
|
connect(gate->pin(1), lsig->pin(0));
|
|
|
|
|
connect(gate->pin(2), rsig->pin(0));
|
2005-01-22 02:06:55 +01:00
|
|
|
break;
|
1999-11-05 22:45:19 +01:00
|
|
|
|
|
|
|
|
case 'e': // ==
|
2000-07-08 06:59:20 +02:00
|
|
|
|
|
|
|
|
/* Handle the special case of single bit compare with a
|
|
|
|
|
single XNOR gate. This is easy and direct. */
|
|
|
|
|
if (dwidth == 1) {
|
2003-03-06 01:28:41 +01:00
|
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
2004-12-11 03:31:25 +01:00
|
|
|
3, NetLogic::XNOR, 1);
|
2000-07-08 06:59:20 +02:00
|
|
|
connect(gate->pin(0), osig->pin(0));
|
|
|
|
|
connect(gate->pin(1), lsig->pin(0));
|
|
|
|
|
connect(gate->pin(2), rsig->pin(0));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-06 02:25:13 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate net == gate."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2001-09-14 06:16:52 +02:00
|
|
|
/* Oh well, do the general case with a NetCompare. */
|
2003-02-26 02:29:24 +01:00
|
|
|
{ NetCompare*cmp = new NetCompare(scope, scope->local_symbol(),
|
2001-09-14 06:16:52 +02:00
|
|
|
dwidth);
|
2005-01-16 05:20:32 +01:00
|
|
|
connect(cmp->pin_DataA(), lsig->pin(0));
|
|
|
|
|
connect(cmp->pin_DataB(), rsig->pin(0));
|
2001-09-14 06:16:52 +02:00
|
|
|
connect(cmp->pin_AEB(), osig->pin(0));
|
|
|
|
|
gate = cmp;
|
1999-11-05 22:45:19 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'n': // !=
|
2000-07-08 06:59:20 +02:00
|
|
|
|
|
|
|
|
/* Handle the special case of single bit compare with a
|
|
|
|
|
single XOR gate. This is easy and direct. */
|
|
|
|
|
if (dwidth == 1) {
|
2003-03-06 01:28:41 +01:00
|
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
2004-12-11 03:31:25 +01:00
|
|
|
3, NetLogic::XOR, 1);
|
2000-07-08 06:59:20 +02:00
|
|
|
connect(gate->pin(0), osig->pin(0));
|
|
|
|
|
connect(gate->pin(1), lsig->pin(0));
|
|
|
|
|
connect(gate->pin(2), rsig->pin(0));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2001-09-14 06:16:52 +02:00
|
|
|
/* Oh well, do the general case with a NetCompare. */
|
2003-02-26 02:29:24 +01:00
|
|
|
{ NetCompare*cmp = new NetCompare(scope, scope->local_symbol(),
|
2001-09-14 06:16:52 +02:00
|
|
|
dwidth);
|
2005-01-16 05:20:32 +01:00
|
|
|
connect(cmp->pin_DataA(), lsig->pin(0));
|
|
|
|
|
connect(cmp->pin_DataB(), rsig->pin(0));
|
2001-09-14 06:16:52 +02:00
|
|
|
connect(cmp->pin_ANEB(), osig->pin(0));
|
|
|
|
|
gate = cmp;
|
|
|
|
|
}
|
1999-11-05 22:45:19 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gate->rise_time(rise);
|
|
|
|
|
gate->fall_time(fall);
|
|
|
|
|
gate->decay_time(decay);
|
|
|
|
|
des->add_node(gate);
|
|
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2000-04-01 23:40:22 +02:00
|
|
|
/*
|
2003-01-27 06:09:17 +01:00
|
|
|
* Elaborate a divider gate. This function create a NetDivide gate
|
2001-01-05 04:19:47 +01:00
|
|
|
* which has exactly the right sized DataA, DataB and Result ports. If
|
|
|
|
|
* the l-value is wider then the result, then pad.
|
2000-04-01 23:40:22 +02:00
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net_div_(Design*des, NetScope*scope,
|
2000-04-01 23:40:22 +02:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay) const
|
2000-04-01 23:40:22 +02:00
|
|
|
{
|
2003-09-23 05:31:28 +02:00
|
|
|
NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
2000-04-01 23:40:22 +02:00
|
|
|
if (lsig == 0) return 0;
|
2003-09-23 05:31:28 +02:00
|
|
|
NetNet*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
2000-04-01 23:40:22 +02:00
|
|
|
if (rsig == 0) return 0;
|
|
|
|
|
|
2001-01-05 04:19:47 +01:00
|
|
|
|
|
|
|
|
// Check the l-value width. If it is unspecified, then use the
|
|
|
|
|
// largest operand width as the l-value width. Restrict the
|
|
|
|
|
// result width to the width of the largest operand, because
|
|
|
|
|
// there is no value is excess divider.
|
|
|
|
|
|
|
|
|
|
unsigned rwidth = lwidth;
|
|
|
|
|
|
|
|
|
|
if (rwidth == 0) {
|
2005-02-19 03:43:38 +01:00
|
|
|
rwidth = lsig->vector_width();
|
|
|
|
|
if (rsig->vector_width() > rwidth)
|
|
|
|
|
rwidth = rsig->vector_width();
|
2001-01-05 04:19:47 +01:00
|
|
|
|
|
|
|
|
lwidth = rwidth;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
if ((rwidth > lsig->vector_width()) && (rwidth > rsig->vector_width())) {
|
|
|
|
|
rwidth = lsig->vector_width();
|
|
|
|
|
if (rsig->vector_width() > rwidth)
|
|
|
|
|
rwidth = rsig->vector_width();
|
2001-01-05 04:19:47 +01:00
|
|
|
}
|
|
|
|
|
|
2005-07-07 18:22:49 +02:00
|
|
|
/* The arguments of a divide must have the same type. */
|
|
|
|
|
if (lsig->data_type() != rsig->data_type()) {
|
|
|
|
|
cerr << get_line() << ": error: Arguments of divide "
|
|
|
|
|
<< "have different data types." << endl;
|
|
|
|
|
cerr << get_line() << ": : Left argument is "
|
|
|
|
|
<< lsig->data_type() << ", right argument is "
|
|
|
|
|
<< rsig->data_type() << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_variable_type_t data_type = lsig->data_type();
|
|
|
|
|
|
2001-01-05 04:19:47 +01:00
|
|
|
// Create a device with the calculated dimensions.
|
2003-02-26 02:29:24 +01:00
|
|
|
NetDivide*div = new NetDivide(scope, scope->local_symbol(), rwidth,
|
2005-02-19 03:43:38 +01:00
|
|
|
lsig->vector_width(),
|
|
|
|
|
rsig->vector_width());
|
2000-04-01 23:40:22 +02:00
|
|
|
des->add_node(div);
|
|
|
|
|
|
2004-06-30 04:16:26 +02:00
|
|
|
div->set_signed(lsig->get_signed() && rsig->get_signed());
|
2001-01-05 04:19:47 +01:00
|
|
|
|
|
|
|
|
// Connect the left and right inputs of the divider to the
|
|
|
|
|
// nets that are the left and right expressions.
|
|
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
connect(div->pin_DataA(), lsig->pin(0));
|
|
|
|
|
connect(div->pin_DataB(), rsig->pin(0));
|
2000-04-01 23:40:22 +02:00
|
|
|
|
2001-01-05 04:19:47 +01:00
|
|
|
|
|
|
|
|
// Make an output signal that is the width of the l-value.
|
|
|
|
|
// Due to above calculation of rwidth, we know that the result
|
2007-03-22 17:08:14 +01:00
|
|
|
// will be no more than the l-value, so it is safe to connect
|
2001-01-05 04:19:47 +01:00
|
|
|
// all the result pins to the osig.
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2000-04-01 23:40:22 +02:00
|
|
|
NetNet::IMPLICIT, lwidth);
|
|
|
|
|
osig->local_flag(true);
|
2005-07-07 18:22:49 +02:00
|
|
|
osig->data_type(data_type);
|
2004-06-30 04:16:26 +02:00
|
|
|
osig->set_signed(div->get_signed());
|
2000-04-01 23:40:22 +02:00
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
connect(div->pin_Result(), osig->pin(0));
|
2001-01-05 04:19:47 +01:00
|
|
|
|
2000-04-01 23:40:22 +02:00
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-17 23:26:15 +02:00
|
|
|
/*
|
|
|
|
|
* Elaborate a modulo gate.
|
|
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net_mod_(Design*des, NetScope*scope,
|
2000-09-17 23:26:15 +02:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay) const
|
2000-09-17 23:26:15 +02:00
|
|
|
{
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*lsig = left_->elaborate_net(des, scope, 0, 0, 0, 0);
|
2000-09-17 23:26:15 +02:00
|
|
|
if (lsig == 0) return 0;
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*rsig = right_->elaborate_net(des, scope, 0, 0, 0, 0);
|
2000-09-17 23:26:15 +02:00
|
|
|
if (rsig == 0) return 0;
|
|
|
|
|
|
2005-07-07 18:22:49 +02:00
|
|
|
/* The arguments of a modulus must have the same type. */
|
|
|
|
|
if (lsig->data_type() != rsig->data_type()) {
|
|
|
|
|
cerr << get_line() << ": error: Arguments of modulus "
|
|
|
|
|
<< "have different data types." << endl;
|
|
|
|
|
cerr << get_line() << ": : Left argument is "
|
|
|
|
|
<< lsig->data_type() << ", right argument is "
|
|
|
|
|
<< rsig->data_type() << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_variable_type_t data_type = lsig->data_type();
|
|
|
|
|
|
2005-03-12 07:43:35 +01:00
|
|
|
/* rwidth is result width. */
|
2002-01-03 05:19:01 +01:00
|
|
|
unsigned rwidth = lwidth;
|
|
|
|
|
if (rwidth == 0) {
|
2005-03-12 07:43:35 +01:00
|
|
|
rwidth = lsig->vector_width();
|
|
|
|
|
if (rsig->vector_width() > rwidth)
|
|
|
|
|
rwidth = rsig->vector_width();
|
|
|
|
|
|
|
|
|
|
lwidth = rwidth;
|
2002-01-03 05:19:01 +01:00
|
|
|
}
|
2005-03-12 07:43:35 +01:00
|
|
|
|
2003-02-26 02:29:24 +01:00
|
|
|
NetModulo*mod = new NetModulo(scope, scope->local_symbol(), rwidth,
|
2005-03-12 07:43:35 +01:00
|
|
|
lsig->vector_width(),
|
|
|
|
|
rsig->vector_width());
|
2005-09-16 01:04:09 +02:00
|
|
|
mod->set_line(*this);
|
2000-09-17 23:26:15 +02:00
|
|
|
des->add_node(mod);
|
|
|
|
|
|
2005-03-12 07:43:35 +01:00
|
|
|
connect(mod->pin_DataA(), lsig->pin(0));
|
|
|
|
|
connect(mod->pin_DataB(), rsig->pin(0));
|
2000-09-17 23:26:15 +02:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2005-03-12 07:43:35 +01:00
|
|
|
NetNet::IMPLICIT, rwidth);
|
2005-09-16 01:04:09 +02:00
|
|
|
osig->set_line(*this);
|
2005-07-07 18:22:49 +02:00
|
|
|
osig->data_type(data_type);
|
2000-09-17 23:26:15 +02:00
|
|
|
osig->local_flag(true);
|
|
|
|
|
|
2005-03-12 07:43:35 +01:00
|
|
|
connect(mod->pin_Result(), osig->pin(0));
|
2000-09-17 23:26:15 +02:00
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net_log_(Design*des, NetScope*scope,
|
1999-12-16 04:46:39 +01:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay) const
|
1999-12-16 04:46:39 +01:00
|
|
|
{
|
2004-06-01 03:04:57 +02:00
|
|
|
NetNet*lsig = left_->elaborate_net(des, scope, 0, 0, 0, 0);
|
|
|
|
|
NetNet*rsig = right_->elaborate_net(des, scope, 0, 0, 0, 0);
|
1999-12-16 04:46:39 +01:00
|
|
|
if (lsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
left_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (rsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot elaborate ";
|
|
|
|
|
right_->dump(cerr);
|
|
|
|
|
cerr << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetLogic*gate;
|
|
|
|
|
switch (op_) {
|
|
|
|
|
case 'a':
|
2003-03-06 01:28:41 +01:00
|
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
2004-12-11 03:31:25 +01:00
|
|
|
3, NetLogic::AND, 1);
|
1999-12-16 04:46:39 +01:00
|
|
|
break;
|
|
|
|
|
case 'o':
|
2003-03-06 01:28:41 +01:00
|
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
2004-12-11 03:31:25 +01:00
|
|
|
3, NetLogic::OR, 1);
|
1999-12-16 04:46:39 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
gate->rise_time(rise);
|
|
|
|
|
gate->fall_time(fall);
|
|
|
|
|
gate->decay_time(decay);
|
|
|
|
|
|
|
|
|
|
// The first OR gate returns 1 if the left value is true...
|
2005-03-19 07:59:53 +01:00
|
|
|
if (lsig->vector_width() > 1) {
|
|
|
|
|
NetUReduce*gate_tmp = new NetUReduce(scope, scope->local_symbol(),
|
|
|
|
|
NetUReduce::OR,
|
|
|
|
|
lsig->vector_width());
|
|
|
|
|
connect(gate_tmp->pin(1), lsig->pin(0));
|
|
|
|
|
connect(gate->pin(1), gate_tmp->pin(0));
|
2000-03-16 20:03:03 +01:00
|
|
|
|
|
|
|
|
/* The reduced logical value is a new nexus, create a
|
|
|
|
|
temporary signal to represent it. */
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2002-06-19 06:20:03 +02:00
|
|
|
NetNet::IMPLICIT, 1);
|
2006-05-01 22:47:58 +02:00
|
|
|
tmp->data_type(IVL_VT_LOGIC);
|
2002-06-19 06:20:03 +02:00
|
|
|
tmp->local_flag(true);
|
2000-03-16 20:03:03 +01:00
|
|
|
connect(gate->pin(1), tmp->pin(0));
|
|
|
|
|
|
2005-03-19 07:59:53 +01:00
|
|
|
des->add_node(gate_tmp);
|
2000-03-16 20:03:03 +01:00
|
|
|
|
1999-12-16 04:46:39 +01:00
|
|
|
} else {
|
|
|
|
|
connect(gate->pin(1), lsig->pin(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The second OR gate returns 1 if the right value is true...
|
2005-03-19 07:59:53 +01:00
|
|
|
if (rsig->vector_width() > 1) {
|
|
|
|
|
NetUReduce*gate_tmp = new NetUReduce(scope, scope->local_symbol(),
|
|
|
|
|
NetUReduce::OR,
|
|
|
|
|
rsig->vector_width());
|
|
|
|
|
connect(gate_tmp->pin(1), rsig->pin(0));
|
|
|
|
|
connect(gate->pin(2), gate_tmp->pin(0));
|
2000-03-16 20:03:03 +01:00
|
|
|
|
|
|
|
|
/* The reduced logical value is a new nexus, create a
|
|
|
|
|
temporary signal to represent it. */
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2002-06-19 06:20:03 +02:00
|
|
|
NetNet::IMPLICIT, 1);
|
2006-05-01 22:47:58 +02:00
|
|
|
tmp->data_type(IVL_VT_LOGIC);
|
2002-06-19 06:20:03 +02:00
|
|
|
tmp->local_flag(true);
|
2000-03-16 20:03:03 +01:00
|
|
|
connect(gate->pin(2), tmp->pin(0));
|
|
|
|
|
|
2005-03-19 07:59:53 +01:00
|
|
|
des->add_node(gate_tmp);
|
2000-03-16 20:03:03 +01:00
|
|
|
|
1999-12-16 04:46:39 +01:00
|
|
|
} else {
|
|
|
|
|
connect(gate->pin(2), rsig->pin(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The output is the AND/OR of the two logic values.
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE);
|
1999-12-16 04:46:39 +01:00
|
|
|
osig->local_flag(true);
|
2005-10-11 18:15:52 +02:00
|
|
|
osig->data_type(IVL_VT_LOGIC);
|
1999-12-16 04:46:39 +01:00
|
|
|
connect(gate->pin(0), osig->pin(0));
|
|
|
|
|
des->add_node(gate);
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net_mul_(Design*des, NetScope*scope,
|
2000-01-13 04:35:35 +01:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay) const
|
2000-01-13 04:35:35 +01:00
|
|
|
{
|
2001-12-31 05:23:59 +01:00
|
|
|
verinum*lnum = left_->eval_const(des, scope);
|
|
|
|
|
verinum*rnum = right_->eval_const(des, scope);
|
|
|
|
|
|
|
|
|
|
/* Detect and handle the special case that both the operands
|
2003-01-27 06:09:17 +01:00
|
|
|
of the multiply are constant expressions. Evaluate the
|
2001-12-31 05:23:59 +01:00
|
|
|
value and make this a simple constant. */
|
|
|
|
|
if (lnum && rnum) {
|
|
|
|
|
verinum prod = *lnum * *rnum;
|
|
|
|
|
if (lwidth == 0)
|
|
|
|
|
lwidth = prod.len();
|
|
|
|
|
|
|
|
|
|
verinum res (verinum::V0, lwidth);
|
|
|
|
|
for (unsigned idx = 0
|
|
|
|
|
; idx < prod.len() && idx < lwidth
|
|
|
|
|
; idx += 1) {
|
|
|
|
|
res.set(idx, prod.get(idx));
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetConst*odev = new NetConst(scope, scope->local_symbol(), res);
|
2007-04-18 03:40:49 +02:00
|
|
|
des->add_node(odev);
|
|
|
|
|
odev->set_line(*this);
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2001-12-31 05:23:59 +01:00
|
|
|
NetNet::IMPLICIT, lwidth);
|
2007-04-18 03:40:49 +02:00
|
|
|
osig->set_line(*this);
|
2001-12-31 05:23:59 +01:00
|
|
|
osig->local_flag(true);
|
2007-04-18 03:40:49 +02:00
|
|
|
osig->data_type(IVL_VT_LOGIC);
|
|
|
|
|
|
|
|
|
|
connect(odev->pin(0), osig->pin(0));
|
|
|
|
|
|
2001-12-31 05:23:59 +01:00
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
2000-01-13 04:35:35 +01:00
|
|
|
if (lsig == 0) return 0;
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*rsig = right_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
2000-01-13 04:35:35 +01:00
|
|
|
if (rsig == 0) return 0;
|
|
|
|
|
|
2005-05-24 03:44:27 +02:00
|
|
|
// The mult is signed if both its operands are signed.
|
|
|
|
|
bool arith_is_signed = lsig->get_signed() && rsig->get_signed();
|
|
|
|
|
|
2005-07-07 18:22:49 +02:00
|
|
|
/* The arguments of a divide must have the same type. */
|
|
|
|
|
if (lsig->data_type() != rsig->data_type()) {
|
|
|
|
|
cerr << get_line() << ": error: Arguments of multiply "
|
|
|
|
|
<< "have different data types." << endl;
|
|
|
|
|
cerr << get_line() << ": : Left argument is "
|
|
|
|
|
<< lsig->data_type() << ", right argument is "
|
|
|
|
|
<< rsig->data_type() << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_variable_type_t data_type = lsig->data_type();
|
|
|
|
|
|
2001-01-24 03:52:30 +01:00
|
|
|
unsigned rwidth = lwidth;
|
2001-12-31 05:23:59 +01:00
|
|
|
if (rwidth == 0) {
|
2005-05-24 03:44:27 +02:00
|
|
|
rwidth = lsig->vector_width() + rsig->vector_width();
|
2005-01-28 06:39:33 +01:00
|
|
|
lwidth = rwidth;
|
2001-12-31 05:23:59 +01:00
|
|
|
}
|
|
|
|
|
|
2005-05-24 03:44:27 +02:00
|
|
|
if (arith_is_signed) {
|
|
|
|
|
lsig = pad_to_width_signed(des, lsig, rwidth);
|
|
|
|
|
rsig = pad_to_width_signed(des, rsig, rwidth);
|
|
|
|
|
}
|
|
|
|
|
|
2003-02-26 02:29:24 +01:00
|
|
|
NetMult*mult = new NetMult(scope, scope->local_symbol(), rwidth,
|
2005-01-28 06:39:33 +01:00
|
|
|
lsig->vector_width(),
|
|
|
|
|
rsig->vector_width());
|
|
|
|
|
mult->set_line(*this);
|
2000-01-13 04:35:35 +01:00
|
|
|
des->add_node(mult);
|
|
|
|
|
|
2005-05-24 03:44:27 +02:00
|
|
|
mult->set_signed( arith_is_signed );
|
2003-03-29 06:51:25 +01:00
|
|
|
|
2005-01-28 06:39:33 +01:00
|
|
|
connect(mult->pin_DataA(), lsig->pin(0));
|
|
|
|
|
connect(mult->pin_DataB(), rsig->pin(0));
|
2000-01-13 04:35:35 +01:00
|
|
|
|
2005-01-28 06:39:33 +01:00
|
|
|
// Make a signal to carry the output from the multiply.
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2005-01-28 06:39:33 +01:00
|
|
|
NetNet::IMPLICIT, rwidth);
|
2005-07-07 18:22:49 +02:00
|
|
|
osig->data_type(data_type);
|
2000-01-13 04:35:35 +01:00
|
|
|
osig->local_flag(true);
|
2005-01-28 06:39:33 +01:00
|
|
|
connect(mult->pin_Result(), osig->pin(0));
|
2000-01-13 04:35:35 +01:00
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEBinary::elaborate_net_shift_(Design*des, NetScope*scope,
|
1999-11-14 21:24:28 +01:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay) const
|
1999-11-14 21:24:28 +01:00
|
|
|
{
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*lsig = left_->elaborate_net(des, scope, lwidth, 0, 0, 0);
|
1999-11-14 21:24:28 +01:00
|
|
|
if (lsig == 0) return 0;
|
|
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
if (lsig->vector_width() > lwidth)
|
|
|
|
|
lwidth = lsig->vector_width();
|
2000-01-02 22:45:31 +01:00
|
|
|
|
2004-06-30 04:16:26 +02:00
|
|
|
bool right_flag = op_ == 'r' || op_ == 'R';
|
|
|
|
|
bool signed_flag = op_ == 'R';
|
2005-07-07 18:22:49 +02:00
|
|
|
ivl_variable_type_t data_type = lsig->data_type();
|
2004-06-30 04:16:26 +02:00
|
|
|
|
1999-11-14 21:24:28 +01:00
|
|
|
/* Handle the special case of a constant shift amount. There
|
|
|
|
|
is no reason in this case to create a gate at all, just
|
|
|
|
|
connect the lsig to the osig with the bit positions
|
2005-02-19 03:43:38 +01:00
|
|
|
shifted. Use a NetPartSelect to select the parts of the
|
|
|
|
|
left expression that survive the shift, and a NetConcat to
|
|
|
|
|
concatenate a constant for padding. */
|
2001-11-07 05:01:59 +01:00
|
|
|
if (verinum*rval = right_->eval_const(des, scope)) {
|
1999-11-14 21:24:28 +01:00
|
|
|
assert(rval->is_defined());
|
|
|
|
|
unsigned dist = rval->as_ulong();
|
|
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
/* Very special case: constant 0 shift. Simply return
|
|
|
|
|
the left signal again. */
|
1999-11-14 21:24:28 +01:00
|
|
|
if (dist == 0) return lsig;
|
|
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
/* Another very special case: constant shift the entire
|
|
|
|
|
value away. The result is a const. */
|
|
|
|
|
if (dist > lwidth) {
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The construction that I'm making will ultimately
|
|
|
|
|
connect its output to the osig here. This will be the
|
|
|
|
|
result that I return from this function. */
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2001-01-24 03:52:30 +01:00
|
|
|
NetNet::WIRE, lwidth);
|
2005-07-07 18:22:49 +02:00
|
|
|
osig->data_type( data_type );
|
1999-11-14 21:24:28 +01:00
|
|
|
osig->local_flag(true);
|
|
|
|
|
|
2004-06-30 04:16:26 +02:00
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
/* Make the constant zero's that I'm going to pad to the
|
|
|
|
|
top or bottom of the left expression. Attach a signal
|
|
|
|
|
to its output so that I don't have to worry about it
|
|
|
|
|
later. If the left expression is less then the
|
|
|
|
|
desired width (and we are doing right shifts) then we
|
|
|
|
|
can combine the expression padding with the distance
|
|
|
|
|
padding to reduce nodes. */
|
|
|
|
|
unsigned pad_width = dist;
|
|
|
|
|
unsigned part_width = lwidth - dist;
|
|
|
|
|
if (op_ == 'r' || op_ == 'R') {
|
|
|
|
|
if (lsig->vector_width() < lwidth) {
|
|
|
|
|
pad_width += lwidth - lsig->vector_width();
|
|
|
|
|
part_width -= lwidth - lsig->vector_width();
|
|
|
|
|
}
|
1999-11-14 21:24:28 +01:00
|
|
|
} else {
|
2005-02-19 03:43:38 +01:00
|
|
|
|
|
|
|
|
/* The left net must be the same width as the
|
|
|
|
|
result. The part select that I'm about to make relies
|
|
|
|
|
on that. */
|
|
|
|
|
lsig = pad_to_width(des, lsig, lwidth);
|
|
|
|
|
|
1999-11-14 21:24:28 +01:00
|
|
|
}
|
|
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
NetNet*zero = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, pad_width);
|
2005-07-07 18:22:49 +02:00
|
|
|
zero->data_type( data_type );
|
2005-02-19 03:43:38 +01:00
|
|
|
zero->local_flag(true);
|
|
|
|
|
zero->set_line(*this);
|
|
|
|
|
|
|
|
|
|
if (op_ == 'R') {
|
|
|
|
|
NetPartSelect*sign_bit
|
|
|
|
|
= new NetPartSelect(lsig, lsig->vector_width()-1,
|
|
|
|
|
1, NetPartSelect::VP);
|
|
|
|
|
des->add_node(sign_bit);
|
|
|
|
|
NetReplicate*sign_pad
|
|
|
|
|
= new NetReplicate(scope, scope->local_symbol(),
|
|
|
|
|
pad_width, pad_width);
|
|
|
|
|
des->add_node(sign_pad);
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, 1);
|
2005-07-07 18:22:49 +02:00
|
|
|
tmp->data_type( data_type );
|
2007-02-05 02:42:31 +01:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->set_line(*this);
|
2005-02-19 03:43:38 +01:00
|
|
|
connect(sign_bit->pin(0), tmp->pin(0));
|
|
|
|
|
connect(sign_bit->pin(0), sign_pad->pin(1));
|
|
|
|
|
|
|
|
|
|
connect(zero->pin(0), sign_pad->pin(0));
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
NetConst*zero_c = new NetConst(scope, scope->local_symbol(),
|
|
|
|
|
verinum(verinum::V0, pad_width));
|
|
|
|
|
des->add_node(zero_c);
|
|
|
|
|
connect(zero->pin(0), zero_c->pin(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make a concatenation operator that will join the
|
|
|
|
|
part-selected right expression at the pad values. */
|
|
|
|
|
NetConcat*cc = new NetConcat(scope, scope->local_symbol(),
|
|
|
|
|
lwidth, 2);
|
|
|
|
|
cc->set_line(*this);
|
|
|
|
|
des->add_node(cc);
|
|
|
|
|
connect(cc->pin(0), osig->pin(0));
|
|
|
|
|
|
|
|
|
|
/* Make the part select of the left expression and
|
|
|
|
|
connect it to the lsb or msb of the concatenation,
|
|
|
|
|
depending on the direction of the shift. */
|
|
|
|
|
NetPartSelect*part;
|
|
|
|
|
|
|
|
|
|
switch (op_) {
|
|
|
|
|
case 'l': // Left shift === {lsig, zero}
|
|
|
|
|
part = new NetPartSelect(lsig, 0, part_width,
|
|
|
|
|
NetPartSelect::VP);
|
|
|
|
|
connect(cc->pin(1), zero->pin(0));
|
|
|
|
|
connect(cc->pin(2), part->pin(0));
|
|
|
|
|
break;
|
|
|
|
|
case 'R':
|
|
|
|
|
case 'r': // right-shift === {zero, lsig}
|
|
|
|
|
part = new NetPartSelect(lsig, dist, part_width,
|
|
|
|
|
NetPartSelect::VP);
|
|
|
|
|
connect(cc->pin(1), part->pin(0));
|
|
|
|
|
connect(cc->pin(2), zero->pin(0));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
des->add_node(part);
|
|
|
|
|
|
2007-01-20 03:10:45 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate shift "
|
|
|
|
|
<< "(" << op_ << ") as concatenation of "
|
|
|
|
|
<< pad_width << " zeros with " << part_width
|
|
|
|
|
<< " bits of expression." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
/* Attach a signal to the part select output (NetConcat
|
|
|
|
|
input) */
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, part_width);
|
2005-07-07 18:22:49 +02:00
|
|
|
tmp->data_type( data_type );
|
2005-02-19 03:43:38 +01:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
connect(part->pin(0), tmp->pin(0));
|
|
|
|
|
|
1999-11-14 21:24:28 +01:00
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2000-01-02 22:45:31 +01:00
|
|
|
// Calculate the number of useful bits for the shift amount,
|
|
|
|
|
// and elaborate the right_ expression as the shift amount.
|
1999-11-14 21:24:28 +01:00
|
|
|
unsigned dwid = 0;
|
2003-06-21 03:21:42 +02:00
|
|
|
while ((1U << dwid) < lwidth)
|
1999-11-14 21:24:28 +01:00
|
|
|
dwid += 1;
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet*rsig = right_->elaborate_net(des, scope, dwid, 0, 0, 0);
|
1999-11-14 21:24:28 +01:00
|
|
|
if (rsig == 0) return 0;
|
|
|
|
|
|
2000-01-02 22:45:31 +01:00
|
|
|
// Make the shift device itself, and the output
|
|
|
|
|
// NetNet. Connect the Result output pins to the osig signal
|
2003-02-26 02:29:24 +01:00
|
|
|
NetCLShift*gate = new NetCLShift(scope, scope->local_symbol(),
|
2005-02-19 03:43:38 +01:00
|
|
|
lwidth, rsig->vector_width(),
|
2004-06-30 04:16:26 +02:00
|
|
|
right_flag, signed_flag);
|
2005-02-19 03:43:38 +01:00
|
|
|
des->add_node(gate);
|
1999-11-14 21:24:28 +01:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2000-01-02 22:45:31 +01:00
|
|
|
NetNet::WIRE, lwidth);
|
2005-07-07 18:22:49 +02:00
|
|
|
osig->data_type( data_type );
|
1999-11-14 21:24:28 +01:00
|
|
|
osig->local_flag(true);
|
2005-02-19 03:43:38 +01:00
|
|
|
osig->set_signed(signed_flag);
|
1999-11-14 21:24:28 +01:00
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
connect(osig->pin(0), gate->pin_Result());
|
2000-01-02 22:45:31 +01:00
|
|
|
|
|
|
|
|
// Connect the lsig (the left expression) to the Data input,
|
2005-03-19 07:23:49 +01:00
|
|
|
// and pad it if necessary. The lwidth is the width of the
|
|
|
|
|
// NetCLShift gate, and the D input must match.
|
|
|
|
|
if (lsig->vector_width() < lwidth)
|
|
|
|
|
lsig = pad_to_width(des, lsig, lwidth);
|
|
|
|
|
|
|
|
|
|
assert(lsig->vector_width() <= lwidth);
|
2005-02-19 03:43:38 +01:00
|
|
|
connect(lsig->pin(0), gate->pin_Data());
|
1999-11-14 21:24:28 +01:00
|
|
|
|
2000-01-02 22:45:31 +01:00
|
|
|
// Connect the rsig (the shift amount expression) to the
|
|
|
|
|
// Distance input.
|
2005-02-19 03:43:38 +01:00
|
|
|
connect(rsig->pin(0), gate->pin_Distance());
|
1999-11-14 21:24:28 +01:00
|
|
|
|
2005-02-19 03:43:38 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Elaborate LPM_SHIFT: width="<<gate->width()
|
|
|
|
|
<< ", swidth="<< gate->width_dist() << endl;
|
|
|
|
|
}
|
1999-11-14 21:24:28 +01:00
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2002-03-09 03:10:22 +01:00
|
|
|
/*
|
|
|
|
|
* This method elaborates a call to a function in the context of a
|
2004-06-01 01:34:36 +02:00
|
|
|
* continuous assignment. The definition of the function contains a
|
|
|
|
|
* list of the ports, and an output port. The NetEUFunc that I create
|
|
|
|
|
* here has a port for all the input ports and the output port. The
|
|
|
|
|
* ports are connected by pins.
|
2002-03-09 03:10:22 +01:00
|
|
|
*/
|
2003-04-11 07:18:08 +02:00
|
|
|
NetNet* PECallFunction::elaborate_net(Design*des, NetScope*scope,
|
|
|
|
|
unsigned width,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2003-04-11 07:18:08 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
2002-03-09 03:10:22 +01:00
|
|
|
{
|
|
|
|
|
unsigned errors = 0;
|
|
|
|
|
unsigned func_pins = 0;
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (path_.front().name[0] == '$')
|
2006-06-18 06:15:50 +02:00
|
|
|
return elaborate_net_sfunc_(des, scope,
|
|
|
|
|
width, rise, fall, decay,
|
|
|
|
|
drive0, drive1);
|
2004-08-28 17:42:11 +02:00
|
|
|
|
2003-04-11 07:18:08 +02:00
|
|
|
|
2002-03-09 03:10:22 +01:00
|
|
|
/* Look up the function definition. */
|
|
|
|
|
NetFuncDef*def = des->find_function(scope, path_);
|
|
|
|
|
if (def == 0) {
|
|
|
|
|
cerr << get_line() << ": error: No function " << path_ <<
|
2007-06-02 05:42:12 +02:00
|
|
|
" in this context (" << scope_path(scope) << ")." << endl;
|
2002-03-09 03:10:22 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
assert(def);
|
|
|
|
|
|
|
|
|
|
NetScope*dscope = def->scope();
|
|
|
|
|
assert(dscope);
|
|
|
|
|
|
2004-06-01 01:34:36 +02:00
|
|
|
/* This must be a ufuction that returns a signal. */
|
|
|
|
|
assert(def->return_sig());
|
|
|
|
|
|
2002-03-09 03:10:22 +01:00
|
|
|
/* check the validity of the parameters. */
|
|
|
|
|
if (! check_call_matches_definition_(des, dscope))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Elaborate all the parameters of the function call,
|
|
|
|
|
and collect the resulting NetNet objects. All the
|
|
|
|
|
parameters take on the size of the target port. */
|
|
|
|
|
|
2004-06-01 01:34:36 +02:00
|
|
|
svector<NetNet*> eparms (def->port_count());
|
2002-03-09 03:10:22 +01:00
|
|
|
for (unsigned idx = 0 ; idx < eparms.count() ; idx += 1) {
|
2004-06-01 01:34:36 +02:00
|
|
|
const NetNet* port_reg = def->port(idx);
|
2002-03-09 03:10:22 +01:00
|
|
|
NetNet*tmp = parms_[idx]->elaborate_net(des, scope,
|
2005-03-18 03:56:03 +01:00
|
|
|
port_reg->vector_width(),
|
2002-03-09 03:10:22 +01:00
|
|
|
0, 0, 0,
|
|
|
|
|
Link::STRONG,
|
|
|
|
|
Link::STRONG);
|
|
|
|
|
if (tmp == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Unable to elaborate "
|
|
|
|
|
<< "port " << idx << " of call to " << path_ <<
|
|
|
|
|
"." << endl;
|
|
|
|
|
errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func_pins += tmp->pin_count();
|
|
|
|
|
eparms[idx] = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (errors > 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NetUserFunc*net = new NetUserFunc(scope,
|
2004-02-18 18:11:54 +01:00
|
|
|
scope->local_symbol(),
|
2002-03-09 03:10:22 +01:00
|
|
|
dscope);
|
|
|
|
|
des->add_node(net);
|
|
|
|
|
|
|
|
|
|
/* Create an output signal and connect it to the output pins
|
|
|
|
|
of the function net. */
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2002-03-09 03:10:22 +01:00
|
|
|
NetNet::WIRE,
|
2005-03-18 03:56:03 +01:00
|
|
|
def->return_sig()->vector_width());
|
2002-03-09 03:10:22 +01:00
|
|
|
osig->local_flag(true);
|
2006-05-01 22:47:58 +02:00
|
|
|
osig->data_type(def->return_sig()->data_type());
|
2002-03-09 03:10:22 +01:00
|
|
|
|
2005-03-18 03:56:03 +01:00
|
|
|
connect(net->pin(0), osig->pin(0));
|
2002-03-09 03:10:22 +01:00
|
|
|
|
|
|
|
|
/* Connect the parameter pins to the parameter expressions. */
|
|
|
|
|
for (unsigned idx = 0 ; idx < eparms.count() ; idx += 1) {
|
2004-06-01 01:34:36 +02:00
|
|
|
const NetNet* port = def->port(idx);
|
2002-03-09 03:10:22 +01:00
|
|
|
NetNet*cur = eparms[idx];
|
|
|
|
|
|
2005-03-18 03:56:03 +01:00
|
|
|
NetNet*tmp = pad_to_width(des, cur, port->vector_width());
|
2002-03-09 03:10:22 +01:00
|
|
|
|
2005-03-18 03:56:03 +01:00
|
|
|
connect(net->pin(idx+1), tmp->pin(0));
|
2002-03-09 03:10:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-18 06:15:50 +02:00
|
|
|
NetNet* PECallFunction::elaborate_net_sfunc_(Design*des, NetScope*scope,
|
|
|
|
|
unsigned width,
|
|
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
|
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
|
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
perm_string name = peek_tail_name(path_);
|
|
|
|
|
|
2006-06-18 06:15:50 +02:00
|
|
|
/* Handle the special case that the function call is to
|
|
|
|
|
$signed. This takes a single expression argument, and
|
|
|
|
|
forces it to be a signed result. Otherwise, it is as if the
|
|
|
|
|
$signed did not exist. */
|
2007-05-24 06:07:11 +02:00
|
|
|
if (strcmp(name, "$signed") == 0) {
|
2006-06-18 06:15:50 +02:00
|
|
|
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
|
|
|
|
cerr << get_line() << ": error: The $signed() function "
|
|
|
|
|
<< "takes exactly one(1) argument." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PExpr*expr = parms_[0];
|
|
|
|
|
NetNet*sub = expr->elaborate_net(des, scope, width, rise,
|
|
|
|
|
fall, decay, drive0, drive1);
|
|
|
|
|
sub->set_signed(true);
|
|
|
|
|
return sub;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* handle $unsigned like $signed */
|
2007-05-24 06:07:11 +02:00
|
|
|
if (strcmp(name, "$unsigned") == 0) {
|
2006-06-18 06:15:50 +02:00
|
|
|
if ((parms_.count() != 1) || (parms_[0] == 0)) {
|
|
|
|
|
cerr << get_line() << ": error: The $unsigned() function "
|
|
|
|
|
<< "takes exactly one(1) argument." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PExpr*expr = parms_[0];
|
|
|
|
|
NetNet*sub = expr->elaborate_net(des, scope, width, rise,
|
|
|
|
|
fall, decay, drive0, drive1);
|
|
|
|
|
sub->set_signed(false);
|
|
|
|
|
return sub;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
const struct sfunc_return_type*def = lookup_sys_func(name);
|
2006-06-18 06:15:50 +02:00
|
|
|
|
|
|
|
|
if (def == 0) {
|
|
|
|
|
cerr << get_line() << ": error: System function "
|
2007-05-24 06:07:11 +02:00
|
|
|
<< peek_tail_name(path_) << " not defined." << endl;
|
2006-06-18 06:15:50 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetSysFunc*net = new NetSysFunc(scope, scope->local_symbol(),
|
|
|
|
|
def, 1+parms_.count());
|
|
|
|
|
des->add_node(net);
|
|
|
|
|
net->set_line(*this);
|
|
|
|
|
|
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, def->wid);
|
|
|
|
|
osig->local_flag(true);
|
|
|
|
|
osig->data_type(def->type);
|
|
|
|
|
osig->set_line(*this);
|
|
|
|
|
|
|
|
|
|
connect(net->pin(0), osig->pin(0));
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
|
|
|
|
|
NetNet*tmp = parms_[idx]->elaborate_net(des, scope, 0,
|
|
|
|
|
0, 0, 0,
|
|
|
|
|
Link::STRONG, Link::STRONG);
|
|
|
|
|
if (tmp == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Unable to elaborate "
|
|
|
|
|
<< "port " << idx << " of call to " << path_ <<
|
|
|
|
|
"." << endl;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connect(net->pin(1+idx), tmp->pin(0));
|
|
|
|
|
}
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2002-03-09 03:10:22 +01:00
|
|
|
|
1999-12-02 05:08:10 +01:00
|
|
|
/*
|
|
|
|
|
* The concatenation operator, as a net, is a wide signal that is
|
|
|
|
|
* connected to all the pins of the elaborated expression nets.
|
|
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEConcat::elaborate_net(Design*des, NetScope*scope,
|
1999-12-02 05:08:10 +01:00
|
|
|
unsigned,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2000-05-07 06:37:55 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
1999-12-02 05:08:10 +01:00
|
|
|
{
|
|
|
|
|
svector<NetNet*>nets (parms_.count());
|
2005-01-30 06:20:38 +01:00
|
|
|
unsigned vector_width = 0;
|
1999-12-02 05:08:10 +01:00
|
|
|
unsigned errors = 0;
|
2000-10-08 06:59:36 +02:00
|
|
|
unsigned repeat = 1;
|
1999-12-02 05:08:10 +01:00
|
|
|
|
2005-01-30 06:20:38 +01:00
|
|
|
/* The repeat expression must evaluate to a compile-time
|
|
|
|
|
constant. This is used to generate the width of the
|
|
|
|
|
concatenation. */
|
1999-12-02 05:08:10 +01:00
|
|
|
if (repeat_) {
|
2006-06-02 06:48:49 +02:00
|
|
|
NetExpr*etmp = elab_and_eval(des, scope, repeat_, -1);
|
2005-01-29 01:37:06 +01:00
|
|
|
assert(etmp);
|
|
|
|
|
NetEConst*erep = dynamic_cast<NetEConst*>(etmp);
|
|
|
|
|
|
|
|
|
|
if (erep == 0) {
|
2007-02-27 07:10:16 +01:00
|
|
|
cerr << get_line() << ": error: Unable to "
|
1999-12-02 05:08:10 +01:00
|
|
|
<< "evaluate constant repeat expression." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-29 01:37:06 +01:00
|
|
|
repeat = erep->value().as_ulong();
|
|
|
|
|
delete etmp;
|
1999-12-02 05:08:10 +01:00
|
|
|
|
2000-10-08 06:59:36 +02:00
|
|
|
if (repeat == 0) {
|
2005-01-29 01:37:06 +01:00
|
|
|
cerr << get_line() << ": error: Concatenation epeat "
|
|
|
|
|
"may not be 0."
|
2000-10-08 06:59:36 +02:00
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
1999-12-02 05:08:10 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-30 06:20:38 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() <<": debug: PEConcat concat repeat="
|
|
|
|
|
<< repeat << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-23 05:31:28 +02:00
|
|
|
/* The operands of the concatenation must contain all
|
|
|
|
|
self-determined arguments. Set this flag to force an error
|
|
|
|
|
message if this is not the case. */
|
|
|
|
|
const bool save_flag = must_be_self_determined_flag;
|
|
|
|
|
must_be_self_determined_flag = true;
|
|
|
|
|
|
1999-12-02 05:08:10 +01:00
|
|
|
/* Elaborate the operands of the concatenation. */
|
|
|
|
|
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
2000-09-26 07:05:58 +02:00
|
|
|
|
2000-10-14 04:23:02 +02:00
|
|
|
if (parms_[idx] == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Empty expressions "
|
|
|
|
|
<< "not allowed in concatenations." << endl;
|
|
|
|
|
errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-26 07:05:58 +02:00
|
|
|
/* Look for the special case of an unsized number in a
|
|
|
|
|
concatenation expression. Mark this as an error, but
|
|
|
|
|
allow elaboration to continue to see if I can find
|
|
|
|
|
more errors. */
|
|
|
|
|
|
|
|
|
|
if (PENumber*tmp = dynamic_cast<PENumber*>(parms_[idx])) {
|
|
|
|
|
if (tmp->value().has_len() == false) {
|
|
|
|
|
cerr << get_line() << ": error: Number "
|
|
|
|
|
<< tmp->value() << " with indefinite size"
|
|
|
|
|
<< " in concatenation." << endl;
|
|
|
|
|
errors += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
nets[idx] = parms_[idx]->elaborate_net(des, scope, 0,
|
1999-12-02 05:08:10 +01:00
|
|
|
rise,fall,decay);
|
|
|
|
|
if (nets[idx] == 0)
|
|
|
|
|
errors += 1;
|
|
|
|
|
else
|
2005-01-30 06:20:38 +01:00
|
|
|
vector_width += nets[idx]->vector_width();
|
1999-12-02 05:08:10 +01:00
|
|
|
}
|
|
|
|
|
|
2003-09-23 05:31:28 +02:00
|
|
|
must_be_self_determined_flag = save_flag;
|
|
|
|
|
|
1999-12-02 05:08:10 +01:00
|
|
|
/* If any of the sub expressions failed to elaborate, then
|
|
|
|
|
delete all those that did and abort myself. */
|
|
|
|
|
if (errors) {
|
|
|
|
|
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
|
|
|
|
if (nets[idx]) delete nets[idx];
|
|
|
|
|
}
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-30 06:20:38 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() <<": debug: PEConcat concat collected "
|
|
|
|
|
<< "width=" << vector_width << ", repeat=" << repeat
|
|
|
|
|
<< " of " << nets.count() << " expressions." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetConcat*dev = new NetConcat(scope, scope->local_symbol(),
|
|
|
|
|
vector_width*repeat,
|
|
|
|
|
nets.count()*repeat);
|
|
|
|
|
dev->set_line(*this);
|
|
|
|
|
des->add_node(dev);
|
|
|
|
|
|
1999-12-02 05:08:10 +01:00
|
|
|
/* Make the temporary signal that connects to all the
|
|
|
|
|
operands, and connect it up. Scan the operands of the
|
|
|
|
|
concat operator from least significant to most significant,
|
2000-10-08 06:59:36 +02:00
|
|
|
which is opposite from how they are given in the list.
|
|
|
|
|
|
2007-03-22 17:08:14 +01:00
|
|
|
Allow for a repeat count other than 1 by repeating the
|
2000-10-08 06:59:36 +02:00
|
|
|
connect loop as many times as necessary. */
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2005-01-30 06:20:38 +01:00
|
|
|
NetNet::IMPLICIT, vector_width * repeat);
|
2005-07-07 18:22:49 +02:00
|
|
|
osig->data_type(IVL_VT_LOGIC);
|
2005-01-30 06:20:38 +01:00
|
|
|
|
|
|
|
|
connect(dev->pin(0), osig->pin(0));
|
|
|
|
|
|
|
|
|
|
unsigned cur_pin = 1;
|
|
|
|
|
for (unsigned rpt = 0; rpt < repeat ; rpt += 1) {
|
|
|
|
|
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
|
|
|
|
NetNet*cur = nets[nets.count()-idx-1];
|
|
|
|
|
connect(dev->pin(cur_pin++), cur->pin(0));
|
1999-12-02 05:08:10 +01:00
|
|
|
}
|
2005-01-30 06:20:38 +01:00
|
|
|
}
|
|
|
|
|
|
1999-12-02 05:08:10 +01:00
|
|
|
|
|
|
|
|
osig->local_flag(true);
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2002-04-23 05:53:59 +02:00
|
|
|
/*
|
2002-08-31 05:48:50 +02:00
|
|
|
* This private method handles the special case that we have a
|
2002-04-23 05:53:59 +02:00
|
|
|
* non-constant bit-select of an identifier. We already know that the
|
|
|
|
|
* signal that is represented is "sig".
|
|
|
|
|
*/
|
|
|
|
|
NetNet* PEIdent::elaborate_net_bitmux_(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*sig,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2002-04-23 05:53:59 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
|
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
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.sel == index_component_t::SEL_BIT);
|
|
|
|
|
ivl_assert(*this, index_tail.msb != 0);
|
|
|
|
|
ivl_assert(*this, index_tail.lsb == 0);
|
2006-02-02 03:43:57 +01:00
|
|
|
|
2002-04-23 05:53:59 +02:00
|
|
|
/* Elaborate the selector. */
|
2005-05-09 01:40:14 +02:00
|
|
|
NetNet*sel;
|
2002-04-23 05:53:59 +02:00
|
|
|
|
2005-05-09 01:40:14 +02:00
|
|
|
if (sig->msb() < sig->lsb()) {
|
2007-05-24 06:07:11 +02:00
|
|
|
NetExpr*sel_expr = index_tail.msb->elaborate_expr(des, scope, -1, false);
|
2005-05-09 01:40:14 +02:00
|
|
|
sel_expr = make_sub_expr(sig->lsb(), sel_expr);
|
|
|
|
|
if (NetExpr*tmp = sel_expr->eval_tree()) {
|
|
|
|
|
delete sel_expr;
|
|
|
|
|
sel_expr = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sel = sel_expr->synthesize(des);
|
2002-04-23 05:53:59 +02:00
|
|
|
|
2005-05-09 01:40:14 +02:00
|
|
|
} else if (sig->lsb() != 0) {
|
2007-05-24 06:07:11 +02:00
|
|
|
NetExpr*sel_expr = index_tail.msb->elaborate_expr(des, scope, -1,false);
|
2005-05-09 01:40:14 +02:00
|
|
|
sel_expr = make_add_expr(sel_expr, - sig->lsb());
|
|
|
|
|
if (NetExpr*tmp = sel_expr->eval_tree()) {
|
|
|
|
|
delete sel_expr;
|
|
|
|
|
sel_expr = tmp;
|
|
|
|
|
}
|
2002-08-31 05:48:50 +02:00
|
|
|
|
2005-05-09 01:40:14 +02:00
|
|
|
sel = sel_expr->synthesize(des);
|
2002-08-31 05:48:50 +02:00
|
|
|
|
|
|
|
|
} else {
|
2007-05-24 06:07:11 +02:00
|
|
|
sel = index_tail.msb->elaborate_net(des, scope, 0, 0, 0, 0);
|
2005-05-09 01:40:14 +02:00
|
|
|
}
|
2002-08-31 05:48:50 +02:00
|
|
|
|
2005-05-09 01:40:14 +02:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Create NetPartSelect "
|
|
|
|
|
<< "using signal " << sel->name() << " as selector"
|
|
|
|
|
<< endl;
|
2002-08-31 05:48:50 +02:00
|
|
|
}
|
2002-04-23 05:53:59 +02:00
|
|
|
|
2005-05-09 01:40:14 +02:00
|
|
|
/* Create a part select that takes a non-constant offset and a
|
|
|
|
|
width of 1. */
|
|
|
|
|
NetPartSelect*mux = new NetPartSelect(sig, sel, 1);
|
|
|
|
|
des->add_node(mux);
|
|
|
|
|
mux->set_line(*this);
|
2002-04-23 05:53:59 +02:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*out = new NetNet(scope, scope->local_symbol(),
|
2005-05-09 01:40:14 +02:00
|
|
|
NetNet::WIRE, 1);
|
2005-07-07 18:22:49 +02:00
|
|
|
out->data_type(sig->data_type());
|
2002-04-23 05:53:59 +02:00
|
|
|
|
2005-05-09 01:40:14 +02:00
|
|
|
connect(out->pin(0), mux->pin(0));
|
2002-04-23 05:53:59 +02:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEIdent::elaborate_net(Design*des, NetScope*scope,
|
1999-11-21 01:13:08 +01:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2000-05-07 06:37:55 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
1999-11-21 01:13:08 +01:00
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
ivl_assert(*this, scope);
|
|
|
|
|
|
|
|
|
|
const name_component_t&name_tail = path_.back();
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2003-09-19 05:50:12 +02:00
|
|
|
NetNet* sig = 0;
|
2007-05-24 06:07:11 +02:00
|
|
|
const NetExpr*par = 0;
|
2003-09-19 05:50:12 +02:00
|
|
|
NetEvent* eve = 0;
|
2003-03-11 00:40:53 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
symbol_search(des, scope, path_, sig, par, eve);
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2003-09-19 05:50:12 +02:00
|
|
|
/* If this is a parameter name, then create a constant node
|
|
|
|
|
that connects to a signal with the correct name. */
|
|
|
|
|
if (par != 0) {
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2003-09-19 05:50:12 +02:00
|
|
|
const NetEConst*pc = dynamic_cast<const NetEConst*>(par);
|
|
|
|
|
assert(pc);
|
|
|
|
|
verinum pvalue = pc->value();
|
2003-04-11 07:18:08 +02:00
|
|
|
|
2007-03-22 17:08:14 +01:00
|
|
|
/* If the desired lwidth is more than the width of the
|
2005-01-29 17:46:22 +01:00
|
|
|
constant value, extend the value to fit the desired
|
|
|
|
|
output. */
|
|
|
|
|
if (lwidth > pvalue.len()) {
|
2006-08-08 07:11:37 +02:00
|
|
|
verinum tmp ((uint64_t)0, lwidth);
|
2005-01-29 17:46:22 +01:00
|
|
|
for (unsigned idx = 0 ; idx < pvalue.len() ; idx += 1)
|
|
|
|
|
tmp.set(idx, pvalue.get(idx));
|
|
|
|
|
|
|
|
|
|
pvalue = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-08 04:43:26 +01:00
|
|
|
sig = new NetNet(scope, scope->local_symbol(),
|
2005-01-29 17:46:22 +01:00
|
|
|
NetNet::IMPLICIT, pvalue.len());
|
2005-09-19 17:21:09 +02:00
|
|
|
sig->set_line(*this);
|
|
|
|
|
sig->data_type(IVL_VT_LOGIC);
|
2003-09-19 05:50:12 +02:00
|
|
|
NetConst*cp = new NetConst(scope, scope->local_symbol(),
|
|
|
|
|
pvalue);
|
2005-09-19 17:21:09 +02:00
|
|
|
cp->set_line(*this);
|
2003-09-19 05:50:12 +02:00
|
|
|
des->add_node(cp);
|
|
|
|
|
for (unsigned idx = 0; idx < sig->pin_count(); idx += 1)
|
|
|
|
|
connect(sig->pin(idx), cp->pin(idx));
|
|
|
|
|
}
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2004-09-24 06:25:19 +02:00
|
|
|
/* Check for the error case that the name is not found, and it
|
|
|
|
|
is hierarchical. We can't just create a name in another
|
|
|
|
|
scope, it's just not allowed. */
|
2007-05-24 06:07:11 +02:00
|
|
|
if (sig == 0 && path_.size() != 1) {
|
2004-09-24 06:25:19 +02:00
|
|
|
cerr << get_line() << ": error: The hierarchical name "
|
|
|
|
|
<< path_ << " is undefined in "
|
2007-06-02 05:42:12 +02:00
|
|
|
<< scope_path(scope) << "." << endl;
|
2004-09-24 06:25:19 +02:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
pform_name_t tmp_path = path_;
|
|
|
|
|
tmp_path.pop_back();
|
2004-09-24 06:25:19 +02:00
|
|
|
|
2007-06-02 05:42:12 +02:00
|
|
|
list<hname_t> stmp_path = eval_scope_path(des, scope, tmp_path);
|
|
|
|
|
NetScope*tmp_scope = des->find_scope(scope, stmp_path);
|
2004-09-24 06:25:19 +02:00
|
|
|
if (tmp_scope == 0) {
|
|
|
|
|
cerr << get_line() << ": : I can't even find "
|
|
|
|
|
<< "the scope " << tmp_path << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-19 05:50:12 +02:00
|
|
|
/* Fallback, this may be an implicitly declared net. */
|
|
|
|
|
if (sig == 0) {
|
2004-06-13 06:56:53 +02:00
|
|
|
NetNet::Type nettype = scope->default_nettype();
|
2007-05-24 06:07:11 +02:00
|
|
|
sig = new NetNet(scope, name_tail.name,
|
2004-06-13 06:56:53 +02:00
|
|
|
nettype, 1);
|
2005-07-11 18:56:50 +02:00
|
|
|
sig->data_type(IVL_VT_LOGIC);
|
2000-03-17 22:50:25 +01:00
|
|
|
|
2004-06-13 06:56:53 +02:00
|
|
|
if (error_implicit || (nettype == NetNet::NONE)) {
|
2003-09-19 05:50:12 +02:00
|
|
|
cerr << get_line() << ": error: "
|
2007-06-02 05:42:12 +02:00
|
|
|
<< scope_path(scope) << "." << name_tail.name
|
2003-09-19 05:50:12 +02:00
|
|
|
<< " not defined in this scope." << endl;
|
|
|
|
|
des->errors += 1;
|
2002-04-22 02:53:39 +02:00
|
|
|
|
2003-09-19 05:50:12 +02:00
|
|
|
} else if (warn_implicit) {
|
|
|
|
|
cerr << get_line() << ": warning: implicit "
|
2007-06-02 05:42:12 +02:00
|
|
|
"definition of wire " << scope_path(scope)
|
2007-05-24 06:07:11 +02:00
|
|
|
<< "." << name_tail.name << "." << endl;
|
1999-11-21 01:13:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
ivl_assert(*this, sig);
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
/* Handle the case that this is an array elsewhere. */
|
|
|
|
|
if (sig->array_dimensions() > 0) {
|
2007-05-24 06:07:11 +02:00
|
|
|
if (name_tail.index.size() == 0) {
|
2007-02-01 04:14:33 +01:00
|
|
|
cerr << get_line() << ": error: Array " << sig->name()
|
|
|
|
|
<< " cannot be used here without an index." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
return elaborate_net_array_(des, scope, sig, lwidth,
|
|
|
|
|
rise, fall, decay,
|
|
|
|
|
drive0, drive1);
|
|
|
|
|
}
|
|
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
return elaborate_net_net_(des, scope, sig, lwidth,
|
|
|
|
|
rise, fall, decay, drive0, drive1);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet* PEIdent::process_select_(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*sig) const
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// If there are more index items then there are array
|
|
|
|
|
// dimensions, then treat them as word part selects. For
|
|
|
|
|
// example, if this is a memory array, then array dimensions
|
2007-07-06 21:46:32 +02:00
|
|
|
// is the first and part select the remainder.
|
2007-07-03 05:53:02 +02:00
|
|
|
unsigned midx, lidx;
|
|
|
|
|
if (! eval_part_select_(des, scope, sig, midx, lidx))
|
|
|
|
|
return sig;
|
|
|
|
|
|
|
|
|
|
unsigned part_count = midx-lidx+1;
|
|
|
|
|
|
|
|
|
|
// Maybe this is a full-width constant part select? If
|
|
|
|
|
// so, do nothing.
|
|
|
|
|
if (part_count == sig->vector_width())
|
|
|
|
|
return sig;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate part select"
|
|
|
|
|
<< " of word from " << sig->name() << "[base="<<lidx
|
|
|
|
|
<< " wid=" << part_count << "]" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetPartSelect*ps = new NetPartSelect(sig, lidx, part_count,
|
|
|
|
|
NetPartSelect::VP);
|
|
|
|
|
ps->set_line(*sig);
|
|
|
|
|
des->add_node(ps);
|
|
|
|
|
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, part_count-1, 0);
|
|
|
|
|
tmp->data_type( sig->data_type() );
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
connect(tmp->pin(0), ps->pin(0));
|
|
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet* PEIdent::elaborate_net_net_(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*sig, unsigned lwidth,
|
|
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
|
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
|
|
|
|
{
|
|
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
index_component_t::ctype_t use_sel = index_component_t::SEL_NONE;
|
|
|
|
|
if (!name_tail.index.empty())
|
|
|
|
|
use_sel = name_tail.index.back().sel;
|
|
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
switch (use_sel) {
|
|
|
|
|
case index_component_t::SEL_IDX_UP:
|
|
|
|
|
case index_component_t::SEL_IDX_DO:
|
|
|
|
|
return elaborate_net_net_idx_up_(des, scope, sig, lwidth,
|
|
|
|
|
rise, fall, decay, drive0, drive1);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/* Catch the case of a non-constant bit select. That should be
|
|
|
|
|
handled elsewhere. */
|
2007-05-24 06:07:11 +02:00
|
|
|
if (use_sel == index_component_t::SEL_BIT) {
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
2006-02-02 03:43:57 +01:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
verinum*mval = index_tail.msb->eval_const(des, scope);
|
1999-11-21 01:13:08 +01:00
|
|
|
if (mval == 0) {
|
2002-04-23 05:53:59 +02:00
|
|
|
return elaborate_net_bitmux_(des, scope, sig, rise,
|
|
|
|
|
fall, decay, drive0, drive1);
|
1999-11-21 01:13:08 +01:00
|
|
|
}
|
2002-04-23 05:53:59 +02:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
delete mval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned midx, lidx;
|
|
|
|
|
if (! eval_part_select_(des, scope, sig, midx, lidx))
|
|
|
|
|
return 0;
|
2000-07-15 07:13:43 +02:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
unsigned part_count = midx-lidx+1;
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
if (part_count != sig->vector_width()) {
|
2004-12-11 03:31:25 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate part select "
|
2005-01-09 21:16:00 +01:00
|
|
|
<< sig->name() << "[base="<<lidx
|
|
|
|
|
<< " wid=" << part_count << "]" << endl;
|
2004-12-11 03:31:25 +01:00
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
NetPartSelect*ps = new NetPartSelect(sig, lidx, part_count,
|
|
|
|
|
NetPartSelect::VP);
|
2004-12-11 03:31:25 +01:00
|
|
|
ps->set_line(*sig);
|
|
|
|
|
des->add_node(ps);
|
|
|
|
|
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2005-01-09 21:16:00 +01:00
|
|
|
NetNet::WIRE, part_count-1, 0);
|
2005-07-07 18:22:49 +02:00
|
|
|
tmp->data_type( sig->data_type() );
|
2004-12-11 03:31:25 +01:00
|
|
|
tmp->local_flag(true);
|
|
|
|
|
connect(tmp->pin(0), ps->pin(0));
|
|
|
|
|
|
1999-11-21 01:13:08 +01:00
|
|
|
sig = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
|
1999-11-21 01:13:08 +01:00
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
NetNet* PEIdent::elaborate_net_net_idx_up_(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*sig, unsigned lwidth,
|
|
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
|
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
2007-06-04 04:19:07 +02:00
|
|
|
{
|
2007-07-03 05:53:02 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
2007-06-04 04:19:07 +02:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
ivl_assert(*this, index_tail.lsb != 0);
|
|
|
|
|
ivl_assert(*this, index_tail.msb != 0);
|
2007-06-04 04:19:07 +02:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1);
|
2007-06-04 04:19:07 +02:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
unsigned long wid = 0;
|
|
|
|
|
calculate_up_do_width_(des, scope, wid);
|
2007-06-04 04:19:07 +02:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
bool down_flag = name_tail.index.back().sel==index_component_t::SEL_IDX_DO;
|
|
|
|
|
|
|
|
|
|
// Handle the special case that the base is constant as
|
|
|
|
|
// well. In this case it can be converted to a conventional
|
|
|
|
|
// part select.
|
|
|
|
|
if (NetEConst*base_c = dynamic_cast<NetEConst*> (base)) {
|
|
|
|
|
long lsv = base_c->value().as_long();
|
|
|
|
|
|
|
|
|
|
// convert from -: to +: form.
|
|
|
|
|
if (down_flag) lsv -= (wid-1);
|
|
|
|
|
|
|
|
|
|
// If the part select convers exactly the entire
|
|
|
|
|
// vector, then do not bother with it. Return the
|
|
|
|
|
// signal itself.
|
|
|
|
|
if (sig->sb_to_idx(lsv) == 0 && wid == sig->vector_width())
|
|
|
|
|
return sig;
|
|
|
|
|
|
|
|
|
|
NetPartSelect*sel = new NetPartSelect(sig, sig->sb_to_idx(lsv),
|
|
|
|
|
wid, NetPartSelect::VP);
|
|
|
|
|
sel->set_line(*this);
|
|
|
|
|
des->add_node(sel);
|
|
|
|
|
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->data_type(sig->data_type());
|
|
|
|
|
connect(tmp->pin(0), sel->pin(0));
|
|
|
|
|
|
|
|
|
|
delete base;
|
|
|
|
|
return tmp;
|
2007-06-04 04:19:07 +02:00
|
|
|
}
|
|
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
if (sig->msb() > sig->lsb()) {
|
|
|
|
|
long offset = sig->lsb();
|
|
|
|
|
if (down_flag)
|
|
|
|
|
offset += (wid-1);
|
|
|
|
|
if (offset != 0)
|
|
|
|
|
base = make_add_expr(base, 0-offset);
|
|
|
|
|
} else {
|
|
|
|
|
long vwid = sig->lsb() - sig->msb() + 1;
|
|
|
|
|
long offset = sig->msb();
|
|
|
|
|
if (down_flag)
|
|
|
|
|
offset += (wid-1);
|
|
|
|
|
base = make_sub_expr(vwid-offset-wid, base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetPartSelect*sel = new NetPartSelect(sig, base->synthesize(des), wid);
|
|
|
|
|
sel->set_line(*this);
|
|
|
|
|
des->add_node(sel);
|
2007-06-04 04:19:07 +02:00
|
|
|
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2007-07-03 05:53:02 +02:00
|
|
|
NetNet::WIRE, wid);
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->data_type(sig->data_type());
|
|
|
|
|
connect(tmp->pin(0), sel->pin(0));
|
2007-06-04 04:19:07 +02:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
delete base;
|
2007-06-04 04:19:07 +02:00
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
NetNet* PEIdent::elaborate_net_array_(Design*des, NetScope*scope,
|
|
|
|
|
NetNet*sig, unsigned lwidth,
|
|
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
|
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
1999-11-21 01:13:08 +01:00
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
ivl_assert(*this, name_tail.index.size() >= 1);
|
|
|
|
|
const index_component_t&index_head = name_tail.index.front();
|
|
|
|
|
ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT);
|
|
|
|
|
ivl_assert(*this, index_head.msb != 0);
|
|
|
|
|
ivl_assert(*this, index_head.lsb == 0);
|
|
|
|
|
|
2007-06-04 04:19:07 +02:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_line() << ": debug: elaborate array "
|
|
|
|
|
<< name_tail.name << " with index " << index_head << endl;
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
NetExpr*index_ex = elab_and_eval(des, scope, index_head.msb, -1);
|
2007-01-16 06:44:14 +01:00
|
|
|
if (index_ex == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (NetEConst*index_co = dynamic_cast<NetEConst*> (index_ex)) {
|
|
|
|
|
long index = index_co->value().as_long();
|
|
|
|
|
|
|
|
|
|
if (!sig->array_index_is_valid(index)) {
|
|
|
|
|
// Oops! The index is a constant known to be
|
|
|
|
|
// outside the array. Change the expression to a
|
|
|
|
|
// constant X vector.
|
|
|
|
|
verinum xxx (verinum::Vx, sig->vector_width());
|
|
|
|
|
NetConst*con_n = new NetConst(scope, scope->local_symbol(), xxx);
|
|
|
|
|
des->add_node(con_n);
|
|
|
|
|
con_n->set_line(*index_co);
|
|
|
|
|
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::IMPLICIT, sig->vector_width());
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->data_type(sig->data_type());
|
|
|
|
|
connect(tmp->pin(0), con_n->pin(0));
|
|
|
|
|
return tmp;
|
2005-04-08 06:52:31 +02:00
|
|
|
}
|
2003-10-20 03:44:28 +02:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
assert(sig->array_index_is_valid(index));
|
|
|
|
|
index = sig->array_index_to_address(index);
|
2003-10-20 03:44:28 +02:00
|
|
|
|
2007-01-19 05:26:24 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate word "
|
|
|
|
|
<< index << " of " << sig->name() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::IMPLICIT, sig->vector_width());
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->local_flag(true);
|
2007-01-19 05:26:24 +01:00
|
|
|
tmp->data_type(sig->data_type());
|
2007-01-16 06:44:14 +01:00
|
|
|
connect(tmp->pin(0), sig->pin(index));
|
2007-06-04 04:19:07 +02:00
|
|
|
|
|
|
|
|
// If there are more indices then needed to get to the
|
|
|
|
|
// word, then there is a part/bit select for the word.
|
|
|
|
|
if (name_tail.index.size() > sig->array_dimensions())
|
|
|
|
|
tmp = process_select_(des, scope, tmp);
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
return tmp;
|
|
|
|
|
}
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
unsigned selwid = index_ex->expr_width();
|
|
|
|
|
|
|
|
|
|
NetArrayDq*mux = new NetArrayDq(scope, scope->local_symbol(),
|
|
|
|
|
sig, selwid);
|
|
|
|
|
mux->set_line(*this);
|
|
|
|
|
des->add_node(mux);
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
// Adjust the expression to calculate the canonical offset?
|
|
|
|
|
if (long array_base = sig->array_first()) {
|
|
|
|
|
index_ex = make_add_expr(index_ex, 0-array_base);
|
|
|
|
|
}
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
NetNet*index_net = index_ex->synthesize(des);
|
|
|
|
|
connect(mux->pin_Address(), index_net->pin(0));
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::IMPLICIT, sig->vector_width());
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
tmp->data_type(sig->data_type());
|
|
|
|
|
connect(tmp->pin(0), mux->pin_Result());
|
2007-06-04 04:19:07 +02:00
|
|
|
#if 0
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
// If there are more index items then there are array
|
|
|
|
|
// dimensions, then treat them as word part selects. For
|
|
|
|
|
// example, if this is a memory array, then array dimensions
|
|
|
|
|
// is 1 and
|
2007-04-17 06:18:10 +02:00
|
|
|
unsigned midx, lidx;
|
|
|
|
|
if (eval_part_select_(des, scope, sig, midx, lidx)) do {
|
|
|
|
|
|
|
|
|
|
unsigned part_count = midx-lidx+1;
|
|
|
|
|
|
|
|
|
|
// Maybe this is a full-width constant part select? If
|
|
|
|
|
// so, do nothing.
|
|
|
|
|
if (part_count == sig->vector_width())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate part select"
|
|
|
|
|
<< " of word from " << sig->name() << "[base="<<lidx
|
|
|
|
|
<< " wid=" << part_count << "]" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetPartSelect*ps = new NetPartSelect(sig, lidx, part_count,
|
|
|
|
|
NetPartSelect::VP);
|
|
|
|
|
ps->set_line(*sig);
|
|
|
|
|
des->add_node(ps);
|
|
|
|
|
|
|
|
|
|
NetNet*tmp2 = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, part_count-1, 0);
|
|
|
|
|
tmp2->data_type( tmp->data_type() );
|
|
|
|
|
tmp2->local_flag(true);
|
|
|
|
|
connect(tmp2->pin(0), ps->pin(0));
|
|
|
|
|
|
|
|
|
|
tmp = tmp2;
|
|
|
|
|
} while (0);
|
2007-06-04 04:19:07 +02:00
|
|
|
#else
|
|
|
|
|
if (name_tail.index.size() > sig->array_dimensions())
|
|
|
|
|
tmp = process_select_(des, scope, sig);
|
2007-04-17 06:18:10 +02:00
|
|
|
|
2007-06-04 04:19:07 +02:00
|
|
|
#endif
|
2007-01-16 06:44:14 +01:00
|
|
|
return tmp;
|
1999-11-21 01:13:08 +01:00
|
|
|
}
|
|
|
|
|
|
2002-11-09 20:20:48 +01:00
|
|
|
/*
|
|
|
|
|
* The concatenation is also OK an an l-value. This method elaborates
|
2004-12-11 03:31:25 +01:00
|
|
|
* it as a structural l-value. The return values is the *input* net of
|
|
|
|
|
* the l-value, which may feed via part selects to the final
|
|
|
|
|
* destination. The caller can connect gate outputs to this signal to
|
|
|
|
|
* make the l-value connections.
|
2002-11-09 20:20:48 +01:00
|
|
|
*/
|
2006-04-28 06:28:35 +02:00
|
|
|
NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope,
|
|
|
|
|
bool implicit_net_ok,
|
|
|
|
|
bool bidirectional_flag) const
|
2002-11-09 20:20:48 +01:00
|
|
|
{
|
|
|
|
|
assert(scope);
|
|
|
|
|
|
|
|
|
|
svector<NetNet*>nets (parms_.count());
|
2004-12-11 03:31:25 +01:00
|
|
|
unsigned width = 0;
|
2002-11-09 20:20:48 +01:00
|
|
|
unsigned errors = 0;
|
|
|
|
|
|
|
|
|
|
if (repeat_) {
|
|
|
|
|
cerr << get_line() << ": sorry: I do not know how to"
|
|
|
|
|
" elaborate repeat concatenation nets." << endl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Elaborate the operands of the concatenation. */
|
|
|
|
|
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
2003-01-19 01:35:39 +01:00
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate subexpression "
|
|
|
|
|
<< idx << " of " << nets.count() << " l-values: "
|
|
|
|
|
<< *parms_[idx] << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-19 01:35:39 +01:00
|
|
|
if (parms_[idx] == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Empty expressions "
|
|
|
|
|
<< "not allowed in concatenations." << endl;
|
|
|
|
|
errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-28 06:28:35 +02:00
|
|
|
if (bidirectional_flag) {
|
|
|
|
|
nets[idx] = parms_[idx]->elaborate_bi_net(des, scope);
|
|
|
|
|
} else {
|
|
|
|
|
nets[idx] = parms_[idx]->elaborate_lnet(des, scope,
|
|
|
|
|
implicit_net_ok);
|
|
|
|
|
}
|
2002-11-09 20:20:48 +01:00
|
|
|
if (nets[idx] == 0)
|
|
|
|
|
errors += 1;
|
|
|
|
|
else
|
2004-12-11 03:31:25 +01:00
|
|
|
width += nets[idx]->vector_width();
|
2002-11-09 20:20:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If any of the sub expressions failed to elaborate, then
|
|
|
|
|
delete all those that did and abort myself. */
|
|
|
|
|
if (errors) {
|
|
|
|
|
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
|
|
|
|
if (nets[idx]) delete nets[idx];
|
|
|
|
|
}
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make the temporary signal that connects to all the
|
|
|
|
|
operands, and connect it up. Scan the operands of the
|
2004-12-11 03:31:25 +01:00
|
|
|
concat operator from most significant to least significant,
|
|
|
|
|
which is the order they are given in the concat list. */
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
2004-12-11 03:31:25 +01:00
|
|
|
NetNet::IMPLICIT, width);
|
|
|
|
|
|
2006-04-30 07:17:48 +02:00
|
|
|
/* Assume that the data types of the nets are all the same, so
|
|
|
|
|
we can take the data type of any, the first will do. */
|
|
|
|
|
osig->data_type(nets[0]->data_type());
|
2007-02-05 02:42:31 +01:00
|
|
|
osig->local_flag(true);
|
|
|
|
|
osig->set_line(*this);
|
2004-12-11 03:31:25 +01:00
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Generating part selects "
|
|
|
|
|
<< "to connect input l-value to subexpressions."
|
|
|
|
|
<< endl;
|
2002-11-09 20:20:48 +01:00
|
|
|
}
|
|
|
|
|
|
2006-04-28 06:28:35 +02:00
|
|
|
NetPartSelect::dir_t part_dir = bidirectional_flag
|
|
|
|
|
? NetPartSelect::BI
|
|
|
|
|
: NetPartSelect::VP;
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
|
|
|
|
|
unsigned wid = nets[idx]->vector_width();
|
|
|
|
|
unsigned off = width - wid;
|
2006-04-28 06:28:35 +02:00
|
|
|
NetPartSelect*ps = new NetPartSelect(osig, off, wid, part_dir);
|
2004-12-11 03:31:25 +01:00
|
|
|
des->add_node(ps);
|
|
|
|
|
|
|
|
|
|
connect(ps->pin(1), osig->pin(0));
|
|
|
|
|
connect(ps->pin(0), nets[idx]->pin(0));
|
|
|
|
|
|
|
|
|
|
assert(wid <= width);
|
|
|
|
|
width -= wid;
|
|
|
|
|
}
|
|
|
|
|
assert(width == 0);
|
|
|
|
|
|
2006-05-01 22:47:58 +02:00
|
|
|
osig->data_type(nets[0]->data_type());
|
2002-11-09 20:20:48 +01:00
|
|
|
osig->local_flag(true);
|
|
|
|
|
return osig;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-28 06:28:35 +02:00
|
|
|
NetNet* PEConcat::elaborate_lnet(Design*des, NetScope*scope,
|
|
|
|
|
bool implicit_net_ok) const
|
|
|
|
|
{
|
|
|
|
|
return elaborate_lnet_common_(des, scope, implicit_net_ok, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet* PEConcat::elaborate_bi_net(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
return elaborate_lnet_common_(des, scope, true, true);
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-07 18:22:49 +02:00
|
|
|
/*
|
|
|
|
|
* Elaborate a number as a NetConst object.
|
|
|
|
|
*/
|
|
|
|
|
NetNet* PEFNumber::elaborate_net(Design*des, NetScope*scope,
|
|
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2005-07-07 18:22:49 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
|
|
|
|
{
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Elaborate real literal node, "
|
|
|
|
|
<< "value=" << value() << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetLiteral*obj = new NetLiteral(scope, scope->local_symbol(), value());
|
|
|
|
|
obj->set_line(*this);
|
|
|
|
|
des->add_node(obj);
|
|
|
|
|
|
|
|
|
|
NetNet*net = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, 1);
|
|
|
|
|
net->data_type(IVL_VT_REAL);
|
|
|
|
|
net->local_flag(true);
|
|
|
|
|
|
|
|
|
|
connect(net->pin(0), obj->pin(0));
|
|
|
|
|
return net;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
/*
|
|
|
|
|
* A private method to create an implicit net.
|
|
|
|
|
*/
|
|
|
|
|
NetNet* PEIdent::make_implicit_net_(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
NetNet::Type nettype = scope->default_nettype();
|
|
|
|
|
NetNet*sig = 0;
|
|
|
|
|
|
|
|
|
|
if (!error_implicit && nettype!=NetNet::NONE) {
|
2007-05-24 06:07:11 +02:00
|
|
|
sig = new NetNet(scope, peek_tail_name(path_),
|
2005-08-06 19:58:16 +02:00
|
|
|
NetNet::IMPLICIT, 1);
|
|
|
|
|
/* Implicit nets are always scalar logic. */
|
|
|
|
|
sig->data_type(IVL_VT_LOGIC);
|
|
|
|
|
|
|
|
|
|
if (warn_implicit) {
|
|
|
|
|
cerr << get_line() << ": warning: implicit "
|
2007-06-02 05:42:12 +02:00
|
|
|
"definition of wire logic " << scope_path(scope)
|
2007-05-24 06:07:11 +02:00
|
|
|
<< "." << peek_tail_name(path_) << "." << endl;
|
2005-08-06 19:58:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
cerr << get_line() << ": error: Net " << path_
|
|
|
|
|
<< " is not defined in this context." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/*
|
|
|
|
|
* This private method evaluates the part selects (if any) for the
|
|
|
|
|
* signal. The sig argument is the NetNet already located for the
|
|
|
|
|
* PEIdent name. The midx and lidx arguments are loaded with the
|
|
|
|
|
* results, which may be the whole vector, or a single bit, or
|
|
|
|
|
* anything in between. The values are in canonical indices.
|
|
|
|
|
*/
|
|
|
|
|
bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig,
|
|
|
|
|
unsigned&midx, unsigned&lidx) const
|
|
|
|
|
{
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
// Only treat as part/bit selects any index that is beyond the
|
|
|
|
|
// word selects for an array. This is not an array, then
|
|
|
|
|
// dimensions==0 and any index is treated as a select.
|
|
|
|
|
if (name_tail.index.size() <= sig->array_dimensions()) {
|
|
|
|
|
midx = sig->vector_width()-1;
|
|
|
|
|
lidx = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
|
|
|
|
|
const index_component_t&index_tail = name_tail.index.back();
|
|
|
|
|
|
|
|
|
|
switch (index_tail.sel) {
|
2006-04-24 07:15:07 +02:00
|
|
|
default:
|
|
|
|
|
cerr << get_line() << ": internal error: "
|
2007-05-24 06:07:11 +02:00
|
|
|
<< "Unexpected sel_ value = " << index_tail.sel << endl;
|
|
|
|
|
ivl_assert(*this, 0);
|
2006-04-24 07:15:07 +02:00
|
|
|
break;
|
2005-01-09 21:16:00 +01:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
case index_component_t::SEL_IDX_DO:
|
|
|
|
|
case index_component_t::SEL_IDX_UP: {
|
|
|
|
|
NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1);
|
2006-04-24 07:15:07 +02:00
|
|
|
NetEConst*tmp = dynamic_cast<NetEConst*>(tmp_ex);
|
2007-07-03 05:53:02 +02:00
|
|
|
ivl_assert(*this, tmp);
|
2005-01-09 21:16:00 +01:00
|
|
|
|
2006-04-24 07:15:07 +02:00
|
|
|
long midx_val = tmp->value().as_long();
|
|
|
|
|
midx = sig->sb_to_idx(midx_val);
|
|
|
|
|
delete tmp_ex;
|
2005-01-09 21:16:00 +01:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
/* The width (a constant) is calculated here. */
|
|
|
|
|
unsigned long wid = 0;
|
|
|
|
|
bool flag = calculate_up_do_width_(des, scope, wid);
|
|
|
|
|
if (! flag)
|
|
|
|
|
return false;
|
2006-04-24 07:15:07 +02:00
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
if (index_tail.sel == index_component_t::SEL_IDX_UP)
|
2006-04-24 07:15:07 +02:00
|
|
|
lidx = sig->sb_to_idx(midx_val+wid-1);
|
|
|
|
|
else
|
|
|
|
|
lidx = sig->sb_to_idx(midx_val-wid+1);
|
|
|
|
|
|
|
|
|
|
if (midx < lidx) {
|
|
|
|
|
long tmp = midx;
|
|
|
|
|
midx = lidx;
|
|
|
|
|
lidx = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
case index_component_t::SEL_PART: {
|
2006-04-24 07:15:07 +02:00
|
|
|
|
2007-07-03 05:53:02 +02:00
|
|
|
long msb, lsb;
|
|
|
|
|
bool flag = calculate_parts_(des, scope, msb, lsb);
|
|
|
|
|
#if 0
|
2007-05-24 06:07:11 +02:00
|
|
|
NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1);
|
2006-04-24 07:15:07 +02:00
|
|
|
NetEConst*tmp = dynamic_cast<NetEConst*>(tmp_ex);
|
|
|
|
|
assert(tmp);
|
|
|
|
|
|
|
|
|
|
long midx_val = tmp->value().as_long();
|
|
|
|
|
midx = sig->sb_to_idx(midx_val);
|
|
|
|
|
delete tmp_ex;
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
tmp_ex = elab_and_eval(des, scope, index_tail.lsb, -1);
|
2006-04-24 07:15:07 +02:00
|
|
|
tmp = dynamic_cast<NetEConst*>(tmp_ex);
|
2006-12-08 04:43:26 +01:00
|
|
|
if (tmp == 0) {
|
|
|
|
|
cerr << get_line() << ": internal error: "
|
|
|
|
|
<< "lsb expression is not constant?: "
|
2007-05-24 06:07:11 +02:00
|
|
|
<< *tmp_ex << ", " << *index_tail.lsb << endl;
|
2006-12-08 04:43:26 +01:00
|
|
|
}
|
2006-04-24 07:15:07 +02:00
|
|
|
assert(tmp);
|
|
|
|
|
|
|
|
|
|
long lidx_val = tmp->value().as_long();
|
|
|
|
|
lidx = sig->sb_to_idx(lidx_val);
|
|
|
|
|
delete tmp_ex;
|
2007-07-03 05:53:02 +02:00
|
|
|
#endif
|
|
|
|
|
lidx = sig->sb_to_idx(lsb);
|
|
|
|
|
midx = sig->sb_to_idx(msb);
|
2006-04-24 07:15:07 +02:00
|
|
|
/* Detect reversed indices of a part select. */
|
|
|
|
|
if (lidx > midx) {
|
|
|
|
|
cerr << get_line() << ": error: Part select "
|
2007-07-03 05:53:02 +02:00
|
|
|
<< sig->name() << "[" << msb << ":"
|
|
|
|
|
<< lsb << "] indices reversed." << endl;
|
2006-04-24 07:15:07 +02:00
|
|
|
cerr << get_line() << ": : Did you mean "
|
2007-07-03 05:53:02 +02:00
|
|
|
<< sig->name() << "[" << lsb << ":"
|
|
|
|
|
<< msb << "]?" << endl;
|
2006-04-24 07:15:07 +02:00
|
|
|
unsigned tmp = midx;
|
|
|
|
|
midx = lidx;
|
|
|
|
|
lidx = tmp;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Detect a part select out of range. */
|
|
|
|
|
if (midx >= sig->vector_width()) {
|
|
|
|
|
cerr << get_line() << ": error: Part select "
|
2007-07-03 05:53:02 +02:00
|
|
|
<< sig->name() << "[" << msb << ":"
|
|
|
|
|
<< lsb << "] out of range." << endl;
|
2006-04-24 07:15:07 +02:00
|
|
|
midx = sig->vector_width() - 1;
|
|
|
|
|
lidx = 0;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
case index_component_t::SEL_BIT:
|
|
|
|
|
if (name_tail.index.size() > sig->array_dimensions()) {
|
2007-06-04 04:19:07 +02:00
|
|
|
verinum*mval = index_tail.msb->eval_const(des, scope);
|
2006-04-24 07:15:07 +02:00
|
|
|
if (mval == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Index of " << path_ <<
|
|
|
|
|
" needs to be constant in this context." <<
|
|
|
|
|
endl;
|
|
|
|
|
cerr << get_line() << ": : Index expression is: "
|
2007-06-04 04:19:07 +02:00
|
|
|
<< *index_tail.msb << endl;
|
2007-06-12 06:05:45 +02:00
|
|
|
cerr << get_line() << ": : Context scope is: "
|
|
|
|
|
<< scope_path(scope) << endl;
|
2006-04-24 07:15:07 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
assert(mval);
|
|
|
|
|
|
|
|
|
|
midx = sig->sb_to_idx(mval->as_long());
|
|
|
|
|
if (midx >= sig->vector_width()) {
|
|
|
|
|
cerr << get_line() << ": error: Index " << sig->name()
|
|
|
|
|
<< "[" << mval->as_long() << "] out of range."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
midx = 0;
|
|
|
|
|
}
|
|
|
|
|
lidx = midx;
|
|
|
|
|
|
|
|
|
|
} else {
|
2007-05-24 06:07:11 +02:00
|
|
|
cerr << get_line() << ": internal error: "
|
|
|
|
|
<< "Bit select " << path_ << endl;
|
|
|
|
|
ivl_assert(*this, 0);
|
2006-04-24 07:15:07 +02:00
|
|
|
midx = sig->vector_width() - 1;
|
|
|
|
|
lidx = 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2005-01-09 21:16:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-21 01:13:08 +01:00
|
|
|
/*
|
2005-08-06 19:58:16 +02:00
|
|
|
* This is the common code for l-value nets and bi-directional
|
|
|
|
|
* nets. There is very little that is different between the two cases,
|
|
|
|
|
* so most of the work for both is done here.
|
1999-11-21 01:13:08 +01:00
|
|
|
*/
|
2005-08-06 19:58:16 +02:00
|
|
|
NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
|
|
|
|
|
bool implicit_net_ok,
|
|
|
|
|
bool bidirectional_flag) const
|
1999-11-21 01:13:08 +01:00
|
|
|
{
|
2003-09-19 05:50:12 +02:00
|
|
|
assert(scope);
|
|
|
|
|
|
|
|
|
|
NetNet* sig = 0;
|
|
|
|
|
const NetExpr*par = 0;
|
|
|
|
|
NetEvent* eve = 0;
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
symbol_search(des, scope, path_, sig, par, eve);
|
2003-09-19 05:50:12 +02:00
|
|
|
|
|
|
|
|
if (eve != 0) {
|
|
|
|
|
cerr << get_line() << ": error: named events (" << path_
|
|
|
|
|
<< ") cannot be l-values in continuous "
|
|
|
|
|
<< "assignments." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-21 01:13:08 +01:00
|
|
|
if (sig == 0) {
|
|
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
if (implicit_net_ok) {
|
2002-11-09 20:20:48 +01:00
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
sig = make_implicit_net_(des, scope);
|
|
|
|
|
if (sig == 0)
|
|
|
|
|
return 0;
|
2002-11-09 20:20:48 +01:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
cerr << get_line() << ": error: Net " << path_
|
|
|
|
|
<< " is not defined in this context." << endl;
|
2003-08-05 05:01:58 +02:00
|
|
|
des->errors += 1;
|
2002-11-09 20:20:48 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
1999-11-21 01:13:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(sig);
|
|
|
|
|
|
|
|
|
|
/* Don't allow registers as assign l-values. */
|
|
|
|
|
if (sig->type() == NetNet::REG) {
|
2002-12-06 04:08:19 +01:00
|
|
|
cerr << get_line() << ": error: reg " << sig->name()
|
2003-08-05 05:01:58 +02:00
|
|
|
<< "; cannot be driven by primitives"
|
|
|
|
|
<< " or continuous assignment." << endl;
|
|
|
|
|
des->errors += 1;
|
1999-11-21 01:13:08 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-11-10 03:08:49 +01:00
|
|
|
if (sig->port_type() == NetNet::PINPUT) {
|
2002-12-06 04:08:19 +01:00
|
|
|
cerr << get_line() << ": warning: L-value ``"
|
|
|
|
|
<< sig->name() << "'' is also an input port." << endl;
|
|
|
|
|
cerr << sig->get_line() << ": warning: input "
|
|
|
|
|
<< sig->name() << "; is coerced to inout." << endl;
|
2001-11-10 03:08:49 +01:00
|
|
|
sig->port_type(NetNet::PINOUT);
|
|
|
|
|
}
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
// Default part select is the entire word.
|
|
|
|
|
unsigned midx = sig->vector_width()-1, lidx = 0;
|
|
|
|
|
// The default word select is the first.
|
|
|
|
|
unsigned widx = 0;
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
const name_component_t&name_tail = path_.back();
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
if (sig->array_dimensions() > 0) {
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
ivl_assert(*this, !name_tail.index.empty());
|
|
|
|
|
|
|
|
|
|
const index_component_t&index_head = name_tail.index.front();
|
2007-11-06 22:01:11 +01:00
|
|
|
if (index_head.sel == index_component_t::SEL_PART) {
|
|
|
|
|
cerr << get_line() << ": error: cannot perform a part "
|
|
|
|
|
<< "select on array " << sig->name() << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2007-05-24 06:07:11 +02:00
|
|
|
ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT);
|
|
|
|
|
|
|
|
|
|
NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1);
|
2007-01-16 06:44:14 +01:00
|
|
|
NetEConst*tmp = dynamic_cast<NetEConst*>(tmp_ex);
|
|
|
|
|
assert(tmp);
|
|
|
|
|
|
|
|
|
|
long widx_val = tmp->value().as_long();
|
|
|
|
|
widx = sig->array_index_to_address(widx_val);
|
|
|
|
|
delete tmp_ex;
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_line() << ": debug: Use [" << widx << "]"
|
|
|
|
|
<< " to index l-value array." << endl;
|
|
|
|
|
|
2007-05-24 06:07:11 +02:00
|
|
|
} else if (!name_tail.index.empty()) {
|
2007-01-16 06:44:14 +01:00
|
|
|
if (! eval_part_select_(des, scope, sig, midx, lidx))
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2000-12-01 03:55:37 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
unsigned subnet_wid = midx-lidx+1;
|
|
|
|
|
|
2007-01-16 06:44:14 +01:00
|
|
|
if (sig->pin_count() > 1) {
|
|
|
|
|
assert(widx < sig->pin_count());
|
|
|
|
|
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
sig->type(), sig->vector_width());
|
|
|
|
|
tmp->set_line(*this);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
connect(sig->pin(widx), tmp->pin(0));
|
|
|
|
|
sig = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/* If the desired l-value vector is narrower then the
|
|
|
|
|
signal itself, then use a NetPartSelect node to
|
|
|
|
|
arrange for connection to the desired bits. All this
|
|
|
|
|
can be skipped if the desired with matches the
|
|
|
|
|
original vector. */
|
|
|
|
|
|
|
|
|
|
if (subnet_wid != sig->vector_width()) {
|
2005-08-06 19:58:16 +02:00
|
|
|
/* If we are processing a tran or inout, then the
|
|
|
|
|
partselect is bi-directional. Otherwise, it is a
|
|
|
|
|
Part-to-Vector select. */
|
|
|
|
|
NetPartSelect::dir_t part_dir;
|
|
|
|
|
if (bidirectional_flag)
|
|
|
|
|
part_dir = NetPartSelect::BI;
|
|
|
|
|
else
|
|
|
|
|
part_dir = NetPartSelect::PV;
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
if (debug_elaborate)
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Elaborate lnet part select "
|
|
|
|
|
<< sig->name()
|
|
|
|
|
<< "[base=" << lidx
|
|
|
|
|
<< " wid=" << subnet_wid <<"]"
|
|
|
|
|
<< endl;
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
NetNet*subsig = new NetNet(sig->scope(),
|
|
|
|
|
sig->scope()->local_symbol(),
|
|
|
|
|
NetNet::WIRE, subnet_wid);
|
2005-07-15 06:13:25 +02:00
|
|
|
subsig->data_type( sig->data_type() );
|
2007-02-05 02:42:31 +01:00
|
|
|
subsig->local_flag(true);
|
|
|
|
|
subsig->set_line(*this);
|
1999-11-21 01:13:08 +01:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
NetPartSelect*sub = new NetPartSelect(sig, lidx, subnet_wid,
|
2005-08-06 19:58:16 +02:00
|
|
|
part_dir);
|
2005-01-09 21:16:00 +01:00
|
|
|
des->add_node(sub);
|
|
|
|
|
connect(sub->pin(0), subsig->pin(0));
|
2002-06-19 06:20:03 +02:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
sig = subsig;
|
1999-11-21 01:13:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-06 19:58:16 +02:00
|
|
|
/*
|
|
|
|
|
* Identifiers in continuous assignment l-values are limited to wires
|
|
|
|
|
* and that ilk. Detect registers and memories here and report errors.
|
|
|
|
|
*/
|
|
|
|
|
NetNet* PEIdent::elaborate_lnet(Design*des, NetScope*scope,
|
|
|
|
|
bool implicit_net_ok) const
|
|
|
|
|
{
|
|
|
|
|
return elaborate_lnet_common_(des, scope, implicit_net_ok, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
|
|
|
|
return elaborate_lnet_common_(des, scope, true, true);
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-16 06:05:15 +02:00
|
|
|
/*
|
|
|
|
|
* This method is used to elaborate identifiers that are ports to a
|
|
|
|
|
* scope. The scope is presumed to be that of the module that has the
|
2005-01-09 21:16:00 +01:00
|
|
|
* port. This elaboration is done inside the module, and is only done
|
|
|
|
|
* to PEIdent objects. This method is used by elaboration of a module
|
|
|
|
|
* instantiation (PGModule::elaborate_mod_) to get NetNet objects for
|
|
|
|
|
* the port.
|
2000-05-16 06:05:15 +02:00
|
|
|
*/
|
|
|
|
|
NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const
|
|
|
|
|
{
|
2001-12-03 05:47:14 +01:00
|
|
|
NetNet*sig = des->find_signal(scope, path_);
|
2000-05-16 06:05:15 +02:00
|
|
|
if (sig == 0) {
|
2001-12-03 05:47:14 +01:00
|
|
|
cerr << get_line() << ": error: no wire/reg " << path_
|
2007-06-02 05:42:12 +02:00
|
|
|
<< " in module " << scope_path(scope) << "." << endl;
|
2000-05-16 06:05:15 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/* Check the port_type of the signal to make sure it is really
|
|
|
|
|
a port, and its direction is resolved. */
|
2000-08-18 06:38:57 +02:00
|
|
|
switch (sig->port_type()) {
|
|
|
|
|
case NetNet::PINPUT:
|
|
|
|
|
case NetNet::POUTPUT:
|
|
|
|
|
case NetNet::PINOUT:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* If the name matches, but the signal is not a port,
|
|
|
|
|
then the user declared the object but there is no
|
|
|
|
|
matching input/output/inout declaration. */
|
|
|
|
|
|
|
|
|
|
case NetNet::NOT_A_PORT:
|
2001-12-03 05:47:14 +01:00
|
|
|
cerr << get_line() << ": error: signal " << path_ << " in"
|
2007-06-02 05:42:12 +02:00
|
|
|
<< " module " << scope_path(scope) << " is not a port." << endl;
|
2000-08-18 06:38:57 +02:00
|
|
|
cerr << get_line() << ": : Are you missing an input/"
|
|
|
|
|
<< "output/inout declaration?" << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* This should not happen. A PWire can only become
|
|
|
|
|
PIMPLICIT if this is a udp reg port, and the make_udp
|
|
|
|
|
function should turn it into an output.... I think. */
|
|
|
|
|
|
|
|
|
|
case NetNet::PIMPLICIT:
|
2001-12-03 05:47:14 +01:00
|
|
|
cerr << get_line() << ": internal error: signal " << path_
|
2007-06-02 05:42:12 +02:00
|
|
|
<< " in module " << scope_path(scope) << " is left as "
|
2000-08-18 06:38:57 +02:00
|
|
|
<< "port type PIMPLICIT." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
unsigned midx;
|
|
|
|
|
unsigned lidx;
|
2000-05-16 06:05:15 +02:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/* Evaluate the part/bit select expressions, to get the part
|
|
|
|
|
select of the signal that attaches to the port. Also handle
|
|
|
|
|
range and direction checking here. */
|
2000-05-16 06:05:15 +02:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
if (! eval_part_select_(des, scope, sig, midx, lidx))
|
|
|
|
|
return 0;
|
2002-06-19 06:20:03 +02:00
|
|
|
|
2000-05-16 06:05:15 +02:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
unsigned swid = midx - lidx + 1;
|
2000-05-16 06:05:15 +02:00
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
if (swid < sig->vector_width()) {
|
|
|
|
|
cerr << get_line() << ": XXXX: Forgot to implement part select"
|
|
|
|
|
<< " of signal port." << endl;
|
2000-05-16 06:05:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-05 22:45:19 +01:00
|
|
|
/*
|
|
|
|
|
* Elaborate a number as a NetConst object.
|
2005-07-07 18:22:49 +02:00
|
|
|
*
|
|
|
|
|
* The code assumes that the result is IVL_VT_LOGIC.
|
1999-11-05 22:45:19 +01:00
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PENumber::elaborate_net(Design*des, NetScope*scope,
|
1999-11-05 22:45:19 +01:00
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2000-05-07 06:37:55 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
1999-11-05 22:45:19 +01:00
|
|
|
{
|
2000-05-02 02:58:11 +02:00
|
|
|
|
2000-01-11 05:20:57 +01:00
|
|
|
/* If we are constrained by a l-value size, then just make a
|
|
|
|
|
number constant with the correct size and set as many bits
|
2005-07-07 18:22:49 +02:00
|
|
|
in that constant as make sense. Pad excess with
|
|
|
|
|
zeros. Also, assume that numbers are meant to be logic
|
|
|
|
|
type. */
|
|
|
|
|
|
2000-01-11 05:20:57 +01:00
|
|
|
if (lwidth > 0) {
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*net = new NetNet(scope, scope->local_symbol(),
|
2000-01-11 05:20:57 +01:00
|
|
|
NetNet::IMPLICIT, lwidth);
|
2005-07-07 18:22:49 +02:00
|
|
|
net->data_type(IVL_VT_LOGIC);
|
2000-01-11 05:20:57 +01:00
|
|
|
net->local_flag(true);
|
2004-06-30 04:16:26 +02:00
|
|
|
net->set_signed(value_->has_sign());
|
2000-01-11 05:20:57 +01:00
|
|
|
|
2007-10-16 05:17:06 +02:00
|
|
|
verinum num = pad_to_width(*value_, lwidth);
|
|
|
|
|
if (num.len() > lwidth)
|
|
|
|
|
num = verinum(num, lwidth);
|
2000-01-11 05:20:57 +01:00
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
NetConst*tmp = new NetConst(scope, scope->local_symbol(), num);
|
|
|
|
|
tmp->pin(0).drive0(drive0);
|
|
|
|
|
tmp->pin(0).drive1(drive1);
|
|
|
|
|
connect(net->pin(0), tmp->pin(0));
|
2000-01-11 05:20:57 +01:00
|
|
|
|
|
|
|
|
des->add_node(tmp);
|
|
|
|
|
return net;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the number has a length, then use that to size the
|
|
|
|
|
number. Generate a constant object of exactly the user
|
|
|
|
|
specified size. */
|
|
|
|
|
if (value_->has_len()) {
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*net = new NetNet(scope, scope->local_symbol(),
|
2000-01-11 05:20:57 +01:00
|
|
|
NetNet::IMPLICIT, value_->len());
|
2005-07-07 18:22:49 +02:00
|
|
|
net->data_type(IVL_VT_LOGIC);
|
2000-01-11 05:20:57 +01:00
|
|
|
net->local_flag(true);
|
2004-06-30 04:16:26 +02:00
|
|
|
net->set_signed(value_->has_sign());
|
2003-03-06 01:28:41 +01:00
|
|
|
NetConst*tmp = new NetConst(scope, scope->local_symbol(),
|
2001-10-28 02:14:53 +01:00
|
|
|
*value_);
|
2004-12-11 03:31:25 +01:00
|
|
|
connect(net->pin(0), tmp->pin(0));
|
2000-01-11 05:20:57 +01:00
|
|
|
|
|
|
|
|
des->add_node(tmp);
|
|
|
|
|
return net;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* None of the above tight constraints are present, so make a
|
|
|
|
|
plausible choice for the width. Try to reduce the width as
|
|
|
|
|
much as possible by eliminating high zeros of unsigned
|
|
|
|
|
numbers. */
|
2003-09-23 05:31:28 +02:00
|
|
|
|
|
|
|
|
if (must_be_self_determined_flag) {
|
|
|
|
|
cerr << get_line() << ": error: No idea how wide to make "
|
|
|
|
|
<< "the unsized constant " << *value_ << "." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-05 22:45:19 +01:00
|
|
|
unsigned width = value_->len();
|
2000-01-11 05:20:57 +01:00
|
|
|
|
|
|
|
|
if (value_->has_sign() && (value_->get(width-1) == verinum::V0)) {
|
|
|
|
|
|
|
|
|
|
/* If the number is signed, but known to be positive,
|
|
|
|
|
then reduce it down as if it were unsigned. */
|
|
|
|
|
while (width > 1) {
|
|
|
|
|
if (value_->get(width-1) != verinum::V0)
|
|
|
|
|
break;
|
|
|
|
|
width -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (value_->has_sign() == false) {
|
|
|
|
|
while ( (width > 1) && (value_->get(width-1) == verinum::V0))
|
|
|
|
|
width -= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verinum num (verinum::V0, width);
|
|
|
|
|
for (unsigned idx = 0 ; idx < width ; idx += 1)
|
|
|
|
|
num.set(idx, value_->get(idx));
|
1999-11-05 22:45:19 +01:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*net = new NetNet(scope, scope->local_symbol(),
|
1999-11-05 22:45:19 +01:00
|
|
|
NetNet::IMPLICIT, width);
|
2005-07-07 18:22:49 +02:00
|
|
|
net->data_type(IVL_VT_LOGIC);
|
1999-11-05 22:45:19 +01:00
|
|
|
net->local_flag(true);
|
2003-03-06 01:28:41 +01:00
|
|
|
NetConst*tmp = new NetConst(scope, scope->local_symbol(), num);
|
2004-12-11 03:31:25 +01:00
|
|
|
connect(net->pin(0), tmp->pin(0));
|
1999-11-05 22:45:19 +01:00
|
|
|
|
1999-12-17 04:38:46 +01:00
|
|
|
des->add_node(tmp);
|
1999-11-05 22:45:19 +01:00
|
|
|
return net;
|
|
|
|
|
}
|
|
|
|
|
|
2001-12-30 22:32:03 +01:00
|
|
|
/*
|
|
|
|
|
* A string is a NetEConst node that is made of the ASCII bits of the
|
|
|
|
|
* string instead of the bits of a number. In fact, a string is just
|
|
|
|
|
* another numeric notation.
|
|
|
|
|
*/
|
|
|
|
|
NetNet* PEString::elaborate_net(Design*des, NetScope*scope,
|
|
|
|
|
unsigned lwidth,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2001-12-30 22:32:03 +01:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
|
|
|
|
{
|
|
|
|
|
unsigned strbits = strlen(text_) * 8;
|
|
|
|
|
NetNet*net;
|
|
|
|
|
|
|
|
|
|
/* If we are constrained by a l-value size, then just make a
|
|
|
|
|
number constant with the correct size and set as many bits
|
|
|
|
|
in that constant as make sense. Pad excess with zeros. */
|
|
|
|
|
if (lwidth > 0) {
|
2003-03-06 01:28:41 +01:00
|
|
|
net = new NetNet(scope, scope->local_symbol(),
|
2001-12-30 22:32:03 +01:00
|
|
|
NetNet::IMPLICIT, lwidth);
|
|
|
|
|
|
|
|
|
|
} else {
|
2003-03-06 01:28:41 +01:00
|
|
|
net = new NetNet(scope, scope->local_symbol(),
|
2001-12-30 22:32:03 +01:00
|
|
|
NetNet::IMPLICIT, strbits);
|
|
|
|
|
}
|
2006-05-01 22:47:58 +02:00
|
|
|
net->data_type(IVL_VT_BOOL);
|
2001-12-30 22:32:03 +01:00
|
|
|
net->local_flag(true);
|
|
|
|
|
|
|
|
|
|
/* Make a verinum that is filled with the 0 pad. */
|
2005-03-13 02:26:48 +01:00
|
|
|
verinum num(verinum::V0, net->vector_width());
|
2001-12-30 22:32:03 +01:00
|
|
|
|
|
|
|
|
unsigned idx;
|
|
|
|
|
for (idx = 0 ; idx < num.len() && idx < strbits; idx += 1) {
|
|
|
|
|
char byte = text_[strbits/8 - 1 - idx/8];
|
|
|
|
|
char mask = 1<<(idx%8);
|
|
|
|
|
num.set(idx, (byte & mask)? verinum::V1 : verinum::V0);
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetConst*tmp = new NetConst(scope, scope->local_symbol(), num);
|
2005-03-13 02:26:48 +01:00
|
|
|
tmp->set_line(*this);
|
2001-12-30 22:32:03 +01:00
|
|
|
des->add_node(tmp);
|
2005-03-13 02:26:48 +01:00
|
|
|
|
|
|
|
|
connect(net->pin(0), tmp->pin(0));
|
|
|
|
|
|
2001-12-30 22:32:03 +01:00
|
|
|
return net;
|
|
|
|
|
}
|
1999-11-05 22:45:19 +01:00
|
|
|
|
1999-11-04 04:53:26 +01:00
|
|
|
/*
|
|
|
|
|
* Elaborate the ternary operator in a netlist by creating a LPM_MUX
|
2000-05-03 23:21:36 +02:00
|
|
|
* with width matching the result, size == 2 and 1 select input. These
|
|
|
|
|
* expressions come from code like:
|
|
|
|
|
*
|
|
|
|
|
* res = test ? a : b;
|
|
|
|
|
*
|
|
|
|
|
* The res has the width requested of this method, and the a and b
|
|
|
|
|
* expressions have their own similar widths. The test expression is
|
|
|
|
|
* only a single bit wide. The output from this function is a NetNet
|
|
|
|
|
* object the width of the <res> expression and connected to the
|
|
|
|
|
* Result pins of the LPM_MUX device. Any width not covered by the
|
|
|
|
|
* width of the mux is padded with a NetConst device.
|
1999-11-04 04:53:26 +01:00
|
|
|
*/
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PETernary::elaborate_net(Design*des, NetScope*scope,
|
1999-11-04 04:53:26 +01:00
|
|
|
unsigned width,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2000-05-07 06:37:55 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
1999-11-04 04:53:26 +01:00
|
|
|
{
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* expr_sig = expr_->elaborate_net(des, scope, 0, 0, 0, 0);
|
|
|
|
|
NetNet* tru_sig = tru_->elaborate_net(des, scope, width, 0, 0, 0);
|
|
|
|
|
NetNet* fal_sig = fal_->elaborate_net(des, scope, width, 0, 0, 0);
|
1999-11-04 04:53:26 +01:00
|
|
|
if (expr_sig == 0 || tru_sig == 0 || fal_sig == 0) {
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-15 02:42:02 +02:00
|
|
|
/* The type of the true and false expressions must
|
|
|
|
|
match. These become the type of the resulting
|
|
|
|
|
expression. */
|
|
|
|
|
|
|
|
|
|
ivl_variable_type_t expr_type = tru_sig->data_type();
|
|
|
|
|
|
|
|
|
|
if (tru_sig->data_type() != fal_sig->data_type()) {
|
|
|
|
|
cerr << get_line() << ": error: True and False clauses of"
|
|
|
|
|
<< " ternary expression have differnt types." << endl;
|
|
|
|
|
cerr << get_line() << ": : True clause is "
|
|
|
|
|
<< tru_sig->data_type() << ", false clause is "
|
|
|
|
|
<< fal_sig->data_type() << "." << endl;
|
|
|
|
|
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
expr_type = IVL_VT_NO_TYPE;
|
|
|
|
|
|
|
|
|
|
} else if (expr_type == IVL_VT_NO_TYPE) {
|
|
|
|
|
cerr << get_line() << ": internal error: True and false "
|
|
|
|
|
<< "clauses of ternary both have NO TYPE." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
2000-09-07 23:28:51 +02:00
|
|
|
|
|
|
|
|
/* The natural width of the expression is the width of the
|
|
|
|
|
largest condition. Normally they should be the same size,
|
|
|
|
|
but if we do not get a size from the context, or the
|
|
|
|
|
expressions resist, we need to cope. */
|
2005-02-12 07:25:40 +01:00
|
|
|
unsigned iwidth = tru_sig->vector_width();
|
2005-07-15 02:42:02 +02:00
|
|
|
if (fal_sig->vector_width() > iwidth)
|
2005-02-12 07:25:40 +01:00
|
|
|
iwidth = fal_sig->vector_width();
|
2000-09-07 23:28:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* If the width is not passed from the context, then take the
|
|
|
|
|
widest result as our width. */
|
2000-05-03 23:21:36 +02:00
|
|
|
if (width == 0)
|
2000-09-07 23:28:51 +02:00
|
|
|
width = iwidth;
|
2000-05-03 23:21:36 +02:00
|
|
|
|
2000-05-26 07:26:11 +02:00
|
|
|
/* If the expression has width, then generate a boolean result
|
|
|
|
|
by connecting an OR gate to calculate the truth value of
|
2000-09-07 23:28:51 +02:00
|
|
|
the result. In the end, the result needs to be a single bit. */
|
2005-02-12 07:25:40 +01:00
|
|
|
if (expr_sig->vector_width() > 1) {
|
|
|
|
|
NetUReduce*log = new NetUReduce(scope, scope->local_symbol(),
|
|
|
|
|
NetUReduce::OR,
|
|
|
|
|
expr_sig->vector_width());
|
|
|
|
|
log->set_line(*this);
|
|
|
|
|
des->add_node(log);
|
|
|
|
|
connect(log->pin(1), expr_sig->pin(0));
|
2000-05-26 07:26:11 +02:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2002-06-19 06:20:03 +02:00
|
|
|
NetNet::IMPLICIT, 1);
|
2006-05-01 22:47:58 +02:00
|
|
|
tmp->data_type(IVL_VT_LOGIC);
|
2000-05-26 07:26:11 +02:00
|
|
|
tmp->local_flag(true);
|
2005-02-12 07:25:40 +01:00
|
|
|
connect(log->pin(0), tmp->pin(0));
|
2000-05-26 07:26:11 +02:00
|
|
|
|
|
|
|
|
expr_sig = tmp;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-12 07:25:40 +01:00
|
|
|
assert(expr_sig->vector_width() == 1);
|
1999-11-04 04:53:26 +01:00
|
|
|
|
2000-05-03 23:21:36 +02:00
|
|
|
/* This is the width of the LPM_MUX device that I'm about to
|
|
|
|
|
create. It may be smaller then the desired output, but I'll
|
2006-04-28 07:09:51 +02:00
|
|
|
handle padding below. Note that in principle the
|
|
|
|
|
alternatives should be padded to the output width first,
|
|
|
|
|
but it is more efficient to pad them only just enough to
|
|
|
|
|
prevent loss, and do the finished padding later.
|
2000-05-03 23:21:36 +02:00
|
|
|
|
2000-11-04 06:06:04 +01:00
|
|
|
Create a NetNet object wide enough to hold the
|
|
|
|
|
result. Also, pad the result values (if necessary) so that
|
|
|
|
|
the mux inputs can be fully connected. */
|
2000-05-03 23:21:36 +02:00
|
|
|
|
2000-09-07 23:28:51 +02:00
|
|
|
unsigned dwidth = (iwidth > width)? width : iwidth;
|
2000-05-03 23:21:36 +02:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*sig = new NetNet(scope, scope->local_symbol(),
|
2006-04-28 07:09:51 +02:00
|
|
|
NetNet::WIRE, dwidth);
|
2005-07-15 02:42:02 +02:00
|
|
|
sig->data_type(expr_type);
|
1999-11-04 04:53:26 +01:00
|
|
|
sig->local_flag(true);
|
|
|
|
|
|
2005-02-12 07:25:40 +01:00
|
|
|
if (fal_sig->vector_width() < dwidth)
|
2001-02-15 07:59:35 +01:00
|
|
|
fal_sig = pad_to_width(des, fal_sig, dwidth);
|
2000-11-04 06:06:04 +01:00
|
|
|
|
2005-02-12 07:25:40 +01:00
|
|
|
if (tru_sig->vector_width() < dwidth)
|
2001-02-15 07:59:35 +01:00
|
|
|
tru_sig = pad_to_width(des, tru_sig, dwidth);
|
2000-11-04 06:06:04 +01:00
|
|
|
|
2006-11-26 07:29:16 +01:00
|
|
|
if (dwidth < fal_sig->vector_width())
|
|
|
|
|
fal_sig = crop_to_width(des, fal_sig, dwidth);
|
|
|
|
|
|
|
|
|
|
if (dwidth < tru_sig->vector_width())
|
|
|
|
|
tru_sig = crop_to_width(des, tru_sig, dwidth);
|
2000-05-03 23:21:36 +02:00
|
|
|
|
|
|
|
|
/* Make the device and connect its outputs to the osig and
|
|
|
|
|
inputs to the tru and false case nets. Also connect the
|
2000-09-07 23:28:51 +02:00
|
|
|
selector bit to the sel input.
|
|
|
|
|
|
|
|
|
|
The inputs are the 0 (false) connected to fal_sig and 1
|
2000-11-04 06:06:04 +01:00
|
|
|
(true) connected to tru_sig. */
|
2000-05-03 23:21:36 +02:00
|
|
|
|
2003-02-26 02:29:24 +01:00
|
|
|
NetMux*mux = new NetMux(scope, scope->local_symbol(), dwidth, 2, 1);
|
2005-02-12 07:25:40 +01:00
|
|
|
connect(mux->pin_Sel(), expr_sig->pin(0));
|
1999-11-04 04:53:26 +01:00
|
|
|
|
2002-08-21 04:28:03 +02:00
|
|
|
/* Connect the data inputs. */
|
2005-02-12 07:25:40 +01:00
|
|
|
connect(mux->pin_Data(0), fal_sig->pin(0));
|
|
|
|
|
connect(mux->pin_Data(1), tru_sig->pin(0));
|
1999-11-06 00:36:31 +01:00
|
|
|
|
2002-08-21 04:28:03 +02:00
|
|
|
/* If there are non-zero output delays, then create bufz
|
|
|
|
|
devices to carry the propagation delays. Otherwise, just
|
|
|
|
|
connect the result to the output. */
|
|
|
|
|
if (rise || fall || decay) {
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
2002-08-21 04:28:03 +02:00
|
|
|
NetNet::WIRE, dwidth);
|
2007-02-01 20:06:06 +01:00
|
|
|
tmp->data_type(expr_type);
|
|
|
|
|
tmp->local_flag(true);
|
2002-08-21 04:28:03 +02:00
|
|
|
|
2005-02-12 07:25:40 +01:00
|
|
|
NetBUFZ*tmpz = new NetBUFZ(scope, scope->local_symbol(), dwidth);
|
|
|
|
|
tmpz->rise_time(rise);
|
|
|
|
|
tmpz->fall_time(fall);
|
|
|
|
|
tmpz->decay_time(decay);
|
|
|
|
|
tmpz->pin(0).drive0(drive0);
|
|
|
|
|
tmpz->pin(0).drive1(drive1);
|
2002-08-21 04:28:03 +02:00
|
|
|
|
2005-02-12 07:25:40 +01:00
|
|
|
connect(mux->pin_Result(), tmp->pin(0));
|
|
|
|
|
connect(tmp->pin(0), tmpz->pin(1));
|
|
|
|
|
connect(sig->pin(0), tmpz->pin(0));
|
2002-08-21 04:28:03 +02:00
|
|
|
|
2005-02-12 07:25:40 +01:00
|
|
|
des->add_node(tmpz);
|
2002-08-21 04:28:03 +02:00
|
|
|
|
|
|
|
|
} else {
|
2005-02-12 07:25:40 +01:00
|
|
|
connect(mux->pin_Result(), sig->pin(0));
|
2002-08-21 04:28:03 +02:00
|
|
|
}
|
2000-09-07 23:28:51 +02:00
|
|
|
|
|
|
|
|
/* If the MUX device result is too narrow to fill out the
|
2005-02-12 07:25:40 +01:00
|
|
|
desired result, pad with zeros... */
|
2006-04-28 07:09:51 +02:00
|
|
|
assert(dwidth <= width);
|
2000-05-03 23:21:36 +02:00
|
|
|
|
1999-11-06 00:36:31 +01:00
|
|
|
des->add_node(mux);
|
|
|
|
|
|
2006-04-28 07:09:51 +02:00
|
|
|
/* If the MUX device results is too narrow to fill out the
|
|
|
|
|
desired result, then pad it. It is OK to have a too-narrow
|
2007-02-26 20:49:48 +01:00
|
|
|
result here because the dwidth choice is >= the width of
|
|
|
|
|
both alternatives. Thus, padding here is equivalent to
|
2006-04-28 07:09:51 +02:00
|
|
|
padding inside, and is cheaper. */
|
|
|
|
|
if (dwidth < width)
|
|
|
|
|
sig = pad_to_width(des, sig, width);
|
|
|
|
|
|
1999-11-06 00:36:31 +01:00
|
|
|
return sig;
|
1999-11-04 04:53:26 +01:00
|
|
|
}
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* PEUnary::elaborate_net(Design*des, NetScope*scope,
|
2000-01-02 20:39:03 +01:00
|
|
|
unsigned width,
|
2006-01-02 06:33:19 +01:00
|
|
|
const NetExpr* rise,
|
|
|
|
|
const NetExpr* fall,
|
|
|
|
|
const NetExpr* decay,
|
2000-05-07 06:37:55 +02:00
|
|
|
Link::strength_t drive0,
|
|
|
|
|
Link::strength_t drive1) const
|
2000-01-02 20:39:03 +01:00
|
|
|
{
|
2000-05-02 02:58:11 +02:00
|
|
|
|
2001-01-24 03:52:30 +01:00
|
|
|
// Some unary operands allow the operand to be
|
|
|
|
|
// self-determined, and some do not.
|
|
|
|
|
unsigned owidth = 0;
|
|
|
|
|
switch (op_) {
|
|
|
|
|
case '~':
|
|
|
|
|
case '-':
|
|
|
|
|
owidth = width;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-24 17:22:23 +02:00
|
|
|
NetNet* sig = 0;
|
2001-01-25 03:05:16 +01:00
|
|
|
NetLogic*gate;
|
|
|
|
|
|
2005-02-03 05:56:20 +01:00
|
|
|
|
2001-01-25 03:05:16 +01:00
|
|
|
// Handle the special case of a 2's complement of a constant
|
|
|
|
|
// value. This can be reduced to a no-op on a precalculated
|
|
|
|
|
// result.
|
|
|
|
|
if (op_ == '-') do {
|
2005-01-30 06:20:38 +01:00
|
|
|
// TODO: Should replace this with a call to
|
|
|
|
|
// elab_and_eval. Possibly blend this with the rest of
|
|
|
|
|
// the elaboration as well.
|
2001-11-07 05:01:59 +01:00
|
|
|
verinum*val = expr_->eval_const(des, scope);
|
2001-01-25 03:05:16 +01:00
|
|
|
if (val == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
2002-08-14 05:57:27 +02:00
|
|
|
if (width == 0)
|
|
|
|
|
width = val->len();
|
|
|
|
|
|
|
|
|
|
assert(width > 0);
|
2003-03-06 01:28:41 +01:00
|
|
|
sig = new NetNet(scope, scope->local_symbol(),
|
2001-01-25 03:05:16 +01:00
|
|
|
NetNet::WIRE, width);
|
2005-07-07 18:22:49 +02:00
|
|
|
sig->data_type(IVL_VT_LOGIC);
|
2001-01-25 03:05:16 +01:00
|
|
|
sig->local_flag(true);
|
|
|
|
|
|
2002-08-14 05:57:27 +02:00
|
|
|
/* Take the 2s complement by taking the 1s complement
|
|
|
|
|
and adding 1. */
|
2005-01-30 06:20:38 +01:00
|
|
|
verinum tmp (v_not(*val), width);
|
2002-08-14 05:57:27 +02:00
|
|
|
verinum one (1UL, width);
|
2005-01-30 06:20:38 +01:00
|
|
|
tmp = verinum(tmp + one, width);
|
|
|
|
|
tmp.has_sign(val->has_sign());
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetConst*con = new NetConst(scope, scope->local_symbol(), tmp);
|
2005-01-30 06:20:38 +01:00
|
|
|
connect(sig->pin(0), con->pin(0));
|
|
|
|
|
|
|
|
|
|
if (debug_elaborate) {
|
|
|
|
|
cerr << get_line() << ": debug: Replace expression "
|
|
|
|
|
<< *this << " with constant " << tmp << "."<<endl;
|
|
|
|
|
}
|
2001-01-25 03:05:16 +01:00
|
|
|
|
2005-01-30 06:20:38 +01:00
|
|
|
delete val;
|
2001-01-25 03:05:16 +01:00
|
|
|
des->add_node(con);
|
|
|
|
|
return sig;
|
2005-01-30 06:20:38 +01:00
|
|
|
|
2001-01-25 03:05:16 +01:00
|
|
|
} while (0);
|
|
|
|
|
|
2001-11-08 06:15:50 +01:00
|
|
|
NetNet* sub_sig = expr_->elaborate_net(des, scope, owidth, 0, 0, 0);
|
2000-01-02 20:39:03 +01:00
|
|
|
if (sub_sig == 0) {
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
assert(sub_sig);
|
|
|
|
|
|
2004-06-24 17:22:23 +02:00
|
|
|
bool reduction=false;
|
2005-02-03 05:56:20 +01:00
|
|
|
NetUReduce::TYPE rtype = NetUReduce::NONE;
|
2004-06-24 17:22:23 +02:00
|
|
|
|
2000-01-02 20:39:03 +01:00
|
|
|
switch (op_) {
|
|
|
|
|
case '~': // Bitwise NOT
|
2003-03-06 01:28:41 +01:00
|
|
|
sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE,
|
2005-01-30 06:20:38 +01:00
|
|
|
sub_sig->vector_width());
|
2005-07-07 18:22:49 +02:00
|
|
|
sig->data_type(sub_sig->data_type());
|
2000-01-02 20:39:03 +01:00
|
|
|
sig->local_flag(true);
|
2005-01-30 06:20:38 +01:00
|
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
|
|
|
|
2, NetLogic::NOT, sub_sig->vector_width());
|
|
|
|
|
gate->set_line(*this);
|
|
|
|
|
des->add_node(gate);
|
|
|
|
|
gate->rise_time(rise);
|
|
|
|
|
gate->fall_time(fall);
|
|
|
|
|
gate->decay_time(decay);
|
|
|
|
|
|
|
|
|
|
connect(gate->pin(1), sub_sig->pin(0));
|
|
|
|
|
connect(gate->pin(0), sig->pin(0));
|
2000-01-02 20:39:03 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'N': // Reduction NOR
|
|
|
|
|
case '!': // Reduction NOT
|
2005-02-03 05:56:20 +01:00
|
|
|
reduction=true; rtype = NetUReduce::NOR; break;
|
2000-01-02 20:39:03 +01:00
|
|
|
case '&': // Reduction AND
|
2005-02-03 05:56:20 +01:00
|
|
|
reduction=true; rtype = NetUReduce::AND; break;
|
2000-01-02 20:39:03 +01:00
|
|
|
case '|': // Reduction OR
|
2005-02-03 05:56:20 +01:00
|
|
|
reduction=true; rtype = NetUReduce::OR; break;
|
2000-01-02 20:39:03 +01:00
|
|
|
case '^': // Reduction XOR
|
2005-02-03 05:56:20 +01:00
|
|
|
reduction=true; rtype = NetUReduce::XOR; break;
|
2000-01-02 22:45:31 +01:00
|
|
|
case 'A': // Reduction NAND (~&)
|
2005-02-03 05:56:20 +01:00
|
|
|
reduction=true; rtype = NetUReduce::NAND; break;
|
2000-01-02 22:45:31 +01:00
|
|
|
case 'X': // Reduction XNOR (~^)
|
2005-02-03 05:56:20 +01:00
|
|
|
reduction=true; rtype = NetUReduce::XNOR; break;
|
2000-01-02 20:39:03 +01:00
|
|
|
|
2001-01-24 03:52:30 +01:00
|
|
|
case '-': // Unary 2's complement.
|
2006-05-19 06:44:55 +02:00
|
|
|
// If this expression is self determined, get its width
|
|
|
|
|
// from the sub_expression.
|
|
|
|
|
if (owidth == 0)
|
|
|
|
|
owidth = sub_sig->vector_width();
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
sig = new NetNet(scope, scope->local_symbol(),
|
2002-09-08 03:37:13 +02:00
|
|
|
NetNet::WIRE, owidth);
|
2005-07-07 18:22:49 +02:00
|
|
|
sig->data_type(sub_sig->data_type());
|
2001-01-24 03:52:30 +01:00
|
|
|
sig->local_flag(true);
|
|
|
|
|
|
2005-01-30 06:20:38 +01:00
|
|
|
if (sub_sig->vector_width() < owidth)
|
2002-09-08 03:37:13 +02:00
|
|
|
sub_sig = pad_to_width(des, sub_sig, owidth);
|
|
|
|
|
|
2005-01-30 06:20:38 +01:00
|
|
|
switch (sub_sig->vector_width()) {
|
2001-01-24 03:52:30 +01:00
|
|
|
case 0:
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1:
|
2003-03-06 01:28:41 +01:00
|
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
2004-12-11 03:31:25 +01:00
|
|
|
2, NetLogic::BUF, 1);
|
2001-01-24 03:52:30 +01:00
|
|
|
connect(gate->pin(0), sig->pin(0));
|
|
|
|
|
connect(gate->pin(1), sub_sig->pin(0));
|
|
|
|
|
des->add_node(gate);
|
|
|
|
|
gate->rise_time(rise);
|
|
|
|
|
gate->fall_time(fall);
|
|
|
|
|
gate->decay_time(decay);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2003-02-26 02:29:24 +01:00
|
|
|
NetAddSub*sub = new NetAddSub(scope, scope->local_symbol(),
|
2005-01-30 06:20:38 +01:00
|
|
|
sig->vector_width());
|
|
|
|
|
sub->attribute(perm_string::literal("LPM_Direction"),
|
|
|
|
|
verinum("SUB"));
|
2002-06-22 06:22:40 +02:00
|
|
|
|
|
|
|
|
des->add_node(sub);
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
connect(sig->pin(0), sub->pin_Result());
|
|
|
|
|
connect(sub_sig->pin(0), sub->pin_DataB());
|
2002-06-22 06:22:40 +02:00
|
|
|
|
|
|
|
|
verinum tmp_num (verinum::V0, sub->width(), true);
|
|
|
|
|
NetConst*tmp_con = new NetConst(scope,
|
2003-03-06 01:28:41 +01:00
|
|
|
scope->local_symbol(),
|
2002-06-22 06:22:40 +02:00
|
|
|
tmp_num);
|
|
|
|
|
des->add_node(tmp_con);
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*tmp_sig = new NetNet(scope, scope->local_symbol(),
|
2002-06-22 06:22:40 +02:00
|
|
|
NetNet::WIRE,
|
2005-01-30 06:20:38 +01:00
|
|
|
sub_sig->vector_width());
|
2006-05-01 22:47:58 +02:00
|
|
|
tmp_sig->data_type(sub_sig->data_type());
|
2002-06-22 06:22:40 +02:00
|
|
|
tmp_sig->local_flag(true);
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
connect(tmp_sig->pin(0), sub->pin_DataA());
|
|
|
|
|
connect(tmp_sig->pin(0), tmp_con->pin(0));
|
2001-01-24 03:52:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2000-01-02 20:39:03 +01:00
|
|
|
default:
|
|
|
|
|
cerr << "internal error: Unhandled UNARY '" << op_ << "'" << endl;
|
|
|
|
|
sig = 0;
|
|
|
|
|
}
|
2005-02-03 05:56:20 +01:00
|
|
|
|
2004-06-24 17:22:23 +02:00
|
|
|
if (reduction) {
|
2005-02-03 05:56:20 +01:00
|
|
|
NetUReduce*rgate;
|
|
|
|
|
|
|
|
|
|
// The output of a reduction operator is 1 bit
|
|
|
|
|
sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, 1);
|
2005-09-14 17:15:44 +02:00
|
|
|
sig->data_type(sub_sig->data_type());
|
2004-06-24 17:22:23 +02:00
|
|
|
sig->local_flag(true);
|
2000-01-02 20:39:03 +01:00
|
|
|
|
2005-02-03 05:56:20 +01:00
|
|
|
rgate = new NetUReduce(scope, scope->local_symbol(),
|
|
|
|
|
rtype, sub_sig->vector_width());
|
|
|
|
|
rgate->set_line(*this);
|
|
|
|
|
connect(rgate->pin(0), sig->pin(0));
|
|
|
|
|
connect(rgate->pin(1), sub_sig->pin(0));
|
|
|
|
|
|
|
|
|
|
des->add_node(rgate);
|
|
|
|
|
rgate->rise_time(rise);
|
|
|
|
|
rgate->fall_time(fall);
|
|
|
|
|
rgate->decay_time(decay);
|
2004-06-24 17:22:23 +02:00
|
|
|
}
|
2000-01-02 20:39:03 +01:00
|
|
|
|
|
|
|
|
return sig;
|
|
|
|
|
}
|
|
|
|
|
|