From f77bdf7e3887f314398bbf5787fcf42b78a257e1 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sun, 24 Jun 2012 15:33:40 -0700 Subject: [PATCH] Handle concatenation of SystemVerilog strings. --- design_dump.cc | 2 +- dup_expr.cc | 4 +-- elab_expr.cc | 29 ++++++++++++++++--- eval_tree.cc | 4 +-- expr_synth.cc | 10 +++---- net_expr.cc | 13 ++++++--- net_nex_input.cc | 2 +- netlist.h | 8 ++++-- t-dll-expr.cc | 2 +- tgt-vvp/draw_vpi.c | 34 +++++++++++++++++++++-- tgt-vvp/eval_string.c | 34 +++++++++++++++++++++++ vvp/codes.h | 3 ++ vvp/compile.cc | 9 ++++++ vvp/lexor.lex | 5 ++++ vvp/opcodes.txt | 35 +++++++++++++++++++++-- vvp/vpi_priv.h | 1 + vvp/vpi_vthr_vector.cc | 63 ++++++++++++++++++++++++++++++++++++++++++ vvp/vthread.cc | 45 ++++++++++++++++++++++++++++++ vvp/vthread.h | 7 +++++ 19 files changed, 283 insertions(+), 27 deletions(-) diff --git a/design_dump.cc b/design_dump.cc index dd2a4555e..bd9756ba8 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1422,7 +1422,7 @@ void NetEConcat::dump(ostream&o) const else o << "{"; - for (unsigned idx = 1 ; idx < parms_.count() ; idx += 1) { + for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) o << ", " << *parms_[idx]; else diff --git a/dup_expr.cc b/dup_expr.cc index c1258eba6..64c832891 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -110,10 +110,10 @@ NetEBShift* NetEBShift::dup_expr() const NetEConcat* NetEConcat::dup_expr() const { - NetEConcat*dup = new NetEConcat(parms_.count(), repeat_); + NetEConcat*dup = new NetEConcat(parms_.size(), repeat_, expr_type_); ivl_assert(*this, dup); dup->set_line(*this); - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) if (parms_[idx]) { NetExpr*tmp = parms_[idx]->dup_expr(); ivl_assert(*this, tmp); diff --git a/elab_expr.cc b/elab_expr.cc index 7d10bb0d8..4f3389c7e 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1782,15 +1782,36 @@ NetExpr* PECastSize::elaborate_expr(Design*des, NetScope*scope, unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) { expr_width_ = 0; + enum {NO, MAYBE, YES} expr_is_string = MAYBE; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { + // Add in the width of this sub-expression. expr_width_ += parms_[idx]->test_width(des, scope, width_modes_[idx]); + + // If we already know this is not a string, then move on. + if (expr_is_string == NO) + continue; + + // If this expression is a string, then the + // concatenation is a string until we find a reason to + // deny it. + if (parms_[idx]->expr_type()==IVL_VT_STRING) { + expr_is_string = YES; + continue; + } + + // If this is a string literal, then this may yet be a string. + if (dynamic_cast (parms_[idx])) + continue; + + // Failed to allow a string result. + expr_is_string = NO; } - expr_type_ = IVL_VT_LOGIC; + expr_type_ = (expr_is_string==YES)? IVL_VT_STRING : IVL_VT_LOGIC; signed_flag_ = false; - /* If there is a repeat expression, then evaluate the constant - value and set the repeat count. */ + // If there is a repeat expression, then evaluate the constant + // value and set the repeat count. if (repeat_ && (scope != tested_scope_)) { NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1, true); if (tmp == 0) return 0; @@ -1919,7 +1940,7 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, } /* Make the empty concat expression. */ - NetEConcat*concat = new NetEConcat(parm_cnt, repeat_count_); + NetEConcat*concat = new NetEConcat(parm_cnt, repeat_count_, expr_type_); concat->set_line(*this); /* Remove any zero width constants. */ diff --git a/eval_tree.cc b/eval_tree.cc index bb63ba09c..cb3edee8f 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1127,7 +1127,7 @@ NetEConst* NetEConcat::eval_tree() } unsigned gap = 0; - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { // Parameter not here? This is an error, but presumably // already caught and we are here just to catch more. @@ -1178,7 +1178,7 @@ NetEConst* NetEConcat::eval_tree() unsigned cur = 0; bool is_string_flag = true; - for (unsigned idx = parms_.count() ; idx > 0 ; idx -= 1) { + for (unsigned idx = parms_.size() ; idx > 0 ; idx -= 1) { NetEConst*expr = dynamic_cast(parms_[idx-1]); if (expr == 0) return 0; diff --git a/expr_synth.cc b/expr_synth.cc index e30940025..536e0cac3 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -702,11 +702,11 @@ NetNet* NetEBShift::synthesize(Design*des, NetScope*scope, NetExpr*root) NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root) { /* First, synthesize the operands. */ - unsigned num_parms = parms_.count(); - NetNet**tmp = new NetNet*[parms_.count()]; + unsigned num_parms = parms_.size(); + NetNet**tmp = new NetNet*[parms_.size()]; bool flag = true; ivl_variable_type_t data_type = IVL_VT_NO_TYPE; - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]->expr_width() == 0) { /* We need to synthesize a replication of zero. */ tmp[idx] = parms_[idx]->synthesize(des, scope, root); @@ -754,8 +754,8 @@ NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root) unsigned count_input_width = 0; unsigned cur_pin = 1; for (unsigned rpt = 0; rpt < repeat(); rpt += 1) { - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { - unsigned concat_item = parms_.count()-idx-1; + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { + unsigned concat_item = parms_.size()-idx-1; if (tmp[concat_item] == 0) continue; connect(concat->pin(cur_pin), tmp[concat_item]->pin(0)); cur_pin += 1; diff --git a/net_expr.cc b/net_expr.cc index b632693fa..4971bf037 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -184,18 +184,23 @@ bool NetEBShift::has_width() const return left_->has_width(); } -NetEConcat::NetEConcat(unsigned cnt, unsigned r) -: parms_(cnt), repeat_(r) +NetEConcat::NetEConcat(unsigned cnt, unsigned r, ivl_variable_type_t vt) +: parms_(cnt), repeat_(r), expr_type_(vt) { expr_width(0); } NetEConcat::~NetEConcat() { - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) delete parms_[idx]; } +ivl_variable_type_t NetEConcat::expr_type() const +{ + return expr_type_; +} + bool NetEConcat::has_width() const { return true; @@ -203,7 +208,7 @@ bool NetEConcat::has_width() const void NetEConcat::set(unsigned idx, NetExpr*e) { - assert(idx < parms_.count()); + assert(idx < parms_.size()); assert(parms_[idx] == 0); parms_[idx] = e; expr_width( expr_width() + repeat_ * e->expr_width() ); diff --git a/net_nex_input.cc b/net_nex_input.cc index a47b60302..f1ece6ec7 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -56,7 +56,7 @@ NexusSet* NetEConcat::nex_input(bool rem_out) { if (parms_[0] == NULL) return NULL; NexusSet*result = parms_[0]->nex_input(rem_out); - for (unsigned idx = 1 ; idx < parms_.count() ; idx += 1) { + for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx] == NULL) { delete result; return NULL; diff --git a/netlist.h b/netlist.h index 7ba64280d..f688a4427 100644 --- a/netlist.h +++ b/netlist.h @@ -3747,16 +3747,17 @@ class NetEBShift : public NetEBinary { class NetEConcat : public NetExpr { public: - NetEConcat(unsigned cnt, unsigned repeat =1); + NetEConcat(unsigned cnt, unsigned repeat, ivl_variable_type_t vt); ~NetEConcat(); // Manipulate the parameters. void set(unsigned idx, NetExpr*e); unsigned repeat() const { return repeat_; } - unsigned nparms() const { return parms_.count() ; } + unsigned nparms() const { return parms_.size() ; } NetExpr* parm(unsigned idx) const { return parms_[idx]; } + virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual bool has_width() const; virtual NetEConcat* dup_expr() const; @@ -3766,8 +3767,9 @@ class NetEConcat : public NetExpr { virtual void dump(ostream&) const; private: - svectorparms_; + std::vectorparms_; unsigned repeat_; + ivl_variable_type_t expr_type_; }; diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 0db2ffd62..13748ad08 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -200,7 +200,7 @@ void dll_target::expr_concat(const NetEConcat*net) assert(cur); cur->type_ = IVL_EX_CONCAT; - cur->value_ = IVL_VT_VECTOR; + cur->value_ = net->expr_type(); cur->width_ = net->expr_width(); cur->signed_ = net->has_sign() ? 1 : 0; cur->sized_ = 1; diff --git a/tgt-vvp/draw_vpi.c b/tgt-vvp/draw_vpi.c index ef4bf50c0..7fe568a4d 100644 --- a/tgt-vvp/draw_vpi.c +++ b/tgt-vvp/draw_vpi.c @@ -31,6 +31,9 @@ struct args_info { char*text; int vec_flag; /* True if the vec must be released. */ struct vector_info vec; + /* String Stack position if this argument is a calculated string. */ + int str_flag; + unsigned str_stack; struct args_info *child; /* Arguments can be nested. */ }; @@ -265,6 +268,11 @@ static void draw_vpi_taskfunc_args(const char*call_string, ivl_parameter_t par; + /* Keep track of how much string stack this function call is + going to need. We'll need this for making stack references, + and also to clean out the stack when done. */ + unsigned str_stack_need = 0; + /* Figure out how many expressions are going to be evaluated for this task call. I won't need to evaluate expressions for items that are VPI objects directly. */ @@ -376,7 +384,17 @@ static void draw_vpi_taskfunc_args(const char*call_string, "W<%u,r>", args[idx].vec.base); break; case IVL_VT_STRING: - /* STRING expressions not supported yet. */ + /* Eval the string into the stack, and tell VPI + about the stack position. */ + draw_eval_string(expr); + args[idx].vec_flag = 0; + args[idx].vec.base = 0; + args[idx].vec.wid = 0; + args[idx].str_flag = 1; + args[idx].str_stack = str_stack_need; + str_stack_need += 1; + buffer[0] = 0; + break; default: assert(0); } @@ -388,7 +406,16 @@ static void draw_vpi_taskfunc_args(const char*call_string, for (idx = 0 ; idx < parm_count ; idx += 1) { struct args_info*ptr; - fprintf(vvp_out, ", %s", args[idx].text); + if (args[idx].str_flag) { + /* If this is a string stack reference, then + calculate the stack depth and use that to + generate the completed string. */ + unsigned pos = str_stack_need - args[idx].str_stack - 1; + fprintf(vvp_out, ", S<%u,str>",pos); + } else { + fprintf(vvp_out, ", %s", args[idx].text); + } + free(args[idx].text); /* Clear the nested children vectors. */ for (ptr = &args[idx]; ptr != NULL; ptr = ptr->child) { @@ -409,6 +436,9 @@ static void draw_vpi_taskfunc_args(const char*call_string, free(args); fprintf(vvp_out, ";\n"); + + if (str_stack_need > 0) + fprintf(vvp_out, " %%pop/str %u;\n", str_stack_need); } void draw_vpi_task_call(ivl_statement_t tnet) diff --git a/tgt-vvp/eval_string.c b/tgt-vvp/eval_string.c index bdb1a82dc..bc4a477b9 100644 --- a/tgt-vvp/eval_string.c +++ b/tgt-vvp/eval_string.c @@ -29,6 +29,36 @@ static void fallback_eval(ivl_expr_t expr) clr_vector(res); } +static void string_ex_concat(ivl_expr_t expr) +{ + unsigned repeat; + + assert(ivl_expr_parms(expr) != 0); + assert(ivl_expr_repeat(expr) != 0); + + /* Push the first string onto the stack, no matter what. */ + draw_eval_string(ivl_expr_parm(expr,0)); + + for (repeat = 0 ; repeat < ivl_expr_repeat(expr) ; repeat += 1) { + unsigned idx; + for (idx = (repeat==0)? 1 : 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { + ivl_expr_t sub = ivl_expr_parm(expr,idx); + + /* Special case: If operand is a string literal, + then concat it using the %concati/str + instruction. */ + if (ivl_expr_type(sub) == IVL_EX_STRING) { + fprintf(vvp_out, " %%concati/str \"%s\";\n", + ivl_expr_string(sub)); + continue; + } + + draw_eval_string(sub); + fprintf(vvp_out, " %%concat/str;\n"); + } + } +} + static void string_ex_signal(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); @@ -53,6 +83,10 @@ void draw_eval_string(ivl_expr_t expr) string_ex_signal(expr); break; + case IVL_EX_CONCAT: + string_ex_concat(expr); + break; + default: fallback_eval(expr); break; diff --git a/vvp/codes.h b/vvp/codes.h index 5f44d3366..44643b464 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -75,6 +75,8 @@ extern bool of_CMPWS(vthread_t thr, vvp_code_t code); extern bool of_CMPWU(vthread_t thr, vvp_code_t code); extern bool of_CMPX(vthread_t thr, vvp_code_t code); extern bool of_CMPZ(vthread_t thr, vvp_code_t code); +extern bool of_CONCAT_STR(vthread_t thr, vvp_code_t code); +extern bool of_CONCATI_STR(vthread_t thr, vvp_code_t code); extern bool of_CVT_RS(vthread_t thr, vvp_code_t code); extern bool of_CVT_RU(vthread_t thr, vvp_code_t code); extern bool of_CVT_RV(vthread_t thr, vvp_code_t code); @@ -147,6 +149,7 @@ extern bool of_NORR(vthread_t thr, vvp_code_t code); extern bool of_OR(vthread_t thr, vvp_code_t code); extern bool of_ORR(vthread_t thr, vvp_code_t code); extern bool of_PAD(vthread_t thr, vvp_code_t code); +extern bool of_POP_STR(vthread_t thr, vvp_code_t code); extern bool of_POW(vthread_t thr, vvp_code_t code); extern bool of_POW_S(vthread_t thr, vvp_code_t code); extern bool of_POW_WR(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index 13722d388..cf12c4aea 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -126,6 +126,8 @@ static const struct opcode_table_s opcode_table[] = { { "%cmp/z", of_CMPZ, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%cmpi/s", of_CMPIS, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%cmpi/u", of_CMPIU, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%concat/str",of_CONCAT_STR,0,{OA_NONE, OA_NONE, OA_NONE} }, + { "%concati/str",of_CONCATI_STR,1,{OA_STRING,OA_NONE, OA_NONE} }, { "%cvt/rs", of_CVT_RS, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, { "%cvt/ru", of_CVT_RU, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, { "%cvt/rv", of_CVT_RV, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, @@ -195,6 +197,7 @@ static const struct opcode_table_s opcode_table[] = { { "%or", of_OR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%or/r", of_ORR, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%pad", of_PAD, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%pop/str",of_POP_STR,1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%pow", of_POW, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%pow/s", of_POW_S, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%pow/wr", of_POW_WR, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, @@ -504,6 +507,12 @@ bool vpi_handle_resolv_list_s::resolve(bool mes) val.ptr = vpip_make_vthr_word(base, ss); sym_set_value(sym_vpi, label(), val); + + } else if (1 == sscanf(label(), "S<%u,str>%n", &base, &n) + && n == strlen(label())) { + + val.ptr = vpip_make_vthr_str_stack(base); + sym_set_value(sym_vpi, label(), val); } } diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 5e48eab09..05b7502ac 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -255,6 +255,11 @@ static char* strdupnew(char const *str) assert(yylval.text); return T_SYMBOL; } +"S<"[0-9]*",str>" { + yylval.text = strdup(yytext); + assert(yylval.text); + return T_SYMBOL; } + "T<"[0-9]*","[0-9]*","[us]">" { yylval.text = strdup(yytext); assert(yylval.text); diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index 9a2791378..38c42b327 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2012 Stephen Williams (steve@icarus.com) * */ @@ -12,13 +12,26 @@ operands. In no case are there more than 3 operands. This chapter describes the specific behavior of each opcode, in enough detail (I hope) that its complete effect can be predicted. -General principles of Arithmetic: +General Principles of Arithmetic (current plan): The binary arithmetic instruction in general takes three parameters, the left operand, the right operand, and the base. The left operand is replaced with the result, which is the same width as the left and right operands. +General Principles of Arithmetic (new plan): + +For strings, all arithmetic is stack based. That is, there is an +abstract stack of strings from which operations pull their operands +and push their results. This is somewhat like FORTH (or an HP calculator +RPN notation) and spares the need to keep register addresses in +operands. I may find this leads to a more compact form of instruction +code, and may lead to more efficient operators overall, and in +particular I may find improved efficiency overall; so after the +experience of implementing it for strings, I'll want to change other +types around to using this method as well. Keep this in mind whenever +considering adding new instructions to vvp. + * %abs/wr , This instruction calculates the absolute value of a real value. It uses @@ -288,6 +301,18 @@ compares them. The results of the comparison go into bits 4 and 5: 4: eq (equal) 5: lt (less than) +For the purposes of calculating the lt bit, the top string is the +right operand and the string underneath is the left operand. This +instruction removes two strings from the stack. + +* %concat/str +* %concati/str + +Pop the top string, and concatenate it to the new top string. Or think +of it as possing the tail, then the head, concatenating them, and +pushing the result. The stack starts with two strings in the stack, +and ends with one string in the stack. + * %cvt/sr , * %cvt/rs , @@ -741,6 +766,12 @@ destination vector in register space. The destination may overlap the source bit. The may not be 0-3. This is useful for zero or sign extending a vector. +* %pop/str + +Pop this many items from the string stack. This is the opposite of the +%pushX/str opcode which pushes a string to the stack. The %pop/str is +not normally needed because the %store/str includes an implicit pop, +but sometimes it is necessary to pop explicitly. * %pow , , * %pow/s , , diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 9f28d5bcd..9bab0c85f 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -610,6 +610,7 @@ vpiHandle vpip_make_real_param(char*name, double value, bool local_flag, vpiHandle vpip_make_vthr_vector(unsigned base, unsigned wid, bool signed_flag); vpiHandle vpip_make_vthr_word(unsigned base, const char*type); +vpiHandle vpip_make_vthr_str_stack(unsigned depth); vpiHandle vpip_make_vthr_A(char*label, unsigned index); vpiHandle vpip_make_vthr_A(char*label, char*symbol); diff --git a/vvp/vpi_vthr_vector.cc b/vvp/vpi_vthr_vector.cc index e69ad7d47..73fbbbac2 100644 --- a/vvp/vpi_vthr_vector.cc +++ b/vvp/vpi_vthr_vector.cc @@ -635,3 +635,66 @@ void vpi_handle_delete() } } #endif + +class __vpiVThrStrStack : public __vpiHandle { + public: + __vpiVThrStrStack(unsigned depth); + int get_type_code(void) const; + int vpi_get(int code); + void vpi_get_value(p_vpi_value val); + private: + const char* name; + unsigned depth_; +}; + +__vpiVThrStrStack::__vpiVThrStrStack(unsigned d) +: depth_(d) +{ +} + +int __vpiVThrStrStack::get_type_code(void) const +{ return vpiConstant; } + +int __vpiVThrStrStack::vpi_get(int code) +{ + switch (code) { + case vpiConstType: + return vpiStringConst; + default: + return 0; + } +} + +void __vpiVThrStrStack::vpi_get_value(p_vpi_value vp) +{ + string val; + char*rbuf = 0; + + if (vpip_current_vthread) + val = vthread_get_str_stack(vpip_current_vthread, depth_); + + switch (vp->format) { + + case vpiObjTypeVal: + vp->format = vpiStringVal; + case vpiStringVal: + rbuf = need_result_buf(val.size()+1, RBUF_VAL); + strcpy(rbuf, val.c_str()); + vp->value.str = rbuf; + break; + + default: + fprintf(stderr, "vvp error: get %d not supported " + "by vpiConstant (String)\n", (int)vp->format); + + vp->format = vpiSuppressVal; + break; + } +} + + +vpiHandle vpip_make_vthr_str_stack(unsigned depth) +{ + struct __vpiVThrStrStack*obj = new __vpiVThrStrStack(depth); + return obj; +} diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 32cbe5e36..5eb5dc494 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -189,6 +189,13 @@ void vthread_put_real(struct vthread_s*thr, unsigned addr, double val) thr->words[addr].w_real = val; } +string vthread_get_str_stack(struct vthread_s*thr, unsigned depth) +{ + assert(depth < thr->stack_str.size()); + unsigned use_index = thr->stack_str.size()-1-depth; + return thr->stack_str[use_index]; +} + template T coerce_to_width(const T&that, unsigned width) { if (that.size() == width) @@ -1817,6 +1824,29 @@ bool of_CMPZ(vthread_t thr, vvp_code_t cp) return true; } +/* + * %concat/str; + */ +bool of_CONCAT_STR(vthread_t thr, vvp_code_t) +{ + assert(thr->stack_str.size() >= 1); + string text = thr->stack_str.back(); + thr->stack_str.pop_back(); + thr->stack_str.back().append(text); + return true; +} + +/* + * %concati/str ; + */ +bool of_CONCATI_STR(vthread_t thr, vvp_code_t cp) +{ + const char*text = cp->text; + assert(thr->stack_str.size() >= 1); + thr->stack_str.back().append(text); + return true; +} + bool of_CVT_RS(vthread_t thr, vvp_code_t cp) { int64_t r = thr->words[cp->bit_idx[1]].w_int; @@ -4112,6 +4142,21 @@ bool of_NOR(vthread_t thr, vvp_code_t cp) return cp->opcode(thr, cp); } +/* + * %pop/str + */ +bool of_POP_STR(vthread_t thr, vvp_code_t cp) +{ + unsigned cnt = cp->number; + assert(cnt <= thr->stack_str.size()); + + for (unsigned idx = 0 ; idx < cnt ; idx += 1) { + thr->stack_str.pop_back(); + } + + return true; +} + bool of_POW(vthread_t thr, vvp_code_t cp) { assert(cp->bit_idx[0] >= 4); diff --git a/vvp/vthread.h b/vvp/vthread.h index 0bdb4225a..50df466e9 100644 --- a/vvp/vthread.h +++ b/vvp/vthread.h @@ -21,6 +21,8 @@ # include "vvp_net.h" +# include + /* * A vthread is a simulation thread that executes instructions when * they are scheduled. This structure contains all the thread specific @@ -117,6 +119,11 @@ extern void vthread_put_bit(struct vthread_s*thr, unsigned addr, vvp_bit4_t bit) extern double vthread_get_real(struct vthread_s*thr, unsigned addr); extern void vthread_put_real(struct vthread_s*thr, unsigned addr, double val); +/* Get the string from the requested position in the vthread string + stack. The top of the stack is depth==0, and items below are + depth==1, etc. */ +extern std::string vthread_get_str_stack(struct vthread_s*thr, unsigned depth); + /* This is used to actually delete a thread once we are done with it. */ extern void vthread_delete(vthread_t thr);