vlog95: Add some support for $signed()/$unsigned() in a CA

This commit is contained in:
Cary R 2013-07-29 10:23:26 -07:00
parent 032f12af45
commit f3917778bc
2 changed files with 144 additions and 23 deletions

View File

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

View File

@ -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, " <signed>");
} 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, " <signed>");
} 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, " <signed>");
} else fprintf(stderr, "Error: No/missing information!");
fprintf(stderr, " (%u)\n", ivl_nexus_ptr_pin(nex_ptr));
}