iverilog/expr_synth.cc

1280 lines
37 KiB
C++

/*
* Copyright (c) 1999-2008 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 "compiler.h"
# include <stdlib.h>
# include <iostream>
# include "netlist.h"
# include "netmisc.h"
# include "ivl_assert.h"
static NetNet* convert_to_real_const(Design*des, NetScope*scope, NetExpr*expr, NetExpr*obj)
{
NetNet* sig;
if (NetEConst*tmp = dynamic_cast<NetEConst*>(expr)) {
verireal vrl(tmp->value().as_double());
NetECReal rlval(vrl);
sig = rlval.synthesize(des, scope);
} else {
cerr << obj->get_fileline() << ": sorry: Cannot convert "
"bit based value (" << *expr << ") to real." << endl;
des->errors += 1;
sig = 0;
}
return sig;
}
/* Note that lsig, rsig and real_args are references. */
static bool process_binary_args(Design*des, NetScope*scope,
NetExpr*left, NetExpr*right,
NetNet*&lsig, NetNet*&rsig, bool&real_args,
NetExpr*obj)
{
if (left->expr_type() == IVL_VT_REAL ||
right->expr_type() == IVL_VT_REAL) {
real_args = true;
/* Currently we will have a runtime assert if both expressions
are not real, though we can convert constants. */
if (left->expr_type() == IVL_VT_REAL) {
lsig = left->synthesize(des, scope);
} else {
lsig = convert_to_real_const(des, scope, left, obj);
}
if (right->expr_type() == IVL_VT_REAL) {
rsig = right->synthesize(des, scope);
} else {
rsig = convert_to_real_const(des, scope, right, obj);
}
} else {
real_args = false;
lsig = left->synthesize(des, scope);
rsig = right->synthesize(des, scope);
}
if (lsig == 0 || rsig == 0) return true;
else return false;
}
NetNet* NetExpr::synthesize(Design*des, NetScope*scope)
{
cerr << get_fileline() << ": internal error: cannot synthesize expression: "
<< *this << endl;
des->errors += 1;
return 0;
}
/*
* Make an LPM_ADD_SUB device from addition operators.
*/
NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope)
{
ivl_assert(*this, (op()=='+') || (op()=='-'));
NetNet *lsig=0, *rsig=0;
bool real_args=false;
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
real_args, this)) {
return 0;
}
assert(expr_width() >= lsig->vector_width());
assert(expr_width() >= rsig->vector_width());
lsig = pad_to_width(des, lsig, expr_width());
rsig = pad_to_width(des, rsig, expr_width());
assert(lsig->vector_width() == rsig->vector_width());
unsigned width=lsig->vector_width();
perm_string path = lsig->scope()->local_symbol();
NetNet*osig = new NetNet(lsig->scope(), path, NetNet::IMPLICIT, width);
osig->local_flag(true);
osig->data_type(expr_type());
perm_string oname = osig->scope()->local_symbol();
NetAddSub *adder = new NetAddSub(lsig->scope(), oname, width);
connect(lsig->pin(0), adder->pin_DataA());
connect(rsig->pin(0), adder->pin_DataB());
connect(osig->pin(0), adder->pin_Result());
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, NetScope*scope)
{
NetNet*lsig = left_->synthesize(des, scope);
NetNet*rsig = right_->synthesize(des, scope);
if (lsig == 0 || rsig == 0) return 0;
/* You cannot do bitwise operations on real values. */
if (lsig->data_type() == IVL_VT_REAL ||
rsig->data_type() == IVL_VT_REAL) {
cerr << get_fileline() << ": error: " << human_readable_op(op_)
<< " operator may not have REAL operands." << endl;
des->errors += 1;
return 0;
}
unsigned width = lsig->vector_width();
if (rsig->vector_width() > width) width = rsig->vector_width();
lsig = pad_to_width(des, lsig, width);
rsig = pad_to_width(des, rsig, width);
assert(lsig->vector_width() == rsig->vector_width());
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, width);
osig->local_flag(true);
osig->data_type(expr_type());
perm_string oname = scope->local_symbol();
NetLogic*gate;
switch (op()) {
case '&':
gate = new NetLogic(scope, oname, 3, NetLogic::AND, width);
break;
case 'A':
gate = new NetLogic(scope, oname, 3, NetLogic::NAND, width);
break;
case '|':
gate = new NetLogic(scope, oname, 3, NetLogic::OR, width);
break;
case '^':
gate = new NetLogic(scope, oname, 3, NetLogic::XOR, width);
break;
case 'O':
gate = new NetLogic(scope, oname, 3, NetLogic::NOR, width);
break;
case 'X':
gate = new NetLogic(scope, oname, 3, NetLogic::XNOR, width);
break;
default:
assert(0);
}
connect(osig->pin(0), gate->pin(0));
connect(lsig->pin(0), gate->pin(1));
connect(rsig->pin(0), gate->pin(2));
gate->set_line(*this);
des->add_node(gate);
return osig;
}
NetNet* NetEBComp::synthesize(Design*des, NetScope*scope)
{
NetNet *lsig=0, *rsig=0;
unsigned width;
bool real_args=false;
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
real_args, this)) {
return 0;
}
if (real_args) {
width = 1;
} else {
width = lsig->vector_width();
if (rsig->vector_width() > width) width = rsig->vector_width();
if (lsig->get_signed())
lsig = pad_to_width_signed(des, lsig, width);
else
lsig = pad_to_width(des, lsig, width);
if (rsig->get_signed())
rsig = pad_to_width_signed(des, rsig, width);
else
rsig = pad_to_width(des, rsig, width);
}
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, 1);
osig->set_line(*this);
osig->local_flag(true);
osig->data_type(IVL_VT_LOGIC);
bool signed_compare = lsig->get_signed() && rsig->get_signed();
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Comparison (" << op_ << ")"
<< " is " << (signed_compare? "signed" : "unsigned")
<< endl;
cerr << get_fileline() << ": : lsig is "
<< (lsig->get_signed()? "signed" : "unsigned")
<< " rsig is " << (rsig->get_signed()? "signed" : "unsigned")
<< endl;
}
if (op_ == 'E' || op_ == 'N') {
NetCaseCmp*gate = new NetCaseCmp(scope, scope->local_symbol(),
width, op_=='E'?true:false);
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));
des->add_node(gate);
return osig;
}
/* Handle the special case of a single bit equality
operation. Make an XNOR gate instead of a comparator. */
if ((width == 1) && (op_ == 'e') && !real_args) {
NetLogic*gate = new NetLogic(scope, scope->local_symbol(),
3, NetLogic::XNOR, 1);
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));
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') && !real_args) {
NetLogic*gate = new NetLogic(scope, scope->local_symbol(),
3, NetLogic::XOR, 1);
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));
des->add_node(gate);
return osig;
}
NetCompare*dev = new NetCompare(scope, scope->local_symbol(), width);
dev->set_line(*this);
des->add_node(dev);
connect(dev->pin_DataA(), lsig->pin(0));
connect(dev->pin_DataB(), rsig->pin(0));
switch (op_) {
case '<':
connect(dev->pin_ALB(), osig->pin(0));
dev->set_signed(signed_compare);
break;
case '>':
connect(dev->pin_AGB(), osig->pin(0));
dev->set_signed(signed_compare);
break;
case 'E': // === ?
if (real_args) {
cerr << get_fileline() << ": error: Case equality may "
"not have real operands." << endl;
des->errors += 1;
return 0;
}
case 'e': // ==
connect(dev->pin_AEB(), osig->pin(0));
break;
case 'G': // >=
connect(dev->pin_AGEB(), osig->pin(0));
dev->set_signed(signed_compare);
break;
case 'L': // <=
connect(dev->pin_ALEB(), osig->pin(0));
dev->set_signed(signed_compare);
break;
case 'N': // !==
if (real_args) {
cerr << get_fileline() << ": error: Case inequality may "
"not have real operands." << endl;
des->errors += 1;
return 0;
}
case 'n': // !=
connect(dev->pin_ANEB(), osig->pin(0));
break;
default:
cerr << get_fileline() << ": internal error: cannot synthesize "
"comparison: " << *this << endl;
des->errors += 1;
return 0;
}
return osig;
}
NetNet* NetEBPow::synthesize(Design*des, NetScope*scope)
{
NetNet *lsig=0, *rsig=0;
unsigned width;
bool real_args=false;
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
real_args, this)) {
return 0;
}
if (real_args) width = 1;
else width = expr_width();
NetPow*powr = new NetPow(scope, scope->local_symbol(), width,
lsig->vector_width(),
rsig->vector_width());
des->add_node(powr);
powr->set_signed( has_sign() );
powr->set_line(*this);
connect(powr->pin_DataA(), lsig->pin(0));
connect(powr->pin_DataB(), rsig->pin(0));
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, width);
osig->set_line(*this);
osig->data_type(expr_type());
osig->local_flag(true);
connect(powr->pin_Result(), osig->pin(0));
return osig;
}
NetNet* NetEBMult::synthesize(Design*des, NetScope*scope)
{
NetNet *lsig=0, *rsig=0;
unsigned width;
bool real_args=false;
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
real_args, this)) {
return 0;
}
if (real_args) width = 1;
else width = expr_width();
NetMult*mult = new NetMult(scope, scope->local_symbol(),
width,
lsig->vector_width(),
rsig->vector_width());
des->add_node(mult);
mult->set_signed( has_sign() );
mult->set_line(*this);
connect(mult->pin_DataA(), lsig->pin(0));
connect(mult->pin_DataB(), rsig->pin(0));
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, width);
osig->set_line(*this);
osig->data_type(expr_type());
osig->local_flag(true);
connect(mult->pin_Result(), osig->pin(0));
return osig;
}
NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope)
{
NetNet *lsig=0, *rsig=0;
unsigned width;
bool real_args=false;
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
real_args, this)) {
return 0;
}
if (real_args) width = 1;
else width = expr_width();
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, width);
osig->set_line(*this);
osig->data_type(lsig->data_type());
osig->set_signed(has_sign());
osig->local_flag(true);
switch (op()) {
case '/': {
NetDivide*div = new NetDivide(scope, scope->local_symbol(),
width,
lsig->vector_width(),
rsig->vector_width());
div->set_line(*this);
div->set_signed(has_sign());
des->add_node(div);
connect(div->pin_DataA(), lsig->pin(0));
connect(div->pin_DataB(), rsig->pin(0));
connect(div->pin_Result(),osig->pin(0));
break;
}
case '%': {
/* Baseline Verilog does not support the % operator with
real arguments, but we allow it in our extended form. */
if (real_args && !gn_icarus_misc_flag) {
cerr << get_fileline() << ": error: Modulus operator "
"may not have REAL operands." << endl;
des->errors += 1;
return 0;
}
NetModulo*div = new NetModulo(scope, scope->local_symbol(),
width,
lsig->vector_width(),
rsig->vector_width());
div->set_line(*this);
des->add_node(div);
connect(div->pin_DataA(), lsig->pin(0));
connect(div->pin_DataB(), rsig->pin(0));
connect(div->pin_Result(),osig->pin(0));
break;
}
default: {
cerr << get_fileline() << ": internal error: "
<< "NetEBDiv has unexpected op() code: "
<< op() << endl;
des->errors += 1;
delete osig;
return 0;
}
}
return osig;
}
NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope)
{
NetNet*lsig = left_->synthesize(des, scope);
NetNet*rsig = right_->synthesize(des, scope);
if (lsig == 0 || rsig == 0) return 0;
/* You cannot currently do logical operations on real values. */
if (lsig->data_type() == IVL_VT_REAL ||
rsig->data_type() == IVL_VT_REAL) {
cerr << get_fileline() << ": sorry: " << human_readable_op(op_)
<< " is currently unsupported for real values." << endl;
des->errors += 1;
return 0;
}
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, 1);
osig->data_type(expr_type());
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, 1);
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, 1);
connect(osig->pin(0), olog->pin(0));
des->add_node(olog);
/* XXXX Here, I need to reduce the parameters with
reduction or. */
/* 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));
assert(rsig->pin_count() == 1);
connect(rsig->pin(0), olog->pin(2));
}
return osig;
}
NetNet* NetEBShift::synthesize(Design*des, NetScope*scope)
{
eval_expr(right_);
NetNet*lsig = left_->synthesize(des, scope);
if (lsig == 0) return 0;
/* Cannot shift a real values. */
if (lsig->data_type() == IVL_VT_REAL) {
cerr << get_fileline() << ": error: shift operator ("
<< human_readable_op(op_)
<< ") cannot shift a real values." << endl;
des->errors += 1;
return 0;
}
const bool right_flag = op_ == 'r' || op_ == 'R';
const bool signed_flag = op_ == 'R';
/* 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 (right_flag)
shift = 0-shift;
if (shift == 0)
return lsig;
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, expr_width());
osig->data_type(expr_type());
osig->local_flag(true);
// ushift is the amount of pad created by the shift.
unsigned long ushift = shift>=0? shift : -shift;
ivl_assert(*this, ushift < osig->vector_width());
// part_width is the bits of the vector that survive the shift.
unsigned long part_width = osig->vector_width() - ushift;
// Create a part select to reduce the width of the lsig
// to the amount left by the shift.
NetPartSelect*psel = new NetPartSelect(lsig, shift<0? ushift : 0,
part_width,
NetPartSelect::VP);
des->add_node(psel);
NetNet*psig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, part_width);
psig->data_type(expr_type());
psig->local_flag(true);
psig->set_line(*this);
connect(psig->pin(0), psel->pin(0));
// Handle the special case of a signed right shift. In
// this case, use the NetSignExtend device to pad the
// result to the desired width.
if (signed_flag && right_flag) {
NetSignExtend*pad = new NetSignExtend(scope, scope->local_symbol(),
osig->vector_width());
des->add_node(pad);
pad->set_line(*this);
connect(pad->pin(1), psig->pin(0));
connect(pad->pin(0), osig->pin(0));
return osig;
}
// Other cases are handled by zero-extending on the
// proper end.
verinum znum (verinum::V0, ushift, true);
NetConst*zcon = new NetConst(scope, scope->local_symbol(),
znum);
des->add_node(zcon);
NetNet*zsig = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, znum.len());
zsig->data_type(osig->data_type());
zsig->local_flag(true);
zsig->set_line(*this);
connect(zcon->pin(0), zsig->pin(0));
NetConcat*ccat = new NetConcat(scope, scope->local_symbol(),
osig->vector_width(), 2);
ccat->set_line(*this);
des->add_node(ccat);
connect(ccat->pin(0), osig->pin(0));
if (shift > 0) {
// Left shift.
connect(ccat->pin(1), zsig->pin(0));
connect(ccat->pin(2), psig->pin(0));
} else {
// Right shift
connect(ccat->pin(1), psig->pin(0));
connect(ccat->pin(2), zsig->pin(0));
}
return osig;
}
NetNet*rsig = right_->synthesize(des, scope);
if (rsig == 0) return 0;
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, expr_width());
osig->data_type(expr_type());
osig->local_flag(true);
NetCLShift*dev = new NetCLShift(scope, scope->local_symbol(),
osig->vector_width(),
rsig->vector_width(),
right_flag, signed_flag);
dev->set_line(*this);
des->add_node(dev);
connect(dev->pin_Result(), osig->pin(0));
assert(lsig->vector_width() == dev->width());
connect(dev->pin_Data(), lsig->pin(0));
connect(dev->pin_Distance(), rsig->pin(0));
return osig;
}
NetNet* NetEConcat::synthesize(Design*des, NetScope*scope)
{
/* First, synthesize the operands. */
NetNet**tmp = new NetNet*[parms_.count()];
bool flag = true;
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
tmp[idx] = parms_[idx]->synthesize(des, scope);
if (tmp[idx] == 0)
flag = false;
}
if (flag == false)
return 0;
ivl_assert(*this, tmp[0]);
/* 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);
osig->data_type(tmp[0]->data_type());
NetConcat*concat = new NetConcat(scope, scope->local_symbol(),
osig->vector_width(),
parms_.count() * repeat());
concat->set_line(*this);
des->add_node(concat);
connect(concat->pin(0), osig->pin(0));
unsigned cur_pin = 1;
for (unsigned rpt = 0; rpt < repeat(); rpt += 1) {
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
unsigned concat_item = parms_.count()-idx-1;
ivl_assert(*this, tmp[concat_item]);
connect(concat->pin(cur_pin), tmp[concat_item]->pin(0));
cur_pin += 1;
}
}
delete[]tmp;
return osig;
}
NetNet* NetEConst::synthesize(Design*des, NetScope*scope)
{
perm_string path = scope->local_symbol();
unsigned width=expr_width();
NetNet*osig = new NetNet(scope, path, NetNet::IMPLICIT, width-1,0);
osig->local_flag(true);
osig->data_type(IVL_VT_LOGIC);
osig->set_signed(has_sign());
NetConst*con = new NetConst(scope, scope->local_symbol(), value());
connect(osig->pin(0), con->pin(0));
des->add_node(con);
return osig;
}
/*
* Create a NetLiteral object to represent real valued constants.
*/
NetNet* NetECReal::synthesize(Design*des, NetScope*scope)
{
perm_string path = scope->local_symbol();
NetNet*osig = new NetNet(scope, path, NetNet::WIRE, 1);
osig->local_flag(true);
osig->data_type(IVL_VT_REAL);
osig->set_signed(has_sign());
osig->set_line(*this);
NetLiteral*con = new NetLiteral(scope, scope->local_symbol(), value_);
des->add_node(con);
con->set_line(*this);
connect(osig->pin(0), con->pin(0));
return osig;
}
/*
* 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, NetScope*scope)
{
NetNet*isig = expr_->synthesize(des, scope);
if (isig == 0) return 0;
if (isig->data_type() == IVL_VT_REAL) {
cerr << get_fileline() << ": error: bit-wise negation ("
<< human_readable_op(op_)
<< ") may not have a REAL operand." << endl;
des->errors += 1;
return 0;
}
unsigned width = isig->vector_width();
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, width);
osig->data_type(expr_type());
osig->local_flag(true);
perm_string oname = scope->local_symbol();
NetLogic*gate;
switch (op()) {
case '~':
gate = new NetLogic(scope, oname, 2, NetLogic::NOT, width);
break;
default:
assert(0);
}
connect(osig->pin(0), gate->pin(0));
connect(isig->pin(0), gate->pin(1));
des->add_node(gate);
return osig;
}
NetNet* NetEUnary::synthesize(Design*des, NetScope*scope)
{
if (op_ == '+')
return expr_->synthesize(des, scope);
if (op_ == '-') {
NetNet*sig = expr_->synthesize(des, scope);
sig = sub_net_from(des, scope, 0, sig);
return sig;
}
if (op_ == 'm') {
NetNet*sub = expr_->synthesize(des, scope);
if (expr_->has_sign() == false)
return sub;
NetNet*sig = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, sub->vector_width());
sig->set_line(*this);
sig->local_flag(true);
sig->data_type(sub->data_type());
NetAbs*tmp = new NetAbs(scope, scope->local_symbol(), sub->vector_width());
des->add_node(tmp);
tmp->set_line(*this);
connect(tmp->pin(1), sub->pin(0));
connect(tmp->pin(0), sig->pin(0));
return sig;
}
cerr << get_fileline() << ": iternal error: "
<< "NetEUnary::synthesize cannot handle op_=" << op_ << endl;
des->errors += 1;
return expr_->synthesize(des, scope);
}
NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope)
{
NetNet*isig = expr_->synthesize(des, scope);
if (isig == 0) return 0;
if (isig->data_type() == IVL_VT_REAL) {
cerr << get_fileline() << ": error: reduction operator ("
<< human_readable_op(op_)
<< ") may not have a REAL operand." << endl;
des->errors += 1;
return 0;
}
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, 1);
osig->data_type(expr_type());
osig->local_flag(true);
NetUReduce::TYPE rtype = NetUReduce::NONE;
switch (op()) {
case 'N':
case '!':
rtype = NetUReduce::NOR;
break;
case '&':
rtype = NetUReduce::AND;
break;
case '|':
rtype = NetUReduce::OR;
break;
case '^':
rtype = NetUReduce::XOR;
break;
case 'A':
rtype = NetUReduce::NAND;
break;
case 'X':
rtype = NetUReduce::XNOR;
break;
default:
cerr << get_fileline() << ": internal error: "
<< "Unable to synthesize " << *this << "." << endl;
return 0;
}
NetUReduce*gate = new NetUReduce(scope, scope->local_symbol(),
rtype, isig->vector_width());
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;
}
/*
* Turn a part/bit select expression into gates.
* We know some things about the expression that elaboration enforces
* for us:
*
* - Expression elaboration already converted the offset expression into
* cannonical form, so we don't have to worry about that here.
*/
NetNet* NetESelect::synthesize(Design *des, NetScope*scope)
{
NetNet*sub = expr_->synthesize(des, scope);
if (sub == 0) return 0;
NetNet*off = 0;
// Detect the special case that there is a base expression and
// it is constant. In this case we can generate fixed part selects.
if (NetEConst*base_const = dynamic_cast<NetEConst*>(base_)) {
verinum base_tmp = base_const->value();
ivl_assert(*this, base_tmp.is_defined());
long base_val = base_tmp.as_long();
unsigned select_width = expr_width();
// Any below X bits?
NetNet*below = 0;
if (base_val < 0) {
unsigned below_width = abs(base_val);
base_val = 0;
ivl_assert(*this, below_width < select_width);
select_width -= below_width;
below = make_const_x(des, scope, below_width);
below->set_line(*this);
}
// Any above bits?.
NetNet*above = 0;
if (base_val+select_width > sub->vector_width()) {
select_width = sub->vector_width() - base_val;
unsigned above_width = expr_width() - select_width;
above = make_const_x(des, scope, above_width);
above->set_line(*this);
}
// Make the make part select.
NetPartSelect*sel = new NetPartSelect(sub, base_val, select_width,
NetPartSelect::VP);
des->add_node(sel);
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, select_width);
tmp->data_type(sub->data_type());
tmp->local_flag(true);
tmp->set_line(*this);
connect(sel->pin(0), tmp->pin(0));
unsigned concat_count = 1;
if (above)
concat_count += 1;
if (below)
concat_count += 1;
if (concat_count > 1) {
NetConcat*cat = new NetConcat(scope, scope->local_symbol(),
expr_width(), concat_count);
cat->set_line(*this);
des->add_node(cat);
if (below) {
connect(cat->pin(1), below->pin(0));
connect(cat->pin(2), tmp->pin(0));
} else {
connect(cat->pin(1), tmp->pin(0));
}
if (above) {
connect(cat->pin(concat_count), above->pin(0));
}
tmp = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, expr_width());
tmp->data_type(sub->data_type());
tmp->local_flag(true);
tmp->set_line(*this);
connect(cat->pin(0), tmp->pin(0));
}
return tmp;
}
// This handles the case that the NetESelect exists to do an
// actual part/bit select. Generate a NetPartSelect object to
// do the work, and replace "sub" with the selected output.
if (base_ != 0) {
off = base_->synthesize(des, scope);
NetPartSelect*sel = new NetPartSelect(sub, off, expr_width());
sel->set_line(*this);
des->add_node(sel);
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, expr_width());
tmp->data_type(sub->data_type());
tmp->local_flag(true);
tmp->set_line(*this);
sub = tmp;
connect(sub->pin(0), sel->pin(0));
}
// Now look for the case that the NetESelect actually exists
// to change the width of the expression. (i.e. to do
// padding.) If this was for an actual part select that at
// this point the output vector_width is exactly right, and we
// are done.
if (sub->vector_width() == expr_width())
return sub;
// The vector_width is not exactly right, so the source is
// probably asking for padding. Create nodes to do sign
// extension or 0 extension, depending on the has_sign() mode
// of the expression.
NetNet*net = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, expr_width());
net->data_type(expr_type());
net->local_flag(true);
net->set_line(*this);
if (has_sign()) {
NetSignExtend*pad = new NetSignExtend(scope,
scope->local_symbol(),
expr_width());
pad->set_line(*this);
des->add_node(pad);
connect(pad->pin(1), sub->pin(0));
connect(pad->pin(0), net->pin(0));
net->set_signed(true);
} else {
NetConcat*cat = new NetConcat(scope, scope->local_symbol(),
expr_width(), 2);
cat->set_line(*this);
des->add_node(cat);
assert(expr_width() > sub->vector_width());
unsigned pad_width = expr_width() - sub->vector_width();
verinum pad((uint64_t)0, pad_width);
NetConst*con = new NetConst(scope, scope->local_symbol(),
pad);
con->set_line(*this);
des->add_node(con);
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, pad_width);
tmp->data_type(expr_type());
tmp->local_flag(true);
tmp->set_line(*this);
connect(tmp->pin(0), con->pin(0));
connect(cat->pin(0), net->pin(0));
connect(cat->pin(1), sub->pin(0));
connect(cat->pin(2), con->pin(0));
}
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, NetScope*scope)
{
NetNet*csig = cond_->synthesize(des, scope),
*tsig = true_val_->synthesize(des, scope),
*fsig = false_val_->synthesize(des, scope);
if (csig == 0 || tsig == 0 || fsig == 0) return 0;
if (tsig->data_type() != fsig->data_type()) {
cerr << get_fileline() << ": error: True and False clauses of "
"ternary expression have different types." << endl;
cerr << get_fileline() << ": : True clause is: "
<< tsig->data_type() << endl;
cerr << get_fileline() << ": : False clause is: "
<< fsig->data_type() << endl;
des->errors += 1;
return 0;
} else if (tsig->data_type() == IVL_VT_NO_TYPE) {
cerr << get_fileline() << ": internal error: True and False "
"clauses of ternary both have NO TYPE." << endl;
des->errors += 1;
return 0;
}
perm_string path = csig->scope()->local_symbol();
ivl_assert(*this, csig->vector_width() == 1);
unsigned width=expr_width();
NetNet*osig = new NetNet(csig->scope(), path, NetNet::IMPLICIT, width);
osig->data_type(expr_type());
osig->local_flag(true);
/* Make sure both value operands are the right width. */
tsig = crop_to_width(des, pad_to_width(des, tsig, width), width);
fsig = crop_to_width(des, pad_to_width(des, fsig, width), width);
assert(width == tsig->vector_width());
assert(width == fsig->vector_width());
perm_string oname = csig->scope()->local_symbol();
NetMux *mux = new NetMux(csig->scope(), oname, width,
2, csig->vector_width());
connect(tsig->pin(0), mux->pin_Data(1));
connect(fsig->pin(0), mux->pin_Data(0));
connect(osig->pin(0), mux->pin_Result());
connect(csig->pin(0), mux->pin_Sel());
des->add_node(mux);
return osig;
}
/*
* When synthesizing a signal expression, it is usually fine to simply
* return the NetNet that it refers to. If this is an array word though,
* a bit more work needs to be done. Return a temporary that represents
* the selected word.
*/
NetNet* NetESignal::synthesize(Design*des, NetScope*scope)
{
if (word_ == 0)
return net_;
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
NetNet::IMPLICIT, net_->vector_width());
tmp->set_line(*this);
tmp->local_flag(true);
tmp->data_type(net_->data_type());
// For NetExpr objects, the word index is already converted to
// a canonical (lsb==0) address. Just use the index directly.
if (NetEConst*index_co = dynamic_cast<NetEConst*> (word_)) {
long index = index_co->value().as_long();
connect(tmp->pin(0), net_->pin(index));
} else {
unsigned selwid = word_->expr_width();
NetArrayDq*mux = new NetArrayDq(scope, scope->local_symbol(),
net_, selwid);
mux->set_line(*this);
des->add_node(mux);
NetNet*index_net = word_->synthesize(des, scope);
connect(mux->pin_Address(), index_net->pin(0));
connect(tmp->pin(0), mux->pin_Result());
}
return tmp;
}
NetNet* NetESFunc::synthesize(Design*des, NetScope*scope)
{
const struct sfunc_return_type*def = lookup_sys_func(name_);
/* We cannot use the default value for system functions in a
* continuous assignment since the function name is NULL. */
if (def == 0 || def->name == 0) {
cerr << get_fileline() << ": error: System function "
<< name_ << " not defined in system "
"table or SFT file(s)." << endl;
des->errors += 1;
return 0;
}
if (debug_elaborate) {
cerr << get_fileline() << ": debug: Net system function "
<< name_ << " returns " << def->type << endl;
}
NetSysFunc*net = new NetSysFunc(scope, scope->local_symbol(),
def, 1+nparms_);
net->set_line(*this);
des->add_node(net);
NetNet*osig = new NetNet(scope, scope->local_symbol(),
NetNet::WIRE, def->wid);
osig->local_flag(true);
osig->set_signed(def->type==IVL_VT_REAL? true : false);
osig->data_type(def->type);
osig->set_line(*this);
connect(net->pin(0), osig->pin(0));
unsigned errors = 0;
for (unsigned idx = 0 ; idx < nparms_ ; idx += 1) {
NetNet*tmp = parms_[idx]->synthesize(des, scope);
if (tmp == 0) {
cerr << get_fileline() << ": error: Unable to elaborate "
<< "argument " << idx << " of call to " << name_ <<
"." << endl;
errors += 1;
des->errors += 1;
continue;
}
connect(net->pin(1+idx), tmp->pin(0));
}
if (errors > 0) return 0;
return osig;
}
NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope)
{
svector<NetNet*> eparms (parms_.count());
/* Synthesize the arguments. */
bool errors = false;
for (unsigned idx = 0; idx < eparms.count(); idx += 1) {
NetNet*tmp = parms_[idx]->synthesize(des, scope);
if (tmp == 0) {
cerr << get_fileline() << ": error: Unable to synthesize "
"port " << idx << " of call to "
<< func_->basename() << "." << endl;
errors = true;
des->errors += 1;
continue;
}
eparms[idx] = tmp;
}
if (errors) return 0;
NetUserFunc*net = new NetUserFunc(scope_, scope_->local_symbol(), func_);
net->set_line(*this);
des->add_node(net);
/* Create an output signal and connect it to the function. */
NetNet*osig = new NetNet(scope_, scope_->local_symbol(), NetNet::WIRE,
result_sig_->vector_width());
osig->local_flag(true);
osig->data_type(result_sig_->expr_type());
connect(net->pin(0), osig->pin(0));
/* Connect the pins to the arguments. */
NetFuncDef*def = func_->func_def();
for (unsigned idx = 0; idx < eparms.count(); idx += 1) {
NetNet*tmp = pad_to_width(des, eparms[idx],
def->port(idx)->vector_width());
connect(net->pin(idx+1), tmp->pin(0));
}
return osig;
}