diff --git a/compiler.h b/compiler.h index fd26fe4bf..5cfd6dbed 100644 --- a/compiler.h +++ b/compiler.h @@ -1,7 +1,7 @@ #ifndef __compiler_H #define __compiler_H /* - * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2013 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 @@ -107,6 +107,10 @@ extern bool debug_optimizer; /* Possibly temporary flag to control virtualization of pin arrays */ extern bool disable_virtual_pins; +/* The vlog95 code generator does not want the compiler to generate concat-Z + * LPM objects so this flag is used to block them from being generated. */ +extern bool disable_concatz_generation; + /* Limit to size of devirtualized arrays */ extern unsigned long array_size_limit; diff --git a/elaborate.cc b/elaborate.cc index 7968e9b47..ca5545859 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -176,11 +176,12 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const need_driver_flag = false; } - /* When we are given a non-default strength value and if the - * drive source is a bit, part or indexed select we need to - * add a driver (BUFZ) to convey the strength information. */ + /* When we are given a non-default strength value and if the drive + * source is a bit, part, indexed select or a concatenation we need + * to add a driver (BUFZ) to convey the strength information. */ if ((drive0 != IVL_DR_STRONG || drive1 != IVL_DR_STRONG) && - (dynamic_cast(rval_expr))) { + ((dynamic_cast(rval_expr)) || + (dynamic_cast(rval_expr)))) { need_driver_flag = true; } diff --git a/eval_tree.cc b/eval_tree.cc index 992f18175..7a7eb2775 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -34,6 +34,18 @@ NetExpr* NetExpr::eval_tree() return 0; } +static void eval_debug(const NetExpr*expr, NetExpr*res, bool is_real) +{ + if (res != 0) { + res->set_line(*expr); + if (debug_eval_tree) { + cerr << expr->get_fileline() << ": debug: Evaluated"; + if (is_real) cerr << " (real)"; + cerr << ": " << *expr << " --> " << *res << endl; + } + } +} + static bool get_real_arg_(const NetExpr*expr, verireal&val) { switch (expr->expr_type()) { @@ -82,18 +94,7 @@ NetExpr* NetEBinary::eval_tree() eval_expr(left_); eval_expr(right_); - NetExpr*res = eval_arguments_(left_, right_); - if (res != 0) { - res->set_line(*this); - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: Evaluated"; - if (left_->expr_type() == IVL_VT_REAL || - right_->expr_type() == IVL_VT_REAL) - cerr << " (real)"; - cerr << ": " << *this << " --> " << *res << endl; - } - } - return res; + return eval_arguments_(left_, right_); } NetExpr* NetEBinary::eval_arguments_(const NetExpr*, const NetExpr*) const @@ -126,6 +127,7 @@ NetECReal* NetEBAdd::eval_tree_real_(const NetExpr*l, const NetExpr*r) const NetECReal*res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); + eval_debug(this, res, true); return res; } @@ -136,17 +138,7 @@ NetExpr* NetEBAdd::eval_tree() // First try to elaborate the expression completely. NetExpr*res = eval_arguments_(left_,right_); - if (res != 0) { - res->set_line(*this); - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: Evaluated"; - if (left_->expr_type() == IVL_VT_REAL || - right_->expr_type() == IVL_VT_REAL) - cerr << " (real)"; - cerr << ": " << *this << " --> " << *res << endl; - } - return res; - } + if (res != 0) return res; // If the expression type is real, then do not attempt the // following alternative processing. @@ -236,6 +228,7 @@ NetExpr* NetEBAdd::eval_arguments_(const NetExpr*l, const NetExpr*r) const NetEConst *res = new NetEConst(val); ivl_assert(*this, res); + eval_debug(this, res, false); return res; } @@ -257,6 +250,7 @@ NetEConst* NetEBBits::eval_arguments_(const NetExpr*l, const NetExpr*r) const verinum res (verinum::V0, expr_width()); NetEConst*tmp = new NetEConst(res); ivl_assert(*this, tmp); + eval_debug(this, tmp, false); return tmp; } @@ -264,6 +258,7 @@ NetEConst* NetEBBits::eval_arguments_(const NetExpr*l, const NetExpr*r) const verinum res (verinum::V0, expr_width()); NetEConst*tmp = new NetEConst(res); ivl_assert(*this, tmp); + eval_debug(this, tmp, false); return tmp; } @@ -306,6 +301,7 @@ NetEConst* NetEBBits::eval_arguments_(const NetExpr*l, const NetExpr*r) const NetEConst*tmp = new NetEConst(res); ivl_assert(*this, tmp); + eval_debug(this, tmp, false); return tmp; } @@ -754,6 +750,8 @@ NetEConst* NetEBComp::eval_arguments_(const NetExpr*l, const NetExpr*r) const break; } + eval_debug(this, res, l->expr_type() == IVL_VT_REAL || + r->expr_type() == IVL_VT_REAL); return res; } @@ -780,6 +778,7 @@ NetExpr* NetEBDiv::eval_tree_real_(const NetExpr*l, const NetExpr*r) const } NetECReal*res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); + eval_debug(this, res, true); return res; } @@ -813,6 +812,7 @@ NetExpr* NetEBDiv::eval_arguments_(const NetExpr*l, const NetExpr*r) const } NetExpr*tmp = new NetEConst(val); ivl_assert(*this, tmp); + eval_debug(this, tmp, false); return tmp; } @@ -846,6 +846,7 @@ NetEConst* NetEBLogic::eval_tree_real_(const NetExpr*l, const NetExpr*r) const NetEConst*tmp = new NetEConst(verinum(res, 1)); ivl_assert(*this, tmp); + eval_debug(this, tmp, true); return tmp; } @@ -912,6 +913,7 @@ NetEConst* NetEBLogic::eval_arguments_(const NetExpr*l, const NetExpr*r) const NetEConst*tmp = new NetEConst(verinum(res, 1)); ivl_assert(*this, tmp); + eval_debug(this, tmp, false); return tmp; } @@ -937,6 +939,7 @@ NetExpr* NetEBMinMax::eval_tree_real_(const NetExpr*l, const NetExpr*r) const NetECReal*res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); + eval_debug(this, res, true); return res; } @@ -974,6 +977,7 @@ NetExpr* NetEBMinMax::eval_arguments_(const NetExpr*l, const NetExpr*r) const } NetEConst*res = new NetEConst(res_val); ivl_assert(*this, res); + eval_debug(this, res, false); return res; } @@ -987,6 +991,7 @@ NetExpr* NetEBMult::eval_tree_real_(const NetExpr*l, const NetExpr*r) const NetECReal*res = new NetECReal( verireal(lval * rval) ); ivl_assert(*this, res); + eval_debug(this, res, true); return res; } @@ -1010,6 +1015,7 @@ NetExpr* NetEBMult::eval_arguments_(const NetExpr*l, const NetExpr*r) const verinum val(lval * rval, wid); NetEConst*tmp = new NetEConst(val); ivl_assert(*this, tmp); + eval_debug(this, tmp, false); return tmp; } @@ -1023,6 +1029,7 @@ NetExpr* NetEBPow::eval_tree_real_(const NetExpr*l, const NetExpr*r) const NetECReal*res = new NetECReal( verireal( pow(lval,rval) ) ); ivl_assert(*this, res); + eval_debug(this, res, true); return res; } @@ -1045,6 +1052,7 @@ NetExpr* NetEBPow::eval_arguments_(const NetExpr*l, const NetExpr*r) const verinum val(pow(lval, rval), wid); NetEConst*res = new NetEConst(val); ivl_assert(*this, res); + eval_debug(this, res, false); return res; } @@ -1085,19 +1093,14 @@ NetEConst* NetEBShift::eval_arguments_(const NetExpr*l, const NetExpr*r) const val.has_sign(has_sign()); res = new NetEConst(val); ivl_assert(*this, res); + eval_debug(this, res, false); return res; } NetEConst* NetEConcat::eval_tree() { - unsigned repeat_val = repeat(); unsigned local_errors = 0; - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << endl; - } - unsigned gap = 0; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { @@ -1141,6 +1144,14 @@ NetEConst* NetEConcat::eval_tree() if (local_errors > 0) return 0; + return eval_arguments_(parms_, gap); +} + +NetEConst* NetEConcat::eval_arguments_(const vector&vals, + unsigned gap) const +{ + unsigned repeat_val = repeat(); + // At this point, the "gap" is the width of a single repeat of // the concatenation. The total width of the result is the gap // times the repeat count. @@ -1150,8 +1161,8 @@ NetEConst* NetEConcat::eval_tree() unsigned cur = 0; bool is_string_flag = true; - for (unsigned idx = parms_.size() ; idx > 0 ; idx -= 1) { - NetEConst*expr = dynamic_cast(parms_[idx-1]); + for (unsigned idx = vals.size() ; idx > 0 ; idx -= 1) { + const NetEConst*expr = dynamic_cast(vals[idx-1]); if (expr == 0) return 0; @@ -1176,16 +1187,13 @@ NetEConst* NetEConcat::eval_tree() val.has_sign( this->has_sign() ); NetEConst*res = new NetEConst(val); + ivl_assert(*this, res); + eval_debug(this, res, false); return res; } NetEConst* NetESelect::eval_tree() { - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << endl; - } - eval_expr(expr_); NetEConst*expr = dynamic_cast(expr_); @@ -1228,6 +1236,7 @@ NetEConst* NetESelect::eval_tree() oval.has_sign(has_sign()); NetEConst*res = new NetEConst(oval); + eval_debug(this, res, false); return res; } @@ -1376,17 +1385,7 @@ NetExpr*NetETernary::blended_arguments_(const NetExpr*te, const NetExpr*fe) cons NetExpr* NetEUnary::eval_tree() { eval_expr(expr_); - NetExpr*res = eval_arguments_(expr_); - if (res != 0) { - res->set_line(*this); - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: Evaluated"; - if (expr_->expr_type() == IVL_VT_REAL) - cerr << " (real)"; - cerr << ": " << *this << " --> " << *res << endl; - } - } - return res; + return eval_arguments_(expr_); } NetExpr* NetEUnary::eval_tree_real_(const NetExpr*ex) const @@ -1412,6 +1411,7 @@ NetExpr* NetEUnary::eval_tree_real_(const NetExpr*ex) const } NetECReal *res = new NetECReal( verireal(res_val) ); ivl_assert(*this, res); + eval_debug(this, res, true); return res; } @@ -1478,6 +1478,7 @@ NetExpr* NetEUnary::eval_arguments_(const NetExpr*ex) const NetEConst *res = new NetEConst(val); ivl_assert(*this, res); + eval_debug(this, res, false); return res; } @@ -1494,6 +1495,7 @@ NetEConst* NetEUReduce::eval_tree_real_(const NetExpr*ex) const NetEConst*tmp = new NetEConst(verinum(res, 1)); ivl_assert(*this, tmp); + eval_debug(this, tmp, true); return tmp; } @@ -1585,15 +1587,14 @@ NetEConst* NetEUReduce::eval_arguments_(const NetExpr*ex) const NetEConst*tmp = new NetEConst(verinum(res, 1)); ivl_assert(*this, tmp); + eval_debug(this, tmp, false); return tmp; } -static NetEConst* evaluate_clog2(NetExpr*&arg_) +NetEConst* NetESFunc::evaluate_clog2_(const NetExpr*arg_) const { - eval_expr(arg_); - - NetEConst*tmpi = dynamic_cast(arg_); - NetECReal*tmpr = dynamic_cast(arg_); + const NetEConst*tmpi = dynamic_cast(arg_); + const NetECReal*tmpr = dynamic_cast(arg_); if (tmpi == 0 && tmpr == 0) return 0; @@ -1612,7 +1613,7 @@ static NetEConst* evaluate_clog2(NetExpr*&arg_) tmp.has_sign(true); rtn = new NetEConst(tmp); - ivl_assert(*arg_, rtn); + ivl_assert(*this, rtn); } else { bool is_neg = false; uint64_t res = 0; @@ -1641,18 +1642,17 @@ static NetEConst* evaluate_clog2(NetExpr*&arg_) tmp.has_sign(true); rtn = new NetEConst(tmp); - ivl_assert(*arg_, rtn); + ivl_assert(*this, rtn); } + eval_debug(this, rtn, false); return rtn; } -static NetECReal* evaluate_math_one_arg(NetExpr*&arg_, const char*name) +NetECReal* NetESFunc::evaluate_math_one_arg_(ID id, const NetExpr*arg_) const { - eval_expr(arg_); - - NetEConst*tmpi = dynamic_cast(arg_); - NetECReal*tmpr = dynamic_cast(arg_); + const NetEConst*tmpi = dynamic_cast(arg_); + const NetECReal*tmpr = dynamic_cast(arg_); NetECReal*res = 0; @@ -1664,79 +1664,79 @@ static NetECReal* evaluate_math_one_arg(NetExpr*&arg_, const char*name) arg = tmpr->value().as_double(); } - if (strcmp(name, "$ln") == 0) { + switch (id) { + case LN: res = new NetECReal(verireal(log(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$log10") == 0) { + break; + case LOG10: res = new NetECReal(verireal(log10(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$exp") == 0) { + break; + case EXP: res = new NetECReal(verireal(exp(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$sqrt") == 0) { + break; + case SQRT: res = new NetECReal(verireal(sqrt(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$floor") == 0) { + break; + case FLOOR: res = new NetECReal(verireal(floor(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$ceil") == 0) { + break; + case CEIL: res = new NetECReal(verireal(ceil(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$sin") == 0) { + break; + case SIN: res = new NetECReal(verireal(sin(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$cos") == 0) { + break; + case COS: res = new NetECReal(verireal(cos(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$tan") == 0) { + break; + case TAN: res = new NetECReal(verireal(tan(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$asin") == 0) { + break; + case ASIN: res = new NetECReal(verireal(asin(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$acos") == 0) { + break; + case ACOS: res = new NetECReal(verireal(acos(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$atan") == 0) { + break; + case ATAN: res = new NetECReal(verireal(atan(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$sinh") == 0) { + break; + case SINH: res = new NetECReal(verireal(sinh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$cosh") == 0) { + break; + case COSH: res = new NetECReal(verireal(cosh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$tanh") == 0) { + break; + case TANH: res = new NetECReal(verireal(tanh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$asinh") == 0) { + break; + case ASINH: res = new NetECReal(verireal(asinh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$acosh") == 0) { + break; + case ACOSH: res = new NetECReal(verireal(acosh(arg))); - ivl_assert(*arg_, res); - } else if (strcmp(name, "$atanh") == 0) { + break; + case ATANH: res = new NetECReal(verireal(atanh(arg))); - ivl_assert(*arg_, res); - } else { - cerr << arg_->get_fileline() << ": warning: Unhandled" - "constant system function " << name << "." << endl; + break; + default: + ivl_assert(*this, 0); + break; } + ivl_assert(*this, res); } + eval_debug(this, res, true); return res; } -static NetECReal* evaluate_math_two_args(NetExpr*&arg0_, NetExpr*&arg1_, - const char*name) +NetECReal* NetESFunc::evaluate_math_two_arg_(ID id, const NetExpr*arg0_, + const NetExpr*arg1_) const { - eval_expr(arg0_); - eval_expr(arg1_); - - NetEConst*tmpi0 = dynamic_cast(arg0_); - NetECReal*tmpr0 = dynamic_cast(arg0_); - NetEConst*tmpi1 = dynamic_cast(arg1_); - NetECReal*tmpr1 = dynamic_cast(arg1_); + const NetEConst*tmpi0 = dynamic_cast(arg0_); + const NetECReal*tmpr0 = dynamic_cast(arg0_); + const NetEConst*tmpi1 = dynamic_cast(arg1_); + const NetECReal*tmpr1 = dynamic_cast(arg1_); NetECReal*res = 0; @@ -1753,73 +1753,78 @@ static NetECReal* evaluate_math_two_args(NetExpr*&arg0_, NetExpr*&arg1_, arg1 = tmpr1->value().as_double(); } - if (strcmp(name, "$pow") == 0) { + switch (id) { + case POW: res = new NetECReal(verireal(pow(arg0, arg1))); - ivl_assert(*arg0_, res); - } else if (strcmp(name, "$atan2") == 0) { + break; + case ATAN2: res = new NetECReal(verireal(atan2(arg0, arg1))); - ivl_assert(*arg0_, res); - } else if (strcmp(name, "$hypot") == 0) { + break; + case HYPOT: res = new NetECReal(verireal(hypot(arg0, arg1))); - ivl_assert(*arg0_, res); - } else { - cerr << arg0_->get_fileline() << ": warning: Unhandled" - "constant system function " << name << "." << endl; + break; + default: + ivl_assert(*this, 0); + break; } + ivl_assert(*this, res); } + eval_debug(this, res, true); return res; } -static NetExpr* evaluate_abs(NetExpr*&arg_) +NetExpr* NetESFunc::evaluate_abs_(const NetExpr*arg_) const { - eval_expr(arg_); - NetExpr*res = 0; - NetEConst*tmpi = dynamic_cast(arg_); + const NetEConst*tmpi = dynamic_cast(arg_); if (tmpi) { verinum arg = tmpi->value(); if (arg.is_negative()) { arg = v_not(arg) + verinum(1); } res = new NetEConst(arg); - ivl_assert(*arg_, res); + ivl_assert(*this, res); } - NetECReal*tmpr = dynamic_cast(arg_); + const NetECReal*tmpr = dynamic_cast(arg_); if (tmpr) { double arg = tmpr->value().as_double(); res = new NetECReal(verireal(fabs(arg))); - ivl_assert(*arg_, res); + ivl_assert(*this, res); } + eval_debug(this, res, tmpr != 0); return res; } -static NetExpr* evaluate_min_max(NetExpr*&arg0_, NetExpr*&arg1_, - const char*name) +NetExpr* NetESFunc::evaluate_min_max_(ID id, const NetExpr*arg0_, + const NetExpr*arg1_) const { - eval_expr(arg0_); - eval_expr(arg1_); - - NetEConst*tmpi0 = dynamic_cast(arg0_); - NetECReal*tmpr0 = dynamic_cast(arg0_); - NetEConst*tmpi1 = dynamic_cast(arg1_); - NetECReal*tmpr1 = dynamic_cast(arg1_); + const NetEConst*tmpi0 = dynamic_cast(arg0_); + const NetECReal*tmpr0 = dynamic_cast(arg0_); + const NetEConst*tmpi1 = dynamic_cast(arg1_); + const NetECReal*tmpr1 = dynamic_cast(arg1_); NetExpr*res = 0; if (tmpi0 && tmpi1) { verinum arg0 = tmpi0->value(); verinum arg1 = tmpi1->value(); - if (strcmp(name, "$min") == 0) { + switch (id) { + case MIN: res = new NetEConst( arg0 < arg1 ? arg0 : arg1); - ivl_assert(*arg0_, res); - } else if (strcmp(name, "$max") == 0) { + break; + case MAX: res = new NetEConst( arg0 < arg1 ? arg1 : arg0); - ivl_assert(*arg0_, res); + break; + default: + ivl_assert(*this, 0); + break; } + ivl_assert(*this, res); + } else if ((tmpi0 || tmpr0) && (tmpi1 || tmpr1)) { double arg0, arg1; if (tmpi0) { @@ -1832,118 +1837,128 @@ static NetExpr* evaluate_min_max(NetExpr*&arg0_, NetExpr*&arg1_, } else { arg1 = tmpr1->value().as_double(); } - if (strcmp(name, "$min") == 0) { + switch (id) { + case MIN: res = new NetECReal(verireal(arg0 < arg1 ? arg0 : arg1)); - ivl_assert(*arg0_, res); - } else if (strcmp(name, "$max") == 0) { + break; + case MAX: res = new NetECReal(verireal(arg0 < arg1 ? arg1 : arg0)); - ivl_assert(*arg0_, res); - } else { - cerr << arg0_->get_fileline() << ": warning: Unhandled" - "constant system function " << name << "." << endl; + break; + default: + ivl_assert(*this, 0); + break; } + ivl_assert(*this, res); } + eval_debug(this, res, tmpr0 || tmpr1); return res; } +NetExpr* NetESFunc::evaluate_one_arg_(ID id, const NetExpr*arg) const +{ + switch (id) { + case ABS: + return evaluate_abs_(arg); + case CLOG2: + return evaluate_clog2_(arg); + default: + return evaluate_math_one_arg_(id, arg); + } +} + +NetExpr* NetESFunc::evaluate_two_arg_(ID id, const NetExpr*arg0, + const NetExpr*arg1) const +{ + switch (id) { + case MIN: + case MAX: + return evaluate_min_max_(id, arg0, arg1); + default: + return evaluate_math_two_arg_(id, arg0, arg1); + } +} + +NetESFunc::ID NetESFunc::built_in_id_() const +{ + /* If we are not targeting at least Verilog-2005, Verilog-AMS + * or using the Icarus misc flag then we do not treat these + * functions as built-in. */ + if (generation_flag < GN_VER2005 && + !gn_icarus_misc_flag && !gn_verilog_ams_flag) { + return NOT_BUILT_IN; + } + + static map built_in_func; + + if (built_in_func.empty()) { + built_in_func["$clog2"] = CLOG2; + built_in_func["$ln" ] = LN; + built_in_func["$log10"] = LOG10; + built_in_func["$exp" ] = EXP; + built_in_func["$sqrt" ] = SQRT; + built_in_func["$floor"] = FLOOR; + built_in_func["$ceil" ] = CEIL; + built_in_func["$sin" ] = SIN; + built_in_func["$cos" ] = COS; + built_in_func["$tan" ] = TAN; + built_in_func["$asin" ] = ASIN; + built_in_func["$acos" ] = ACOS; + built_in_func["$atan" ] = ATAN; + built_in_func["$sinh" ] = SINH; + built_in_func["$cosh" ] = COSH; + built_in_func["$tanh" ] = TANH; + built_in_func["$asinh"] = ASINH; + built_in_func["$acosh"] = ACOSH; + built_in_func["$atanh"] = ATANH; + built_in_func["$abs" ] = ABS; + built_in_func["$pow" ] = POW; + built_in_func["$atan2"] = ATAN2; + built_in_func["$hypot"] = HYPOT; + built_in_func["$min" ] = MIN; + built_in_func["$max" ] = MAX; + } + + map::iterator idx = built_in_func.find(name_); + if (idx == built_in_func.end()) + return NOT_BUILT_IN; + + ID id = idx->second; + + if (is_ams_(id) && !gn_icarus_misc_flag && !gn_verilog_ams_flag) + return NOT_BUILT_IN; + + return id; +} + NetExpr* NetESFunc::eval_tree() { - /* If we are not targeting at least Verilog-2005, Verilog-AMS - * or using the Icarus misc flag then we do not support these - * functions as constant. */ - if (generation_flag < GN_VER2005 && - !gn_icarus_misc_flag && !gn_verilog_ams_flag) { + ID id = built_in_id_(); + if (id == NOT_BUILT_IN) + return 0; + + switch (nargs_(id)) { + case 1: + if (parms_.size() != 1) { + cerr << get_fileline() << ": error: " << name_ + << " takes one argument." << endl; + return 0; + } + eval_expr(parms_[0]); + return evaluate_one_arg_(id, parms_[0]); + case 2: + if (parms_.size() != 2) { + cerr << get_fileline() << ": error: " << name_ + << " takes two arguments." << endl; + return 0; + } + eval_expr(parms_[0]); + eval_expr(parms_[1]); + return evaluate_two_arg_(id, parms_[0], parms_[1]); + default: + ivl_assert(*this, 0); return 0; } - - const char*nm = name(); - NetExpr*rtn = 0; - /* Only $clog2 and the builtin mathematical functions can - * be a constant system function. */ - if (strcmp(nm, "$clog2") == 0 || - strcmp(nm, "$ln") == 0 || - strcmp(nm, "$log10") == 0 || - strcmp(nm, "$exp") == 0 || - strcmp(nm, "$sqrt") == 0 || - strcmp(nm, "$floor") == 0 || - strcmp(nm, "$ceil") == 0 || - strcmp(nm, "$sin") == 0 || - strcmp(nm, "$cos") == 0 || - strcmp(nm, "$tan") == 0 || - strcmp(nm, "$asin") == 0 || - strcmp(nm, "$acos") == 0 || - strcmp(nm, "$atan") == 0 || - strcmp(nm, "$sinh") == 0 || - strcmp(nm, "$cosh") == 0 || - strcmp(nm, "$tanh") == 0 || - strcmp(nm, "$asinh") == 0 || - strcmp(nm, "$acosh") == 0 || - strcmp(nm, "$atanh") == 0) { - if (nparms() != 1 || parm(0) == 0) { - cerr << get_fileline() << ": error: " << nm - << " takes a single argument." << endl; - return 0; - } - NetExpr*arg = parm(0)->dup_expr(); - if (strcmp(nm, "$clog2") == 0) { - rtn = evaluate_clog2(arg); - } else { - rtn = evaluate_math_one_arg(arg, nm); - } - delete arg; - } - - if (strcmp(nm, "$pow") == 0 || - strcmp(nm, "$atan2") == 0 || - strcmp(nm, "$hypot") == 0) { - if (nparms() != 2 || parm(0) == 0 || parm(1) == 0) { - cerr << get_fileline() << ": error: " << nm - << " takes two arguments." << endl; - return 0; - } - NetExpr*arg0 = parm(0)->dup_expr(); - NetExpr*arg1 = parm(1)->dup_expr(); - rtn = evaluate_math_two_args(arg0, arg1, nm); - delete arg0; - delete arg1; - } - - if ((gn_icarus_misc_flag || gn_verilog_ams_flag) && - (strcmp(nm, "$abs") == 0)) { - if (nparms() != 1 || parm(0) == 0) { - cerr << get_fileline() << ": error: " << nm - << " takes a single argument." << endl; - return 0; - } - NetExpr*arg = parm(0)->dup_expr(); - rtn = evaluate_abs(arg); - delete arg; - } - - if ((gn_icarus_misc_flag || gn_verilog_ams_flag) && - (strcmp(nm, "$min") == 0 || strcmp(nm, "$max") == 0)) { - if (nparms() != 2 || parm(0) == 0 || parm(1) == 0) { - cerr << get_fileline() << ": error: " << nm - << " takes two arguments." << endl; - return 0; - } - NetExpr*arg0 = parm(0)->dup_expr(); - NetExpr*arg1 = parm(1)->dup_expr(); - rtn = evaluate_min_max(arg0, arg1, nm); - delete arg0; - delete arg1; - } - - if (rtn != 0) { - rtn->set_line(*this); - - if (debug_eval_tree) - cerr << get_fileline() << ": debug: Evaluated: " << *this - << " --> " << *rtn << endl; - } - - return rtn; } NetExpr* NetEUFunc::eval_tree() diff --git a/main.cc b/main.cc index 5c2bb207d..be73a3bd6 100644 --- a/main.cc +++ b/main.cc @@ -1,5 +1,5 @@ const char COPYRIGHT[] = - "Copyright (c) 1998-2012 Stephen Williams (steve@icarus.com)"; + "Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it @@ -175,6 +175,7 @@ bool debug_optimizer = false; bool disable_virtual_pins = false; unsigned long array_size_limit = 16777216; // Minimum required by IEEE-1364? unsigned recursive_mod_limit = 10; +bool disable_concatz_generation = false; /* * Verbose messages enabled. @@ -967,6 +968,9 @@ int main(int argc, char*argv[]) flag_tmp = flags["RECURSIVE_MOD_LIMIT"]; if (flag_tmp) recursive_mod_limit = strtoul(flag_tmp,NULL,0); + flag_tmp = flags["DISABLE_CONCATZ_GENERATION"]; + if (flag_tmp) disable_concatz_generation = strcmp(flag_tmp,"true")==0; + /* Parse the input. Make the pform. */ pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); int rc = pform_parse(argv[optind]); diff --git a/net_func_eval.cc b/net_func_eval.cc index b156bdc1b..ba4fbc21f 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -55,8 +55,10 @@ NetExpr* NetFuncDef::evaluate_function(const LineInfo&loc, const std::vectorevaluate_function_find_locals(loc, context_map); - // Perform the evaluation - bool flag = statement_->evaluate_function(loc, context_map); + // Perform the evaluation. Note that if there were errors + // when compiling the function definition, we may not have + // a valid statement. + bool flag = statement_ && statement_->evaluate_function(loc, context_map); // Extract the result... ptr = context_map.find(scope_->basename()); @@ -280,21 +282,38 @@ NetExpr* NetEBinary::evaluate_function(const LineInfo&loc, } NetExpr*res = eval_arguments_(lval, rval); - if (res != 0) { - res->set_line(*this); - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: Evaluated"; - if (lval->expr_type() == IVL_VT_REAL || - rval->expr_type() == IVL_VT_REAL) - cerr << " (real)"; - cerr << ": " << *this << " --> " << *res << endl; - } - } delete lval; delete rval; return res; } +NetExpr* NetEConcat::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + vectorvals(parms_.size()); + unsigned gap = 0; + + unsigned valid_vals = 0; + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { + ivl_assert(*this, parms_[idx]); + vals[idx] = parms_[idx]->evaluate_function(loc, context_map); + if (vals[idx] == 0) continue; + + gap += vals[idx]->expr_width(); + + valid_vals += 1; + } + + NetExpr*res = 0; + if (valid_vals == parms_.size()) { + res = eval_arguments_(vals, gap); + } + for (unsigned idx = 0 ; idx < vals.size() ; idx += 1) { + delete vals[idx]; + } + return res; +} + NetExpr* NetEConst::evaluate_function(const LineInfo&, map&) const { @@ -397,19 +416,45 @@ NetExpr* NetEUnary::evaluate_function(const LineInfo&loc, if (val == 0) return 0; NetExpr*res = eval_arguments_(val); - if (res != 0) { - res->set_line(*this); - if (debug_eval_tree) { - cerr << get_fileline() << ": debug: Evaluated"; - if (val->expr_type() == IVL_VT_REAL) - cerr << " (real)"; - cerr << ": " << *this << " --> " << *res << endl; - } - } delete val; return res; } +NetExpr* NetESFunc::evaluate_function(const LineInfo&loc, + map&context_map) const +{ + ID id = built_in_id_(); + if (id == NOT_BUILT_IN) { + cerr << get_fileline() << ": error: " << name_ + << " is not a built-in function, so cannot" + << " be used in a constant function." << endl; + return 0; + } + + NetExpr*val0 = 0; + NetExpr*val1 = 0; + NetExpr*res = 0; + switch (nargs_(id)) { + case 1: + val0 = parms_[0]->evaluate_function(loc, context_map); + if (val0 == 0) break; + res = evaluate_one_arg_(id, val0); + break; + case 2: + val0 = parms_[0]->evaluate_function(loc, context_map); + val1 = parms_[1]->evaluate_function(loc, context_map); + if (val0 == 0 || val1 == 0) break; + res = evaluate_two_arg_(id, val0, val1); + break; + default: + ivl_assert(*this, 0); + break; + } + delete val0; + delete val1; + return res; +} + NetExpr* NetEUFunc::evaluate_function(const LineInfo&loc, map&context_map) const { diff --git a/netlist.h b/netlist.h index 904ec51f5..d6c928a38 100644 --- a/netlist.h +++ b/netlist.h @@ -3789,6 +3789,8 @@ class NetEConcat : public NetExpr { virtual bool has_width() const; virtual NetEConcat* dup_expr() const; virtual NetEConst* eval_tree(); + virtual NetExpr* evaluate_function(const LineInfo&loc, + std::map&ctx) const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -3797,6 +3799,8 @@ class NetEConcat : public NetExpr { std::vectorparms_; unsigned repeat_; ivl_variable_type_t expr_type_; + + NetEConst* eval_arguments_(const vector&vals, unsigned gap) const; }; @@ -4001,6 +4005,8 @@ class NetESFunc : public NetExpr { const NetExpr* parm(unsigned idx) const; virtual NetExpr* eval_tree(); + virtual NetExpr* evaluate_function(const LineInfo&loc, + std::map&ctx) const; virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); @@ -4012,11 +4018,43 @@ class NetESFunc : public NetExpr { virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); private: + enum ID { NOT_BUILT_IN = 0x0, + MATH_ONE_ARG = 0x100, + CLOG2, LN, LOG10, EXP, SQRT, FLOOR, CEIL, + SIN, COS, TAN, ASIN, ACOS, ATAN, + SINH, COSH, TANH, ASINH, ACOSH, ATANH, + AMS_ONE_ARG = 0x180, + ABS, + MATH_TWO_ARG = 0x200, + POW, ATAN2, HYPOT, + AMS_TWO_ARG = 0x280, + MIN, MAX }; + + bool is_ams_(ID id) const { return id & 0x80; }; + unsigned nargs_(ID id) const { return id >> 8; }; + const char* name_; ivl_variable_type_t type_; netenum_t*enum_type_; std::vectorparms_; + ID built_in_id_() const; + + NetExpr* evaluate_one_arg_(ID id, const NetExpr*arg) const; + NetExpr* evaluate_two_arg_(ID id, const NetExpr*arg0, + const NetExpr*arg1) const; + + NetEConst* evaluate_clog2_(const NetExpr*arg) const; + + NetECReal* evaluate_math_one_arg_(ID id, const NetExpr*arg) const; + NetECReal* evaluate_math_two_arg_(ID id, const NetExpr*arg0, + const NetExpr*arg1) const; + + NetExpr* evaluate_abs_(const NetExpr*arg) const; + + NetExpr* evaluate_min_max_(ID id, const NetExpr*arg0, + const NetExpr*arg1) const; + private: // not implemented NetESFunc(const NetESFunc&); NetESFunc& operator= (const NetESFunc&); diff --git a/netmisc.cc b/netmisc.cc index 98e90ed47..783c9a506 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2013 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 @@ -1127,6 +1127,17 @@ void collapse_partselect_pv_to_concat(Design*des, NetNet*sig) ivl_assert(*sig, idx == ps_map.size()); + /* The vlog95 and possibly other code generators do not want + * to have a group of part selects turned into a transparent + * concatenation. */ + if (disable_concatz_generation) { +// HERE: If the part selects have matching strengths then we can use +// a normal concat with a buf-Z after if the strengths are not +// both strong. We would ideally delete any buf-Z driving the +// concat, but that is not required for the vlog95 generator. + return; + } + // Ah HAH! The NetPartSelect::PV objects exactly cover the // target signal. We can replace all of them with a single // concatenation. diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index d490c6a7a..32b3001fb 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2013 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -105,6 +105,8 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) case 'l': oper = "<<"; break; case 'r': oper = ">>"; break; case 'R': oper = ">>>"; break; + case 'm': oper = "<"; break; + case 'M': oper = ">"; break; } fprintf(vlog_out, "("); @@ -174,6 +176,20 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) vlog_errors += 1; } break; + /* Convert Verilog-A min() or max() functions. This only works + * when the arguments have no side effect. */ + case 'm': + case 'M': + fprintf(vlog_out, "(("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ") %s (", oper); + emit_expr(scope, ivl_expr_oper2(expr), wid); + fprintf(vlog_out, ") ? ("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ") : ("); + emit_expr(scope, ivl_expr_oper2(expr), wid); + fprintf(vlog_out, "))"); + break; default: emit_expr(scope, ivl_expr_oper1(expr), wid); fprintf(vlog_out, ""); @@ -381,6 +397,16 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) ivl_expr_t sel_expr = ivl_expr_oper2(expr); ivl_expr_t sig_expr = ivl_expr_oper1(expr); ivl_select_type_t sel_type = ivl_expr_sel_type(expr); + /* If this is a dynamic array select, translate it differently. */ + if ((ivl_expr_type(sig_expr) == IVL_EX_SIGNAL) && + (ivl_signal_data_type(ivl_expr_signal(sig_expr)) == IVL_VT_DARRAY)) { + assert(sel_expr); + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_expr(scope, sel_expr, 0); + fprintf(vlog_out, "]"); + return; + } if (sel_expr) { unsigned width = ivl_expr_width(expr); ivl_expr_type_t type = ivl_expr_type(sig_expr); @@ -456,6 +482,9 @@ static void emit_expr_func(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) } emit_expr(scope, ivl_expr_parm(expr, count), 0); fprintf(vlog_out, ")"); + /* User functions without arguments are not supported. */ + } else if (ivl_expr_type(expr) == IVL_EX_UFUNC) { + fprintf(vlog_out, "()"); } } @@ -558,6 +587,20 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) ivl_expr_lineno(expr)); vlog_errors += 1; break; + /* Convert Verilog-A abs() function. This only works when the + * argument has no side effect. */ + case 'm': + fprintf(vlog_out, "(("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ") > "); + if (ivl_expr_value(expr) == IVL_VT_REAL) fprintf(vlog_out, "0.0"); + else fprintf(vlog_out, "0"); + fprintf(vlog_out, " ? ("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ") : -("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, "))"); + break; default: fprintf(vlog_out, ""); emit_expr(scope, ivl_expr_oper1(expr), wid); @@ -571,6 +614,17 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) } } +/* + * Class properties are not supported in vlog95, but they can be translated. + */ +void emit_class_property(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) +{ + ivl_signal_t sig = ivl_expr_signal(expr); + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + fprintf(vlog_out, ".%s", ivl_expr_name(expr)); +} + void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { switch (ivl_expr_type(expr)) { @@ -597,11 +651,30 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) case IVL_EX_EVENT: emit_expr_event(scope, expr, wid); break; + case IVL_EX_NEW: + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: New operator " + "is not supported.\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr)); + vlog_errors += 1; + break; + case IVL_EX_NULL: + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Null operator " + "is not supported.\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr)); + vlog_errors += 1; + break; case IVL_EX_NUMBER: emit_number(ivl_expr_bits(expr), ivl_expr_width(expr), ivl_expr_signed(expr), ivl_expr_file(expr), ivl_expr_lineno(expr)); break; + case IVL_EX_PROPERTY: + emit_class_property(scope, expr, wid); + break; case IVL_EX_REALNUM: emit_real_number(ivl_expr_dvalue(expr)); break; diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 9b13abe3f..40be840b3 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -108,10 +108,53 @@ static void emit_gate_strength(ivl_net_logic_t nlogic, unsigned strength_type) "gate", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic)); } +/* + * Look for a single driver behind an LPM that passes strength information + * and get the real drive information from it. + */ +static void get_unique_lpm_drive(ivl_lpm_t lpm, ivl_drive_t *drive1, + ivl_drive_t *drive0) +{ + ivl_nexus_t nex = ivl_lpm_data(lpm, 0); + unsigned idx, count = ivl_nexus_ptrs(nex); + unsigned have_driver = 0; + + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + ivl_drive_t cur_drive1 = ivl_nexus_ptr_drive1(nex_ptr); + ivl_drive_t cur_drive0 = ivl_nexus_ptr_drive0(nex_ptr); + if ((cur_drive1 == IVL_DR_HiZ) && + (cur_drive0 == IVL_DR_HiZ)) continue; + assert(! have_driver); + *drive1 = cur_drive1; + *drive0 = cur_drive0; + have_driver = 1; + } + + /* This should never happen. */ + if (! have_driver) { + fprintf(stderr, "%s:%u: vlog95 error: Unable to find drive " + "information for strength transparent LPM.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + } +} + static void emit_lpm_strength(ivl_lpm_t lpm) { - emit_strength(ivl_lpm_drive1(lpm), ivl_lpm_drive0(lpm), 2, - "LPM", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + ivl_lpm_type_t type = ivl_lpm_type(lpm); + ivl_drive_t drive1 = IVL_DR_STRONG; + ivl_drive_t drive0 = IVL_DR_STRONG; + /* This LPM object passes strength information so we need to look + * for the strength information at the real driver. */ + if (type == IVL_LPM_PART_PV) { + get_unique_lpm_drive(lpm, &drive1, &drive0); + } else { + drive1 = ivl_lpm_drive1(lpm); + drive0 = ivl_lpm_drive0(lpm); + } + emit_strength(drive1, drive0, 2, "LPM", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); } static void emit_delay(ivl_scope_t scope, ivl_expr_t rise, ivl_expr_t fall, @@ -193,6 +236,7 @@ static ivl_nexus_t get_lpm_output(ivl_scope_t scope, ivl_lpm_t lpm) { ivl_nexus_t output = 0; switch (ivl_lpm_type(lpm)) { + case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: case IVL_LPM_CAST_INT: @@ -213,6 +257,7 @@ static ivl_nexus_t get_lpm_output(ivl_scope_t scope, ivl_lpm_t lpm) case IVL_LPM_MUX: case IVL_LPM_PART_PV: case IVL_LPM_PART_VP: + case IVL_LPM_POW: case IVL_LPM_RE_AND: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: @@ -773,6 +818,19 @@ static void emit_lpm_func(ivl_scope_t scope, ivl_lpm_t lpm) static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) { switch (ivl_lpm_type(lpm)) { + /* Convert Verilog-A abs() function. This only works when the + * argument has no side effect. */ + case IVL_LPM_ABS: + fprintf(vlog_out, "(("); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); + fprintf(vlog_out, ") > "); +// HERE: If this is a real net then use 0.0. See the expr code. + fprintf(vlog_out, "0 ? ("); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); + fprintf(vlog_out, ") : -("); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); + fprintf(vlog_out, "))"); + break; case IVL_LPM_ADD: fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); @@ -830,8 +888,14 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0); fprintf(vlog_out, ")"); break; - case IVL_LPM_CONCAT: + /* A concat-Z should never be generated, but report it as an + * error if one is generated. */ case IVL_LPM_CONCATZ: + fprintf(stderr, "%s:%u: vlog95 error: Transparent concatenations " + "should not be generated.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + case IVL_LPM_CONCAT: emit_lpm_concat(scope, lpm); break; case IVL_LPM_DIVIDE: @@ -877,6 +941,17 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) case IVL_LPM_PART_VP: emit_lpm_part_select(scope, lpm); break; + case IVL_LPM_POW: + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); + fprintf(vlog_out, " ** "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0); + fprintf(vlog_out, ")"); + fprintf(stderr, "%s:%u: vlog95 error: Power operator is not " + "supported.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + break; case IVL_LPM_RE_AND: fprintf(vlog_out, "(&"); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index f20da5fd0..a0354c2c1 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -717,7 +717,14 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) case IVL_SCT_FUNCTION: assert(indent != 0); fprintf(vlog_out, "\n%*cfunction", indent, ' '); - assert(ivl_scope_ports(scope) >= 2); + if (ivl_scope_ports(scope) < 2) { + fprintf(stderr, "%s:%u: vlog95 error: Function (%s) has " + "no argments (or return value).\n", + ivl_scope_file(scope), + ivl_scope_lineno(scope), + ivl_scope_tname(scope)); + vlog_errors += 1; + } /* The function return information is the zero port. */ emit_func_return(ivl_scope_port(scope, 0)); fprintf(vlog_out, " "); diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index d34cdd97b..e58918b1d 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2013 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -174,6 +174,39 @@ static void emit_stmt_lval_ips(ivl_scope_t scope, ivl_lval_t lval, } } +/* + * Dynamic arrays are not supported in vlog95, but this assignment can be + * translated correctly. + */ +static void emit_stmt_lval_darray(ivl_scope_t scope, ivl_lval_t lval, + ivl_signal_t sig) +{ + ivl_expr_t idx = ivl_lval_idx(lval); + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + if (idx) { + fprintf(vlog_out, "["); + emit_expr(scope, idx, 0); + fprintf(vlog_out, "]"); + } +} + +/* + * Class or class properties are not supported in vlog95, but this assignment + * can be translated correctly. + */ +static void emit_stmt_lval_class(ivl_scope_t scope, ivl_lval_t lval, + ivl_signal_t sig) +{ + int idx = ivl_lval_property_idx(lval); + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + if (idx >= 0) { + ivl_type_t sig_type = ivl_signal_net_type(sig); + fprintf(vlog_out, ".%s", ivl_type_prop_name(sig_type, idx)); + } +} + static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) { ivl_signal_t sig = ivl_lval_sig(lval); @@ -182,6 +215,18 @@ static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) unsigned width = ivl_lval_width(lval); int msb, lsb; assert(width > 0); + assert(sig); + + switch (ivl_signal_data_type(sig)) { + case IVL_VT_DARRAY: + emit_stmt_lval_darray(scope, lval, sig); + return; + case IVL_VT_CLASS: + emit_stmt_lval_class(scope, lval, sig); + return; + default: + break; + } /* If there are no selects then just print the name. */ sel_expr = ivl_lval_part_off(lval); @@ -688,6 +733,9 @@ static void emit_port(ivl_scope_t scope, struct port_expr_s port_expr) * appropriate task call. It returns true (1) if it successfully * translated the block to a task call, otherwise it returns false * (0) to indicate the block needs to be emitted. + * + * When calling automatic tasks there is an initial ALLOC statement + * and a final FREE statement. */ static unsigned is_utask_call_with_args(ivl_scope_t scope, ivl_statement_t stmt) @@ -695,17 +743,31 @@ static unsigned is_utask_call_with_args(ivl_scope_t scope, unsigned idx, ports, task_idx = 0; unsigned count = ivl_stmt_block_count(stmt); unsigned lineno = ivl_stmt_lineno(stmt); + unsigned start, stop, is_auto = 0; ivl_scope_t task_scope = 0; port_expr_t port_exprs; /* Check to see if the block is of the basic form first. */ for (idx = 0; idx < count; idx += 1) { ivl_statement_t tmp = ivl_stmt_block_stmt(stmt, idx); + /* For an automatic task the ALLOC must be first. */ + if (ivl_statement_type(tmp) == IVL_ST_ALLOC) { + if (idx == 0) { + is_auto = 1; + continue; + } + } if (ivl_statement_type(tmp) == IVL_ST_ASSIGN) continue; if (ivl_statement_type(tmp) == IVL_ST_UTASK && !task_scope) { task_idx = idx; task_scope = ivl_stmt_call(tmp); assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); continue; + } + /* For an automatic task the FREE must be last. */ + if (ivl_statement_type(tmp) == IVL_ST_FREE) { + if (idx == count-1) { + if (is_auto) continue; + } } return 0; } @@ -720,8 +782,11 @@ static unsigned is_utask_call_with_args(ivl_scope_t scope, port_exprs[idx].type = IVL_SIP_NONE; port_exprs[idx].expr.rval = 0; } + /* Check that the input arguments are correct. */ - for (idx = 0; idx < task_idx; idx += 1) { + if (is_auto) start = 1; + else start = 0; + for (idx = start; idx < task_idx; idx += 1) { ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); unsigned port = utask_in_port_idx(task_scope, assign); if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { @@ -732,7 +797,9 @@ static unsigned is_utask_call_with_args(ivl_scope_t scope, port_exprs[port].expr.rval = ivl_stmt_rval(assign); } /* Check that the output arguments are correct. */ - for (idx = task_idx + 1; idx < count; idx += 1) { + if (is_auto) stop = count-1; + else stop = count; + for (idx = task_idx + 1; idx < stop; idx += 1) { ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); unsigned port = utask_out_port_idx(task_scope, assign); if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { diff --git a/tgt-vlog95/vlog95-s.conf b/tgt-vlog95/vlog95-s.conf index 19573a944..04e49e471 100644 --- a/tgt-vlog95/vlog95-s.conf +++ b/tgt-vlog95/vlog95-s.conf @@ -2,3 +2,4 @@ functor:synth2 functor:synth functor:syn-rules flag:DLL=vlog95.tgt +flag:DISABLE_CONCATZ_GENERATION=true diff --git a/tgt-vlog95/vlog95.conf b/tgt-vlog95/vlog95.conf index ee96d5cb3..362c35c6c 100644 --- a/tgt-vlog95/vlog95.conf +++ b/tgt-vlog95/vlog95.conf @@ -1 +1,2 @@ flag:DLL=vlog95.tgt +flag:DISABLE_CONCATZ_GENERATION=true