vlog95: Add some support for $signed()/$unsigned() in a CA
This commit is contained in:
parent
032f12af45
commit
f3917778bc
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue