1095 lines
30 KiB
C++
1095 lines
30 KiB
C++
/*
|
|
* Copyright (c) 1999-2010 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
|
|
*/
|
|
|
|
# include "config.h"
|
|
|
|
# include <iostream>
|
|
# include <typeinfo>
|
|
|
|
# include "netlist.h"
|
|
# include "netmisc.h"
|
|
# include "compiler.h"
|
|
|
|
NetNet* NetExpr::synthesize(Design*des)
|
|
{
|
|
cerr << get_line() << ": internal error: cannot synthesize expression: "
|
|
<< *this << endl;
|
|
cerr << get_line() << ": : typeid="
|
|
<< typeid(*this).name() << endl;
|
|
des->errors += 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* For the NetEBitSel expression, create a NetMux node that selects a
|
|
* bit from the input.
|
|
*/
|
|
NetNet* NetEBitSel::synthesize(Design*des)
|
|
{
|
|
NetNet*net = sig_->synthesize(des);
|
|
assert(net);
|
|
|
|
NetNet*adr = idx_->synthesize(des);
|
|
if (adr == 0)
|
|
return 0;
|
|
|
|
NetScope*scope = adr->scope();
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::WIRE, 1);
|
|
osig->set_line(*this);
|
|
osig->local_flag(true);
|
|
|
|
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
|
1, net->pin_count(), adr->pin_count());
|
|
mux->set_line(*this);
|
|
des->add_node(mux);
|
|
|
|
for (unsigned idx = 0 ; idx < net->pin_count() ; idx += 1)
|
|
connect(mux->pin_Data(0, idx), net->pin(idx));
|
|
|
|
for (unsigned idx = 0 ; idx < adr->pin_count() ; idx += 1)
|
|
connect(mux->pin_Sel(idx), adr->pin(idx));
|
|
|
|
connect(mux->pin_Result(0), osig->pin(0));
|
|
|
|
return osig;
|
|
}
|
|
|
|
/*
|
|
* Make an LPM_ADD_SUB device from addition operators.
|
|
*/
|
|
NetNet* NetEBAdd::synthesize(Design*des)
|
|
{
|
|
assert((op()=='+') || (op()=='-'));
|
|
|
|
NetNet*lsig = left_->synthesize(des);
|
|
NetNet*rsig = right_->synthesize(des);
|
|
|
|
assert(expr_width() >= lsig->pin_count());
|
|
assert(expr_width() >= rsig->pin_count());
|
|
|
|
lsig = pad_to_width(des, lsig, expr_width());
|
|
rsig = pad_to_width(des, rsig, expr_width());
|
|
|
|
assert(lsig->pin_count() == rsig->pin_count());
|
|
unsigned width=lsig->pin_count();
|
|
|
|
perm_string path = lsig->scope()->local_symbol();
|
|
NetNet*osig = new NetNet(lsig->scope(), path, NetNet::IMPLICIT, width);
|
|
osig->local_flag(true);
|
|
|
|
perm_string oname = osig->scope()->local_symbol();
|
|
NetAddSub *adder = new NetAddSub(lsig->scope(), oname, width);
|
|
for (unsigned idx = 0 ; idx < width; idx += 1) {
|
|
connect(lsig->pin(idx), adder->pin_DataA(idx));
|
|
connect(rsig->pin(idx), adder->pin_DataB(idx));
|
|
connect(osig->pin(idx), adder->pin_Result(idx));
|
|
}
|
|
des->add_node(adder);
|
|
|
|
switch (op()) {
|
|
case '+':
|
|
adder->attribute(perm_string::literal("LPM_Direction"), verinum("ADD"));
|
|
break;
|
|
case '-':
|
|
adder->attribute(perm_string::literal("LPM_Direction"), verinum("SUB"));
|
|
break;
|
|
}
|
|
|
|
return osig;
|
|
}
|
|
|
|
/*
|
|
* The bitwise logic operators are turned into discrete gates pretty
|
|
* easily. Synthesize the left and right sub-expressions to get
|
|
* signals, then just connect a single gate to each bit of the vector
|
|
* of the expression.
|
|
*/
|
|
NetNet* NetEBBits::synthesize(Design*des)
|
|
{
|
|
NetNet*lsig = left_->synthesize(des);
|
|
NetNet*rsig = right_->synthesize(des);
|
|
|
|
NetScope*scope = lsig->scope();
|
|
assert(scope);
|
|
string path = des->local_symbol(scope->name());
|
|
|
|
if (lsig->pin_count() != rsig->pin_count()) {
|
|
cerr << get_line() << ": internal error: bitwise (" << op_
|
|
<< ") widths do not match: " << lsig->pin_count()
|
|
<< " != " << rsig->pin_count() << endl;
|
|
cerr << get_line() << ": : width="
|
|
<< lsig->pin_count() << ": " << *left_ << endl;
|
|
cerr << get_line() << ": : width="
|
|
<< rsig->pin_count() << ": " << *right_ << endl;
|
|
return 0;
|
|
}
|
|
|
|
assert(lsig->pin_count() == rsig->pin_count());
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, lsig->pin_count());
|
|
osig->local_flag(true);
|
|
|
|
for (unsigned idx = 0 ; idx < osig->pin_count() ; idx += 1) {
|
|
perm_string oname = scope->local_symbol();
|
|
NetLogic*gate;
|
|
|
|
/* If the rsig bit is constant, then look for special
|
|
cases that I can use to reduce the generated
|
|
logic. If I find one, then handle it immediately and
|
|
skip the rest of the processing of this bit. */
|
|
if (rsig->pin(idx).nexus()->drivers_constant()) {
|
|
verinum::V bval = rsig->pin(idx).nexus()->driven_value();
|
|
|
|
/* (A & 0) is (0) */
|
|
if ((op() == '&') && bval == verinum::V0) {
|
|
connect(osig->pin(idx), rsig->pin(idx));
|
|
continue;
|
|
}
|
|
|
|
/* (A & 1) is A */
|
|
if ((op() == '&') && bval == verinum::V1) {
|
|
connect(osig->pin(idx), lsig->pin(idx));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (op()) {
|
|
case '&':
|
|
gate = new NetLogic(scope, oname, 3, NetLogic::AND);
|
|
break;
|
|
case '|':
|
|
gate = new NetLogic(scope, oname, 3, NetLogic::OR);
|
|
break;
|
|
case '^':
|
|
gate = new NetLogic(scope, oname, 3, NetLogic::XOR);
|
|
break;
|
|
case 'O':
|
|
gate = new NetLogic(scope, oname, 3, NetLogic::NOR);
|
|
break;
|
|
case 'X':
|
|
gate = new NetLogic(scope, oname, 3, NetLogic::XNOR);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
connect(osig->pin(idx), gate->pin(0));
|
|
connect(lsig->pin(idx), gate->pin(1));
|
|
connect(rsig->pin(idx), gate->pin(2));
|
|
|
|
des->add_node(gate);
|
|
}
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEBComp::synthesize(Design*des)
|
|
{
|
|
NetEConst*lcon = reinterpret_cast<NetEConst*>(left_);
|
|
NetEConst*rcon = reinterpret_cast<NetEConst*>(right_);
|
|
|
|
/* Handle the special case where one of the inputs is constant
|
|
0. We can use an OR gate to do the comparison. Synthesize
|
|
the non-const side as normal, then or(nor) the signals
|
|
together to get result. */
|
|
if ((rcon && (rcon->value() == verinum(0UL,rcon->expr_width())))
|
|
|| (lcon && (lcon->value() == verinum(0UL,lcon->expr_width())))) {
|
|
|
|
NetNet*lsig = rcon
|
|
? left_->synthesize(des)
|
|
: right_->synthesize(des);
|
|
NetScope*scope = lsig->scope();
|
|
assert(scope);
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, 1);
|
|
osig->local_flag(true);
|
|
|
|
NetLogic*gate;
|
|
switch (op_) {
|
|
case 'e':
|
|
case 'E':
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
|
lsig->pin_count()+1, NetLogic::NOR);
|
|
break;
|
|
case 'n':
|
|
case 'N':
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
|
lsig->pin_count()+1, NetLogic::OR);
|
|
break;
|
|
|
|
case '>':
|
|
/* sig > 0 is true if any bit in sig is set. This
|
|
is very much like sig != 0. (0 > sig) shouldn't
|
|
happen. */
|
|
if (rcon) {
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
|
lsig->pin_count()+1, NetLogic::OR);
|
|
} else {
|
|
assert(0);
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
|
lsig->pin_count()+1, NetLogic::NOR);
|
|
}
|
|
break;
|
|
|
|
case '<':
|
|
/* 0 < sig is handled like sig > 0. */
|
|
if (! rcon) {
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
|
lsig->pin_count()+1, NetLogic::OR);
|
|
} else {
|
|
assert(0);
|
|
gate = new NetLogic(scope, scope->local_symbol(),
|
|
lsig->pin_count()+1, NetLogic::NOR);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
connect(gate->pin(0), osig->pin(0));
|
|
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
|
|
connect(gate->pin(idx+1), lsig->pin(idx));
|
|
|
|
des->add_node(gate);
|
|
return osig;
|
|
}
|
|
|
|
NetNet*lsig = left_->synthesize(des);
|
|
NetNet*rsig = right_->synthesize(des);
|
|
|
|
NetScope*scope = lsig->scope();
|
|
assert(scope);
|
|
|
|
unsigned width = lsig->pin_count();
|
|
if (rsig->pin_count() > lsig->pin_count())
|
|
width = rsig->pin_count();
|
|
|
|
lsig = pad_to_width(des, lsig, width);
|
|
rsig = pad_to_width(des, rsig, width);
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, 1);
|
|
osig->local_flag(true);
|
|
|
|
/* Handle the special case of a single bit equality
|
|
operation. Make an XNOR gate instead of a comparator. */
|
|
if ((width == 1) && ((op_ == 'e') || (op_ == 'E'))) {
|
|
NetLogic*gate = new NetLogic(scope, scope->local_symbol(),
|
|
3, NetLogic::XNOR);
|
|
connect(gate->pin(0), osig->pin(0));
|
|
connect(gate->pin(1), lsig->pin(0));
|
|
connect(gate->pin(2), rsig->pin(0));
|
|
des->add_node(gate);
|
|
return osig;
|
|
}
|
|
|
|
/* Handle the special case of a single bit inequality
|
|
operation. This is similar to single bit equality, but uses
|
|
an XOR instead of an XNOR gate. */
|
|
if ((width == 1) && ((op_ == 'n') || (op_ == 'N'))) {
|
|
NetLogic*gate = new NetLogic(scope, scope->local_symbol(),
|
|
3, NetLogic::XOR);
|
|
connect(gate->pin(0), osig->pin(0));
|
|
connect(gate->pin(1), lsig->pin(0));
|
|
connect(gate->pin(2), rsig->pin(0));
|
|
des->add_node(gate);
|
|
return osig;
|
|
}
|
|
|
|
|
|
NetCompare*dev = new NetCompare(scope, scope->local_symbol(), width);
|
|
des->add_node(dev);
|
|
|
|
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx += 1)
|
|
connect(dev->pin_DataA(idx), lsig->pin(idx));
|
|
|
|
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx += 1)
|
|
connect(dev->pin_DataB(idx), rsig->pin(idx));
|
|
|
|
|
|
switch (op_) {
|
|
case '<':
|
|
connect(dev->pin_ALB(), osig->pin(0));
|
|
break;
|
|
case '>':
|
|
connect(dev->pin_AGB(), osig->pin(0));
|
|
break;
|
|
case 'e': // ==
|
|
case 'E': // === ?
|
|
connect(dev->pin_AEB(), osig->pin(0));
|
|
break;
|
|
case 'G': // >=
|
|
connect(dev->pin_AGEB(), osig->pin(0));
|
|
break;
|
|
case 'L': // <=
|
|
connect(dev->pin_ALEB(), osig->pin(0));
|
|
break;
|
|
case 'n': // !=
|
|
case 'N': // !==
|
|
connect(dev->pin_ANEB(), osig->pin(0));
|
|
break;
|
|
|
|
default:
|
|
cerr << get_line() << ": internal error: cannot synthesize "
|
|
"comparison: " << *this << endl;
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEBMult::synthesize(Design*des)
|
|
{
|
|
NetNet*lsig = left_->synthesize(des);
|
|
NetNet*rsig = right_->synthesize(des);
|
|
|
|
if (lsig == 0)
|
|
return 0;
|
|
|
|
if (rsig == 0)
|
|
return 0;
|
|
|
|
NetScope*scope = lsig->scope();
|
|
assert(scope);
|
|
|
|
NetMult*mult = new NetMult(scope, scope->local_symbol(),
|
|
expr_width(),
|
|
lsig->pin_count(),
|
|
rsig->pin_count());
|
|
des->add_node(mult);
|
|
|
|
mult->set_signed( has_sign() );
|
|
mult->set_line(*this);
|
|
|
|
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));
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, expr_width());
|
|
osig->local_flag(true);
|
|
|
|
for (unsigned idx = 0 ; idx < osig->pin_count() ; idx += 1)
|
|
connect(mult->pin_Result(idx), osig->pin(idx));
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEBDiv::synthesize(Design*des)
|
|
{
|
|
NetNet*lsig = left_->synthesize(des);
|
|
NetNet*rsig = right_->synthesize(des);
|
|
|
|
NetScope*scope = lsig->scope();
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, expr_width());
|
|
osig->local_flag(true);
|
|
|
|
switch (op()) {
|
|
|
|
case '/': {
|
|
NetDivide*div = new NetDivide(scope, scope->local_symbol(),
|
|
expr_width(),
|
|
lsig->pin_count(),
|
|
rsig->pin_count());
|
|
des->add_node(div);
|
|
|
|
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));
|
|
for (unsigned idx = 0 ; idx < osig->pin_count() ; idx += 1)
|
|
connect(div->pin_Result(idx), osig->pin(idx));
|
|
break;
|
|
}
|
|
|
|
case '%': {
|
|
NetModulo*div = new NetModulo(scope, scope->local_symbol(),
|
|
expr_width(),
|
|
lsig->pin_count(),
|
|
rsig->pin_count());
|
|
des->add_node(div);
|
|
|
|
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));
|
|
for (unsigned idx = 0 ; idx < osig->pin_count() ; idx += 1)
|
|
connect(div->pin_Result(idx), osig->pin(idx));
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
cerr << get_line() << ": internal error: "
|
|
<< "NetEBDiv has unexpected op() code: "
|
|
<< op() << endl;
|
|
des->errors += 1;
|
|
|
|
delete osig;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEBLogic::synthesize(Design*des)
|
|
{
|
|
NetNet*lsig = left_->synthesize(des);
|
|
NetNet*rsig = right_->synthesize(des);
|
|
|
|
if (lsig == 0)
|
|
return 0;
|
|
|
|
if (rsig == 0)
|
|
return 0;
|
|
|
|
NetScope*scope = lsig->scope();
|
|
assert(scope);
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, 1);
|
|
osig->local_flag(true);
|
|
|
|
|
|
if (op() == 'o') {
|
|
|
|
/* Logic OR can handle the reduction *and* the logical
|
|
comparison with a single wide OR gate. So handle this
|
|
magically. */
|
|
|
|
perm_string oname = scope->local_symbol();
|
|
|
|
NetLogic*olog = new NetLogic(scope, oname,
|
|
lsig->pin_count()+rsig->pin_count()+1,
|
|
NetLogic::OR);
|
|
|
|
connect(osig->pin(0), olog->pin(0));
|
|
|
|
unsigned pin = 1;
|
|
for (unsigned idx = 0 ; idx < lsig->pin_count() ; idx = 1)
|
|
connect(olog->pin(pin+idx), lsig->pin(idx));
|
|
|
|
pin += lsig->pin_count();
|
|
for (unsigned idx = 0 ; idx < rsig->pin_count() ; idx = 1)
|
|
connect(olog->pin(pin+idx), rsig->pin(idx));
|
|
|
|
des->add_node(olog);
|
|
|
|
} else {
|
|
assert(op() == 'a');
|
|
|
|
/* Create the logic AND gate. This is a single bit
|
|
output, with inputs for each of the operands. */
|
|
NetLogic*olog;
|
|
perm_string oname = scope->local_symbol();
|
|
|
|
olog = new NetLogic(scope, oname, 3, NetLogic::AND);
|
|
olog->set_line(*this);
|
|
|
|
connect(osig->pin(0), olog->pin(0));
|
|
des->add_node(olog);
|
|
|
|
/* Here, I need to reduce the parameters with
|
|
reduction or. Only do this if we must. */
|
|
if (lsig->pin_count() > 1)
|
|
lsig = reduction_or(des, lsig);
|
|
|
|
if (rsig->pin_count() > 1)
|
|
rsig = reduction_or(des, rsig);
|
|
|
|
/* By this point, the left and right parameters have been
|
|
reduced to single bit values. Now we just connect them to
|
|
the logic gate. */
|
|
assert(lsig->pin_count() == 1);
|
|
connect(lsig->pin(0), olog->pin(1));
|
|
|
|
if (rsig->pin_count() != 1) {
|
|
cerr << olog->get_line() << ": internal error: "
|
|
<< "right argument not reduced. expr=" << *this << endl;
|
|
}
|
|
|
|
assert(rsig->pin_count() == 1);
|
|
connect(rsig->pin(0), olog->pin(2));
|
|
}
|
|
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEBShift::synthesize(Design*des)
|
|
{
|
|
if (! dynamic_cast<NetEConst*>(right_)) {
|
|
NetExpr*tmp = right_->eval_tree();
|
|
if (tmp) {
|
|
delete right_;
|
|
right_ = tmp;
|
|
}
|
|
}
|
|
|
|
NetNet*lsig = left_->synthesize(des);
|
|
if (lsig == 0)
|
|
return 0;
|
|
|
|
bool right_flag = op_ == 'r' || op_ == 'R';
|
|
bool signed_flag = op_ == 'R';
|
|
|
|
NetScope*scope = lsig->scope();
|
|
|
|
/* Detect the special case where the shift amount is
|
|
constant. Evaluate the shift amount, and simply reconnect
|
|
the left operand to the output, but shifted. */
|
|
if (NetEConst*rcon = dynamic_cast<NetEConst*>(right_)) {
|
|
verinum shift_v = rcon->value();
|
|
long shift = shift_v.as_long();
|
|
|
|
if (op() == 'r')
|
|
shift = 0-shift;
|
|
|
|
if (shift == 0)
|
|
return lsig;
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, expr_width());
|
|
osig->local_flag(true);
|
|
|
|
NetConst*zcon = new NetConst(scope, scope->local_symbol(),
|
|
verinum::V0);
|
|
des->add_node(zcon);
|
|
NetNet*zsig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::WIRE, 1);
|
|
connect(zcon->pin(0), zsig->pin(0));
|
|
|
|
if (shift > 0) {
|
|
unsigned long ushift = shift;
|
|
for (unsigned idx = 0; idx < osig->pin_count(); idx += 1)
|
|
if (idx < ushift) {
|
|
connect(osig->pin(idx), zsig->pin(0));
|
|
} else {
|
|
connect(osig->pin(idx), lsig->pin(idx-ushift));
|
|
}
|
|
} else {
|
|
unsigned long dshift = 0-shift;
|
|
for (unsigned idx = 0; idx < osig->pin_count() ; idx += 1)
|
|
if (idx+dshift < lsig->pin_count())
|
|
connect(osig->pin(idx), lsig->pin(idx+dshift));
|
|
else
|
|
connect(osig->pin(idx), zsig->pin(0));
|
|
}
|
|
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet*rsig = right_->synthesize(des);
|
|
if (rsig == 0)
|
|
return 0;
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, expr_width());
|
|
osig->local_flag(true);
|
|
|
|
NetCLShift*dev = new NetCLShift(scope, scope->local_symbol(),
|
|
osig->pin_count(),
|
|
rsig->pin_count(),
|
|
right_flag, signed_flag);
|
|
des->add_node(dev);
|
|
|
|
for (unsigned idx = 0 ; idx < dev->width() ; idx += 1)
|
|
connect(dev->pin_Result(idx), osig->pin(idx));
|
|
|
|
assert(lsig->pin_count() >= dev->width());
|
|
for (unsigned idx = 0 ; idx < dev->width() ; idx += 1)
|
|
connect(dev->pin_Data(idx), lsig->pin(idx));
|
|
|
|
for (unsigned idx = 0 ; idx < dev->width_dist() ; idx += 1)
|
|
connect(dev->pin_Distance(idx), rsig->pin(idx));
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEConcat::synthesize(Design*des)
|
|
{
|
|
/* First, synthesize the operands. */
|
|
NetNet**tmp = new NetNet*[parms_.count()];
|
|
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1)
|
|
tmp[idx] = parms_[idx]->synthesize(des);
|
|
|
|
assert(tmp[0]);
|
|
NetScope*scope = tmp[0]->scope();
|
|
assert(scope);
|
|
|
|
/* Make a NetNet object to carry the output vector. */
|
|
perm_string path = scope->local_symbol();
|
|
NetNet*osig = new NetNet(scope, path, NetNet::IMPLICIT, expr_width());
|
|
osig->local_flag(true);
|
|
|
|
/* Connect the output vector to the operands. */
|
|
unsigned obit = 0;
|
|
for (unsigned idx = parms_.count() ; idx > 0 ; idx -= 1) {
|
|
|
|
assert(tmp[idx-1]);
|
|
|
|
for (unsigned bit = 0; bit < tmp[idx-1]->pin_count(); bit += 1) {
|
|
connect(osig->pin(obit), tmp[idx-1]->pin(bit));
|
|
obit += 1;
|
|
}
|
|
|
|
if (tmp[idx-1]->local_flag() && tmp[idx-1]->get_refs() == 0)
|
|
delete tmp[idx-1];
|
|
}
|
|
|
|
delete[]tmp;
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEConst::synthesize(Design*des)
|
|
{
|
|
NetScope*scope = des->find_root_scope();
|
|
assert(scope);
|
|
|
|
perm_string path = scope->local_symbol();
|
|
unsigned width=expr_width();
|
|
|
|
NetNet*osig = new NetNet(scope, path, NetNet::IMPLICIT, width);
|
|
osig->local_flag(true);
|
|
osig->set_signed(has_sign());
|
|
NetConst*con = new NetConst(scope, scope->local_symbol(), value());
|
|
for (unsigned idx = 0 ; idx < width; idx += 1)
|
|
connect(osig->pin(idx), con->pin(idx));
|
|
|
|
des->add_node(con);
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEMemory::synthesize(Design*des)
|
|
{
|
|
NetScope*scope = mem_->scope();
|
|
|
|
NetNet*explode = mem_->explode_to_reg();
|
|
unsigned width = expr_width();
|
|
|
|
assert(idx_);
|
|
NetNet*addr = idx_->synthesize(des);
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT,
|
|
width);
|
|
osig->set_line(*this);
|
|
|
|
if (explode) {
|
|
if (debug_synth)
|
|
cerr << get_line() << ": debug: synthesize read of "
|
|
<< explode->pin_count() << " bit exploded memory." << endl;
|
|
|
|
/* Only make a mux big enough to address the words that
|
|
the address can generate. (If the address is
|
|
0-extended, then only the low words are addressable.) */
|
|
unsigned use_count = mem_->count();
|
|
if (use_count > (1U << addr->pin_count())) {
|
|
use_count = 1 << addr->pin_count();
|
|
|
|
if (debug_synth)
|
|
cerr << get_line() << ": debug: "
|
|
<< "Index expression can only address "
|
|
<< use_count << " of "
|
|
<< mem_->count() << " words." << endl;
|
|
}
|
|
|
|
/* This is a reference to an exploded memory. So locate
|
|
the reg vector and use the addr expression as a
|
|
select into a MUX. */
|
|
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
|
width, use_count, addr->pin_count());
|
|
des->add_node(mux);
|
|
mux->set_line(*this);
|
|
|
|
for (unsigned idx = 0 ; idx < width ; idx += 1)
|
|
connect(mux->pin_Result(idx), osig->pin(idx));
|
|
for (unsigned idx = 0 ; idx < mux->sel_width() ; idx += 1)
|
|
connect(mux->pin_Sel(idx), addr->pin(idx));
|
|
|
|
for (unsigned wrd = 0 ; wrd < use_count ; wrd += 1)
|
|
for (unsigned idx = 0 ; idx < width ; idx += 1) {
|
|
unsigned bit = wrd*width + idx;
|
|
connect(mux->pin_Data(idx, wrd), explode->pin(bit));
|
|
}
|
|
|
|
if (debug_synth)
|
|
cerr << get_line() << ": debug: synthesis done." << endl;
|
|
|
|
} else {
|
|
cerr << get_line() << ": internal error: Synthesize memory "
|
|
<< "expression that is not exploded?" << endl;
|
|
des->errors += 1;
|
|
}
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetECReal::synthesize(Design*des)
|
|
{
|
|
cerr << get_line() << ": error: Real constants are "
|
|
<< "not synthesizable." << endl;
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The bitwise unary logic operator (there is only one) is turned
|
|
* into discrete gates just as easily as the binary ones above.
|
|
*/
|
|
NetNet* NetEUBits::synthesize(Design*des)
|
|
{
|
|
NetNet*isig = expr_->synthesize(des);
|
|
|
|
NetScope*scope = isig->scope();
|
|
assert(scope);
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, isig->pin_count());
|
|
osig->local_flag(true);
|
|
|
|
for (unsigned idx = 0 ; idx < osig->pin_count() ; idx += 1) {
|
|
perm_string oname = scope->local_symbol();
|
|
NetLogic*gate;
|
|
|
|
switch (op()) {
|
|
case '~':
|
|
gate = new NetLogic(scope, oname, 2, NetLogic::NOT);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
connect(osig->pin(idx), gate->pin(0));
|
|
connect(isig->pin(idx), gate->pin(1));
|
|
|
|
des->add_node(gate);
|
|
}
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetEUFunc::synthesize(Design*des)
|
|
{
|
|
assert(func_);
|
|
|
|
NetFuncDef* def = func_->func_def();
|
|
assert(def);
|
|
|
|
assert(parms_.count() == def->port_count());
|
|
|
|
svector<NetNet*> inports (parms_.count());
|
|
|
|
unsigned errors = 0;
|
|
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
|
|
inports[idx] = parms_[idx]->synthesize(des);
|
|
if (inports[idx] == 0)
|
|
errors += 1;
|
|
}
|
|
|
|
if (errors > 0) {
|
|
cerr << get_line() << ": error: "
|
|
<< "Cannot continue with function instance synthesis."
|
|
<< endl;
|
|
return 0;
|
|
}
|
|
|
|
NetNet*out = def->synthesize(des, inports);
|
|
|
|
if (! out) {
|
|
cerr << get_line() << ": error: "
|
|
<< "User defined functions do not synthesize." << endl;
|
|
def->dump(cerr, 4);
|
|
des->errors += 1;
|
|
return 0;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
NetNet* NetFuncDef::synthesize(Design*des, const svector<NetNet*>&inports_)
|
|
{
|
|
|
|
/* First run synthesis for the function definition as if this
|
|
where a toplevel process. This will create the logic that
|
|
the function represents, but connected to the port nets for
|
|
the function definition. We will detach those common ports
|
|
later. */
|
|
NexusSet nex_set;
|
|
statement_->nex_output(nex_set);
|
|
|
|
const perm_string tmp1 = scope()->local_symbol();
|
|
NetNet*nex_out = new NetNet(scope(), tmp1, NetNet::WIRE,
|
|
nex_set.count());
|
|
for (unsigned idx = 0 ; idx < nex_out->pin_count() ; idx += 1)
|
|
connect(nex_set[idx], nex_out->pin(idx));
|
|
|
|
bool flag = statement_->synth_async_noaccum(des, scope(), false, 0,
|
|
nex_out, nex_out);
|
|
|
|
if (!flag) {
|
|
delete nex_out;
|
|
return 0;
|
|
}
|
|
|
|
/* Connect the inports_ vectors to the input ports and detach
|
|
the static arg ports themselves. This moves the input from
|
|
the synthesized device from the static ports to the actual
|
|
ports from the instance context. */
|
|
for (unsigned idx = 0 ; idx < port_count() ; idx += 1) {
|
|
NetNet*in = inports_[idx];
|
|
NetNet*arg = ports_[idx];
|
|
|
|
in = pad_to_width(des, in, arg->pin_count());
|
|
|
|
for (unsigned pin = 0 ; pin < arg->pin_count() ; pin += 1) {
|
|
connect(in->pin(pin), arg->pin(pin));
|
|
arg->pin(pin).unlink();
|
|
}
|
|
}
|
|
|
|
/* Detach the output signal from the synthesized result. We
|
|
use instead the nex_out that was returned from the
|
|
synthesis of the function. This is how we account for the
|
|
fact that the function may be synthesized multiple times to
|
|
go into multiple expression. Each synthesis needs a unique
|
|
output. */
|
|
for (unsigned idx = 0 ; idx < result_sig_->pin_count() ; idx += 1)
|
|
result_sig_->pin(idx).unlink();
|
|
|
|
return nex_out;
|
|
}
|
|
|
|
NetNet* NetEUReduce::synthesize(Design*des)
|
|
{
|
|
NetNet*isig = expr_->synthesize(des);
|
|
|
|
NetScope*scope = isig->scope();
|
|
assert(scope);
|
|
|
|
NetNet*osig = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, 1);
|
|
osig->local_flag(true);
|
|
|
|
perm_string oname = scope->local_symbol();
|
|
NetLogic*gate;
|
|
|
|
switch (op()) {
|
|
case 'N':
|
|
case '!':
|
|
gate = new NetLogic(scope, oname, isig->pin_count()+1,
|
|
NetLogic::NOR);
|
|
break;
|
|
|
|
case '&':
|
|
gate = new NetLogic(scope, oname, isig->pin_count()+1,
|
|
NetLogic::AND);
|
|
break;
|
|
|
|
case '|':
|
|
gate = new NetLogic(scope, oname, isig->pin_count()+1,
|
|
NetLogic::OR);
|
|
break;
|
|
|
|
case '^':
|
|
gate = new NetLogic(scope, oname, isig->pin_count()+1,
|
|
NetLogic::XOR);
|
|
break;
|
|
|
|
case 'A':
|
|
gate = new NetLogic(scope, oname, isig->pin_count()+1,
|
|
NetLogic::NAND);
|
|
break;
|
|
|
|
case 'X':
|
|
gate = new NetLogic(scope, oname, isig->pin_count()+1,
|
|
NetLogic::XNOR);
|
|
break;
|
|
|
|
default:
|
|
cerr << get_line() << ": internal error: "
|
|
<< "Unable to synthesize " << *this << "." << endl;
|
|
return 0;
|
|
}
|
|
|
|
des->add_node(gate);
|
|
connect(gate->pin(0), osig->pin(0));
|
|
for (unsigned idx = 0 ; idx < isig->pin_count() ; idx += 1)
|
|
connect(gate->pin(1+idx), isig->pin(idx));
|
|
|
|
return osig;
|
|
}
|
|
|
|
NetNet* NetESelect::synthesize(Design *des)
|
|
{
|
|
// XXXX For now, only support pad form
|
|
assert(base_ == 0);
|
|
|
|
NetNet*sub = expr_->synthesize(des);
|
|
if (sub == 0)
|
|
return 0;
|
|
|
|
NetScope*scope = sub->scope();
|
|
NetNet*net = new NetNet(scope, scope->local_symbol(),
|
|
NetNet::IMPLICIT, expr_width());
|
|
if (has_sign()) {
|
|
unsigned idx;
|
|
|
|
for (idx = 0 ; idx < sub->pin_count() ; idx += 1)
|
|
connect(sub->pin(idx), net->pin(idx));
|
|
|
|
for ( ; idx < net->pin_count(); idx += 1)
|
|
connect(sub->pin(sub->pin_count()-1), net->pin(idx));
|
|
|
|
} else {
|
|
unsigned idx;
|
|
for (idx = 0 ; idx < sub->pin_count() ; idx += 1)
|
|
connect(sub->pin(idx), net->pin(idx));
|
|
|
|
NetConst*tmp = new NetConst(scope, scope->local_symbol(),
|
|
verinum::V0);
|
|
|
|
for ( ; idx < net->pin_count() ; idx += 1)
|
|
connect(net->pin(idx), tmp->pin(0));
|
|
|
|
des->add_node(tmp);
|
|
}
|
|
|
|
return net;
|
|
}
|
|
|
|
/*
|
|
* Synthesize a ?: operator as a NetMux device. Connect the condition
|
|
* expression to the select input, then connect the true and false
|
|
* expressions to the B and A inputs. This way, when the select input
|
|
* is one, the B input, which is the true expression, is selected.
|
|
*/
|
|
NetNet* NetETernary::synthesize(Design *des)
|
|
{
|
|
NetNet*csig = cond_->synthesize(des);
|
|
NetNet*tsig = true_val_->synthesize(des);
|
|
NetNet*fsig = false_val_->synthesize(des);
|
|
|
|
perm_string path = csig->scope()->local_symbol();
|
|
|
|
assert(csig->pin_count() == 1);
|
|
|
|
unsigned width=expr_width();
|
|
NetNet*osig = new NetNet(csig->scope(), path, NetNet::IMPLICIT, width);
|
|
osig->local_flag(true);
|
|
|
|
/* Make sure both value operands are the right width. */
|
|
tsig = pad_to_width(des, tsig, width);
|
|
fsig = pad_to_width(des, fsig, width);
|
|
|
|
assert(width <= tsig->pin_count());
|
|
assert(width <= fsig->pin_count());
|
|
|
|
perm_string oname = csig->scope()->local_symbol();
|
|
NetMux *mux = new NetMux(csig->scope(), oname, width, 2, 1);
|
|
mux->set_line(*this);
|
|
for (unsigned idx = 0 ; idx < width; idx += 1) {
|
|
connect(tsig->pin(idx), mux->pin_Data(idx, 1));
|
|
connect(fsig->pin(idx), mux->pin_Data(idx, 0));
|
|
connect(osig->pin(idx), mux->pin_Result(idx));
|
|
}
|
|
des->add_node(mux);
|
|
connect(csig->pin(0), mux->pin_Sel(0));
|
|
|
|
return osig;
|
|
}
|
|
|
|
/*
|
|
* When synthesizing a signal expression, it is usually fine to simply
|
|
* return the NetNet that it refers to. If this is a part select,
|
|
* though, a bit more work needs to be done. Return a temporary that
|
|
* represents the connections to the selected bits.
|
|
*
|
|
* For example, if there is a reg foo, like so:
|
|
* reg [5:0] foo;
|
|
* and this expression node represents a part select foo[3:2], then
|
|
* create a temporary like so:
|
|
*
|
|
* foo
|
|
* +---+
|
|
* | 5 |
|
|
* +---+
|
|
* tmp | 4 |
|
|
* +---+ +---+
|
|
* | 1 | <---> | 3 |
|
|
* +---+ +---+
|
|
* | 0 | <---> | 2 |
|
|
* +---+ +---+
|
|
* | 1 |
|
|
* +---+
|
|
* | 0 |
|
|
* +---+
|
|
* The temporary is marked as a temporary and returned to the
|
|
* caller. This causes the caller to get only the selected part of the
|
|
* signal, and when it hooks up to tmp, it hooks up to the right parts
|
|
* of foo.
|
|
*/
|
|
NetNet* NetESignal::synthesize(Design*des)
|
|
{
|
|
if (warn_unused) {
|
|
if (net_->peek_lref() == 0 && net_->type()==NetNet::REG) {
|
|
cerr << get_line() << ": warning: "
|
|
<< "reg " << net_->name()
|
|
<< " is used by logic but never assigned." << endl;
|
|
}
|
|
|
|
if (net_->peek_lref() == 0 && net_->type()==NetNet::INTEGER) {
|
|
cerr << get_line() << ": warning: "
|
|
<< "reg " << net_->name()
|
|
<< " is used by logic but never assigned." << endl;
|
|
}
|
|
}
|
|
|
|
/* If there is no part select, then the synthesis is trivial. */
|
|
if ((lsi_ == 0) && (msi_ == (net_->pin_count() - 1)))
|
|
return net_;
|
|
|
|
assert(msi_ >= lsi_);
|
|
unsigned wid = msi_ - lsi_ + 1;
|
|
|
|
NetScope*scope = net_->scope();
|
|
assert(scope);
|
|
|
|
perm_string name = scope->local_symbol();
|
|
NetNet*tmp = new NetNet(scope, name, NetNet::WIRE, wid);
|
|
tmp->local_flag(true);
|
|
tmp->set_line(*this);
|
|
|
|
for (unsigned idx = 0 ; idx < wid ; idx += 1)
|
|
connect(tmp->pin(idx), net_->pin(idx+lsi_));
|
|
|
|
return tmp;
|
|
}
|