Handle concatenation of SystemVerilog strings.

This commit is contained in:
Stephen Williams 2012-06-24 15:33:40 -07:00
parent 2bd3d9ed5d
commit f77bdf7e38
19 changed files with 283 additions and 27 deletions

View File

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

View File

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

View File

@ -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<PEString*> (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. */

View File

@ -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<NetEConst*>(parms_[idx-1]);
if (expr == 0)
return 0;

View File

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

View File

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

View File

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

View File

@ -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:
svector<NetExpr*>parms_;
std::vector<NetExpr*>parms_;
unsigned repeat_;
ivl_variable_type_t expr_type_;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 <bit-o>, <bit-i>
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 <string>
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 <bit-l>, <bit-r>
* %cvt/rs <bit-l>, <bit-r>
@ -741,6 +766,12 @@ destination vector in register space. The destination may overlap
the source bit. The <dst> may not be 0-3. This is useful for zero
or sign extending a vector.
* %pop/str <num>
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 <bit-l>, <bit-r>, <wid>
* %pow/s <bit-l>, <bit-r>, <wid>

View File

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

View File

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

View File

@ -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 <class T> 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 <string>;
*/
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 <number>
*/
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);

View File

@ -21,6 +21,8 @@
# include "vvp_net.h"
# include <string>
/*
* 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);