vlog95: Better unsupported select warnings and variable parameter bit select

Since parameters are now passed by reference use that information to print
the parameter name in a select vs trying to figure it out by searching the
scope looking for a parameter with the same file, line and value information.

Only print the $signed() may need to be removed messages for a select
expression that will actually be cast to signed.

Use the actual parameter information to warn that -pallowsigned=1 is needed
for a parameter when the LSB > MSB.
This commit is contained in:
Cary R 2013-07-05 16:22:06 -07:00
parent f6b8d613ab
commit e2361fab29
3 changed files with 220 additions and 256 deletions

View File

@ -25,8 +25,204 @@
* emitted so it can print the numeric value instead of the parameter name. */
ivl_parameter_t emitting_param = 0;
// HERE: Do we need to use wid in these routines? We should probably use
// it to verify that the expressions have the expected width.
/*
* Data type used to signify if a $signed or $unsigned should be emitted.
*/
typedef enum expr_sign_e {
NO_SIGN = 0,
NEED_SIGNED = 1,
NEED_UNSIGNED = 2
} expr_sign_t;
// HERE: Do we need to use wid in the following emit routines? We should
// probably use it to verify that the expressions have the expected
// width. For now it is not being used, but is passed.
static expr_sign_t get_binary_sign_type(ivl_expr_t expr)
{
ivl_expr_t expr1, expr2;
int opr_sign = 0;
int expr_sign = ivl_expr_signed(expr);
expr_sign_t rtn = NO_SIGN;
switch (ivl_expr_opcode(expr)) {
case 'E':
case 'e':
case 'N':
case 'n':
case '<':
case 'L':
case '>':
case 'G':
/* The comparison operators always act as if the argument is
* unsigned. */
break;
case 'l':
case 'r':
case 'R':
/* For the shift operators only the left operand is used to
* determine the sign information. */
opr_sign = ivl_expr_signed(ivl_expr_oper1(expr));
break;
default:
/* For the rest of the opcodes the operator is considered to
* be signed if either argument is real or if both arguments
* are signed. */
expr1 = ivl_expr_oper1(expr);
expr2 = ivl_expr_oper2(expr);
if ((ivl_expr_value(expr1) == IVL_VT_REAL) ||
(ivl_expr_value(expr2) == IVL_VT_REAL)) {
opr_sign = 1;
} else if (ivl_expr_signed(expr1) && ivl_expr_signed(expr2)) {
opr_sign = 1;
}
break;
}
/* Check to see if a $signed() or $unsigned() are needed. */
if (expr_sign && ! opr_sign) rtn = NEED_SIGNED;
if (! expr_sign && opr_sign) rtn = NEED_UNSIGNED;
return rtn;
}
static expr_sign_t get_select_sign_type(ivl_expr_t expr,
unsigned can_skip_unsigned)
{
int opr_sign = 0;
int expr_sign = ivl_expr_signed(expr);
expr_sign_t rtn = NO_SIGN;
/* If there is no select expression then the sign is determined by
* the expression that is being selected (padded). */
if (! ivl_expr_oper2(expr)) {
opr_sign = ivl_expr_signed(ivl_expr_oper1(expr));
}
/* Check to see if a $signed() or $unsigned() are needed. */
if (expr_sign && ! opr_sign) rtn = NEED_SIGNED;
if (! expr_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED;
return rtn;
}
static expr_sign_t get_signal_sign_type(ivl_expr_t expr,
unsigned can_skip_unsigned)
{
int opr_sign = ivl_signal_signed(ivl_expr_signal(expr));
int expr_sign = ivl_expr_signed(expr);
expr_sign_t rtn = NO_SIGN;
/* Check to see if a $signed() or $unsigned() are needed. */
if (expr_sign && ! opr_sign) rtn = NEED_SIGNED;
if (! expr_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED;
return rtn;
}
static expr_sign_t get_unary_sign_type(ivl_expr_t expr)
{
ivl_expr_t expr1;
int opr_sign = 0;
int expr_sign = ivl_expr_signed(expr);
expr_sign_t rtn = NO_SIGN;
switch (ivl_expr_opcode(expr)) {
case '&':
case '|':
case '^':
case 'A':
case 'N':
case 'X':
/* The reduction operators always act as if the argument is
* unsigned. */
break;
case 'r':
/* For a cast to real the expression should be signed and no
* sign conversion is needed. */
opr_sign = expr_sign;
if (! expr_sign) {
fprintf(stderr, "%s:%u: vlog95 error: Cast to real "
"expression is not signed.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr));
vlog_errors += 1;
}
break;
case '2':
case 'v':
/* If the cast to a vector value is from a real then no sign
* conversion is needed. Otherwise use the actual argument. */
expr1 = ivl_expr_oper1(expr);
if (ivl_expr_value(expr1) == IVL_VT_REAL) {
opr_sign = expr_sign;
} else {
opr_sign = ivl_expr_signed(expr1);
}
break;
default:
/* For the rest of the opcodes the argument sign type depends
* on the actual argument. */
opr_sign = ivl_expr_signed(ivl_expr_oper1(expr));
break;
}
/* Check to see if a $signed() or $unsigned() are needed. */
if (expr_sign && ! opr_sign) rtn = NEED_SIGNED;
if (! expr_sign && opr_sign) rtn = NEED_UNSIGNED;
return rtn;
}
/*
* Determine if a $signed() or $unsigned() system function is needed to get
* the expression sign information correct. can_skip_unsigned may be set for
* the binary/ternary operators if one of the operands will implicitly cast
* the expression to unsigned. See calc_can_skip_unsigned() for the details.
*/
static expr_sign_t get_sign_type(ivl_expr_t expr, unsigned can_skip_unsigned)
{
expr_sign_t rtn = NO_SIGN;
switch (ivl_expr_type(expr)) {
case IVL_EX_BINARY:
rtn = get_binary_sign_type(expr);
break;
case IVL_EX_CONCAT:
/* A concatenation is always unsigned so add a $signed() when
* needed. */
if (ivl_expr_signed(expr)) rtn = NEED_SIGNED;
break;
case IVL_EX_SELECT:
rtn = get_select_sign_type(expr, can_skip_unsigned);
break;
case IVL_EX_SIGNAL:
rtn = get_signal_sign_type(expr, can_skip_unsigned);
break;
case IVL_EX_UNARY:
rtn = get_unary_sign_type(expr);
break;
/* These do not currently have sign casting information. A select
* is used for that purpose. */
case IVL_EX_ARRAY:
case IVL_EX_DELAY:
case IVL_EX_ENUMTYPE:
case IVL_EX_EVENT:
case IVL_EX_NEW:
case IVL_EX_NULL:
case IVL_EX_NUMBER:
case IVL_EX_PROPERTY:
case IVL_EX_REALNUM:
case IVL_EX_SCOPE:
case IVL_EX_SFUNC:
case IVL_EX_SHALLOWCOPY:
case IVL_EX_STRING:
case IVL_EX_TERNARY:
case IVL_EX_UFUNC:
default:
break;
}
return rtn;
}
/*
* We can convert any 2^n ** <unsigned variable> expression to
@ -371,65 +567,21 @@ static void emit_expr_scope(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
emit_expr_scope_piece(ivl_expr_scope(expr));
}
static unsigned emit_param_name_in_scope(ivl_scope_t scope, ivl_expr_t expr)
{
unsigned idx, count, lineno;
const char *file;
count = ivl_scope_params(scope);
file = ivl_expr_file(expr);
lineno = ivl_expr_lineno(expr);
for (idx = 0; idx < count; idx += 1) {
ivl_parameter_t par = ivl_scope_param(scope, idx);
if (lineno != ivl_parameter_lineno(par)) continue;
if (strcmp(file, ivl_parameter_file(par)) == 0) {
/* Check that the appropriate expression bits match the
* original parameter bits. */
ivl_expr_t pex = ivl_parameter_expr(par);
unsigned wid = ivl_expr_width(expr);
unsigned param_wid = ivl_expr_width(pex);
const char *my_bits = ivl_expr_bits(expr);
const char *param_bits = ivl_expr_bits(pex);
unsigned bit;
if (param_wid < wid) wid = param_wid;
for (bit = 0; bit < wid; bit += 1) {
if (my_bits[bit] != param_bits[bit]) {
fprintf(stderr, "%s:%u: vlog95 error: Constant "
"expression bits do not match "
"parameter bits.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
break;
}
}
// HERE: Does this work with an out of scope parameter reference?
// What about real parameters?
emit_id(ivl_parameter_basename(par));
return 1;
}
}
return 0;
}
static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
{
/* A select of a number is really a parameter select. */
if (ivl_expr_type(expr) == IVL_EX_NUMBER) {
/* Look in the current scope. */
if (emit_param_name_in_scope(scope, expr)) return;
/* Now look in the enclosing module scope if this is not a
* module scope. */
if (ivl_scope_type(scope) != IVL_SCT_MODULE) {
if (emit_param_name_in_scope(get_module_scope(scope),
expr)) return;
ivl_parameter_t param = ivl_expr_parameter(expr);
if (param) {
emit_scope_call_path(scope, ivl_parameter_scope(param));
emit_id(ivl_parameter_basename(param));
} else {
fprintf(vlog_out, "<missing>");
fprintf(stderr, "%s:%u: vlog95 error: Unable to find "
"parameter for select expression \n",
ivl_expr_file(expr), ivl_expr_lineno(expr));
vlog_errors += 1;
}
// HERE: For now we only look in the current and module scope for the
// parameter that is being selected. We need to also look in other
// scopes, but that involves a big search.
fprintf(vlog_out, "<missing>");
fprintf(stderr, "%s:%u: vlog95 error: Unable to find parameter "
"for select expression \n",
ivl_expr_file(expr), ivl_expr_lineno(expr));
vlog_errors += 1;
} else {
emit_expr(scope, expr, wid, 0);
}
@ -539,6 +691,7 @@ static void emit_expr_ips(ivl_scope_t scope, ivl_expr_t sig_expr,
static void check_select_signed(ivl_expr_t sig_expr, ivl_expr_t sel_expr,
int msb, int lsb)
{
expr_sign_t sign_type = get_sign_type(sel_expr, 0);
char msg[64];
snprintf(msg, sizeof(msg), "%s:%u",
ivl_expr_file(sel_expr),
@ -554,7 +707,7 @@ static void check_select_signed(ivl_expr_t sig_expr, ivl_expr_t sel_expr,
* cannot currently determine if the base expression started out
* signed or not so any extra $signed() operators will need to be
* removed manually. */
if ((msb > lsb) && (lsb > 0)) {
if ((msb > lsb) && (lsb > 0) && (sign_type == NEED_SIGNED)) {
fprintf(stderr, "%s: vlog95 sorry: The translation of a non-zero "
"based select with MSB > LSB is not smart enough "
"to remove extra $signed() operators.\n", msg);
@ -564,11 +717,12 @@ static void check_select_signed(ivl_expr_t sig_expr, ivl_expr_t sel_expr,
vlog_errors += 1;
/* If the element is not a parameter and the LSB > MSB then the cast
* to signed ($signed()) from the normalization precess may need to
* to signed ($signed()) from the normalization process may need to
* be removed. If the select expression is a constant number then
* this is not needed. */
} else if ((lsb > msb) && (ivl_expr_type(sig_expr) != IVL_EX_NUMBER) &&
(ivl_expr_type(sel_expr) != IVL_EX_NUMBER)) {
(ivl_expr_type(sel_expr) != IVL_EX_NUMBER) &&
(sign_type == NEED_SIGNED)) {
fprintf(stderr, "%s: vlog95 sorry: The translation of a select "
"with LSB > MSB is not smart enough to remove "
"extra $signed() operators.\n", msg);
@ -582,12 +736,11 @@ static void check_select_signed(ivl_expr_t sig_expr, ivl_expr_t sel_expr,
* expression 100% correct. */
} else if ((! allow_signed) &&
(ivl_expr_type(sig_expr) == IVL_EX_NUMBER)) {
int plsb, pmsb;
// HERE: Need to get the compiler to pass the MSB and LSB for the parameter
// before this test will work correctly. The name code already finds
// the parameter so this code needs to use that information.
plsb = lsb;
pmsb = msb;
int pmsb, plsb;
ivl_parameter_t param = ivl_expr_parameter(sig_expr);
assert(param);
pmsb = ivl_parameter_msb(param);;
plsb = ivl_parameter_lsb(param);;
if (plsb > pmsb) {
fprintf(stderr, "%s: vlog95 note: Translating a LSB > "
"MSB parameter select requires the "
@ -827,195 +980,6 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
}
}
/*
* Data type used to signify if a $signed or $unsigned should be emitted.
*/
typedef enum expr_sign_e {
NO_SIGN = 0,
NEED_SIGNED = 1,
NEED_UNSIGNED = 2
} expr_sign_t;
static expr_sign_t get_binary_sign_type(ivl_expr_t expr)
{
ivl_expr_t expr1, expr2;
int opr_sign = 0;
int expr_sign = ivl_expr_signed(expr);
expr_sign_t rtn = NO_SIGN;
switch (ivl_expr_opcode(expr)) {
case 'E':
case 'e':
case 'N':
case 'n':
case '<':
case 'L':
case '>':
case 'G':
/* The comparison operators always act as if the argument is
* unsigned. */
break;
case 'l':
case 'r':
case 'R':
/* For the shift operators only the left operand is used to
* determine the sign information. */
opr_sign = ivl_expr_signed(ivl_expr_oper1(expr));
break;
default:
/* For the rest of the opcodes the operator is considered to
* be signed if either argument is real or if both arguments
* are signed. */
expr1 = ivl_expr_oper1(expr);
expr2 = ivl_expr_oper2(expr);
if ((ivl_expr_value(expr1) == IVL_VT_REAL) ||
(ivl_expr_value(expr2) == IVL_VT_REAL)) {
opr_sign = 1;
} else if (ivl_expr_signed(expr1) && ivl_expr_signed(expr2)) {
opr_sign = 1;
}
break;
}
/* Check to see if a $signed() or $unsigned() are needed. */
if (expr_sign && ! opr_sign) rtn = NEED_SIGNED;
if (! expr_sign && opr_sign) rtn = NEED_UNSIGNED;
return rtn;
}
static expr_sign_t get_select_sign_type(ivl_expr_t expr,
unsigned can_skip_unsigned)
{
int opr_sign = 0;
int expr_sign = ivl_expr_signed(expr);
expr_sign_t rtn = NO_SIGN;
/* If there is no select expression then the sign is determined by
* the expression that is being selected (padded). */
if (! ivl_expr_oper2(expr)) {
opr_sign = ivl_expr_signed(ivl_expr_oper1(expr));
}
/* Check to see if a $signed() or $unsigned() are needed. */
if (expr_sign && ! opr_sign) rtn = NEED_SIGNED;
if (! expr_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED;
return rtn;
}
static expr_sign_t get_signal_sign_type(ivl_expr_t expr,
unsigned can_skip_unsigned)
{
int opr_sign = ivl_signal_signed(ivl_expr_signal(expr));
int expr_sign = ivl_expr_signed(expr);
expr_sign_t rtn = NO_SIGN;
/* Check to see if a $signed() or $unsigned() are needed. */
if (expr_sign && ! opr_sign) rtn = NEED_SIGNED;
if (! expr_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED;
return rtn;
}
static expr_sign_t get_unary_sign_type(ivl_expr_t expr)
{
ivl_expr_t expr1;
int opr_sign = 0;
int expr_sign = ivl_expr_signed(expr);
expr_sign_t rtn = NO_SIGN;
switch (ivl_expr_opcode(expr)) {
case '&':
case '|':
case '^':
case 'A':
case 'N':
case 'X':
/* The reduction operators always act as if the argument is
* unsigned. */
break;
case 'r':
/* For a cast to real the expression should be signed and no
* sign conversion is needed. */
opr_sign = expr_sign;
if (! expr_sign) {
fprintf(stderr, "%s:%u: vlog95 error: Cast to real "
"expression is not signed.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr));
vlog_errors += 1;
}
break;
case '2':
case 'v':
/* If the cast to a vector value is from a real then no sign
* conversion is needed. Otherwise use the actual argument. */
expr1 = ivl_expr_oper1(expr);
if (ivl_expr_value(expr1) == IVL_VT_REAL) {
opr_sign = expr_sign;
} else {
opr_sign = ivl_expr_signed(expr1);
}
break;
default:
/* For the rest of the opcodes the argument sign type depends
* on the actual argument. */
opr_sign = ivl_expr_signed(ivl_expr_oper1(expr));
break;
}
/* Check to see if a $signed() or $unsigned() are needed. */
if (expr_sign && ! opr_sign) rtn = NEED_SIGNED;
if (! expr_sign && opr_sign) rtn = NEED_UNSIGNED;
return rtn;
}
expr_sign_t get_sign_type(ivl_expr_t expr, unsigned can_skip_unsigned)
{
expr_sign_t rtn = NO_SIGN;
switch (ivl_expr_type(expr)) {
case IVL_EX_BINARY:
rtn = get_binary_sign_type(expr);
break;
case IVL_EX_CONCAT:
/* A concatenation is always unsigned so add a $signed() when
* needed. */
if (ivl_expr_signed(expr)) rtn = NEED_SIGNED;
break;
case IVL_EX_SELECT:
rtn = get_select_sign_type(expr, can_skip_unsigned);
break;
case IVL_EX_SIGNAL:
rtn = get_signal_sign_type(expr, can_skip_unsigned);
break;
case IVL_EX_UNARY:
rtn = get_unary_sign_type(expr);
break;
/* These do not currently have sign casting information. A select
* is used for that purpose. */
case IVL_EX_ARRAY:
case IVL_EX_DELAY:
case IVL_EX_ENUMTYPE:
case IVL_EX_EVENT:
case IVL_EX_NEW:
case IVL_EX_NULL:
case IVL_EX_NUMBER:
case IVL_EX_PROPERTY:
case IVL_EX_REALNUM:
case IVL_EX_SCOPE:
case IVL_EX_SFUNC:
case IVL_EX_SHALLOWCOPY:
case IVL_EX_STRING:
case IVL_EX_TERNARY:
case IVL_EX_UFUNC:
default:
break;
}
return rtn;
}
void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid,
unsigned can_skip_unsigned)
{

View File

@ -838,7 +838,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm,
/* Convert the Verilog-A abs() function. This only works when the
* argument has no side effect. */
case IVL_LPM_ABS:
// HERE: If this is a real net then use the $abs() function to get nan to
// HERE: If this is a real net then use the $abs() function to get NaN to
// work correctly. See the expr code.
fprintf(vlog_out, "((");
emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0);

View File

@ -880,7 +880,7 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent)
fprintf(vlog_out, "\n%*cfunction", indent, ' ');
if (ivl_scope_ports(scope) < 2) {
fprintf(stderr, "%s:%u: vlog95 error: Function (%s) has "
"no argments (or return value).\n",
"no arguments (or return value).\n",
ivl_scope_file(scope),
ivl_scope_lineno(scope),
ivl_scope_tname(scope));