diff --git a/PExpr.h b/PExpr.h index 0d0b66f33..31998cdb0 100644 --- a/PExpr.h +++ b/PExpr.h @@ -306,6 +306,11 @@ class PEIdent : public PExpr { private: // Common functions to calculate parts of part/bit selects. bool calculate_parts_(Design*, NetScope*, long&msb, long&lsb) const; + NetExpr* calculate_up_do_base_(Design*, NetScope*) const; + bool calculate_param_range_(Design*, NetScope*, + const NetExpr*msb_ex, long&msb, + const NetExpr*lsb_ex, long&lsb) const; + bool calculate_up_do_width_(Design*, NetScope*, unsigned long&wid) const; private: @@ -321,6 +326,18 @@ class PEIdent : public PExpr { NetScope*found, const NetExpr*par_msb, const NetExpr*par_lsb) const; + NetExpr*elaborate_expr_param_part_(Design*des, + NetScope*scope, + const NetExpr*par, + NetScope*found, + const NetExpr*par_msb, + const NetExpr*par_lsb) const; + NetExpr*elaborate_expr_param_idx_up_(Design*des, + NetScope*scope, + const NetExpr*par, + NetScope*found, + const NetExpr*par_msb, + const NetExpr*par_lsb) const; NetExpr*elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, diff --git a/elab_expr.cc b/elab_expr.cc index d452b31e4..44f4ad86f 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -882,6 +882,51 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, return flag; } +/* + * When we know that this is an indexed part select (up or down) this + * method calculates the up/down base, as far at it can be calculated. + */ +NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const +{ + const name_component_t&name_tail = path_.back(); + ivl_assert(*this, !name_tail.index.empty()); + + const index_component_t&index_tail = name_tail.index.back(); + ivl_assert(*this, index_tail.lsb != 0); + ivl_assert(*this, index_tail.msb != 0); + + NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1); + return tmp; +} + +bool PEIdent::calculate_param_range_(Design*des, NetScope*scope, + const NetExpr*par_msb, long&par_msv, + const NetExpr*par_lsb, long&par_lsv) const +{ + if (par_msb == 0) { + // If the parameter doesn't have an explicit range, then + // just return range values of 0:0. The par_msv==0 is + // correct. The par_msv is not necessarily correct, but + // clients of this function don't need a correct value. + ivl_assert(*this, par_lsb == 0); + par_msv = 0; + par_lsv = 0; + return true; + } + + const NetEConst*tmp = dynamic_cast (par_msb); + ivl_assert(*this, tmp); + + par_msv = tmp->value().as_long(); + + tmp = dynamic_cast (par_lsb); + ivl_assert(*this, tmp); + + par_lsv = tmp->value().as_long(); + + return true; +} + unsigned PEIdent::test_width(Design*des, NetScope*scope, unsigned min, unsigned lval, bool&unsized_flag) const @@ -1094,6 +1139,131 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return 0; } +static verinum param_part_select_bits(const verinum&par_val, long wid, + long lsv, long par_lsv) +{ + verinum result (verinum::Vx, wid, true); + + for (long idx = 0 ; idx < wid ; idx += 1) { + long off = idx + lsv - par_lsv; + if (off < 0) + result.set(idx, verinum::Vx); + else if (off < (long)par_val.len()) + result.set(idx, par_val.get(off)); + else if (par_val.is_string()) // Pad strings with nulls. + result.set(idx, verinum::V0); + else if (par_val.has_len()) // Pad sized parameters with X + result.set(idx, verinum::Vx); + else // Unsized parameters are "infinite" width. + result.set(idx, sign_bit(par_val)); + } + + // If the input is a string, and the part select is working on + // byte boundaries, then make the result into a string. + if (par_val.is_string() && (labs(lsv-par_lsv)%8 == 0) && (wid%8 == 0)) + return result.as_string(); + + return result; +} + +NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, + const NetExpr*par, + NetScope*found_in, + const NetExpr*par_msb, + const NetExpr*par_lsb) const +{ + long msv, lsv; + bool flag = calculate_parts_(des, scope, msv, lsv); + if (!flag) + return 0; + + long par_msv, par_lsv; + flag = calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv); + if (!flag) + return 0; + + // Notice that the par_msv is not used is this function other + // then for this test. It is used to tell the direction that + // the bits are numbers, so that we can make sure the + // direction matches the part select direction. After that, + // we only need the par_lsv. + if (msv>lsv && par_msv=par_lsv) { + cerr << get_fileline() << ": error: Part select " + << "[" << msv << ":" << lsv << "] is out of order." << endl; + des->errors += 1; + return 0; + } + + long wid = 1 + labs(msv-lsv); + + if (debug_elaborate) + cerr << get_fileline() << ": debug: Calculate part select " + << "[" << msv << ":" << lsv << "] from range " + << "[" << par_msv << ":" << par_lsv << "]." << endl; + + const NetEConst*par_ex = dynamic_cast (par); + ivl_assert(*this, par_ex); + + verinum result = param_part_select_bits(par_ex->value(), wid, lsv, par_lsv); + NetEConst*result_ex = new NetEConst(result); + result_ex->set_line(*this); + + return result_ex; +} + +NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, + const NetExpr*par, + NetScope*found_in, + const NetExpr*par_msb, + const NetExpr*par_lsb) const +{ + + long par_msv, par_lsv; + bool flag = calculate_param_range_(des, scope, par_msb, par_msv, par_lsb, par_lsv); + if (!flag) + return 0; + + NetExpr*base = calculate_up_do_base_(des, scope); + if (base == 0) + return 0; + + unsigned long wid = 0; + calculate_up_do_width_(des, scope, wid); + + const NetEConst*par_ex = dynamic_cast (par); + ivl_assert(*this, par_ex); + + if (debug_elaborate) + cerr << get_fileline() << ": debug: Calculate part select " + << "[" << *base << "+:" << wid << "] from range " + << "[" << par_msv << ":" << par_lsv << "]." << endl; + + // Handle the special case that the base is constant. In this + // case, just precalculate the entire constant result. + if (NetEConst*base_c = dynamic_cast (base)) { + long lsv = base_c->value().as_long(); + + // Watch out for reversed bit numbering. We're making + // the part select from LSB to MSB. + if (par_msv < par_lsv) + lsv = lsv - wid + 1; + + verinum result = param_part_select_bits(par_ex->value(), wid, + lsv, par_lsv); + NetEConst*result_ex = new NetEConst(result); + result_ex->set_line(*this); + return result_ex; + } + + if ((par_msb < par_lsb) && (wid>1)) + base = make_add_expr(base, 1-(long)wid); + + NetExpr*tmp = par->dup_expr(); + tmp = new NetESelect(tmp, base, wid); + tmp->set_line(*this); + return tmp; +} + /* * Handle the case that the identifier is a parameter reference. The * parameter expression has already been located for us (as the par @@ -1106,84 +1276,29 @@ NetExpr* PEIdent::elaborate_expr_param(Design*des, const NetExpr*par_msb, const NetExpr*par_lsb) const { - NetExpr*tmp = par->dup_expr(); - const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) use_sel = name_tail.index.back().sel; - if (use_sel == index_component_t::SEL_PART) { - ivl_assert(*this, !name_tail.index.empty()); - const index_component_t&index_tail = name_tail.index.back(); - ivl_assert(*this, index_tail.msb); - ivl_assert(*this, index_tail.lsb); + // NOTE TO SELF: This is the way I want to see this code + // structured. This closely follows the structure of the + // elaborate_expr_net_ code, which splits all the various + // selects to different methods. + if (use_sel == index_component_t::SEL_PART) + return elaborate_expr_param_part_(des, scope, par, found_in, + par_msb, par_lsb); - /* If the identifier has a part select, we support - it by pulling the right bits out and making a - sized unsigned constant. This code assumes the - lsb of a parameter is 0 and the msb is the - width of the parameter. */ + if (use_sel == index_component_t::SEL_IDX_UP) + return elaborate_expr_param_idx_up_(des, scope, par, found_in, + par_msb, par_lsb); - verinum*lsn = index_tail.lsb->eval_const(des, scope); - verinum*msn = index_tail.msb->eval_const(des, scope); - if ((lsn == 0) || (msn == 0)) { - cerr << get_fileline() << ": error: " - "Part select expressions must be " - "constant expressions." << endl; - des->errors += 1; - return 0; - } + // NOTE TO SELF (continued): The code below should be + // rewritten in the above format, as I get to it. - long lsb = lsn->as_long(); - long msb = msn->as_long(); - if ((lsb < 0) || (msb < lsb)) { - cerr << get_fileline() << ": error: invalid part " - << "select: " << path_ - << "["<errors += 1; - return 0; - } - unsigned long ulsb=lsb; - unsigned long umsb=msb; + NetExpr*tmp = par->dup_expr(); - NetEConst*le = dynamic_cast(tmp); - assert(le); - - verinum result (verinum::V0, msb-lsb+1, true); - verinum exl = le->value(); - - /* Pull the bits from the parameter, one at a - time. If the bit is within the range, simply - copy it to the result. If the bit is outside - the range, we sign extend signed unsized - numbers, zero extend unsigned unsigned numbers, - and X extend sized numbers. */ - for (unsigned long idx = ulsb ; idx <= umsb ; idx += 1) { - if (idx < exl.len()) - result.set(idx-lsb, exl.get(idx)); - else if (exl.is_string()) - result.set(idx-lsb, verinum::V0); - else if (exl.has_len()) - result.set(idx-lsb, verinum::Vx); - else if (exl.has_sign()) - result.set(idx-lsb, exl.get(exl.len()-1)); - else - result.set(idx-lsb, verinum::V0); - } - - /* If the input is a string, and the part select - is working on byte boundaries, then the result - can be made into a string. */ - if (exl.is_string() - && (lsb%8 == 0) - && (result.len()%8 == 0)) - result = verinum(result.as_string()); - - delete tmp; - tmp = new NetEConst(result); - - } else if (use_sel == index_component_t::SEL_IDX_UP || use_sel == index_component_t::SEL_IDX_DO) { + if (use_sel == index_component_t::SEL_IDX_DO) { ivl_assert(*this, !name_tail.index.empty()); const index_component_t&index_tail = name_tail.index.back(); @@ -1370,8 +1485,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, // Special case: The index is out of range, so the value // of this expression is a 'bx vector the width of a word. if (!net->array_index_is_valid(addr)) { - verinum xxx (verinum::Vx, net->vector_width()); - NetEConst*resx = new NetEConst(xxx); + NetEConst*resx = make_const_x(net->vector_width()); resx->set_line(*this); delete word_index; return resx; @@ -1437,16 +1551,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, negative values. However, the width that they represent is unsigned. Remember that any order is possible, i.e., [1:0], [-4:6], etc. */ - unsigned long wid = 1 + ((msv>lsv)? (msv-lsv) : (lsv-msv)); - if (wid > net->vector_width()) { - cerr << get_fileline() << ": error: part select [" - << msv << ":" << lsv << "] out of range." << endl; - des->errors += 1; - //delete lsn; - //delete msn; - return net; - } - ivl_assert(*this, wid <= net->vector_width()); + unsigned long wid = 1 + labs(msv-lsv); if (net->sig()->sb_to_idx(msv) < net->sig()->sb_to_idx(lsv)) { cerr << get_fileline() << ": error: part select [" @@ -1457,27 +1562,79 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, return net; } - - if (net->sig()->sb_to_idx(msv) >= (signed) net->vector_width()) { - cerr << get_fileline() << ": error: part select [" - << msv << ":" << lsv << "] out of range." << endl; - des->errors += 1; - //delete lsn; - //delete msn; - return net; - } + long sb_lsb = net->sig()->sb_to_idx(lsv); + long sb_msb = net->sig()->sb_to_idx(msv); // If the part select covers exactly the entire // vector, then do not bother with it. Return the // signal itself. - if (net->sig()->sb_to_idx(lsv) == 0 && wid == net->vector_width()) + if (sb_lsb == 0 && wid == net->vector_width()) return net; - NetExpr*ex = new NetEConst(verinum(net->sig()->sb_to_idx(lsv))); - NetESelect*ss = new NetESelect(net, ex, wid); - ss->set_line(*this); + // If the part select covers NONE of the vector, then return a + // constant X. - return ss; + if ((sb_lsb >= (signed) net->vector_width()) || (sb_msb < 0)) { + NetEConst*tmp = make_const_x(wid); + tmp->set_line(*this); + return tmp; + } + + // If the part select is entirely within the vector, then make + // a simple part select. + if (sb_lsb >= 0 && sb_msb < (signed)net->vector_width()) { + NetExpr*ex = new NetEConst(verinum(sb_lsb)); + NetESelect*ss = new NetESelect(net, ex, wid); + ss->set_line(*this); + return ss; + } + + // Now the hard stuff. The part select is falling off at least + // one end. We're going to need a NetEConcat to mix the + // selection with overrun. + + NetEConst*bot = 0; + if (sb_lsb < 0) { + bot = make_const_x( 0-sb_lsb ); + bot->set_line(*this); + sb_lsb = 0; + } + NetEConst*top = 0; + if (sb_msb >= (signed)net->vector_width()) { + top = make_const_x( 1+sb_msb-net->vector_width() ); + top->set_line(*this); + sb_msb = net->vector_width()-1; + } + + unsigned concat_count = 1; + if (bot) concat_count += 1; + if (top) concat_count += 1; + + NetEConcat*concat = new NetEConcat(concat_count); + concat->set_line(*this); + + if (bot) { + concat_count -= 1; + concat->set(concat_count, bot); + } + if (sb_lsb == 0 && sb_msb+1 == (signed)net->vector_width()) { + concat_count -= 1; + concat->set(concat_count, net); + } else { + NetExpr*ex = new NetEConst(verinum(sb_lsb)); + ex->set_line(*this); + NetESelect*ss = new NetESelect(net, ex, 1+sb_msb-sb_lsb); + ss->set_line(*this); + concat_count -= 1; + concat->set(concat_count, ss); + } + if (top) { + concat_count -= 1; + concat->set(concat_count, top); + } + ivl_assert(*this, concat_count==0); + + return concat; } /* @@ -1486,14 +1643,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, NetESignal*net, NetScope*found_in) const { - const name_component_t&name_tail = path_.back(); - ivl_assert(*this, !name_tail.index.empty()); - - const index_component_t&index_tail = name_tail.index.back(); - ivl_assert(*this, index_tail.lsb != 0); - ivl_assert(*this, index_tail.msb != 0); - - NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1); + NetExpr*base = calculate_up_do_base_(des, scope); unsigned long wid = 0; calculate_up_do_width_(des, scope, wid); @@ -1618,8 +1768,7 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, /* The bit select is out of range of the vector. This is legal, but returns a constant 1'bx value. */ - verinum x (verinum::Vx); - NetEConst*tmp = new NetEConst(x); + NetEConst*tmp = make_const_x(1); tmp->set_line(*this); cerr << get_fileline() << ": warning: Bit select [" diff --git a/elab_scope.cc b/elab_scope.cc index 04cce6626..5887fd147 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -98,7 +98,16 @@ void Module::elaborate_parm_item_(perm_string name, const param_expr_t&cur, tmp->low_expr = 0; } - if (range->high_expr) { + if (range->high_expr && range->high_expr==range->low_expr) { + // Detect the special case of a "point" + // range. These are called out by setting the high + // and low expression ranges to the same + // expression. The exclude_flags should be false + // in this case + ivl_assert(*range->high_expr, tmp->low_open_flag==false && tmp->high_open_flag==false); + tmp->high_expr = tmp->low_expr; + + } else if (range->high_expr) { tmp->high_expr = elab_and_eval(des, scope, range->high_expr, -1); ivl_assert(*range->high_expr, tmp->high_expr); } else { diff --git a/netmisc.cc b/netmisc.cc index ac69d6885..39787d621 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -123,6 +123,13 @@ NetExpr* make_sub_expr(long val, NetExpr*expr) return res; } +NetEConst* make_const_x(unsigned long wid) +{ + verinum xxx (verinum::Vx, wid); + NetEConst*resx = new NetEConst(xxx); + return resx; +} + NetExpr* condition_reduce(NetExpr*expr) { if (expr->expr_width() == 1) diff --git a/netmisc.h b/netmisc.h index 439e86422..e82c3ef20 100644 --- a/netmisc.h +++ b/netmisc.h @@ -102,6 +102,11 @@ extern NetNet*add_to_net(Design*des, NetNet*sig, long val); extern NetExpr*make_add_expr(NetExpr*expr, long val); extern NetExpr*make_sub_expr(long val, NetExpr*expr); +/* + * Make a NetEConst object that contains only X bits. + */ +extern NetEConst*make_const_x(unsigned long wid); + /* * In some cases the lval is accessible as a pointer to the head of * a list of NetAssign_ objects. This function returns the width of diff --git a/parse.y b/parse.y index 7e81e9a3f..0775a325c 100644 --- a/parse.y +++ b/parse.y @@ -323,10 +323,15 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %left K_POW %left UNARY_PREC + /* to resolve dangling else ambiguity. */ %nonassoc less_than_K_else %nonassoc K_else + /* to resolve exclude (... ambiguity */ +%nonassoc '(' +%nonassoc K_exclude + %% /* A degenerate source file can be completely empty. */ @@ -2432,7 +2437,8 @@ parameter_value_range { $$ = pform_parameter_value_range($1, true, $3, false, $5); } | from_exclude '(' value_range_expression ':' value_range_expression ')' { $$ = pform_parameter_value_range($1, true, $3, true, $5); } - /* | K_exclude expression */ + | K_exclude expression + { $$ = pform_parameter_value_range(true, false, $2, false, $2); } ; value_range_expression diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index 2930c6a21..5ea34554f 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -285,10 +285,17 @@ static void draw_vpi_taskfunc_args(const char*call_string, word_ex = 0; } } - if (word_ex) - break; - - snprintf(buffer, sizeof buffer, "&A", sig, use_word); + if (word_ex) { + struct vector_info av; + av = draw_eval_expr(word_ex, STUFF_OK_XZ); + snprintf(buffer, sizeof buffer, + "&A>", sig, av.base, av.wid); + args[idx].vec = av; + args[idx].vec_flag = 1; + } else { + snprintf(buffer, sizeof buffer, + "&A", sig, use_word); + } args[idx].text = strdup(buffer); continue; } diff --git a/vpi/sys_finish.c b/vpi/sys_finish.c index 504bb5475..f55dd312e 100644 --- a/vpi/sys_finish.c +++ b/vpi/sys_finish.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999 Stephen Williams (steve@icarus.com) + * 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 @@ -16,44 +16,11 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#ifdef HAVE_CVS_IDENT -#ident "$Id: sys_finish.c,v 1.11 2007/04/09 22:49:33 steve Exp $" -#endif -# include "vpi_config.h" - -# include "vpi_user.h" -# include - -static PLI_INT32 sys_finish_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle arg; - - /* The argument is optional. */ - if (argv == 0) return 0; - arg = vpi_scan(argv); - - /* A string diagnostic message level makes no sense. */ - if (vpi_get(vpiType, arg) == vpiConstant && - vpi_get(vpiConstType, arg) == vpiStringConst) { - vpi_printf("Error: %s does not take a string argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - /* These functions take at most one argument (diagnostic message). */ - arg = vpi_scan(argv); - if (arg != 0) { - vpi_printf("Error: %s takes at most one argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - /* vpi_scan returning 0 (NULL) has already freed argv. */ - return 0; -} +#include "vpi_config.h" +#include "vpi_user.h" +#include "sys_priv.h" +#include static PLI_INT32 sys_finish_calltf(PLI_BYTE8 *name) { @@ -88,55 +55,16 @@ void sys_finish_register() tf_data.type = vpiSysTask; tf_data.tfname = "$finish"; tf_data.calltf = sys_finish_calltf; - tf_data.compiletf = sys_finish_compiletf; + tf_data.compiletf = sys_one_opt_numeric_arg_compiletf; tf_data.sizetf = 0; - tf_data.user_data = (PLI_BYTE8*)"$finish"; + tf_data.user_data = "$finish"; vpi_register_systf(&tf_data); tf_data.type = vpiSysTask; tf_data.tfname = "$stop"; tf_data.calltf = sys_finish_calltf; - tf_data.compiletf = sys_finish_compiletf; + tf_data.compiletf = sys_one_opt_numeric_arg_compiletf; tf_data.sizetf = 0; - tf_data.user_data = (PLI_BYTE8*)"$stop"; + tf_data.user_data = "$stop"; vpi_register_systf(&tf_data); } - -/* - * $Log: sys_finish.c,v $ - * Revision 1.11 2007/04/09 22:49:33 steve - * More strict use of PLI_BYTE8 type. - * - * Revision 1.10 2006/10/30 22:45:37 steve - * Updates for Cygwin portability (pr1585922) - * - * Revision 1.9 2004/01/21 01:22:53 steve - * Give the vip directory its own configure and vpi_config.h - * - * Revision 1.8 2003/02/21 03:24:03 steve - * Make the $stop system task really vpiStop. - * - * Revision 1.7 2002/08/12 01:35:04 steve - * conditional ident string using autoconfig. - * - * Revision 1.6 2001/07/25 03:10:50 steve - * Create a config.h.in file to hold all the config - * junk, and support gcc 3.0. (Stephan Boettcher) - * - * Revision 1.5 2001/01/01 19:33:44 steve - * Add $stop that does a finish. - * - * Revision 1.4 2000/02/23 02:56:56 steve - * Macintosh compilers do not support ident. - * - * Revision 1.3 1999/08/28 02:10:44 steve - * Call the right vpiFinish code. - * - * Revision 1.2 1999/08/19 02:51:03 steve - * Add vpi_sim_control - * - * Revision 1.1 1999/08/15 01:23:56 steve - * Convert vvm to implement system tasks with vpi. - * - */ - diff --git a/vpi/sys_icarus.c b/vpi/sys_icarus.c index a493c2b06..d2e1921d2 100644 --- a/vpi/sys_icarus.c +++ b/vpi/sys_icarus.c @@ -20,54 +20,13 @@ #include #include "sys_priv.h" -/* - * Routine to finish the simulation and return a value to the - * calling environment. - */ -static PLI_INT32 finish_and_return_compiletf(PLI_BYTE8* ud) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - assert(callh); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - (void) ud; /* Not used! */ - - /* We must have at least one argument. */ - if (argv == 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("$finish_and_return requires an argument.\n"); - vpi_control(vpiFinish, 1); - return 0; - } - - /* This must be a numeric argument. */ - if (! is_numeric_obj(vpi_scan(argv))) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("The argument to $finish_and_return must be numeric.\n"); - vpi_control(vpiFinish, 1); - return 0; - } - - /* We can only have one argument. */ - if (vpi_scan(argv) != 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("$finish_and_return takes only a single argument.\n"); - vpi_control(vpiFinish, 1); - return 0; - } - - return 0; -} - -static PLI_INT32 finish_and_return_calltf(PLI_BYTE8* ud) +static PLI_INT32 finish_and_return_calltf(PLI_BYTE8* name) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); vpiHandle arg; s_vpi_value val; - (void) ud; /* Not used! */ + (void) name; /* Not used! */ /* Get the return value. */ arg = vpi_scan(argv); @@ -92,9 +51,9 @@ void sys_special_register(void) tf_data.type = vpiSysTask; tf_data.calltf = finish_and_return_calltf; - tf_data.compiletf = finish_and_return_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.tfname = "$finish_and_return"; - tf_data.user_data = 0; + tf_data.user_data = "$finish_and_return"; vpi_register_systf(&tf_data); } diff --git a/vpi/sys_lxt.c b/vpi/sys_lxt.c index 72689046e..df6563fab 100644 --- a/vpi/sys_lxt.c +++ b/vpi/sys_lxt.c @@ -782,7 +782,7 @@ void sys_lxt_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; - tf_data.compiletf = sys_dumplimit_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; vpi_register_systf(&tf_data); diff --git a/vpi/sys_lxt2.c b/vpi/sys_lxt2.c index 92a11bbd9..debe0adad 100644 --- a/vpi/sys_lxt2.c +++ b/vpi/sys_lxt2.c @@ -797,7 +797,7 @@ void sys_lxt2_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; - tf_data.compiletf = sys_dumplimit_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; vpi_register_systf(&tf_data); diff --git a/vpi/sys_priv.c b/vpi/sys_priv.c index 51625a214..8e72ecd73 100644 --- a/vpi/sys_priv.c +++ b/vpi/sys_priv.c @@ -18,6 +18,7 @@ */ #include +#include #include "sys_priv.h" PLI_UINT64 timerec_to_time64(const struct t_vpi_time*time) @@ -129,3 +130,108 @@ vpiHandle sys_func_module(vpiHandle obj) return obj; } + +/* + * Standard compiletf routines. + */ + +/* For system functions that do not take an argument. */ +PLI_INT32 sys_no_arg_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + + /* Make sure there are no arguments. */ + if (argv != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 0; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s does not take an argument.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} + +/* For system functions that take a single numeric argument. */ +PLI_INT32 sys_one_numeric_arg_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + + /* Check that there is an argument and that it is numeric. */ + if (argv == 0) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a single numeric argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + if (! is_numeric_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes a single numeric argument.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} + +/* For system functions that take a single optional numeric argument. */ +PLI_INT32 sys_one_opt_numeric_arg_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + + /* The argument is optional so just return if none are found. */ + if (argv == 0) return 0; + + if (! is_numeric_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's argument must be numeric.\n", name); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + if (vpi_scan(argv) != 0) { + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s %s takes a single numeric argument.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); + } + + return 0; +} diff --git a/vpi/sys_priv.h b/vpi/sys_priv.h index 31a09a7bd..4f0d14a7b 100644 --- a/vpi/sys_priv.h +++ b/vpi/sys_priv.h @@ -50,4 +50,11 @@ extern unsigned is_string_obj(vpiHandle obj); extern vpiHandle sys_func_module(vpiHandle obj); +/* + * The standard compiletf routines. + */ +extern PLI_INT32 sys_no_arg_compiletf(PLI_BYTE8 *name); +extern PLI_INT32 sys_one_numeric_arg_compiletf(PLI_BYTE8 *name); +extern PLI_INT32 sys_one_opt_numeric_arg_compiletf(PLI_BYTE8 *name); + #endif diff --git a/vpi/sys_time.c b/vpi/sys_time.c index 034a7ce70..5d040f966 100644 --- a/vpi/sys_time.c +++ b/vpi/sys_time.c @@ -116,7 +116,7 @@ void sys_time_register() tf_data.tfname = "$time"; tf_data.sysfunctype = vpiTimeFunc; tf_data.calltf = sys_time_calltf; - tf_data.compiletf = 0; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$time"; vpi_register_systf(&tf_data); @@ -125,7 +125,7 @@ void sys_time_register() tf_data.tfname = "$realtime"; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = sys_realtime_calltf; - tf_data.compiletf = 0; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$realtime"; vpi_register_systf(&tf_data); @@ -134,7 +134,7 @@ void sys_time_register() tf_data.tfname = "$stime"; tf_data.sysfunctype = vpiIntFunc; tf_data.calltf = sys_time_calltf; - tf_data.compiletf = 0; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$stime"; vpi_register_systf(&tf_data); @@ -143,7 +143,7 @@ void sys_time_register() tf_data.tfname = "$simtime"; tf_data.sysfunctype = vpiTimeFunc; tf_data.calltf = sys_time_calltf; - tf_data.compiletf = 0; + tf_data.compiletf = sys_no_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$simtime"; vpi_register_systf(&tf_data); diff --git a/vpi/sys_vcd.c b/vpi/sys_vcd.c index b4c14185b..d83f8e2d6 100644 --- a/vpi/sys_vcd.c +++ b/vpi/sys_vcd.c @@ -795,7 +795,7 @@ void sys_vcd_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dumplimit_calltf; - tf_data.compiletf = sys_dumplimit_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; vpi_register_systf(&tf_data); diff --git a/vpi/sys_vcdoff.c b/vpi/sys_vcdoff.c index e12f750ef..b29f6a2b9 100644 --- a/vpi/sys_vcdoff.c +++ b/vpi/sys_vcdoff.c @@ -85,7 +85,7 @@ void sys_vcdoff_register() tf_data.type = vpiSysTask; tf_data.tfname = "$dumplimit"; tf_data.calltf = sys_dummy_calltf; - tf_data.compiletf = sys_dumplimit_compiletf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; tf_data.sizetf = 0; tf_data.user_data = "$dumplimit"; vpi_register_systf(&tf_data); diff --git a/vpi/vams_simparam.c b/vpi/vams_simparam.c index e917e8475..bc094270e 100644 --- a/vpi/vams_simparam.c +++ b/vpi/vams_simparam.c @@ -34,7 +34,7 @@ /* * Check that the routines are called with the correct arguments. */ -static PLI_INT32 simparam_compiletf(PLI_BYTE8* ud) +static PLI_INT32 simparam_compiletf(PLI_BYTE8* name_ext) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); assert(callh != 0); @@ -45,7 +45,7 @@ static PLI_INT32 simparam_compiletf(PLI_BYTE8* ud) if (argv == 0) { vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("$simparam%s requires an argument.\n", ud); + vpi_printf("$simparam%s requires a string argument.\n", name_ext); vpi_control(vpiFinish, 1); return 0; } @@ -55,7 +55,8 @@ static PLI_INT32 simparam_compiletf(PLI_BYTE8* ud) if (! is_string_obj(arg)) { vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("The first argument to $simparam%s must be a string.\n", ud); + vpi_printf("The first argument to $simparam%s must be a string.\n", + name_ext); vpi_control(vpiFinish, 1); } @@ -64,12 +65,12 @@ static PLI_INT32 simparam_compiletf(PLI_BYTE8* ud) if (arg == 0) return 0; /* For the string version the default must also be a string. */ - if (strcmp(ud, "$str") == 0) { + if (strcmp(name_ext, "$str") == 0) { if (! is_string_obj(arg)) { vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("When provided, the second argument to $simparam%s" - "must be a string.\n", ud); + "must be a string.\n", name_ext); vpi_control(vpiFinish, 1); } /* For the rest the default must be numeric. */ @@ -78,23 +79,32 @@ static PLI_INT32 simparam_compiletf(PLI_BYTE8* ud) vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("When provided, the second argument to $simparam%s" - "must be numeric.\n", ud); + "must be numeric.\n", name_ext); vpi_control(vpiFinish, 1); } } /* We can only have two argument. */ if (vpi_scan(argv) != 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("$simparam%s takes at most two arguments.\n", ud); - vpi_control(vpiFinish, 1); + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; + + vpi_printf("%s $simparam%s takes at most two arguments.\n", + msg, name_ext); + vpi_printf("%*s Found %u extra argument%s.\n", + strlen(msg), " ", argc, argc == 1 ? "" : "s"); + vpi_control(vpiFinish, 1); } return 0; } -static PLI_INT32 simparam_calltf(PLI_BYTE8* ud) +static PLI_INT32 simparam_calltf(PLI_BYTE8* name_ext) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); @@ -153,7 +163,8 @@ static PLI_INT32 simparam_calltf(PLI_BYTE8* ud) if (! have_def_val) { vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("unknown parameter name \"%s\".\n", param); + vpi_printf("$simparam%s unknown parameter name \"%s\".\n", + name_ext, param); } retval = defval; } @@ -168,7 +179,7 @@ static PLI_INT32 simparam_calltf(PLI_BYTE8* ud) return 0; } -static PLI_INT32 simparam_str_calltf(PLI_BYTE8* ud) +static PLI_INT32 simparam_str_calltf(PLI_BYTE8* name_ext) { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); @@ -217,7 +228,8 @@ static PLI_INT32 simparam_str_calltf(PLI_BYTE8* ud) if (defval == 0) { vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("unknown parameter name \"%s\".\n", param); + vpi_printf("$simparam%s unknown parameter name \"%s\".\n", + name_ext, param); defval = strdup(""); } retval = defval; @@ -234,9 +246,9 @@ static PLI_INT32 simparam_str_calltf(PLI_BYTE8* ud) return 0; } -static PLI_INT32 simparam_str_sizetf(PLI_BYTE8* ud) +static PLI_INT32 simparam_str_sizetf(PLI_BYTE8* name_ext) { - (void) ud; //* Not used! */ + (void) name_ext; //* Not used! */ return MAX_STRING_RESULT; // 128 characters max! } diff --git a/vpi/vcd_priv.c b/vpi/vcd_priv.c index 389bae21f..1f18fd2a3 100644 --- a/vpi/vcd_priv.c +++ b/vpi/vcd_priv.c @@ -188,25 +188,10 @@ void set_nexus_ident(int nex, const char *id) /* * Since the compiletf routines are all the same they are located here, - * so we only need a single copy. + * so we only need a single copy. Some are generic enough they can use + * the ones in sys_priv.c (no arg, one numeric arg. */ -/* $dumpall, $dumpflush, $dumpoff and $dumpon do not take an argument. */ -PLI_INT32 sys_no_arg_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - - if (argv != 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s does not take an argument.\n", name); - vpi_control(vpiFinish, 1); - } - - return 0; -} - /* $dumpfile takes a single string argument. */ PLI_INT32 sys_dumpfile_compiletf(PLI_BYTE8 *name) { @@ -217,7 +202,7 @@ PLI_INT32 sys_dumpfile_compiletf(PLI_BYTE8 *name) if (argv == 0) { vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s requires an argument.\n", name); + vpi_printf("%s requires a single string argument.\n", name); vpi_control(vpiFinish, 1); return 0; } @@ -228,44 +213,19 @@ PLI_INT32 sys_dumpfile_compiletf(PLI_BYTE8 *name) vpi_control(vpiFinish, 1); } - /* Check that there is only a single argument. */ + /* Make sure there are no extra arguments. */ if (vpi_scan(argv) != 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s takes a single argument.\n", name); - vpi_control(vpiFinish, 1); - } + char msg [64]; + snprintf(msg, 64, "ERROR: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); - return 0; -} + unsigned argc = 1; + while (vpi_scan(argv)) argc += 1; -/* $dumplimit takes a single numeric argument. */ -PLI_INT32 sys_dumplimit_compiletf(PLI_BYTE8 *name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - - /* Check that there is an argument and that it is numeric. */ - if (argv == 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s requires an argument.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - if (! is_numeric_obj(vpi_scan(argv))) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s's argument must be numeric.\n", name); - vpi_control(vpiFinish, 1); - } - - /* Check that there is only a single argument. */ - if (vpi_scan(argv) != 0) { - vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s takes a single argument.\n", name); + vpi_printf("%s %s takes a single string argument.\n", msg, name); + vpi_printf("%*s Found %u extra argument%s.\n", + strlen(msg), " ", argc, argc == 1 ? "" : "s"); vpi_control(vpiFinish, 1); } diff --git a/vpi/vcd_priv.h b/vpi/vcd_priv.h index 2645f94f7..acc0544cd 100644 --- a/vpi/vcd_priv.h +++ b/vpi/vcd_priv.h @@ -44,9 +44,7 @@ extern const char*find_nexus_ident(int nex); extern void set_nexus_ident(int nex, const char *id); /* The compiletf routines are common for the VCD, LXT and LXT2 dumpers. */ -extern PLI_INT32 sys_no_arg_compiletf(PLI_BYTE8 *name); extern PLI_INT32 sys_dumpfile_compiletf(PLI_BYTE8 *name); -extern PLI_INT32 sys_dumplimit_compiletf(PLI_BYTE8 *name); extern PLI_INT32 sys_dumpvars_compiletf(PLI_BYTE8 *name); #endif diff --git a/vvp/README.txt b/vvp/README.txt index ec2c6dd58..2577a9d17 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -754,6 +754,47 @@ by the fork atomically joins that scope. Once the transient thread joins the scope, it stays there until it ends. Threads never change scopes, not even transient threads. +VPI TASK/FUNCTION CALLS + +Threads call vpi tasks with the %vpi_call or %vpi_func +instructions. The formats are: + + %vpi_call , ... ; + %vpi_func , ... ; + %vpi_func/r , ... ; + +The is an index into the string table. The indexed string +is the source code file name where this call appears. The is +the line number from the source code where this task/function appears. + +The is a string that is the name of the system +task/function. For example, "$display", $strobe", etc. This name is +looked up and compared with the registered system tasks/functions. + +The ... is a comma (",") separated list of arguments. These are +made available to the VPI code as vpi handles. + +* The &A<> argument + +The &A<> argument is a reference to the word of a variable array. The +syntax is: + + &A '<' , '>' + +The is the label for a variable array, and the is +the cannonical word index as an unsigned integer. + +* The T<> argument + +This is the catch-all for arguments that are not otherwise +handled. This references the bits directly in the thread. The format +is: + + T '<' , , '>' + +The and are the base of a vector value in the thread and +the width of the vector. The is 's' or 'u' for signed or unsigned. + TRUTH TABLES The logic that a functor represents is expressed as a truth table. The diff --git a/vvp/array.cc b/vvp/array.cc index 34ed440d9..8143f2c23 100644 --- a/vvp/array.cc +++ b/vvp/array.cc @@ -1,6 +1,6 @@ /* * Copyright (c) 2007-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 @@ -55,6 +55,8 @@ struct __vpiArray { unsigned array_count; struct __vpiDecConst first_addr; struct __vpiDecConst last_addr; + struct __vpiDecConst msb; + struct __vpiDecConst lsb; // If this is a net array, nets lists the handles. vpiHandle*nets; // If this is a var array, then these are used instead of nets. @@ -81,7 +83,25 @@ struct __vpiArrayIndex { struct __vpiArrayVthrA { struct __vpiHandle base; struct __vpiArray*array; + // If wid==0, then address is the address into the array. unsigned address; + // If wid >0, then the address is the base and wid the vector + // width of the index to pull from the thread. + unsigned wid; + + unsigned get_address() const + { + if (wid == 0) + return address; + vvp_vector4_t tmp (wid); + for (unsigned idx = 0 ; idx < wid ; idx += 1) { + vvp_bit4_t bit = vthread_get_bit(vpip_current_vthread, address+idx); + tmp.set_bit(idx, bit); + } + unsigned long val; + vector4_to_value(tmp, val); + return val; + } }; /* @@ -121,8 +141,10 @@ static vpiHandle array_index_scan(vpiHandle ref, int); static int array_index_free_object(vpiHandle ref); static int vpi_array_var_word_get(int code, vpiHandle); +static char*vpi_array_var_word_get_str(int code, vpiHandle); static void vpi_array_var_word_get_value(vpiHandle, p_vpi_value); static vpiHandle vpi_array_var_word_put_value(vpiHandle, p_vpi_value, int); +static vpiHandle vpi_array_var_word_get_handle(int code, vpiHandle ref); static int vpi_array_vthr_A_get(int code, vpiHandle); static char*vpi_array_vthr_A_get_str(int code, vpiHandle); @@ -171,10 +193,10 @@ static const struct __vpirt vpip_array_index_rt = { static const struct __vpirt vpip_array_var_word_rt = { vpiMemoryWord, &vpi_array_var_word_get, - 0, + &vpi_array_var_word_get_str, &vpi_array_var_word_get_value, &vpi_array_var_word_put_value, - 0, + &vpi_array_var_word_get_handle, 0, 0, 0 @@ -342,11 +364,34 @@ static int vpi_array_var_word_get(int code, vpiHandle ref) case vpiSize: return (int) parent->vals_width; + case vpiLeftRange: + return parent->msb.value; + + case vpiRightRange: + return parent->lsb.value; + default: return 0; } } +static char*vpi_array_var_word_get_str(int code, vpiHandle ref) +{ + struct __vpiArrayWord*obj = array_var_word_from_handle(ref); + struct __vpiArray*parent; + + assert(obj); + unsigned index = decode_array_word_pointer(obj, parent); + + if (code == vpiFile) { // Not implemented for now! + return simple_set_rbuf_str(file_names[0]); + } + + char sidx [64]; + snprintf(sidx, 63, "%d", (int)index + parent->first_addr.value); + return generic_get_str(code, &parent->scope->base, parent->name, sidx); +} + static void vpi_array_var_word_get_value(vpiHandle ref, p_vpi_value value) { struct __vpiArrayWord*obj = array_var_word_from_handle(ref); @@ -377,6 +422,35 @@ static vpiHandle vpi_array_var_word_put_value(vpiHandle ref, p_vpi_value vp, int return ref; } +static vpiHandle vpi_array_var_word_get_handle(int code, vpiHandle ref) +{ + struct __vpiArrayWord*obj = array_var_word_from_handle(ref); + struct __vpiArray*parent; + + assert(obj); + decode_array_word_pointer(obj, parent); + + switch (code) { + + case vpiIndex: + break; // Not implemented! + + case vpiLeftRange: + return &parent->msb.base; + + case vpiRightRange: + return &parent->lsb.base; + + case vpiParent: + return &parent->base; + + case vpiScope: + return &parent->scope->base; + } + + return 0; +} + # define ARRAY_ITERATOR(ref) (assert(ref->vpi_type->type_code==vpiIterator), \ (struct __vpiArrayIterator*)ref) @@ -460,9 +534,14 @@ static int vpi_array_vthr_A_get(int code, vpiHandle ref) return 0; // Not implemented for now! case vpiSize: - assert(parent->vals); return parent->vals_width; + case vpiLeftRange: + return parent->msb.value; + + case vpiRightRange: + return parent->lsb.value; + // For now &A<> is only a constant select. This will need // to be changed when it supports variable selection. case vpiConstantSelect: @@ -483,9 +562,9 @@ static char*vpi_array_vthr_A_get_str(int code, vpiHandle ref) return simple_set_rbuf_str(file_names[0]); } - char index [64]; - snprintf(index, 63, "%d", (int)obj->address + parent->first_addr.value); - return generic_get_str(code, &parent->scope->base, parent->name, index); + char sidx [64]; + snprintf(sidx, 63, "%d", (int)obj->get_address() + parent->first_addr.value); + return generic_get_str(code, &parent->scope->base, parent->name, sidx); } static void vpi_array_vthr_A_get_value(vpiHandle ref, p_vpi_value value) @@ -495,18 +574,10 @@ static void vpi_array_vthr_A_get_value(vpiHandle ref, p_vpi_value value) struct __vpiArray*parent = obj->array; assert(parent); - assert(parent->vals); - assert(obj->address < parent->array_count); - unsigned index = obj->address; - unsigned width = parent->vals_width; - - /* If we don't have a value yet just return X. */ - if (parent->vals[index].size() == 0) { - vpip_vec4_get_value(vvp_vector4_t(width), width, false, value); - } else { - vpip_vec4_get_value(parent->vals[index], width, false, value); - } + unsigned index = obj->get_address(); + vvp_vector4_t tmp = array_get_word(parent, index); + vpip_vec4_get_value(tmp, parent->vals_width, false, value); } static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int) @@ -515,10 +586,10 @@ static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int) assert(obj); struct __vpiArray*parent = obj->array; - unsigned index = obj->address; + unsigned index = obj->get_address(); assert(parent); - assert(obj->address < parent->array_count); + assert(index < parent->array_count); vvp_vector4_t val = vec4_from_vpi_value(vp, parent->vals_width); array_set_word(parent, index, 0, val); @@ -534,6 +605,18 @@ static vpiHandle vpi_array_vthr_A_get_handle(int code, vpiHandle ref) switch (code) { + case vpiIndex: + break; // Not implemented! + + case vpiLeftRange: + return &parent->msb.base; + + case vpiRightRange: + return &parent->lsb.base; + + case vpiParent: + return &parent->base; + case vpiScope: return &parent->scope->base; } @@ -541,7 +624,6 @@ static vpiHandle vpi_array_vthr_A_get_handle(int code, vpiHandle ref) return 0; } - void array_set_word(vvp_array_t arr, unsigned address, unsigned part_off, @@ -646,6 +728,8 @@ static vpiHandle vpip_make_array(char*label, const char*name, obj->nets = 0; obj->vals = 0; obj->vals_width = 0; + vpip_make_dec_const(&obj->msb, 0); + vpip_make_dec_const(&obj->lsb, 0); obj->vals_words = 0; // Initialize (clear) the read-ports list. @@ -704,6 +788,8 @@ void compile_var_array(char*label, char*name, int last, int first, /* Make the words. */ arr->vals = new vvp_vector4_t[arr->array_count]; arr->vals_width = labs(msb-lsb) + 1; + vpip_make_dec_const(&arr->msb, msb); + vpip_make_dec_const(&arr->lsb, lsb); free(label); free(name); @@ -976,13 +1062,40 @@ vpiHandle vpip_make_vthr_A(char*label, unsigned addr) obj->array = array_find(label); assert(obj->array); + free(label); obj->address = addr; + obj->wid = 0; assert(addr < obj->array->array_count); return &(obj->base); } +vpiHandle vpip_make_vthr_A(char*label, char*symbol) +{ + struct __vpiArrayVthrA*obj = (struct __vpiArrayVthrA*) + malloc(sizeof (struct __vpiArrayVthrA)); + + obj->base.vpi_type = &vpip_array_vthr_A_rt; + + obj->array = array_find(label); + assert(obj->array); + free(label); + + unsigned base; + unsigned wid; + char sflag; + int rc = sscanf(symbol, "T<%u,%u,%c>", &base, &wid, &sflag); + assert(rc == 3); + free(symbol); + + obj->address = base; + obj->wid = wid; + assert(sflag == 'u'); + + return &(obj->base); +} + void compile_array_cleanup(void) { if (array_table) { diff --git a/vvp/parse.y b/vvp/parse.y index 1bce45a2d..8fb6e57e8 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -817,6 +817,8 @@ argument } | K_A '<' T_SYMBOL ',' T_NUMBER '>' { $$ = vpip_make_vthr_A($3, $5); } + | K_A '<' T_SYMBOL ',' T_SYMBOL '>' + { $$ = vpip_make_vthr_A($3, $5); } | K_PV '<' T_SYMBOL ',' T_NUMBER ',' T_NUMBER '>' { $$ = vpip_make_PV($3, $5, $7); } | K_PV '<' T_SYMBOL ',' '-' T_NUMBER ',' T_NUMBER '>' diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index be3f42bef..4d7b3791e 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -446,6 +446,7 @@ vpiHandle vpip_make_vthr_vector(unsigned base, unsigned wid, bool signed_flag); vpiHandle vpip_make_vthr_word(unsigned base, const char*type); vpiHandle vpip_make_vthr_A(char*symbol, unsigned index); +vpiHandle vpip_make_vthr_A(char*symbol, char*symbol); /* * This function is called before any compilation to load VPI diff --git a/vvp/vvp_island.cc b/vvp/vvp_island.cc index b56692fca..6c23c79d5 100644 --- a/vvp/vvp_island.cc +++ b/vvp/vvp_island.cc @@ -25,6 +25,7 @@ # include # include # include +# include #ifdef HAVE_MALLOC_H # include #endif