From 2bef6b86241d4aaf9eda20a643b68d2577776cca Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 30 Jun 2012 17:05:25 -0700 Subject: [PATCH] Detect and implement string.len() method, and string[index] expressions Implement the string.len() method in the system.vpi, and implement the string[index] method in vvp. --- PExpr.h | 5 ++ elab_expr.cc | 140 ++++++++++++++++++++++++++++++++---------- ivl_target.h | 8 +++ tgt-stub/expression.c | 47 ++++++++++---- tgt-vvp/eval_expr.c | 36 ++++++++++- vpi/Makefile.in | 3 +- vpi/sys_string.c | 90 +++++++++++++++++++++++++++ vpi/sys_table.c | 2 + vpi/system.sft | 2 + vvp/codes.h | 1 + vvp/compile.cc | 1 + vvp/opcodes.txt | 10 +++ vvp/vpi_priv.h | 1 + vvp/vpi_string.cc | 16 +++++ vvp/vthread.cc | 34 ++++++++++ 15 files changed, 350 insertions(+), 46 deletions(-) create mode 100644 vpi/sys_string.c diff --git a/PExpr.h b/PExpr.h index 98a16e14a..ec1e67310 100644 --- a/PExpr.h +++ b/PExpr.h @@ -717,8 +717,13 @@ class PECallFunction : public PExpr { bool check_call_matches_definition_(Design*des, NetScope*dscope) const; + NetExpr* cast_to_width_(NetExpr*expr, unsigned wid) const; + NetExpr*elaborate_expr_enum_method_(Design*des, NetScope*scope, + unsigned expr_wid) const; + NetExpr*elaborate_expr_string_method_(Design*des, NetScope*scope) const; + NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, unsigned expr_wid, unsigned flags) const; diff --git a/elab_expr.cc b/elab_expr.cc index 4f3389c7e..822fa8686 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1586,39 +1586,16 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, // this is system verilog, then test to see if the name is // really a method attached to an object. if (gn_system_verilog() && path_.size() >= 2) { - pform_name_t use_path = path_; - perm_string method_name = peek_tail_name(use_path); - use_path.pop_back(); + NetExpr*tmp = elaborate_expr_enum_method_(des, scope, expr_wid); + if (tmp) return tmp; + } - NetNet *net; - const NetExpr *par; - NetEvent *eve; - const NetExpr *ex1, *ex2; - - symbol_search(this, des, scope, use_path, - net, par, eve, ex1, ex2); - - // Check to see if we have a net and if so is it an - // enumeration? If so then check to see if this is an - // enumeration method call. - if (net != 0) { - if (netenum_t*netenum = net->enumeration()) { - // We may need the net expression for the - // enumeration variable so get it. - NetESignal*expr = new NetESignal(net); - expr->set_line(*this); - // This expression cannot be a select! - assert(use_path.back().index.empty()); - - PExpr*tmp = parms_.size() ? parms_[0] : 0; - return check_for_enum_methods(this, des, scope, - netenum, use_path, - method_name, expr, - expr_wid, tmp, - parms_.size()); - } - - } + // Maybe this is a method attached to a string variable? + // If this is System Verilog, then test to see if the + // name is really a method attached to a string object. + if (gn_system_verilog() && path_.size() >= 2) { + NetExpr*tmp = elaborate_expr_string_method_(des, scope); + if (tmp) return tmp; } // Nothing was found so report this as an error. @@ -1759,6 +1736,79 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, return 0; } +NetExpr* PECallFunction::elaborate_expr_enum_method_(Design*des, NetScope*scope, + unsigned expr_wid) const +{ + pform_name_t use_path = path_; + perm_string method_name = peek_tail_name(use_path); + use_path.pop_back(); + + NetNet *net; + const NetExpr *par; + NetEvent *eve; + const NetExpr *ex1, *ex2; + + symbol_search(this, des, scope, use_path, + net, par, eve, ex1, ex2); + + // Check to see if we have a net and if so is it an + // enumeration? If so then check to see if this is an + // enumeration method call. + if (net != 0) { + if (netenum_t*netenum = net->enumeration()) { + // We may need the net expression for the + // enumeration variable so get it. + NetESignal*expr = new NetESignal(net); + expr->set_line(*this); + // This expression cannot be a select! + assert(use_path.back().index.empty()); + + PExpr*tmp = parms_.size() ? parms_[0] : 0; + return check_for_enum_methods(this, des, scope, + netenum, use_path, + method_name, expr, + expr_wid, tmp, + parms_.size()); + } + + } + + return 0; +} + +NetExpr* PECallFunction::elaborate_expr_string_method_(Design*des, NetScope*scope) const +{ + pform_name_t use_path = path_; + perm_string method_name = peek_tail_name(use_path); + use_path.pop_back(); + + NetNet *net; + const NetExpr *par; + NetEvent *eve; + const NetExpr *ex1, *ex2; + + symbol_search(this, des, scope, use_path, + net, par, eve, ex1, ex2); + + // Check to see if we have a net and if so is it an + // enumeration? If so then check to see if this is an + // enumeration method call. + if (net == 0) + return 0; + + if (net->data_type() != IVL_VT_STRING) + return 0; + + if (method_name == "len") { + NetESFunc*sys_expr = new NetESFunc("$ivl_string_method$len", + IVL_VT_BOOL, 32, 1); + sys_expr->parm(0, new NetESignal(net)); + return sys_expr; + } + + return 0; +} + unsigned PECastSize::test_width(Design*des, NetScope*scope, width_mode_t&) { expr_width_ = size_; @@ -3637,6 +3687,32 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, return res; } + if (net->sig()->data_type()==IVL_VT_STRING && (msv < 0)) { + // Special case: This is a constant bit select of + // a string, and the index is < 0. For example: + // string foo; + // ... foo[-1] ... + // This is known to be 8'h00. + NetEConst*tmp = make_const_0(8); + tmp->set_line(*this); + delete mux; + return tmp; + } + + if (net->sig()->data_type()==IVL_VT_STRING) { + // Special case: This is a select of a string + // variable. Generate a NetESelect and attach it + // to the NetESignal. This should be interpreted + // as a character select downstream. + if (debug_elaborate) { + cerr << get_fileline() << ": debug: " + << "Bit select of string becomes NetESelect." << endl; + } + NetESelect*res = new NetESelect(net, mux, 8); + res->set_line(*net); + return res; + } + long idx = net->sig()->sb_to_idx(prefix_indices,msv); if (idx >= (long)net->vector_width() || idx < 0) { diff --git a/ivl_target.h b/ivl_target.h index 0639ca7e5..f025837b5 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -813,6 +813,14 @@ extern unsigned ivl_event_lineno(ivl_event_t net); * conversion from signal units to vector units, so the result of * ivl_expr_oper1 should range from 0 to ivl_expr_width(). * + * This exprsesion is also used to implement string substrings. If the + * sub-expression (oper1) is IVL_VT_STRING, then the base expression + * (oper2) is a charaster address, with 0 the first address of the + * string, 1 the second, and so on. This is OPPOSITE how a part select + * of a string cast to a vector works, to be aware. The size of the + * expression is an even multiple of 8, and is 8 times the number of + * characters to pick. + * * - IVL_EX_SIGNAL * This expression references a signal vector. The ivl_expr_signal * function gets a handle for the signal that is referenced. The diff --git a/tgt-stub/expression.c b/tgt-stub/expression.c index 79420225b..93bd3099c 100644 --- a/tgt-stub/expression.c +++ b/tgt-stub/expression.c @@ -165,6 +165,40 @@ static void show_memory_expression(ivl_expr_t net, unsigned ind) width); } +static void show_select_expression(ivl_expr_t net, unsigned ind) +{ + unsigned width = ivl_expr_width(net); + const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; + ivl_expr_t oper1 = ivl_expr_oper1(net); + ivl_expr_t oper2 = ivl_expr_oper2(net); + + if (ivl_expr_value(oper1) == IVL_VT_STRING) { + /* If the sub-expression is a STRING, then this is a + substring and the code generator will handle it + differently. */ + fprintf(out, "%*s\n", ind, "", width); + show_expression(oper1, ind+3); + show_expression(oper2, ind+3); + + } else if (oper2) { + /* If oper2 is present, then it is the base of a part + select. The width of the expression defines the range + of the part select. */ + fprintf(out, "%*s\n", ind, "", + width, sign); + show_expression(oper1, ind+3); + show_expression(oper2, ind+3); + + } else { + /* There is no base expression so this is a pad + operation. The sub-expression is padded (signed or + unsigned as appropriate) to the expression width. */ + fprintf(out, "%*s\n", ind, "", + width, sign); + show_expression(oper1, ind+3); + } +} + static void show_signal_expression(ivl_expr_t net, unsigned ind) { unsigned width = ivl_expr_width(net); @@ -312,18 +346,7 @@ void show_expression(ivl_expr_t net, unsigned ind) } case IVL_EX_SELECT: - /* The SELECT expression can be used to express part - select, or if the base is null vector extension. */ - if (ivl_expr_oper2(net)) { - fprintf(out, "%*s\n", ind, "", - width, sign); - show_expression(ivl_expr_oper1(net), ind+3); - show_expression(ivl_expr_oper2(net), ind+3); - } else { - fprintf(out, "%*s\n", ind, "", - width, sign); - show_expression(ivl_expr_oper1(net), ind+3); - } + show_select_expression(net, ind); break; case IVL_EX_STRING: diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 7f3f12082..4aa95ca2d 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -2762,6 +2762,25 @@ static struct vector_info draw_select_unsized_literal(ivl_expr_t expr, return res; } +static void draw_select_string_dest(ivl_expr_t expr, struct vector_info res) +{ + ivl_expr_t sube = ivl_expr_oper1(expr); + ivl_expr_t shift = ivl_expr_oper2(expr); + int shift_i; + + draw_eval_string(sube); + + shift_i = allocate_word(); + draw_eval_expr_into_integer(shift, shift_i); + + assert(res.wid % 8 == 0); + + clr_word(shift_i); + + fprintf(vvp_out, " %%substr/v %u, %d, %u;\n", res.base, shift_i, res.wid); + fprintf(vvp_out, " %%pop/str 1;\n"); +} + static struct vector_info draw_select_expr(ivl_expr_t expr, unsigned wid, int stuff_ok_flag) { @@ -2784,6 +2803,15 @@ static struct vector_info draw_select_expr(ivl_expr_t expr, unsigned wid, return res; } + /* Special case: The sub expression is a string, so do a + string select then a part select from that. */ + if (ivl_expr_value(sube) == IVL_VT_STRING) { + res.base = allocate_vector(wid); + res.wid = wid; + draw_select_string_dest(expr, res); + return res; + } + if (ivl_expr_type(sube) == IVL_EX_SIGNAL) { res = draw_select_signal(expr, sube, shift, ivl_expr_width(expr), wid); @@ -2886,6 +2914,13 @@ static void draw_select_expr_dest(ivl_expr_t expr, struct vector_info dest, ivl_expr_t sube = ivl_expr_oper1(expr); ivl_expr_t shift= ivl_expr_oper2(expr); + /* Special case: The sub expression is a string, so do a + string select then a part select from that. */ + if (ivl_expr_value(sube) == IVL_VT_STRING) { + draw_select_string_dest(expr, dest); + return; + } + /* If the shift expression is not present, then this is really a pad expression, and that can be handled pretty easily. Evaluate the subexpression into the destination, @@ -3013,7 +3048,6 @@ static struct vector_info draw_sfunc_expr(ivl_expr_t expr, unsigned wid) unsigned parm_count = ivl_expr_parms(expr); struct vector_info res; - /* If the function has no parameters, then use this short-form to draw the statement. */ if (parm_count == 0) { diff --git a/vpi/Makefile.in b/vpi/Makefile.in index a0aa48993..e9734d7a1 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -55,7 +55,8 @@ LDFLAGS = @LDFLAGS@ O = sys_table.o sys_convert.o sys_deposit.o sys_display.o sys_fileio.o \ sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o sys_random.o \ sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \ - sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o sys_priv.o \ + sys_string.o sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o \ + sys_priv.o \ sdf_lexor.o sdf_parse.o stringheap.o vams_simparam.o \ table_mod.o table_mod_lexor.o table_mod_parse.o OPP = vcd_priv2.o diff --git a/vpi/sys_string.c b/vpi/sys_string.c new file mode 100644 index 000000000..faff55cc9 --- /dev/null +++ b/vpi/sys_string.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2012 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "sys_priv.h" +# include + +static PLI_INT32 one_string_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv; + vpiHandle arg; + + argv = vpi_iterate(vpiArgument, callh); + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a string argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + arg = vpi_scan(argv); + if (arg == 0) return 0; + + arg = vpi_scan(argv); + if (arg != 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s has too many arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + return 0; +} + +static PLI_INT32 len_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv; + vpiHandle arg; + + argv = vpi_iterate(vpiArgument, callh); + assert(argv); + arg = vpi_scan(argv); + assert(arg); + vpi_free_object(argv); + + int res = vpi_get(vpiSize, arg); + + s_vpi_value value; + value.format = vpiIntVal; + value.value.integer = res; + + vpi_put_value(callh, &value, 0, vpiNoDelay); + + return 0; +} + +void sys_string_register(void) +{ + s_vpi_systf_data tf_data; + vpiHandle res; + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.tfname = "$ivl_string_method$len"; + tf_data.calltf = len_calltf; + tf_data.compiletf = one_string_arg_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$ivl_string_method$len"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); +} diff --git a/vpi/sys_table.c b/vpi/sys_table.c index 8a7dabffe..50bafe553 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -33,6 +33,7 @@ extern void sys_queue_register(); extern void sys_random_register(); extern void sys_random_mti_register(); extern void sys_readmem_register(); +extern void sys_string_register(); extern void sys_scanf_register(); extern void sys_sdf_register(); extern void sys_time_register(); @@ -205,6 +206,7 @@ void (*vlog_startup_routines[])() = { sys_random_mti_register, sys_readmem_register, sys_scanf_register, + sys_string_register, sys_time_register, sys_lxt_or_vcd_register, sys_sdf_register, diff --git a/vpi/system.sft b/vpi/system.sft index 0b43b1c65..2e65eade3 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -21,3 +21,5 @@ $abstime vpiSysFuncReal $simparam vpiSysFuncReal $simparam$str vpiSysFuncSized 1024 unsigned $table_model vpiSysFuncReal + +$ivl_string_method$len vpiSysFuncInt diff --git a/vvp/codes.h b/vvp/codes.h index 44643b464..c611ae28a 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -171,6 +171,7 @@ extern bool of_STORE_STR(vthread_t thr, vvp_code_t code); extern bool of_SUB(vthread_t thr, vvp_code_t code); extern bool of_SUB_WR(vthread_t thr, vvp_code_t code); extern bool of_SUBI(vthread_t thr, vvp_code_t code); +extern bool of_SUBSTR_V(vthread_t thr, vvp_code_t code); extern bool of_VPI_CALL(vthread_t thr, vvp_code_t code); extern bool of_WAIT(vthread_t thr, vvp_code_t code); extern bool of_XNOR(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index cf12c4aea..bb41277e4 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -218,6 +218,7 @@ static const struct opcode_table_s opcode_table[] = { { "%sub", of_SUB, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%sub/wr", of_SUB_WR, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, { "%subi", of_SUBI, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%substr/v",of_SUBSTR_V,3,{OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%wait", of_WAIT, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%xnor", of_XNOR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%xnor/r", of_XNORR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index 38c42b327..068f06d82 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -926,6 +926,16 @@ indexed value is subtracted from the left indexed value, and the result placed in the left index. + +* %substr/v , , + +This instruction extracts the substring of the top string in the string +stack and delivers the result to vector space. The , part is +the location where the result goes, and is the index register +that holds the index. This is the general method for getting string +values into the vector space. The string value is NOT popped. + + * %vpi_call [, ...] This instruction makes a call to a system task that was declared using diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 9bab0c85f..6b3ee0ef8 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -484,6 +484,7 @@ class __vpiStringVar : public __vpiHandle { __vpiStringVar(__vpiScope*scope, const char*name, vvp_net_t*net); int get_type_code(void) const; + int vpi_get(int code); void vpi_get_value(p_vpi_value val); inline vvp_net_t* get_net() const { return net_; } diff --git a/vvp/vpi_string.cc b/vvp/vpi_string.cc index 8de7dacfa..d86e64313 100644 --- a/vvp/vpi_string.cc +++ b/vvp/vpi_string.cc @@ -40,6 +40,22 @@ __vpiStringVar::__vpiStringVar(__vpiScope*sc, const char*na, vvp_net_t*ne) int __vpiStringVar::get_type_code(void) const { return vpiStringVar; } +int __vpiStringVar::vpi_get(int code) +{ + vvp_fun_signal_string*fun = dynamic_cast (net_->fun); + assert(fun); + string str = fun->get_string(); + + switch (code) { + case vpiSize: + // The vpiSize of a string variable is the number of + // bytes (characters) in that string. + return str.size(); + default: + return 0; + } +} + void __vpiStringVar::vpi_get_value(p_vpi_value val) { vvp_fun_signal_string*fun = dynamic_cast (net_->fun); diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 5eb5dc494..e4327d724 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -4711,6 +4711,40 @@ bool of_SUBI(vthread_t thr, vvp_code_t cp) return true; } +/* + * %substr/v , , + */ +bool of_SUBSTR_V(vthread_t thr, vvp_code_t cp) +{ + string&val = thr->stack_str.back(); + uint32_t bitl = cp->bit_idx[0]; + uint32_t sel = cp->bit_idx[1]; + unsigned wid = cp->number; + + thr_check_addr(thr, bitl+wid); + assert(bitl >= 4); + + int32_t use_sel = thr->words[sel].w_int; + + vvp_vector4_t tmp (8); + unsigned char_count = wid/8; + for (unsigned idx = 0 ; idx < char_count ; idx += 1) { + unsigned long byte; + if (use_sel < 0) + byte = 0x00; + else if ((size_t)use_sel >= val.size()) + byte = 0x00; + else + byte = val[use_sel]; + + thr->bits4.setarray(bitl, 8, &byte); + bitl += 8; + use_sel += 1; + } + + return true; +} + bool of_FILE_LINE(vthread_t, vvp_code_t cp) { if (show_file_line) {