diff --git a/ivl_target.h b/ivl_target.h index df108d13a..a2130848e 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -662,6 +662,11 @@ extern ivl_nexus_t ivl_event_pos(ivl_event_t net, unsigned idx); * IVL core figures all this out for you. At any rate, this method * can be applied to any expression node. * + * ivl_expr_sized + * This method returns false (0) if the expression node does not + * have a defined size. This is unusual, but may happen for + * constant expressions. + * * ivl_expr_type * Get the type of the expression node. Every expression node has a * type, which can affect how some of the other expression methods @@ -782,6 +787,8 @@ extern ivl_scope_t ivl_expr_scope(ivl_expr_t net); extern ivl_signal_t ivl_expr_signal(ivl_expr_t net); /* any expression */ extern int ivl_expr_signed(ivl_expr_t net); + /* any expression */ +extern int ivl_expr_sized(ivl_expr_t net); /* IVL_EX_STRING */ extern const char* ivl_expr_string(ivl_expr_t net); /* IVL_EX_ULONG */ diff --git a/t-dll-api.cc b/t-dll-api.cc index aa155fc52..bf4597458 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -518,6 +518,12 @@ extern "C" int ivl_expr_signed(ivl_expr_t net) return net->signed_; } +extern "C" int ivl_expr_sized(ivl_expr_t net) +{ + assert(net); + return net->sized_; +} + extern "C" const char* ivl_expr_string(ivl_expr_t net) { assert(net->type_ == IVL_EX_STRING); diff --git a/t-dll-expr.cc b/t-dll-expr.cc index e67caca11..43f638e43 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -60,6 +60,7 @@ void dll_target::sub_off_from_expr_(long off) tmpc->value_ = IVL_VT_VECTOR; tmpc->width_ = expr_->width_; tmpc->signed_ = expr_->signed_; + tmpc->sized_ = 1; tmpc->u_.number_.bits_ = bits = (char*)malloc(tmpc->width_); for (unsigned idx = 0 ; idx < tmpc->width_ ; idx += 1) { bits[idx] = (off & 1)? '1' : '0'; @@ -74,6 +75,7 @@ void dll_target::sub_off_from_expr_(long off) tmps->value_ = IVL_VT_VECTOR; tmps->width_ = tmpc->width_; tmps->signed_ = tmpc->signed_; + tmps->sized_ = 1; tmps->u_.binary_.op_ = '-'; tmps->u_.binary_.lef_ = expr_; tmps->u_.binary_.rig_ = tmpc; @@ -92,6 +94,7 @@ void dll_target::mul_expr_by_const_(long val) tmpc->value_ = IVL_VT_VECTOR; tmpc->width_ = expr_->width_; tmpc->signed_ = expr_->signed_; + tmpc->sized_ = 1; tmpc->u_.number_.bits_ = bits = (char*)malloc(tmpc->width_); for (unsigned idx = 0 ; idx < tmpc->width_ ; idx += 1) { bits[idx] = (val & 1)? '1' : '0'; @@ -106,6 +109,7 @@ void dll_target::mul_expr_by_const_(long val) tmps->value_ = IVL_VT_VECTOR; tmps->width_ = tmpc->width_; tmps->signed_ = tmpc->signed_; + tmps->sized_ = 1; tmps->u_.binary_.op_ = '*'; tmps->u_.binary_.lef_ = expr_; tmps->u_.binary_.rig_ = tmpc; @@ -125,6 +129,7 @@ ivl_expr_t dll_target::expr_from_value_(const verinum&val) expr->value_= IVL_VT_VECTOR; expr->width_= val.len(); expr->signed_ = val.has_sign()? 1 : 0; + expr->sized_= 1; expr->u_.number_.bits_ = bits = (char*)malloc(expr->width_ + 1); for (idx = 0 ; idx < expr->width_ ; idx += 1) switch (val.get(idx)) { @@ -160,6 +165,7 @@ void dll_target::expr_access_func(const NetEAccess*net) expr_->lineno = net->get_lineno(); expr_->width_ = 1; expr_->signed_= 1; + expr_->sized_ = 1; expr_->u_.branch_.branch = net->get_branch()->target_obj(); expr_->u_.branch_.nature = net->get_nature(); @@ -183,6 +189,7 @@ void dll_target::expr_binary(const NetEBinary*net) expr_->value_= get_expr_type(net); expr_->width_= net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; + expr_->sized_= 1; expr_->u_.binary_.op_ = net->op(); expr_->u_.binary_.lef_ = left; @@ -200,6 +207,7 @@ void dll_target::expr_concat(const NetEConcat*net) cur->value_ = IVL_VT_VECTOR; cur->width_ = net->expr_width(); cur->signed_ = net->has_sign() ? 1 : 0; + cur->sized_ = 1; cur->u_.concat_.rept = net->repeat(); cur->u_.concat_.parms = net->nparms(); @@ -236,6 +244,7 @@ void dll_target::expr_const(const NetEConst*net) expr_->type_ = IVL_EX_NUMBER; expr_->width_= net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; + expr_->sized_= net->has_width()? 1 : 0; expr_->u_.number_.bits_ = bits = (char*)malloc(expr_->width_); for (idx = 0 ; idx < expr_->width_ ; idx += 1) switch (val.get(idx)) { @@ -294,6 +303,7 @@ void dll_target::expr_creal(const NetECReal*net) expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr_->width_ = net->expr_width(); expr_->signed_ = 1; + expr_->sized_ = 1; expr_->type_ = IVL_EX_REALNUM; FILE_NAME(expr_, net); expr_->value_= IVL_VT_REAL; @@ -358,6 +368,7 @@ void dll_target::expr_select(const NetESelect*net) expr_->value_= IVL_VT_VECTOR; expr_->width_= net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; + expr_->sized_= 1; expr_->u_.binary_.lef_ = left; expr_->u_.binary_.rig_ = rght; @@ -375,6 +386,7 @@ void dll_target::expr_sfunc(const NetESFunc*net) expr->value_= net->expr_type(); expr->width_= net->expr_width(); expr->signed_ = net->has_sign()? 1 : 0; + expr->sized_= 1; /* system function names are lex_strings strings. */ expr->u_.sfunc_.name_ = net->name(); @@ -405,6 +417,7 @@ void dll_target::expr_ternary(const NetETernary*net) expr->value_= net->expr_type(); expr->width_ = net->expr_width(); expr->signed_ = net->has_sign()? 1 : 0; + expr->sized_ = 1; net->cond_expr()->expr_scan(this); assert(expr_); @@ -447,6 +460,7 @@ void dll_target::expr_signal(const NetESignal*net) expr_->lineno= net->get_lineno(); expr_->width_= net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; + expr_->sized_= 1; expr_->u_.signal_.word = word_expr; expr_->u_.signal_.sig = sig; @@ -474,6 +488,7 @@ void dll_target::expr_ufunc(const NetEUFunc*net) expr->value_= net->expr_type(); expr->width_= net->expr_width(); expr->signed_ = net->has_sign()? 1 : 0; + expr->sized_= 1; expr->u_.ufunc_.def = lookup_scope_(net->func()); assert(expr->u_.ufunc_.def->type_ == IVL_SCT_FUNCTION); @@ -507,6 +522,7 @@ void dll_target::expr_unary(const NetEUnary*net) expr_->value_= net->expr_type(); expr_->width_ = net->expr_width(); expr_->signed_ = net->has_sign()? 1 : 0; + expr_->sized_ = 1; expr_->u_.unary_.op_ = net->op(); expr_->u_.unary_.sub_ = sub; } diff --git a/t-dll.h b/t-dll.h index d6e256f7a..ab7670840 100644 --- a/t-dll.h +++ b/t-dll.h @@ -213,6 +213,7 @@ struct ivl_expr_s { unsigned width_; unsigned signed_ : 1; + unsigned sized_ : 1; union { struct { diff --git a/tgt-stub/expression.c b/tgt-stub/expression.c index 204709e46..018f5035a 100644 --- a/tgt-stub/expression.c +++ b/tgt-stub/expression.c @@ -257,6 +257,7 @@ void show_expression(ivl_expr_t net, unsigned ind) ivl_parameter_t par = ivl_expr_parameter(net); unsigned width = ivl_expr_width(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; + const char*sized = ivl_expr_sized(net)? "sized" : "unsized"; const char*vt = vt_type_string(net); switch (code) { @@ -292,7 +293,7 @@ void show_expression(ivl_expr_t net, unsigned ind) for (idx = width ; idx > 0 ; idx -= 1) fprintf(out, "%c", bits[idx-1]); - fprintf(out, ", %s %s", sign, vt); + fprintf(out, ", %s %s %s", sign, sized, vt); if (par != 0) fprintf(out, ", parameter=%s", ivl_parameter_basename(par)); diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index c9f7874a9..d3a38bedf 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -2479,26 +2479,93 @@ static struct vector_info draw_select_expr(ivl_expr_t exp, unsigned wid, /* Evaluate the sub-expression. */ subv = draw_eval_expr(sube, 0); - /* Any bit select of a constant zero is another constant zero, - so short circuit and return the value we know. */ - if (subv.base == 0) { + /* Special case: Any bit/part select of an unsized constant + zero by an unsigned base is another constant zero, so short + circuit and return the value we know. */ + if (subv.base == 0 && !ivl_expr_sized(sube) && !ivl_expr_signed(shift)) { + fprintf(vvp_out, "; Part select of unsized zero with unsized shift is zero.\n"); subv.wid = wid; return subv; } - /* Evaluate the bit select base expression and store the - result into index register 0. */ + /* Special case: Any bit/part select of an unsized constant -1 + by an unsigned base is another constant -1, so short + circuit and return the value we know. */ + if (subv.base == 1 && ivl_expr_signed(sube) && !ivl_expr_sized(sube) && !ivl_expr_signed(shift)) { + fprintf(vvp_out, "; Part select of unsized -1 with unsized shift is -1.\n"); + subv.wid = wid; + return subv; + } + + /* Evaluate the bit select base expression. */ shiv = draw_eval_expr(shift, STUFF_OK_XZ); - /* Detect and handle the special case that the shift is a - constant 0. Skip the shift, and return the subexpression - with the width trimmed down to the desired width. */ + /* Special case: If the shift is a constant 0, skip the shift + and return the subexpression with the width trimmed down to + the part select width. */ if (shiv.base == 0) { + fprintf(vvp_out, "; Part select with shift==0 skips shift.\n"); assert(subv.wid >= wid); res.base = subv.base; return res; } + /* Special case: If the expression is an unsized zero (and we + know that the shift is signed) then the expression value is + 0 if the shift is >= 0, and x otherwise. The trickery in + this special case assumes the output width is 1. */ + if (subv.base==0 && !ivl_expr_sized(sube) && wid==1) { + assert(ivl_expr_signed(shift)); + + if (shiv.base < 4) { + assert(shiv.base != 0); + res.base = 2; + res.wid = wid; + return res; + } + + /* Test if the shift is <0 by looking at the sign + bit. If the sign bit is 1, then it is negative and + the result is 1'bx. If the sign bit is 0, then the + result is 1'b0. */ + clr_vector(shiv); + res.base = allocate_vector(wid); + res.wid = wid; + fprintf(vvp_out, " %%mov %u, 2, 1;\n", res.base); + fprintf(vvp_out, " %%and %u, %u, 1; x if shift<0, 0 otherwise\n", + res.base, shiv.base+shiv.wid-1); + return res; + } + + /* Special case: If the expression is an unsized -1 (and we + know that the shift is signed) then the expression value is + 1 if the shift is >= 0, and x otherwise. The trickery in + this special case assumes the output width is 1. */ + if (subv.base==1 && ivl_expr_signed(sube) && !ivl_expr_sized(sube) && wid==1) { + assert(ivl_expr_signed(shift)); + + if (shiv.base < 4) { + assert(shiv.base != 0); + res.base = 2; + res.wid = wid; + return res; + } + + /* Test if the shift is <0 by looking at the sign + bit. If the sign bit is 1, then it is negative and + the result is 1'bx. If the sign bit is 0, then the + result is 1'b1. */ + clr_vector(shiv); + res.base = allocate_vector(wid); + res.wid = wid; + fprintf(vvp_out, " %%mov %u, 2, 1;\n", res.base); + fprintf(vvp_out, " %%nand %u, %u, 1; x if shift<0, 1 otherwise\n", + res.base, shiv.base+shiv.wid-1); + return res; + } + + /* Store the bit select base into index register 0, in + preparation for doing a shift. */ if (ivl_expr_signed(shift)) { fprintf(vvp_out, " %%ix/get/s 0, %u, %u;\n", shiv.base, shiv.wid);