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:
Stephen Williams 2012-06-30 17:05:25 -07:00
parent f77bdf7e38
commit 2bef6b8624
15 changed files with 350 additions and 46 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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:

View File

@ -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) {

View File

@ -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

90
vpi/sys_string.c Normal file
View File

@ -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);
}

View File

@ -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,

View File

@ -21,3 +21,5 @@ $abstime vpiSysFuncReal
$simparam vpiSysFuncReal
$simparam$str vpiSysFuncSized 1024 unsigned
$table_model vpiSysFuncReal
$ivl_string_method$len vpiSysFuncInt

View File

@ -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);

View File

@ -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} },

View File

@ -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

View File

@ -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_; }

View File

@ -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);

View File

@ -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) {