diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 89e83d674..f504f8b68 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -34,7 +34,7 @@ typedef enum expr_sign_e { NEED_UNSIGNED = 2 } expr_sign_t; -static expr_sign_t get_binary_sign_type(ivl_expr_t expr) +static expr_sign_t expr_get_binary_sign_type(ivl_expr_t expr) { ivl_expr_t oper1, oper2; int opr_sign = 0; @@ -75,15 +75,15 @@ static expr_sign_t get_binary_sign_type(ivl_expr_t expr) break; } - /* Check to see if a $signed() or $unsigned() are needed. */ + /* Check to see if a $signed() or $unsigned() is 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) +static expr_sign_t expr_get_select_sign_type(ivl_expr_t expr, + unsigned can_skip_unsigned) { int opr_sign = 0; int expr_sign = ivl_expr_signed(expr); @@ -99,28 +99,28 @@ static expr_sign_t get_select_sign_type(ivl_expr_t expr, if (ivl_expr_type(oper1) != IVL_EX_SIGNAL) can_skip_unsigned -= 1; } - /* Check to see if a $signed() or $unsigned() are needed. */ + /* Check to see if a $signed() or $unsigned() is 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) +static expr_sign_t expr_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. */ + /* Check to see if a $signed() or $unsigned() is 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) +static expr_sign_t expr_get_unary_sign_type(ivl_expr_t expr) { ivl_expr_t expr1; int opr_sign = 0; @@ -166,7 +166,7 @@ static expr_sign_t get_unary_sign_type(ivl_expr_t expr) break; } - /* Check to see if a $signed() or $unsigned() are needed. */ + /* Check to see if a $signed() or $unsigned() is needed. */ if (expr_sign && ! opr_sign) rtn = NEED_SIGNED; if (! expr_sign && opr_sign) rtn = NEED_UNSIGNED; @@ -177,7 +177,7 @@ static expr_sign_t get_unary_sign_type(ivl_expr_t expr) * This routine is used to determine if the expression is a binary operator * where the width could be different to create a self-determined context. */ -static unsigned is_binary_self_det(ivl_expr_t expr) +static unsigned expr_is_binary_self_det(ivl_expr_t expr) { unsigned rtn = 0; if (ivl_expr_type(expr) == IVL_EX_BINARY) { @@ -211,9 +211,9 @@ static unsigned is_binary_self_det(ivl_expr_t expr) * 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 wid, - unsigned can_skip_unsigned, - unsigned is_full_prec) +static expr_sign_t expr_get_sign_type(ivl_expr_t expr, unsigned wid, + unsigned can_skip_unsigned, + unsigned is_full_prec) { unsigned expr_wid = ivl_expr_width(expr); expr_sign_t rtn = NO_SIGN; @@ -221,7 +221,7 @@ static expr_sign_t get_sign_type(ivl_expr_t expr, unsigned wid, switch (type) { case IVL_EX_BINARY: - rtn = get_binary_sign_type(expr); + rtn = expr_get_binary_sign_type(expr); break; case IVL_EX_CONCAT: /* A concatenation is always unsigned so add a $signed() when @@ -229,22 +229,22 @@ static expr_sign_t get_sign_type(ivl_expr_t expr, unsigned wid, if (ivl_expr_signed(expr)) rtn = NEED_SIGNED; break; case IVL_EX_SELECT: - rtn = get_select_sign_type(expr, can_skip_unsigned); + rtn = expr_get_select_sign_type(expr, can_skip_unsigned); /* If there is no select expression then this is padding so use * the actual expressions width if it is a binary operator that * could create a self-determined context. */ if (! ivl_expr_oper2(expr)) { ivl_expr_t oper1 = ivl_expr_oper1(expr); - if (is_binary_self_det(oper1)) { + if (expr_is_binary_self_det(oper1)) { expr_wid = ivl_expr_width(oper1); } } break; case IVL_EX_SIGNAL: - rtn = get_signal_sign_type(expr, can_skip_unsigned); + rtn = expr_get_signal_sign_type(expr, can_skip_unsigned); break; case IVL_EX_UNARY: - rtn = get_unary_sign_type(expr); + rtn = expr_get_unary_sign_type(expr); break; /* These do not currently have sign casting information. A select * is used for that purpose. */ @@ -774,8 +774,9 @@ 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, - ivl_expr_width(sel_expr), 0, 1); + expr_sign_t sign_type = expr_get_sign_type(sel_expr, + ivl_expr_width(sel_expr), + 0, 1); char msg[64]; snprintf(msg, sizeof(msg), "%s:%u", ivl_expr_file(sel_expr), @@ -1095,8 +1096,8 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid, } /* In a self-determined context the expression set the width. */ if (! wid) wid = ivl_expr_width(expr); - sign_type = get_sign_type(expr, wid, can_skip_unsigned, - is_full_prec); + sign_type = expr_get_sign_type(expr, wid, can_skip_unsigned, + is_full_prec); /* Check to see if a $signed() or $unsigned() needs to be emitted * before the expression. */ diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index c30fffcdb..e7af3cf65 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -21,6 +21,97 @@ # include "config.h" # include "vlog95_priv.h" +/* + * Data type used to signify if a $signed or $unsigned should be emitted. + */ +typedef enum lpm_sign_e { + NO_SIGN = 0, + NEED_SIGNED = 1, + NEED_UNSIGNED = 2 +} lpm_sign_t; + +static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, + int*base, unsigned*array_word); + +/* + * Look to see if the nexus driver is signed. + */ +static int nexus_driver_is_signed(ivl_scope_t scope, ivl_nexus_t nex) +{ + int is_signed = 0; + unsigned sign_set = 0; + unsigned idx, count = ivl_nexus_ptrs(nex); + + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + ivl_lpm_t t_lpm = ivl_nexus_ptr_lpm(nex_ptr); + ivl_net_const_t t_net_const = ivl_nexus_ptr_con(nex_ptr); + ivl_net_logic_t t_nlogic = ivl_nexus_ptr_log(nex_ptr); + ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); + ivl_drive_t cur_drive1 = ivl_nexus_ptr_drive1(nex_ptr); + ivl_drive_t cur_drive0 = ivl_nexus_ptr_drive0(nex_ptr); + if ((cur_drive1 == IVL_DR_HiZ) && + (cur_drive0 == IVL_DR_HiZ)) continue; + /* Only one driver can set the sign information. */ + assert( ! sign_set); + if (t_lpm) { + sign_set = 1; + is_signed = ivl_lpm_signed(t_lpm); + } + if (t_net_const) { + sign_set = 1; + is_signed = ivl_const_signed(t_net_const); + } + if (t_nlogic) { + sign_set = 1; + /* A BUFZ is used to drive a local signal so look for the + * local signal to get the sign information. */ + if (ivl_logic_type(t_nlogic) == IVL_LO_BUFZ) { + unsigned array_word = 0; + int base = 0; + ivl_signal_t sig; + assert(ivl_logic_pins(t_nlogic) == 2); + sig = nexus_is_signal(scope, + ivl_logic_pin(t_nlogic, 0), + &base, &array_word); + assert(sig); + is_signed = ivl_signal_signed(sig); + } + /* The rest of the logic type are always unsigned. */ + } + if (t_sig) { + sign_set = 1; + is_signed = ivl_signal_signed(t_sig); + } + } + + return is_signed; +} + +static lpm_sign_t lpm_get_sign_type(ivl_lpm_t lpm, + unsigned can_skip_unsigned) +{ + lpm_sign_t rtn = NO_SIGN; + int opr_sign = 0; + int lpm_sign = ivl_lpm_signed(lpm); + + switch (ivl_lpm_type(lpm)) { + case IVL_LPM_SIGN_EXT: + opr_sign = nexus_driver_is_signed(ivl_lpm_scope(lpm), + ivl_lpm_data(lpm, 0)); + break; + default: + opr_sign = lpm_sign; + break; + } + + /* Check to see if a $signed() or $unsigned() is needed. */ + if (lpm_sign && ! opr_sign) rtn = NEED_SIGNED; + if (! lpm_sign && opr_sign && ! can_skip_unsigned) rtn = NEED_UNSIGNED; + + return rtn; +} + static unsigned emit_drive(ivl_drive_t drive) { switch (drive) { @@ -834,6 +925,29 @@ static void emit_lpm_func(ivl_scope_t scope, ivl_lpm_t lpm) static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, unsigned sign_extend) { + lpm_sign_t sign_type; + /* Check to see if a $signed() or $unsigned() needs to be emitted + * before the expression. */ + sign_type = lpm_get_sign_type(lpm, 0); + if (sign_type == NEED_SIGNED) { + fprintf(vlog_out, "$signed("); + if (! allow_signed) { + fprintf(stderr, "%s:%u: vlog95 error: $signed() is not " + "supported.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + } + } + if (sign_type == NEED_UNSIGNED) { + fprintf(vlog_out, "$unsigned("); + if (! allow_signed) { + fprintf(stderr, "%s:%u: vlog95 error: $unsigned() is not " + "supported.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + } + } + switch (ivl_lpm_type(lpm)) { /* Convert the Verilog-A abs() function. This only works when the * argument has no side effect. */ @@ -1056,6 +1170,9 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, (int)ivl_lpm_type(lpm)); vlog_errors += 1; } + + /* Close the $signed() or $unsigned() if needed. */ + if (sign_type != NO_SIGN) fprintf(vlog_out, ")"); } static void emit_posedge_dff_prim() @@ -1871,6 +1988,7 @@ void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex) case IVL_LPM_UFUNC: fprintf(stderr, "U-func"); break; default: fprintf(stderr, "<%d>", ivl_lpm_type(lpm)); } + if (ivl_lpm_signed(lpm)) fprintf(stderr, " "); } else if (net_const) { ivl_scope_t const_scope = ivl_const_scope(net_const); assert(! nlogic); @@ -1879,6 +1997,7 @@ void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex) if (scope != const_scope) { fprintf(stderr, " (%s)", ivl_scope_name(const_scope)); } + if (ivl_const_signed(net_const)) fprintf(stderr, " "); } else if (nlogic) { ivl_scope_t logic_scope = ivl_logic_scope(nlogic); ivl_logic_t logic_type = ivl_logic_type(nlogic); @@ -1975,6 +2094,7 @@ void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex) case IVL_VT_CLASS: fprintf(stderr, " class"); break; } + if (ivl_signal_signed(sig)) fprintf(stderr, " "); } else fprintf(stderr, "Error: No/missing information!"); fprintf(stderr, " (%u)\n", ivl_nexus_ptr_pin(nex_ptr)); }