Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2034 lines (1738 sloc) 61.426 kB
/*
* Copyright (c) 1999-2000 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if !defined(WINNT) && !defined(macintosh)
#ident "$Id: elab_net.cc,v 1.77 2001/10/28 01:14:53 steve Exp $"
#endif
# include "config.h"
# include "PExpr.h"
# include "netlist.h"
# include "netmisc.h"
# include "compiler.h"
# include <iostream>
/*
* 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.
*/
NetNet* PEBinary::elaborate_net(Design*des, const string&path,
unsigned width,
unsigned long rise,
unsigned long fall,
unsigned long decay,
Link::strength_t drive0,
Link::strength_t drive1) const
{
switch (op_) {
case '*':
return elaborate_net_mul_(des, path, width, rise, fall, decay);
case '%':
return elaborate_net_mod_(des, path, width, rise, fall, decay);
case '/':
return elaborate_net_div_(des, path, width, rise, fall, decay);
case '+':
case '-':
return elaborate_net_add_(des, path, width, rise, fall, decay);
case '|': // Bitwise OR
case '&':
case '^':
case 'X': // Exclusing NOR
return elaborate_net_bit_(des, path, width, rise, fall, decay);
case 'E': // === (case equals)
case 'e': // ==
case 'N': // !== (case not-equals)
case 'n': // !=
case '<':
case '>':
case 'L': // <=
case 'G': // >=
return elaborate_net_cmp_(des, path, width, rise, fall, decay);
case 'a': // && (logical and)
case 'o': // || (logical or)
return elaborate_net_log_(des, path, width, rise, fall, decay);
case 'l': // <<
case 'r': // >>
return elaborate_net_shift_(des, path, width, rise, fall, decay);
}
NetNet*lsig = left_->elaborate_net(des, path, width, 0, 0, 0),
*rsig = right_->elaborate_net(des, path, width, 0, 0, 0);
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;
NetNode*gate;
NetNode*gate_t;
switch (op_) {
case '^': // XOR
case 'X': // XNOR
case '&': // AND
case '|': // Bitwise OR
assert(0);
break;
case 'E': // === (Case equals)
case 'e': // ==
case 'n': // !=
case '<':
case '>':
case 'G': // >=
case 'L': // <=
assert(0);
break;
case '+':
assert(0);
break;
case 'l':
case 'r':
assert(0);
break;
default:
cerr << get_line() << ": internal error: unsupported"
" combinational operator (" << op_ << ")." << endl;
des->errors += 1;
osig = 0;
}
if (NetTmp*tmp = dynamic_cast<NetTmp*>(lsig))
delete tmp;
if (NetTmp*tmp = dynamic_cast<NetTmp*>(rsig))
delete tmp;
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.
*/
NetNet* PEBinary::elaborate_net_add_(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
NetNet*lsig = left_->elaborate_net(des, path, lwidth, 0, 0, 0),
*rsig = right_->elaborate_net(des, path, lwidth, 0, 0, 0);
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;
NetNode*gate;
NetNode*gate_t;
string name = des->local_symbol(path);
unsigned width = lsig->pin_count();
if (rsig->pin_count() > lsig->pin_count())
width = rsig->pin_count();
/* The owidth is the output width of the lpm_add_sub
device. If the desired with is greater then the width of
the operands, then widen the adder and let code below pad
the operands. If this is an adder, we can take advantage of
the carry bit. */
unsigned owidth = width;
switch (op_) {
case '+':
if (lwidth > owidth) {
owidth = lwidth;
width = lwidth-1;
}
break;
case '-':
if (lwidth > owidth) {
owidth = lwidth;
width = lwidth;
}
break;
default:
assert(0);
}
// Pad out the operands, if necessary, the match the width of
// the adder device.
if (lsig->pin_count() < width)
lsig = pad_to_width(des, lsig, width);
if (rsig->pin_count() < width)
rsig = pad_to_width(des, rsig, width);
// Make the adder as wide as the widest operand
osig = new NetNet(scope, des->local_symbol(path),
NetNet::WIRE, owidth);
osig->local_flag(true);
NetAddSub*adder = new NetAddSub(scope, name, width);
// Connect the adder to the various parts.
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
connect(lsig->pin(idx), adder->pin_DataA(idx));
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx += 1)
connect(rsig->pin(idx), adder->pin_DataB(idx));
for (unsigned idx = 0 ; idx < width ; idx += 1)
connect(osig->pin(idx), adder->pin_Result(idx));
if (owidth > width)
connect(osig->pin(width), adder->pin_Cout());
gate = adder;
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
des->add_node(gate);
switch (op_) {
case '+':
gate->attribute("LPM_Direction", "ADD");
break;
case '-':
gate->attribute("LPM_Direction", "SUB");
break;
}
return osig;
}
/*
* 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.
*/
NetNet* PEBinary::elaborate_net_bit_(Design*des, const string&path,
unsigned width,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
NetNet*lsig = left_->elaborate_net(des, path, width, 0, 0, 0),
*rsig = right_->elaborate_net(des, path, width, 0, 0, 0);
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;
}
if (lsig->pin_count() < rsig->pin_count())
lsig = pad_to_width(des, lsig, rsig->pin_count());
if (rsig->pin_count() < lsig->pin_count())
rsig = pad_to_width(des, rsig, lsig->pin_count());
if (lsig->pin_count() != rsig->pin_count()) {
cerr << get_line() << ": internal error: lsig pin count ("
<< lsig->pin_count() << ") != rsig pin count ("
<< rsig->pin_count() << ")." << endl;
des->errors += 1;
return 0;
}
assert(lsig->pin_count() == rsig->pin_count());
NetNet*osig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE,
lsig->pin_count());
osig->local_flag(true);
switch (op_) {
case '^': // XOR
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1) {
NetLogic*gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::XOR);
connect(gate->pin(1), lsig->pin(idx));
connect(gate->pin(2), rsig->pin(idx));
connect(gate->pin(0), osig->pin(idx));
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
des->add_node(gate);
}
break;
case 'X': // XNOR
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1) {
NetLogic*gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::XNOR);
connect(gate->pin(1), lsig->pin(idx));
connect(gate->pin(2), rsig->pin(idx));
connect(gate->pin(0), osig->pin(idx));
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
des->add_node(gate);
}
break;
case '&': // AND
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1) {
NetLogic*gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::AND);
connect(gate->pin(1), lsig->pin(idx));
connect(gate->pin(2), rsig->pin(idx));
connect(gate->pin(0), osig->pin(idx));
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
des->add_node(gate);
}
break;
case '|': // Bitwise OR
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1) {
NetLogic*gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::OR);
connect(gate->pin(1), lsig->pin(idx));
connect(gate->pin(2), rsig->pin(idx));
connect(gate->pin(0), osig->pin(idx));
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
des->add_node(gate);
}
break;
default:
assert(0);
}
return osig;
}
/*
* 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.
*/
NetNet* PEBinary::elaborate_net_cmp_(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
NetNet*lsig = left_->elaborate_net(des, path, 0, 0, 0, 0),
*rsig = right_->elaborate_net(des, path, 0, 0, 0, 0);
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;
}
unsigned dwidth = lsig->pin_count();
if (rsig->pin_count() > dwidth) dwidth = rsig->pin_count();
NetNet*zero = 0;
if (lsig->pin_count() != rsig->pin_count()) {
NetConst*tmp = new NetConst(scope, des->local_symbol(path),
verinum::V0);
des->add_node(tmp);
zero = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
connect(tmp->pin(0), zero->pin(0));
}
NetNet*osig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
osig->local_flag(true);
NetNode*gate;
//NetNode*gate_t;
switch (op_) {
case '<':
case '>':
case 'L':
case 'G': {
NetCompare*cmp = new
NetCompare(scope, des->local_symbol(path), dwidth);
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
connect(cmp->pin_DataA(idx), lsig->pin(idx));
for (unsigned idx = lsig->pin_count(); idx < dwidth ; idx += 1)
connect(cmp->pin_DataA(idx), zero->pin(0));
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx += 1)
connect(cmp->pin_DataB(idx), rsig->pin(idx));
for (unsigned idx = rsig->pin_count(); idx < dwidth ; idx += 1)
connect(cmp->pin_DataB(idx), zero->pin(0));
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;
}
gate = cmp;
break;
}
case 'E': // Case equals (===)
case 'N': // Case equals (!==)
// The comparison generates gates to bitwise compare
// each pair, and AND all the comparison results.
gate = new NetLogic(scope, des->local_symbol(path),
1+dwidth,
(op_ == 'E')? NetLogic::AND : NetLogic::NAND);
connect(gate->pin(0), osig->pin(0));
for (unsigned idx = 0 ; idx < dwidth ; idx += 1) {
NetCaseCmp*cmp = new NetCaseCmp(scope,
des->local_symbol(path));
if (idx < lsig->pin_count())
connect(cmp->pin(1), lsig->pin(idx));
else
connect(cmp->pin(1), zero->pin(0));
if (idx < rsig->pin_count())
connect(cmp->pin(2), rsig->pin(idx));
else
connect(cmp->pin(2), zero->pin(0));
connect(cmp->pin(0), gate->pin(idx+1));
des->add_node(cmp);
// Attach a label to this intermediate wire
NetNet*tmp = new NetNet(scope, des->local_symbol(path),
NetNet::WIRE);
tmp->local_flag(true);
connect(cmp->pin(0), tmp->pin(0));
}
break;
case 'e': // ==
/* Handle the special case of single bit compare with a
single XNOR gate. This is easy and direct. */
if (dwidth == 1) {
gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::XNOR);
connect(gate->pin(0), osig->pin(0));
connect(gate->pin(1), lsig->pin(0));
connect(gate->pin(2), rsig->pin(0));
break;
}
/* Oh well, do the general case with a NetCompare. */
{ NetCompare*cmp = new NetCompare(scope, des->local_symbol(path),
dwidth);
for (unsigned idx = 0 ; idx < dwidth ; idx += 1) {
if (idx < lsig->pin_count())
connect(cmp->pin_DataA(idx), lsig->pin(idx));
else
connect(cmp->pin_DataA(idx), zero->pin(0));
if (idx < rsig->pin_count())
connect(cmp->pin_DataB(idx), rsig->pin(idx));
else
connect(cmp->pin_DataB(idx), zero->pin(0));
}
connect(cmp->pin_AEB(), osig->pin(0));
gate = cmp;
}
break;
case 'n': // !=
/* Handle the special case of single bit compare with a
single XOR gate. This is easy and direct. */
if (dwidth == 1) {
gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::XOR);
connect(gate->pin(0), osig->pin(0));
connect(gate->pin(1), lsig->pin(0));
connect(gate->pin(2), rsig->pin(0));
break;
}
/* Oh well, do the general case with a NetCompare. */
{ NetCompare*cmp = new NetCompare(scope, des->local_symbol(path),
dwidth);
for (unsigned idx = 0 ; idx < dwidth ; idx += 1) {
if (idx < lsig->pin_count())
connect(cmp->pin_DataA(idx), lsig->pin(idx));
else
connect(cmp->pin_DataA(idx), zero->pin(0));
if (idx < rsig->pin_count())
connect(cmp->pin_DataB(idx), rsig->pin(idx));
else
connect(cmp->pin_DataB(idx), zero->pin(0));
}
connect(cmp->pin_ANEB(), osig->pin(0));
gate = cmp;
}
break;
default:
assert(0);
}
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
des->add_node(gate);
return osig;
}
/*
* Elaborate a divider gate. This function create a NetDevide gate
* which has exactly the right sized DataA, DataB and Result ports. If
* the l-value is wider then the result, then pad.
*/
NetNet* PEBinary::elaborate_net_div_(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
NetNet*lsig = left_->elaborate_net(des, path, 0, 0, 0, 0);
if (lsig == 0) return 0;
NetNet*rsig = right_->elaborate_net(des, path, 0, 0, 0, 0);
if (rsig == 0) return 0;
// 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) {
rwidth = lsig->pin_count();
if (rsig->pin_count() > rwidth)
rwidth = rsig->pin_count();
lwidth = rwidth;
}
if ((rwidth > lsig->pin_count()) && (rwidth > rsig->pin_count())) {
rwidth = lsig->pin_count();
if (rsig->pin_count() > rwidth)
rwidth = rsig->pin_count();
}
// Create a device with the calculated dimensions.
NetDivide*div = new NetDivide(scope, des->local_symbol(path), rwidth,
lsig->pin_count(),
rsig->pin_count());
des->add_node(div);
// Connect the left and right inputs of the divider to the
// nets that are the left and right expressions.
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
connect(div->pin_DataA(idx), lsig->pin(idx));
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx += 1)
connect(div->pin_DataB(idx), rsig->pin(idx));
// Make an output signal that is the width of the l-value.
// Due to above calculation of rwidth, we know that the result
// will be no more then the l-value, so it is safe to connect
// all the result pins to the osig.
NetNet*osig = new NetNet(scope, des->local_symbol(path),
NetNet::IMPLICIT, lwidth);
osig->local_flag(true);
for (unsigned idx = 0 ; idx < rwidth ; idx += 1)
connect(div->pin_Result(idx), osig->pin(idx));
// If the lvalue is larger then the result, then pad the
// output with constant 0. This can happen for example in
// cases like this:
// wire [3;0] a, b;
// wire [7:0] r = a / b;
if (rwidth < osig->pin_count()) {
NetConst*tmp = new NetConst(scope, des->local_symbol(path),
verinum::V0);
des->add_node(tmp);
for (unsigned idx = rwidth ; idx < osig->pin_count() ; idx += 1)
connect(osig->pin(idx), tmp->pin(0));
}
return osig;
}
/*
* Elaborate a modulo gate.
*/
NetNet* PEBinary::elaborate_net_mod_(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
NetNet*lsig = left_->elaborate_net(des, path, 0, 0, 0, 0);
if (lsig == 0) return 0;
NetNet*rsig = right_->elaborate_net(des, path, 0, 0, 0, 0);
if (rsig == 0) return 0;
unsigned rwidth = lsig->pin_count();
if (rsig->pin_count() > rwidth)
rwidth = rsig->pin_count();
NetModulo*mod = new NetModulo(scope, des->local_symbol(path), rwidth,
lsig->pin_count(),
rsig->pin_count());
des->add_node(mod);
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
connect(mod->pin_DataA(idx), lsig->pin(idx));
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx += 1)
connect(mod->pin_DataB(idx), rsig->pin(idx));
if (lwidth == 0) lwidth = rwidth;
NetNet*osig = new NetNet(scope, des->local_symbol(path),
NetNet::IMPLICIT, lwidth);
osig->local_flag(true);
unsigned cnt = osig->pin_count();
if (cnt > rwidth) cnt = rwidth;
for (unsigned idx = 0 ; idx < cnt ; idx += 1)
connect(mod->pin_Result(idx), osig->pin(idx));
/* If the lvalue is larger then the result, then pad the
output with constant 0. */
if (cnt < osig->pin_count()) {
NetConst*tmp = new NetConst(scope, des->local_symbol(path),
verinum::V0);
des->add_node(tmp);
for (unsigned idx = cnt ; idx < osig->pin_count() ; idx += 1)
connect(osig->pin(idx), tmp->pin(0));
}
return osig;
}
NetNet* PEBinary::elaborate_net_log_(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
NetNet*lsig = left_->elaborate_net(des, path, 0, 0, 0, 0),
*rsig = right_->elaborate_net(des, path, 0, 0, 0, 0);
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;
NetLogic*gate_t;
switch (op_) {
case 'a':
gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::AND);
break;
case 'o':
gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::OR);
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...
if (lsig->pin_count() > 1) {
gate_t = new NetLogic(scope, des->local_symbol(path),
1+lsig->pin_count(), NetLogic::OR);
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
connect(gate_t->pin(idx+1), lsig->pin(idx));
connect(gate->pin(1), gate_t->pin(0));
/* The reduced logical value is a new nexus, create a
temporary signal to represent it. */
NetNet*tmp = new NetTmp(scope, des->local_symbol(path));
connect(gate->pin(1), tmp->pin(0));
des->add_node(gate_t);
} else {
connect(gate->pin(1), lsig->pin(0));
}
// The second OR gate returns 1 if the right value is true...
if (rsig->pin_count() > 1) {
gate_t = new NetLogic(scope, des->local_symbol(path),
1+rsig->pin_count(), NetLogic::OR);
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx += 1)
connect(gate_t->pin(idx+1), rsig->pin(idx));
connect(gate->pin(2), gate_t->pin(0));
/* The reduced logical value is a new nexus, create a
temporary signal to represent it. */
NetNet*tmp = new NetTmp(scope, des->local_symbol(path));
connect(gate->pin(2), tmp->pin(0));
des->add_node(gate_t);
} else {
connect(gate->pin(2), rsig->pin(0));
}
// The output is the AND/OR of the two logic values.
NetNet*osig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
osig->local_flag(true);
connect(gate->pin(0), osig->pin(0));
des->add_node(gate);
return osig;
}
NetNet* PEBinary::elaborate_net_mul_(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
NetNet*lsig = left_->elaborate_net(des, path, lwidth, 0, 0, 0);
if (lsig == 0) return 0;
NetNet*rsig = right_->elaborate_net(des, path, lwidth, 0, 0, 0);
if (rsig == 0) return 0;
unsigned rwidth = lwidth;
NetMult*mult = new NetMult(scope, des->local_symbol(path), rwidth,
lsig->pin_count(),
rsig->pin_count());
des->add_node(mult);
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
connect(mult->pin_DataA(idx), lsig->pin(idx));
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx += 1)
connect(mult->pin_DataB(idx), rsig->pin(idx));
if (lwidth == 0) lwidth = rwidth;
NetNet*osig = new NetNet(scope, des->local_symbol(path),
NetNet::IMPLICIT, lwidth);
osig->local_flag(true);
unsigned cnt = osig->pin_count();
if (cnt > rwidth) cnt = rwidth;
for (unsigned idx = 0 ; idx < cnt ; idx += 1)
connect(mult->pin_Result(idx), osig->pin(idx));
/* If the lvalue is larger then the result, then pad the
output with constant 0. */
if (cnt < osig->pin_count()) {
NetConst*tmp = new NetConst(scope, des->local_symbol(path),
verinum::V0);
des->add_node(tmp);
for (unsigned idx = cnt ; idx < osig->pin_count() ; idx += 1)
connect(osig->pin(idx), tmp->pin(0));
}
return osig;
}
NetNet* PEBinary::elaborate_net_shift_(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
NetNet*lsig = left_->elaborate_net(des, path, lwidth, 0, 0, 0);
if (lsig == 0) return 0;
if (lsig->pin_count() > lwidth)
lwidth = lsig->pin_count();
/* 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
shifted. */
if (verinum*rval = right_->eval_const(des, path)) {
assert(rval->is_defined());
unsigned dist = rval->as_ulong();
if (dist > lwidth)
dist = lwidth;
/* Very special case, constant 0 shift. */
if (dist == 0) return lsig;
NetNet*osig = new NetNet(scope, des->local_symbol(path),
NetNet::WIRE, lwidth);
osig->local_flag(true);
NetConst*zero = new NetConst(scope, des->local_symbol(path),
verinum::V0);
des->add_node(zero);
if (op_ == 'l') {
unsigned idx;
for (idx = 0 ; idx < dist ; idx += 1)
connect(osig->pin(idx), zero->pin(0));
for ( ; (idx<lwidth) && ((idx-dist) < lsig->pin_count())
; idx += 1)
connect(osig->pin(idx), lsig->pin(idx-dist));
for ( ; idx < lwidth ; idx += 1)
connect(osig->pin(idx), zero->pin(0));
} else {
assert(op_ == 'r');
unsigned idx;
unsigned keep = lsig->pin_count()-dist;
for (idx = 0 ; idx < keep ; idx += 1)
connect(osig->pin(idx), lsig->pin(idx+dist));
for (idx = keep ; idx < lwidth ; idx += 1)
connect(osig->pin(idx), zero->pin(0));
}
return osig;
}
// Calculate the number of useful bits for the shift amount,
// and elaborate the right_ expression as the shift amount.
unsigned dwid = 0;
while ((1 << dwid) < lwidth)
dwid += 1;
NetNet*rsig = right_->elaborate_net(des, path, dwid, 0, 0, 0);
if (rsig == 0) return 0;
// Make the shift device itself, and the output
// NetNet. Connect the Result output pins to the osig signal
NetCLShift*gate = new NetCLShift(scope, des->local_symbol(path),
lwidth, rsig->pin_count());
NetNet*osig = new NetNet(scope, des->local_symbol(path),
NetNet::WIRE, lwidth);
osig->local_flag(true);
for (unsigned idx = 0 ; idx < lwidth ; idx += 1)
connect(osig->pin(idx), gate->pin_Result(idx));
// Connect the lsig (the left expression) to the Data input,
// and pad it if necessary with constant zeros.
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
connect(lsig->pin(idx), gate->pin_Data(idx));
if (lsig->pin_count() < lwidth) {
NetConst*zero = new NetConst(scope, des->local_symbol(path),
verinum::V0);
NetTmp*tmp = new NetTmp(scope, des->local_symbol(path));
des->add_node(zero);
connect(zero->pin(0), tmp->pin(0));
for (unsigned idx = lsig->pin_count() ; idx < lwidth ; idx += 1)
connect(zero->pin(0), gate->pin_Data(idx));
}
// Connect the rsig (the shift amount expression) to the
// Distance input.
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx += 1)
connect(rsig->pin(idx), gate->pin_Distance(idx));
if (op_ == 'r') {
NetTmp*tmp = new NetTmp(scope, des->local_symbol(path));
NetConst*dir = new NetConst(scope, des->local_symbol(path),
verinum::V1);
connect(dir->pin(0), gate->pin_Direction());
connect(tmp->pin(0), gate->pin_Direction());
des->add_node(dir);
}
des->add_node(gate);
return osig;
}
/*
* The concatenation operator, as a net, is a wide signal that is
* connected to all the pins of the elaborated expression nets.
*/
NetNet* PEConcat::elaborate_net(Design*des, const string&path,
unsigned,
unsigned long rise,
unsigned long fall,
unsigned long decay,
Link::strength_t drive0,
Link::strength_t drive1) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
svector<NetNet*>nets (parms_.count());
unsigned pins = 0;
unsigned errors = 0;
unsigned repeat = 1;
if (repeat_) {
verinum*rep = repeat_->eval_const(des, path);
if (rep == 0) {
cerr << get_line() << ": internal error: Unable to "
<< "evaluate constant repeat expression." << endl;
des->errors += 1;
return 0;
}
repeat = rep->as_ulong();
if (repeat == 0) {
cerr << get_line() << ": error: Invalid repeat value."
<< endl;
des->errors += 1;
delete rep;
return 0;
}
}
/* Elaborate the operands of the concatenation. */
for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) {
if (parms_[idx] == 0) {
cerr << get_line() << ": error: Empty expressions "
<< "not allowed in concatenations." << endl;
errors += 1;
continue;
}
/* 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;
}
}
nets[idx] = parms_[idx]->elaborate_net(des, path, 0,
rise,fall,decay);
if (nets[idx] == 0)
errors += 1;
else
pins += nets[idx]->pin_count();
}
/* 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
concat operator from least significant to most significant,
which is opposite from how they are given in the list.
Allow for a repeat count other then 1 by repeating the
connect loop as many times as necessary. */
NetNet*osig = new NetNet(scope, des->local_symbol(path),
NetNet::IMPLICIT, pins * repeat);
pins = 0;
for (unsigned rpt = 0 ; rpt < repeat ; rpt += 1)
for (unsigned idx = nets.count() ; idx > 0 ; idx -= 1) {
NetNet*cur = nets[idx-1];
for (unsigned pin = 0; pin < cur->pin_count(); pin += 1) {
connect(osig->pin(pins), cur->pin(pin));
pins += 1;
}
}
osig->local_flag(true);
return osig;
}
NetNet* PEIdent::elaborate_net(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay,
Link::strength_t drive0,
Link::strength_t drive1) const
{
NetScope*scope = des->find_scope(path);
NetNet*sig = des->find_signal(scope, text_);
if (sig == 0) {
/* If the identifier is a memory instead of a signal,
then handle it elsewhere. Create a RAM. */
if (NetMemory*mem = des->find_memory(scope, text_))
return elaborate_net_ram_(des, path, mem, lwidth,
rise, fall, decay);
if (const NetExpr*pe = des->find_parameter(scope, text_)) {
const NetEConst*pc = dynamic_cast<const NetEConst*>(pe);
assert(pc);
verinum pvalue = pc->value();
sig = new NetNet(scope, path+"."+text_, NetNet::IMPLICIT,
pc->expr_width());
NetConst*cp = new NetConst(scope, des->local_symbol(path),
pvalue);
des->add_node(cp);
for (unsigned idx = 0; idx < sig->pin_count(); idx += 1)
connect(sig->pin(idx), cp->pin(idx));
} else {
sig = new NetNet(scope, path+"."+text_, NetNet::IMPLICIT, 1);
if (warn_implicit)
cerr << get_line() << ": warning: implicit "
"definition of wire " << path << "." <<
text_ << "." << endl;
}
}
assert(sig);
if (msb_ && lsb_) {
verinum*mval = msb_->eval_const(des, path);
if (mval == 0) {
cerr << msb_->get_line() << ": error: unable to "
"evaluate constant expression: " << *msb_ <<
endl;
des->errors += 1;
return 0;
}
verinum*lval = lsb_->eval_const(des, path);
if (lval == 0) {
cerr << lsb_->get_line() << ": error: unable to "
"evaluate constant expression: " << *lsb_ <<
endl;
delete mval;
des->errors += 1;
return 0;
}
assert(mval);
assert(lval);
unsigned midx = sig->sb_to_idx(mval->as_long());
unsigned lidx = sig->sb_to_idx(lval->as_long());
/* This is a part select, create a new NetNet object
that connects to just the desired parts of the
identifier. Make sure the NetNet::Type is compatible
with the sig type.
Be careful to check the bit ordering. If the msb is
less significant then the msb, then the source is
broken. I can hack it in order to go on, but report
an error. */
if (midx < lidx) {
cerr << get_line() << ": error: part select "
<< sig->name() << "[" << mval->as_long() << ":"
<< lval->as_long() << "] "
<< "has bit order reversed." << endl;
des->errors += 1;
unsigned tmp = midx;
midx = lidx;
lidx = tmp;
}
NetNet*tmp = new NetNet(scope, des->local_symbol(path),
sig->type(), midx-lidx+1);
tmp->local_flag(true);
/* Check that the bit or part select of the signal is
within the range of the part. The lidx is the
normalized index of the LSB, so that plus the desired
width must be <= the width of the references signal. */
if ((lidx + tmp->pin_count()) > sig->pin_count()) {
cerr << get_line() << ": error: bit/part select ["
<< mval->as_long() << ":" << lval->as_long()
<< "] out of range for " << sig->name() << endl;
des->errors += 1;
return sig;
}
for (unsigned idx = lidx ; idx <= midx ; idx += 1)
connect(tmp->pin(idx-lidx), sig->pin(idx));
sig = tmp;
} else if (msb_) {
verinum*mval = msb_->eval_const(des, path);
if (mval == 0) {
cerr << get_line() << ": error: index of " << text_ <<
" needs to be constant in this context." <<
endl;
des->errors += 1;
return 0;
}
assert(mval);
unsigned idx = sig->sb_to_idx(mval->as_long());
if (idx >= sig->pin_count()) {
cerr << get_line() << ": error: index " << sig->name() <<
"[" << mval->as_long() << "] out of range." << endl;
des->errors += 1;
idx = 0;
}
/* This is a bit select, create a compatible NetNet with
a single bit that links to the selected bit of the
expression. */
NetNet*tmp = new NetNet(scope, des->local_symbol(path),
sig->type(), 1);
tmp->local_flag(true);
connect(tmp->pin(0), sig->pin(idx));
sig = tmp;
}
return sig;
}
/*
* When I run into an identifier in an expression that referrs to a
* memory, create a RAM port object.
*/
NetNet* PEIdent::elaborate_net_ram_(Design*des, const string&path,
NetMemory*mem, unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
if (msb_ == 0) {
cerr << get_line() << ": error: memory reference without"
" the required index expression." << endl;
des->errors += 1;
return 0;
}
NetNet*adr = msb_->elaborate_net(des, path, 0, 0, 0, 0);
if (adr == 0)
return 0;
NetRamDq*ram = new NetRamDq(scope, des->local_symbol(mem->name()),
mem, adr->pin_count());
des->add_node(ram);
for (unsigned idx = 0 ; idx < adr->pin_count() ; idx += 1)
connect(ram->pin_Address(idx), adr->pin(idx));
NetNet*osig = new NetNet(scope, des->local_symbol(mem->name()),
NetNet::IMPLICIT, ram->width());
osig->local_flag(true);
for (unsigned idx = 0 ; idx < osig->pin_count() ; idx += 1)
connect(ram->pin_Q(idx), osig->pin(idx));
return osig;
}
/*
* 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, const string&path) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
NetNet*sig = des->find_signal(scope, text_);
if (sig == 0) {
/* Don't allow memories here. Is it a memory? */
if (des->find_memory(scope, text_)) {
cerr << get_line() << ": error: memories (" << text_
<< ") cannot be l-values in continuous "
<< "assignments." << endl;
return 0;
}
/* Fine, create an implicit wire as an l-value. */
sig = new NetNet(scope, path+"."+text_, NetNet::IMPLICIT, 1);
if (warn_implicit)
cerr << get_line() << ": warning: implicit "
" definition of wire " << path << "." <<
text_ << "." << endl;
}
assert(sig);
/* Don't allow registers as assign l-values. */
if (sig->type() == NetNet::REG) {
cerr << get_line() << ": error: registers (" << sig->name()
<< ") cannot be l-values in continuous"
<< " assignments." << endl;
return 0;
}
if (msb_ && lsb_) {
/* Detect a part select. Evaluate the bits and elaborate
the l-value by creating a sub-net that links to just
the right pins. */
verinum*mval = msb_->eval_const(des, path);
assert(mval);
verinum*lval = lsb_->eval_const(des, path);
assert(lval);
unsigned midx = sig->sb_to_idx(mval->as_long());
unsigned lidx = sig->sb_to_idx(lval->as_long());
if (midx >= lidx) {
NetTmp*tmp = new NetTmp(scope, des->local_symbol(path),
midx-lidx+1);
if (tmp->pin_count() > sig->pin_count()) {
cerr << get_line() << ": bit select out of "
<< "range for " << sig->name() << endl;
return sig;
}
for (unsigned idx = lidx ; idx <= midx ; idx += 1)
connect(tmp->pin(idx-lidx), sig->pin(idx));
sig = tmp;
} else {
NetTmp*tmp = new NetTmp(scope, des->local_symbol(path),
lidx-midx+1);
if (tmp->pin_count() > sig->pin_count()) {
cerr << get_line() << ": error: "
<< "part select out of range for "
<< sig->name() << "." << endl;
des->errors += 1;
return sig;
}
assert(tmp->pin_count() <= sig->pin_count());
for (unsigned idx = lidx ; idx >= midx ; idx -= 1)
connect(tmp->pin(idx-midx), sig->pin(idx));
sig = tmp;
}
} else if (msb_) {
verinum*mval = msb_->eval_const(des, path);
if (mval == 0) {
cerr << get_line() << ": error: index of " << text_ <<
" needs to be constant in l-value of assignment." <<
endl;
des->errors += 1;
return 0;
}
assert(mval);
unsigned idx = sig->sb_to_idx(mval->as_long());
if (idx >= sig->pin_count()) {
cerr << get_line() << "; index " << sig->name() <<
"[" << mval->as_long() << "] out of range." << endl;
des->errors += 1;
idx = 0;
}
NetTmp*tmp = new NetTmp(scope, des->local_symbol(path), 1);
connect(tmp->pin(0), sig->pin(idx));
sig = tmp;
}
return sig;
}
/*
* 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
* port.
*/
NetNet* PEIdent::elaborate_port(Design*des, NetScope*scope) const
{
const string path = scope->name();
NetNet*sig = des->find_signal(scope, text_);
if (sig == 0) {
cerr << get_line() << ": error: no wire/reg " << text_
<< " in module " << scope->name() << "." << endl;
des->errors += 1;
return 0;
}
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:
cerr << get_line() << ": error: signal " << text_ << " in"
<< " module " << scope->name() << " is not a port." << endl;
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:
cerr << get_line() << ": internal error: signal " << text_
<< " in module " << scope->name() << " is left as "
<< "port type PIMPLICIT." << endl;
des->errors += 1;
return 0;
}
if (msb_ && lsb_) {
/* Detect a part select. Evaluate the bits and elaborate
the l-value by creating a sub-net that links to just
the right pins. */
verinum*mval = msb_->eval_const(des, path);
assert(mval);
verinum*lval = lsb_->eval_const(des, path);
assert(lval);
unsigned midx = sig->sb_to_idx(mval->as_long());
unsigned lidx = sig->sb_to_idx(lval->as_long());
if (midx >= lidx) {
NetTmp*tmp = new NetTmp(scope, des->local_symbol(path),
midx-lidx+1);
if (tmp->pin_count() > sig->pin_count()) {
cerr << get_line() << ": bit select out of "
<< "range for " << sig->name() << endl;
return sig;
}
for (unsigned idx = lidx ; idx <= midx ; idx += 1)
connect(tmp->pin(idx-lidx), sig->pin(idx));
sig = tmp;
} else {
NetTmp*tmp = new NetTmp(scope, des->local_symbol(path),
lidx-midx+1);
assert(tmp->pin_count() <= sig->pin_count());
for (unsigned idx = lidx ; idx >= midx ; idx -= 1)
connect(tmp->pin(idx-midx), sig->pin(idx));
sig = tmp;
}
} else if (msb_) {
verinum*mval = msb_->eval_const(des, path);
if (mval == 0) {
cerr << get_line() << ": index of " << text_ <<
" needs to be constant in port context." <<
endl;
des->errors += 1;
return 0;
}
assert(mval);
unsigned idx = sig->sb_to_idx(mval->as_long());
if (idx >= sig->pin_count()) {
cerr << get_line() << "; index " << sig->name() <<
"[" << mval->as_long() << "] out of range." << endl;
des->errors += 1;
idx = 0;
}
NetTmp*tmp = new NetTmp(scope, des->local_symbol(path), 1);
connect(tmp->pin(0), sig->pin(idx));
sig = tmp;
}
return sig;
}
/*
* Elaborate a number as a NetConst object.
*/
NetNet* PENumber::elaborate_net(Design*des, const string&path,
unsigned lwidth,
unsigned long rise,
unsigned long fall,
unsigned long decay,
Link::strength_t drive0,
Link::strength_t drive1) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
/* 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) {
NetNet*net = new NetNet(scope, des->local_symbol(path),
NetNet::IMPLICIT, lwidth);
net->local_flag(true);
/* when expanding a constant to fit into the net, extend
the Vx or Vz values if they are in the sign position,
otherwise extend the number with 0 bits. */
verinum::V top_v = verinum::V0;
switch (value_->get(value_->len()-1)) {
case verinum::Vx:
top_v = verinum::Vx;
break;
case verinum::Vz:
top_v = verinum::Vz;
break;
}
verinum num(top_v, net->pin_count());
unsigned idx;
for (idx = 0 ; idx < num.len() && idx < value_->len(); idx += 1)
num.set(idx, value_->get(idx));
NetConst*tmp = new NetConst(scope, des->local_symbol(path),
num);
for (idx = 0 ; idx < net->pin_count() ; idx += 1) {
tmp->pin(idx).drive0(drive0);
tmp->pin(idx).drive1(drive1);
connect(net->pin(idx), tmp->pin(idx));
}
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()) {
NetNet*net = new NetNet(scope, des->local_symbol(path),
NetNet::IMPLICIT, value_->len());
net->local_flag(true);
NetConst*tmp = new NetConst(scope, des->local_symbol(path),
*value_);
for (unsigned idx = 0 ; idx < value_->len() ; idx += 1)
connect(net->pin(idx), tmp->pin(idx));
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. */
unsigned width = value_->len();
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));
NetNet*net = new NetNet(scope, des->local_symbol(path),
NetNet::IMPLICIT, width);
net->local_flag(true);
NetConst*tmp = new NetConst(scope, des->local_symbol(path), num);
for (unsigned idx = 0 ; idx < width ; idx += 1)
connect(net->pin(idx), tmp->pin(idx));
des->add_node(tmp);
return net;
}
/*
* Elaborate the ternary operator in a netlist by creating a LPM_MUX
* 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.
*/
NetNet* PETernary::elaborate_net(Design*des, const string&path,
unsigned width,
unsigned long rise,
unsigned long fall,
unsigned long decay,
Link::strength_t drive0,
Link::strength_t drive1) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
NetNet* expr_sig = expr_->elaborate_net(des, path, 0, 0, 0, 0);
NetNet* tru_sig = tru_->elaborate_net(des, path, width, 0, 0, 0);
NetNet* fal_sig = fal_->elaborate_net(des, path, width, 0, 0, 0);
if (expr_sig == 0 || tru_sig == 0 || fal_sig == 0) {
des->errors += 1;
return 0;
}
/* 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. */
unsigned iwidth = tru_sig->pin_count();
if (fal_sig->pin_count() > iwidth)
iwidth = fal_sig->pin_count();
/* If the width is not passed from the context, then take the
widest result as our width. */
if (width == 0)
width = iwidth;
/* If the expression has width, then generate a boolean result
by connecting an OR gate to calculate the truth value of
the result. In the end, the result needs to be a single bit. */
if (expr_sig->pin_count() > 1) {
NetLogic*log = new NetLogic(scope, des->local_symbol(path),
expr_sig->pin_count()+1,
NetLogic::OR);
for (unsigned idx = 0; idx < expr_sig->pin_count(); idx += 1)
connect(log->pin(idx+1), expr_sig->pin(idx));
NetNet*tmp = new NetTmp(scope, des->local_symbol(path));
tmp->local_flag(true);
connect(tmp->pin(0), log->pin(0));
des->add_node(log);
expr_sig = tmp;
}
assert(expr_sig->pin_count() == 1);
/* 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
handle padding below.
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. */
unsigned dwidth = (iwidth > width)? width : iwidth;
NetNet*sig = new NetNet(scope, des->local_symbol(path),
NetNet::WIRE, width);
sig->local_flag(true);
if (fal_sig->pin_count() < dwidth)
fal_sig = pad_to_width(des, fal_sig, dwidth);
if (tru_sig->pin_count() < dwidth)
tru_sig = pad_to_width(des, tru_sig, dwidth);
/* Make the device and connect its outputs to the osig and
inputs to the tru and false case nets. Also connect the
selector bit to the sel input.
The inputs are the 0 (false) connected to fal_sig and 1
(true) connected to tru_sig. */
NetMux*mux = new NetMux(scope, des->local_symbol(path), dwidth, 2, 1);
connect(mux->pin_Sel(0), expr_sig->pin(0));
for (unsigned idx = 0 ; idx < dwidth ; idx += 1) {
connect(mux->pin_Result(idx), sig->pin(idx));
connect(mux->pin_Data(idx,0), fal_sig->pin(idx));
connect(mux->pin_Data(idx,1), tru_sig->pin(idx));
}
/* If the MUX device result is too narrow to fill out the
desired result, pad with zeros by creating a NetConst device. */
if (dwidth < width) {
verinum vpad (verinum::V0, width-dwidth);
NetConst*pad = new NetConst(scope, des->local_symbol(path), vpad);
des->add_node(pad);
for (unsigned idx = dwidth ; idx < width ; idx += 1)
connect(sig->pin(idx), pad->pin(idx-dwidth));
}
des->add_node(mux);
return sig;
}
NetNet* PEUnary::elaborate_net(Design*des, const string&path,
unsigned width,
unsigned long rise,
unsigned long fall,
unsigned long decay,
Link::strength_t drive0,
Link::strength_t drive1) const
{
NetScope*scope = des->find_scope(path);
assert(scope);
// Some unary operands allow the operand to be
// self-determined, and some do not.
unsigned owidth = 0;
switch (op_) {
case '~':
case '-':
owidth = width;
break;
}
NetNet* sig;
NetLogic*gate;
// 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 {
verinum*val = expr_->eval_const(des, path);
if (val == 0)
break;
sig = new NetNet(scope, des->local_symbol(path),
NetNet::WIRE, width);
sig->local_flag(true);
verinum tmp(v_not(*val) + verinum(1UL, width), width);
NetConst*con = new NetConst(scope, des->local_symbol(path), tmp);
for (unsigned idx = 0 ; idx < width ; idx += 1)
connect(sig->pin(idx), con->pin(idx));
des->add_node(con);
return sig;
} while (0);
NetNet* sub_sig = expr_->elaborate_net(des, path, owidth, 0, 0, 0);
if (sub_sig == 0) {
des->errors += 1;
return 0;
}
assert(sub_sig);
switch (op_) {
case '~': // Bitwise NOT
sig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE,
sub_sig->pin_count());
sig->local_flag(true);
for (unsigned idx = 0 ; idx < sub_sig->pin_count() ; idx += 1) {
gate = new NetLogic(scope, des->local_symbol(path),
2, NetLogic::NOT);
connect(gate->pin(1), sub_sig->pin(idx));
connect(gate->pin(0), sig->pin(idx));
des->add_node(gate);
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
}
break;
case 'N': // Reduction NOR
case '!': // Reduction NOT
sig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
sig->local_flag(true);
gate = new NetLogic(scope, des->local_symbol(path),
1+sub_sig->pin_count(), NetLogic::NOR);
connect(gate->pin(0), sig->pin(0));
for (unsigned idx = 0 ; idx < sub_sig->pin_count() ; idx += 1)
connect(gate->pin(idx+1), sub_sig->pin(idx));
des->add_node(gate);
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
break;
case '&': // Reduction AND
sig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
sig->local_flag(true);
gate = new NetLogic(scope, des->local_symbol(path),
1+sub_sig->pin_count(), NetLogic::AND);
connect(gate->pin(0), sig->pin(0));
for (unsigned idx = 0 ; idx < sub_sig->pin_count() ; idx += 1)
connect(gate->pin(idx+1), sub_sig->pin(idx));
des->add_node(gate);
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
break;
case '|': // Reduction OR
sig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
sig->local_flag(true);
gate = new NetLogic(scope, des->local_symbol(path),
1+sub_sig->pin_count(), NetLogic::OR);
connect(gate->pin(0), sig->pin(0));
for (unsigned idx = 0 ; idx < sub_sig->pin_count() ; idx += 1)
connect(gate->pin(idx+1), sub_sig->pin(idx));
des->add_node(gate);
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
break;
case '^': // Reduction XOR
sig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
sig->local_flag(true);
gate = new NetLogic(scope, des->local_symbol(path),
1+sub_sig->pin_count(), NetLogic::XOR);
connect(gate->pin(0), sig->pin(0));
for (unsigned idx = 0 ; idx < sub_sig->pin_count() ; idx += 1)
connect(gate->pin(idx+1), sub_sig->pin(idx));
des->add_node(gate);
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
break;
case 'A': // Reduction NAND (~&)
sig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
sig->local_flag(true);
gate = new NetLogic(scope, des->local_symbol(path),
1+sub_sig->pin_count(), NetLogic::NAND);
connect(gate->pin(0), sig->pin(0));
for (unsigned idx = 0 ; idx < sub_sig->pin_count() ; idx += 1)
connect(gate->pin(idx+1), sub_sig->pin(idx));
des->add_node(gate);
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
break;
case 'X': // Reduction XNOR (~^)
sig = new NetNet(scope, des->local_symbol(path), NetNet::WIRE);
sig->local_flag(true);
gate = new NetLogic(scope, des->local_symbol(path),
1+sub_sig->pin_count(), NetLogic::XNOR);
connect(gate->pin(0), sig->pin(0));
for (unsigned idx = 0 ; idx < sub_sig->pin_count() ; idx += 1)
connect(gate->pin(idx+1), sub_sig->pin(idx));
des->add_node(gate);
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
break;
case '-': // Unary 2's complement.
sig = new NetNet(scope, des->local_symbol(path),
NetNet::WIRE, sub_sig->pin_count());
sig->local_flag(true);
switch (sub_sig->pin_count()) {
case 0:
assert(0);
break;
case 1:
gate = new NetLogic(scope, des->local_symbol(path),
2, NetLogic::BUF);
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;
case 2:
gate = new NetLogic(scope, des->local_symbol(path),
2, NetLogic::BUF);
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);
gate = new NetLogic(scope, des->local_symbol(path),
3, NetLogic::XOR);
connect(gate->pin(0), sig->pin(1));
connect(gate->pin(1), sub_sig->pin(0));
connect(gate->pin(2), sub_sig->pin(1));
des->add_node(gate);
gate->rise_time(rise);
gate->fall_time(fall);
gate->decay_time(decay);
break;
default:
cerr << get_line() << ": internal error: Wide unary "
<< "minus not supported here." << endl;
des->errors += 1;
sig = 0;
break;
}
break;
default:
cerr << "internal error: Unhandled UNARY '" << op_ << "'" << endl;
sig = 0;
}
return sig;
}
/*
* $Log: elab_net.cc,v $
* Revision 1.77 2001/10/28 01:14:53 steve
* NetObj constructor finally requires a scope.
*
* Revision 1.76 2001/10/16 02:19:26 steve
* Support IVL_LPM_DIVIDE for structural divide.
*
* Revision 1.75 2001/09/14 04:20:49 steve
* dead code.
*
* Revision 1.74 2001/09/14 04:16:52 steve
* Elaborate == to NetCompare instead of XNOR and AND
* gates. This allows code generators to generate
* better code in certain cases.
*
* Revision 1.73 2001/07/25 03:10:48 steve
* Create a config.h.in file to hold all the config
* junk, and support gcc 3.0. (Stephan Boettcher)
*
* Revision 1.72 2001/07/07 04:37:18 steve
* Generate !== an an inverted ===
*
* Revision 1.71 2001/07/04 22:59:25 steve
* handle left shifter in dll output.
*
* Revision 1.70 2001/07/01 23:37:48 steve
* Make sure tmp net gets connected to ramdq output
*
* Revision 1.69 2001/06/16 23:45:05 steve
* Add support for structural multiply in t-dll.
* Add code generators and vvp support for both
* structural and behavioral multiply.
*
* Revision 1.68 2001/06/15 04:14:18 steve
* Generate vvp code for GT and GE comparisons.
*
* Revision 1.67 2001/06/07 02:12:43 steve
* Support structural addition.
*
* Revision 1.66 2001/05/17 03:34:47 steve
* Make error message include error: prefix.
*
* Revision 1.65 2001/04/14 22:50:39 steve
* Less picky about ternary operands.
*
* Revision 1.64 2001/02/15 06:59:36 steve
* FreeBSD port has a maintainer now.
*
* Revision 1.63 2001/02/09 20:18:15 steve
* Detect part select out of range in nets. (PR#138)
*
* Revision 1.62 2001/02/08 01:10:30 steve
* Remove dead code.
*
* Revision 1.61 2001/01/25 02:05:16 steve
* Handle wide net constants with unary minus.
*
* Revision 1.60 2001/01/24 02:52:30 steve
* Handle some special cases of unary 2's complement,
* and improve netlist expression width handling.
*
* Revision 1.59 2001/01/18 03:16:35 steve
* NetMux needs a scope. (PR#115)
*
* Revision 1.58 2001/01/16 04:51:52 steve
* Fix out-of-bound pins for comparator (PR#108)
*
* Revision 1.57 2001/01/10 03:13:23 steve
* Build task outputs as lval instead of nets. (PR#98)
*
* Revision 1.56 2001/01/05 03:19:47 steve
* Fix net division to cope with small output sizes.
*
* Revision 1.55 2000/12/01 02:55:37 steve
* Detect part select errors on l-values.
*
* Revision 1.54 2000/11/04 05:06:04 steve
* pad different width inputs to muxes. (PR#14)
*
* Revision 1.53 2000/10/30 21:35:40 steve
* Detect reverse bit order in part select. (PR#33)
*
* Revision 1.52 2000/10/30 20:55:53 steve
* get width right for reversed part select net. (PR#33)
*
* Revision 1.51 2000/10/14 02:23:02 steve
* Check for missing concat subexpressions (PR#11)
*
* Revision 1.50 2000/10/08 04:59:36 steve
* Fix repeat concatenation with multiple expressions (PR#10)
*
* Revision 1.49 2000/10/07 19:45:42 steve
* Put logic devices into scopes.
*
* Revision 1.48 2000/09/26 05:05:58 steve
* Detect indefinite widths where definite widths are required.
*
* Revision 1.47 2000/09/17 21:26:15 steve
* Add support for modulus (Eric Aardoom)
*
* Revision 1.46 2000/09/07 21:28:51 steve
* more robust abut ternary bit widths.
*
* Revision 1.45 2000/09/02 20:54:20 steve
* Rearrange NetAssign to make NetAssign_ separate.
*
* Revision 1.44 2000/08/18 04:38:57 steve
* Proper error messages when port direction is missing.
*
* Revision 1.43 2000/08/01 22:44:26 steve
* Extend x or z that is top bit of a constant.
*
* Revision 1.42 2000/07/15 05:13:43 steve
* Detect muxing Vz as a bufufN.
*
* Revision 1.41 2000/07/08 04:59:20 steve
* Eleminate reduction gate for 1-bit compares.
*
* Revision 1.40 2000/07/06 18:13:24 steve
* Connect all the l and r bits of a NE expression.
*
* Revision 1.39 2000/06/03 02:13:15 steve
* Output signal of + is a temporary.
*
* Revision 1.38 2000/05/26 05:26:11 steve
* Handle wide conditions in ternary operator.
*
* Revision 1.37 2000/05/16 04:05:15 steve
* Module ports are really special PEIdent
* expressions, because a name can be used
* many places in the port list.
*/
Jump to Line
Something went wrong with that request. Please try again.