diff --git a/design_dump.cc b/design_dump.cc index 3c77aa1e9..53b625f3d 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -302,6 +302,14 @@ void NetArrayDq::dump_node(ostream&o, unsigned ind) const dump_obj_attr(o, ind+4); } +void NetCastReal::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "Cast to real (NetCastReal): " << + name() << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + void NetCLShift::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "Combinatorial shift (NetCLShift): " << diff --git a/elab_net.cc b/elab_net.cc index 8cbab1fdf..4b5370e34 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -716,6 +716,16 @@ NetNet* PEBinary::elaborate_net_div_(Design*des, NetScope*scope, unsigned rwidth = lwidth; + // If either operand is IVL_VT_REAL, then cast the other to + // IVL_VT_REAL so that the division can become IVL_VT_REAL. + + if (lsig->data_type()==IVL_VT_REAL || rsig->data_type()==IVL_VT_REAL) { + if (lsig->data_type() != IVL_VT_REAL) + lsig = cast_to_real(des, scope, lsig); + if (rsig->data_type() != IVL_VT_REAL) + rsig = cast_to_real(des, scope, rsig); + } + if (rwidth == 0) { rwidth = lsig->vector_width(); if (rsig->vector_width() > rwidth) diff --git a/emit.cc b/emit.cc index 606d13beb..84c4d6a25 100644 --- a/emit.cc +++ b/emit.cc @@ -72,6 +72,11 @@ bool NetCaseCmp::emit_node(struct target_t*tgt) const return true; } +bool NetCastReal::emit_node(struct target_t*tgt) const +{ + return tgt->lpm_cast_real(this); +} + bool NetCLShift::emit_node(struct target_t*tgt) const { tgt->lpm_clshift(this); diff --git a/ivl_target.h b/ivl_target.h index 6ab07a9ea..61069a6eb 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -254,6 +254,7 @@ typedef enum ivl_lpm_type_e { IVL_LPM_ABS = 32, IVL_LPM_ADD = 0, IVL_LPM_ARRAY = 30, + IVL_LPM_CAST_REAL = 33, IVL_LPM_CONCAT = 16, IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */ IVL_LPM_CMP_EQ = 10, diff --git a/netlist.cc b/netlist.cc index fefd23581..db630eb6b 100644 --- a/netlist.cc +++ b/netlist.cc @@ -849,6 +849,15 @@ const NetScope* NetProcTop::scope() const return scope_; } +NetCastReal::NetCastReal(NetScope*scope, perm_string n, bool signed_flag) +: NetNode(scope, n, 2), signed_flag_(signed_flag) +{ + pin(0).set_dir(Link::OUTPUT); + pin(0).set_name(perm_string::literal("O"), 0); + pin(1).set_dir(Link::INPUT); + pin(1).set_name(perm_string::literal("I"), 0); +} + NetConcat::NetConcat(NetScope*scope, perm_string n, unsigned wid, unsigned cnt) : NetNode(scope, n, cnt+1), width_(wid) { diff --git a/netlist.h b/netlist.h index e23fcb3fd..7c9186060 100644 --- a/netlist.h +++ b/netlist.h @@ -916,6 +916,26 @@ class NetArrayDq : public NetNode { }; +/* + * Convert an input to IVL_VT_REAL. The input is pin(1), which can be + * any vector type (VT_BOOL or VT_LOGIC) and the output is pin(0), + * which is IVL_VT_REAL. The conversion interprets the input as an + * unsigned value unless the signed_flag is true. + */ +class NetCastReal : public NetNode { + + public: + NetCastReal(NetScope*s, perm_string n, bool signed_flag); + + bool signed_flag() const { return signed_flag_; } + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*) const; + + private: + bool signed_flag_; +}; + /* * This type represents the LPM_CLSHIFT device. */ diff --git a/netmisc.cc b/netmisc.cc index 39787d621..eea23d1e2 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -76,6 +76,25 @@ NetNet* add_to_net(Design*des, NetNet*sig, long val) #endif } +NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) +{ + if (src->data_type() == IVL_VT_REAL) + return src; + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE); + tmp->data_type(IVL_VT_REAL); + tmp->set_line(*src); + + NetCastReal*cast = new NetCastReal(scope, scope->local_symbol(), src->get_signed()); + cast->set_line(*src); + des->add_node(cast); + + connect(cast->pin(0), tmp->pin(0)); + connect(cast->pin(1), src->pin(0)); + + return tmp; +} + /* * Add a signed constant to an existing expression. Generate a new * NetEBAdd node that has the input expression and an expression made diff --git a/netmisc.h b/netmisc.h index e82c3ef20..3c8ce1ca9 100644 --- a/netmisc.h +++ b/netmisc.h @@ -64,6 +64,12 @@ extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w); extern NetNet*pad_to_width_signed(Design*des, NetNet*n, unsigned w); +/* + * Generate the nodes necessary to cast an expression (a net) to a + * real value. + */ +extern NetNet*cast_to_real(Design*des, NetScope*scope, NetNet*src); + /* * Take the input expression and return a variation that assures that * the expression is 1-bit wide and logical. This reflects the needs diff --git a/t-dll-api.cc b/t-dll-api.cc index a9c4c561e..e5e275aad 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -886,6 +886,7 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx) assert(net); switch (net->type) { case IVL_LPM_ABS: + case IVL_LPM_CAST_REAL: assert(idx == 0); return net->u_.arith.a; @@ -1028,20 +1029,18 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net, unsigned idx) switch (net->type) { case IVL_LPM_ABS: case IVL_LPM_ADD: - case IVL_LPM_DIVIDE: - case IVL_LPM_MOD: - case IVL_LPM_MULT: - case IVL_LPM_POW: - case IVL_LPM_SUB: - assert(idx == 0); - return net->u_.arith.q; - + case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_NEE: + case IVL_LPM_DIVIDE: + case IVL_LPM_MOD: + case IVL_LPM_MULT: + case IVL_LPM_POW: + case IVL_LPM_SUB: assert(idx == 0); return net->u_.arith.q; @@ -1144,6 +1143,7 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net) return 0; case IVL_LPM_ABS: case IVL_LPM_ADD: + case IVL_LPM_CAST_REAL: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_GE: diff --git a/t-dll.cc b/t-dll.cc index 0261a8346..edeb58a8c 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1550,6 +1550,39 @@ void dll_target::lpm_clshift(const NetCLShift*net) scope_add_lpm(obj->scope, obj); } +bool dll_target::lpm_cast_real(const NetCastReal*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_CAST_REAL; + obj->name = net->name(); // NetCastReal names are permallocated + assert(net->scope()); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + + obj->width = 0; + obj->u_.arith.signed_flag = net->signed_flag()? 1 : 0; + + const Nexus*nex; + + nex = net->pin(0).nexus(); + assert(nex->t_cookie()); + + obj->u_.arith.q = nex->t_cookie(); + + nex = net->pin(1).nexus(); + assert(nex->t_cookie()); + obj->u_.arith.a = nex->t_cookie(); + + nexus_lpm_add(obj->u_.arith.q, obj, 0, IVL_DR_STRONG, IVL_DR_STRONG); + nexus_lpm_add(obj->u_.arith.a, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + + make_lpm_delays_(obj, net); + + scope_add_lpm(obj->scope, obj); + + return true; +} + /* * Make out of the NetCompare object an ivl_lpm_s object. The * comparators in ivl_target do not support < or <=, but they can be diff --git a/t-dll.h b/t-dll.h index 63f0a1bb2..3b017bf83 100644 --- a/t-dll.h +++ b/t-dll.h @@ -76,6 +76,7 @@ struct dll_target : public target_t, public expr_scan_t { void lpm_abs(const NetAbs*); void lpm_add_sub(const NetAddSub*); bool lpm_array_dq(const NetArrayDq*); + bool lpm_cast_real(const NetCastReal*); void lpm_clshift(const NetCLShift*); void lpm_compare(const NetCompare*); void lpm_divide(const NetDivide*); diff --git a/target.cc b/target.cc index e474c7cf7..0d71e0e26 100644 --- a/target.cc +++ b/target.cc @@ -107,6 +107,13 @@ bool target_t::lpm_array_dq(const NetArrayDq*) return false; } +bool target_t::lpm_cast_real(const NetCastReal*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetCastReal." << endl; + return false; +} + void target_t::lpm_clshift(const NetCLShift*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index d002a9797..89c78c037 100644 --- a/target.h +++ b/target.h @@ -72,6 +72,7 @@ struct target_t { virtual void lpm_add_sub(const NetAddSub*); virtual bool lpm_array_dq(const NetArrayDq*); virtual void lpm_clshift(const NetCLShift*); + virtual bool lpm_cast_real(const NetCastReal*); virtual void lpm_compare(const NetCompare*); virtual void lpm_divide(const NetDivide*); virtual void lpm_modulo(const NetModulo*); diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index ecec77782..4238a4820 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -240,6 +240,31 @@ static void show_lpm_array(ivl_lpm_t net) } } +static void show_lpm_cast_real(ivl_lpm_t net) +{ + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_CAST_REAL %s: \n", + ivl_lpm_basename(net), width); + + ivl_nexus_t q = ivl_lpm_q(net,0); + ivl_nexus_t a = ivl_lpm_data(net,0); + fprintf(out, " O: %s\n", ivl_nexus_name(ivl_lpm_q(net,0))); + fprintf(out, " A: %s\n", ivl_nexus_name(ivl_lpm_data(net,0))); + + if (type_of_nexus(q) != IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of Q is %s, expecting real\n", + data_type_string(type_of_nexus(q))); + stub_errors += 1; + } + + if (type_of_nexus(a) == IVL_VT_REAL) { + fprintf(out, " ERROR: Data type of A is %s, expecting !real\n", + data_type_string(type_of_nexus(a))); + stub_errors += 1; + } +} + static void show_lpm_divide(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -802,6 +827,10 @@ static void show_lpm(ivl_lpm_t net) show_lpm_array(net); break; + case IVL_LPM_CAST_REAL: + show_lpm_cast_real(net); + break; + case IVL_LPM_DIVIDE: show_lpm_divide(net); break; diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 9a9d51d6d..f7616ade0 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -387,6 +387,7 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: + case IVL_LPM_CAST_REAL: case IVL_LPM_CONCAT: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index ed1aa48d8..1f2cdeaec 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1079,6 +1079,17 @@ static void draw_lpm_abs(ivl_lpm_t net) net, dly, src_table[0]); } +static void draw_lpm_cast_real(ivl_lpm_t net) +{ + const char*src_table[1]; + draw_lpm_data_inputs(net, 0, 1, src_table); + + const char*dly = draw_lpm_output_delay(net); + + fprintf(vvp_out, "L_%p%s .cast/real %s;\n", + net, dly, src_table[0]); +} + static void draw_lpm_add(ivl_lpm_t net) { const char*src_table[2]; @@ -1644,6 +1655,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net) draw_lpm_abs(net); return; + case IVL_LPM_CAST_REAL: + draw_lpm_cast_real(net); + return; + case IVL_LPM_ADD: case IVL_LPM_SUB: case IVL_LPM_MULT: diff --git a/vpi/sys_fileio.c b/vpi/sys_fileio.c index 9d705a82b..3d666a9d2 100644 --- a/vpi/sys_fileio.c +++ b/vpi/sys_fileio.c @@ -20,6 +20,7 @@ # include "vpi_user.h" # include "sys_priv.h" # include +# include # include # include # include @@ -91,6 +92,7 @@ static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name) s_vpi_value val; int fail = 0; char *mode_string = 0; + unsigned idx; vpiHandle item = vpi_scan(argv); vpiHandle mode = vpi_scan(argv); @@ -103,10 +105,54 @@ static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name) vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s's mode argument was not a valid string.\n", + vpi_printf("%s's mode argument is not a valid string.\n", name); fail = 1; } + + /* Make sure the mode string is correct. */ + if (strlen(val.value.str) > 3) { + vpi_printf("WARNING: %s line %d: ", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's mode argument (%s) is too long.\n", + name, val.value.str); + fail = 1; + } else { + unsigned bin = 0, plus = 0; + switch (val.value.str[0]) { + case 'r': + case 'w': + case 'a': + for (idx = 1; idx < 3 ; idx++) { + if (val.value.str[idx] == '\0') break; + switch (val.value.str[idx]) { + case 'b': + if (bin) fail = 1; + bin = 1; + break; + case '+': + if (plus) fail = 1; + plus = 1; + break; + default: + fail = 1; + break; + } + } + if (! fail) break; + + default: + vpi_printf("WARNING: %s line %d: ", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's mode argument (%s) is invalid.\n", + name, val.value.str); + fail = 1; + break; + } + } + mode_string = strdup(val.value.str); vpi_free_object(argv); @@ -121,12 +167,31 @@ static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name) if (val.format != vpiStringVal || !*(val.value.str)) { vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s's file name argument was not a valid string.\n", + vpi_printf("%s's file name argument is not a valid string.\n", name); fail = 1; if (mode) free(mode_string); } + /* + * Verify that the file name is composed of only printable + * characters. + */ + unsigned len = strlen(val.value.str); + for (idx = 0; idx < len; idx++) { + if (! isprint(val.value.str[idx])) { + char msg [64]; + snprintf(msg, 64, "WARNING: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s %s's file name argument contains non-" + "printable characters.\n", msg, name); + vpi_printf("%*s \"%s\"\n", strlen(msg), " ", val.value.str); + fail = 1; + if (mode) free(mode_string); + } + } + /* If either the mode or file name are not valid just return. */ if (fail) return 0; @@ -168,11 +233,29 @@ static PLI_INT32 sys_fopenrwa_calltf(PLI_BYTE8*name) if (val.format != vpiStringVal || !*(val.value.str)) { vpi_printf("WARNING: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s's file name argument was not a valid string.\n", + vpi_printf("%s's file name argument is not a valid string.\n", name); return 0; } + /* + * Verify that the file name is composed of only printable + * characters. + */ + unsigned idx, len = strlen(val.value.str); + for (idx = 0; idx < len; idx++) { + if (! isprint(val.value.str[idx])) { + char msg [64]; + snprintf(msg, 64, "WARNING: %s line %d:", + vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s %s's file name argument contains non-" + "printable characters.\n", msg, name); + vpi_printf("%*s \"%s\"\n", strlen(msg), " ", val.value.str); + return 0; + } + } + /* Open the file and return the result. */ val.format = vpiIntVal; val.value.integer = vpi_fopen(val.value.str, mode); @@ -616,19 +699,19 @@ static PLI_INT32 sys_common_fd_calltf(PLI_BYTE8*name) val.format = vpiIntVal; switch (name[4]) { - case 'l': /* $ftell() */ + case 'l': /* $ftell() */ val.value.integer = ftell(fp); break; - case 'f': /* $feof() is from 1264-2005*/ + case 'f': /* $feof() is from 1264-2005*/ val.value.integer = feof(fp); break; - case 'i': /* $rewind() */ + case 'i': /* $rewind() */ val.value.integer = fseek(fp, 0L, SEEK_SET); break; - case 't': /* $fgetc() */ + case 't': /* $fgetc() */ val.value.integer = fgetc(fp); break; - default: + default: vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s cannot be processed with this routine.\n", name); diff --git a/vvp/arith.cc b/vvp/arith.cc index c7b827bbc..d263981aa 100644 --- a/vvp/arith.cc +++ b/vvp/arith.cc @@ -90,6 +90,22 @@ void vvp_arith_abs::recv_real(vvp_net_ptr_t ptr, double bit) vvp_send_real(ptr.ptr()->out, out); } +vvp_arith_cast_real::vvp_arith_cast_real(bool signed_flag) +: signed_(signed_flag) +{ +} + +vvp_arith_cast_real::~vvp_arith_cast_real() +{ +} + +void vvp_arith_cast_real::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) +{ + double val; + vector4_to_value(bit, val, signed_); + vvp_send_real(ptr.ptr()->out, val); +} + // Division vvp_arith_div::vvp_arith_div(unsigned wid, bool signed_flag) diff --git a/vvp/arith.h b/vvp/arith.h index 41ccf5371..31a479dd3 100644 --- a/vvp/arith.h +++ b/vvp/arith.h @@ -60,6 +60,17 @@ class vvp_arith_abs : public vvp_net_fun_t { private: }; +class vvp_arith_cast_real : public vvp_net_fun_t { + public: + explicit vvp_arith_cast_real(bool signed_flag); + ~vvp_arith_cast_real(); + + void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit); + + private: + bool signed_; +}; + class vvp_arith_div : public vvp_arith_ { public: diff --git a/vvp/array.cc b/vvp/array.cc index 75bb22f44..29de4f15d 100644 --- a/vvp/array.cc +++ b/vvp/array.cc @@ -56,6 +56,8 @@ vvp_array_t array_find(const char*label) * represent the words of the array. The vpi_array_t is a pointer to this. */ struct __vpiArray { + __vpiArray() { } + struct __vpiHandle base; struct __vpiScope*scope; const char*name; /* Permanently allocated string */ @@ -64,11 +66,11 @@ struct __vpiArray { struct __vpiDecConst last_addr; struct __vpiDecConst msb; struct __vpiDecConst lsb; + unsigned vals_width; // 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. - vvp_vector4_t *vals; - unsigned vals_width; + vvp_vector4array_t *vals; struct __vpiArrayWord*vals_words; class vvp_fun_arrayport*ports_; @@ -411,7 +413,7 @@ static int vpi_array_var_word_get(int code, vpiHandle ref) switch (code) { case vpiSize: - return (int) parent->vals_width; + return (int) parent->vals->width(); case vpiLeftRange: return parent->msb.value; @@ -448,14 +450,9 @@ static void vpi_array_var_word_get_value(vpiHandle ref, p_vpi_value value) assert(obj); unsigned index = decode_array_word_pointer(obj, parent); - unsigned width = parent->vals_width; + 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); - } + vpip_vec4_get_value(parent->vals->get_word(index), width, false, value); } static vpiHandle vpi_array_var_word_put_value(vpiHandle ref, p_vpi_value vp, int flags) @@ -696,18 +693,18 @@ void array_set_word(vvp_array_t arr, if (arr->vals) { assert(arr->nets == 0); if (part_off != 0 || val.size() != arr->vals_width) { - if (arr->vals[address].size() == 0) - arr->vals[address] = vvp_vector4_t(arr->vals_width, BIT4_X); - if ((part_off + val.size()) > arr->vals[address].size()) { + vvp_vector4_t tmp = arr->vals->get_word(address); + if ((part_off + val.size()) > tmp.size()) { cerr << "part_off=" << part_off << " val.size()=" << val.size() - << " arr->vals[address].size()=" << arr->vals[address].size() + << " arr->vals[address].size()=" << tmp.size() << " arr->vals_width=" << arr->vals_width << endl; assert(0); } - arr->vals[address].set_vec(part_off, val); + tmp.set_vec(part_off, val); + arr->vals->set_word(address, tmp); } else { - arr->vals[address] = val; + arr->vals->set_word(address, val); } array_word_change(arr, address); return; @@ -730,14 +727,7 @@ vvp_vector4_t array_get_word(vvp_array_t arr, unsigned address) if (arr->vals) { assert(arr->nets == 0); - vvp_vector4_t tmp; - if (address < arr->array_count) - tmp = arr->vals[address]; - - if (tmp.size() == 0) - tmp = vvp_vector4_t(arr->vals_width, BIT4_X); - - return tmp; + return arr->vals->get_word(address); } assert(arr->vals == 0); @@ -848,8 +838,8 @@ void compile_var_array(char*label, char*name, int last, int first, struct __vpiArray*arr = ARRAY_HANDLE(obj); /* Make the words. */ - arr->vals = new vvp_vector4_t[arr->array_count]; arr->vals_width = labs(msb-lsb) + 1; + arr->vals = new vvp_vector4array_t(arr->vals_width, arr->array_count); vpip_make_dec_const(&arr->msb, msb); vpip_make_dec_const(&arr->lsb, lsb); @@ -989,7 +979,8 @@ void array_word_change(vvp_array_t array, unsigned long addr) if (cur->cb_data.cb_rtn != 0) { if (cur->cb_data.value) - vpip_vec4_get_value(array->vals[addr], array->vals_width, + vpip_vec4_get_value(array->vals->get_word(addr), + array->vals_width, false, cur->cb_data.value); callback_execute(cur); diff --git a/vvp/compile.cc b/vvp/compile.cc index 18174c937..9cdc5d514 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -889,6 +889,21 @@ template void make_arith(T_ *arith, char*label, free(argv); } +void compile_arith_cast_real(char*label, unsigned argc, struct symb_s*argv) +{ + vvp_arith_cast_real*arith = new vvp_arith_cast_real(false); + + vvp_net_t* ptr = new vvp_net_t; + ptr->fun = arith; + + define_functor_symbol(label, ptr); + free(label); + + assert(argc == 1); + inputs_connect(ptr, argc, argv); + free(argv); +} + void compile_arith_abs(char*label, unsigned argc, struct symb_s*argv) { vvp_arith_abs*arith = new vvp_arith_abs; diff --git a/vvp/compile.h b/vvp/compile.h index 93daf0f30..0d57b3bf5 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -151,6 +151,8 @@ extern void compile_arith_pow(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); extern void compile_arith_abs(char*label, unsigned argc, struct symb_s*argv); +extern void compile_arith_cast_real(char*label, + unsigned argc, struct symb_s*argv); extern void compile_arith_div(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); extern void compile_arith_mod(char*label, long width, diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 0e21eb087..2f8b381c3 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -107,6 +107,7 @@ ".array/real" { return K_ARRAY_R; } ".array/s" { return K_ARRAY_S; } ".array/port" { return K_ARRAY_PORT; } +".cast/real" { return K_CAST_REAL; } ".cmp/eeq" { return K_CMP_EEQ; } ".cmp/eq" { return K_CMP_EQ; } ".cmp/eq.r" { return K_CMP_EQ_R; } diff --git a/vvp/parse.y b/vvp/parse.y index 6947f8996..c7678f0ae 100644 --- a/vvp/parse.y +++ b/vvp/parse.y @@ -71,6 +71,7 @@ static struct __vpiModPath*modpath_dst = 0; %token K_ARITH_MULT K_ARITH_MULT_R K_ARITH_SUB K_ARITH_SUB_R %token K_ARITH_SUM K_ARITH_SUM_R K_ARITH_POW K_ARITH_POW_R K_ARITH_POW_S %token K_ARRAY K_ARRAY_I K_ARRAY_R K_ARRAY_S K_ARRAY_PORT +%token K_CAST_REAL %token K_CMP_EEQ K_CMP_EQ K_CMP_EQ_R K_CMP_NEE K_CMP_NE K_CMP_NE_R %token K_CMP_GE K_CMP_GE_R K_CMP_GE_S K_CMP_GT K_CMP_GT_R K_CMP_GT_S %token K_CONCAT K_DEBUG K_DELAY K_DFF @@ -252,6 +253,11 @@ statement compile_arith_abs($1, obj.cnt, obj.vect); } + | T_LABEL K_CAST_REAL symbols ';' + { struct symbv_s obj = $3; + compile_arith_cast_real($1, obj.cnt, obj.vect); + } + /* Arithmetic statements generate functor arrays of a given width that take like size input vectors. */ diff --git a/vvp/schedule.cc b/vvp/schedule.cc index 1d4f497bd..c2546120d 100644 --- a/vvp/schedule.cc +++ b/vvp/schedule.cc @@ -118,7 +118,7 @@ void del_thr_event_s::run_run(void) struct assign_vector4_event_s : public event_s { /* The default constructor. */ - assign_vector4_event_s() { } + assign_vector4_event_s(const vvp_vector4_t&that) : val(that) { } /* A constructor that makes the val directly. */ assign_vector4_event_s(const vvp_vector4_t&that, unsigned adr, unsigned wid) : val(that,adr,wid) { } @@ -572,23 +572,10 @@ void schedule_assign_vector(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, vvp_time64_t delay) { - struct assign_vector4_event_s*cur = new struct assign_vector4_event_s; + struct assign_vector4_event_s*cur = new struct assign_vector4_event_s(bit); cur->ptr = ptr; cur->base = base; cur->vwid = vwid; - cur->val = bit; - schedule_event_(cur, delay, SEQ_NBASSIGN); -} - -void schedule_assign_vector(vvp_net_ptr_t ptr, - const vvp_vector4_t&bit, - vvp_time64_t delay) -{ - struct assign_vector4_event_s*cur = new struct assign_vector4_event_s; - cur->ptr = ptr; - cur->val = bit; - cur->vwid = 0; - cur->base = 0; schedule_event_(cur, delay, SEQ_NBASSIGN); } @@ -619,11 +606,10 @@ void schedule_assign_array_word(vvp_array_t mem, schedule_event_(cur, delay, SEQ_NBASSIGN); } -void schedule_set_vector(vvp_net_ptr_t ptr, vvp_vector4_t bit) +void schedule_set_vector(vvp_net_ptr_t ptr, const vvp_vector4_t&bit) { - struct assign_vector4_event_s*cur = new struct assign_vector4_event_s; + struct assign_vector4_event_s*cur = new struct assign_vector4_event_s(bit); cur->ptr = ptr; - cur->val = bit; cur->base = 0; cur->vwid = 0; schedule_event_(cur, 0, SEQ_ACTIVE); @@ -647,9 +633,8 @@ void schedule_set_vector(vvp_net_ptr_t ptr, double bit) void schedule_init_vector(vvp_net_ptr_t ptr, vvp_vector4_t bit) { - struct assign_vector4_event_s*cur = new struct assign_vector4_event_s; + struct assign_vector4_event_s*cur = new struct assign_vector4_event_s(bit); cur->ptr = ptr; - cur->val = bit; cur->base = 0; cur->vwid = 0; cur->next = schedule_init_list; diff --git a/vvp/schedule.h b/vvp/schedule.h index d40813709..b0f7bea23 100644 --- a/vvp/schedule.h +++ b/vvp/schedule.h @@ -47,9 +47,6 @@ extern void schedule_assign_vector(vvp_net_ptr_t ptr, const vvp_vector4_t&val, vvp_time64_t delay); -extern void schedule_assign_vector(vvp_net_ptr_t ptr, - const vvp_vector4_t&val, - vvp_time64_t delay); extern void schedule_assign_plucked_vector(vvp_net_ptr_t ptr, vvp_time64_t delay, const vvp_vector4_t&val, @@ -66,7 +63,7 @@ extern void schedule_assign_array_word(vvp_array_t mem, * constant value (i.e. C4<...>) to the input of a functor. This * creates an event in the active queue. */ -extern void schedule_set_vector(vvp_net_ptr_t ptr, vvp_vector4_t val); +extern void schedule_set_vector(vvp_net_ptr_t ptr, const vvp_vector4_t&val); extern void schedule_set_vector(vvp_net_ptr_t ptr, vvp_vector8_t val); extern void schedule_set_vector(vvp_net_ptr_t ptr, double val); diff --git a/vvp/vpip_hex.cc b/vvp/vpip_hex.cc index 3ef3056cd..9c6a6a2e4 100644 --- a/vvp/vpip_hex.cc +++ b/vvp/vpip_hex.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-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,9 +16,6 @@ * 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: vpip_hex.cc,v 1.4 2006/02/21 02:39:27 steve Exp $" -#endif # include "config.h" # include "vpi_priv.h" @@ -140,21 +137,24 @@ void vpip_vec4_to_hex_str(const vvp_vector4_t&bits, char*buf, } } - if (slen > 0) { - unsigned padd = 0; + /* Fill in X or Z if they are the only thing in the value. */ + switch (bits.size() % 4) { + case 1: + if (val == 2) val = 170; + else if (val == 3) val = 255; + break; + case 2: + if (val == 10) val = 170; + else if (val == 15) val = 255; + break; + case 3: + if (val == 42) val = 170; + else if (val == 63) val = 255; + break; + } + if (slen > 0) { slen -= 1; buf[slen] = hex_digits[val]; - switch(buf[slen]) { - case 'X': padd = 2; break; - case 'Z': padd = 3; break; - } - if (padd) { - for (unsigned idx = bits.size() % 4; idx < 4; idx += 1) { - val = val | padd << 2*idx; - } - buf[slen] = hex_digits[val]; - } } } - diff --git a/vvp/vpip_oct.cc b/vvp/vpip_oct.cc index e8a1ac057..f60e87b24 100644 --- a/vvp/vpip_oct.cc +++ b/vvp/vpip_oct.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-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,9 +16,6 @@ * 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: vpip_oct.cc,v 1.4 2006/02/21 02:39:27 steve Exp $" -#endif # include "config.h" # include "vpi_priv.h" @@ -120,9 +117,20 @@ void vpip_vec4_to_oct_str(const vvp_vector4_t&bits, char*buf, unsigned nbuf, } } + /* Fill in X or Z if they are the only thing in the value. */ + switch (bits.size() % 3) { + case 1: + if (val == 2) val = 42; + else if (val == 3) val = 63; + break; + case 2: + if (val == 10) val = 42; + else if (val == 15) val = 63; + break; + } + if (slen > 0) { slen -= 1; buf[slen] = oct_digits[val]; } } - diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 7f38809f0..f8a6d5b49 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -707,7 +707,7 @@ bool of_ASSIGN_V0(vthread_t thr, vvp_code_t cp) schedule_assign_plucked_vector(ptr, delay, thr->bits4, bit, wid); } else { vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid); - schedule_assign_vector(ptr, value, delay); + schedule_assign_plucked_vector(ptr, delay, value, 0, wid); } return true; @@ -732,7 +732,7 @@ bool of_ASSIGN_V0D(vthread_t thr, vvp_code_t cp) schedule_assign_plucked_vector(ptr, delay, thr->bits4, bit, wid); } else { vvp_vector4_t value = vthread_bits_to_vector(thr, bit, wid); - schedule_assign_vector(ptr, value, delay); + schedule_assign_plucked_vector(ptr, delay, value, 0, wid); } return true; diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index 01393d105..bad2894e7 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -1177,6 +1177,94 @@ bool vector4_to_value(const vvp_vector4_t&vec, double&val, bool signed_flag) return flag; } +vvp_vector4array_t::vvp_vector4array_t(unsigned width, unsigned words) +: width_(width), words_(words) +{ + array_ = new v4cell[words_]; + + if (width_ <= vvp_vector4_t::BITS_PER_WORD) { + for (unsigned idx = 0 ; idx < words_ ; idx += 1) { + array_[idx].abits_val_ = vvp_vector4_t::WORD_X_ABITS; + array_[idx].bbits_val_ = vvp_vector4_t::WORD_X_BBITS; + } + } else { + for (unsigned idx = 0 ; idx < words_ ; idx += 1) { + array_[idx].abits_ptr_ = 0; + array_[idx].bbits_ptr_ = 0; + } + } +} + +vvp_vector4array_t::~vvp_vector4array_t() +{ + if (array_) { + if (width_ > vvp_vector4_t::BITS_PER_WORD) { + for (unsigned idx = 0 ; idx < words_ ; idx += 1) + if (array_[idx].abits_ptr_) + delete[]array_[idx].abits_ptr_; + } + delete[]array_; + } +} + +void vvp_vector4array_t::set_word(unsigned index, const vvp_vector4_t&that) +{ + assert(index < words_); + assert(that.size_ == width_); + + v4cell&cell = array_[index]; + + if (width_ <= vvp_vector4_t::BITS_PER_WORD) { + cell.abits_val_ = that.abits_val_; + cell.bbits_val_ = that.bbits_val_; + return; + } + + unsigned cnt = (width_ + vvp_vector4_t::BITS_PER_WORD-1)/vvp_vector4_t::BITS_PER_WORD; + + if (cell.abits_ptr_ == 0) { + cell.abits_ptr_ = new unsigned long[2*cnt]; + cell.bbits_ptr_ = cell.abits_ptr_ + cnt; + } + + for (unsigned idx = 0 ; idx < cnt ; idx += 1) + cell.abits_ptr_[idx] = that.abits_ptr_[idx]; + for (unsigned idx = 0 ; idx < cnt ; idx += 1) + cell.bbits_ptr_[idx] = that.bbits_ptr_[idx]; +} + +vvp_vector4_t vvp_vector4array_t::get_word(unsigned index) const +{ + if (index >= words_) + return vvp_vector4_t(width_, BIT4_X); + + assert(index < words_); + + v4cell&cell = array_[index]; + + if (width_ <= vvp_vector4_t::BITS_PER_WORD) { + vvp_vector4_t res; + res.size_ = width_; + res.abits_val_ = cell.abits_val_; + res.bbits_val_ = cell.bbits_val_; + return res; + } + + vvp_vector4_t res (width_, BIT4_X); + if (cell.abits_ptr_ == 0) + return res; + + unsigned cnt = (width_ + vvp_vector4_t::BITS_PER_WORD-1)/vvp_vector4_t::BITS_PER_WORD; + + for (unsigned idx = 0 ; idx < cnt ; idx += 1) + res.abits_ptr_[idx] = cell.abits_ptr_[idx]; + for (unsigned idx = 0 ; idx < cnt ; idx += 1) + res.bbits_ptr_[idx] = cell.bbits_ptr_[idx]; + + return res; + +} + template T coerce_to_width(const T&that, unsigned width) { if (that.size() == width) @@ -2621,7 +2709,7 @@ void vvp_wide_fun_core::propagate_vec4(const vvp_vector4_t&bit, vvp_time64_t delay) { if (delay) - schedule_assign_vector(ptr_->out, bit, delay); + schedule_assign_plucked_vector(ptr_->out, delay, bit, 0, bit.size()); else vvp_send_vec4(ptr_->out, bit); } @@ -2774,16 +2862,13 @@ ostream& operator <<(ostream&out, vvp_scalar_t a) return out; } -vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b) +/* + * This function is only called if the actual interface function rules + * out some of the eazy cases. If we get here, we can assume that + * neither of the values is HiZ, and the values are not exactly equal. + */ +vvp_scalar_t fully_featured_resolv_(vvp_scalar_t a, vvp_scalar_t b) { - // If the value is 0, that is the same as HiZ. In that case, - // resolution is simply a matter of returning the *other* value. - if (a.value_ == 0) - return b; - if (b.value_ == 0) - return a; - - vvp_scalar_t res = a; if (UNAMBIG(a.value_) && UNAMBIG(b.value_)) { @@ -2792,108 +2877,112 @@ vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b) but different values, then this becomes ambiguous. */ - if (a.value_ == b.value_) { + if ((b.value_&0x07) > (a.value_&0x07)) { - /* values are equal. do nothing. */ + /* b value is stronger. Take it. */ + return b; - } else if ((b.value_&0x07) > (res.value_&0x07)) { + } else if ((b.value_&0x77) == (a.value_&0x77)) { - /* New value is stronger. Take it. */ - res.value_ = b.value_; - - } else if ((b.value_&0x77) == (res.value_&0x77)) { - - /* Strengths are the same. Make value ambiguous. */ - res.value_ = (res.value_&0x70) | (b.value_&0x07) | 0x80; + // Strengths are the same. Since we know already + // that the values are not the same, Make value + // into "x". + vvp_scalar_t tmp (a); + tmp.value_ = (tmp.value_&0x77) | 0x80; + return tmp; } else { - /* Must be res is the stronger one. */ + /* Must be "a" is the stronger one. */ + return a; } - } else if (UNAMBIG(res.value_)) { - unsigned tmp = 0; + } - if ((res.value_&0x70) > (b.value_&0x70)) - tmp |= res.value_&0xf0; + /* If one of the signals is unambiguous, then it + will sweep up the weaker parts of the ambiguous + signal. The result may be ambiguous, or maybe not. */ + + if (UNAMBIG(a.value_)) { + vvp_scalar_t res; + + if ((a.value_&0x70) > (b.value_&0x70)) + res.value_ |= a.value_&0xf0; else - tmp |= b.value_&0xf0; + res.value_ |= b.value_&0xf0; - if ((res.value_&0x07) > (b.value_&0x07)) - tmp |= res.value_&0x0f; + if ((a.value_&0x07) > (b.value_&0x07)) + res.value_ |= a.value_&0x0f; else - tmp |= b.value_&0x0f; + res.value_ |= b.value_&0x0f; - res.value_ = tmp; + return res; } else if (UNAMBIG(b.value_)) { - /* If one of the signals is unambiguous, then it - will sweep up the weaker parts of the ambiguous - signal. The result may be ambiguous, or maybe not. */ + vvp_scalar_t res; - unsigned tmp = 0; - - if ((b.value_&0x70) > (res.value_&0x70)) - tmp |= b.value_&0xf0; + if ((b.value_&0x70) > (a.value_&0x70)) + res.value_ |= b.value_&0xf0; else - tmp |= res.value_&0xf0; + res.value_ |= a.value_&0xf0; - if ((b.value_&0x07) > (res.value_&0x07)) - tmp |= b.value_&0x0f; + if ((b.value_&0x07) > (a.value_&0x07)) + res.value_ |= b.value_&0x0f; else - tmp |= res.value_&0x0f; + res.value_ |= a.value_&0x0f; - res.value_ = tmp; + return res; - } else { - - /* If both signals are ambiguous, then the result - has an even wider ambiguity. */ - - unsigned tmp = 0; - int sv1a = a.value_&0x80 ? STREN1(a.value_) : - STREN1(a.value_); - int sv0a = a.value_&0x08 ? STREN0(a.value_) : - STREN0(a.value_); - int sv1b = b.value_&0x80 ? STREN1(b.value_) : - STREN1(b.value_); - int sv0b = b.value_&0x08 ? STREN0(b.value_) : - STREN0(b.value_); - - int sv1 = sv1a; - int sv0 = sv0a; - - if (sv0a > sv1) - sv1 = sv0a; - if (sv1b > sv1) - sv1 = sv1b; - if (sv0b > sv1) - sv1 = sv0b; - - if (sv1a < sv0) - sv0 = sv1a; - if (sv1b < sv0) - sv0 = sv1b; - if (sv0b < sv0) - sv0 = sv0b; - - if (sv1 > 0) { - tmp |= 0x80; - tmp |= sv1 << 4; - } else { - /* Set the MSB when both arguments MSBs are set. This - can only happen if both one strengths are zero. */ - tmp |= (a.value_&b.value_)&0x80; - tmp |= (-sv1) << 4; - } - - if (sv0 > 0) { - tmp |= 0x08; - tmp |= sv0; - } else { - tmp |= (-sv0); - } - - res.value_ = tmp; } + + /* If both signals are ambiguous, then the result + has an even wider ambiguity. */ + + unsigned tmp = 0; + int sv1a = a.value_&0x80 ? STREN1(a.value_) : - STREN1(a.value_); + int sv0a = a.value_&0x08 ? STREN0(a.value_) : - STREN0(a.value_); + int sv1b = b.value_&0x80 ? STREN1(b.value_) : - STREN1(b.value_); + int sv0b = b.value_&0x08 ? STREN0(b.value_) : - STREN0(b.value_); + + int sv1 = sv1a; + int sv0 = sv0a; + + if (sv0a > sv1) + sv1 = sv0a; + if (sv1b > sv1) + sv1 = sv1b; + if (sv0b > sv1) + sv1 = sv0b; + + if (sv1a < sv0) + sv0 = sv1a; + if (sv1b < sv0) + sv0 = sv1b; + if (sv0b < sv0) + sv0 = sv0b; + + if (sv1 > 0) { + tmp |= 0x80; + tmp |= sv1 << 4; + } else { + /* Set the MSB when both arguments MSBs are set. This + can only happen if both one strengths are zero. */ + tmp |= (a.value_&b.value_)&0x80; + tmp |= (-sv1) << 4; + } + + if (sv0 > 0) { + tmp |= 0x08; + tmp |= sv0; + } else { + tmp |= (-sv0); + } + + vvp_scalar_t res; + res.value_ = tmp; + /* Canonicalize the HiZ value. */ if ((res.value_&0x77) == 0) res.value_ = 0; diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 928bfb3a9..c9a0fe70f 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -122,6 +122,7 @@ extern int edge(vvp_bit4_t from, vvp_bit4_t to); class vvp_vector4_t { friend vvp_vector4_t operator ~(const vvp_vector4_t&that); + friend class vvp_vector4array_t; public: explicit vvp_vector4_t(unsigned size =0, vvp_bit4_t bits =BIT4_X); @@ -383,6 +384,42 @@ extern bool vector4_to_value(const vvp_vector4_t&a, long&val, bool is_signed); extern bool vector4_to_value(const vvp_vector4_t&a, unsigned long&val); extern bool vector4_to_value(const vvp_vector4_t&a, double&val, bool is_signed); +/* + * vvp_vector4array_t + */ +class vvp_vector4array_t { + + public: + vvp_vector4array_t(unsigned width, unsigned words); + ~vvp_vector4array_t(); + + unsigned width() const { return width_; } + unsigned words() const { return words_; } + + vvp_vector4_t get_word(unsigned idx) const; + void set_word(unsigned idx, const vvp_vector4_t&that); + + private: + struct v4cell { + union { + unsigned long abits_val_; + unsigned long*abits_ptr_; + }; + union { + unsigned long bbits_val_; + unsigned long*bbits_ptr_; + }; + }; + + unsigned width_; + unsigned words_; + v4cell* array_; + + private: // Not implemented + vvp_vector4array_t(const vvp_vector4array_t&); + vvp_vector4array_t& operator = (const vvp_vector4array_t&); +}; + /* vvp_vector2_t */ class vvp_vector2_t { @@ -479,7 +516,7 @@ inline unsigned vvp_vector2_t::size() const */ class vvp_scalar_t { - friend vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b); + friend vvp_scalar_t fully_featured_resolv_(vvp_scalar_t a, vvp_scalar_t b); public: // Make a HiZ value. @@ -543,7 +580,24 @@ inline vvp_bit4_t vvp_scalar_t::value() const } -extern vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b); +inline vvp_scalar_t resolve(vvp_scalar_t a, vvp_scalar_t b) +{ + extern vvp_scalar_t fully_featured_resolv_(vvp_scalar_t a, vvp_scalar_t b); + + // If the value is HiZ, resolution is simply a matter of + // returning the *other* value. + if (a.is_hiz()) + return b; + if (b.is_hiz()) + return a; + // If the values are the identical, then resolution is simply + // returning *either* value. + if (a .eeq( b )) + return a; + + return fully_featured_resolv_(a,b); +} + extern ostream& operator<< (ostream&, vvp_scalar_t); /*