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.
This commit is contained in:
parent
f77bdf7e38
commit
2bef6b8624
5
PExpr.h
5
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;
|
||||
|
|
|
|||
140
elab_expr.cc
140
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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<substring: width=%u/8>\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<select: width=%u, %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<expr pad: width=%u, %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<select: width=%u, %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<expr pad: width=%u, %s>\n", ind, "",
|
||||
width, sign);
|
||||
show_expression(ivl_expr_oper1(net), ind+3);
|
||||
}
|
||||
show_select_expression(net, ind);
|
||||
break;
|
||||
|
||||
case IVL_EX_STRING:
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <assert.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -21,3 +21,5 @@ $abstime vpiSysFuncReal
|
|||
$simparam vpiSysFuncReal
|
||||
$simparam$str vpiSysFuncSized 1024 unsigned
|
||||
$table_model vpiSysFuncReal
|
||||
|
||||
$ivl_string_method$len vpiSysFuncInt
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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} },
|
||||
|
|
|
|||
|
|
@ -926,6 +926,16 @@ indexed value is subtracted from the left indexed value, and the
|
|||
result placed in the left index.
|
||||
|
||||
|
||||
|
||||
* %substr/v <bit-l>, <sel>, <wid>
|
||||
|
||||
This instruction extracts the substring of the top string in the string
|
||||
stack and delivers the result to vector space. The <bit>,<wid> part is
|
||||
the location where the result goes, and <sel> 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 <name> [, ...]
|
||||
|
||||
This instruction makes a call to a system task that was declared using
|
||||
|
|
|
|||
|
|
@ -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_; }
|
||||
|
|
|
|||
|
|
@ -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<vvp_fun_signal_string*> (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<vvp_fun_signal_string*> (net_->fun);
|
||||
|
|
|
|||
|
|
@ -4711,6 +4711,40 @@ bool of_SUBI(vthread_t thr, vvp_code_t cp)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* %substr/v <bitl>, <index>, <wid>
|
||||
*/
|
||||
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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue