From 88cec4d6f6a6fb20ea48d98439db8ea2e771d259 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 10 Feb 2011 20:42:51 -0800 Subject: [PATCH 01/35] vlog95: Fix pull gate bug, emit local expr sigs as CA, etc. This patch fixes the following bugs in the vlog95 converter: When emitting a signal as an expression, if the signal is local then emit the expression that is driving the local signal. The casts need to emit the expression. When emitting pull devices they have different default strength rules (pull is the default). For compatibility pull devices should only have a single output net. Extend the code that searches for the = pattern to also check for an . The previous event code only looks for the repeat event pattern. --- tgt-vlog95/expr.c | 21 ++++++---- tgt-vlog95/logic_lpm.c | 82 +++++++++++++++++++++++++--------------- tgt-vlog95/misc.c | 7 +++- tgt-vlog95/stmt.c | 38 ++++++++++++------- tgt-vlog95/vlog95_priv.h | 4 +- 5 files changed, 97 insertions(+), 55 deletions(-) diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 2c3ed2100..c42e0f456 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -351,14 +351,18 @@ static void emit_expr_sfunc(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) static void emit_expr_signal(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_signal_t sig = ivl_expr_signal(expr); - emit_scope_module_path(scope, ivl_signal_scope(sig)); - fprintf(vlog_out, "%s", ivl_signal_basename(sig)); - if (ivl_signal_dimensions(sig)) { - int lsb = ivl_signal_array_base(sig); - int msb = lsb + ivl_signal_array_count(sig); - fprintf(vlog_out, "["); - emit_scaled_expr(scope, ivl_expr_oper1(expr), msb, lsb); - fprintf(vlog_out, "]"); + if (ivl_signal_local(sig)) { + emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0)); + } else { + emit_scope_module_path(scope, ivl_signal_scope(sig)); + fprintf(vlog_out, "%s", ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + int lsb = ivl_signal_array_base(sig); + int msb = lsb + ivl_signal_array_count(sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, ivl_expr_oper1(expr), msb, lsb); + fprintf(vlog_out, "]"); + } } } @@ -411,6 +415,7 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) case 'i': case 'r': /* A cast is a noop. */ + emit_expr(scope, ivl_expr_oper1(expr), wid); break; default: fprintf(vlog_out, ""); diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 7da84fa7d..7416d2883 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -54,30 +54,45 @@ static void emit_strength(ivl_drive_t drive1, ivl_drive_t drive0, const char *file, unsigned lineno) { assert(strength_type <= 2); - if ((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG)) { + if ((strength_type == 2) && + ((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG))) { fprintf(vlog_out, " ("); - if (strength_type > 0) { - if (emit_drive(drive1)) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " - "1 drive (%d)\n", file, lineno, - type, (int)drive1); - vlog_errors += 1; - } - fprintf(vlog_out, "1"); + if (emit_drive(drive1)) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " + "1 drive (%d)\n", file, lineno, + type, (int)drive1); + vlog_errors += 1; } - if (strength_type == 2) fprintf(vlog_out, ", "); - if ((strength_type & 0x01) == 0) { - if (emit_drive(drive0)) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " - "0 drive (%d)\n", file, lineno, - type, (int)drive1); - vlog_errors += 1; - } - fprintf(vlog_out, "0"); + fprintf(vlog_out, "1, "); + if (emit_drive(drive0)) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " + "0 drive (%d)\n", file, lineno, + type, (int)drive0); + vlog_errors += 1; } - fprintf(vlog_out, ")"); + fprintf(vlog_out, "0)"); + } else if ((strength_type == 1) && (drive1 != IVL_DR_PULL)) { + fprintf(vlog_out, " ("); + if (emit_drive(drive1)) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " + "1 drive (%d)\n", file, lineno, + type, (int)drive1); + vlog_errors += 1; + } + fprintf(vlog_out, "1)"); + } else if ((strength_type == 0) && (drive0 != IVL_DR_PULL)) { + fprintf(vlog_out, " ("); + if (emit_drive(drive0)) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s " + "0 drive (%d)\n", file, lineno, + type, (int)drive0); + vlog_errors += 1; + } + fprintf(vlog_out, "0)"); } } @@ -108,28 +123,28 @@ static void emit_delay(ivl_scope_t scope, ivl_expr_t rise, ivl_expr_t fall, /* If all three delays match then we only have a single delay. */ if ((rise == fall) && (rise == decay)) { fprintf(vlog_out, " #("); - emit_scaled_delayx(scope, rise); + emit_scaled_delayx(scope, rise, 0); fprintf(vlog_out, ")"); return; } /* If we have a gate that only supports two delays then print them. */ if (dly_count == 2) { fprintf(vlog_out, " #("); - emit_scaled_delayx(scope, rise); + emit_scaled_delayx(scope, rise, 0); fprintf(vlog_out, ", "); - emit_scaled_delayx(scope, fall); + emit_scaled_delayx(scope, fall, 0); fprintf(vlog_out, ")"); return; } /* What's left is a gate that supports three delays. */ fprintf(vlog_out, " #("); - emit_scaled_delayx(scope, rise); + emit_scaled_delayx(scope, rise, 0); fprintf(vlog_out, ", "); - emit_scaled_delayx(scope, fall); + emit_scaled_delayx(scope, fall, 0); if (decay) { fprintf(vlog_out, ", "); - emit_scaled_delayx(scope, decay); + emit_scaled_delayx(scope, decay, 0); } fprintf(vlog_out, ")"); } @@ -215,7 +230,7 @@ static ivl_nexus_t get_lpm_output(ivl_scope_t scope, ivl_lpm_t lpm) static void emit_logic_as_ca(ivl_scope_t scope, ivl_net_logic_t nlogic); static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm); -static void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) +void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) { /* If there is no nexus then there is nothing to print. */ if (! nex) return; @@ -857,11 +872,13 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) case IVL_LO_PULLDOWN: fprintf(vlog_out, "pulldown"); dly_count = 0; + outputs = 0; strength_type = 0; break; case IVL_LO_PULLUP: fprintf(vlog_out, "pullup"); dly_count = 0; + outputs = 0; strength_type = 1; break; case IVL_LO_RCMOS: @@ -919,7 +936,13 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, idx)); fprintf(vlog_out, ", "); } - emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, count)); + if (strength_type == 2) { + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, idx)); + } else { + /* A pull gate only has a single output connection. */ + assert(count == 0); + emit_name_of_logic_nexus(scope, nlogic, ivl_logic_pin(nlogic, idx)); + } fprintf(vlog_out, ");"); emit_logic_file_line(nlogic); fprintf(vlog_out, "\n"); @@ -990,7 +1013,6 @@ void emit_tran(ivl_scope_t scope, ivl_switch_t tran) } fprintf(vlog_out, ");"); if (emit_file_line) { -assert(ivl_switch_lineno(tran)); fprintf(vlog_out, " /* %s:%u */", ivl_switch_file(tran), ivl_switch_lineno(tran)); diff --git a/tgt-vlog95/misc.c b/tgt-vlog95/misc.c index 9388ac90c..7c54e5a34 100644 --- a/tgt-vlog95/misc.c +++ b/tgt-vlog95/misc.c @@ -65,7 +65,7 @@ void emit_scaled_delay(ivl_scope_t scope, uint64_t delay) * Emit a constant or variable delay that has been rescaled to the given * scopes timescale. */ -void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr) +void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) { if (ivl_expr_type(expr) == IVL_EX_NUMBER) { assert(! ivl_expr_signed(expr)); @@ -109,6 +109,9 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr) emit_expr(scope, expr, 0); return; } else { +// HERE: If we have a statement delay then a real variable delay has already +// been encoded as int((real expr) * tu_tp_scale) * tp_sim_scale. So +// we need to look for that pattern as well. uint64_t scale_val, scale = 1; int rtype; assert(! ivl_expr_signed(expr)); @@ -154,7 +157,7 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr) scale *= 10; exponent -= 1; } - if (scale != scale_val) { + if (scale != scale_val) { fprintf(vlog_out, ""); fprintf(stderr, "%s:%u: vlog95 error: Variable time " "expression/value scale coefficient " diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index d7671fdcc..fe6648ca3 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -150,18 +150,20 @@ static unsigned emit_stmt_lval(ivl_scope_t scope, ivl_statement_t stmt) } /* - * Icarus translated = into + * Icarus translated = into * begin * = ; - * = ; + * = ; * end * This routine looks for this pattern and turns it back into the * appropriate blocking assignment. */ -static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) +static unsigned find_delayed_or_event_assign(ivl_scope_t scope, + ivl_statement_t stmt) { unsigned wid; ivl_statement_t assign, delay, delayed_assign; + ivl_statement_type_t delay_type; ivl_lval_t lval; ivl_expr_t rval; ivl_signal_t lsig, rsig; @@ -173,7 +175,9 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; /* The second must be a delayx. */ delay = ivl_stmt_block_stmt(stmt, 1); - if (ivl_statement_type(delay) != IVL_ST_DELAYX) return 0; + delay_type = ivl_statement_type(delay); + if ((delay_type != IVL_ST_DELAYX) && + (delay_type != IVL_ST_WAIT)) return 0; /* The statement for the delayx must be an assign. */ delayed_assign = ivl_stmt_sub_stmt(delay); if (ivl_statement_type(delayed_assign) != IVL_ST_ASSIGN) return 0; @@ -201,8 +205,14 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) // HERE: Do we need to calculate the width? The compiler should have already // done this for us. wid = emit_stmt_lval(scope, delayed_assign); - fprintf(vlog_out, " = #("); - emit_scaled_delayx(scope, ivl_stmt_delay_expr(delay)); + fprintf(vlog_out, " = "); + if (delay_type == IVL_ST_DELAYX) { + fprintf(vlog_out, "#("); + emit_scaled_delayx(scope, ivl_stmt_delay_expr(delay), 1); + } else { + fprintf(vlog_out, "@("); + emit_event(scope, delay); + } fprintf(vlog_out, ") "); emit_expr(scope, ivl_stmt_rval(assign), wid); fprintf(vlog_out, ";"); @@ -213,16 +223,17 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) } /* - * Icarus translated = into + * Icarus translated = repeat() into * begin * = ; - * ; + * repeat() ; * = ; * end * This routine looks for this pattern and turns it back into the * appropriate blocking assignment. */ -static unsigned find_event_assign(ivl_scope_t scope, ivl_statement_t stmt) +static unsigned find_repeat_event_assign(ivl_scope_t scope, + ivl_statement_t stmt) { unsigned wid; ivl_statement_t assign, event, event_assign, repeat = 0; @@ -237,8 +248,7 @@ static unsigned find_event_assign(ivl_scope_t scope, ivl_statement_t stmt) if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; /* The second must be a repeat with an event or an event. */ event = ivl_stmt_block_stmt(stmt, 1); - if (ivl_statement_type(event) != IVL_ST_REPEAT && - ivl_statement_type(event) != IVL_ST_WAIT) return 0; + if (ivl_statement_type(event) != IVL_ST_REPEAT) return 0; if (ivl_statement_type(event) == IVL_ST_REPEAT) { repeat = event; event = ivl_stmt_sub_stmt(repeat); @@ -704,7 +714,7 @@ static void emit_stmt_delay(ivl_scope_t scope, ivl_statement_t stmt) static void emit_stmt_delayx(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*c#(", get_indent(), ' '); - emit_scaled_delayx(scope, ivl_stmt_delay_expr(stmt)); + emit_scaled_delayx(scope, ivl_stmt_delay_expr(stmt), 1); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; @@ -880,8 +890,8 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) if (ivl_stmt_block_scope(stmt)) { emit_stmt_block_named(scope, stmt); } else { - if (find_delayed_assign(scope, stmt)) break; - if (find_event_assign(scope, stmt)) break; + if (find_delayed_or_event_assign(scope, stmt)) break; + if (find_repeat_event_assign(scope, stmt)) break; if (find_wait(scope, stmt)) break; emit_stmt_block(scope, stmt); } diff --git a/tgt-vlog95/vlog95_priv.h b/tgt-vlog95/vlog95_priv.h index ecf22da6c..bdca9b6a8 100644 --- a/tgt-vlog95/vlog95_priv.h +++ b/tgt-vlog95/vlog95_priv.h @@ -69,7 +69,8 @@ extern void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt); extern void emit_tran(ivl_scope_t scope, ivl_switch_t tran); extern void emit_scaled_delay(ivl_scope_t scope, uint64_t delay); -extern void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr); +extern void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, + unsigned is_stmt); extern void emit_scaled_expr(ivl_scope_t scope, ivl_expr_t expr, int msb, int lsb); extern void emit_scaled_range(ivl_scope_t scope, ivl_expr_t expr, @@ -78,6 +79,7 @@ extern void emit_scope_path(ivl_scope_t scope, ivl_scope_t call_scope); extern void emit_scope_variables(ivl_scope_t scope); extern void emit_scope_module_path(ivl_scope_t scope, ivl_scope_t call_scope); extern void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex); +extern void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex); extern void emit_const_nexus(ivl_scope_t scope, ivl_net_const_t const_net); extern void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig); extern void emit_icarus_generated_udps(); From 28c10311f6804be72e86064b40cfb9d0c7c1a41c Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 11 Feb 2011 09:30:42 -0800 Subject: [PATCH 02/35] vlog95: Rework delayx to handle a CA vs using emit_expr_signal(). It was incorrect to extend emit_expr_signal() to handle a local signal (a continuous assignment). The only way this should ever happen is when evaluating a variable delay expression. It is better to do this checking in the delayx evaluation where we know if we are processing a delay for a statement or a continuous assignment. This keeps the emit_expr_signal() routine cleaner and we only allow a CA delay for a continuous assignment. --- tgt-vlog95/expr.c | 20 ++++++++------------ tgt-vlog95/misc.c | 22 ++++++++++++++++++---- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index c42e0f456..3379194ca 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -351,18 +351,14 @@ static void emit_expr_sfunc(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) static void emit_expr_signal(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_signal_t sig = ivl_expr_signal(expr); - if (ivl_signal_local(sig)) { - emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0)); - } else { - emit_scope_module_path(scope, ivl_signal_scope(sig)); - fprintf(vlog_out, "%s", ivl_signal_basename(sig)); - if (ivl_signal_dimensions(sig)) { - int lsb = ivl_signal_array_base(sig); - int msb = lsb + ivl_signal_array_count(sig); - fprintf(vlog_out, "["); - emit_scaled_expr(scope, ivl_expr_oper1(expr), msb, lsb); - fprintf(vlog_out, "]"); - } + emit_scope_module_path(scope, ivl_signal_scope(sig)); + fprintf(vlog_out, "%s", ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + int lsb = ivl_signal_array_base(sig); + int msb = lsb + ivl_signal_array_count(sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, ivl_expr_oper1(expr), msb, lsb); + fprintf(vlog_out, "]"); } } diff --git a/tgt-vlog95/misc.c b/tgt-vlog95/misc.c index 7c54e5a34..b5e8f91b4 100644 --- a/tgt-vlog95/misc.c +++ b/tgt-vlog95/misc.c @@ -61,6 +61,21 @@ void emit_scaled_delay(ivl_scope_t scope, uint64_t delay) free(frac); } +static void emit_delay(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) +{ + /* A delay in a continuous assignment can also be a continuous + * assignment expression. */ + if (ivl_expr_type(expr) == IVL_EX_SIGNAL) { + ivl_signal_t sig = ivl_expr_signal(expr); + if (ivl_signal_local(sig)) { + assert(! is_stmt); + emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0)); + return; + } + } + emit_expr(scope, expr, 0); +} + /* * Emit a constant or variable delay that has been rescaled to the given * scopes timescale. @@ -93,7 +108,7 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) } else { int exponent = ivl_scope_time_units(scope) - sim_precision; assert(exponent >= 0); - if (exponent == 0) emit_expr(scope, expr, 0); + if (exponent == 0) emit_delay(scope, expr, is_stmt); /* A real delay variable is not scaled by the compiler. */ else if (ivl_expr_type(expr) == IVL_EX_SIGNAL) { ivl_signal_t sig = ivl_expr_signal(expr); @@ -106,8 +121,7 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) vlog_errors += 1; return; } - emit_expr(scope, expr, 0); - return; + emit_delay(scope, expr, is_stmt); } else { // HERE: If we have a statement delay then a real variable delay has already // been encoded as int((real expr) * tu_tp_scale) * tp_sim_scale. So @@ -169,7 +183,7 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) vlog_errors += 1; return; } - emit_expr(scope, ivl_expr_oper1(expr), 0); + emit_delay(scope, ivl_expr_oper1(expr), is_stmt); } } } From df85a3358309735aebe807940351745457f1df6f Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 11 Feb 2011 18:14:16 -0800 Subject: [PATCH 03/35] vlog95: Rework some of the delay emitting code and enclose unary in () This patch reworks the delay emitting code to handle delay expressions better. It also encloses the emitted unary operators in parenthesis to make sequenced unary operators work properly. --- tgt-vlog95/expr.c | 3 ++- tgt-vlog95/misc.c | 12 ++++++++---- tgt-vlog95/stmt.c | 6 +++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 3379194ca..f1fc3bbaf 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -404,8 +404,9 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) case 'N': case 'X': case '!': - fprintf(vlog_out, "%s", oper); + fprintf(vlog_out, "(%s", oper); emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ")"); break; case '2': case 'i': diff --git a/tgt-vlog95/misc.c b/tgt-vlog95/misc.c index b5e8f91b4..459c6b46f 100644 --- a/tgt-vlog95/misc.c +++ b/tgt-vlog95/misc.c @@ -82,7 +82,10 @@ static void emit_delay(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) */ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) { - if (ivl_expr_type(expr) == IVL_EX_NUMBER) { + ivl_expr_type_t type = ivl_expr_type(expr); + if (type == IVL_EX_DELAY) { + emit_scaled_delay(scope, ivl_expr_delay_val(expr)); + } else if (type == IVL_EX_NUMBER) { assert(! ivl_expr_signed(expr)); int rtype; uint64_t value = get_uint64_from_number(expr, &rtype); @@ -108,9 +111,10 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) } else { int exponent = ivl_scope_time_units(scope) - sim_precision; assert(exponent >= 0); - if (exponent == 0) emit_delay(scope, expr, is_stmt); - /* A real delay variable is not scaled by the compiler. */ - else if (ivl_expr_type(expr) == IVL_EX_SIGNAL) { + if ((exponent == 0) && (type == IVL_EX_SIGNAL)) { + emit_delay(scope, expr, is_stmt); + /* A real delay variable is not scaled by the compiler. */ + } else if (type == IVL_EX_SIGNAL) { ivl_signal_t sig = ivl_expr_signal(expr); if (ivl_signal_data_type(sig) != IVL_VT_REAL) { fprintf(vlog_out, ""); diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index fe6648ca3..20ae4fa21 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -80,9 +80,9 @@ static void emit_stmt_inter_delay(ivl_scope_t scope, ivl_statement_t stmt) } if (delay) { assert(nevents == 0); - fprintf(vlog_out, "#"); - emit_expr(scope, delay, 0); - fprintf(vlog_out, " "); + fprintf(vlog_out, "#("); + emit_scaled_delayx(scope, delay, 1); + fprintf(vlog_out, ") "); } } From e503c97ceecbfdf2b524a07eeba41a9eb2c1b696 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 13 Feb 2011 23:27:09 -0800 Subject: [PATCH 04/35] vlog95: Add support for emitting variable real procedural delays. This patch adds code to emit variable real procedural delays by removing the scaling that is added by the compiler. It also recognizes that all variable continuous assignment delays are scaled at run time. --- tgt-vlog95/misc.c | 197 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 137 insertions(+), 60 deletions(-) diff --git a/tgt-vlog95/misc.c b/tgt-vlog95/misc.c index 459c6b46f..af09b7932 100644 --- a/tgt-vlog95/misc.c +++ b/tgt-vlog95/misc.c @@ -76,6 +76,84 @@ static void emit_delay(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) emit_expr(scope, expr, 0); } +/* + * Check to see if the bit based expression is of the form (expr) * + */ +static unsigned check_scaled_expr(ivl_expr_t expr, uint64_t scale, + const char *msg, unsigned must_match) +{ + uint64_t scale_val; + int rtype; + if ((ivl_expr_type(expr) != IVL_EX_BINARY) || + (ivl_expr_opcode(expr) != '*') || + (ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_NUMBER)) { + fprintf(stderr, "%s:%u: vlog95 error: %s expression/value " + "cannot be scaled.\n ", + ivl_expr_file(expr), ivl_expr_lineno(expr), msg); + vlog_errors += 1; + return 0; + } + scale_val = get_uint64_from_number(ivl_expr_oper2(expr), &rtype); + if (rtype > 0) { + fprintf(stderr, "%s:%u: vlog95 error: %s expression/value " + "scale coefficient was greater then 64 bits " + "(%d).\n", ivl_expr_file(expr), + ivl_expr_lineno(expr), msg, rtype); + vlog_errors += 1; + return 0; + } + if (rtype < 0) { + fprintf(stderr, "%s:%u: vlog95 error: %s expression/value " + "scale coefficient has an undefined bit.\n", + ivl_expr_file(expr), ivl_expr_lineno(expr), msg); + vlog_errors += 1; + return 0; + } + if (scale != scale_val) { + if (must_match) { + fprintf(stderr, "%s:%u: vlog95 error: %s expression/value " + "scale coefficient did not match expected " + "value (%"PRIu64" != %"PRIu64").\n", + ivl_expr_file(expr), ivl_expr_lineno(expr), + msg, scale, scale_val); + vlog_errors += 1; + return 0; + } + return 2; + } + /* Yes, this expression is of the correct form. */ + return 1; +} + +/* + * Check to see if the real expression is of the form (expr) * + */ +static unsigned check_scaled_real_expr(ivl_expr_t expr, double scale) +{ + double scale_val; + if ((ivl_expr_type(expr) != IVL_EX_BINARY) || + (ivl_expr_opcode(expr) != '*') || + (ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_REALNUM)) { + fprintf(stderr, "%s:%u: vlog95 error: Variable real time unit " + " expression/value cannot be scaled.\n ", + ivl_expr_file(expr), ivl_expr_lineno(expr)); + vlog_errors += 1; + return 0; + } + scale_val = ivl_expr_dvalue(ivl_expr_oper2(expr)); + if (scale != scale_val) { + fprintf(stderr, "%s:%u: vlog95 error: Variable real time unit " + "expression/value scale coefficient did not " + "match expected value (%g != %g).\n", + ivl_expr_file(expr), ivl_expr_lineno(expr), + scale, scale_val); + vlog_errors += 1; + return 0; + } + /* Yes, this expression is of the correct form. */ + return 1; +} + /* * Emit a constant or variable delay that has been rescaled to the given * scopes timescale. @@ -115,79 +193,78 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt) emit_delay(scope, expr, is_stmt); /* A real delay variable is not scaled by the compiler. */ } else if (type == IVL_EX_SIGNAL) { - ivl_signal_t sig = ivl_expr_signal(expr); - if (ivl_signal_data_type(sig) != IVL_VT_REAL) { + if (is_stmt) { fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Only real " - "delay variables are scaled at run " - "time.\n", ivl_expr_file(expr), + fprintf(stderr, "%s:%u: vlog95 error: Only continuous " + "assignment delay variables are scaled " + "at run time.\n", ivl_expr_file(expr), ivl_expr_lineno(expr)); vlog_errors += 1; return; } emit_delay(scope, expr, is_stmt); } else { -// HERE: If we have a statement delay then a real variable delay has already -// been encoded as int((real expr) * tu_tp_scale) * tp_sim_scale. So -// we need to look for that pattern as well. - uint64_t scale_val, scale = 1; - int rtype; + uint64_t iscale = 1; + unsigned rtn; assert(! ivl_expr_signed(expr)); - /* This is as easy as removing the multiple that was - * added to scale the value to the simulation time, - * but we need to verify that the scaling value is - * correct first. */ - if ((ivl_expr_type(expr) != IVL_EX_BINARY) || - (ivl_expr_opcode(expr) != '*') || - (ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_NUMBER)) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Variable time " - "expression/value cannot be scaled.\n ", - ivl_expr_file(expr), - ivl_expr_lineno(expr)); - vlog_errors += 1; - return; - } - scale_val = get_uint64_from_number(ivl_expr_oper2(expr), - &rtype); - if (rtype > 0) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Variable time " - "expression/value scale coefficient " - "was greater then 64 bits (%d).\n", - ivl_expr_file(expr), - ivl_expr_lineno(expr), - rtype); - vlog_errors += 1; - return; - } - if (rtype < 0) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Variable time " - "expression/value scale coefficient " - "has an undefined bit.\n", - ivl_expr_file(expr), - ivl_expr_lineno(expr)); - vlog_errors += 1; - return; - } + /* Calculate the integer time scaling coefficient. */ while (exponent > 0) { - scale *= 10; + iscale *= 10; exponent -= 1; } - if (scale != scale_val) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 error: Variable time " - "expression/value scale coefficient " - "did not match expected value " - "(%"PRIu64" != %"PRIu64").\n", - ivl_expr_file(expr), - ivl_expr_lineno(expr), - scale, scale_val); - vlog_errors += 1; + /* Check to see if this is an integer time value. */ + rtn = check_scaled_expr(expr, iscale, "Variable time", 0); + /* This may be a scaled real value. */ + if (rtn == 2){ + ivl_expr_t tmp_expr; + uint64_t rprec = 1; + /* This could be a scaled real time so calculate + * the real time scaling coefficients and check + * that the expression matches (statements only). */ + exponent = ivl_scope_time_precision(scope) - + sim_precision; + assert(exponent >= 0); + while (exponent > 0) { + rprec *= 10; + exponent -= 1; + } + /* Verify that the precision scaling is correct. */ + if (! check_scaled_expr(expr, rprec, + "Variable real time prec.", + 1)) { + fprintf(vlog_out, ""); + return; + } + /* Verify that the left operator is a real to + * integer cast. */ + tmp_expr = ivl_expr_oper1(expr); + if ((ivl_expr_type(tmp_expr) != IVL_EX_UNARY) || + (ivl_expr_opcode(tmp_expr) != 'i')) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 error: Real time " + "value does not have a cast to " + "integer.\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr)); + vlog_errors += 1; + return; + } + /* Check that the cast value is scaled correctly. */ + assert(iscale >= rprec); + tmp_expr = ivl_expr_oper1(tmp_expr); + assert(ivl_expr_value(tmp_expr) == IVL_VT_REAL); + if (! check_scaled_real_expr(tmp_expr, iscale/rprec)) { + fprintf(vlog_out, ""); + return; + } + assert(is_stmt); + emit_delay(scope, ivl_expr_oper1(tmp_expr), is_stmt); + return; + } else if (rtn == 1) { + emit_delay(scope, ivl_expr_oper1(expr), is_stmt); return; } - emit_delay(scope, ivl_expr_oper1(expr), is_stmt); + fprintf(vlog_out, ""); } } } From 754457504054d32216ccbc3d67c79d8d09ac6700 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 13 Feb 2011 23:31:26 -0800 Subject: [PATCH 05/35] vlog95: Add preliminary logic gate name/range and assert logic pins in CA. Add a preliminary gate name and range (zero based) for logic primitives. Also assert that a logic gate in a continuous assignment has the correct number of inputs/outputs. --- tgt-vlog95/logic_lpm.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 7416d2883..9e2c29b2b 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -16,6 +16,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +# include +# include # include "config.h" # include "vlog95_priv.h" @@ -294,9 +296,10 @@ void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex) static void emit_logic_as_ca(ivl_scope_t scope, ivl_net_logic_t nlogic) { -// HERE: Do we need to check that the pin count is correct for these? + unsigned inputs = ivl_logic_pins(nlogic) - 1; switch (ivl_logic_type(nlogic)) { case IVL_LO_AND: + assert(inputs == 2); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " & "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); @@ -304,33 +307,40 @@ static void emit_logic_as_ca(ivl_scope_t scope, ivl_net_logic_t nlogic) case IVL_LO_BUF: // case IVL_LO_BUFT: case IVL_LO_BUFZ: + assert(inputs == 1); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); break; case IVL_LO_NAND: + assert(inputs == 2); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " ~& "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); break; case IVL_LO_NOR: + assert(inputs == 2); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " ~| "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); break; case IVL_LO_NOT: + assert(inputs == 1); fprintf(vlog_out, "~ "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); break; case IVL_LO_OR: + assert(inputs == 2); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); break; case IVL_LO_XNOR: + assert(inputs == 2); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " ~^ "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); break; case IVL_LO_XOR: + assert(inputs == 2); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1)); fprintf(vlog_out, " ^ "); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2)); @@ -558,6 +568,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) static void emit_posedge_dff_prim() { fprintf(vlog_out, "\n"); +// HERE: Add copyright info for this primitive. LGPL? fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized " "positive edge D-FF. */\n"); fprintf(vlog_out, "primitive IVL_posedge_DFF " @@ -803,9 +814,11 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) // gate only allows a signal or a signal bit select for the output(s) // and a scalar expression for the input. We also need to modify the // compiler to support logical 'and' and logical 'or' since they -// short circuit. +// short circuit. Verify input count. unsigned idx, count, dly_count, strength_type = 2; unsigned outputs = 1; + unsigned width = ivl_logic_width(nlogic); + const char *name; /* Skip gates that have a local nexus as the output since they are * part of a continuous assignment. */ if (is_local_nexus(scope, ivl_logic_pin(nlogic, 0))) return; @@ -922,8 +935,26 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) ivl_logic_delay(nlogic, 2), dly_count); // HERE: The name has the location information encoded in it. We need to -// remove this and rebuild the instance array. For now skip the name. -// fprintf(vlog_out, " %s(", ivl_logic_basename(nlogic)); +// remove this and rebuild the instance array. For now we just strip +// this encoding and create an zero based range. Need to skip the +// local names _s. + name = ivl_logic_basename(nlogic); + if (name) { + char *fixed_name = strdup(name); + unsigned lp = strlen(name) - 1; + if (fixed_name[lp] == '>') { + while (fixed_name[lp] != '<') { + assert(lp > 0); + lp -= 1; + } + fixed_name[lp] = 0; + } + fprintf(vlog_out, " %s", fixed_name); + free(fixed_name); + if (width > 1) { + fprintf(vlog_out, " [%u:0]", width-1); + } + } fprintf(vlog_out, " ("); count = ivl_logic_pins(nlogic); count -= 1; From 96a9cb8d543cffd2cd1db48944ea60199813fefd Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 14 Feb 2011 14:28:16 -0800 Subject: [PATCH 06/35] vlog95: Add a copyright notice for the Icarus UDPs and print a note. This patch adds copyright information for the Icarus generated UDPs (lesser GPL version 2.1). It also prints a note to let the user know that this copyrighted code is being included in the output file. They can then remove the primitives if needed. These are likely only needed when the output is from synthesis. At the moment we only have a positive edge D-FF. In the future a negative edge FF and D latches will also be needed. --- COPYING.lesser | 502 +++++++++++++++++++++++++++++++++++++++++ tgt-vlog95/logic_lpm.c | 41 +++- 2 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 COPYING.lesser diff --git a/COPYING.lesser b/COPYING.lesser new file mode 100644 index 000000000..4362b4915 --- /dev/null +++ b/COPYING.lesser @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 9e2c29b2b..64c5c690c 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -568,7 +568,6 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) static void emit_posedge_dff_prim() { fprintf(vlog_out, "\n"); -// HERE: Add copyright info for this primitive. LGPL? fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized " "positive edge D-FF. */\n"); fprintf(vlog_out, "primitive IVL_posedge_DFF " @@ -603,8 +602,48 @@ static void emit_posedge_dff_prim() static unsigned need_posedge_dff_prim = 0; +/* + * Synthesis creates a D-FF LPM object. To allow this to be simulated as + * Verilog we need to generate a D-FF UDP that is used to represent this + * LPM. Since this must be included with user derived code it must be + * licensed using the lesser GPL to avoid the requirement that their code + * also be licensed under the GPL. We print a note that LGPL code is + * being included in the output so the user can remove it if desired. + * + * The general idea with all this is that we want the user to be able to + * simulate a synthesized D-FF, etc., but we don't want them to take the + * ideas behind the primitive(s) and claim them as their own. + */ void emit_icarus_generated_udps() { + /* Emit the copyright information and LGPL note and then emit any + * needed primitives. */ + if (need_posedge_dff_prim) { + fprintf(vlog_out, +"\n" +"/*\n" +" * This is the copyright information for the following primitive(s)\n" +" * (library elements).\n" +" *\n" +" * Copyright (C) 2011 Cary R. (cygcary@yahoo.com)\n" +" *\n" +" * This library is free software; you can redistribute it and/or\n" +" * modify it under the terms of the GNU Lesser General Public\n" +" * License as published by the Free Software Foundation; either\n" +" * version 2.1 of the License, or (at your option) any later version.\n" +" *\n" +" * This library is distributed in the hope that it will be useful,\n" +" * but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +" * Lesser General Public License for more details.\n" +" *\n" +" * You should have received a copy of the GNU Lesser General Public\n" +" * License along with this library; if not, write to the Free Software\n" +" * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n" +" */\n"); + fprintf(stderr, +"NOTE: vlog95: Adding LGPL 2.1 primitive(s) at the end of the output file.\n"); + } if (need_posedge_dff_prim) emit_posedge_dff_prim(); } From 757611b8e9d1e39f15e528f233e3a92f9c4a32e5 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 18 Feb 2011 11:45:06 -0800 Subject: [PATCH 07/35] vlog95: Add partial support for procedural variable indexed part selects. This patch adds partial support of procedural variable indexed part selects (both R and L-value). The restriction is that this currently only works on a zero based little endian vector. I need to know if the select is an up or down select to correctly denormalize the base expression for the general case. --- tgt-vlog95/expr.c | 60 +++++++++++++++++++++++++++++-- tgt-vlog95/misc.c | 49 ++++++++------------------ tgt-vlog95/stmt.c | 90 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 147 insertions(+), 52 deletions(-) diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index f1fc3bbaf..e73bd6a8f 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -263,7 +263,7 @@ static unsigned emit_param_name_in_scope(ivl_scope_t scope, ivl_expr_t expr) static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { - /* A select of a number is reall a parameter select. */ + /* 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; @@ -285,6 +285,50 @@ static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) } } +/* + * Emit an indexed part select as a concatenation of bit selects. + */ +static void emit_expr_ips(ivl_scope_t scope, ivl_expr_t sig_expr, + ivl_expr_t sel_expr, unsigned wid, + unsigned msb, unsigned lsb) +{ + unsigned idx; + if ((msb >= lsb) && (lsb != 0)) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " + "select lsb must be zero.\n", + ivl_expr_file(sel_expr), + ivl_expr_lineno(sel_expr)); + vlog_errors += 1; + return; + } + if (msb < lsb) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " + "selects must be little endian.\n", + ivl_expr_file(sel_expr), + ivl_expr_lineno(sel_expr)); + vlog_errors += 1; + return; + } + fprintf(vlog_out, "{"); + for (idx = wid - 1; idx > 0; idx -= 1) { + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); +// HERE: Ideally we should simplify the scaled expression and the addition +// of the idx value. Also we should simplify any offset with the base +// expression as a compiler enhancement. All this will require a +// modified/updated version of emit_scaled_expr(). We can remove the +// above fails when this is finished. + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " + %u], ", idx); + } + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]}"); +} + static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_expr_t sel_expr = ivl_expr_oper2(expr); @@ -308,13 +352,23 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, ")" ); } else { - emit_select_name(scope, sig_expr, wid); + /* A bit select. */ if (width == 1) { + emit_select_name(scope, sig_expr, wid); fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); } else { - emit_scaled_range(scope, sel_expr, width, msb, lsb); + if (ivl_expr_type(sel_expr) == IVL_EX_NUMBER) { + /* A constant part select. */ + emit_select_name(scope, sig_expr, wid); + emit_scaled_range(scope, sel_expr, width, + msb, lsb); + } else { + /* An indexed part select. */ + emit_expr_ips(scope, sig_expr, sel_expr, width, + msb, lsb); + } } } } else { diff --git a/tgt-vlog95/misc.c b/tgt-vlog95/misc.c index af09b7932..e9a947a13 100644 --- a/tgt-vlog95/misc.c +++ b/tgt-vlog95/misc.c @@ -353,42 +353,21 @@ void emit_scaled_range(ivl_scope_t scope, ivl_expr_t expr, unsigned width, int msb, int lsb) { if (msb >= lsb) { - if (ivl_expr_type(expr) == IVL_EX_NUMBER) { - int rtype; - int64_t value = get_valid_int64_from_number(expr, &rtype, - "range value"); - if (rtype) return; - value += lsb; - fprintf(vlog_out, "[%"PRId64":%"PRId64"]", - value + (int64_t)(width - 1), value); - } else { -// HERE: Need to scale the select expression and create a concatenation of -// variable bit selects for that. We need the signal name as well. -// As an optimization determine if this is an up or down to simplify -// the generated expression. - fprintf(vlog_out, "[:]"); - fprintf(stderr, "%s:%u: vlog95 error: Indexed part-selects " - "are not currently supported.\n", - ivl_expr_file(expr), ivl_expr_lineno(expr)); - vlog_errors += 1; - } + int rtype; + int64_t value = get_valid_int64_from_number(expr, &rtype, + "range value"); + if (rtype) return; + value += lsb; + fprintf(vlog_out, "[%"PRId64":%"PRId64"]", + value + (int64_t)(width - 1), value); } else { - if (ivl_expr_type(expr) == IVL_EX_NUMBER) { - int rtype; - int64_t value = get_valid_int64_from_number(expr, &rtype, - "range value"); - if (rtype) return; - value = (int64_t)lsb - value; - fprintf(vlog_out, "[%"PRId64":%"PRId64"]", - value - (int64_t)(width - 1), value); - } else { -// HERE: Do basically the same as above. - fprintf(vlog_out, "[:]"); - fprintf(stderr, "%s:%u: vlog95 error: Indexed part-selects " - "are not currently supported.\n", - ivl_expr_file(expr), ivl_expr_lineno(expr)); - vlog_errors += 1; - } + int rtype; + int64_t value = get_valid_int64_from_number(expr, &rtype, + "range value"); + if (rtype) return; + value = (int64_t)lsb - value; + fprintf(vlog_out, "[%"PRId64":%"PRId64"]", + value - (int64_t)(width - 1), value); } } diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index 20ae4fa21..ddd5bf618 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -86,40 +86,102 @@ static void emit_stmt_inter_delay(ivl_scope_t scope, ivl_statement_t stmt) } } -static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) +static void emit_stmt_lval_name(ivl_scope_t scope, ivl_lval_t lval, + ivl_signal_t sig) { - ivl_expr_t expr; - ivl_signal_t sig = ivl_lval_sig(lval); - unsigned width = ivl_lval_width(lval); - int msb, lsb; - assert(width > 0); + ivl_expr_t array_idx = ivl_lval_idx(lval); emit_scope_module_path(scope, ivl_signal_scope(sig)); fprintf(vlog_out, "%s", ivl_signal_basename(sig)); - /* Check to see if we have an array word access. */ - expr = ivl_lval_idx(lval); - if (expr) { + if (array_idx) { + int msb, lsb; assert(ivl_signal_dimensions(sig)); fprintf(vlog_out, "["); /* For an array the LSB/MSB order is not important. They are * always accessed from base counting up. */ lsb = ivl_signal_array_base(sig); msb = lsb + ivl_signal_array_count(sig) - 1; - emit_scaled_expr(scope, expr, msb, lsb); + emit_scaled_expr(scope, array_idx, msb, lsb); fprintf(vlog_out, "]"); } +} - /* If there are no selects then just return. */ - if (width == ivl_signal_width(sig)) return; +static void emit_stmt_lval_ips(ivl_scope_t scope, ivl_lval_t lval, + ivl_signal_t sig, ivl_expr_t sel_expr, + unsigned wid, unsigned msb, unsigned lsb) +{ + unsigned idx; + if ((msb >= lsb) && (lsb != 0)) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " + "select lsb must be zero.\n", + ivl_expr_file(sel_expr), + ivl_expr_lineno(sel_expr)); + vlog_errors += 1; + return; + } + if (msb < lsb) { + fprintf(vlog_out, ""); + fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " + "selects must be little endian.\n", + ivl_expr_file(sel_expr), + ivl_expr_lineno(sel_expr)); + vlog_errors += 1; + return; + } + + fprintf(vlog_out, "{"); + for (idx = wid - 1; idx > 0; idx -= 1) { + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); +// HERE: Ideally we should simplify the scaled expression and the addition +// of the idx value. Also we should simplify any offset with the base +// expression as a compiler enhancement. All this will require a +// modified/updated version of emit_scaled_expr(). We can remove the +// above fails when this is finished. + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " + %u], ", idx); + } + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]}"); +} + +static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) +{ + ivl_signal_t sig = ivl_lval_sig(lval); + ivl_expr_t sel_expr; + unsigned width = ivl_lval_width(lval); + int msb, lsb; + assert(width > 0); + + /* If there are no selects then just print the name. */ + if (width == ivl_signal_width(sig)) { + emit_stmt_lval_name(scope, lval, sig); + return; + } /* We have some kind of select. */ lsb = ivl_signal_lsb(sig); msb = ivl_signal_msb(sig); + sel_expr = ivl_lval_part_off(lval); + assert(sel_expr); + /* A bit select. */ if (width == 1) { + emit_stmt_lval_name(scope, lval, sig); fprintf(vlog_out, "["); - emit_scaled_expr(scope, ivl_lval_part_off(lval), msb, lsb); + emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); } else { - emit_scaled_range(scope, ivl_lval_part_off(lval), width, msb, lsb); + /* A constant part select. */ + if (ivl_expr_type(sel_expr) == IVL_EX_NUMBER) { + emit_stmt_lval_name(scope, lval, sig); + emit_scaled_range(scope, sel_expr, width, msb, lsb); + /* An indexed part select. */ + } else { + emit_stmt_lval_ips(scope, lval, sig, sel_expr, width, + msb, lsb); + } } } From 53abd7a5b245a756242b47d43e6b6dfa3d3057e1 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 20 Feb 2011 10:57:27 -0800 Subject: [PATCH 08/35] Add support for getting the original port names of a UDP definition. This patch adds support to the compiler/ivl interface for getting the original UDP definition port names. ivl_udp_port() was added to get this information. --- ivl.def | 1 + ivl_target.h | 1 + net_udp.cc | 11 ++++++++++ netlist.h | 5 ++++- t-dll-api.cc | 8 +++++++ t-dll.cc | 60 +++++++++++++++++++++++++--------------------------- t-dll.h | 1 + 7 files changed, 55 insertions(+), 32 deletions(-) diff --git a/ivl.def b/ivl.def index 801c966e4..086d50b26 100644 --- a/ivl.def +++ b/ivl.def @@ -288,6 +288,7 @@ ivl_udp_file ivl_udp_lineno ivl_udp_name ivl_udp_nin +ivl_udp_port ivl_udp_row ivl_udp_rows ivl_udp_sequ diff --git a/ivl_target.h b/ivl_target.h index deb046635..fcde050c5 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1040,6 +1040,7 @@ extern unsigned ivl_udp_rows(ivl_udp_t net); extern const char* ivl_udp_name(ivl_udp_t net); extern const char* ivl_udp_file(ivl_udp_t net); extern unsigned ivl_udp_lineno(ivl_udp_t net); +extern const char* ivl_udp_port(ivl_udp_t net, unsigned idx); extern const char* ivl_lpm_file(ivl_lpm_t net); extern unsigned ivl_lpm_lineno(ivl_lpm_t net); diff --git a/net_udp.cc b/net_udp.cc index a2d7aa3d0..e9ca4e0bd 100644 --- a/net_udp.cc +++ b/net_udp.cc @@ -79,3 +79,14 @@ char NetUDP::get_initial() const assert(0); return 'x'; } + +unsigned NetUDP::port_count() const +{ + return udp->ports.count(); +} + +string NetUDP::port_name(unsigned idx) const +{ + assert(idx < udp->ports.count()); + return udp->ports[idx]; +} diff --git a/netlist.h b/netlist.h index 403ad6a4a..51fc92dd1 100644 --- a/netlist.h +++ b/netlist.h @@ -42,6 +42,7 @@ # include "LineInfo.h" # include "svector.h" # include "Attrib.h" +# include "PUdp.h" #ifdef HAVE_IOSFWD # include @@ -2144,7 +2145,6 @@ class NetUReduce : public NetNode { * 1 are listed. * */ -#include "PUdp.h" class NetUDP : public NetNode { @@ -2169,6 +2169,9 @@ class NetUDP : public NetNode { unsigned udp_lineno() const { return udp->get_lineno(); } char get_initial() const; + unsigned port_count() const; + string port_name(unsigned idx) const; + private: mutable unsigned table_idx; PUdp *udp; diff --git a/t-dll-api.cc b/t-dll-api.cc index 37c0255c4..49a640c19 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -867,6 +867,14 @@ extern "C" char ivl_udp_init(ivl_udp_t net) return net->init; } +extern "C" const char* ivl_udp_port(ivl_udp_t net, unsigned idx) +{ + assert(idx <= net->nin); + assert(net->ports); + assert(net->ports[idx].c_str()); + return net->ports[idx].c_str(); +} + extern "C" const char* ivl_udp_row(ivl_udp_t net, unsigned idx) { assert(idx < net->nrows); diff --git a/t-dll.cc b/t-dll.cc index 1141626d8..2ae7b0c93 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1270,38 +1270,36 @@ void dll_target::udp(const NetUDP*net) static map udps; ivl_udp_t u; - if (udps.find(net->udp_name()) != udps.end()) - { - u = udps[net->udp_name()]; - } - else - { - u = new struct ivl_udp_s; - u->nrows = net->rows(); - u->table = (ivl_udp_s::ccharp_t*)malloc((u->nrows+1)*sizeof(char*)); - u->table[u->nrows] = 0x0; - u->nin = net->nin(); - u->sequ = net->is_sequential(); - u->file = net->udp_file(); - u->lineno = net->udp_lineno(); - if (u->sequ) - u->init = net->get_initial(); - else - u->init = 'x'; - u->name = net->udp_name(); - string inp; - char out; - unsigned int i = 0; - if (net->first(inp, out)) - do - { - string tt = inp+out; - u->table[i++] = strings_.add(tt.c_str()); - } while (net->next(inp, out)); - assert(i==u->nrows); + if (udps.find(net->udp_name()) != udps.end()) { + u = udps[net->udp_name()]; + } else { + u = new struct ivl_udp_s; + u->nrows = net->rows(); + u->table = (ivl_udp_s::ccharp_t*)malloc((u->nrows+1)*sizeof(char*)); + u->table[u->nrows] = 0x0; + u->nin = net->nin(); + u->sequ = net->is_sequential(); + u->file = net->udp_file(); + u->lineno = net->udp_lineno(); + if (u->sequ) u->init = net->get_initial(); + else u->init = 'x'; + u->name = net->udp_name(); + string inp; + char out; + unsigned int i = 0; + if (net->first(inp, out)) do { + string tt = inp+out; + u->table[i++] = strings_.add(tt.c_str()); + } while (net->next(inp, out)); + assert(i==u->nrows); + assert((u->nin + 1) == net->port_count()); + u->ports = new string [u->nin + 1]; + for(unsigned idx = 0; idx <= u->nin; idx += 1) { + u->ports[idx] = net->port_name(idx); + } - udps[net->udp_name()] = u; - } + udps[net->udp_name()] = u; + } obj->udp = u; diff --git a/t-dll.h b/t-dll.h index 56aae7b02..0ff28e3da 100644 --- a/t-dll.h +++ b/t-dll.h @@ -517,6 +517,7 @@ struct ivl_udp_s { ccharp_t*table; // zero terminated array of pointers perm_string file; unsigned lineno; + string*ports; }; /* From 026fe92a461ae28e42565bed426979f0f9931528 Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 20 Feb 2011 10:59:54 -0800 Subject: [PATCH 09/35] vlog95: UDP definitions now use the original port names. This patch modifies the UDP definition emitting code to use the original port names instead of made up ones. --- tgt-vlog95/udp.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tgt-vlog95/udp.c b/tgt-vlog95/udp.c index 30cffd3b3..29a9acb30 100644 --- a/tgt-vlog95/udp.c +++ b/tgt-vlog95/udp.c @@ -184,23 +184,23 @@ static void emit_udp(ivl_udp_t udp) "file %s at line %u. */\n", ivl_udp_file(udp), ivl_udp_lineno(udp)); fprintf(vlog_out, "primitive %s (", ivl_udp_name(udp)); -// HERE: we need to keep and export the real pin names. - fprintf(vlog_out, "out"); + fprintf(vlog_out, "%s", ivl_udp_port(udp, 0)); count = ivl_udp_nin(udp); for (idx = 1; idx <= count; idx += 1) { - fprintf(vlog_out, ", in%u", idx); + fprintf(vlog_out, ", %s", ivl_udp_port(udp, idx)); } fprintf(vlog_out, ");\n"); -// HERE: we need to keep and export the real pin names. - fprintf(vlog_out, "%*coutput out;\n", indent_incr, ' '); + fprintf(vlog_out, "%*coutput %s;\n", indent_incr, ' ', + ivl_udp_port(udp, 0)); for (idx = 1; idx <= count; idx += 1) { - fprintf(vlog_out, "%*cinput in%u;\n", indent_incr, ' ', idx); + fprintf(vlog_out, "%*cinput %s;\n", indent_incr, ' ', + ivl_udp_port(udp, idx)); } if (ivl_udp_sequ(udp)) { char init = ivl_udp_init(udp); fprintf(vlog_out, "\n"); -// HERE: we need to keep and export the real pin names. - fprintf(vlog_out, "%*creg out;\n", indent_incr, ' '); + fprintf(vlog_out, "%*creg %s;\n", indent_incr, ' ', + ivl_udp_port(udp, 0)); switch (init) { case '0': case '1': @@ -209,9 +209,8 @@ static void emit_udp(ivl_udp_t udp) init = 'x'; break; } -// HERE: we need to keep and export the real pin names. - fprintf(vlog_out, "%*cinitial out = 1'b%c;\n", - indent_incr, ' ', init); + fprintf(vlog_out, "%*cinitial %s = 1'b%c;\n", + indent_incr, ' ', ivl_udp_port(udp, 0), init); } fprintf(vlog_out, "\n"); fprintf(vlog_out, "%*ctable\n", indent_incr, ' '); From a6220fe562555ccedd519e2a8611d7515379bfb6 Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 23 Feb 2011 09:15:36 -0800 Subject: [PATCH 10/35] Add support for passing variable indexed part select type information This patch modifies the compiler and the ivl interface to pass the type of indexed part select that is being represented (up/down) in a procedural L-value or R-value. This is needed by any back end that wants to correctly denormalize the (zero based) base expression. --- dup_expr.cc | 4 ++-- elab_expr.cc | 10 +++++----- elab_lval.cc | 5 ++++- ivl.def | 2 ++ ivl_target.h | 9 +++++++++ net_assign.cc | 9 ++++++++- net_expr.cc | 10 ++++++++-- netlist.h | 10 ++++++++-- t-dll-api.cc | 21 +++++++++++++++++++-- t-dll-expr.cc | 9 +++++---- t-dll-proc.cc | 2 ++ t-dll.h | 7 +++++++ 12 files changed, 79 insertions(+), 19 deletions(-) diff --git a/dup_expr.cc b/dup_expr.cc index ca65810e8..db7c2bead 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -95,7 +95,7 @@ NetESelect* NetESelect::dup_expr() const { NetESelect*tmp = new NetESelect(expr_->dup_expr(), base_? base_->dup_expr() : 0, - expr_width()); + expr_width(), sel_type_); assert(tmp); tmp->set_line(*this); return tmp; diff --git a/elab_expr.cc b/elab_expr.cc index b47b38278..878596e4e 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -2587,7 +2587,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_up_(Design*des, NetScope*scope, base = normalize_variable_base(base, par_msv, par_lsv, wid, true); NetExpr*tmp = par->dup_expr(); - tmp = new NetESelect(tmp, base, wid); + tmp = new NetESelect(tmp, base, wid, IVL_SEL_IDX_UP); tmp->set_line(*this); return tmp; } @@ -2667,7 +2667,7 @@ NetExpr* PEIdent::elaborate_expr_param_idx_do_(Design*des, NetScope*scope, base = normalize_variable_base(base, par_msv, par_lsv, wid, false); NetExpr*tmp = par->dup_expr(); - tmp = new NetESelect(tmp, base, wid); + tmp = new NetESelect(tmp, base, wid, IVL_SEL_IDX_DOWN); tmp->set_line(*this); return tmp; } @@ -3176,7 +3176,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, base = normalize_variable_base(base, net->msi(), net->lsi(), wid, true); - NetESelect*ss = new NetESelect(net, base, wid); + NetESelect*ss = new NetESelect(net, base, wid, IVL_SEL_IDX_UP); ss->set_line(*this); if (debug_elaborate) { @@ -3263,7 +3263,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, base = normalize_variable_base(base, net->msi(), net->lsi(), wid, false); - NetESelect*ss = new NetESelect(net, base, wid); + NetESelect*ss = new NetESelect(net, base, wid, IVL_SEL_IDX_DOWN); ss->set_line(*this); if (debug_elaborate) { diff --git a/elab_lval.cc b/elab_lval.cc index bc785a299..5104ac48d 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -470,6 +470,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, expr_type_tmp, unsized_flag_tmp); NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1); + ivl_select_type_t sel_type = IVL_SEL_OTHER; // Handle the special case that the base is constant. For this // case we can reduce the expression. @@ -532,10 +533,12 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, if (use_sel == index_component_t::SEL_IDX_UP) { base = normalize_variable_base(base, reg->msb(), reg->lsb(), wid, true); + sel_type = IVL_SEL_IDX_UP; } else { // This is assumed to be a SEL_IDX_DO. base = normalize_variable_base(base, reg->msb(), reg->lsb(), wid, false); + sel_type = IVL_SEL_IDX_DOWN; } } @@ -543,7 +546,7 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, cerr << get_fileline() << ": debug: Set part select width=" << wid << ", base=" << *base << endl; - lv->set_part(base, wid); + lv->set_part(base, wid, sel_type); return true; } diff --git a/ivl.def b/ivl.def index 086d50b26..2eceb62dd 100644 --- a/ivl.def +++ b/ivl.def @@ -68,6 +68,7 @@ ivl_expr_parm ivl_expr_parms ivl_expr_repeat ivl_expr_scope +ivl_expr_sel_type ivl_expr_signal ivl_expr_signed ivl_expr_sized @@ -134,6 +135,7 @@ ivl_lpm_width ivl_lval_idx ivl_lval_mux ivl_lval_part_off +ivl_lval_sel_type ivl_lval_sig ivl_lval_width diff --git a/ivl_target.h b/ivl_target.h index fcde050c5..41cf61d9c 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -229,6 +229,12 @@ typedef enum ivl_expr_type_e { IVL_EX_UNARY = 14 } ivl_expr_type_t; +typedef enum ivl_select_type_e { + IVL_SEL_OTHER = 0, + IVL_SEL_IDX_UP = 1, + IVL_SEL_IDX_DOWN = 2 +} ivl_select_type_t; + /* This is the type code for an ivl_net_logic_t object. */ typedef enum ivl_logic_e { IVL_LO_NONE = 0, @@ -844,6 +850,8 @@ extern ivl_expr_t ivl_expr_parm(ivl_expr_t net, unsigned idx); extern unsigned ivl_expr_parms(ivl_expr_t net); /* IVL_EX_CONCAT */ extern unsigned ivl_expr_repeat(ivl_expr_t net); + /* IVL_EX_SELECT */ +extern ivl_select_type_t ivl_expr_sel_type(ivl_expr_t net); /* IVL_EX_EVENT */ extern ivl_event_t ivl_expr_event(ivl_expr_t net); /* IVL_EX_SCOPE */ @@ -1409,6 +1417,7 @@ extern unsigned ivl_lval_width(ivl_lval_t net); extern ivl_expr_t ivl_lval_mux(ivl_lval_t net); /* XXXX Obsolete? */ extern ivl_expr_t ivl_lval_idx(ivl_lval_t net); extern ivl_expr_t ivl_lval_part_off(ivl_lval_t net); +extern ivl_select_type_t ivl_lval_sel_type(ivl_lval_t net); extern ivl_signal_t ivl_lval_sig(ivl_lval_t net); diff --git a/net_assign.cc b/net_assign.cc index 48e3adc39..0ea1a2b2e 100644 --- a/net_assign.cc +++ b/net_assign.cc @@ -77,6 +77,11 @@ const NetExpr* NetAssign_::get_base() const return base_; } +ivl_select_type_t NetAssign_::select_type() const +{ + return sel_type_; +} + unsigned NetAssign_::lwidth() const { return lwid_; @@ -120,10 +125,12 @@ NetNet* NetAssign_::sig() const return sig_; } -void NetAssign_::set_part(NetExpr*base, unsigned wid) +void NetAssign_::set_part(NetExpr*base, unsigned wid, + ivl_select_type_t sel_type) { base_ = base; lwid_ = wid; + sel_type_ = sel_type; } /* diff --git a/net_expr.cc b/net_expr.cc index b31bf1c45..f54da9ea2 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -524,8 +524,9 @@ netenum_t* NetENetenum::netenum() const return netenum_; } -NetESelect::NetESelect(NetExpr*exp, NetExpr*base, unsigned wid) -: expr_(exp), base_(base) +NetESelect::NetESelect(NetExpr*exp, NetExpr*base, unsigned wid, + ivl_select_type_t sel_type) +: expr_(exp), base_(base), sel_type_(sel_type) { expr_width(wid); } @@ -546,6 +547,11 @@ const NetExpr*NetESelect::select() const return base_; } +ivl_select_type_t NetESelect::select_type() const +{ + return sel_type_; +} + bool NetESelect::has_width() const { return true; diff --git a/netlist.h b/netlist.h index 51fc92dd1..676a348fd 100644 --- a/netlist.h +++ b/netlist.h @@ -2301,9 +2301,11 @@ class NetAssign_ { // Get the base index of the part select, or 0 if there is no // part select. const NetExpr* get_base() const; + ivl_select_type_t select_type() const; void set_word(NetExpr*); - void set_part(NetExpr* loff, unsigned wid); + void set_part(NetExpr* loff, unsigned wid, + ivl_select_type_t = IVL_SEL_OTHER); // Get the width of the r-value that this node expects. This // method accounts for the presence of the mux, so it is not @@ -2345,6 +2347,7 @@ class NetAssign_ { // indexed part select base NetExpr*base_; unsigned lwid_; + ivl_select_type_t sel_type_; }; class NetAssignBase : public NetProc { @@ -3644,11 +3647,13 @@ class NetEConcat : public NetExpr { class NetESelect : public NetExpr { public: - NetESelect(NetExpr*exp, NetExpr*base, unsigned wid); + NetESelect(NetExpr*exp, NetExpr*base, unsigned wid, + ivl_select_type_t sel_type = IVL_SEL_OTHER); ~NetESelect(); const NetExpr*sub_expr() const; const NetExpr*select() const; + ivl_select_type_t select_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual bool set_width(unsigned w, bool last_chance =false); @@ -3662,6 +3667,7 @@ class NetESelect : public NetExpr { private: NetExpr*expr_; NetExpr*base_; + ivl_select_type_t sel_type_; }; /* diff --git a/t-dll-api.cc b/t-dll-api.cc index 49a640c19..ad0efc8ba 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -436,9 +436,11 @@ extern "C" ivl_expr_t ivl_expr_oper1(ivl_expr_t net) assert(net); switch (net->type_) { case IVL_EX_BINARY: - case IVL_EX_SELECT: return net->u_.binary_.lef_; + case IVL_EX_SELECT: + return net->u_.select_.expr_; + case IVL_EX_UNARY: return net->u_.unary_.sub_; @@ -463,9 +465,11 @@ extern "C" ivl_expr_t ivl_expr_oper2(ivl_expr_t net) assert(net); switch (net->type_) { case IVL_EX_BINARY: - case IVL_EX_SELECT: return net->u_.binary_.rig_; + case IVL_EX_SELECT: + return net->u_.select_.base_; + case IVL_EX_TERNARY: return net->u_.ternary_.true_e; @@ -569,6 +573,13 @@ extern "C" ivl_scope_t ivl_expr_scope(ivl_expr_t net) return net->u_.scope_.scope; } +extern "C" ivl_select_type_t ivl_expr_sel_type(ivl_expr_t net) +{ + assert(net); + assert(net->type_ == IVL_EX_SELECT); + return net->u_.select_.sel_type_; +} + extern "C" ivl_signal_t ivl_expr_signal(ivl_expr_t net) { assert(net); @@ -1467,6 +1478,12 @@ extern "C" ivl_expr_t ivl_lval_part_off(ivl_lval_t net) return net->loff; } +extern "C" ivl_select_type_t ivl_lval_sel_type(ivl_lval_t net) +{ + assert(net); + return net->sel_type; +} + extern "C" unsigned ivl_lval_width(ivl_lval_t net) { assert(net); diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 3c3b3030a..99fba7a13 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -359,13 +359,13 @@ void dll_target::expr_select(const NetESelect*net) assert(expr_ == 0); net->sub_expr()->expr_scan(this); - ivl_expr_t left = expr_; + ivl_expr_t expr = expr_; expr_ = 0; if (net->select()) net->select()->expr_scan(this); - ivl_expr_t rght = expr_; + ivl_expr_t base = expr_; expr_ = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); @@ -376,8 +376,9 @@ void dll_target::expr_select(const NetESelect*net) expr_->sized_= 1; FILE_NAME(expr_, net); - expr_->u_.binary_.lef_ = left; - expr_->u_.binary_.rig_ = rght; + expr_->u_.select_.sel_type_ = net->select_type(); + expr_->u_.select_.expr_ = expr; + expr_->u_.select_.base_ = base; } void dll_target::expr_sfunc(const NetESFunc*net) diff --git a/t-dll-proc.cc b/t-dll-proc.cc index 0f5fb8852..bb75c982f 100644 --- a/t-dll-proc.cc +++ b/t-dll-proc.cc @@ -153,9 +153,11 @@ void dll_target::make_assign_lvals_(const NetAssignBase*net) if (loff == 0) { cur->loff = 0; + cur->sel_type = IVL_SEL_OTHER; } else { loff->expr_scan(this); cur->loff = expr_; + cur->sel_type = asn->select_type(); expr_ = 0; } diff --git a/t-dll.h b/t-dll.h index 0ff28e3da..db0828627 100644 --- a/t-dll.h +++ b/t-dll.h @@ -231,6 +231,12 @@ struct ivl_expr_s { ivl_expr_t rig_; } binary_; + struct { + ivl_select_type_t sel_type_; + ivl_expr_t expr_; + ivl_expr_t base_; + } select_; + struct { ivl_branch_t branch; ivl_nature_t nature; @@ -427,6 +433,7 @@ enum ivl_lval_type_t { struct ivl_lval_s { ivl_expr_t loff; + ivl_select_type_t sel_type; ivl_expr_t idx; unsigned width_; unsigned type_ : 8; From b95e46e6216cb1a80736ed33cd537cf33c953372 Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 23 Feb 2011 09:16:55 -0800 Subject: [PATCH 11/35] vlog95: Add more procedural variable indexed part select code This patch adds the rest of the functionality needed to correctly emit both L-value and R-value procedural variable indexed part selects as a concatenation of variable bit selects. --- tgt-vlog95/expr.c | 121 ++++++++++++++++++++++++++--------------- tgt-vlog95/logic_lpm.c | 2 +- tgt-vlog95/stmt.c | 112 +++++++++++++++++++++++++------------- 3 files changed, 152 insertions(+), 83 deletions(-) diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index e73bd6a8f..79aad04b1 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -289,69 +289,101 @@ static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) * Emit an indexed part select as a concatenation of bit selects. */ static void emit_expr_ips(ivl_scope_t scope, ivl_expr_t sig_expr, - ivl_expr_t sel_expr, unsigned wid, - unsigned msb, unsigned lsb) + ivl_expr_t sel_expr, ivl_select_type_t sel_type, + unsigned wid, int msb, int lsb) { unsigned idx; - if ((msb >= lsb) && (lsb != 0)) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " - "select lsb must be zero.\n", - ivl_expr_file(sel_expr), - ivl_expr_lineno(sel_expr)); - vlog_errors += 1; - return; - } - if (msb < lsb) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " - "selects must be little endian.\n", - ivl_expr_file(sel_expr), - ivl_expr_lineno(sel_expr)); - vlog_errors += 1; - return; - } + assert(wid > 0); fprintf(vlog_out, "{"); - for (idx = wid - 1; idx > 0; idx -= 1) { - emit_select_name(scope, sig_expr, wid); - fprintf(vlog_out, "["); -// HERE: Ideally we should simplify the scaled expression and the addition -// of the idx value. Also we should simplify any offset with the base -// expression as a compiler enhancement. All this will require a -// modified/updated version of emit_scaled_expr(). We can remove the -// above fails when this is finished. - emit_scaled_expr(scope, sel_expr, msb, lsb); - fprintf(vlog_out, " + %u], ", idx); + if (msb >= lsb) { + if (sel_type == IVL_SEL_IDX_DOWN) { + lsb += wid - 1; + msb += wid - 1; + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]"); + for (idx = 1; idx < wid; idx += 1) { + fprintf(vlog_out, ", "); + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " - %u]", idx); + } + fprintf(vlog_out, "}"); + } else { + assert(sel_type == IVL_SEL_IDX_UP); + for (idx = wid - 1; idx > 0; idx -= 1) { + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " + %u], ", idx); + } + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]}"); + } + } else { + if (sel_type == IVL_SEL_IDX_UP) { + lsb -= wid - 1; + msb -= wid - 1; + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]"); + for (idx = 1; idx < wid; idx += 1) { + fprintf(vlog_out, ", "); + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " + %u]", idx); + } + fprintf(vlog_out, "}"); + } else { + assert(sel_type == IVL_SEL_IDX_DOWN); + for (idx = wid - 1; idx > 0; idx -= 1) { + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " - %u], ", idx); + } + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]}"); + } } - emit_select_name(scope, sig_expr, wid); - fprintf(vlog_out, "["); - emit_scaled_expr(scope, sel_expr, msb, lsb); - fprintf(vlog_out, "]}"); } static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) { ivl_expr_t sel_expr = ivl_expr_oper2(expr); ivl_expr_t sig_expr = ivl_expr_oper1(expr); + ivl_select_type_t sel_type = ivl_expr_sel_type(expr); if (sel_expr) { - int msb = 1; - int lsb = 0; unsigned width = ivl_expr_width(expr); ivl_expr_type_t type = ivl_expr_type(sig_expr); assert(width > 0); - if (type == IVL_EX_SIGNAL) { - ivl_signal_t sig = ivl_expr_signal(sig_expr); - msb = ivl_signal_msb(sig); - lsb = ivl_signal_lsb(sig); - } /* The compiler uses selects for some shifts. */ if (type != IVL_EX_NUMBER && type != IVL_EX_SIGNAL) { fprintf(vlog_out, "(" ); emit_select_name(scope, sig_expr, wid); fprintf(vlog_out, " >> " ); - emit_scaled_expr(scope, sel_expr, msb, lsb); + emit_scaled_expr(scope, sel_expr, 1, 0); fprintf(vlog_out, ")" ); } else { + int msb; + int lsb; + if (type == IVL_EX_SIGNAL) { + ivl_signal_t sig = ivl_expr_signal(sig_expr); + msb = ivl_signal_msb(sig); + lsb = ivl_signal_lsb(sig); + } else { +// HERE: Need to get the parameter and then the MSB/LSB information. + msb = 1; + lsb = 0; + } /* A bit select. */ if (width == 1) { emit_select_name(scope, sig_expr, wid); @@ -366,8 +398,9 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) msb, lsb); } else { /* An indexed part select. */ - emit_expr_ips(scope, sig_expr, sel_expr, width, - msb, lsb); + assert(sel_type != IVL_SEL_OTHER); + emit_expr_ips(scope, sig_expr, sel_expr, + sel_type, width, msb, lsb); } } } diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 64c5c690c..b3e66c3cb 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -359,7 +359,7 @@ static void emit_lpm_array(ivl_scope_t scope, ivl_lpm_t lpm) ivl_signal_t sig = ivl_lpm_array(lpm); emit_scope_module_path(scope, ivl_signal_scope(sig)); fprintf(vlog_out, "%s[", ivl_signal_basename(sig)); -// HERE : Need to scale this to match array base. +// HERE: Need to scale this to match array base. emit_nexus_as_ca(scope, ivl_lpm_select(lpm)); fprintf(vlog_out, "]"); } diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index ddd5bf618..4d0f9cef9 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -107,50 +107,78 @@ static void emit_stmt_lval_name(ivl_scope_t scope, ivl_lval_t lval, static void emit_stmt_lval_ips(ivl_scope_t scope, ivl_lval_t lval, ivl_signal_t sig, ivl_expr_t sel_expr, - unsigned wid, unsigned msb, unsigned lsb) + ivl_select_type_t sel_type, + unsigned wid, int msb, int lsb) { unsigned idx; - if ((msb >= lsb) && (lsb != 0)) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " - "select lsb must be zero.\n", - ivl_expr_file(sel_expr), - ivl_expr_lineno(sel_expr)); - vlog_errors += 1; - return; - } - if (msb < lsb) { - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 sorry: Variable indexed part " - "selects must be little endian.\n", - ivl_expr_file(sel_expr), - ivl_expr_lineno(sel_expr)); - vlog_errors += 1; - return; - } - + assert(wid > 0); fprintf(vlog_out, "{"); - for (idx = wid - 1; idx > 0; idx -= 1) { - emit_stmt_lval_name(scope, lval, sig); - fprintf(vlog_out, "["); -// HERE: Ideally we should simplify the scaled expression and the addition -// of the idx value. Also we should simplify any offset with the base -// expression as a compiler enhancement. All this will require a -// modified/updated version of emit_scaled_expr(). We can remove the -// above fails when this is finished. - emit_scaled_expr(scope, sel_expr, msb, lsb); - fprintf(vlog_out, " + %u], ", idx); + if (msb >= lsb) { + if (sel_type == IVL_SEL_IDX_DOWN) { + lsb += wid - 1; + msb += wid - 1; + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]"); + for (idx = 1; idx < wid; idx += 1) { + fprintf(vlog_out, ", "); + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " - %u]", idx); + } + fprintf(vlog_out, "}"); + } else { + assert(sel_type == IVL_SEL_IDX_UP); + for (idx = wid - 1; idx > 0; idx -= 1) { + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " + %u], ", idx); + } + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]}"); + } + } else { + if (sel_type == IVL_SEL_IDX_UP) { + lsb -= wid - 1; + msb -= wid - 1; + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]"); + for (idx = 1; idx < wid; idx += 1) { + fprintf(vlog_out, ", "); + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " + %u]", idx); + } + fprintf(vlog_out, "}"); + } else { + assert(sel_type == IVL_SEL_IDX_DOWN); + for (idx = wid - 1; idx > 0; idx -= 1) { + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, " - %u], ", idx); + } + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_scaled_expr(scope, sel_expr, msb, lsb); + fprintf(vlog_out, "]}"); + } } - emit_stmt_lval_name(scope, lval, sig); - fprintf(vlog_out, "["); - emit_scaled_expr(scope, sel_expr, msb, lsb); - fprintf(vlog_out, "]}"); } static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) { ivl_signal_t sig = ivl_lval_sig(lval); ivl_expr_t sel_expr; + ivl_select_type_t sel_type; unsigned width = ivl_lval_width(lval); int msb, lsb; assert(width > 0); @@ -165,6 +193,7 @@ static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) lsb = ivl_signal_lsb(sig); msb = ivl_signal_msb(sig); sel_expr = ivl_lval_part_off(lval); + sel_type = ivl_lval_sel_type(lval); assert(sel_expr); /* A bit select. */ if (width == 1) { @@ -179,8 +208,9 @@ static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) emit_scaled_range(scope, sel_expr, width, msb, lsb); /* An indexed part select. */ } else { - emit_stmt_lval_ips(scope, lval, sig, sel_expr, width, - msb, lsb); + assert(sel_type != IVL_SEL_OTHER); + emit_stmt_lval_ips(scope, lval, sig, sel_expr, sel_type, + width, msb, lsb); } } } @@ -1035,6 +1065,7 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt) void emit_process(ivl_scope_t scope, ivl_process_t proc) { + ivl_statement_t stmt; fprintf(vlog_out, "\n%*c", get_indent(), ' '); switch (ivl_process_type(proc)) { case IVL_PR_INITIAL: @@ -1056,6 +1087,11 @@ void emit_process(ivl_scope_t scope, ivl_process_t proc) ivl_process_file(proc), ivl_process_lineno(proc)); } - single_indent = 1; - emit_stmt(scope, ivl_process_stmt(proc)); + stmt = ivl_process_stmt(proc); + if (ivl_statement_type(stmt) == IVL_ST_NOOP) { + fprintf(vlog_out, " begin\n%*cend\n", get_indent(), ' '); + } else { + single_indent = 1; + emit_stmt(scope, stmt); + } } From c90a8964485a4bbc7fe472fff93b1d16a1ce4644 Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 23 Feb 2011 11:06:29 -0800 Subject: [PATCH 12/35] vlog95: Don't print a logic/UPD NULL name. If the logic/UDP name is NULL then don't print anything. --- tgt-vlog95/logic_lpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index b3e66c3cb..1feffb16d 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -978,7 +978,7 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) // this encoding and create an zero based range. Need to skip the // local names _s. name = ivl_logic_basename(nlogic); - if (name) { + if (name && *name) { char *fixed_name = strdup(name); unsigned lp = strlen(name) - 1; if (fixed_name[lp] == '>') { From 468fd3d6839b988f23a3c8fd7051cf6f03e438e9 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 24 Feb 2011 17:37:56 -0800 Subject: [PATCH 13/35] Allows multiple attribute instances. This patch adds the ability to have multiple attribute instances (e.g. (* foo = 1 *) (* bar = 2 *)). --- parse.y | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/parse.y b/parse.y index 9ad2c3e4f..75ff5b473 100644 --- a/parse.y +++ b/parse.y @@ -426,7 +426,7 @@ static list* make_named_number(perm_string name, PExpr*val =0) %type port_name_list parameter_value_byname_list %type attribute -%type attribute_list attribute_list_opt +%type attribute_list attribute_instance_list attribute_list_opt %type case_item %type case_items @@ -522,11 +522,24 @@ real_or_realtime variety of different objects. The syntax inside the (* *) is a comma separated list of names or names with assigned values. */ attribute_list_opt - : K_PSTAR attribute_list K_STARP { $$ = $2; } - | K_PSTAR K_STARP { $$ = 0; } + : attribute_instance_list | { $$ = 0; } ; +attribute_instance_list + : K_PSTAR K_STARP { $$ = 0; } + | K_PSTAR attribute_list K_STARP { $$ = $2; } + | attribute_instance_list K_PSTAR K_STARP { $$ = $1; } + | attribute_instance_list K_PSTAR attribute_list K_STARP + { list*tmp = $1; + if (tmp) { + tmp->splice(tmp->end(), *$3); + delete $3; + $$ = tmp; + } else $$ = $3; + } + ; + attribute_list : attribute_list ',' attribute { list*tmp = $1; From fc3118bd7f110d4c6c3dbdd63167b1e81ff67dbc Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 24 Feb 2011 20:58:15 -0800 Subject: [PATCH 14/35] Don't delete L-value concat signals and give a better error message. This patch removes the code that was deleting the signals in an L-value concat if the concatenation failed to elaborate. This is incorrect since the signal could be used in another context that is processed later. The error message when an output/inout port fails to elaborate was also improved to give the expression that is connected to the port. --- elab_net.cc | 5 ----- elaborate.cc | 10 ++++++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/elab_net.cc b/elab_net.cc index 452ef8e6a..05cbaf525 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -84,12 +84,7 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, } - /* If any of the sub expressions failed to elaborate, then - delete all those that did and abort myself. */ if (errors) { - for (unsigned idx = 0 ; idx < nets.count() ; idx += 1) { - if (nets[idx]) delete nets[idx]; - } des->errors += errors; return 0; } diff --git a/elaborate.cc b/elaborate.cc index 64dcdd7b0..0ecc01b0f 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -1411,8 +1411,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const << "Inout port expression must support " << "continuous assignment." << endl; cerr << pins[idx]->get_fileline() << ": : " - << "Port of " << rmod->mod_name() - << " is " << rmod->ports[idx]->name << endl; + << "Port " << rmod->ports[idx]->name << " of " + << rmod->mod_name() << " is connected to " + << *pins[idx] << endl; des->errors += 1; continue; } @@ -1457,8 +1458,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const << "Output port expression must support " << "continuous assignment." << endl; cerr << pins[idx]->get_fileline() << ": : " - << "Port of " << rmod->mod_name() - << " is " << rmod->ports[idx]->name << endl; + << "Port " << rmod->ports[idx]->name << " of " + << rmod->mod_name() << " is connected to " + << *pins[idx] << endl; des->errors += 1; continue; } From 2df68508243099b8e0ed7306efbc9593e7b1ee53 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 25 Feb 2011 18:18:16 -0800 Subject: [PATCH 15/35] vlog95: A parameter in 1364-1995 is always zero based. The 1364-1995 version of Verilog does not support non zero based parameters. This patch removes the comment requesting the parameter MSB/LSB information to denormalize the expression and replaces it with one stating that we need to keep the compiler normalization. --- tgt-vlog95/expr.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 79aad04b1..e5bad93e1 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -280,6 +280,7 @@ static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) 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); } @@ -373,16 +374,16 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) emit_scaled_expr(scope, sel_expr, 1, 0); fprintf(vlog_out, ")" ); } else { - int msb; - int lsb; + /* A constant/parameter must be zero based in 1364-1995 + * so keep the compiler generated normalization. This + * does not always work for selects before the parameter + * since 1364-1995 does not support signed math. */ + int msb = 1; + int lsb = 0; if (type == IVL_EX_SIGNAL) { ivl_signal_t sig = ivl_expr_signal(sig_expr); msb = ivl_signal_msb(sig); lsb = ivl_signal_lsb(sig); - } else { -// HERE: Need to get the parameter and then the MSB/LSB information. - msb = 1; - lsb = 0; } /* A bit select. */ if (width == 1) { From 312b4da46fe94a2af619b3c95f0503334bf19a4e Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sat, 26 Feb 2011 22:59:52 +0000 Subject: [PATCH 16/35] Expression width rework. This patch is a major rework of expression elaboration and evaluation in the compiler, aimed at better compliance with the IEEE standard. --- Makefile.in | 2 +- PDelays.cc | 8 +- PExpr.cc | 25 +- PExpr.h | 260 +++--- design_dump.cc | 8 +- dup_expr.cc | 146 +++- elab_expr.cc | 1865 ++++++++++++++++++------------------------- elab_lval.cc | 20 +- elab_net.cc | 12 - elab_scope.cc | 18 +- elab_sig.cc | 13 +- elaborate.cc | 169 +--- elaborate_analog.cc | 4 +- eval_tree.cc | 318 +++----- net_design.cc | 41 +- net_expr.cc | 276 +------ net_proc.cc | 5 +- netlist.cc | 124 +-- netlist.h | 146 +--- netmisc.cc | 143 +++- netmisc.h | 47 +- set_width.cc | 486 ----------- t-dll-expr.cc | 4 +- verinum.cc | 37 +- verinum.h | 9 +- 25 files changed, 1500 insertions(+), 2686 deletions(-) delete mode 100644 set_width.cc diff --git a/Makefile.in b/Makefile.in index 33d264a54..8704bfc8f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -106,7 +106,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ netenum.o net_event.o net_expr.o net_func.o net_link.o net_modulo.o \ net_nex_input.o net_nex_output.o net_proc.o net_scope.o net_tran.o \ net_udp.o pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ - pform_disciplines.o pform_dump.o pform_types.o set_width.o \ + pform_disciplines.o pform_dump.o pform_types.o \ symbol_search.o sync.o sys_funcs.o verinum.o verireal.o target.o \ Attrib.o HName.o Module.o PDelays.o PEvent.o PExpr.o PGate.o \ PGenerate.o PScope.o PSpec.o PTask.o PUdp.o PFunction.o PWire.o \ diff --git a/PDelays.cc b/PDelays.cc index 40ea8b262..8e9ea79fc 100644 --- a/PDelays.cc +++ b/PDelays.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -78,11 +78,7 @@ unsigned PDelays::delay_count() const static NetExpr*calculate_val(Design*des, NetScope*scope, PExpr*expr) { - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - expr->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - NetExpr*dex = expr->elaborate_expr(des, scope, -1, false); - eval_expr(dex); + NetExpr*dex = elab_and_eval(des, scope, expr, -1); /* Print a warning if we find default and `timescale based * delays in the design, since this is likely an error. */ diff --git a/PExpr.cc b/PExpr.cc index cefe23bc7..bd529f174 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2010 Stephen Williams + * Copyright (c) 1998-2011 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -31,8 +31,10 @@ PExpr::PExpr() { - expr_width_ = 0; - expr_type_ = IVL_VT_NO_TYPE; + expr_type_ = IVL_VT_NO_TYPE; + expr_width_ = 0; + min_width_ = 0; + signed_flag_ = false; } PExpr::~PExpr() @@ -94,8 +96,8 @@ bool PEBinary::has_aa_term(Design*des, NetScope*scope) const PEBComp::PEBComp(char op, PExpr*l, PExpr*r) : PEBinary(op, l, r) { - left_width_ = 0; - right_width_ = 0; + l_width_ = 0; + r_width_ = 0; } PEBComp::~PEBComp() @@ -204,13 +206,16 @@ bool PECallFunction::has_aa_term(Design*des, NetScope*scope) const } PEConcat::PEConcat(const list&p, PExpr*r) -: parms_(p.size()), tested_widths_(p.size()), repeat_(r) +: parms_(p.size()), width_modes_(p.size()), repeat_(r) { int tmp_idx = 0; assert(parms_.size() == p.size()); for (list::const_iterator idx = p.begin() ; idx != p.end() ; ++idx) parms_[tmp_idx++] = *idx; + + tested_scope_ = 0; + repeat_count_ = 1; } PEConcat::~PEConcat() @@ -437,3 +442,11 @@ bool PEUnary::has_aa_term(Design*des, NetScope*scope) const assert(expr_); return expr_->has_aa_term(des, scope); } + +PEVoid::PEVoid() +{ +} + +PEVoid::~PEVoid() +{ +} diff --git a/PExpr.h b/PExpr.h index 15adfc7f4..15794e6a4 100644 --- a/PExpr.h +++ b/PExpr.h @@ -1,7 +1,7 @@ #ifndef __PExpr_H #define __PExpr_H /* - * Copyright (c) 1998-2010 Stephen Williams + * Copyright (c) 1998-2011 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -43,6 +43,8 @@ class NetScope; class PExpr : public LineInfo { public: + enum width_mode_t { SIZED, EXPAND, LOSSLESS, UNSIZED }; + PExpr(); virtual ~PExpr(); @@ -62,53 +64,65 @@ class PExpr : public LineInfo { // references to automatically allocated variables. virtual bool has_aa_term(Design*des, NetScope*scope) const; - // This method tests the width that the expression wants to - // be. It is used by elaboration of assignments to figure out - // the width of the expression. + // This method tests the type and width that the expression wants + // to be. It should be called before elaborating an expression to + // figure out the type and width of the expression. It also figures + // out the minimum width that can be used to evaluate the expression + // without changing the result. This allows the expression width to + // be pruned when not all bits of the result are used. // - // The "min" is the width of the local context, so is the - // minimum width that this function should return. Initially - // this is the same as the lval width. + // Normally mode should be initialised to SIZED before starting to + // test the width of an expression. In SIZED mode the expression + // width will be calculated strictly according to the IEEE standard + // rules for expression width. + // If the expression contains an unsized literal, mode will be + // changed to LOSSLESS. In LOSSLESS mode the expression width will + // be calculated as the minimum width necessary to avoid arithmetic + // overflow or underflow. + // If the expression both contains an unsized literal and contains + // an operation that coerces a vector operand to a different type + // (signed <-> unsigned), mode is changed to UNSIZED. UNSIZED mode + // is the same as LOSSLESS, except that the final expression width + // will be forced to be at least integer_width. This is necessary + // to ensure compatibility with the IEEE standard, which requires + // unsized literals to be treated as having the same width as an + // integer. The lossless width calculation is inadequate in this + // case because coercing an operand to a different type means that + // the expression no longer obeys the normal rules of arithmetic. // - // The "lval" is the width of the destination where this - // result is going to go. This can be used to constrain the - // amount that an expression can reasonably expand. For - // example, there is no point expanding an addition to beyond - // the lval. This extra bit of information allows the - // expression to optimize itself a bit. If the lval==0, then - // the subexpression should not make l-value related - // optimizations. + // If mode is initialised to EXPAND instead of SIZED, the expression + // width will be calculated as the minimum width necessary to avoid + // arithmetic overflow or underflow, even if it contains no unsized + // literals. mode will be changed LOSSLESS or UNSIZED as described + // above. This supports a non-standard mode of expression width + // calculation. // - // The expr_type is an output argument that gives the - // calculated type for the expression. - // - // The unsized_flag is set to true if the expression is - // unsized and therefore expandable. This happens if a - // sub-expression is an unsized literal. Some expressions make - // special use of that. - virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + // When the final value of mode is UNSIZED, the width returned by + // this method is the calculated lossless width, but the width + // returned by a subsequent call to the expr_width method will be + // the final expression width. + virtual unsigned test_width(Design*des, NetScope*scope, + width_mode_t&mode); // After the test_width method is complete, these methods // return valid results. ivl_variable_type_t expr_type() const { return expr_type_; } unsigned expr_width() const { return expr_width_; } + unsigned min_width() const { return min_width_; } + bool has_sign() const { return signed_flag_; } + + // This method allows the expression type (signed/unsigned) + // to be propagated down to any context-dependant operands. + void cast_signed(bool flag) { signed_flag_ = flag; } // Procedural elaboration of the expression. The expr_width is - // the width of the context of the expression (i.e. the - // l-value width of an assignment), - // - // ... or -1 if the expression is self-determined. or - // ... or -2 if the expression is losslessly - // self-determined. This can happen in situations where the - // result is going to a pseudo-infinitely wide context. + // the required width of the expression. // // The sys_task_arg flag is true if expressions are allowed to // be incomplete. virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; // This method elaborates the expression as gates, but // restricted for use as l-values of continuous assignments. @@ -140,9 +154,13 @@ class PExpr : public LineInfo { virtual bool is_the_same(const PExpr*that) const; protected: + unsigned fix_width_(width_mode_t mode); + // The derived class test_width methods should fill these in. ivl_variable_type_t expr_type_; unsigned expr_width_; + unsigned min_width_; + bool signed_flag_; private: // not implemented PExpr(const PExpr&); @@ -165,14 +183,13 @@ class PEConcat : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -181,9 +198,11 @@ class PEConcat : public PExpr { bool bidirectional_flag) const; private: vectorparms_; - std::valarraytested_widths_; + std::valarraywidth_modes_; PExpr*repeat_; + NetScope*tested_scope_; + unsigned repeat_count_; }; /* @@ -232,11 +251,10 @@ class PEFNumber : public PExpr { virtual verinum* eval_const(Design*des, NetScope*sc) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual void dump(ostream&) const; @@ -262,9 +280,7 @@ class PEIdent : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); // Identifiers are allowed (with restrictions) is assign l-values. virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; @@ -277,7 +293,8 @@ class PEIdent : public PExpr { bool is_force) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; // Elaborate the PEIdent as a port to a module. This method // only applies to Ident expressions. @@ -326,13 +343,14 @@ class PEIdent : public PExpr { NetScope*found, const NetExpr*par_msb, const NetExpr*par_lsb, - int expr_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, NetScope*found, const NetExpr*par_msb, - const NetExpr*par_lsb) const; + const NetExpr*par_lsb, + unsigned expr_wid) const; NetExpr*elaborate_expr_param_idx_up_(Design*des, NetScope*scope, const NetExpr*par, @@ -349,28 +367,31 @@ class PEIdent : public PExpr { NetScope*scope, NetNet*net, NetScope*found, + unsigned expr_wid, bool sys_task_arg) const; NetExpr*elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found, + unsigned expr_wid, bool sys_task_arg) const; NetExpr*elaborate_expr_net_part_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found, + unsigned expr_wid) const; NetExpr*elaborate_expr_net_idx_up_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; NetExpr*elaborate_expr_net_idx_do_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; NetExpr*elaborate_expr_net_bit_(Design*des, - NetScope*scope, - NetESignal*net, - NetScope*found) const; + NetScope*scope, + NetESignal*net, + NetScope*found) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, @@ -390,12 +411,10 @@ class PENumber : public PExpr { virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - int expr_width, bool) const; + unsigned expr_wid, bool) const; virtual NetAssign_* elaborate_lval(Design*des, NetScope*scope, bool is_force) const; @@ -425,12 +444,10 @@ class PEString : public PExpr { virtual void dump(ostream&) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetEConst*elaborate_expr(Design*des, NetScope*, - int expr_width, bool) const; + unsigned expr_wid, bool) const; verinum* eval_const(Design*, NetScope*) const; private: @@ -450,16 +467,15 @@ class PEUnary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: - NetExpr* elaborate_expr_bits_(NetExpr*operand, int expr_wid) const; + NetExpr* elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const; private: char op_; @@ -479,12 +495,11 @@ class PEBinary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; protected: @@ -493,22 +508,22 @@ class PEBinary : public PExpr { PExpr*right_; NetExpr*elaborate_expr_base_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_eval_expr_base_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_bits_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_div_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_lshift_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_rshift_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_mult_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; NetExpr*elaborate_expr_base_add_(Design*, NetExpr*lp, NetExpr*rp, - int use_wid) const; + unsigned expr_wid) const; }; @@ -523,16 +538,14 @@ class PEBComp : public PEBinary { ~PEBComp(); virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, bool sys_task_arg) const; private: - int left_width_; - int right_width_; + unsigned l_width_; + unsigned r_width_; }; /* @@ -545,12 +558,10 @@ class PEBLogic : public PEBinary { ~PEBLogic(); virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); NetExpr* elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, bool sys_task_arg) const; }; /* @@ -565,17 +576,15 @@ class PEBLeftWidth : public PEBinary { ~PEBLeftWidth() =0; virtual NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const =0; + unsigned expr_wid) const =0; protected: virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, - int expr_width, bool sys_task_arg) const; - + unsigned expr_wid, + bool sys_task_arg) const; }; class PEBPower : public PEBLeftWidth { @@ -585,7 +594,7 @@ class PEBPower : public PEBLeftWidth { ~PEBPower(); NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const; + unsigned expr_wid) const; }; class PEBShift : public PEBLeftWidth { @@ -595,7 +604,7 @@ class PEBShift : public PEBLeftWidth { ~PEBShift(); NetExpr*elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const; + unsigned expr_wid) const; }; /* @@ -615,17 +624,16 @@ class PETernary : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); virtual NetExpr*elaborate_expr(Design*des, NetScope*, - int expr_width, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; private: NetExpr* elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, int use_wid) const; + PExpr*expr, unsigned expr_wid) const; private: PExpr*expr_; @@ -658,12 +666,11 @@ class PECallFunction : public PExpr { virtual bool has_aa_term(Design*des, NetScope*scope) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool sys_task_arg) const; + unsigned expr_wid, + bool sys_task_arg) const; virtual unsigned test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); private: pform_name_t path_; @@ -671,14 +678,29 @@ class PECallFunction : public PExpr { bool check_call_matches_definition_(Design*des, NetScope*dscope) const; - NetExpr* cast_to_width_(NetExpr*expr, int wid, bool signed_flag) const; + NetExpr* cast_to_width_(NetExpr*expr, unsigned wid) const; - NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const; - NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t) const; + NetExpr* elaborate_sfunc_(Design*des, NetScope*scope, + unsigned expr_wid) const; + NetExpr* elaborate_access_func_(Design*des, NetScope*scope, ivl_nature_t, + unsigned expr_wid) const; unsigned test_width_sfunc_(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type, - bool&unsized_flag); + width_mode_t&mode); +}; + +/* + * This class is used for error recovery. All methods do nothing and return + * null or default values. + */ +class PEVoid : public PExpr { + + public: + explicit PEVoid(); + ~PEVoid(); + + virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, + unsigned expr_wid, + bool sys_task_arg) const; }; #endif diff --git a/design_dump.cc b/design_dump.cc index 8d02de40d..afdfa5031 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -1396,12 +1396,8 @@ void NetEBinary::dump(ostream&o) const void NetEConcat::dump(ostream&o) const { - if (repeat_calculated_) { - if (repeat_value_ != 1) - o << repeat_value_; - } else if (repeat_) { - o << "<" << *repeat_ << ">"; - } + if (repeat_ != 1) + o << repeat_; if (parms_[0]) o << "{" << *parms_[0]; diff --git a/dup_expr.cc b/dup_expr.cc index db7c2bead..d541161e9 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -32,19 +32,103 @@ NetEAccess* NetEAccess::dup_expr() const return tmp; } -NetEBComp* NetEBComp::dup_expr() const +NetEBinary* NetEBinary::dup_expr() const { - NetEBComp*tmp = new NetEBComp(op_, left_->dup_expr(), - right_->dup_expr()); - assert(tmp); + ivl_assert(*this, 0); + return 0; +} + +NetEBAdd* NetEBAdd::dup_expr() const +{ + NetEBAdd*tmp = new NetEBAdd(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } +NetEBBits* NetEBBits::dup_expr() const +{ + NetEBBits*tmp = new NetEBBits(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBComp* NetEBComp::dup_expr() const +{ + NetEBComp*tmp = new NetEBComp(op_, left_->dup_expr(), right_->dup_expr()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBDiv* NetEBDiv::dup_expr() const +{ + NetEBDiv*tmp = new NetEBDiv(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBLogic* NetEBLogic::dup_expr() const +{ + NetEBLogic*tmp = new NetEBLogic(op_, left_->dup_expr(), right_->dup_expr()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBMult* NetEBMult::dup_expr() const +{ + NetEBMult*tmp = new NetEBMult(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBPow* NetEBPow::dup_expr() const +{ + NetEBPow*tmp = new NetEBPow(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEBShift* NetEBShift::dup_expr() const +{ + NetEBShift*tmp = new NetEBShift(op_, left_->dup_expr(), right_->dup_expr(), + expr_width(), has_sign()); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetEConcat* NetEConcat::dup_expr() const +{ + NetEConcat*dup = new NetEConcat(parms_.count(), repeat_); + ivl_assert(*this, dup); + dup->set_line(*this); + for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) + if (parms_[idx]) { + NetExpr*tmp = parms_[idx]->dup_expr(); + ivl_assert(*this, tmp); + dup->parms_[idx] = tmp; + } + + dup->expr_width(expr_width()); + + return dup; +} + NetEConst* NetEConst::dup_expr() const { NetEConst*tmp = new NetEConst(value_); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -52,7 +136,7 @@ NetEConst* NetEConst::dup_expr() const NetEConstEnum* NetEConstEnum::dup_expr() const { NetEConstEnum*tmp = new NetEConstEnum(scope_, name_, enum_set_, value()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -60,7 +144,15 @@ NetEConstEnum* NetEConstEnum::dup_expr() const NetEConstParam* NetEConstParam::dup_expr() const { NetEConstParam*tmp = new NetEConstParam(scope_, name_, value()); - assert(tmp); + ivl_assert(*this, tmp); + tmp->set_line(*this); + return tmp; +} + +NetECReal* NetECReal::dup_expr() const +{ + NetECReal*tmp = new NetECReal(value_); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -68,26 +160,26 @@ NetEConstParam* NetEConstParam::dup_expr() const NetECRealParam* NetECRealParam::dup_expr() const { NetECRealParam*tmp = new NetECRealParam(scope_, name_, value()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEEvent* NetEEvent::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } NetENetenum* NetENetenum::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } NetEScope* NetEScope::dup_expr() const { - assert(0); + ivl_assert(*this, 0); return 0; } @@ -96,7 +188,7 @@ NetESelect* NetESelect::dup_expr() const NetESelect*tmp = new NetESelect(expr_->dup_expr(), base_? base_->dup_expr() : 0, expr_width(), sel_type_); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -104,11 +196,11 @@ NetESelect* NetESelect::dup_expr() const NetESFunc* NetESFunc::dup_expr() const { NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms()); - assert(tmp); + ivl_assert(*this, tmp); tmp->cast_signed(has_sign()); for (unsigned idx = 0 ; idx < nparms() ; idx += 1) { - assert(parm(idx)); + ivl_assert(*this, parm(idx)); tmp->parm(idx, parm(idx)->dup_expr()); } @@ -119,7 +211,7 @@ NetESFunc* NetESFunc::dup_expr() const NetESignal* NetESignal::dup_expr() const { NetESignal*tmp = new NetESignal(net_, word_); - assert(tmp); + ivl_assert(*this, tmp); tmp->expr_width(expr_width()); tmp->set_line(*this); return tmp; @@ -129,8 +221,10 @@ NetETernary* NetETernary::dup_expr() const { NetETernary*tmp = new NetETernary(cond_->dup_expr(), true_val_->dup_expr(), - false_val_->dup_expr()); - assert(tmp); + false_val_->dup_expr(), + expr_width(), + has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -141,29 +235,29 @@ NetEUFunc* NetEUFunc::dup_expr() const svector tmp_parms (parms_.count()); for (unsigned idx = 0 ; idx < tmp_parms.count() ; idx += 1) { - assert(parms_[idx]); + ivl_assert(*this, parms_[idx]); tmp_parms[idx] = parms_[idx]->dup_expr(); } tmp = new NetEUFunc(scope_, func_, result_sig_->dup_expr(), tmp_parms); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUBits* NetEUBits::dup_expr() const { - NetEUBits*tmp = new NetEUBits(op_, expr_->dup_expr()); - assert(tmp); + NetEUBits*tmp = new NetEUBits(op_, expr_->dup_expr(), expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetEUnary* NetEUnary::dup_expr() const { - NetEUnary*tmp = new NetEUnary(op_, expr_->dup_expr()); - assert(tmp); + NetEUnary*tmp = new NetEUnary(op_, expr_->dup_expr(), expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } @@ -171,15 +265,15 @@ NetEUnary* NetEUnary::dup_expr() const NetEUReduce* NetEUReduce::dup_expr() const { NetEUReduce*tmp = new NetEUReduce(op_, expr_->dup_expr()); - assert(tmp); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } NetECast* NetECast::dup_expr() const { - NetECast*tmp = new NetECast(op_, expr_->dup_expr()); - assert(tmp); + NetECast*tmp = new NetECast(op_, expr_->dup_expr(), expr_width(), has_sign()); + ivl_assert(*this, tmp); tmp->set_line(*this); return tmp; } diff --git a/elab_expr.cc b/elab_expr.cc index 878596e4e..b9cf8eec8 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -81,163 +81,140 @@ static NetBranch* find_existing_implicit_branch(NetNet*sig, NetNet*gnd) } NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, - ivl_variable_type_t data_type_lv, int expr_wid_lv, + ivl_variable_type_t lv_type, unsigned lv_width, PExpr*expr) { - bool unsized_flag = type_is_vectorable(data_type_lv)? false : true; - unsigned use_lval_wid = type_is_vectorable(data_type_lv)? expr_wid_lv : 0; - unsigned use_min_wid = expr_wid_lv; - - /* Find out what the r-value width is going to be. We - guess it will be the l-value width, but it may turn - out to be something else based on self-determined - widths inside. */ - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = expr->test_width(des, scope, use_min_wid, use_lval_wid, rval_type, unsized_flag); - - if (debug_elaborate) { - cerr << expr->get_fileline() << ": debug: r-value tested " - << "type=" << rval_type - << ", width=" << expr_wid - << ", min=" << use_min_wid - << ", unsized_flag=" << (unsized_flag?"true":"false") << endl; - } - - switch (data_type_lv) { + int context_wid = -1; + switch (lv_type) { case IVL_VT_REAL: case IVL_VT_STRING: - unsized_flag = true; - expr_wid = -2; - expr_wid_lv = -1; break; case IVL_VT_BOOL: case IVL_VT_LOGIC: + context_wid = lv_width; break; case IVL_VT_VOID: case IVL_VT_NO_TYPE: ivl_assert(*expr, 0); - expr_wid = -2; - expr_wid_lv = -1; break; } - NetExpr*result = elab_and_eval(des, scope, expr, expr_wid, expr_wid_lv); - return result; + return elab_and_eval(des, scope, expr, context_wid); } /* - * The default behavior for the test_width method is to just return the - * minimum width that is passed in. + * If the mode is UNSIZED, make sure the final expression width is at + * least integer_width, but return the calculated lossless width to + * the caller. */ -unsigned PExpr::test_width(Design*, NetScope*, - unsigned min, unsigned, - ivl_variable_type_t&, bool&) +unsigned PExpr::fix_width_(width_mode_t mode) { - if (debug_elaborate) { - cerr << get_fileline() << ": debug: test_width defaults to " - << min << ", ignoring unsized_flag. typeid=" - << typeid(*this).name() << endl; - } - return min; + unsigned width = expr_width_; + if ((mode == UNSIZED) && (width < integer_width)) + expr_width_ = integer_width; + + return width; } -NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, int, bool) const +unsigned PExpr::test_width(Design*des, NetScope*, width_mode_t&) { - cerr << get_fileline() << ": internal error: I do not know how to elaborate" - << " expression. " << endl; + cerr << get_fileline() << ": internal error: I do not know how to" + << " test the width of this expression. " << endl; + cerr << get_fileline() << ": : Expression is: " << *this + << endl; + des->errors += 1; + return 1; +} + +NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, unsigned, bool) const +{ + cerr << get_fileline() << ": internal error: I do not know how to" + << " elaborate this expression. " << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; return 0; } -unsigned PEBinary::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + +unsigned PEBinary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - ivl_variable_type_t expr_type_left = IVL_VT_NO_TYPE; - ivl_variable_type_t expr_type_right= IVL_VT_NO_TYPE; + ivl_assert(*this, left_); + ivl_assert(*this, right_); - bool flag = unsized_flag; + unsigned l_width = left_->test_width(des, scope, mode); - bool flag_left = flag; - unsigned wid_left = left_->test_width(des,scope, min, 0, expr_type_left, flag_left); + width_mode_t saved_mode = mode; - bool flag_right = flag; - unsigned wid_right = right_->test_width(des,scope, min, 0, expr_type_right, flag_right); + unsigned r_width = right_->test_width(des, scope, mode); - if (flag_right && !flag) { - unsized_flag = true; - wid_left = left_->test_width(des, scope, min, 0, expr_type_left, unsized_flag); - } + // If the width mode changed, retest the left operand, as it + // may choose a different width if it is in an unsized context. + if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) + l_width = left_->test_width(des, scope, mode); - if (flag_left && !flag) { - unsized_flag = true; - wid_right = right_->test_width(des, scope, min, 0, expr_type_right, unsized_flag); - } + ivl_variable_type_t l_type = left_->expr_type(); + ivl_variable_type_t r_type = right_->expr_type(); - if (expr_type_left == IVL_VT_REAL || expr_type_right == IVL_VT_REAL) + if (l_type == IVL_VT_REAL || r_type == IVL_VT_REAL) expr_type_ = IVL_VT_REAL; - else if (expr_type_left==IVL_VT_LOGIC || expr_type_right==IVL_VT_LOGIC) + else if (l_type == IVL_VT_LOGIC || r_type == IVL_VT_LOGIC) expr_type_ = IVL_VT_LOGIC; else expr_type_ = IVL_VT_BOOL; - switch (op_) { - case '+': - case '-': - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - if (unsized_flag && type_is_vectorable(expr_type_)) - min += 1; - break; + if (expr_type_ == IVL_VT_REAL) { + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; + } else { + expr_width_ = max(l_width, r_width); + min_width_ = max(left_->min_width(), right_->min_width()); + signed_flag_ = left_->has_sign() && right_->has_sign(); - case '*': - if (unsized_flag && type_is_vectorable(expr_type_)) { - unsigned use_wid = wid_left + wid_right; - if (use_wid > integer_width) - use_wid = integer_width; - if (use_wid > min) - min = use_wid; - } - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - break; + // If the operands are different types, the expression is + // forced to unsigned. In this case the lossless width + // calculation is unreliable and we need to make sure the + // final expression width is at least integer_width. + if ((mode == LOSSLESS) && (left_->has_sign() != right_->has_sign())) + mode = UNSIZED; - case 'l': // << Should be handled by PEBShift - case '<': // < Should be handled by PEBComp - case '>': // > Should be handled by PEBComp - case 'e': // == Should be handled by PEBComp - case 'E': // === Should be handled by PEBComp - case 'L': // <= Should be handled by PEBComp - case 'G': // >= Should be handled by PEBComp - case 'n': // != Should be handled by PEBComp - case 'N': // !== Should be handled by PEBComp - case 'p': // ** should be handled by PEBPower - ivl_assert(*this, 0); - default: - if (wid_left > min) - min = wid_left; - if (wid_right > min) - min = wid_right; - break; + switch (op_) { + case '+': + case '-': + if (mode != SIZED) + expr_width_ += 1; + break; + + case '*': + if (mode != SIZED) + expr_width_ = l_width + r_width; + break; + + case '%': + case '/': + min_width_ = max(min_width_, expr_width_); + break; + + case 'l': // << Should be handled by PEBShift + case 'r': // << Should be handled by PEBShift + case 'R': // << Should be handled by PEBShift + case '<': // < Should be handled by PEBComp + case '>': // > Should be handled by PEBComp + case 'e': // == Should be handled by PEBComp + case 'E': // === Should be handled by PEBComp + case 'L': // <= Should be handled by PEBComp + case 'G': // >= Should be handled by PEBComp + case 'n': // != Should be handled by PEBComp + case 'N': // !== Should be handled by PEBComp + case 'p': // ** should be handled by PEBPower + ivl_assert(*this, 0); + default: + break; + } } - if (type_is_vectorable(expr_type_)) { - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - expr_width_ = min; - } else - expr_width_ = 1; - - expr_type__ = expr_type_; - return expr_width_; + return fix_width_(mode); } /* @@ -246,104 +223,66 @@ unsigned PEBinary::test_width(Design*des, NetScope*scope, * types. */ NetExpr* PEBinary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The context passes in the width that this expression is - // expected to use. But if that width is <0, we are in a - // self-determined context and we must use the width that was - // calculated by a previous call to test_with. - int use_wid = expr_wid; - if (use_wid < 0 && expr_type_ != IVL_VT_REAL) { - ivl_assert(*this, expr_width_ > 0); - use_wid = expr_width_; + // Handle the special case that one of the operands is a real + // value and the other is a vector type. In that case, + // elaborate the vectorable argument as self-determined. + // Propagate the expression type (signed/unsigned) down to + // any context-determined operands. + unsigned l_width = expr_wid; + unsigned r_width = expr_wid; + if (left_->expr_type()==IVL_VT_REAL + && type_is_vectorable(right_->expr_type())) { + r_width = right_->expr_width(); + } else { + right_->cast_signed(signed_flag_); + } + if (right_->expr_type()==IVL_VT_REAL + && type_is_vectorable(left_->expr_type())) { + l_width = left_->expr_width(); + } else { + left_->cast_signed(signed_flag_); } - NetExpr*lp = left_->elaborate_expr(des, scope, use_wid, false); - NetExpr*rp = right_->elaborate_expr(des, scope, use_wid, false); + NetExpr*lp = left_->elaborate_expr(des, scope, l_width, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } - // Handle the special case that one of the operands is a real - // value and the other is a vector type. In that case, - // re-elaborate the vectorable argument as self-determined - // lossless. - if (lp->expr_type()==IVL_VT_REAL - && type_is_vectorable(rp->expr_type()) - && expr_wid != -2) { - delete rp; - rp = right_->elaborate_expr(des, scope, -2, false); - } - - if (rp->expr_type()==IVL_VT_REAL - && type_is_vectorable(lp->expr_type()) - && expr_wid != -2) { - delete lp; - lp = left_->elaborate_expr(des, scope, -2, false); - } - - NetExpr*tmp = elaborate_eval_expr_base_(des, lp, rp, expr_wid); - return tmp; -} - -void suppress_binary_operand_sign_if_needed(NetExpr*lp, NetExpr*rp) -{ - // If an argument is a non-vector type, then this type - // suppression does not apply. - if (! type_is_vectorable(lp->expr_type())) - return; - if (! type_is_vectorable(rp->expr_type())) - return; - - // If either operand is unsigned, then treat the whole - // expression as unsigned. This test needs to be done here - // instead of in *_expr_base_ because it needs to be done - // ahead of any subexpression evaluation (because they need to - // know their signedness to evaluate) and because there are - // exceptions to this rule. - if (! lp->has_sign()) - rp->cast_signed(false); - if (! rp->has_sign()) - lp->cast_signed(false); -} - -NetExpr* PEBinary::elaborate_eval_expr_base_(Design*des, - NetExpr*lp, - NetExpr*rp, - int expr_wid) const -{ - /* If either expression can be evaluated ahead of time, then - do so. This can prove helpful later. */ - eval_expr(lp, expr_wid); - eval_expr(rp, expr_wid); + // If either expression can be evaluated ahead of time, then + // do so. This can prove helpful later. + eval_expr(lp, l_width); + eval_expr(rp, r_width); return elaborate_expr_base_(des, lp, rp, expr_wid); } /* - * This is common elaboration of the operator. It presumes that the + * This is the common elaboration of the operator. It presumes that the * operands are elaborated as necessary, and all I need to do is make * the correct NetEBinary object and connect the parameters. */ NetExpr* PEBinary::elaborate_expr_base_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (debug_elaborate) { cerr << get_fileline() << ": debug: elaborate expression " - << *this << " expr_wid=" << expr_wid << endl; + << *this << " expr_width=" << expr_wid << endl; } NetExpr*tmp; switch (op_) { default: - tmp = new NetEBinary(op_, lp, rp); + tmp = new NetEBinary(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; @@ -357,9 +296,12 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, return 0; case 'p': - tmp = new NetEBPow(op_, lp, rp); - tmp->set_line(*this); - break; + cerr << get_fileline() << ": internal error: " + << "Elaboration of " << human_readable_op(op_) + << " Should have been handled in NetEBPower::elaborate." + << endl; + des->errors += 1; + return 0; case '*': tmp = elaborate_expr_base_mult_(des, lp, rp, expr_wid); @@ -391,7 +333,8 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, case '+': case '-': - tmp = elaborate_expr_base_add_(des, lp, rp, expr_wid); + tmp = new NetEBAdd(op_, lp, rp, expr_wid, signed_flag_); + tmp->set_line(*this); break; case 'E': /* === */ @@ -411,7 +354,7 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, case 'm': // min(l,r) case 'M': // max(l,r) - tmp = new NetEBMinMax(op_, lp, rp); + tmp = new NetEBMinMax(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); break; } @@ -421,7 +364,7 @@ NetExpr* PEBinary::elaborate_expr_base_(Design*des, NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " @@ -431,20 +374,7 @@ NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, return 0; } - // If either of the arguments is unsigned, then process both - // of them as unsigned. This only impacts the padding that is - // done to get the operands to the expr_wid. - if (! lp->has_sign()) rp->cast_signed(false); - if (! rp->has_sign()) lp->cast_signed(false); - - if (expr_wid > 0) { - if (type_is_vectorable(lp->expr_type())) - lp = pad_to_width(lp, expr_wid, *this); - if (type_is_vectorable(rp->expr_type())) - rp = pad_to_width(rp, expr_wid, *this); - } - - NetEBBits*tmp = new NetEBBits(op_, lp, rp); + NetEBBits*tmp = new NetEBBits(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; @@ -452,7 +382,7 @@ NetExpr* PEBinary::elaborate_expr_base_bits_(Design*des, NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { /* The % operator does not support real arguments in baseline Verilog. But we allow it in our extended @@ -466,23 +396,7 @@ NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, } } - // If either of the arguments is unsigned, then process both - // of them as unsigned. This only impacts the padding that is - // done to get the operands to the expr_wid. - if (! lp->has_sign()) rp->cast_signed(false); - if (! rp->has_sign()) lp->cast_signed(false); - - /* The original elaboration of the left and right expressions - already tried to elaborate to the expr_wid. If the - expressions are not that width by now, then they need to be - padded. The divide expression operands must be the width - of the output. */ - if (expr_wid > 0) { - lp = pad_to_width(lp, expr_wid, *this); - rp = pad_to_width(rp, expr_wid, *this); - } - - NetEBDiv*tmp = new NetEBDiv(op_, lp, rp); + NetEBDiv*tmp = new NetEBDiv(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; @@ -490,7 +404,7 @@ NetExpr* PEBinary::elaborate_expr_base_div_(Design*des, NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " @@ -502,20 +416,6 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, NetExpr*tmp; - long use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - - if (use_wid == 0) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Oops, left expression width is not known, " - << "so expression width is not known. Punt." << endl; - tmp = new NetEBShift(op_, lp, rp); - tmp->set_line(*this); - return tmp; - } - // If the left expression is constant, then there are some // special cases we can work with. If the left expression is // not constant, but the right expression is constant, then @@ -524,8 +424,8 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, if (NetEConst*lpc = dynamic_cast (lp)) { - // Special case: The left expression is zero. No matter - // what the shift, the result is going to be zero. + // Special case: The left expression is zero. No matter what + // the shift, the result is going to be zero. if (lpc->value().is_defined() && lpc->value().is_zero()) { if (debug_elaborate) @@ -533,92 +433,62 @@ NetExpr* PEBinary::elaborate_expr_base_lshift_(Design*des, << "Shift of zero always returns zero." << " Elaborate as constant zero." << endl; - tmp = make_const_0(use_wid); - tmp->set_line(*this); - return tmp; - } + tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + return tmp; + } + + // Special case: Both operands are constants. Precalculate + // the entire value here. if (NetEConst*rpc = dynamic_cast (rp)) { - // Handle the super-special case that both - // operands are constants. Precalculate the - // entire value here. - verinum lpval = lpc->value(); unsigned shift = rpc->value().as_ulong(); - verinum result = lpc->value() << shift; - // If the l-value has explicit size, or - // there is a context determined size, use that. - if (lpval.has_len() || expr_wid > 0) { - int use_len = lpval.len(); - if (expr_wid > 0 && expr_wid > use_len) - use_len = expr_wid; - result = verinum(result, use_len); - } - + verinum result (lpc->value() << shift, expr_wid); tmp = new NetEConst(result); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Precalculate " << *lpc << " << " << shift << " to constant " << *tmp << " (expr_wid=" << expr_wid << ")" << endl; - } else { - // Handle the special case that the left - // operand is constant. If it is unsized, we - // may have to expand it to an integer width. - verinum lpval = lpc->value(); - if (lpval.len() < integer_width && !lpval.has_len()) { - lpval = verinum(lpval, integer_width); - lpc = new NetEConst(lpval); - lpc->set_line(*lp); - } - - tmp = new NetEBShift(op_, lpc, rp); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Adjust " << *this - << " to this " << *tmp - << " to allow for integer widths." << endl; + return tmp; } } else if (NetEConst*rpc = dynamic_cast (rp)) { long shift = rpc->value().as_long(); - use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - if (shift >= use_wid || (-shift) >= (long)lp->expr_width()) { + // Special case: The shift is at least the size of the entire + // left operand. Elaborate as a constant-0. + if (shift >= expr_wid) { + if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Value left-shifted " << shift - << " beyond width of " << use_wid + << " beyond width of " << expr_wid << ". Elaborate as constant zero." << endl; - tmp = make_const_0(use_wid); + tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); - } else { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Left shift expression by constant " - << shift << " bits. (use_wid=" << use_wid << ")" << endl; - lp = pad_to_width(lp, use_wid, *this); - tmp = new NetEBShift(op_, lp, rp); + return tmp; } - - } else { - // Left side is not constant, so handle it the - // default way. - if (expr_wid >= 0) - lp = pad_to_width(lp, expr_wid, *this); - tmp = new NetEBShift(op_, lp, rp); } + // Fallback, handle the general case. + tmp = new NetEBShift(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); + return tmp; } NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (lp->expr_type() == IVL_VT_REAL || rp->expr_type() == IVL_VT_REAL) { cerr << get_fileline() << ": error: " @@ -630,20 +500,6 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, NetExpr*tmp; - long use_wid = lp->expr_width(); - if (expr_wid > 0) - use_wid = expr_wid; - - if (use_wid == 0) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Oops, left expression width is not known, " - << "so expression width is not known. Punt." << endl; - tmp = new NetEBShift(op_, lp, rp); - tmp->set_line(*this); - return tmp; - } - if (NetEConst*lpc = dynamic_cast (lp)) { // Special case: The left expression is zero. No matter @@ -655,31 +511,33 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, << "Shift of zero always returns zero." << " Elaborate as constant zero." << endl; - tmp = make_const_0(use_wid); + tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); + return tmp; } } if (NetEConst*rpc = dynamic_cast (rp)) { - unsigned long shift = rpc->value().as_ulong(); + long shift = rpc->value().as_ulong(); // Special case: The shift is the size of the entire // left operand, and the shift is unsigned. Elaborate as // a constant-0. - if ((op_=='r' || (lp->has_sign()==false)) && - shift >= lp->expr_width()) { + if ((op_=='r' || !signed_flag_) && shift >= expr_wid) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Value right-shifted " << shift - << " beyond width of " << lp->expr_width() + << " beyond width of " << expr_wid << ". Elaborate as constant zero." << endl; - tmp = make_const_0(use_wid); + tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); - return tmp; + return tmp; } // Special case: the shift is the size of the entire @@ -689,73 +547,51 @@ NetExpr* PEBinary::elaborate_expr_base_rshift_(Design*des, // The above test assures us that op_ == 'R' && the left // argument is signed when the shift is greater than the // expression width. - if (shift >= lp->expr_width()) { + if (shift >= expr_wid) { if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Value signed-right-shifted " << shift - << " beyond width of " << lp->expr_width() + << " beyond width of " << expr_wid << ". Elaborate as replicated top bit." << endl; - ivl_assert(*this, lp->expr_width() > 0); - ivl_assert(*this, use_wid > 0); - - tmp = new NetEConst(verinum(lp->expr_width()-1)); + tmp = new NetEConst(verinum(expr_wid-1)); tmp->set_line(*this); tmp = new NetESelect(lp, tmp, 1); + tmp->set_line(*this); + tmp = pad_to_width(tmp, expr_wid, *this); tmp->cast_signed(true); - tmp = pad_to_width(tmp, use_wid, *this); - return tmp; - } - // If this is lossless, then pad the left expression - // enough to cover the right shift. - if (expr_wid == -2 && use_wid+shift > lp->expr_width()) { - lp->cast_signed(lp->has_sign() && op_=='R'); - lp = pad_to_width(lp, use_wid + shift, *this); + return tmp; } tmp = new NetEConst(verinum(shift)); tmp->set_line(*this); - long tmp_wid = lp->expr_width() - shift; - if (tmp_wid > use_wid) - tmp_wid = use_wid; - ivl_assert(*this, tmp_wid > 0); - ivl_assert(*this, use_wid > 0); + // Pad the left expression enough to cover the right shift. + lp->cast_signed(signed_flag_ && op_=='R'); + lp = pad_to_width(lp, expr_wid + shift, *this); // Implement the right-shift by part-selecting the low - // bits out. Pad the result of the part select back out - // to the desired size. - tmp = new NetESelect(lp, tmp, tmp_wid); + // bits out. + tmp = new NetESelect(lp, tmp, expr_wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); - tmp->cast_signed(lp->has_sign() && op_=='R'); - tmp = pad_to_width(tmp, use_wid, *this); + return tmp; } // Fallback, handle the general case. - if (expr_wid > 0) - lp = pad_to_width(lp, expr_wid, *this); - tmp = new NetEBShift(op_, lp, rp); + tmp = new NetEBShift(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); + return tmp; } NetExpr* PEBinary::elaborate_expr_base_mult_(Design*, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { - // First, Make sure that signed arguments are padded to the - // width of the output. This is necessary for 2s complement - // multiplication to come out right. - if (expr_wid > 0) { - if (lp->has_sign() && lp->expr_type() != IVL_VT_REAL) - lp = pad_to_width(lp, expr_wid, *this); - if (rp->has_sign() && rp->expr_type() != IVL_VT_REAL) - rp = pad_to_width(rp, expr_wid, *this); - } - // Keep constants on the right side. if (dynamic_cast(lp)) { NetExpr*tmp = lp; @@ -767,100 +603,65 @@ NetExpr* PEBinary::elaborate_expr_base_mult_(Design*, if (NetEConst*rp_const = dynamic_cast (rp)) { verinum rp_val = rp_const->value(); - int use_wid = expr_wid; - if (use_wid < 0) - use_wid = max(rp->expr_width(), lp->expr_width()); - if (! rp_val.is_defined()) { - NetEConst*tmp = make_const_x(use_wid); + NetEConst*tmp = make_const_x(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + return tmp; } if (rp_val.is_zero()) { - NetEConst*tmp = make_const_0(use_wid); + NetEConst*tmp = make_const_0(expr_wid); + tmp->cast_signed(signed_flag_); + tmp->set_line(*this); + return tmp; } } - // If this expression is unsigned, then make sure the - // arguments are unsigned so that the padding below doesn't - // cause any sign extension to happen. - suppress_binary_operand_sign_if_needed(lp, rp); - - // Multiply will guess a width that is the sum of the - // widths of the operand. If that sum is too small, then - // pad one of the arguments enough that the sum is the - // desired width. - if (expr_wid > (long)(lp->expr_width() + rp->expr_width())) - lp = pad_to_width(lp, expr_wid - rp->expr_width(), *this); - - NetEBMult*tmp = new NetEBMult(op_, lp, rp); + NetEBMult*tmp = new NetEBMult(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); - if (expr_wid > 0 && type_is_vectorable(tmp->expr_type())) - tmp->set_width(expr_wid, false); - return tmp; } -NetExpr* PEBinary::elaborate_expr_base_add_(Design*, - NetExpr*lp, NetExpr*rp, - int expr_wid) const +unsigned PEBComp::test_width(Design*des, NetScope*scope, width_mode_t&) { - NetExpr*tmp; - bool use_lossless_flag = expr_wid == -2; + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // If this expression is not vectorable, then do NOT pass the - // lossless flag to the NetEBAdd constructor. For non- - // vectorable, lossless is implicit. - if (! type_is_vectorable(lp->expr_type())) - use_lossless_flag = false; - if (! type_is_vectorable(rp->expr_type())) - use_lossless_flag = false; + // The width and type of a comparison are fixed and well known. + expr_type_ = IVL_VT_LOGIC; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; - // If the expression is unsigned, then force the operands to - // unsigned so that the set_width below doesn't cause them to - // be sign-extended. - suppress_binary_operand_sign_if_needed(lp, rp); + // The widths of the operands are semi-self-determined. They + // affect each other, but not the result. + width_mode_t mode = SIZED; - tmp = new NetEBAdd(op_, lp, rp, use_lossless_flag); - if (expr_wid > 0 && type_is_vectorable(tmp->expr_type())) - tmp->set_width(expr_wid); + unsigned l_width = left_->test_width(des, scope, mode); - tmp->set_line(*this); - return tmp; -} + width_mode_t saved_mode = mode; -unsigned PEBComp::test_width(Design*des, NetScope*scope, unsigned, unsigned, - ivl_variable_type_t&my_expr_type, - bool&) -{ - // The width and type of a comparison operator is fixed and - // well known. Set them now. - expr_type_ = IVL_VT_LOGIC; - my_expr_type = expr_type_; - expr_width_ = 1; + unsigned r_width = right_->test_width(des, scope, mode); - // The widths of operands are self-determined, but need to be - // figured out. - bool unsized_flag = false; - ivl_variable_type_t left_type = IVL_VT_NO_TYPE; - unsigned left_width = left_->test_width(des, scope, 0, 0, left_type, unsized_flag); - bool left_unsized_flag = unsized_flag; - ivl_variable_type_t right_type = IVL_VT_NO_TYPE; - unsigned right_width = right_->test_width(des, scope, 0, 0, right_type, unsized_flag); + // If the width mode changed, retest the left operand, as it + // may choose a different width if it is in an unsized context. + if ((mode != SIZED) && (saved_mode == SIZED)) + l_width = left_->test_width(des, scope, mode); - if (left_unsized_flag != unsized_flag) { - left_width = left_->test_width(des, scope, 0, 0, left_type, unsized_flag); - } + ivl_variable_type_t l_type = left_->expr_type(); + ivl_variable_type_t r_type = right_->expr_type(); - int try_wid_l = left_width; - if (type_is_vectorable(left_type) && (right_width > left_width)) - try_wid_l = right_width; + l_width_ = l_width; + if (type_is_vectorable(l_type) && (r_width > l_width)) + l_width_ = r_width; - int try_wid_r = right_width; - if (type_is_vectorable(right_type) && (left_width > right_width)) - try_wid_r = left_width; + r_width_ = r_width; + if (type_is_vectorable(r_type) && (l_width > r_width)) + r_width_ = l_width; // If the expression is unsized and smaller then the integer // minimum, then tweak the size up. @@ -869,52 +670,48 @@ unsigned PEBComp::test_width(Design*des, NetScope*scope, unsigned, unsigned, // exactly the right width to behave just like infinite // width. I suspect that adding 1 more is sufficient in all // cases, but I'm not certain. Ideas? - if (type_is_vectorable(left_type) && unsized_flag && try_wid_l<(int)integer_width) - try_wid_l += 1; - if (type_is_vectorable(right_type) && unsized_flag && try_wid_r<(int)integer_width) - try_wid_r += 1; + if (mode != SIZED) { + if (type_is_vectorable(l_type) && (l_width_ < integer_width)) + l_width_ += 1; + if (type_is_vectorable(r_type) && (r_width_ < integer_width)) + r_width_ += 1; + } if (debug_elaborate) { cerr << get_fileline() << ": debug: " << "Comparison expression operands are " - << left_width << " bits and " - << right_width << " bits. Resorting to " - << try_wid_l << " bits and " - << try_wid_r << " bits." << endl; + << l_type << " " << l_width << " bits and " + << r_type << " " << r_width << " bits. Resorting to " + << l_width_ << " bits and " + << r_width_ << " bits." << endl; } - left_width_ = try_wid_l; - right_width_ = try_wid_r; - - return 1; + return expr_width_; } NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, - int, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - NetExpr*lp = left_->elaborate_expr(des, scope, left_width_, false); - NetExpr*rp = right_->elaborate_expr(des, scope, right_width_, false); + // Propagate the comparison type (signed/unsigned) down to + // the operands. + if (type_is_vectorable(left_->expr_type()) && !left_->has_sign()) + right_->cast_signed(false); + if (type_is_vectorable(right_->expr_type()) && !right_->has_sign()) + left_->cast_signed(false); + + NetExpr*lp = left_->elaborate_expr(des, scope, l_width_, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width_, false); if ((lp == 0) || (rp == 0)) { delete lp; delete rp; return 0; } - suppress_binary_operand_sign_if_needed(lp, rp); - - // The arguments of a compare need to have matching widths, so - // pad the width here. This matters because if the arguments - // are signed, then this padding will do sign extension. - if (type_is_vectorable(lp->expr_type())) - lp = pad_to_width(lp, left_width_, *this); - if (type_is_vectorable(rp->expr_type())) - rp = pad_to_width(rp, right_width_, *this); - - eval_expr(lp, left_width_); - eval_expr(rp, right_width_); + eval_expr(lp, l_width_); + eval_expr(rp, r_width_); // Handle some operand-specific special cases... switch (op_) { @@ -933,49 +730,36 @@ NetExpr* PEBComp::elaborate_expr(Design*des, NetScope*scope, break; } - NetEBComp*tmp = new NetEBComp(op_, lp, rp); + NetExpr*tmp = new NetEBComp(op_, lp, rp); tmp->set_line(*this); - bool flag = tmp->set_width(1); - if (flag == false) { - cerr << get_fileline() << ": internal error: " - "expression bit width of comparison != 1." << endl; - des->errors += 1; - } + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); return tmp; } -unsigned PEBLogic::test_width(Design*, NetScope*, - unsigned, unsigned, - ivl_variable_type_t&expr_type_out, - bool&) +unsigned PEBLogic::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_LOGIC; - expr_width_ = 1; - expr_type_out = expr_type_; + // The width and type of a logical operation are fixed. + expr_type_ = IVL_VT_LOGIC; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + + // The widths of the operands are self determined. We don't need + // them now, so they can be tested when they are elaborated. + return expr_width_; } NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, - int, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The left and right expressions are self-determined and - // independent. Run their test_width methods independently. We - // don't need the widths here, but we do need the expressions - // to calculate their self-determined width and type. - - bool left_flag = false; - ivl_variable_type_t left_type = IVL_VT_NO_TYPE; - left_->test_width(des, scope, 0, 0, left_type, left_flag); - - bool right_flag = false; - ivl_variable_type_t right_type = IVL_VT_NO_TYPE; - right_->test_width(des, scope, 0, 0, right_type, right_flag); - - NetExpr*lp = elab_and_eval(des, scope, left_, -1); + NetExpr*lp = elab_and_eval(des, scope, left_, -1); NetExpr*rp = elab_and_eval(des, scope, right_, -1); if ((lp == 0) || (rp == 0)) { delete lp; @@ -986,103 +770,163 @@ NetExpr*PEBLogic::elaborate_expr(Design*des, NetScope*scope, lp = condition_reduce(lp); rp = condition_reduce(rp); - NetEBLogic*tmp = new NetEBLogic(op_, lp, rp); + NetExpr*tmp = new NetEBLogic(op_, lp, rp); tmp->set_line(*this); - tmp->set_width(1); + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); return tmp; } -unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - unsigned wid_left = left_->test_width(des,scope,min, lval, expr_type__, unsized_flag); + ivl_assert(*this, left_); + ivl_assert(*this, right_); - // The right expression is self-determined and has no impact - // on the expression size that is generated. + // The right operand is self determined. Test its type and + // width for use later. We only need to know its width now + // if the left operand is unsized and we need to calculate + // the lossless width. + width_mode_t r_mode = SIZED; + unsigned r_width = right_->test_width(des, scope, r_mode); - if (wid_left < min) - wid_left = min; - if (wid_left < lval) - wid_left = lval; + expr_width_ = left_->test_width(des, scope, mode); + expr_type_ = left_->expr_type(); + signed_flag_ = left_->has_sign(); - if (unsized_flag - && type_is_vectorable(expr_type__) - && wid_left > 0 - && wid_left < integer_width) { - wid_left = integer_width; + if ((mode != SIZED) && type_is_vectorable(expr_type_)) { + // We need to make our best guess at the right operand + // value, to minimise the calculated width. This is + // particularly important for the power operator... - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of unsized " << human_readable_op(op_) - << " is padded to compiler integer width=" << wid_left - << endl; + // Start off by assuming the maximum value for the + // type and width of the right operand. + long r_val = LONG_MAX; + if (r_width < sizeof(long)*8) { + r_val = (1L << r_width) - 1L; + if (right_->has_sign()) + r_val >>= 1; + } + + // If the right operand is constant, we can use the + // actual value. + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + if (rp) { + eval_expr(rp, r_width); + } else { + // error recovery + PEVoid*tmp = new PEVoid(); + tmp->set_line(*this); + delete right_; + right_ = tmp; + } + NetEConst*rc = dynamic_cast (rp); + if (rc && (r_width < sizeof(long)*8)) + r_val = rc->value().as_long(); + + // Clip to a sensible range to avoid underflow/overflow + // in the following calculations. 1024 bits should be + // enough for anyone... + if (r_val < 0) + r_val = 0; + if (r_val > 1024) + r_val = 1024; + + // If the left operand is a simple unsized number, we + // can calculate the actual width required for the power + // operator. + PENumber*lc = dynamic_cast (left_); + + // Now calculate the lossless width. + unsigned use_width = expr_width_; + switch (op_) { + case 'l': // << + use_width += (unsigned)r_val; + break; + + case 'r': // >> + case 'R': // >>> + // A logical shift will effectively coerce a signed + // operand to unsigned. We have to assume an arithmetic + // shift may do the same, as we don't yet know the final + // expression type. + if ((mode == LOSSLESS) && signed_flag_) + mode = UNSIZED; + break; + + case 'p': // ** + if (lc && rc) { + verinum result = pow(lc->value(), rc->value()); + use_width = result.len(); + } else { + if (signed_flag_) use_width -= 1; + use_width *= (unsigned)r_val; + if (signed_flag_) use_width += 2; + } + break; + + default: + cerr << get_fileline() << ": internal error: " + << "Unexpected opcode " << human_readable_op(op_) + << " in PEBLeftWidth::test_width." << endl; + des->errors += 1; + } + + // If the right operand is not constant, we could end up + // grossly overestimating the required width. So in this + // case, don't expand beyond the width of an integer + // (which meets the requirements of the standard). + if ((rc == 0) && (use_width > expr_width_) && (use_width > integer_width)) + use_width = integer_width; + + expr_width_ = use_width; } - expr_type_ = expr_type__; - expr_width_ = wid_left; + if (op_ == 'l') + min_width_ = left_->min_width(); + else + min_width_ = expr_width_; - // Run a test-width on the shift amount so that its types are - // worked out for elaboration later on. We don't need the - // value now. - ivl_variable_type_t rtype = IVL_VT_NO_TYPE; - bool rflag = false; - unsigned wid_right = right_->test_width(des, scope, 0, 0, rtype, rflag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of exponent of " << op_ << " expression " - << "returns wid=" << wid_right << ", type=" << rtype - << ", flag=" << rflag << endl; - - return expr_width_; + return fix_width_(mode); } NetExpr*PEBLeftWidth::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { - assert(left_); - assert(right_); + ivl_assert(*this, left_); - NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false); - if (expr_wid > 0 && lp->expr_width() < (unsigned)expr_wid) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Pad left operand of " << human_readable_op(op_) - << " to " << expr_wid << "." << endl; - lp = pad_to_width(lp, expr_wid, *this); - } + unsigned r_width = right_->expr_width(); - NetExpr*rp = right_->elaborate_expr(des, scope, -1, false); - if ((lp == 0) || (rp == 0)) { + NetExpr*lp = left_->elaborate_expr(des, scope, expr_wid, false); + NetExpr*rp = right_->elaborate_expr(des, scope, r_width, false); + if (lp == 0 || rp == 0) { delete lp; delete rp; return 0; } - - eval_expr(lp); - eval_expr(rp); + eval_expr(lp, expr_wid); + eval_expr(rp, r_width); return elaborate_expr_leaf(des, lp, rp, expr_wid); } NetExpr*PEBPower::elaborate_expr_leaf(Design*, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { if (debug_elaborate) { cerr << get_fileline() << ": debug: elaborate expression " << *this << " expr_wid=" << expr_wid << endl; } - NetExpr*tmp = new NetEBPow(op_, lp, rp); + NetExpr*tmp = new NetEBPow(op_, lp, rp, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, - int expr_wid) const + unsigned expr_wid) const { NetExpr*tmp = 0; @@ -1107,85 +951,91 @@ NetExpr*PEBShift::elaborate_expr_leaf(Design*des, NetExpr*lp, NetExpr*rp, } unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + width_mode_t&mode) { perm_string name = peek_tail_name(path_); - if (name=="$signed"|| name=="$unsigned") { + if (name=="$signed" || name=="$unsigned") { PExpr*expr = parms_[0]; if (expr == 0) return 0; - // The argument width is self-determined. - expr_width_ = expr->test_width(des, scope, 0, 0, expr_type__, unsized_flag); - expr_type_ = expr_type__; + // The argument type/width is self-determined, but affects + // the result width. + width_mode_t arg_mode = SIZED; + expr_width_ = expr->test_width(des, scope, arg_mode); + expr_type_ = expr->expr_type(); + min_width_ = expr->min_width(); + signed_flag_ = (name[1] == 's'); - // The result width is context dependent. - if (expr_width_ > min) - min = expr_width_; + if ((arg_mode != SIZED) && type_is_vectorable(expr_type_)) { + if (mode < LOSSLESS) + mode = LOSSLESS; + if (expr_width_ < integer_width) + expr_width_ = integer_width; + } if (debug_elaborate) - cerr << get_fileline() << ": debug: $signed/$unsigned" - << " argument width = " << expr_width_ - << ", result width = " << min << "." << endl; + cerr << get_fileline() << ": debug: " << name + << " argument width = " << expr_width_ << "." << endl; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - return min; - } - - // Run through the arguments of the system function and make - // sure their widths/types are calculated. They are all self- - // determined. - for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - PExpr*expr = parms_[idx]; - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - unsigned wid = expr->test_width(des,scope,0,0,sub_type,flag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: test_width" - << " of " << name << " argument " << idx+1 - << " returns type=" << sub_type - << ", wid=" << wid << endl; + return expr_width_; } if (name=="$sizeof" || name=="$bits") { + PExpr*expr = parms_[0]; + if (expr == 0) + return 0; + + // The argument type/width is self-determined and doesn't + // affect the result type/width. + width_mode_t arg_mode = SIZED; + expr->test_width(des, scope, arg_mode); + + expr_type_ = IVL_VT_BOOL; + expr_width_ = integer_width; + min_width_ = integer_width; + signed_flag_ = false; + if (debug_elaborate) cerr << get_fileline() << ": debug: test_width" - << " of $sizeof/$bits returns test_width" + << " of " << name << " returns test_width" << " of compiler integer." << endl; - expr_type_ = IVL_VT_BOOL; - expr_width_= integer_width; - - expr_type__ = expr_type_; return expr_width_; } if (name=="$is_signed") { + PExpr*expr = parms_[0]; + if (expr == 0) + return 0; + + // The argument type/width is self-determined and doesn't + // affect the result type/width. + width_mode_t arg_mode = SIZED; + expr->test_width(des, scope, arg_mode); + + expr_type_ = IVL_VT_BOOL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + if (debug_elaborate) cerr << get_fileline() << ": debug: test_width" << " of $is_signed returns test_width" << " of 1." << endl; - expr_type_ = IVL_VT_BOOL; - expr_width_ = 1; - expr_type__ = expr_type_; return expr_width_; } /* Get the return type of the system function by looking it up in the sfunc_table. */ - const struct sfunc_return_type*sfunc_info - = lookup_sys_func(peek_tail_name(path_)); + const struct sfunc_return_type*sfunc_info = lookup_sys_func(name); - expr_type_ = sfunc_info->type; - expr_width_ = sfunc_info->wid; - - expr_type__ = expr_type_; + expr_type_ = sfunc_info->type; + expr_width_ = sfunc_info->wid; + min_width_ = expr_width_; + signed_flag_ = sfunc_info->signed_flag; if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " @@ -1197,12 +1047,10 @@ unsigned PECallFunction::test_width_sfunc_(Design*des, NetScope*scope, } unsigned PECallFunction::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) + width_mode_t&mode) { if (peek_tail_name(path_)[0] == '$') - return test_width_sfunc_(des, scope, min, lval, expr_type__, unsized_flag); + return test_width_sfunc_(des, scope, mode); // The width of user defined functions depends only on the // width of the return value. The arguments are entirely @@ -1212,9 +1060,11 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, // If this is an access function, then the width and // type are known by definition. if (find_access_function(path_)) { - expr_type_ = IVL_VT_REAL; - expr_width_ = 1; - expr_type__ = expr_type_; + expr_type_ = IVL_VT_REAL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; + return expr_width_; } @@ -1229,18 +1079,17 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, assert(dscope); if (NetNet*res = dscope->find_signal(dscope->basename())) { - expr_type_ = res->data_type(); + expr_type_ = res->data_type(); + expr_width_ = res->vector_width(); + min_width_ = expr_width_; + signed_flag_ = res->get_signed(); + if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " - << "of function returns width " << res->vector_width() + << "of function returns width " << expr_width_ << ", type=" << expr_type_ << "." << endl; - if (! type_is_vectorable(expr_type__)) - unsized_flag = true; - - expr_width_ = res->vector_width(); - expr_type__ = expr_type_; return expr_width_; } @@ -1248,44 +1097,28 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, return 0; } -NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, int wid, bool signed_flag) const +NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, unsigned wid) const { /* If the expression is a const, then replace it with a new const. This is a more efficient result. */ if (NetEConst*tmp = dynamic_cast(expr)) { - /* If this is an unsized signed constant then we may need - * to extend it up to integer_width. */ - if (! tmp->has_width() && tmp->has_sign() && (wid > 0)) { - unsigned pwidth = tmp->expr_width(); - if ((unsigned)wid > integer_width) { - if (integer_width > pwidth) pwidth = integer_width; - } else { - if ((unsigned)wid > pwidth) pwidth = wid; - } - tmp = dynamic_cast(pad_to_width(tmp, pwidth, - *expr)); - assert(tmp); - } - tmp->cast_signed(signed_flag); - if (wid > (int)(tmp->expr_width())) { - verinum oval = pad_to_width(tmp->value(), wid); - tmp = new NetEConst(oval); + tmp->cast_signed(signed_flag_); + if (wid > tmp->expr_width()) { + tmp = new NetEConst(verinum(tmp->value(), wid)); tmp->set_line(*this); delete expr; } return tmp; } - if (wid < 0) - wid = expr->expr_width(); - if (debug_elaborate) cerr << get_fileline() << ": debug: cast to " << wid - << " bits" << endl; + << " bits " << (signed_flag_?"signed":"unsigned") << endl; NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); - tmp->cast_signed(signed_flag); + return tmp; } @@ -1295,16 +1128,18 @@ NetExpr*PECallFunction::cast_to_width_(NetExpr*expr, int wid, bool signed_flag) * size_tf functions, make assumptions about widths based on some * known function names. */ -NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_wid) const +NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, + unsigned expr_wid) const { + perm_string name = peek_tail_name(path_); /* Catch the special case that the system function is the $signed function. Its argument will be evaluated as a self-determined expression. */ - if (strcmp(peek_tail_name(path_), "$signed") == 0) { + if (name=="$signed" || name=="$unsigned") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $signed() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } @@ -1312,93 +1147,54 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w PExpr*expr = parms_[0]; NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true); - return cast_to_width_(sub, expr_wid, true); - } - /* As above, for the $unsigned function. */ - if (strcmp(peek_tail_name(path_), "$unsigned") == 0) { - if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $unsigned() function " - << "takes exactly one(1) argument." << endl; - des->errors += 1; - return 0; - } - - PExpr*expr = parms_[0]; - NetExpr*sub = expr->elaborate_expr(des, scope, expr_width_, true); - - return cast_to_width_(sub, expr_wid, false); + return cast_to_width_(sub, expr_wid); } /* Interpret the internal $sizeof system function to return the bit width of the sub-expression. The value of the sub-expression is not used, so the expression itself can be deleted. */ - if ((strcmp(peek_tail_name(path_), "$sizeof") == 0) - || (strcmp(peek_tail_name(path_), "$bits") == 0)) { + if (name=="$sizeof" || name=="$bits") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $bits() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } - if (strcmp(peek_tail_name(path_), "$sizeof") == 0) + if (name=="$sizeof") cerr << get_fileline() << ": warning: $sizeof is deprecated." << " Use $bits() instead." << endl; PExpr*expr = parms_[0]; - ivl_assert(*this, expr); - /* Elaborate the sub-expression to get its - self-determined width, and save that width. Then - delete the expression because we don't really want - the expression itself. */ - long sub_expr_width = 0; - if (NetExpr*tmp = expr->elaborate_expr(des, scope, -1, true)) { - sub_expr_width = tmp->expr_width(); - delete tmp; - } - - verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned)); + verinum val ( (uint64_t)expr->expr_width(), integer_width); NetEConst*sub = new NetEConst(val); sub->set_line(*this); - return sub; + return cast_to_width_(sub, expr_wid); } /* Interpret the internal $is_signed system function to return a single bit flag -- 1 if the expression is signed, 0 - otherwise. The subexpression is elaborated but not - evaluated. */ - if (strcmp(peek_tail_name(path_), "$is_signed") == 0) { + otherwise. */ + if (name=="$is_signed") { if ((parms_.size() != 1) || (parms_[0] == 0)) { - cerr << get_fileline() << ": error: The $is_signed() function " - << "takes exactly one(1) argument." << endl; + cerr << get_fileline() << ": error: The " << name + << " function takes exactly one(1) argument." << endl; des->errors += 1; return 0; } PExpr*expr = parms_[0]; - NetExpr*sub = expr->elaborate_expr(des, scope, -1, true); - verinum val (sub->has_sign()? verinum::V1 : verinum::V0, 1); - delete sub; - - sub = new NetEConst(val); + verinum val (expr->has_sign()? verinum::V1 : verinum::V0, 1); + NetEConst*sub = new NetEConst(val); sub->set_line(*this); - return sub; + return cast_to_width_(sub, expr_wid); } - /* Get the return type of the system function by looking it up - in the sfunc_table. */ - const struct sfunc_return_type*sfunc_info - = lookup_sys_func(peek_tail_name(path_)); - - ivl_variable_type_t sfunc_type = sfunc_info->type; - unsigned wid = sfunc_info->wid; - - /* How many parameters are there? The Verilog language allows empty parameters in certain contexts, so the parser will allow things like func(1,,3). It will also cause func() to @@ -1411,11 +1207,8 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w if ((nparms == 1) && (parms_[0] == 0)) nparms = 0; - NetESFunc*fun = new NetESFunc(peek_tail_name(path_), sfunc_type, - wid, nparms); + NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms); fun->set_line(*this); - if (sfunc_info->signed_flag) - fun->cast_signed(true); /* Now run through the expected parameters. If we find that there are missing parameters, print an error message. @@ -1428,9 +1221,8 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*expr = parms_[idx]; if (expr) { - NetExpr*tmp1 = expr->elaborate_expr(des, scope, -1, true); - eval_expr(tmp1); - fun->parm(idx, tmp1); + NetExpr*tmp = elab_sys_task_arg(des, scope, name, idx, expr); + fun->parm(idx, tmp); } else { missing_parms += 1; @@ -1439,19 +1231,22 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w } if (missing_parms > 0) { - cerr << get_fileline() << ": error: The function " - << peek_tail_name(path_) + cerr << get_fileline() << ": error: The function " << name << " has been called with empty parameters." << endl; cerr << get_fileline() << ": : Verilog doesn't allow " << "passing empty parameters to functions." << endl; des->errors += 1; } - return fun; + NetExpr*tmp = pad_to_width(fun, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; } NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, - ivl_nature_t nature) const + ivl_nature_t nature, + unsigned expr_wid) const { // An access function must have 1 or 2 arguments. ivl_assert(*this, parms_.size()==2 || parms_.size()==1); @@ -1500,14 +1295,17 @@ NetExpr* PECallFunction::elaborate_access_func_(Design*des, NetScope*scope, ivl_assert(*this, 0); } - NetEAccess*tmp = new NetEAccess(branch, nature); + NetExpr*tmp = new NetEAccess(branch, nature); tmp->set_line(*this); + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); + return tmp; } NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { if (peek_tail_name(path_)[0] == '$') return elaborate_sfunc_(des, scope, expr_wid); @@ -1519,7 +1317,8 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, // way. ivl_nature_t access_nature = find_access_function(path_); if (access_nature) - return elaborate_access_func_(des, scope, access_nature); + return elaborate_access_func_(des, scope, access_nature, + expr_wid); // We do not currently support constant user function and // this is where things fail because of that, though we @@ -1546,8 +1345,6 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if ((parms_count == 1) && (parms_[0] == 0)) parms_count = 0; - - svector parms (parms_count); /* Elaborate the input expressions for the function. This is @@ -1561,7 +1358,7 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, if (tmp) { parms[idx] = elaborate_rval_expr(des, scope, def->port(idx)->data_type(), - def->port(idx)->vector_width(), + (unsigned)def->port(idx)->vector_width(), tmp); if (NetEEvent*evt = dynamic_cast (parms[idx])) { cerr << evt->get_fileline() << ": error: An event '" @@ -1603,8 +1400,11 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, NetESignal*eres = new NetESignal(res); NetEUFunc*func = new NetEUFunc(scope, dscope, eres, parms); func->set_line(*this); - func->cast_signed(res->get_signed()); - return func; + + NetExpr*tmp = pad_to_width(func, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; } cerr << get_fileline() << ": internal error: Unable to locate " @@ -1614,76 +1414,23 @@ NetExpr* PECallFunction::elaborate_expr(Design*des, NetScope*scope, return 0; } -unsigned PEConcat::test_width(Design*des, NetScope*scope, - unsigned, unsigned, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEConcat::test_width(Design*des, NetScope*scope, width_mode_t&) { - expr_type_ = IVL_VT_LOGIC; - - unsigned count_width = 0; + expr_width_ = 0; for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - tested_widths_[idx] = parms_[idx]->test_width(des, scope, 0, 0, expr_type__, unsized_flag); - count_width += tested_widths_[idx]; + expr_width_ += parms_[idx]->test_width(des, scope, width_modes_[idx]); } - if (repeat_) { - unsigned repeat_count = 1; - - // The repeat expression is self-determined and - // its own type. - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - repeat_->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - - // Try to evaluate the repeat expression now, so - // that we can give the caller an accurate - // expression width. - NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1); - if (NetEConst*tmp_c = dynamic_cast (tmp)) { - repeat_count = tmp_c->value().as_ulong(); - - } else { - // Gack! Can't evaluate expression yet! - // Unfortunately, it is possible that this - // expression may turn out to be constant later in - // elaboration, so we can't really get away with - // reporting an error. - repeat_count = 1; - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "CONCAT MISSING TEST_WIDTH WHEN REPEAT IS PRESENT!" - << endl; - } - count_width *= repeat_count; - } - - expr_type__ = expr_type_; - unsized_flag = false; - return count_width; -} - -// Keep track of the concatenation/repeat depth. -static int concat_depth = 0; - -NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const -{ - concat_depth += 1; - NetExpr* repeat = 0; - - if (debug_elaborate) { - cerr << get_fileline() << ": debug: Elaborate expr=" << *this - << ", expr_wid=" << expr_wid << endl; - } + expr_type_ = IVL_VT_LOGIC; + signed_flag_ = false; /* If there is a repeat expression, then evaluate the constant value and set the repeat count. */ - if (repeat_) { + if (repeat_ && (scope != tested_scope_)) { need_constant_expr = true; NetExpr*tmp = elab_and_eval(des, scope, repeat_, -1); need_constant_expr = false; - assert(tmp); + if (tmp == 0) return 0; if (tmp->expr_type() == IVL_VT_REAL) { cerr << tmp->get_fileline() << ": error: Concatenation " @@ -1696,7 +1443,7 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, if (rep == 0) { cerr << get_fileline() << ": error: " - "Concatenation repeat expression cannot be evaluated." + "Concatenation repeat expression is not constant." << endl; cerr << get_fileline() << ": : The expression is: " << *tmp << endl; @@ -1709,7 +1456,6 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, << "may not be undefined (" << rep->value() << ")." << endl; des->errors += 1; - concat_depth -= 1; return 0; } @@ -1718,23 +1464,43 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, << "may not be negative (" << rep->value().as_long() << ")." << endl; des->errors += 1; - concat_depth -= 1; return 0; } - if (rep->value().is_zero() && concat_depth < 2) { - cerr << get_fileline() << ": error: Concatenation repeat " - << "may not be zero in this context." << endl; - des->errors += 1; - concat_depth -= 1; - return 0; - } + repeat_count_ = rep->value().as_ulong(); - repeat = rep; + tested_scope_ = scope; + } + expr_width_ *= repeat_count_; + min_width_ = expr_width_; + + return expr_width_; +} + +// Keep track of the concatenation/repeat depth. +static int concat_depth = 0; + +NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, + unsigned expr_wid, bool) const +{ + concat_depth += 1; + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: Elaborate expr=" << *this + << ", expr_wid=" << expr_wid << endl; + } + + if (repeat_count_ == 0 && concat_depth < 2) { + cerr << get_fileline() << ": error: Concatenation repeat " + << "may not be zero in this context." << endl; + des->errors += 1; + concat_depth -= 1; + return 0; } unsigned wid_sum = 0; unsigned parm_cnt = 0; + unsigned parm_errors = 0; svector parms(parms_.size()); /* Elaborate all the parameters and attach them to the concat node. */ @@ -1747,25 +1513,29 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, } assert(parms_[idx]); - NetExpr*ex = elab_and_eval(des, scope, parms_[idx], - tested_widths_[idx], 0); + unsigned wid = parms_[idx]->expr_width(); + NetExpr*ex = parms_[idx]->elaborate_expr(des, scope, wid, false); if (ex == 0) continue; ex->set_line(*parms_[idx]); + eval_expr(ex, -1); + if (ex->expr_type() == IVL_VT_REAL) { cerr << ex->get_fileline() << ": error: " << "Concatenation operand can not be real: " << *parms_[idx] << endl; des->errors += 1; + parm_errors += 1; continue; } - if (! ex->has_width()) { + if (width_modes_[idx] != SIZED) { cerr << ex->get_fileline() << ": error: " << "Concatenation operand \"" << *parms_[idx] << "\" has indefinite width." << endl; des->errors += 1; + parm_errors += 1; continue; } @@ -1778,29 +1548,34 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, } wid_sum += ex->expr_width(); } + if (parm_errors) { + concat_depth -= 1; + return 0; + } /* Make the empty concat expression. */ - NetEConcat*tmp = new NetEConcat(parm_cnt, repeat); - tmp->set_line(*this); + NetEConcat*concat = new NetEConcat(parm_cnt, repeat_count_); + concat->set_line(*this); /* Remove any zero width constants. */ unsigned off = 0; for (unsigned idx = 0 ; idx < parm_cnt ; idx += 1) { while (parms[off+idx] == 0) off += 1; - tmp->set(idx, parms[off+idx]); + concat->set(idx, parms[off+idx]); } - tmp->set_width(wid_sum * tmp->repeat()); - if (wid_sum == 0 && concat_depth < 2) { cerr << get_fileline() << ": error: Concatenation may not " << "have zero width in this context." << endl; des->errors += 1; concat_depth -= 1; - delete tmp; + delete concat; return 0; } + NetExpr*tmp = pad_to_width(concat, expr_wid, *this); + tmp->cast_signed(signed_flag_); + concat_depth -= 1; return tmp; } @@ -1812,24 +1587,20 @@ NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, * * Absent any better idea, we call all real valued results a width of 1. */ -unsigned PEFNumber::test_width(Design*, NetScope*, - unsigned, unsigned, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEFNumber::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_REAL; - expr_width_ = 1; - unsized_flag = false; + expr_type_ = IVL_VT_REAL; + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; - expr_type__ = expr_type_; - return 1; + return expr_width_; } -NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, int, bool) const +NetExpr* PEFNumber::elaborate_expr(Design*, NetScope*, unsigned, bool) const { NetECReal*tmp = new NetECReal(*value_); tmp->set_line(*this); - tmp->set_width(1U, false); return tmp; } @@ -1849,20 +1620,12 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, ivl_assert(*this, index_tail.sel == index_component_t::SEL_PART); ivl_assert(*this, index_tail.msb && index_tail.lsb); - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool tmp_flag = false; - int msb_wid = index_tail.msb->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - - tmp_type = IVL_VT_NO_TYPE; - tmp_flag = false; - int lsb_wid = index_tail.lsb->test_width(des, scope, 0, 0, tmp_type, tmp_flag); - /* This handles part selects. In this case, there are two bit select expressions, and both must be constant. Evaluate them and pass the results back to the caller. */ need_constant_expr = true; - NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, lsb_wid); + NetExpr*lsb_ex = elab_and_eval(des, scope, index_tail.lsb, -1); need_constant_expr = false; NetEConst*lsb_c = dynamic_cast(lsb_ex); if (lsb_c == 0) { @@ -1882,7 +1645,7 @@ bool PEIdent::calculate_parts_(Design*des, NetScope*scope, } need_constant_expr = true; - NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, msb_wid); + NetExpr*msb_ex = elab_and_eval(des, scope, index_tail.msb, -1); need_constant_expr = false; NetEConst*msb_c = dynamic_cast(msb_ex); if (msb_c == 0) { @@ -1920,7 +1683,6 @@ bool PEIdent::calculate_up_do_width_(Design*des, NetScope*scope, /* Calculate the width expression (in the lsb_ position) first. If the expression is not constant, error but guess 1 so we can keep going and find more errors. */ - probe_expr_width(des, scope, index_tail.lsb); need_constant_expr = true; NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1); need_constant_expr = false; @@ -1953,7 +1715,6 @@ NetExpr* PEIdent::calculate_up_do_base_(Design*des, NetScope*scope) const ivl_assert(*this, index_tail.lsb != 0); ivl_assert(*this, index_tail.msb != 0); - probe_expr_width(des, scope, index_tail.msb); NetExpr*tmp = elab_and_eval(des, scope, index_tail.msb, -1); return tmp; } @@ -1985,23 +1746,7 @@ bool PEIdent::calculate_param_range_(Design*, NetScope*, return true; } -static void probe_index_expr_width(Design*des, NetScope*scope, - const name_component_t&name) -{ - for (list::const_iterator cur = name.index.begin() - ; cur != name.index.end() ; ++ cur ) { - - if (cur->msb) - probe_expr_width(des, scope, cur->msb); - if (cur->lsb) - probe_expr_width(des, scope, cur->lsb); - } -} - -unsigned PEIdent::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEIdent::test_width(Design*des, NetScope*scope, width_mode_t&mode) { NetNet* net = 0; const NetExpr*par = 0; @@ -2018,7 +1763,6 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; if (!name_tail.index.empty()) { - probe_index_expr_width(des, scope, name_tail); const index_component_t&index_tail = name_tail.index.back(); // Skip full array word net selects. if (!net || (name_tail.index.size() > net->array_dimensions())) { @@ -2059,17 +1803,21 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, } if (use_width != UINT_MAX) { - expr_type_ = IVL_VT_LOGIC; // Assume bit/parts selects are logic - expr_width_ = max(use_width, min); - expr_type__ = expr_type_; + expr_type_ = IVL_VT_LOGIC; // Assume bit/parts selects are logic + expr_width_ = use_width; + min_width_ = use_width; + signed_flag_ = false; + return expr_width_; } // The width of a signal expression is the width of the signal. if (net != 0) { - expr_type_ = net->data_type(); - expr_width_= max(net->vector_width(), (unsigned long)min); - expr_type__ = expr_type_; + expr_type_ = net->data_type(); + expr_width_ = net->vector_width(); + min_width_ = expr_width_; + signed_flag_ = net->get_signed(); + return expr_width_; } @@ -2079,35 +1827,51 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, netenum_t*use_enum = par_enum->enumeration(); ivl_assert(*this, use_enum != 0); - expr_type_ = use_enum->base_type(); - unsized_flag = false; - expr_width_ = max(use_enum->base_width(), min); - expr_type__ = expr_type_; + expr_type_ = use_enum->base_type(); + expr_width_ = use_enum->base_width(); + min_width_ = expr_width_; + signed_flag_ = false; + return expr_width_; } // The width of a parameter is the width of the parameter value // (as evaluated earlier). if (par != 0) { - expr_type_ = par->expr_type(); - expr_width_ = par->expr_width(); - expr_type__ = expr_type_; - if (!par->has_width()) - unsized_flag = true; + expr_type_ = par->expr_type(); + expr_width_ = par->expr_width(); + min_width_ = expr_width_; + signed_flag_ = par->has_sign(); + + if ((mode < LOSSLESS) && !par->has_width()) + mode = LOSSLESS; + return expr_width_; } + if (path_.size() == 1 + && scope->genvar_tmp.str() + && strcmp(peek_tail_name(path_), scope->genvar_tmp) == 0) { + verinum val (scope->genvar_tmp_val); + expr_type_ = IVL_VT_BOOL; + expr_width_ = val.len(); + min_width_ = expr_width_; + signed_flag_ = true; + + if (mode < LOSSLESS) + mode = LOSSLESS; + + return expr_width_; + } + // Not a net, and not a parameter? Give up on the type, but - // set the width that we collected. - expr_type_ = IVL_VT_NO_TYPE; - expr_width_ = min; + // set the width to 0. + expr_type_ = IVL_VT_NO_TYPE; + expr_width_ = 0; + min_width_ = 0; + signed_flag_ = false; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(min >= lval); - - expr_type__ = expr_type_; - return min; + return expr_width_; } /* @@ -2122,7 +1886,7 @@ unsigned PEIdent::test_width(Design*des, NetScope*scope, * The signal name may be escaped, but that affects nothing here. */ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool sys_task_arg) const + unsigned expr_wid, bool sys_task_arg) const { assert(scope); @@ -2145,9 +1909,18 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, ex1, ex2); // If the identifier name is a parameter name, then return - // a reference to the parameter expression. - if (par != 0) - return elaborate_expr_param_(des, scope, par, found_in, ex1, ex2, expr_wid); + // the parameter value. + if (par != 0) { + NetExpr*tmp = elaborate_expr_param_(des, scope, par, found_in, + ex1, ex2, expr_wid); + + if (!tmp) return 0; + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; + } // If this is a parameter expression, no other identifiers are valid. if (is_param_expr) { @@ -2160,8 +1933,17 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, // If the identifier names a signal (a register or wire) // then create a NetESignal node to handle it. - if (net != 0) - return elaborate_expr_net(des, scope, net, found_in, sys_task_arg); + if (net != 0) { + NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, + expr_wid, sys_task_arg); + + if (!tmp) return 0; + + tmp = pad_to_width(tmp, expr_wid, *this); + tmp->cast_signed(signed_flag_); + + return tmp; + } // If the identifier is a named event. // is a variable reference. @@ -2181,7 +1963,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: " << path_ << " is genvar with value " << scope->genvar_tmp_val << "." << endl; - verinum val (scope->genvar_tmp_val); + verinum val (scope->genvar_tmp_val, expr_wid); NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); return tmp; @@ -2259,7 +2041,8 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, return tmp; } - NetExpr*expr = elaborate_expr_net(des, scope, net, found_in, false); + NetExpr*expr = elaborate_expr_net(des, scope, net, found_in, + expr_wid, false); NetESFunc*sys_expr = 0; if (method_name == "name") { @@ -2278,7 +2061,8 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, << "Unknown method name `" << method_name << "'" << " attached to " << use_path << "." << endl; des->errors += 1; - return elaborate_expr_net(des, scope, net, found_in, false); + return elaborate_expr_net(des, scope, net, found_in, + expr_wid, false); } sys_expr->set_line(*this); @@ -2393,7 +2177,8 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, const NetExpr*par, NetScope*, const NetExpr*par_msb, - const NetExpr*par_lsb) const + const NetExpr*par_lsb, + unsigned expr_wid) const { long msv, lsv; bool parts_defined_flag; @@ -2422,7 +2207,8 @@ NetExpr* PEIdent::elaborate_expr_param_part_(Design*des, NetScope*scope, "Replacing select with a constant 'bx." << endl; } - NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false)); + verinum val(verinum::Vx, expr_wid, true); + NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); return tmp; } @@ -2683,7 +2469,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, NetScope*found_in, const NetExpr*par_msb, const NetExpr*par_lsb, - int expr_wid) const + unsigned expr_wid) const { const name_component_t&name_tail = path_.back(); index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; @@ -2705,7 +2491,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, // selects to different methods. if (use_sel == index_component_t::SEL_PART) return elaborate_expr_param_part_(des, scope, par, found_in, - par_msb, par_lsb); + par_msb, par_lsb, expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_param_idx_up_(des, scope, par, found_in, @@ -2719,6 +2505,8 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, // rewritten in the above format, as I get to it. NetExpr*tmp = par->dup_expr(); + if (!tmp) + return 0; if (use_sel == index_component_t::SEL_BIT) { ivl_assert(*this, !name_tail.index.empty()); @@ -2736,8 +2524,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, /* Handle the case where a parameter has a bit select attached to it. Generate a NetESelect object to select the bit as desired. */ - NetExpr*mtmp = index_tail.msb->elaborate_expr(des, scope, -1,false); - eval_expr(mtmp); + NetExpr*mtmp = elab_and_eval(des, scope, index_tail.msb, -1); /* Let's first try to get constant values for both the parameter and the bit select. If they are @@ -2841,21 +2628,21 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Elaborate parameter <" << path_ - << "> as enumeration constant." << endl; + << "> as enumeration constant." << *etmp << endl; tmp = etmp->dup_expr(); + tmp = pad_to_width(tmp, expr_wid, *this); } else { /* No bit or part select. Make the constant into a - NetEConstParam if possible. */ + NetEConstParam or NetECRealParam as appropriate. */ NetEConst*ctmp = dynamic_cast(tmp); if (ctmp != 0) { + verinum cvalue = cast_to_width(ctmp->value(), expr_wid); + perm_string name = peek_tail_name(path_); NetEConstParam*ptmp - = new NetEConstParam(found_in, name, ctmp->value()); - - if (expr_wid > 0) - ptmp->set_width((unsigned)expr_wid); + = new NetEConstParam(found_in, name, cvalue); if (debug_elaborate) cerr << get_fileline() << ": debug: " @@ -2889,6 +2676,7 @@ NetExpr* PEIdent::elaborate_expr_param_(Design*des, */ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, + unsigned expr_wid, bool sys_task_arg) const { const name_component_t&name_tail = path_.back(); @@ -2978,7 +2766,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, } if (word_sel == index_component_t::SEL_PART) - return elaborate_expr_net_part_(des, scope, res, found_in); + return elaborate_expr_net_part_(des, scope, res, found_in, expr_wid); if (word_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_net_idx_up_(des, scope, res, found_in); @@ -2998,7 +2786,8 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, * Handle part selects of NetNet identifiers. */ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*, + unsigned expr_wid) const { long msv, lsv; bool parts_defined_flag; @@ -3028,7 +2817,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, "Replacing select with a constant 'bx." << endl; } - NetEConst*tmp = new NetEConst(verinum(verinum::Vx, 1, false)); + NetEConst*tmp = new NetEConst(verinum(verinum::Vx, expr_wid, true)); tmp->set_line(*this); return tmp; } @@ -3103,7 +2892,7 @@ NetExpr* PEIdent::elaborate_expr_net_part_(Design*des, NetScope*scope, * Part select indexed up, i.e. net[ +: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*) const { NetExpr*base = calculate_up_do_base_(des, scope); @@ -3191,7 +2980,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_up_(Design*des, NetScope*scope, * Part select indexed down, i.e. net[ -: ] */ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, - NetESignal*net, NetScope*)const + NetESignal*net, NetScope*)const { NetExpr*base = calculate_up_do_base_(des, scope); @@ -3275,7 +3064,7 @@ NetExpr* PEIdent::elaborate_expr_net_idx_do_(Design*des, NetScope*scope, } NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, - NetESignal*net, NetScope*) const + NetESignal*net, NetScope*) const { const name_component_t&name_tail = path_.back(); ivl_assert(*this, !name_tail.index.empty()); @@ -3376,10 +3165,12 @@ NetExpr* PEIdent::elaborate_expr_net_bit_(Design*des, NetScope*scope, NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, NetNet*net, NetScope*found_in, + unsigned expr_wid, bool sys_task_arg) const { if (net->array_dimensions() > 0) - return elaborate_expr_net_word_(des, scope, net, found_in, sys_task_arg); + return elaborate_expr_net_word_(des, scope, net, found_in, + expr_wid, sys_task_arg); NetESignal*node = new NetESignal(net); node->set_line(*this); @@ -3403,7 +3194,8 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, // selected bits. The lsb_ and msb_ expressions are from // the foo[msb:lsb] expression in the original. if (use_sel == index_component_t::SEL_PART) - return elaborate_expr_net_part_(des, scope, node, found_in); + return elaborate_expr_net_part_(des, scope, node, found_in, + expr_wid); if (use_sel == index_component_t::SEL_IDX_UP) return elaborate_expr_net_idx_up_(des, scope, node, found_in); @@ -3422,104 +3214,72 @@ NetExpr* PEIdent::elaborate_expr_net(Design*des, NetScope*scope, return node; } -unsigned PENumber::test_width(Design*, NetScope*, - unsigned min, unsigned lval, - ivl_variable_type_t&use_expr_type, - bool&unsized_flag) +unsigned PENumber::test_width(Design*, NetScope*, width_mode_t&mode) { - expr_type_ = IVL_VT_LOGIC; - unsigned use_wid = value_->len(); - if (min > use_wid) - use_wid = min; + expr_type_ = IVL_VT_LOGIC; + expr_width_ = value_->len(); + min_width_ = 1; + signed_flag_ = value_->has_sign(); - if (! value_->has_len()) - unsized_flag = true; + if ((mode < LOSSLESS) && !value_->has_len()) + mode = LOSSLESS; - if (lval > 0 && lval < use_wid) - use_wid = lval; - - use_expr_type = expr_type_; - expr_width_ = use_wid; - return use_wid; + return expr_width_; } NetEConst* PENumber::elaborate_expr(Design*, NetScope*, - int expr_width__, bool) const + unsigned expr_wid, bool) const { assert(value_); - verinum tvalue = *value_; - - // If the expr_width is >0, then the context is requesting a - // specific size (for example this is part of the r-values of - // an assignment) so we pad to the desired width and ignore - // the self-determined size. - if (expr_width__ > 0) { - tvalue = pad_to_width(tvalue, expr_width__); - if ( (tvalue.len() > (unsigned)expr_width__) || - (is_param_expr && !tvalue.has_len()) ) { - verinum tmp (tvalue, expr_width__); - tmp.has_sign(tvalue.has_sign()); - tvalue = tmp; - } - } - - NetEConst*tmp = new NetEConst(tvalue); + verinum val = *value_; + if (val.has_len()) + val.has_sign(signed_flag_); + val = cast_to_width(val, expr_wid); + NetEConst*tmp = new NetEConst(val); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); + return tmp; } -unsigned PEString::test_width(Design*, NetScope*, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&) +unsigned PEString::test_width(Design*, NetScope*, width_mode_t&) { - expr_type_ = IVL_VT_BOOL; - expr_width_ = text_? 8*strlen(text_) : 0; - if (min > expr_width_) - expr_width_ = min; + expr_type_ = IVL_VT_BOOL; + expr_width_ = text_? verinum(text_).len() : 0; + min_width_ = expr_width_; + signed_flag_ = false; - expr_type__ = expr_type_; - // We don't use (need?) lval so make sure the minimum width - // is greater than or equal to the the L-value width. - assert(expr_width_ >= lval); return expr_width_; } NetEConst* PEString::elaborate_expr(Design*, NetScope*, - int, bool) const + unsigned expr_wid, bool) const { - NetEConst*tmp = new NetEConst(value()); + verinum val(value()); + val = pad_to_width(val, expr_wid); + NetEConst*tmp = new NetEConst(val); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); + return tmp; } -unsigned PETernary::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&use_expr_type, - bool&flag) +unsigned PETernary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { - // The condition of the ternary is self-determined, but we - // test its width to force its type to be calculated. - ivl_variable_type_t con_type = IVL_VT_NO_TYPE; - bool con_flag = false; - expr_->test_width(des, scope, 0, 0, con_type, con_flag); + // The condition of the ternary is self-determined, so + // we will test its width when we elaborate it. - ivl_variable_type_t tru_type = IVL_VT_NO_TYPE; - unsigned tru_wid = tru_->test_width(des, scope, min, lval, tru_type,flag); + // Test the width of the true and false clauses. + unsigned tru_width = tru_->test_width(des, scope, mode); - bool initial_flag = flag; - ivl_variable_type_t fal_type = IVL_VT_NO_TYPE; - unsigned fal_wid = fal_->test_width(des, scope, min, lval, fal_type,flag); + width_mode_t saved_mode = mode; - // If the false clause is unsized, then try again with the - // true clause, because it might choose a different width if - // it is in an unsized context. - if (initial_flag == false && flag == true) { - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "False clause is unsized, so retest width of true clause." - << endl; - tru_wid = tru_->test_width(des, scope, max(min,fal_wid), lval, tru_type, flag); + unsigned fal_width = fal_->test_width(des, scope, mode); + + // If the width mode changed, retest the true clause, as it + // may choose a different width if it is in an unsized context. + if ((mode >= LOSSLESS) && (saved_mode < LOSSLESS)) { + tru_width = tru_->test_width(des, scope, mode); } // If either of the alternatives is IVL_VT_REAL, then the @@ -3527,28 +3287,42 @@ unsigned PETernary::test_width(Design*des, NetScope*scope, // of the alternatives is IVL_VT_LOGIC, then the expression as // a whole is IVL_VT_LOGIC. The fallback assumes that the // types are the same and we take that. + ivl_variable_type_t tru_type = tru_->expr_type(); + ivl_variable_type_t fal_type = fal_->expr_type(); + if (tru_type == IVL_VT_REAL || fal_type == IVL_VT_REAL) { expr_type_ = IVL_VT_REAL; - expr_width_ = 1; } else if (tru_type == IVL_VT_LOGIC || fal_type == IVL_VT_LOGIC) { expr_type_ = IVL_VT_LOGIC; - expr_width_ = max(tru_wid,fal_wid); } else { ivl_assert(*this, tru_type == fal_type); expr_type_ = tru_type; - expr_width_ = max(tru_wid,fal_wid); + } + if (expr_type_ == IVL_VT_REAL) { + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = true; + } else { + expr_width_ = max(tru_width, fal_width); + min_width_ = max(tru_->min_width(), fal_->min_width()); + signed_flag_ = tru_->has_sign() && fal_->has_sign(); + + // If the alternatives are different types, the expression + // is forced to unsigned. In this case the lossless width + // calculation is unreliable and we need to make sure the + // final expression width is at least integer_width. + if ((mode == LOSSLESS) && (tru_->has_sign() != fal_->has_sign())) + mode = UNSIZED; } if (debug_elaborate) cerr << get_fileline() << ": debug: " << "Ternary expression type=" << expr_type_ << ", width=" << expr_width_ - << ", unsized_flag=" << flag << " (tru_type=" << tru_type << ", fal_type=" << fal_type << ")" << endl; - use_expr_type = expr_type_; - return expr_width_; + return fix_width_(mode); } bool NetETernary::test_operand_compat(ivl_variable_type_t l, @@ -3576,26 +3350,12 @@ bool NetETernary::test_operand_compat(ivl_variable_type_t l, * methods. If any elaboration fails, then give up and return 0. */ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { assert(expr_); assert(tru_); assert(fal_); - int use_wid = expr_wid >= 0? expr_wid : 0; - - if (expr_wid < 0) { - use_wid = expr_width(); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Self-sized ternary chooses wid="<< use_wid - << ", type=" << expr_type() - << ", expr=" << *this - << endl; - - ivl_assert(*this, use_wid > 0); - } - // Elaborate and evaluate the condition expression. Note that // it is always self-determined. NetExpr*con = elab_and_eval(des, scope, expr_, -1); @@ -3620,16 +3380,8 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: Short-circuit " "elaborate TRUE clause of ternary." << endl; - if (use_wid <= 0) { - cerr << get_fileline() << ": internal error: " - << "Unexpected use_wid=" << use_wid - << " processing short-circuit TRUE clause" - << " of expression: " << *this << endl; - } - ivl_assert(*this, use_wid > 0); - NetExpr*texpr = elab_and_eval_alternative_(des, scope, tru_, - use_wid); - return pad_to_width(texpr, use_wid, *this); + + return elab_and_eval_alternative_(des, scope, tru_, expr_wid); } // Condition is constant FALSE, so we only need the @@ -3639,29 +3391,21 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, cerr << get_fileline() << ": debug: Short-circuit " "elaborate FALSE clause of ternary." << endl; - if (use_wid <= 0) { - cerr << get_fileline() << ": internal error: " - << "Unexpected use_wid=" << use_wid - << " processing short-circuit FALSE clause" - << " of expression: " << *this << endl; - } - ivl_assert(*this, use_wid > 0); - NetExpr*texpr = elab_and_eval_alternative_(des, scope, fal_, - use_wid); - return pad_to_width(texpr, use_wid, *this); + + return elab_and_eval_alternative_(des, scope, fal_, expr_wid); } // X and Z conditions need to blend both results, so we // can't short-circuit. } - NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, use_wid); + NetExpr*tru = elab_and_eval_alternative_(des, scope, tru_, expr_wid); if (tru == 0) { delete con; return 0; } - NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, use_wid); + NetExpr*fal = elab_and_eval_alternative_(des, scope, fal_, expr_wid); if (fal == 0) { delete con; delete tru; @@ -3677,15 +3421,7 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, return 0; } - suppress_binary_operand_sign_if_needed(tru, fal); - - /* Whatever the width we choose for the ternary operator, we - need to make sure the operands match. */ - tru = pad_to_width(tru, use_wid, *this); - fal = pad_to_width(fal, use_wid, *this); - - NetETernary*res = new NetETernary(con, tru, fal); - res->cast_signed(tru->has_sign() && fal->has_sign()); + NetETernary*res = new NetETernary(con, tru, fal, expr_wid, signed_flag_); res->set_line(*this); return res; } @@ -3697,19 +3433,24 @@ NetExpr*PETernary::elaborate_expr(Design*des, NetScope*scope, * self-determined. */ NetExpr* PETernary::elab_and_eval_alternative_(Design*des, NetScope*scope, - PExpr*expr, int use_wid) const + PExpr*expr, unsigned expr_wid) const { + unsigned context_wid = expr_wid; if (type_is_vectorable(expr->expr_type()) && !type_is_vectorable(expr_type_)) { - return elab_and_eval(des, scope, expr, -1); + expr_wid = expr->expr_width(); + context_wid = -1; + } else { + expr->cast_signed(signed_flag_); } + NetExpr*tmp = expr->elaborate_expr(des, scope, expr_wid, false); + if (tmp == 0) return 0; - return elab_and_eval(des, scope, expr, use_wid); + eval_expr(tmp, context_wid); + + return tmp; } -unsigned PEUnary::test_width(Design*des, NetScope*scope, - unsigned min, unsigned lval, - ivl_variable_type_t&expr_type__, - bool&unsized_flag) +unsigned PEUnary::test_width(Design*des, NetScope*scope, width_mode_t&mode) { switch (op_) { case '&': // Reduction AND @@ -3718,59 +3459,42 @@ unsigned PEUnary::test_width(Design*des, NetScope*scope, case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) - { - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - unsigned swid = expr_->test_width(des, scope, 0, 0, sub_type, flag); - if (debug_elaborate) - cerr << get_fileline() << ": debug: " - << "Test width of sub-expression of " << op_ - << " returns " << swid << "." << endl; - - expr_type_ = sub_type; - } - expr_width_ = 1; - - expr_type__ = expr_type_; - return expr_width_; - case '!': - { - ivl_variable_type_t sub_type = IVL_VT_NO_TYPE; - bool flag = false; - expr_->test_width(des, scope, 0, 0, sub_type, flag); - expr_type_ = (sub_type==IVL_VT_BOOL)? IVL_VT_BOOL : IVL_VT_LOGIC; - } - // Logical ! always returns a single-bit LOGIC or BOOL value. - expr_width_ = 1; - expr_type__ = expr_type_; - return expr_width_; + { + width_mode_t sub_mode = SIZED; + unsigned sub_width = expr_->test_width(des, scope, sub_mode); + + expr_type_ = expr_->expr_type(); + expr_width_ = 1; + min_width_ = 1; + signed_flag_ = false; + + if ((op_ == '!') && (expr_type_ != IVL_VT_BOOL)) + expr_type_ = IVL_VT_LOGIC; + + if (debug_elaborate) + cerr << get_fileline() << ": debug: " + << "Test width of sub-expression of " << op_ + << " returns " << sub_width << "." << endl; + + } + return expr_width_; } - unsigned test_wid = expr_->test_width(des, scope, min, lval, expr_type__, unsized_flag); - switch (op_) { - // For these operators, the act of padding to the - // minimum width can have an important impact on the - // calculation. So don't let the tested width be less - // then the minimum width. - case '-': - case '+': - case '~': - if (test_wid < min) - test_wid = min; - break; - } + expr_width_ = expr_->test_width(des, scope, mode); + expr_type_ = expr_->expr_type(); + min_width_ = expr_->min_width(); + signed_flag_ = expr_->has_sign(); - expr_type_ = expr_type__; - expr_width_ = test_wid; - return test_wid; + return fix_width_(mode); } NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, - int expr_wid, bool) const + unsigned expr_wid, bool) const { /* Reduction operators and ! always have a self determined width. */ + unsigned sub_width = expr_wid; switch (op_) { case '!': case '&': // Reduction AND @@ -3779,12 +3503,11 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, case 'A': // Reduction NAND (~&) case 'N': // Reduction NOR (~|) case 'X': // Reduction NXOR (~^) - expr_wid = -1; + sub_width = expr_->expr_width(); default: break; } - - NetExpr*ip = expr_->elaborate_expr(des, scope, expr_wid, false); + NetExpr*ip = expr_->elaborate_expr(des, scope, sub_width, false); if (ip == 0) return 0; ivl_assert(*expr_, expr_type_ != IVL_VT_NO_TYPE); @@ -3792,7 +3515,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, NetExpr*tmp; switch (op_) { default: - tmp = new NetEUnary(op_, ip); + tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); break; @@ -3800,31 +3523,13 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, if (NetEConst*ipc = dynamic_cast(ip)) { verinum val = ipc->value(); - if (expr_wid > 0) - val = pad_to_width(val, expr_wid); - - /* When taking the - of a number, extend it one - bit to accommodate a possible sign bit. - - NOTE: This may not be correct! The test_width - is supposed to detect the special case that we - want to do lossless self-determined - expressions, and the function that calls - elaborate_expr should account for that in the - expr_wid argument. */ - unsigned use_len = val.len(); - if (expr_wid < 0) - use_len += 1; /* Calculate unary minus as 0-val */ - verinum zero (verinum::V0, use_len, val.has_len()); + verinum zero (verinum::V0, expr_wid); zero.has_sign(val.has_sign()); - verinum nval = zero - val; - - if (val.has_len()) - nval = verinum(nval, val.len()); - nval.has_sign(val.has_sign()); + verinum nval = verinum(zero - val, expr_wid); tmp = new NetEConst(nval); + tmp->cast_signed(signed_flag_); tmp->set_line(*this); delete ip; @@ -3838,9 +3543,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, delete ip; } else { - if (expr_wid > 0) - ip = pad_to_width(ip, expr_wid, *this); - tmp = new NetEUnary(op_, ip); + tmp = new NetEUnary(op_, ip, expr_wid, signed_flag_); tmp->set_line(*this); } break; @@ -3898,6 +3601,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, } tmp->set_line(*this); } + tmp = pad_to_width(tmp, expr_wid, *this); break; case '&': // Reduction AND @@ -3915,6 +3619,7 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, } tmp = new NetEUReduce(op_, ip); tmp->set_line(*this); + tmp = pad_to_width(tmp, expr_wid, *this); break; case '~': @@ -3925,15 +3630,13 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope, return tmp; } -NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, int expr_wid) const +NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const { // Handle the special case that the operand is a // constant. Simply calculate the constant results of the // expression and return that. if (NetEConst*ctmp = dynamic_cast (operand)) { verinum value = ctmp->value(); - if (expr_wid > (int)value.len()) - value = pad_to_width(value, expr_wid); // The only operand that I know can get here is the // unary not (~). @@ -3946,14 +3649,16 @@ NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, int expr_wid) const return ctmp; } - if (expr_wid > (int)operand->expr_width()) - operand = pad_to_width(operand, expr_wid, *this); - - NetEUBits*tmp = new NetEUBits(op_, operand); + NetEUBits*tmp = new NetEUBits(op_, operand, expr_wid, signed_flag_); tmp->set_line(*this); return tmp; } +NetExpr* PEVoid::elaborate_expr(Design*, NetScope*, unsigned, bool) const +{ + return 0; +} + NetNet* Design::find_discipline_reference(ivl_discipline_t dis, NetScope*scope) { NetNet*gnd = discipline_references_[dis->name()]; diff --git a/elab_lval.cc b/elab_lval.cc index 5104ac48d..0a933ddb0 100644 --- a/elab_lval.cc +++ b/elab_lval.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -255,12 +255,6 @@ NetAssign_* PEIdent::elaborate_lval_net_word_(Design*des, ivl_assert(*this, index_head.msb != 0); ivl_assert(*this, index_head.lsb == 0); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_head.msb->test_width(des, scope, integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); - NetExpr*word = elab_and_eval(des, scope, index_head.msb, -1); // If there is a non-zero base to the memory, then build an @@ -335,12 +329,6 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des, NetNet*reg = lv->sig(); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); - // Bit selects have a single select expression. Evaluate the // constant value and treat it as a part select with a bit // width of 1. @@ -463,12 +451,6 @@ bool PEIdent::elaborate_lval_net_idx_(Design*des, unsigned long wid; calculate_up_do_width_(des, scope, wid); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); - NetExpr*base = elab_and_eval(des, scope, index_tail.msb, -1); ivl_select_type_t sel_type = IVL_SEL_OTHER; diff --git a/elab_net.cc b/elab_net.cc index 05cbaf525..f2610be27 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -198,12 +198,6 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, case index_component_t::SEL_IDX_DO: case index_component_t::SEL_IDX_UP: { - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_tail.msb->test_width(des, scope, - integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); need_constant_expr = true; NetExpr*tmp_ex = elab_and_eval(des, scope, index_tail.msb, -1); need_constant_expr = false; @@ -477,12 +471,6 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, } ivl_assert(*this, index_head.sel == index_component_t::SEL_BIT); - // These are not used, but they need to have a default value. - ivl_variable_type_t expr_type_tmp = IVL_VT_NO_TYPE; - bool unsized_flag_tmp = false; - index_head.msb->test_width(des, scope, - integer_width, integer_width, - expr_type_tmp, unsized_flag_tmp); need_constant_expr = true; NetExpr*tmp_ex = elab_and_eval(des, scope, index_head.msb, -1); need_constant_expr = false; diff --git a/elab_scope.cc b/elab_scope.cc index 98b271267..b15b5a429 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -61,7 +61,6 @@ static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, tmp->high_open_flag = range->high_open_flag; if (range->low_expr) { - probe_expr_width(des, scope, range->low_expr); tmp->low_expr = elab_and_eval(des, scope, range->low_expr, -1); ivl_assert(*range->low_expr, tmp->low_expr); } else { @@ -78,7 +77,6 @@ static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, tmp->high_expr = tmp->low_expr; } else if (range->high_expr) { - probe_expr_width(des, scope, range->high_expr); tmp->high_expr = elab_and_eval(des, scope, range->high_expr, -1); ivl_assert(*range->high_expr, tmp->high_expr); } else { @@ -136,8 +134,8 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, { bool rc_flag; assert(enum_type->range->size() == 2); - NetExpr*msb_ex = enum_type->range->front()->elaborate_expr(des, scope, -2, false); - NetExpr*lsb_ex = enum_type->range->back() ->elaborate_expr(des, scope, -2, false); + NetExpr*msb_ex = elab_and_eval(des, scope, enum_type->range->front(), -1); + NetExpr*lsb_ex = elab_and_eval(des, scope, enum_type->range->back(), -1); long msb = 0; rc_flag = eval_as_long(msb, msb_ex); @@ -162,7 +160,6 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope, // There is an explicit value. elaborate/evaluate // the value and assign it to the enumeration name. NetExpr*val = elab_and_eval(des, scope, cur->parm, - use_enum->base_width(), use_enum->base_width()); NetEConst*val_const = dynamic_cast (val); if (val_const == 0) { @@ -557,7 +554,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) // The initial value for the genvar does not need (nor can it // use) the genvar itself, so we can evaluate this expression // the same way any other parameter value is evaluated. - probe_expr_width(des, container, loop_init); need_constant_expr = true; NetExpr*init_ex = elab_and_eval(des, container, loop_init, -1); need_constant_expr = false; @@ -624,7 +620,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) cerr << get_fileline() << ": debug: genvar init = " << genvar << endl; container->genvar_tmp = loop_index; container->genvar_tmp_val = genvar; - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -673,7 +668,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) elaborate_subscope_(des, scope); // Calculate the step for the loop variable. - probe_expr_width(des, container, loop_step); need_constant_expr = true; NetExpr*step_ex = elab_and_eval(des, container, loop_step, -1); need_constant_expr = false; @@ -692,7 +686,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) container->genvar_tmp_val = genvar; delete step; delete test_ex; - probe_expr_width(des, container, loop_test); test_ex = elab_and_eval(des, container, loop_test, -1); test = dynamic_cast(test_ex); assert(test); @@ -707,7 +700,6 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else_flag) { - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*test_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -800,7 +792,6 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else bool PGenerate::generate_scope_case_(Design*des, NetScope*container) { - probe_expr_width(des, container, loop_test); need_constant_expr = true; NetExpr*case_value_ex = elab_and_eval(des, container, loop_test, -1); need_constant_expr = false; @@ -833,7 +824,6 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) bool match_flag = false; for (unsigned idx = 0 ; idx < item->item_test.size() && !match_flag ; idx +=1 ) { - probe_expr_width(des, container, item->item_test[idx]); need_constant_expr = true; NetExpr*item_value_ex = elab_and_eval(des, container, item->item_test[idx], -1); need_constant_expr = false; @@ -1206,8 +1196,6 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const */ void PGModule::elaborate_scope_mod_instances_(Design*des, Module*mod, NetScope*sc) const { - if (msb_) probe_expr_width(des, sc, msb_); - if (lsb_) probe_expr_width(des, sc, lsb_); need_constant_expr = true; NetExpr*mse = msb_ ? elab_and_eval(des, sc, msb_, -1) : 0; NetExpr*lse = lsb_ ? elab_and_eval(des, sc, lsb_, -1) : 0; diff --git a/elab_sig.cc b/elab_sig.cc index 2e70cb4a4..6aaaca577 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -471,8 +471,6 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const case PTF_REG_S: if (return_type_.range) { ivl_assert(*this, return_type_.range->size() == 2); - probe_expr_width(des, scope, return_type_.range->at(0)); - probe_expr_width(des, scope, return_type_.range->at(1)); need_constant_expr = true; NetExpr*me = elab_and_eval(des, scope, @@ -545,8 +543,6 @@ void PFunction::elaborate_sig(Design*des, NetScope*scope) const case PTF_ATOM2: case PTF_ATOM2_S: ivl_assert(*this, return_type_.range != 0); - probe_expr_width(des, scope, (*return_type_.range)[0]); - probe_expr_width(des, scope, (*return_type_.range)[1]); long use_wid; { need_constant_expr = true; @@ -852,7 +848,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const bool bad_lsb = false, bad_msb = false; /* If they exist get the port definition MSB and LSB */ if (port_set_ && port_msb_ != 0) { - probe_expr_width(des, scope, port_msb_); /* We do not currently support constant user function. */ need_constant_expr = true; NetExpr*texpr = elab_and_eval(des, scope, port_msb_, -1); @@ -870,7 +865,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - probe_expr_width(des, scope, port_lsb_); /* We do not currently support constant user function. */ need_constant_expr = true; texpr = elab_and_eval(des, scope, port_lsb_, -1); @@ -898,7 +892,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const /* If they exist get the net/etc. definition MSB and LSB */ if (net_set_ && net_msb_ != 0 && !bad_msb && !bad_lsb) { - probe_expr_width(des, scope, net_msb_); /* We do not currently support constant user function. */ need_constant_expr = true; NetExpr*texpr = elab_and_eval(des, scope, net_msb_, -1); @@ -916,7 +909,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const delete texpr; - probe_expr_width(des, scope, net_lsb_); /* We do not currently support constant user function. */ need_constant_expr = true; texpr = elab_and_eval(des, scope, net_lsb_, -1); @@ -1011,9 +1003,6 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const if (lidx_ || ridx_) { assert(lidx_ && ridx_); - probe_expr_width(des, scope, lidx_); - probe_expr_width(des, scope, ridx_); - need_constant_expr = true; NetExpr*lexp = elab_and_eval(des, scope, lidx_, -1); NetExpr*rexp = elab_and_eval(des, scope, ridx_, -1); diff --git a/elaborate.cc b/elaborate.cc index 0ecc01b0f..f50259303 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -115,6 +115,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const cerr << get_fileline() << ": debug: PGAssign: elaborated r-value" << " width="<< rval->vector_width() << ", type="<< rval->data_type() + << ", signed="<< rval->get_signed() << ", expr=" << *rval_expr << endl; } @@ -227,11 +228,6 @@ unsigned PGBuiltin::calculate_array_count_(Design*des, NetScope*scope, gate. Figure out how many are desired. */ if (msb_) { need_constant_expr = true; - ivl_variable_type_t use_type; - bool flag = false; - msb_->test_width(des, scope, 0, 0, use_type, flag); - flag = false; - lsb_->test_width(des, scope, 0, 0, use_type, flag); NetExpr*msb_exp = elab_and_eval(des, scope, msb_, -1); NetExpr*lsb_exp = elab_and_eval(des, scope, lsb_, -1); need_constant_expr = false; @@ -820,12 +816,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const sig = lval_sigs[idx]; } else { - unsigned use_width = array_count * instance_width; - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool flag = false; - ex->test_width(des, scope, 0, use_width, tmp_type, flag); - NetExpr*tmp = elab_and_eval(des, scope, ex, - use_width, use_width); + NetExpr*tmp = elab_and_eval(des, scope, ex, -1); sig = tmp->synthesize(des, scope, tmp); delete tmp; } @@ -1332,13 +1323,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const port is actually empty on the inside. We assume in that case that the port is input. */ - ivl_variable_type_t tmp_type = IVL_VT_NO_TYPE; - bool flag = false; - pins[idx]->test_width(des, scope, 0, desired_vector_width, - tmp_type, flag); - NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], - desired_vector_width, - desired_vector_width); + NetExpr*tmp_expr = elab_and_eval(des, scope, pins[idx], -1); if (tmp_expr == 0) { cerr << pins[idx]->get_fileline() << ": internal error: Port expression " @@ -1891,7 +1876,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const if (pins[idx] == 0) continue; - NetExpr*expr_tmp = elab_and_eval(des, scope, pins[idx], 1, 1); + NetExpr*expr_tmp = elab_and_eval(des, scope, pins[idx], 1); if (expr_tmp == 0) { cerr << "internal error: Expression too complicated " "for elaboration:" << *pins[idx] << endl; @@ -2046,7 +2031,6 @@ NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, */ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) { - probe_expr_width(des, scope, expr); NetExpr*dex = elab_and_eval(des, scope, expr, -1); /* Print a warning if we find default and `timescale based @@ -2101,12 +2085,11 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) NetExpr*scal_val = new NetECReal(verireal(round)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); + dex = new NetEBMult('*', dex, scal_val, 1, true); dex->set_line(*expr); // Cast this part of the expression to an integer. - dex = new NetECast('i', dex); - dex->set_width(64); + dex = new NetECast('i', dex, 64, false); dex->set_line(*expr); // Now scale the integer value. @@ -2117,8 +2100,7 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) scal_val = new NetEConst(verinum(scale, 64)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); - dex->set_width(64); + dex = new NetEBMult('*', dex, scal_val, 64, false); dex->set_line(*expr); } else { int shift = scope->time_unit() - des->get_precision(); @@ -2128,7 +2110,7 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) NetExpr*scal_val = new NetEConst(verinum(scale, 64)); scal_val->set_line(*expr); - dex = new NetEBMult('*', dex, scal_val); + dex = new NetEBMult('*', dex, scal_val, 64, false); dex->set_line(*expr); } @@ -2176,17 +2158,6 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const if (delay || event_) { unsigned wid = count_lval_width(lv); - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - - if (wid > rv->expr_width()) { - cerr << get_fileline() << ": error: Unable to match " - "expression width of " << rv->expr_width() << - " to l-value width of " << wid << "." << endl; - //XXXX delete rv; - return 0; - } - NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::REG, wid); tmp->local_flag(true); @@ -2276,22 +2247,6 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const return bl; } - /* Based on the specific type of the l-value, do cleanup - processing on the r-value. */ - if (rv->expr_type() == IVL_VT_REAL) { - - // The r-value is a real. Casting will happen in the - // code generator, so leave it. - - } else { - unsigned wid = count_lval_width(lv); - if (wid > rv->expr_width()) { - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - ivl_assert(*this, rv->expr_width() >= wid); - } - if (lv->expr_type() == IVL_VT_BOOL && rv->expr_type() != IVL_VT_BOOL) { if (debug_elaborate) cerr << get_fileline() << ": debug: Cast expression to int2" << endl; @@ -2342,20 +2297,6 @@ NetProc* PAssignNB::elaborate(Design*des, NetScope*scope) const NetExpr*rv = elaborate_rval_(des, scope, count_lval_width(lv), lv->expr_type()); if (rv == 0) return 0; - /* Handle the (common) case that the r-value is a vector. This - includes just about everything but reals. In this case, we - need to pad the r-value to match the width of the l-value. - - If in this case the l-val is a variable (i.e., real) then - the width to pad to will be 0, so this code is harmless. */ - if (rv->expr_type() == IVL_VT_REAL) { - - } else { - unsigned wid = count_lval_width(lv); - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - NetExpr*delay = 0; if (delay_ != 0) { assert(count_ == 0 && event_ == 0); @@ -2507,7 +2448,6 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const { assert(scope); - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate this case" @@ -2562,7 +2502,6 @@ NetProc* PCase::elaborate(Design*des, NetScope*scope) const NetExpr*gu = 0; NetProc*st = 0; assert(cur_expr); - probe_expr_width(des, scope, cur_expr); gu = elab_and_eval(des, scope, cur_expr, -1); if (cur->stat) @@ -2585,7 +2524,6 @@ NetProc* PCondit::elaborate(Design*des, NetScope*scope) const << " with conditional: " << *expr_ << endl; // Elaborate and try to evaluate the conditional expression. - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" @@ -2701,29 +2639,12 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const svectoreparms (parm_count); + perm_string name = peek_tail_name(path_); + for (unsigned idx = 0 ; idx < parm_count ; idx += 1) { PExpr*ex = parm(idx); if (ex != 0) { - ivl_variable_type_t use_type; - bool flag = false; - int use_wid = ex->test_width(des,scope,0,0, use_type, flag); - if (debug_elaborate) - cerr << ex->get_fileline() << ": debug: " - << "Argument " << (idx+1) - << " of system task tests its width as " << use_wid - << ", type=" << use_type - << ", unsized_flag=" << flag << endl; - - // If the argument expression is unsized, then - // elaborate as self-determined *lossless* instead - // of sized. - if (flag==true) - use_wid = -2; - - eparms[idx] = ex->elaborate_expr(des, scope, use_wid, true); - if (eparms[idx]) - eval_expr(eparms[idx]); - + eparms[idx] = elab_sys_task_arg(des, scope, name, idx, ex); } else { eparms[idx] = 0; } @@ -2733,8 +2654,7 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const // $sdf_annotate system task. There will be nothing for $sdf // to annotate, and the user is intending to turn the behavior // off anyhow, so replace the system task invocation with a no-op. - if (gn_specify_blocks_flag == false - && peek_tail_name(path_) == "$sdf_annotate") { + if (gn_specify_blocks_flag == false && name == "$sdf_annotate") { cerr << get_fileline() << ": warning: Omitting $sdf_annotate() " << "since specify blocks are being omitted." << endl; @@ -2743,8 +2663,7 @@ NetProc* PCallTask::elaborate_sys(Design*des, NetScope*scope) const return noop; } - NetSTask*cur = new NetSTask(peek_tail_name(path_), def_sfunc_as_task, - eparms); + NetSTask*cur = new NetSTask(name, def_sfunc_as_task, eparms); cur->set_line(*this); return cur; } @@ -2871,11 +2790,6 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const des->errors += 1; continue; } - if (wid > rv->expr_width()) { - rv->set_width(wid); - rv = pad_to_width(rv, wid, *this); - } - ivl_assert(*this, rv->expr_width() >= wid); NetAssign*pr = new NetAssign(lv, rv); pr->set_line(*this); @@ -2988,9 +2902,6 @@ NetCAssign* PCAssign::elaborate(Design*des, NetScope*scope) const if (rexp == 0) return 0; - rexp->set_width(lwid); - rexp = pad_to_width(rexp, lwid, *this); - dev = new NetCAssign(lval, rexp); if (debug_elaborate) { @@ -3306,8 +3217,7 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, } } - probe_expr_width(des, scope, expr_[idx]->expr()); - NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), 0); + NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), -1); if (tmp == 0) { expr_[idx]->dump(cerr); cerr << endl; @@ -3409,8 +3319,9 @@ NetProc* PEventStatement::elaborate_wait(Design*des, NetScope*scope, /* Elaborate wait expression. Don't eval yet, we will do that shortly, after we apply a reduction or. */ - probe_expr_width(des, scope, pe); - NetExpr*expr = pe->elaborate_expr(des, scope, -1, false); + PExpr::width_mode_t mode; + pe->test_width(des, scope, mode); + NetExpr*expr = pe->elaborate_expr(des, scope, pe->expr_width(), false); if (expr == 0) { cerr << get_fileline() << ": error: Unable to elaborate" " wait condition expression." << endl; @@ -3607,9 +3518,6 @@ NetForce* PForce::elaborate(Design*des, NetScope*scope) const if (rexp == 0) return 0; - rexp->set_width(lwid, true); - rexp = pad_to_width(rexp, lwid, *this); - if (ltype==IVL_VT_BOOL && rexp->expr_type()!=IVL_VT_BOOL) { if (debug_elaborate) { cerr << get_fileline() << ": debug: " @@ -3670,32 +3578,14 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const assert(sig); NetAssign_*lv = new NetAssign_(sig); - /* Calculate the width of the initialization as if this were - any other assignment statement. */ - unsigned use_width = lv->lwidth(); - bool unsized_flag = false; - ivl_variable_type_t expr1_type = IVL_VT_NO_TYPE; - use_width = expr1_->test_width(des, scope, use_width, use_width, expr1_type, unsized_flag); - /* Make the r-value of the initial assignment, and size it properly. Then use it to build the assignment statement. */ - etmp = elab_and_eval(des, scope, expr1_, use_width); - etmp->set_width(use_width); - etmp = pad_to_width(etmp, use_width, *this); + etmp = elaborate_rval_expr(des, scope, lv->expr_type(), lv->lwidth(), + expr1_); if (debug_elaborate) { cerr << get_fileline() << ": debug: FOR initial assign: " << sig->name() << " = " << *etmp << endl; - assert(etmp->expr_width() >= lv->lwidth()); - } - - /* Based on the specific type of the l-value, do cleanup - processing on the r-value. */ - if (etmp->expr_type() != IVL_VT_REAL) { - unsigned wid = count_lval_width(lv); - etmp->set_width(wid); - etmp = pad_to_width(etmp, wid, *this); - assert(etmp->expr_width() >= wid); } NetAssign*init = new NetAssign(lv, etmp); @@ -3729,9 +3619,16 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const assert(sig); lv = new NetAssign_(sig); - /* Make the rvalue of the increment expression, and size it - for the lvalue. */ - etmp = elab_and_eval(des, scope, expr2_, lv->lwidth()); + /* Make the r-value of the increment assignment, and size it + properly. Then use it to build the assignment statement. */ + etmp = elaborate_rval_expr(des, scope, lv->expr_type(), lv->lwidth(), + expr2_); + + if (debug_elaborate) { + cerr << get_fileline() << ": debug: FOR increment assign: " + << sig->name() << " = " << *etmp << endl; + } + NetAssign*step = new NetAssign(lv, etmp); step->set_line(*this); @@ -3741,7 +3638,6 @@ NetProc* PForStatement::elaborate(Design*des, NetScope*scope) const /* Elaborate the condition expression. Try to evaluate it too, in case it is a constant. This is an interesting case worthy of a warning. */ - probe_expr_width(des, scope, cond_); NetExpr*ce = elab_and_eval(des, scope, cond_, -1); if (ce == 0) { delete top; @@ -3832,7 +3728,6 @@ NetProc* PRepeat::elaborate(Design*des, NetScope*scope) const { assert(scope); - probe_expr_width(des, scope, expr_); NetExpr*expr = elab_and_eval(des, scope, expr_, -1); if (expr == 0) { cerr << get_fileline() << ": Unable to elaborate" @@ -3956,7 +3851,6 @@ NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const */ NetProc* PWhile::elaborate(Design*des, NetScope*scope) const { - probe_expr_width(des, scope, cond_); NetExpr*tmp = elab_and_eval(des, scope, cond_, -1); NetWhile*loop = new NetWhile(tmp, statement_->elaborate(des, scope)); return loop; @@ -4059,8 +3953,7 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const them for the timescale/precision of the scope. */ for (unsigned idx = 0 ; idx < ndelays ; idx += 1) { PExpr*exp = delays[idx]; - probe_expr_width(des, scope, exp); - NetExpr*cur = elab_and_eval(des, scope, exp, 0); + NetExpr*cur = elab_and_eval(des, scope, exp, -1); if (NetEConst*con = dynamic_cast (cur)) { verinum fn = con->value(); @@ -4098,7 +3991,6 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const NetNet*condit_sig = 0; if (conditional && condition) { - probe_expr_width(des, scope, condition); NetExpr*tmp = elab_and_eval(des, scope, condition, -1); ivl_assert(*condition, tmp); @@ -4255,7 +4147,6 @@ bool Module::elaborate(Design*des, NetScope*scope) const for (specparam_it_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { - probe_expr_width(des, scope, (*cur).second); need_constant_expr = true; NetExpr*val = elab_and_eval(des, scope, (*cur).second, -1); need_constant_expr = false; diff --git a/elaborate_analog.cc b/elaborate_analog.cc index 5d538b98d..25cbdd4e6 100644 --- a/elaborate_analog.cc +++ b/elaborate_analog.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008,2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -28,8 +28,6 @@ NetProc* AContrib::elaborate(Design*des, NetScope*scope) const { - probe_expr_width(des, scope, lval_); - probe_expr_width(des, scope, rval_); NetExpr*lval = elab_and_eval(des, scope, lval_, -1); NetExpr*rval = elab_and_eval(des, scope, rval_, -1); diff --git a/eval_tree.cc b/eval_tree.cc index bb37e1401..10e359a8e 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -29,7 +29,7 @@ # include "ivl_assert.h" # include "netmisc.h" -NetExpr* NetExpr::eval_tree(int) +NetExpr* NetExpr::eval_tree() { return 0; } @@ -114,12 +114,12 @@ NetECReal* NetEBAdd::eval_tree_real_() return res; } -NetExpr* NetEBAdd::eval_tree(int prune_to_width) +NetExpr* NetEBAdd::eval_tree() { - eval_expr(left_, prune_to_width); - eval_expr(right_, prune_to_width); + eval_expr(left_); + eval_expr(right_); - if (left_->expr_type() == IVL_VT_REAL || right_->expr_type()==IVL_VT_REAL) + if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); NetEConst*lc = dynamic_cast(left_); @@ -131,33 +131,23 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); + verinum val; switch (op_) { case '+': - val = lval + rval; + val = verinum(lval + rval, wid); break; case '-': - val = lval - rval; + val = verinum(lval - rval, wid); break; default: return 0; } - /* Result might have known width. */ - if (has_width()) { - unsigned lwid = lc->expr_width(); - unsigned rwid = rc->expr_width(); - unsigned wid = (rwid > lwid) ? rwid : lwid; - if (prune_to_width < 0) - wid += 1; - verinum val2=verinum(val,wid); - val=val2; - } else { - /* No fixed width, so trim the bits losslessly. */ - verinum val2 = trim_vnum(val); - val = val2; - } - NetEConst *res = new NetEConst(val); ivl_assert(*this, res); res->set_line(*this); @@ -188,37 +178,20 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - if (lval.len() < expr_width()) - lval = pad_to_width(lval, expr_width()); - if (rval.len() < expr_width()) - rval = pad_to_width(rval, expr_width()); - - if (se->expr_width() > this->expr_width()) { - cerr << get_fileline() << ": internal error: " - << "expr_width()=" << expr_width() - << ", sub expr_width()=" << se->expr_width() - << ", sub expression=" << *se << endl; - } - ivl_assert(*this, se->expr_width() <= this->expr_width()); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); verinum val; if (op_ == se->op_) { /* (a + lval) + rval --> a + (rval+lval) */ /* (a - lval) - rval --> a - (rval+lval) */ - val = rval + lval; + val = verinum(rval + lval, wid); } else { /* (a - lval) + rval --> a + (rval-lval) */ /* (a + lval) - rval --> a - (rval-lval) */ - val = rval - lval; - } - - // Since we padded the operands above to be the minimum - // width, the val should also be at least expr_width(). - ivl_assert(*this, val.len() >= expr_width()); - if (val.len() > expr_width()) { - verinum tmp (val, expr_width()); - tmp.has_sign(val.has_sign()); - val = tmp; + val = verinum(rval - lval, wid); } NetEConst*tmp = new NetEConst(val); @@ -236,15 +209,15 @@ NetExpr* NetEBAdd::eval_tree(int prune_to_width) return 0; } -NetEConst* NetEBBits::eval_tree(int prune_to_width) +NetEConst* NetEBBits::eval_tree() { if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } - eval_expr(left_, prune_to_width); - eval_expr(right_, prune_to_width); + eval_expr(left_); + eval_expr(right_); NetEConst*lc = dynamic_cast(left_); NetEConst*rc = dynamic_cast(right_); @@ -266,79 +239,33 @@ NetEConst* NetEBBits::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - unsigned lwid = lc->expr_width(); - if (lwid == 0) lwid = lval.len(); - - unsigned rwid = rc->expr_width(); - if (rwid == 0) rwid = rval.len(); - unsigned wid = expr_width(); - if (wid == 0) - wid = (rwid > lwid)? rwid : lwid; + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); verinum res (verinum::V0, wid); - if (lwid > wid) - lwid = wid; - if (rwid > wid) - rwid = wid; - - // Sub-expressions of bitwise operators need to be the same - // width. Pad them out if necessary. - if (lwid < wid) { - lval = pad_to_width(lval, wid); - lwid = wid; - } - if (rwid < wid) { - rval = pad_to_width(rval, wid); - rwid = wid; - } - switch (op()) { case '|': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) | rval.get(idx)); - if (lwid < rwid) - for (unsigned idx = lwid ; idx < rwid ; idx += 1) - res.set(idx, rval.get(idx)); - - if (rwid < lwid) - for (unsigned idx = rwid ; idx < lwid ; idx += 1) - res.set(idx, lval.get(idx)); - break; } case '&': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) & rval.get(idx)); break; } case '^': { - unsigned cnt = lwid; - if (cnt > wid) cnt = wid; - if (cnt > rwid) cnt = rwid; - for (unsigned idx = 0 ; idx < cnt ; idx += 1) + for (unsigned idx = 0 ; idx < wid ; idx += 1) res.set(idx, lval.get(idx) ^ rval.get(idx)); - if (lwid < rwid) - for (unsigned idx = lwid ; idx < rwid ; idx += 1) - res.set(idx, rval.get(idx)); - - if (rwid < lwid) - for (unsigned idx = rwid ; idx < lwid ; idx += 1) - res.set(idx, lval.get(idx)); - break; } @@ -761,7 +688,7 @@ NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag) return result; } -NetEConst* NetEBComp::eval_tree(int) +NetEConst* NetEBComp::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -850,11 +777,8 @@ NetExpr* NetEBDiv::eval_tree_real_() * The NetEBDiv operator includes the / and % operators. First evaluate * the sub-expressions, then perform the required operation. */ -NetExpr* NetEBDiv::eval_tree(int prune_to_width) +NetExpr* NetEBDiv::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -865,20 +789,26 @@ NetExpr* NetEBDiv::eval_tree(int prune_to_width) NetEConst*rc = dynamic_cast(right_); if (lc == 0 || rc == 0) return 0; - // Make sure the expression is evaluated at the - // expression width. - verinum lval = pad_to_width(lc->value(), expr_width()); - verinum rval = pad_to_width(rc->value(), expr_width()); + verinum lval = lc->value(); + verinum rval = rc->value(); - NetExpr*tmp = 0; + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); + + verinum val; switch (op_) { case '/': - tmp = new NetEConst(lval / rval); + val = verinum(lval / rval, wid); break; case '%': - tmp = new NetEConst(lval % rval); + val = verinum(lval % rval, wid); break; + default: + return 0; } + NetExpr*tmp = new NetEConst(val); ivl_assert(*this, tmp); tmp->set_line(*this); @@ -928,7 +858,7 @@ NetEConst* NetEBLogic::eval_tree_real_() return tmp; } -NetEConst* NetEBLogic::eval_tree(int) +NetEConst* NetEBLogic::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -1024,11 +954,8 @@ NetExpr* NetEBMult::eval_tree_real_() return res; } -NetExpr* NetEBMult::eval_tree(int prune_to_width) +NetExpr* NetEBMult::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -1042,7 +969,13 @@ NetExpr* NetEBMult::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - NetEConst*tmp = new NetEConst(lval * rval); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + ivl_assert(*this, rval.len() == wid); + + verinum val(lval * rval, wid); + NetEConst*tmp = new NetEConst(val); ivl_assert(*this, tmp); tmp->set_line(*this); @@ -1072,11 +1005,8 @@ NetExpr* NetEBPow::eval_tree_real_() return res; } -NetExpr* NetEBPow::eval_tree(int prune_to_width) +NetExpr* NetEBPow::eval_tree() { -// assert(prune_to_width <= 0); -// HERE - eval_expr(left_); eval_expr(right_); @@ -1090,7 +1020,12 @@ NetExpr* NetEBPow::eval_tree(int prune_to_width) verinum lval = lc->value(); verinum rval = rc->value(); - NetEConst*res = new NetEConst( pow(lval,rval) ); + unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lval.len() == wid); + + verinum val(pow(lval, rval), wid); + NetEConst*res = new NetEConst(val); ivl_assert(*this, res); res->set_line(*this); @@ -1105,7 +1040,7 @@ NetExpr* NetEBPow::eval_tree(int prune_to_width) * Evaluate the shift operator if possible. For this to work, both * operands must be constant. */ -NetEConst* NetEBShift::eval_tree(int prune_to_width) +NetEConst* NetEBShift::eval_tree() { eval_expr(left_); eval_expr(right_); @@ -1116,79 +1051,44 @@ NetEConst* NetEBShift::eval_tree(int prune_to_width) NetEConst*res; - verinum rv = re->value(); verinum lv = le->value(); + verinum rv = re->value(); - /* Make an early estimate of the expression width. */ unsigned wid = expr_width(); + ivl_assert(*this, wid > 0); + ivl_assert(*this, lv.len() == wid); if (rv.is_defined()) { - unsigned shift = rv.as_ulong(); if (debug_eval_tree) { cerr << get_fileline() << ": debug: " << "Evaluate " << lv << "<<" << op() << ">> " - << rv << ", wid=" << wid << ", shift=" << shift - << ", lv.has_len()=" << lv.has_len() << endl; + << rv << ", wid=" << wid << ", shift=" << shift << endl; } - if ((wid == 0) || ! lv.has_len()) { - /* If the caller doesn't care what the width is, - then calculate a width from the trimmed left - expression, plus the shift. This avoids - data loss. */ - lv = trim_vnum(lv); - wid = lv.len(); - if (op() == 'l') - wid = lv.len() + shift; + verinum val; + switch (op_) { + case 'l': + val = verinum(lv << shift, wid); + break; + case 'r': + lv.has_sign(false); + val = verinum(lv >> shift, wid); + break; + case 'R': + lv.has_sign(true); + val = verinum(lv >> shift, wid); + break; + default: + return 0; } + val.has_sign(has_sign()); - if (prune_to_width > 0 && wid > (unsigned)prune_to_width) - wid = prune_to_width; - - assert(wid > 0); - verinum::V pad = verinum::V0; - if (op() == 'R' && has_sign()) { - pad = lv[lv.len()-1]; - } - verinum nv (pad, wid, lv.has_len()); - - if (op() == 'r' || op() == 'R') { - unsigned cnt = wid; - if (cnt > nv.len()) - cnt = nv.len(); - if (shift >= lv.len()) - cnt = 0; - else if (cnt > (lv.len()-shift)) - cnt = (lv.len()-shift); - for (unsigned idx = 0 ; idx < cnt ; idx += 1) - nv.set(idx, lv[idx+shift]); - - } else { - unsigned cnt = wid; - if (cnt > lv.len()) - cnt = lv.len(); - if (shift >= nv.len()) - cnt = 0; - else if (cnt > (nv.len()-shift)) - cnt = nv.len() - shift; - - for (unsigned idx = 0 ; idx < cnt ; idx += 1) - nv.set(idx+shift, lv[idx]); - } - - res = new NetEConst(nv); - + res = new NetEConst(val); } else { - if (wid == 0) wid = left_->expr_width(); - - if (prune_to_width > 0 && wid > (unsigned)prune_to_width) - wid = prune_to_width; - - assert(wid > 0); - verinum nv (verinum::Vx, wid); - res = new NetEConst(nv); + verinum val (verinum::Vx, wid); + res = new NetEConst(val); } res->set_line(*this); @@ -1196,7 +1096,7 @@ NetEConst* NetEBShift::eval_tree(int prune_to_width) return res; } -NetEConst* NetEConcat::eval_tree(int prune_to_width) +NetEConst* NetEConcat::eval_tree() { // HERE unsigned repeat_val = repeat(); @@ -1204,7 +1104,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } unsigned gap = 0; @@ -1225,7 +1125,7 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) // that is here. If I succeed, reset the parameter to // the evaluated value. assert(parms_[idx]); - NetExpr*expr = parms_[idx]->eval_tree(0); + NetExpr*expr = parms_[idx]->eval_tree(); if (expr) { expr->set_line(*parms_[idx]); delete parms_[idx]; @@ -1285,16 +1185,15 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) val.has_sign( this->has_sign() ); NetEConst*res = new NetEConst(val); - res->set_width(val.len()); return res; } -NetEConst* NetESelect::eval_tree(int prune_to_width) +NetEConst* NetESelect::eval_tree() { // HERE if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluating expression:" - << *this << ", prune_to_width=" << prune_to_width << endl; + << *this << endl; } eval_expr(expr_); @@ -1318,14 +1217,12 @@ NetEConst* NetESelect::eval_tree(int prune_to_width) verinum::V pad_bit = verinum::Vx; if (base_ == 0) { - /* If the base is NULL (different from 0) the this - select is here for sign extension. So calculate a - proper pad bit. Extend x or z or 0, and sign extend 1 - if this is signed. */ - unsigned top = expr->expr_width()-1; - - pad_bit = eval.get(top); - if (pad_bit==verinum::V1 && !has_sign()) + /* If the base is NULL (different from 0) then this + select is here for zero or sign extension. So + calculate a proper pad bit. */ + if (has_sign()) + pad_bit = eval.get(expr->expr_width()-1); + else pad_bit = verinum::V0; } @@ -1365,12 +1262,12 @@ static void print_ternary_cond(NetExpr*expr) * evaluates to x or z, then merge the constant bits of the true and * false expressions. */ -NetExpr* NetETernary::eval_tree(int prune_to_width) +NetExpr* NetETernary::eval_tree() { eval_expr(cond_); switch (const_logical(cond_)) { case C_0: - eval_expr(false_val_, prune_to_width); + eval_expr(false_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " @@ -1393,7 +1290,7 @@ NetExpr* NetETernary::eval_tree(int prune_to_width) return false_val_->dup_expr(); case C_1: - eval_expr(true_val_, prune_to_width); + eval_expr(true_val_); if (debug_eval_tree) { cerr << get_fileline() << ": debug: Evaluate ternary with " << "constant condition value: "; @@ -1425,8 +1322,8 @@ NetExpr* NetETernary::eval_tree(int prune_to_width) expressions down to constants then compare the values to build up a constant result. */ - eval_expr(true_val_, prune_to_width); - eval_expr(false_val_, prune_to_width); + eval_expr(true_val_); + eval_expr(false_val_); NetEConst*t = dynamic_cast(true_val_); NetEConst*f = dynamic_cast(false_val_); @@ -1509,7 +1406,7 @@ NetExpr* NetEUnary::eval_tree_real_() return res; } -NetExpr* NetEUnary::eval_tree(int prune_to_width) +NetExpr* NetEUnary::eval_tree() { eval_expr(expr_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); @@ -1529,7 +1426,7 @@ NetExpr* NetEUnary::eval_tree(int prune_to_width) if (val.is_defined()) { verinum tmp (verinum::V0, val.len()); tmp.has_sign(val.has_sign()); - val = tmp - val; + val = verinum(tmp - val, val.len()); } else { for (unsigned idx = 0 ; idx < val.len() ; idx += 1) val.set(idx, verinum::Vx); @@ -1572,9 +1469,9 @@ NetExpr* NetEUnary::eval_tree(int prune_to_width) } -NetExpr* NetEUBits::eval_tree(int prune_to_width) +NetExpr* NetEUBits::eval_tree() { - return NetEUnary::eval_tree(prune_to_width); + return NetEUnary::eval_tree(); } NetEConst* NetEUReduce::eval_tree_real_() @@ -1598,7 +1495,7 @@ NetEConst* NetEUReduce::eval_tree_real_() return tmp; } -NetEConst* NetEUReduce::eval_tree(int) +NetEConst* NetEUReduce::eval_tree() { eval_expr(expr_); if (expr_type() == IVL_VT_REAL) return eval_tree_real_(); @@ -1955,9 +1852,8 @@ static NetExpr* evaluate_min_max(NetExpr*&arg0_, NetExpr*&arg1_, return res; } -NetExpr* NetESFunc::eval_tree(int prune_to_width) +NetExpr* NetESFunc::eval_tree() { -// assert(prune_to_width <= 0); // HERE /* If we are not targeting at least Verilog-2005, Verilog-AMS * or using the Icarus misc flag then we do not support these @@ -2056,7 +1952,7 @@ NetExpr* NetESFunc::eval_tree(int prune_to_width) return rtn; } -NetExpr* NetEUFunc::eval_tree(int) +NetExpr* NetEUFunc::eval_tree() { if (need_constant_expr) { cerr << get_fileline() << ": sorry: constant user " diff --git a/net_design.cc b/net_design.cc index 7dab97b6f..9bd4d1a69 100644 --- a/net_design.cc +++ b/net_design.cc @@ -332,8 +332,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the msb expression, if it is present. */ PExpr*msb_expr = (*cur).second.msb_expr; if (msb_expr) { - probe_expr_width(des, this, msb_expr); - (*cur).second.msb = elab_and_eval(des, this, msb_expr, -2); + (*cur).second.msb = elab_and_eval(des, this, msb_expr, -1); if (! eval_as_long(msb, (*cur).second.msb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate msb expression " @@ -349,8 +348,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) /* Evaluate the lsb expression, if it is present. */ PExpr*lsb_expr = (*cur).second.lsb_expr; if (lsb_expr) { - probe_expr_width(des, this, lsb_expr); - (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -2); + (*cur).second.lsb = elab_and_eval(des, this, lsb_expr, -1); if (! eval_as_long(lsb, (*cur).second.lsb)) { cerr << (*cur).second.val->get_fileline() << ": error: Unable to evaluate lsb expression " @@ -367,25 +365,11 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; - unsigned lval_wid = 0; + int lv_width = -2; if (range_flag) - lval_wid = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; + lv_width = (msb >= lsb) ? 1 + msb - lsb : 1 + lsb - msb; - bool unsized_flag = false; - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = val_expr->test_width(des, val_scope, lval_wid, lval_wid, - rval_type, unsized_flag); - - if (unsized_flag && !range_flag) - expr_wid = -1; - - int prune_wid = -1; - if (gn_strict_expr_width_flag) - prune_wid = 0; - if (range_flag) - prune_wid = lval_wid; - - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, expr_wid, prune_wid); + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width); if (! expr) return; @@ -427,7 +411,7 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) if (range_flag) { /* If we have a real value convert it to an integer. */ if(NetECReal*tmp = dynamic_cast(expr)) { - verinum nval(tmp->value().as_long64(), lval_wid); + verinum nval(tmp->value().as_long64(), (unsigned)lv_width); expr = new NetEConst(nval); expr->set_line(*((*cur).second.val)); (*cur).second.val = expr; @@ -504,18 +488,7 @@ void NetScope::evaluate_parameter_real_(Design*des, param_ref_t cur) PExpr*val_expr = (*cur).second.val_expr; NetScope*val_scope = (*cur).second.val_scope; - bool unsized_flag = false; - ivl_variable_type_t rval_type = IVL_VT_NO_TYPE; - int expr_wid = val_expr->test_width(des, val_scope, 0, 0, - rval_type, unsized_flag); - if (unsized_flag) - expr_wid = -1; - - int prune_wid = -1; - if (gn_strict_expr_width_flag) - prune_wid = 0; - - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, expr_wid, prune_wid); + NetExpr*expr = elab_and_eval(des, val_scope, val_expr, -1); if (! expr) return; diff --git a/net_expr.cc b/net_expr.cc index f54da9ea2..b632693fa 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -38,92 +38,17 @@ netenum_t*NetExpr::enumeration() const } /* - * Create an add/sub node from the two operands. Make a best guess of - * the + * Create an add/sub node from the two operands. */ -NetEBAdd::NetEBAdd(char op__, NetExpr*l, NetExpr*r, bool lossless_flag) -: NetEBinary(op__, l, r) +NetEBAdd::NetEBAdd(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - NetEConst* tmp; - - /* Catch the special case that one of the operands is an - unsized constant number. If so, then we should set the - width of that number to the size of the other operand, plus - one. This expands the expression to account for the largest - possible result. - - Remember to handle the special case of an unsized constant, - which we define to be at least "integer_width" bits. - - The set_width applied to a constant value will only - truncate the constant so far as it can still hold its - logical value, so this is safe to do. */ - if ( (tmp = dynamic_cast(r)) - && (! tmp->has_width()) - && (tmp->expr_width() > l->expr_width() || integer_width > l->expr_width()) ) { - - verinum tmp_v = trim_vnum(tmp->value()); - unsigned target_width = l->expr_width(); - if (target_width < tmp_v.len()) - target_width = tmp_v.len(); - if (lossless_flag) - target_width += 1; - if (target_width < integer_width) - target_width = integer_width; - - r->set_width(target_width); - - /* Note: This constant value will not gain a defined - width from this. Make sure. */ - assert(! r->has_width() ); - - expr_width(target_width); - - } else if ( (tmp = dynamic_cast(l)) - && (! tmp->has_width()) - && (tmp->expr_width() > r->expr_width() || integer_width > r->expr_width()) ) { - - verinum tmp_v = trim_vnum(tmp->value()); - unsigned target_width = r->expr_width(); - if (target_width < tmp_v.len()) - target_width = tmp_v.len(); - if (lossless_flag) - target_width += 1; - if (target_width < integer_width) - target_width = integer_width; - - l->set_width(target_width); - - /* Note: This constant value will not gain a defined - width from this. Make sure. */ - assert(! l->has_width() ); - - expr_width(target_width); - - } else if (r->expr_width() > l->expr_width()) { - unsigned loss_pad = lossless_flag? 1 : 0; - expr_width(r->expr_width() + loss_pad); - - } else { - unsigned loss_pad = lossless_flag? 1 : 0; - expr_width(l->expr_width() + loss_pad); - } - - cast_signed(l->has_sign() && r->has_sign()); } NetEBAdd::~NetEBAdd() { } -NetEBAdd* NetEBAdd::dup_expr() const -{ - NetEBAdd*result = new NetEBAdd(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBAdd::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -137,50 +62,10 @@ ivl_variable_type_t NetEBAdd::expr_type() const /* * Create a comparison operator with two sub-expressions. - * - * Handle the special case of an unsized constant on the left or right - * side by resizing the number to match the other - * expression. Otherwise, the netlist will have to allow the - * expressions to have different widths. */ NetEBComp::NetEBComp(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +: NetEBinary(op__, l, r, 1, false) { - // The output of compare is always unsigned. - cast_signed_base_(false); - - if (NetEConst*tmp = dynamic_cast(r)) do { - - if (tmp->has_width()) - break; - - if (l->expr_width() == 0) - break; - - if (tmp->expr_width() == l->expr_width()) - break; - - tmp->set_width(l->expr_width()); - - } while (0); - - if (NetEConst*tmp = dynamic_cast(l)) do { - - if (tmp->has_width()) - break; - - if (r->expr_width() == 0) - break; - - if (tmp->expr_width() == r->expr_width()) - break; - - tmp->set_width(r->expr_width()); - - } while (0); - - - expr_width(1); } NetEBComp::~NetEBComp() @@ -207,29 +92,15 @@ ivl_variable_type_t NetEBComp::expr_type() const return IVL_VT_BOOL; } -NetEBDiv::NetEBDiv(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBDiv::NetEBDiv(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - unsigned w = l->expr_width(); - if (r->expr_width() > w) - w = r->expr_width(); - - expr_width(w); - cast_signed(l->has_sign() && r->has_sign()); } NetEBDiv::~NetEBDiv() { } -NetEBDiv* NetEBDiv::dup_expr() const -{ - NetEBDiv*result = new NetEBDiv(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBDiv::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -241,11 +112,9 @@ ivl_variable_type_t NetEBDiv::expr_type() const return IVL_VT_LOGIC; } -NetEBMinMax::NetEBMinMax(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBMinMax::NetEBMinMax(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - expr_width( max(l->expr_width(), r->expr_width()) ); - cast_signed(l->has_sign() || r->has_sign()); } NetEBMinMax::~NetEBMinMax() @@ -262,30 +131,15 @@ ivl_variable_type_t NetEBMinMax::expr_type() const return IVL_VT_LOGIC; } -NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - if (expr_type() == IVL_VT_REAL) { - expr_width(1); - cast_signed(true); - } else { - expr_width(l->expr_width() + r->expr_width()); - cast_signed(l->has_sign() && r->has_sign()); - } } NetEBMult::~NetEBMult() { } -NetEBMult* NetEBMult::dup_expr() const -{ - NetEBMult*result = new NetEBMult(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBMult::expr_type() const { if (left_->expr_type() == IVL_VT_REAL) @@ -297,27 +151,15 @@ ivl_variable_type_t NetEBMult::expr_type() const return IVL_VT_LOGIC; } -NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - assert(op__ == 'p'); - /* You could need up to a * (2^b - 1) bits. */ - expr_width(l->expr_width()); - cast_signed(l->has_sign() || r->has_sign()); } NetEBPow::~NetEBPow() { } -NetEBPow* NetEBPow::dup_expr() const -{ - NetEBPow*result = new NetEBPow(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - ivl_variable_type_t NetEBPow::expr_type() const { if (right_->expr_type() == IVL_VT_REAL) @@ -328,13 +170,9 @@ ivl_variable_type_t NetEBPow::expr_type() const return IVL_VT_LOGIC; } -NetEBShift::NetEBShift(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBShift::NetEBShift(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - expr_width(l->expr_width()); - - // The >>> is signed if the left operand is signed. - if (op__ == 'R') cast_signed(l->has_sign()); } NetEBShift::~NetEBShift() @@ -346,24 +184,9 @@ bool NetEBShift::has_width() const return left_->has_width(); } -NetEBShift* NetEBShift::dup_expr() const -{ - NetEBShift*result = new NetEBShift(op_, left_->dup_expr(), - right_->dup_expr()); - result->set_line(*this); - return result; -} - -NetEConcat::NetEConcat(unsigned cnt, NetExpr* r) +NetEConcat::NetEConcat(unsigned cnt, unsigned r) : parms_(cnt), repeat_(r) { - if (repeat_ == 0) { - repeat_calculated_ = true; - repeat_value_ = 1; - } else { - repeat_calculated_ = false; - } - expr_width(0); } @@ -383,63 +206,7 @@ void NetEConcat::set(unsigned idx, NetExpr*e) assert(idx < parms_.count()); assert(parms_[idx] == 0); parms_[idx] = e; - expr_width( expr_width() + e->expr_width() ); -} - -NetEConcat* NetEConcat::dup_expr() const -{ - NetEConcat*dup = new NetEConcat(parms_.count(), 0); - dup->set_line(*this); - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) - if (parms_[idx]) { - NetExpr*tmp = parms_[idx]->dup_expr(); - assert(tmp); - dup->parms_[idx] = tmp; - } - - - dup->repeat_ = repeat_? repeat_->dup_expr() : 0; - dup->repeat_value_ = repeat_value_; - dup->repeat_calculated_ = repeat_calculated_; - dup->expr_width(expr_width()); - - return dup; -} - -unsigned NetEConcat::repeat() -{ - if (repeat_calculated_) - return repeat_value_; - - eval_expr(repeat_); - - NetEConst*repeat_const = dynamic_cast(repeat_); - - /* This should not be possible, as it was checked earlier to - assure that this is a constant expression. */ - if (repeat_const == 0) { - cerr << get_fileline() << ": internal error: repeat expression " - << "is not a compile time constant." << endl; - cerr << get_fileline() << ": : Expression is: " - << *repeat_ << endl; - repeat_calculated_ = true; - repeat_value_ = 1; - return 1; - } - - repeat_calculated_ = true; - repeat_value_ = repeat_const->value().as_ulong(); - - delete repeat_; - repeat_ = 0; - - return repeat_value_; -} - -unsigned NetEConcat::repeat() const -{ - assert(repeat_calculated_); - return repeat_value_; + expr_width( expr_width() + repeat_ * e->expr_width() ); } NetEConstEnum::NetEConstEnum(NetScope*s, perm_string n, netenum_t*eset, const verinum&v) @@ -461,7 +228,7 @@ NetECReal::NetECReal(const verireal&val) : value_(val) { expr_width(1); - cast_signed(true); + cast_signed_base_(true); } NetECReal::~NetECReal() @@ -475,14 +242,7 @@ const verireal& NetECReal::value() const bool NetECReal::has_width() const { - return false; -} - -NetECReal* NetECReal::dup_expr() const -{ - NetECReal*tmp = new NetECReal(value_); - tmp->set_line(*this); - return tmp; + return true; } ivl_variable_type_t NetECReal::expr_type() const diff --git a/net_proc.cc b/net_proc.cc index 103deeb7e..83487dd91 100644 --- a/net_proc.cc +++ b/net_proc.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -100,8 +100,6 @@ void NetCase::set_case(unsigned idx, NetExpr*e, NetProc*p) assert(idx < nitems_); items_[idx].guard = e; items_[idx].statement = p; - if (items_[idx].guard) - items_[idx].guard->set_width(expr_->expr_width()); } NetDisable::NetDisable(NetScope*tgt) @@ -169,4 +167,3 @@ const NetExpr* NetRepeat::expr() const { return expr_; } - diff --git a/netlist.cc b/netlist.cc index 05e3899a0..6af72c71d 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2095,21 +2095,9 @@ NetExpr::~NetExpr() { } -bool NetExpr::has_sign() const -{ - return signed_flag_; -} - void NetExpr::cast_signed(bool flag) { - signed_flag_ = flag; -} - -void NetExpr::expr_width(unsigned w) -{ - // Catch underflow wrap errors. - ivl_assert(*this, w < (UINT_MAX - 256)); - width_ = w; + cast_signed_base_(flag); } bool NetExpr::has_width() const @@ -2119,37 +2107,22 @@ bool NetExpr::has_width() const /* * Create a bitwise operator node from the opcode and the left and - * right expressions. Don't worry about the width of the expression - * yet, we'll get that from the l-value, whatever that turns out to - * be. However, if we don't, our default will be the width of the - * largest operand. + * right expressions. */ -NetEBBits::NetEBBits(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +NetEBBits::NetEBBits(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) +: NetEBinary(op__, l, r, wid, signed_flag) { - if (r->expr_width() > l->expr_width()) - expr_width(r->expr_width()); - else - expr_width(l->expr_width()); } NetEBBits::~NetEBBits() { } -NetEBBits* NetEBBits::dup_expr() const -{ - NetEBBits*result = new NetEBBits(op_, left_->dup_expr(), - right_->dup_expr()); - return result; -} - -NetEBinary::NetEBinary(char op__, NetExpr*l, NetExpr*r) +NetEBinary::NetEBinary(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : op_(op__), left_(l), right_(r) { - // Binary expressions of all sorts are signed if both the - // arguments are signed. - cast_signed_base_( left_->has_sign() && right_->has_sign()); + expr_width(wid); + cast_signed_base_(signed_flag); } NetEBinary::~NetEBinary() @@ -2163,29 +2136,15 @@ bool NetEBinary::has_width() const return left_->has_width() && right_->has_width(); } -NetEBinary* NetEBinary::dup_expr() const -{ - assert(0); - return 0; -} - NetEBLogic::NetEBLogic(char op__, NetExpr*l, NetExpr*r) -: NetEBinary(op__, l, r) +: NetEBinary(op__, l, r, 1, false) { - expr_width(1); } NetEBLogic::~NetEBLogic() { } -NetEBLogic* NetEBLogic::dup_expr() const -{ - NetEBLogic*result = new NetEBLogic(op_, left_->dup_expr(), - right_->dup_expr()); - return result; -} - NetEConst::NetEConst(const verinum&val) : NetExpr(val.len()), value_(val) { @@ -2196,6 +2155,12 @@ NetEConst::~NetEConst() { } +void NetEConst::cast_signed(bool flag) +{ + cast_signed_base_(flag); + value_.has_sign(flag); +} + const verinum& NetEConst::value() const { return value_; @@ -2218,6 +2183,17 @@ ivl_variable_type_t NetEConst::expr_type() const return IVL_VT_LOGIC; } +void NetEConst::trim() + +{ + if (value_.is_string()) + return; + + value_.has_len(false); + value_ = trim_vnum(value_); + expr_width(value_.len()); +} + NetEConstParam::NetEConstParam(NetScope*s, perm_string n, const verinum&v) : NetEConst(v), scope_(s), name_(n) { @@ -2272,7 +2248,7 @@ NetESignal::NetESignal(NetNet*n) { net_->incr_eref(); set_line(*n); - cast_signed(net_->get_signed()); + cast_signed_base_(net_->get_signed()); } NetESignal::NetESignal(NetNet*n, NetExpr*w) @@ -2280,7 +2256,7 @@ NetESignal::NetESignal(NetNet*n, NetExpr*w) { net_->incr_eref(); set_line(*n); - cast_signed(net_->get_signed()); + cast_signed_base_(net_->get_signed()); } NetESignal::~NetESignal() @@ -2334,20 +2310,12 @@ ivl_variable_type_t NetESignal::expr_type() const * should have the same width. NOTE: This matching of the widths really * has to be done in elaboration. */ -NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f) +NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, + unsigned wid, bool signed_flag) : cond_(c), true_val_(t), false_val_(f) { - if (type_is_vectorable(expr_type())) { - // use widest result - if (true_val_->expr_width() > false_val_->expr_width()) - expr_width(true_val_->expr_width()); - else - expr_width(false_val_->expr_width()); - } else { - expr_width(1); - } - - cast_signed(c->has_sign() && t->has_sign() && f->has_sign()); + expr_width(wid); + cast_signed_base_(signed_flag); } NetETernary::~NetETernary() @@ -2399,23 +2367,10 @@ ivl_variable_type_t NetETernary::expr_type() const return tru; } -NetEUnary::NetEUnary(char op__, NetExpr*ex) -: NetExpr(ex->expr_width()), op_(op__), expr_(ex) +NetEUnary::NetEUnary(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetExpr(wid), op_(op__), expr_(ex) { - switch (op_) { - case '!': - expr_width(1); - break; - } - switch (op_) { - case '-': - case '+': - case 'm': // abs() - cast_signed(ex->has_sign()); - break; - default: - ; - } + cast_signed_base_(signed_flag); } NetEUnary::~NetEUnary() @@ -2428,8 +2383,8 @@ ivl_variable_type_t NetEUnary::expr_type() const return expr_->expr_type(); } -NetEUBits::NetEUBits(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +NetEUBits::NetEUBits(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetEUnary(op__, ex, wid, signed_flag) { } @@ -2443,9 +2398,8 @@ ivl_variable_type_t NetEUBits::expr_type() const } NetEUReduce::NetEUReduce(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +: NetEUnary(op__, ex, 1, false) { - expr_width(1); } NetEUReduce::~NetEUReduce() @@ -2457,8 +2411,8 @@ ivl_variable_type_t NetEUReduce::expr_type() const return expr_->expr_type(); } -NetECast::NetECast(char op__, NetExpr*ex) -: NetEUnary(op__, ex) +NetECast::NetECast(char op__, NetExpr*ex, unsigned wid, bool signed_flag) +: NetEUnary(op__, ex, wid, signed_flag) { } diff --git a/netlist.h b/netlist.h index 676a348fd..623d023c0 100644 --- a/netlist.h +++ b/netlist.h @@ -1601,12 +1601,8 @@ class NetTran : public NetNode, public IslandBranch { * There are cases where expressions need to be represented. The * NetExpr class is the root of a hierarchy that serves that purpose. * - * The expr_width() is the width of the expression, that accounts - * for the widths of the sub-expressions I might have. It is up to the - * derived classes to properly set the expr width, if need be. The - * set_width() method is used to compel an expression to have a - * certain width, and is used particularly when the expression is an - * rvalue in an assignment statement. + * The expr_width() is the width of the expression, which is calculated + * before the expression is elaborated. */ class NetExpr : public LineInfo { public: @@ -1622,27 +1618,9 @@ class NetExpr : public LineInfo { // How wide am I? unsigned expr_width() const { return width_; } - // Coerce the expression to have a specific width. If the - // coercion works, then return true. Otherwise, return false. - // A coercion will work or not depending on the implementation - // in the derived class. Normally, the width will be set if - // the expression is: - // - already the requested size, OR - // - otherwise unsized. - // Normally, the resize will not allow a width size that loses - // data. For example, it will not reduce a constant expression - // to the point where significant bits are lost. But if the - // last_chance flag is true, then the method assumes that high - // bits will be lost anyhow, so try harder. Loss will be - // allowed, but it still won't resize fixed size expressions - // such as vector signals. This flag is meant to be used by - // elaboration of procedural assignment to set the expression - // width to the l-value width, if possible. - virtual bool set_width(unsigned wid, bool last_chance =false); - // This method returns true if the expression is // signed. Unsigned expressions return false. - bool has_sign() const; + bool has_sign() const { return signed_flag_; } virtual void cast_signed(bool flag); // This returns true if the expression has a definite @@ -1664,14 +1642,7 @@ class NetExpr : public LineInfo { // equivalent expression that is reduced as far as compile // time knows how. Essentially, this is designed to fold // constants. - // - // The prune_to_width is the maximum width that the result - // should be. If it is 0 or -1, then do not prune the - // result. If it is -1, go through special efforts to preserve - // values that may expand. A width of 0 corresponds to a - // self-determined context, and a width of -1 corresponds to - // an infinitely wide context. - virtual NetExpr*eval_tree(int prune_to_width = -1); + virtual NetExpr*eval_tree(); // Make a duplicate of myself, and subexpressions if I have // any. This is a deep copy operation. @@ -1696,10 +1667,9 @@ class NetExpr : public LineInfo { // the expression output. virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); - protected: - void expr_width(unsigned w); - void cast_signed_base_(bool flag) {signed_flag_ = flag; } + void expr_width(unsigned wid) { width_ = wid; } + void cast_signed_base_(bool flag) { signed_flag_ = flag; } private: unsigned width_; @@ -1723,11 +1693,15 @@ class NetEConst : public NetExpr { const verinum&value() const; - virtual bool set_width(unsigned w, bool last_chance =false); - virtual void cast_signed(bool sign_flag); + virtual void cast_signed(bool flag); virtual bool has_width() const; virtual ivl_variable_type_t expr_type() const; + /* This method allows the constant value to be converted + to an unsized value. This is used after evaluating a + unsized constant expression. */ + virtual void trim(); + virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1750,7 +1724,6 @@ class NetEConstEnum : public NetEConst { const NetScope*scope() const; netenum_t*enumeration() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1772,7 +1745,6 @@ class NetEConstParam : public NetEConst { perm_string name() const; const NetScope*scope() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -1794,10 +1766,6 @@ class NetECReal : public NetExpr { const verireal&value() const; - // Reals can be used in vector expressions. Conversions will - // be done at the right time. - virtual bool set_width(unsigned w, bool last_chance); - // This type has no self-determined width. This is false. virtual bool has_width() const; @@ -3129,14 +3097,13 @@ class NetEUFunc : public NetExpr { const NetScope* func() const; - virtual bool set_width(unsigned, bool last_chance); virtual ivl_variable_type_t expr_type() const; virtual void dump(ostream&) const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEUFunc*dup_expr() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); private: @@ -3332,7 +3299,7 @@ class NetAnalogTop : public LineInfo, public Attrib { class NetEBinary : public NetExpr { public: - NetEBinary(char op, NetExpr*l, NetExpr*r); + NetEBinary(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBinary(); const NetExpr*left() const { return left_; } @@ -3340,8 +3307,6 @@ class NetEBinary : public NetExpr { char op() const { return op_; } - virtual bool set_width(unsigned w, bool last_chance =false); - // A binary expression node only has a definite // self-determinable width if the operands both have definite // widths. @@ -3371,15 +3336,13 @@ class NetEBinary : public NetExpr { class NetEBAdd : public NetEBinary { public: - NetEBAdd(char op, NetExpr*l, NetExpr*r, bool lossless_flag =false); + NetEBAdd(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBAdd(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBAdd* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3394,15 +3357,13 @@ class NetEBAdd : public NetEBinary { class NetEBDiv : public NetEBinary { public: - NetEBDiv(char op, NetExpr*l, NetExpr*r); + NetEBDiv(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBDiv(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBDiv* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3426,12 +3387,11 @@ class NetEBDiv : public NetEBinary { class NetEBBits : public NetEBinary { public: - NetEBBits(char op, NetExpr*l, NetExpr*r); + NetEBBits(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBBits(); - virtual bool set_width(unsigned w, bool last_chance); virtual NetEBBits* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); }; @@ -3456,13 +3416,11 @@ class NetEBComp : public NetEBinary { NetEBComp(char op, NetExpr*l, NetExpr*r); ~NetEBComp(); - virtual bool set_width(unsigned w, bool last_chance =false); - /* A compare expression has a definite width. */ virtual bool has_width() const; virtual ivl_variable_type_t expr_type() const; virtual NetEBComp* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3492,9 +3450,8 @@ class NetEBLogic : public NetEBinary { NetEBLogic(char op, NetExpr*l, NetExpr*r); ~NetEBLogic(); - virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEBLogic* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3511,7 +3468,7 @@ class NetEBLogic : public NetEBinary { class NetEBMinMax : public NetEBinary { public: - NetEBMinMax(char op, NetExpr*l, NetExpr*r); + NetEBMinMax(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBMinMax(); virtual ivl_variable_type_t expr_type() const; @@ -3525,15 +3482,13 @@ class NetEBMinMax : public NetEBinary { class NetEBMult : public NetEBinary { public: - NetEBMult(char op, NetExpr*l, NetExpr*r); + NetEBMult(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBMult(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); - virtual void cast_signed(bool sign_flag); virtual NetEBMult* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3546,14 +3501,13 @@ class NetEBMult : public NetEBinary { class NetEBPow : public NetEBinary { public: - NetEBPow(char op, NetExpr*l, NetExpr*r); + NetEBPow(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBPow(); virtual ivl_variable_type_t expr_type() const; - virtual bool set_width(unsigned w, bool last_chance); virtual NetEBPow* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); private: @@ -3572,17 +3526,15 @@ class NetEBPow : public NetEBinary { class NetEBShift : public NetEBinary { public: - NetEBShift(char op, NetExpr*l, NetExpr*r); + NetEBShift(char op, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag); ~NetEBShift(); - virtual bool set_width(unsigned w, bool last_chance); - // A shift expression only needs the left expression to have a // definite width to give the expression a definite width. virtual bool has_width() const; virtual NetEBShift* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3602,31 +3554,27 @@ class NetEBShift : public NetEBinary { class NetEConcat : public NetExpr { public: - NetEConcat(unsigned cnt, NetExpr* repeat =0); + NetEConcat(unsigned cnt, unsigned repeat =1); ~NetEConcat(); // Manipulate the parameters. void set(unsigned idx, NetExpr*e); - unsigned repeat(); - unsigned repeat() const; + unsigned repeat() const { return repeat_; } unsigned nparms() const { return parms_.count() ; } NetExpr* parm(unsigned idx) const { return parms_[idx]; } virtual NexusSet* nex_input(bool rem_out = true); virtual bool has_width() const; - virtual bool set_width(unsigned w, bool last_chance =false); virtual NetEConcat* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; private: svectorparms_; - NetExpr* repeat_; - unsigned repeat_value_; - bool repeat_calculated_; + unsigned repeat_; }; @@ -3656,10 +3604,9 @@ class NetESelect : public NetExpr { ivl_select_type_t select_type() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual bool set_width(unsigned w, bool last_chance =false); virtual bool has_width() const; virtual void expr_scan(struct expr_scan_t*) const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual NetESelect* dup_expr() const; virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root); virtual void dump(ostream&) const; @@ -3757,11 +3704,10 @@ class NetESFunc : public NetExpr { NetExpr* parm(unsigned idx); const NetExpr* parm(unsigned idx) const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); - virtual bool set_width(unsigned, bool last_chance); virtual netenum_t* enumeration() const; virtual void dump(ostream&) const; @@ -3788,17 +3734,15 @@ class NetESFunc : public NetExpr { class NetETernary : public NetExpr { public: - NetETernary(NetExpr*c, NetExpr*t, NetExpr*f); + NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, unsigned wid, bool signed_flag); ~NetETernary(); - virtual bool set_width(unsigned w, bool last_chance); - const NetExpr*cond_expr() const; const NetExpr*true_expr() const; const NetExpr*false_expr() const; virtual NetETernary* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); @@ -3837,16 +3781,14 @@ class NetETernary : public NetExpr { class NetEUnary : public NetExpr { public: - NetEUnary(char op, NetExpr*ex); + NetEUnary(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetEUnary(); char op() const { return op_; } const NetExpr* expr() const { return expr_; } - virtual bool set_width(unsigned w, bool last_chance); - virtual NetEUnary* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual ivl_variable_type_t expr_type() const; @@ -3865,13 +3807,13 @@ class NetEUnary : public NetExpr { class NetEUBits : public NetEUnary { public: - NetEUBits(char op, NetExpr*ex); + NetEUBits(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetEUBits(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUBits* dup_expr() const; - virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual NetExpr* eval_tree(); virtual ivl_variable_type_t expr_type() const; }; @@ -3881,10 +3823,9 @@ class NetEUReduce : public NetEUnary { NetEUReduce(char op, NetExpr*ex); ~NetEUReduce(); - virtual bool set_width(unsigned w, bool last_chance); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual NetEUReduce* dup_expr() const; - virtual NetEConst* eval_tree(int prune_to_width = -1); + virtual NetEConst* eval_tree(); virtual ivl_variable_type_t expr_type() const; private: @@ -3894,7 +3835,7 @@ class NetEUReduce : public NetEUnary { class NetECast : public NetEUnary { public: - NetECast(char op, NetExpr*ex); + NetECast(char op, NetExpr*ex, unsigned wid, bool signed_flag); ~NetECast(); virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); @@ -3920,7 +3861,6 @@ class NetESignal : public NetExpr { ~NetESignal(); perm_string name() const; - virtual bool set_width(unsigned, bool last_chance); virtual NetESignal* dup_expr() const; NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); diff --git a/netmisc.cc b/netmisc.cc index 4d0ea5c06..d3fc7a18d 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -184,9 +184,9 @@ NetNet* cast_to_real(Design*des, NetScope*scope, NetNet*src) NetExpr* cast_to_int2(NetExpr*expr) { - NetECast*cast = new NetECast('2', expr); + NetECast*cast = new NetECast('2', expr, expr->expr_width(), + expr->has_sign()); cast->set_line(*expr); - cast->cast_signed(expr->has_sign()); return cast; } @@ -208,17 +208,14 @@ static NetExpr* make_add_expr(NetExpr*expr, long val) val = -val; } - verinum val_v (val); + verinum val_v (val, expr->expr_width()); val_v.has_sign(true); - if (expr->has_width()) { - val_v = verinum(val_v, expr->expr_width()); - } - NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); - NetEBAdd*res = new NetEBAdd(add_op, expr, val_c); + NetEBAdd*res = new NetEBAdd(add_op, expr, val_c, expr->expr_width(), + expr->has_sign()); res->set_line(*expr); return res; @@ -235,7 +232,8 @@ static NetExpr* make_sub_expr(long val, NetExpr*expr) NetEConst*val_c = new NetEConst(val_v); val_c->set_line(*expr); - NetEBAdd*res = new NetEBAdd('-', val_c, expr); + NetEBAdd*res = new NetEBAdd('-', val_c, expr, expr->expr_width(), + expr->has_sign()); res->set_line(*expr); return res; @@ -444,11 +442,10 @@ NetExpr* condition_reduce(NetExpr*expr) return expr; verinum zero (verinum::V0, expr->expr_width()); + zero.has_sign(expr->has_sign()); NetEConst*ezero = new NetEConst(zero); ezero->set_line(*expr); - ezero->cast_signed(expr->has_sign()); - ezero->set_width(expr->expr_width()); NetEBComp*cmp = new NetEBComp('n', expr, ezero); cmp->set_line(*expr); @@ -457,43 +454,130 @@ NetExpr* condition_reduce(NetExpr*expr) return cmp; } -void probe_expr_width(Design*des, NetScope*scope, PExpr*pe) +static const char*width_mode_name(PExpr::width_mode_t mode) { - ivl_variable_type_t expr_type = IVL_VT_NO_TYPE; - bool flag = false; - pe->test_width(des, scope, 0, 0, expr_type, flag); + switch (mode) { + case PExpr::SIZED: + return "sized"; + case PExpr::LOSSLESS: + return "lossless"; + case PExpr::UNSIZED: + return "unsized"; + default: + return "??"; + } } -NetExpr* elab_and_eval(Design*des, NetScope*scope, - const PExpr*pe, int expr_wid, int prune_width) +NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, int context_width) { - NetExpr*tmp = pe->elaborate_expr(des, scope, expr_wid, false); + PExpr::width_mode_t mode = PExpr::SIZED; + if ((context_width == -2) && !gn_strict_expr_width_flag) + mode = PExpr::EXPAND; + + pe->test_width(des, scope, mode); + + // Get the final expression width. If the expression is unsized, + // this may be different from the value returned by test_width(). + unsigned expr_width = pe->expr_width(); + + // If context_width is positive, this is the RHS of an assignment, + // so the LHS width must also be included in the width calculation. + if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) + && (expr_width < (unsigned)context_width)) + expr_width = context_width; + + if (debug_elaborate) { + cerr << pe->get_fileline() << ": debug: test_width of " + << *pe << endl; + cerr << pe->get_fileline() << ": " + << "returns type=" << pe->expr_type() + << ", width=" << expr_width + << ", signed=" << pe->has_sign() + << ", mode=" << width_mode_name(mode) << endl; + } + + // If we can get the same result using a smaller expression + // width, do so. + if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) + && (expr_width > (unsigned)context_width)) { + expr_width = max(pe->min_width(), (unsigned)context_width); + + if (debug_elaborate) { + cerr << pe->get_fileline() << ": " + << "pruned to width=" << expr_width << endl; + } + } + + NetExpr*tmp = pe->elaborate_expr(des, scope, expr_width, false); if (tmp == 0) return 0; - eval_expr(tmp, prune_width); + eval_expr(tmp, context_width); + + if (NetEConst*ce = dynamic_cast(tmp)) { + if ((mode >= PExpr::LOSSLESS) && (context_width < 0)) + ce->trim(); + } return tmp; } -void eval_expr(NetExpr*&expr, int prune_width) +NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, + unsigned arg_idx, PExpr*pe) +{ + PExpr::width_mode_t mode = PExpr::SIZED; + pe->test_width(des, scope, mode); + + if (debug_elaborate) { + cerr << pe->get_fileline() << ": debug: test_width of " + << name << " argument " << (arg_idx+1) << " " << *pe << endl; + cerr << pe->get_fileline() << ": " + << "returns type=" << pe->expr_type() + << ", width=" << pe->expr_width() + << ", signed=" << pe->has_sign() + << ", mode=" << width_mode_name(mode) << endl; + } + + NetExpr*tmp = pe->elaborate_expr(des, scope, pe->expr_width(), true); + if (tmp == 0) return 0; + + eval_expr(tmp, -1); + + if (NetEConst*ce = dynamic_cast(tmp)) { + if (mode != PExpr::SIZED) + ce->trim(); + } + + return tmp; +} + +void eval_expr(NetExpr*&expr, int context_width) { assert(expr); if (dynamic_cast(expr)) return; - /* Resize a constant if allowed and needed. */ - if (NetEConst *tmp = dynamic_cast(expr)) { - if (prune_width <= 0) return; - if (tmp->has_width()) return; - if ((unsigned)prune_width <= tmp->expr_width()) return; - expr = pad_to_width(expr, (unsigned)prune_width, *expr); - return; - } - NetExpr*tmp = expr->eval_tree(prune_width); + NetExpr*tmp = expr->eval_tree(); if (tmp != 0) { tmp->set_line(*expr); delete expr; expr = tmp; } + + if (context_width <= 0) return; + + NetEConst *ce = dynamic_cast(expr); + if (ce == 0) return; + + // The expression is a constant, so resize it if needed. + if (ce->expr_width() < (unsigned)context_width) { + expr = pad_to_width(expr, context_width, *expr); + } + if (ce->expr_width() > (unsigned)context_width) { + verinum value(ce->value(), context_width); + ce = new NetEConst(value); + ce->set_line(*expr); + delete expr; + expr = ce; + } } bool eval_as_long(long&value, NetExpr*expr) @@ -560,7 +644,6 @@ hname_t eval_path_component(Design*des, NetScope*scope, assert(index.sel == index_component_t::SEL_BIT); // Evaluate the bit select to get a number. - probe_expr_width(des, scope, index.msb); NetExpr*tmp = elab_and_eval(des, scope, index.msb, -1); ivl_assert(*index.msb, tmp); diff --git a/netmisc.h b/netmisc.h index 14c6628df..6f98d30d4 100644 --- a/netmisc.h +++ b/netmisc.h @@ -1,7 +1,7 @@ #ifndef __netmisc_H #define __netmisc_H /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -151,45 +151,38 @@ extern bool is_param_expr; * constant expression. If it cannot be evaluated, it returns whatever * it can. If the expression cannot be elaborated, return 0. * - * The expr_width is the width of the context where the expression is - * being elaborated, or -1 if the expression is self-determined width. - * - * The prune_width is the maximum width of the result, and is passed - * to the eval_tree method of the expression to limit constant - * results. The evaluation will prune any constant result down to the - * prune_width (if >0) so should only be used at the point where it is - * bound to the destination. + * The context_width is the width of the context where the expression is + * being elaborated, or -1 if the expression is self-determined, or -2 + * if the expression is lossless self-determined (this last option is + * treated as standard self-determined if the gn_strict_expr_width flag + * is set). */ class PExpr; extern NetExpr* elab_and_eval(Design*des, NetScope*scope, - const PExpr*pe, int expr_wid, - int prune_width =-1); - -void probe_expr_width(Design*des, NetScope*scope, PExpr*pe); + PExpr*pe, int context_width); +/* + * This function is a variant of elab_and_eval that elaborates and + * evaluates the arguments of a system task. + */ +extern NetExpr* elab_sys_task_arg(Design*des, NetScope*scope, perm_string name, + unsigned arg_idx, PExpr*pe); /* * This function elaborates an expression as if it is for the r-value - * of an assignment, The data_type_lv and expr_wid_lv are the type and - * with of the l-value, and the expr is the expression to - * elaborate. The result is the NetExpr elaborated and evaluated. - * (See elab_expr.cc) + * of an assignment, The lv_type and lv_width are the type and width + * of the l-value, and the expr is the expression to elaborate. The + * result is the NetExpr elaborated and evaluated. (See elab_expr.cc) */ extern NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, - ivl_variable_type_t data_type_lv, - int expr_wid_lv, PExpr*expr); + ivl_variable_type_t lv_type, + unsigned lv_width, PExpr*expr); /* - * Used by elaboration to suppress the sign of an operand if the other - * is unsigned. - */ -extern void suppress_binary_operand_sign_if_needed(NetExpr*lp, NetExpr*rp); - -/* - * This procedure elaborates an expression and if the elaboration is + * This procedure evaluates an expression and if the evaluation is * successful the original expression is replaced with the new one. */ -void eval_expr(NetExpr*&expr, int prune_width =-1); +void eval_expr(NetExpr*&expr, int context_width =-1); /* * Get the long integer value for the passed in expression, if diff --git a/set_width.cc b/set_width.cc deleted file mode 100644 index 9e5dd4278..000000000 --- a/set_width.cc +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) - * - * This source code is free software; you can redistribute it - * and/or modify it in source code form under the terms of the GNU - * General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -# include "config.h" - -# include - -/* - * This file contains set_width methods for the various NetExpr - * classes. The set_width method is used by elaboration to ask the - * expression to resize itself. If the expression can't, then the - * set_width method will return false and the caller will arrange for - * whatever is needed to deal with the size mismatch. - */ -# include "netlist.h" -# include "netmisc.h" -# include "compiler.h" -# include - - -bool NetExpr::set_width(unsigned w, bool) -{ - cerr << get_fileline() << ": internal warning: " - <>) - /* these operators are handled in the derived class. */ - assert(0); - break; - - /* The default rule is that the operands of the binary - operator might as well use the same width as the - output from the binary operation. */ - default: - expr_width(left_->expr_width() > right_->expr_width() - ? left_->expr_width() : right_->expr_width()); - cerr << "NetEBinary::set_width(): Using default for " << - op_ << "." << endl; - flag = false; - - case '%': - case '/': - flag = left_->set_width(w) && flag; - flag = right_->set_width(w) && flag; - expr_width(w); - break; - } - return flag; -} - -/* - * The bitwise logical operators have operands the same size as the - * result. Anything else is a mess. - */ -bool NetEBAdd::set_width(unsigned w, bool) -{ - - unsigned wid = w; - if (left_->expr_width() > wid) - wid = left_->expr_width(); - if (right_->expr_width() > wid) - wid = right_->expr_width(); - - left_->set_width(wid); - right_->set_width(wid); - - if (left_->expr_width() < wid) { - NetExpr*tmp = new NetESelect(left_, 0, wid); - tmp->cast_signed(left_->has_sign()); - left_ = tmp; - } - - if (right_->expr_width() < wid) { - NetExpr*tmp = new NetESelect(right_, 0, wid); - tmp->cast_signed(right_->has_sign()); - right_ = tmp; - } - - expr_width(wid); - return w == wid; -} - -void NetEBAdd::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - cast_signed_base_(sign_flag); -} - -/* - * The bitwise logical operators have operands the same size as the - * result. Anything else is a mess. I first try to get the operands to - * shrink to the desired size. I then expand operands that are too small. - */ -bool NetEBBits::set_width(unsigned w, bool) -{ - /* First, give the operands a chance to adjust themselves to - the requested width. */ - left_->set_width(w); - right_->set_width(w); - - - /* */ - - unsigned use_width = w; - if (left_->expr_width() > use_width) - use_width = left_->expr_width(); - if (right_->expr_width() > use_width) - use_width = right_->expr_width(); - - /* If the operands end up too small, then pad them to suit. */ - - if (left_->expr_width() < use_width) { - NetExpr*tmp = pad_to_width(left_, use_width, *this); - assert(tmp); - left_ = tmp; - } - - if (right_->expr_width() < w) { - NetExpr*tmp = pad_to_width(right_, use_width, *this); - assert(tmp); - right_ = tmp; - } - - - /* And here is the final width. If this is not the size the - caller requested, then return false. Otherwise, return - true. */ - expr_width(use_width); - return w == use_width; -} - -/* - * Comparison operators allow the subexpressions to have - * their own natural width, but the comparison operator result has a - * fixed width of 1. - */ -bool NetEBComp::set_width(unsigned w, bool) -{ - return w == 1; -} - -/* - * There is nothing we can do to the operands of a division to make it - * confirm to the requested width. Force the context to pad or truncate. - */ -bool NetEBDiv::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -void NetEBDiv::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - - cast_signed_base_(sign_flag); -} - -bool NetEBLogic::set_width(unsigned w, bool) -{ - bool flag; - flag = left_->set_width(right_->expr_width()); - if (!flag) - flag = right_->set_width(left_->expr_width()); - return w == 1; -} - -/* - * There is nothing we can do to the operands of a multiply to make it - * confirm to the requested width. Force the context to pad or truncate. - */ -bool NetEBMult::set_width(unsigned w, bool) -{ - if (w < left_->expr_width()) - left_->set_width(w); - if (w < right_->expr_width()) - right_->expr_width(); - - expr_width(w); - return true; -} - -void NetEBMult::cast_signed(bool sign_flag) -{ - if (has_sign() == sign_flag) - return; - - if (sign_flag == false) { - left_->cast_signed(sign_flag); - right_->cast_signed(sign_flag); - } - - cast_signed_base_(sign_flag); -} - -bool NetEBPow::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * The shift operator allows the shift amount to have its own - * natural width. The width of the operator result is the width of the - * left operand, the value that is to be shifted. - */ -bool NetEBShift::set_width(unsigned w, bool) -{ - switch (op()) { - - case 'l': - left_->set_width(w); - if (left_->expr_width() < w) - left_ = pad_to_width(left_, w, *this); - break; - - case 'r': - case 'R': - if (left_->expr_width() < w) - left_ = pad_to_width(left_, w, *this); - break; - - default: - assert(0); - } - - expr_width(left_->expr_width()); - - return w == expr_width(); -} - -/* - * Add up the widths from all the expressions that are concatenated - * together. This is the width of the expression, tough luck if you - * want it otherwise. - * - * If during the course of elaboration one of the sub-expressions is - * broken, then don't count it in the width. This doesn't really - * matter because the null expression is indication of an error and - * the compiler will not go beyond elaboration. - */ -bool NetEConcat::set_width(unsigned w, bool) -{ - unsigned sum = 0; - for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) - if (parms_[idx] != 0) - sum += parms_[idx]->expr_width(); - - sum *= repeat(); - expr_width(sum); - return w == sum; -} - -bool NetEConst::set_width(unsigned w, bool last_chance) -{ - /* Make the value signed if the NetEConst is signed. - * This happens when $signed() is called, so this - * sign information needs to be propagated. */ - value_.has_sign(has_sign()); - - if (w == value_.len()) { - expr_width(w); - return true; - } - - assert(w != 0); - - if (w > value_.len()) { - verinum::V pad = verinum::V0; - if (value_.has_sign()) { - pad = value_.get(value_.len()-1); - - /* It appears that you always have a defined length here, - * so this logic may be in error. */ - } else if (value_.len() != 0 && !value_.has_len()) - switch (value_.get(value_.len()-1)) { - case verinum::V1: - case verinum::V0: - break; - case verinum::Vx: - pad = verinum::Vx; - break; - case verinum::Vz: - pad = verinum::Vz; - break; - } - - verinum tmp (verinum::V0, w, has_width()); - for (unsigned idx = 0 ; idx < value_.len() ; idx += 1) - tmp.set(idx, value_[idx]); - for (unsigned idx = value_.len() ; idx < w ; idx += 1) - tmp.set(idx, pad); - - tmp.has_sign(value_.has_sign()); - value_ = tmp; - - expr_width(w); - return true; - - } else { - unsigned use_w = w; - - verinum::V pad_bit = value_.has_sign() - ? value_[value_.len() - 1] - : verinum::V0; - - if (! last_chance) { - // Don't reduce a number too small to hold all the - // significant bits. - for (unsigned idx = w ; idx < value_.len() ; idx += 1) - if (value_[idx] != pad_bit) - use_w = idx+1; - - // Correct for the special case of signed value. We - // cannot have the result change sign on us. - if (value_.has_sign() && (use_w < value_.len()) - && (value_[use_w-1] != pad_bit)) - use_w += 1; - } - - verinum tmp (verinum::V0, use_w, has_width()); - for (unsigned idx = 0 ; idx < use_w ; idx += 1) - tmp.set(idx, value_[idx]); - - tmp.has_sign(value_.has_sign()); - value_ = tmp; - expr_width(use_w); - return w == use_w; - } -} - -void NetEConst::cast_signed(bool sign_flag) -{ - value_.has_sign(sign_flag); - cast_signed_base_(sign_flag); -} - -/* - * Parameter vectors cannot be resized because they refer to a common - * value. Ditto for enumeration names. - */ -bool NetEConstParam::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -bool NetEConstEnum::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * Real constants can have whatever width the environment wants, - * because it isn't really a vector. The environment will convert this - * to a vector at the right time. - */ -bool NetECReal::set_width(unsigned w, bool) -{ - expr_width(w); - return true; -} -#if 0 -bool NetEMemory::set_width(unsigned w, bool) -{ - if (w != mem_->width()) - return false; - - expr_width(w); - return true; -} -#endif -bool NetESelect::set_width(unsigned, bool) -{ - return expr_width() == 1; -} - -bool NetESFunc::set_width(unsigned w, bool) -{ - return w == expr_width(); -} - -/* - * The signal should automatically pad with zeros to get to the desired - * width. Do not allow signal bits to be truncated, however. - */ -bool NetESignal::set_width(unsigned w, bool) -{ - return w == vector_width(); -} - -bool NetETernary::set_width(unsigned w, bool last_chance) -{ - bool flag = true; - flag = flag && true_val_->set_width(w, last_chance); - flag = flag && false_val_->set_width(w, last_chance); - - /* The ternary really insists that the true and false clauses - have the same width. Even if we fail to make the width that - the user requests, at least pad the smaller width to suit - the larger. */ - if (true_val_->expr_width() < false_val_->expr_width()) - true_val_ = pad_to_width(true_val_, false_val_->expr_width(), - *this); - if (false_val_->expr_width() < true_val_->expr_width()) - false_val_ = pad_to_width(false_val_, true_val_->expr_width(), - *this); - - expr_width(true_val_->expr_width()); - return flag; -} - -/* - * XXXX FIX ME: For now, just take whatever the caller says as my - * width. What I really need to do is note the width of the output - * parameter of the function definition and take that into account. - */ -bool NetEUFunc::set_width(unsigned w, bool) -{ - return w == result_sig_->expr_width(); -} - -bool NetEUnary::set_width(unsigned w, bool) -{ - bool flag = true; - switch (op_) { - case '~': - case '-': - case 'r': - case 'i': - flag = expr_->set_width(w); - expr_width(w); - break; - case '!': - return w == 1; - default: - flag = expr_width() == w; - break; - } - - return flag; -} - -/* - * Unary reduction operators allow its operand to have any width. The - * result is defined to be 1. - */ -bool NetEUReduce::set_width(unsigned w, bool) -{ - return w == 1; -} diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 99fba7a13..0db2ffd62 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -274,8 +274,8 @@ void dll_target::expr_param(const NetEConstParam*net) << ivl_scope_name(scop) << endl; } assert(par); - assert(par->value); - expr_ = par->value; + expr_const(net); + expr_->u_.string_.parameter = par; } void dll_target::expr_rparam(const NetECRealParam*net) diff --git a/verinum.cc b/verinum.cc index 5c79077dc..1b576c224 100644 --- a/verinum.cc +++ b/verinum.cc @@ -287,7 +287,7 @@ verinum::verinum(const verinum&that) verinum::verinum(const verinum&that, unsigned nbits) { - string_flag_ = false; + string_flag_ = that.string_flag_ && (that.nbits_ == nbits); nbits_ = nbits; bits_ = new V[nbits_]; has_len_ = true; @@ -591,6 +591,39 @@ verinum pad_to_width(const verinum&that, unsigned width) return val; } +verinum cast_to_width(const verinum&that, unsigned width) +{ + if (that.has_len() && (that.len() == width)) + return that; + + if (that.len() >= width) + return verinum(that, width); + + if (that.len() == 0) { + verinum val (verinum::V0, width, true); + val.has_sign(that.has_sign()); + return val; + } + + verinum::V pad = that[that.len()-1]; + if (pad==verinum::V1 && !that.has_sign()) + pad = verinum::V0; + if (that.has_len() && !that.has_sign()) { + if (pad==verinum::Vx) + pad = verinum::V0; + if (pad==verinum::Vz) + pad = verinum::V0; + } + + verinum val(pad, width, true); + + for (unsigned idx = 0 ; idx < that.len() ; idx += 1) + val.set(idx, that[idx]); + + val.has_sign(that.has_sign()); + return val; +} + /* * This function returns a version of the verinum that has only as * many bits as are needed to accurately represent the value. It takes @@ -1097,6 +1130,7 @@ verinum pow(const verinum&left, const verinum&right) verinum operator << (const verinum&that, unsigned shift) { verinum result(verinum::V0, that.len() + shift, that.has_len()); + result.has_sign(that.has_sign()); for (unsigned idx = 0 ; idx < that.len() ; idx += 1) result.set(idx+shift, that.get(idx)); @@ -1119,6 +1153,7 @@ verinum operator >> (const verinum&that, unsigned shift) verinum result(that.has_sign()? that.get(that.len()-1) : verinum::V0, that.len() - shift, that.has_len()); + result.has_sign(that.has_sign()); for (unsigned idx = shift ; idx < that.len() ; idx += 1) result.set(idx-shift, that.get(idx)); diff --git a/verinum.h b/verinum.h index d44941291..acab1d673 100644 --- a/verinum.h +++ b/verinum.h @@ -1,7 +1,7 @@ #ifndef __verinum_H #define __verinum_H /* - * Copyright (c) 1998-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -64,6 +64,7 @@ class verinum { // A number "has a length" if the length was specified fixed // in some way. + bool has_len(bool flag) { has_len_ = flag; return has_len_; } bool has_len() const { return has_len_; } bool has_sign(bool flag) { has_sign_ = flag; return has_sign_; } @@ -126,6 +127,12 @@ inline verinum::V sign_bit(const verinum&val) extension, if the value is signed. */ extern verinum pad_to_width(const verinum&, unsigned width); +/* Return a verinum that has the same value as the input, but is + exactly the requested width. This may involve sign extension, + if the value is signed. The returned verinum will have fixed + width. */ +extern verinum cast_to_width(const verinum&, unsigned width); + /* Return a verinum that is minimal. That is, it has only the length needed to accurately represent the contained value, signed or not. */ extern verinum trim_vnum(const verinum&); From 06447817d3e8848e052a59be320b2711ac996dcc Mon Sep 17 00:00:00 2001 From: Cary R Date: Sun, 27 Feb 2011 19:20:16 -0800 Subject: [PATCH 17/35] Add support for tracing procedural statements. This patch adds support for tracing procedural statement execution in vvp. This is accomplished by adding a new opcode that is inserted before the code that represents a procedural statement. These opcodes also trigger a message whenever time advances. By default these opcodes are not added. To add them, pass the -pfileline=1 flag to the compiler. In the future we may add support for turning the debug output on and off once the opcodes have been added with a system task or from the interactive prompt. --- driver/iverilog.man.in | 6 +- elaborate.cc | 6 + tgt-vvp/draw_ufunc.c | 8 +- tgt-vvp/eval_bool.c | 7 +- tgt-vvp/eval_expr.c | 14 +- tgt-vvp/vvp.c | 37 +++- tgt-vvp/vvp_priv.h | 8 +- tgt-vvp/vvp_process.c | 477 ++++++++++++++++++++++++++++++++++++++--- vpi_user.h | 4 +- vvp/Makefile.in | 4 +- vvp/codes.cc | 4 +- vvp/codes.h | 3 +- vvp/compile.cc | 19 +- vvp/compile.h | 5 +- vvp/lexor.lex | 11 +- vvp/opcodes.txt | 14 +- vvp/parse.y | 13 +- vvp/schedule.cc | 8 +- vvp/vpi_priv.h | 13 +- vvp/vthread.cc | 14 +- 20 files changed, 614 insertions(+), 61 deletions(-) diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in index 5ec8bf596..3bd8482cf 100644 --- a/driver/iverilog.man.in +++ b/driver/iverilog.man.in @@ -1,4 +1,4 @@ -.TH iverilog 1 "January 8th, 2011" "" "Version %M.%m.%n %E" +.TH iverilog 1 "February 27th, 2011" "" "Version %M.%m.%n %E" .SH NAME iverilog - Icarus Verilog compiler @@ -258,7 +258,9 @@ checking the syntax of the Verilog source. .B vvp This is the default. The vvp target generates code for the vvp runtime. The output is a complete program that simulates the design -but must be run by the \fBvvp\fP command. +but must be run by the \fBvvp\fP command. The -pfileline=1 option +can be used to add procedural statement debugging opcodes to the +generated code. .TP 8 .B fpga This is a synthesis target that supports a variety of fpga devices, diff --git a/elaborate.cc b/elaborate.cc index f50259303..d671827ac 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2192,6 +2192,7 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const return 0; } st = event_->elaborate(des, scope); + st->set_line(*this); if (st == 0) { cerr << event_->get_fileline() << ": error: " "unable to elaborate event expression." @@ -2217,12 +2218,15 @@ NetProc* PAssign::elaborate(Design*des, NetScope*scope) const // We need a repeat statement. } else { st = new NetRepeat(count, st); + st->set_line(*this); } } else { st = new NetRepeat(count, st); + st->set_line(*this); } } else { st = event_->elaborate_st(des, scope, a2); + st->set_line(*this); if (st == 0) { cerr << event_->get_fileline() << ": error: " "unable to elaborate event expression." @@ -3852,7 +3856,9 @@ NetProc* PTrigger::elaborate(Design*des, NetScope*scope) const NetProc* PWhile::elaborate(Design*des, NetScope*scope) const { NetExpr*tmp = elab_and_eval(des, scope, cond_, -1); + tmp->set_line(*this); NetWhile*loop = new NetWhile(tmp, statement_->elaborate(des, scope)); + loop->set_line(*this); return loop; } diff --git a/tgt-vvp/draw_ufunc.c b/tgt-vvp/draw_ufunc.c index 554462193..a7b6ecd10 100644 --- a/tgt-vvp/draw_ufunc.c +++ b/tgt-vvp/draw_ufunc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2005-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -51,7 +51,7 @@ static void function_argument_real(ivl_signal_t port, ivl_expr_t expr) /* ports cannot be arrays. */ assert(ivl_signal_dimensions(port) == 0); - fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", port, res); + fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", port, res); clr_word(res); } @@ -180,9 +180,9 @@ int draw_ufunc_real(ivl_expr_t expr) /* Call the function */ - fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); - fprintf(vvp_out, " %%join;\n"); + fprintf(vvp_out, " %%join;\n"); /* Return value signal cannot be an array. */ assert(ivl_signal_dimensions(retval) == 0); diff --git a/tgt-vvp/eval_bool.c b/tgt-vvp/eval_bool.c index 5a8c1ce6f..e37a51f75 100644 --- a/tgt-vvp/eval_bool.c +++ b/tgt-vvp/eval_bool.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2005-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -54,7 +54,8 @@ static int eval_bool64_logic(ivl_expr_t expr) if (ivl_expr_signed(expr)) s_flag = "/s"; - fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n", s_flag, res, tmp.base, tmp.wid); + fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n", s_flag, res, + tmp.base, tmp.wid); clr_vector(tmp); return res; @@ -75,7 +76,7 @@ static int draw_number_bool64(ivl_expr_t expr) res = allocate_word(); low = val % UINT64_C(0x100000000); hig = val / UINT64_C(0x100000000); - fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", res, low, hig); + fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", res, low, hig); return res; } diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index 9a20299b4..4c96e9cce 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -750,7 +750,7 @@ static struct vector_info draw_binary_expr_lor(ivl_expr_t expr, unsigned wid, if (wid == 1) { if (lv.base >= 4 && lv.base < 8) { unsigned tmp = allocate_vector(1); - fprintf(vvp_out, " %%mov %u, %u, 1;\n", tmp, lv.base); + fprintf(vvp_out, " %%mov %u, %u, 1;\n", tmp, lv.base); lv.base = tmp; } return lv; @@ -932,7 +932,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, if (number_is_immediate(le,16,0) && !number_is_unknown(le)) { long imm = get_number_immediate(le); assert(imm >= 0); - fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, + fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, rv.base, imm, rv.wid); } else { lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ); @@ -948,7 +948,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, if (number_is_immediate(re,16,0) && !number_is_unknown(re)) { long imm = get_number_immediate(re); assert(imm >= 0); - fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, + fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, lv.base, imm, lv.wid); } else { rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ); @@ -964,7 +964,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, if (number_is_immediate(re,16,0) && !number_is_unknown(re)) { long imm = get_number_immediate(re); assert(imm >= 0); - fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, + fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, lv.base, imm, lv.wid); } else { rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ); @@ -979,7 +979,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr, if (number_is_immediate(le,16,0) && !number_is_unknown(le)) { long imm = get_number_immediate(le); assert(imm >= 0); - fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, + fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag, rv.base, imm, rv.wid); } else { lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ); @@ -1039,7 +1039,7 @@ static struct vector_info draw_logic_immediate(ivl_expr_t expr, switch (ivl_expr_opcode(expr)) { case '&': - fprintf(vvp_out, " %%andi %u, %lu, %u;\n", lv.base, imm, lv.wid); + fprintf(vvp_out, " %%andi %u, %lu, %u;\n", lv.base, imm, lv.wid); break; default: diff --git a/tgt-vvp/vvp.c b/tgt-vvp/vvp.c index 1317d2591..3277a7005 100644 --- a/tgt-vvp/vvp.c +++ b/tgt-vvp/vvp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -22,12 +22,13 @@ # include "vvp_priv.h" # include # include +# include # include # include static const char*version_string = "Icarus Verilog VVP Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" @@ -45,6 +46,7 @@ static const char*version_string = FILE*vvp_out = 0; int vvp_errors = 0; +unsigned show_file_line = 0; __inline__ static void draw_execute_header(ivl_design_t des) { @@ -95,8 +97,39 @@ int target_design(ivl_design_t des) unsigned size; unsigned idx; const char*path = ivl_design_flag(des, "-o"); + /* Use -pfileline to determine if file and line information is + * printed for procedural statements. (e.g. -pfileline=1). + * The default is no file/line information will be included. */ + const char*fileline = ivl_design_flag(des, "fileline"); + assert(path); + /* Check to see if file/line information should be included. */ + if (strcmp(fileline, "") != 0) { + char *eptr; + long fl_value = strtol(fileline, &eptr, 0); + /* Nothing usable in the file/line string. */ + if (fileline == eptr) { + fprintf(stderr, "vvp error: Unable to extract file/line " + "information from string: %s\n", fileline); + return 1; + } + /* Extra stuff at the end. */ + if (*eptr != 0) { + fprintf(stderr, "vvp error: Extra characters '%s' " + "included at end of file/line string: %s\n", + eptr, fileline); + return 1; + } + /* The file/line flag must be positive. */ + if (fl_value < 0) { + fprintf(stderr, "vvp error: File/line flag (%ld) must " + "be positive.\n", fl_value); + return 1; + } + show_file_line = fl_value > 0; + } + #ifdef HAVE_FOPEN64 vvp_out = fopen64(path, "w"); #else diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index aba52522b..b46c6492a 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -1,7 +1,7 @@ #ifndef __vvp_priv_H #define __vvp_priv_H /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -39,6 +39,12 @@ extern FILE* vvp_out; */ extern int vvp_errors; +/* + * Set to non-zero when the user wants to display file and line number + * information for procedural statements. + */ +extern unsigned show_file_line; + struct vector_info { unsigned base; unsigned wid; diff --git a/tgt-vvp/vvp_process.c b/tgt-vvp/vvp_process.c index dce4d6e31..9804c49e1 100644 --- a/tgt-vvp/vvp_process.c +++ b/tgt-vvp/vvp_process.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -111,13 +111,13 @@ static void set_to_lvariable(ivl_lval_t lval, unsigned skip_set = transient_id++; if (word_ix) { draw_eval_expr_into_integer(word_ix, 3); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); } else { - fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); + fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); } draw_eval_expr_into_integer(part_off_ex, 1); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); @@ -128,12 +128,12 @@ static void set_to_lvariable(ivl_lval_t lval, unsigned skip_set = transient_id++; if (word_ix) { draw_eval_expr_into_integer(word_ix, 3); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); } else { - fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); + fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); } - fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); if (word_ix) /* Only need this label if word_ix is set. */ fprintf(vvp_out, "t_%u ;\n", skip_set); @@ -155,9 +155,9 @@ static void set_to_lvariable(ivl_lval_t lval, unsigned skip_set = transient_id++; unsigned index_reg = 3; draw_eval_expr_into_integer(word_ix, index_reg); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); - fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); } @@ -171,9 +171,10 @@ static void set_to_lvariable(ivl_lval_t lval, directly to the word and save the index calculation. */ if (word_ix == 0) { if (use_word < ivl_signal_array_count(sig)) { - fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); - fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); + fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", + use_word); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); } else { fprintf(vvp_out, " ; %%set/v v%p_%lu, %u, %u " @@ -184,9 +185,9 @@ static void set_to_lvariable(ivl_lval_t lval, unsigned skip_set = transient_id++; unsigned index_reg = 3; draw_eval_expr_into_integer(word_ix, index_reg); - fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); - fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); - fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", + fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); + fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); + fprintf(vvp_out, " %%set/av v%p, %u, %u;\n", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); } @@ -535,6 +536,24 @@ static void set_vec_to_lval(ivl_statement_t net, struct vector_info res) } } +/* + * Routine to insert statement tracing information into the output stream + * when requested by the user (compiler). + */ +static void show_stmt_file_line(ivl_statement_t net, const char* desc) +{ + if (show_file_line) { + /* If the line number is not zero then the file should also + * be set. It's safe to skip the assert during debug, but + * the assert represents missing file/line information that + * should be reported/fixed. */ + unsigned lineno = ivl_stmt_lineno(net); + assert(lineno); + fprintf(vvp_out, " %%file_line %d %d \"%s\";\n", + ivl_file_table_index(ivl_stmt_file(net)), lineno, desc); + } +} + static int show_stmt_alloc(ivl_statement_t net) { ivl_scope_t scope = ivl_stmt_call(net); @@ -637,6 +656,8 @@ static int show_stmt_assign(ivl_statement_t net) ivl_lval_t lval; ivl_signal_t sig; + show_stmt_file_line(net, "Blocking assignment."); + lval = ivl_stmt_lval(net, 0); sig = ivl_lval_sig(lval); @@ -739,6 +760,8 @@ static int show_stmt_assign_nb(ivl_statement_t net) ivl_signal_t sig; unsigned nevents = ivl_stmt_nevent(net); + show_stmt_file_line(net, "Nonblocking assignment."); + /* If we have an event control build the control structure. */ if (nevents) { assert(del == 0); @@ -922,6 +945,8 @@ static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope) unsigned idx, default_case; + show_stmt_file_line(net, "Case statement."); + local_count += count + 1; /* First draw the branch table. All the non-default cases @@ -1039,8 +1064,9 @@ static int show_stmt_case_r(ivl_statement_t net, ivl_scope_t sscope) unsigned idx, default_case; - local_count += count + 1; + show_stmt_file_line(net, "Case statement."); + local_count += count + 1; /* First draw the branch table. All the non-default cases generate a branch out of here, to the code that implements @@ -1282,6 +1308,8 @@ static int show_stmt_cassign(ivl_statement_t net) ivl_expr_t rval; ivl_signal_t sig; + show_stmt_file_line(net, "Assign statement."); + rval = ivl_stmt_rval(net); assert(rval); @@ -1318,6 +1346,8 @@ static int show_stmt_deassign(ivl_statement_t net) ivl_signal_t sig = ivl_lval_sig(ivl_stmt_lval(net, 0)); unsigned lidx; + show_stmt_file_line(net, "Deassign statement."); + if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) { ivl_lval_t lval; @@ -1372,8 +1402,11 @@ static int show_stmt_condit(ivl_statement_t net, ivl_scope_t sscope) int rc = 0; unsigned lab_false, lab_out; ivl_expr_t expr = ivl_stmt_cond_expr(net); - struct vector_info cond - = draw_eval_expr(expr, STUFF_OK_XZ|STUFF_OK_47|STUFF_OK_RO); + struct vector_info cond; + + show_stmt_file_line(net, "If statement."); + + cond = draw_eval_expr(expr, STUFF_OK_XZ|STUFF_OK_47|STUFF_OK_RO); assert(cond.wid == 1); @@ -1426,6 +1459,8 @@ static int show_stmt_delay(ivl_statement_t net, ivl_scope_t sscope) unsigned long low = delay % UINT64_C(0x100000000); unsigned long hig = delay / UINT64_C(0x100000000); + show_stmt_file_line(net, "Delay statement."); + fprintf(vvp_out, " %%delay %lu, %lu;\n", low, hig); /* Lots of things can happen during a delay. */ clear_expression_lookaside(); @@ -1447,6 +1482,8 @@ static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) ivl_expr_t expr = ivl_stmt_delay_expr(net); ivl_statement_t stmt = ivl_stmt_sub_stmt(net); + show_stmt_file_line(net, "Delay statement."); + switch (ivl_expr_value(expr)) { case IVL_VT_BOOL: @@ -1480,8 +1517,10 @@ static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) static int show_stmt_disable(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; - ivl_scope_t target = ivl_stmt_call(net); + + show_stmt_file_line(net, "Disable statement."); + fprintf(vvp_out, " %%disable S_%p;\n", target); return rc; @@ -1492,6 +1531,8 @@ static int show_stmt_force(ivl_statement_t net) ivl_expr_t rval; ivl_signal_t sig; + show_stmt_file_line(net, "Force statement."); + rval = ivl_stmt_rval(net); assert(rval); @@ -1524,6 +1565,8 @@ static int show_stmt_forever(ivl_statement_t net, ivl_scope_t sscope) ivl_statement_t stmt = ivl_stmt_sub_stmt(net); unsigned lab_top = local_count++; + show_stmt_file_line(net, "Forever statement."); + fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_top); rc += show_statement(stmt, sscope); fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); @@ -1614,6 +1657,8 @@ static int show_stmt_release(ivl_statement_t net) ivl_signal_t sig = ivl_lval_sig(ivl_stmt_lval(net, 0)); unsigned lidx; + show_stmt_file_line(net, "Release statement."); + if (sig && ivl_signal_data_type(sig) == IVL_VT_REAL) { unsigned type = 0; ivl_lval_t lval; @@ -1682,9 +1727,13 @@ static int show_stmt_repeat(ivl_statement_t net, ivl_scope_t sscope) int rc = 0; unsigned lab_top = local_count++, lab_out = local_count++; ivl_expr_t expr = ivl_stmt_cond_expr(net); - struct vector_info cnt = draw_eval_expr(expr, 0); + struct vector_info cnt; const char *sign = ivl_expr_signed(expr) ? "s" : "u"; + show_stmt_file_line(net, "Repeat statement."); + + cnt = draw_eval_expr(expr, 0); + /* Test that 0 < expr */ fprintf(vvp_out, "T_%u.%u %%cmp/%s 0, %u, %u;\n", thread_count, lab_top, sign, cnt.base, cnt.wid); @@ -1712,6 +1761,9 @@ static int show_stmt_trigger(ivl_statement_t net) { ivl_event_t ev = ivl_stmt_events(net, 0); assert(ev); + + show_stmt_file_line(net, "Event trigger statement."); + fprintf(vvp_out, " %%set/v E_%p, 0,1;\n", ev); return 0; } @@ -1720,6 +1772,8 @@ static int show_stmt_utask(ivl_statement_t net) { ivl_scope_t task = ivl_stmt_call(net); + show_stmt_file_line(net, "User task call."); + fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(task))); fprintf(vvp_out, ", S_%p;\n", task); @@ -1730,6 +1784,8 @@ static int show_stmt_utask(ivl_statement_t net) static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) { + show_stmt_file_line(net, "Event wait (@) statement."); + if (ivl_stmt_nevent(net) == 1) { ivl_event_t ev = ivl_stmt_events(net, 0); fprintf(vvp_out, " %%wait E_%p;\n", ev); @@ -1793,6 +1849,8 @@ static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope) unsigned top_label = local_count++; unsigned out_label = local_count++; + show_stmt_file_line(net, "While statement."); + /* Start the loop. The top of the loop starts a basic block because it can be entered from above or from the bottom of the loop. */ @@ -1824,6 +1882,8 @@ static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope) static int show_system_task_call(ivl_statement_t net) { + show_stmt_file_line(net, "System task call."); + draw_vpi_task_call(net); /* VPI calls can manipulate anything, so clear the expression @@ -1833,6 +1893,361 @@ static int show_system_task_call(ivl_statement_t net) return 0; } +/* + * Icarus translated = into + * begin + * = ; + * = ; + * end + * This routine looks for this pattern so we only emit one %file_line opcode. + */ + +static unsigned is_delayed_or_event_assign(ivl_scope_t scope, + ivl_statement_t stmt) +{ + ivl_statement_t assign, delay, delayed_assign; + ivl_statement_type_t delay_type; + ivl_lval_t lval; + ivl_expr_t rval; + ivl_signal_t lsig, rsig; + + /* We must have two block elements. */ + if (ivl_stmt_block_count(stmt) != 2) return 0; + /* The first must be an assign. */ + assign = ivl_stmt_block_stmt(stmt, 0); + if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; + /* The second must be a delayx. */ + delay = ivl_stmt_block_stmt(stmt, 1); + delay_type = ivl_statement_type(delay); + if ((delay_type != IVL_ST_DELAYX) && + (delay_type != IVL_ST_WAIT)) return 0; + /* The statement for the delayx must be an assign. */ + delayed_assign = ivl_stmt_sub_stmt(delay); + if (ivl_statement_type(delayed_assign) != IVL_ST_ASSIGN) return 0; + /* The L-value must be a single signal. */ + if (ivl_stmt_lvals(assign) != 1) return 0; + lval = ivl_stmt_lval(assign, 0); + /* It must not have an array select. */ + if (ivl_lval_idx(lval)) return 0; + /* It must not have a non-zero base. */ + if (ivl_lval_part_off(lval)) return 0; + lsig = ivl_lval_sig(lval); + /* It must not be part of the signal. */ + if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0; + /* The R-value must be a single signal. */ + rval = ivl_stmt_rval(delayed_assign); + if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0; + /* It must not be an array word. */ + if (ivl_expr_oper1(rval)) return 0; + rsig = ivl_expr_signal(rval); + /* The two signals must be the same. */ + if (lsig != rsig) return 0; + /* And finally the three statements must have the same line number + * as the block. */ + if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || + (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delay)) || + (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delayed_assign))) { + return 0; + } + /* The pattern matched so this block represents a blocking + * assignment with an inter-assignment delay or event. */ + if (delay_type == IVL_ST_DELAYX) { + show_stmt_file_line(stmt, "Blocking assignment (delay)."); + } else { + show_stmt_file_line(stmt, "Blocking assignment (event)."); + } + return 1; +} + +/* + * Icarus translated = repeat() into + * begin + * = ; + * repeat() ; + * = ; + * end + * This routine looks for this pattern so we only emit one %file_line opcode. + */ +static unsigned is_repeat_event_assign(ivl_scope_t scope, + ivl_statement_t stmt) +{ + ivl_statement_t assign, event, event_assign, repeat; + ivl_lval_t lval; + ivl_expr_t rval; + ivl_signal_t lsig, rsig; + + /* We must have three block elements. */ + if (ivl_stmt_block_count(stmt) != 3) return 0; + /* The first must be an assign. */ + assign = ivl_stmt_block_stmt(stmt, 0); + if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; + /* The second must be a repeat with an event or an event. */ + repeat = ivl_stmt_block_stmt(stmt, 1); + if (ivl_statement_type(repeat) != IVL_ST_REPEAT) return 0; + /* The repeat must have an event statement. */ + event = ivl_stmt_sub_stmt(repeat); + if (ivl_statement_type(event) != IVL_ST_WAIT) return 0; + /* The third must be an assign. */ + event_assign = ivl_stmt_block_stmt(stmt, 2); + if (ivl_statement_type(event_assign) != IVL_ST_ASSIGN) return 0; + /* The L-value must be a single signal. */ + if (ivl_stmt_lvals(assign) != 1) return 0; + lval = ivl_stmt_lval(assign, 0); + /* It must not have an array select. */ + if (ivl_lval_idx(lval)) return 0; + /* It must not have a non-zero base. */ + if (ivl_lval_part_off(lval)) return 0; + lsig = ivl_lval_sig(lval); + /* It must not be part of the signal. */ + if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0; + /* The R-value must be a single signal. */ + rval = ivl_stmt_rval(event_assign); + if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0; + /* It must not be an array word. */ + if (ivl_expr_oper1(rval)) return 0; + rsig = ivl_expr_signal(rval); + /* The two signals must be the same. */ + if (lsig != rsig) return 0; + /* And finally the four statements must have the same line number + * as the block. */ + if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || + (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(repeat)) || + (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event)) || + (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event_assign))) { + return 0; + } + + /* The pattern matched so this block represents a blocking + * assignment with an inter-assignment repeat event. */ + show_stmt_file_line(stmt, "Blocking assignment (repeat event)."); + return 1; +} + +/* + * Icarus translated wait( into + * begin + * while ( !== 1'b1) @(); + * + * end + * This routine looks for this pattern and turns it back into a + * wait statement. + */ +static unsigned is_wait(ivl_scope_t scope, ivl_statement_t stmt) +{ + ivl_statement_t while_wait, wait, wait_stmt; + ivl_expr_t while_expr, expr; + const char *bits; + /* We must have two block elements. */ + if (ivl_stmt_block_count(stmt) != 2) return 0; + /* The first must be a while. */ + while_wait = ivl_stmt_block_stmt(stmt, 0); + if (ivl_statement_type(while_wait) != IVL_ST_WHILE) return 0; + /* That has a wait with a NOOP statement. */ + wait = ivl_stmt_sub_stmt(while_wait); + if (ivl_statement_type(wait) != IVL_ST_WAIT) return 0; + wait_stmt = ivl_stmt_sub_stmt(wait); + if (ivl_statement_type(wait_stmt) != IVL_ST_NOOP) return 0; + /* Check that the while condition has the correct form. */ + while_expr = ivl_stmt_cond_expr(while_wait); + if (ivl_expr_type(while_expr) != IVL_EX_BINARY) return 0; + if (ivl_expr_opcode(while_expr) != 'N') return 0; + /* Has a second operator that is a constant 1'b1. */ + expr = ivl_expr_oper2(while_expr); + if (ivl_expr_type(expr) != IVL_EX_NUMBER) return 0; + if (ivl_expr_width(expr) != 1) return 0; + bits = ivl_expr_bits(expr); + if (*bits != '1') return 0; + /* There is no easy way to verify that the @ sensitivity list + * matches the first expression so that is not currently checked. */ + /* And finally the two statements that represent the wait must + * have the same line number as the block. */ + if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_wait)) || + (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(wait))) { + return 0; + } + + /* The pattern matched so this block represents a wait statement. */ + show_stmt_file_line(stmt, "Wait statement."); + return 1; +} + +/* + * Check to see if the statement L-value is a port in the given scope. + * If it is return the zero based port number. + */ +static unsigned utask_in_port_idx(ivl_scope_t scope, ivl_statement_t stmt) +{ + unsigned idx, ports = ivl_scope_ports(scope); + ivl_lval_t lval = ivl_stmt_lval(stmt, 0); + ivl_signal_t lsig = ivl_lval_sig(lval); + const char *sig_name; + /* The L-value must be a single signal. */ + if (ivl_stmt_lvals(stmt) != 1) return ports; + /* It must not have an array select. */ + if (ivl_lval_idx(lval)) return ports; + /* It must not have a non-zero base. */ + if (ivl_lval_part_off(lval)) return ports; + /* It must not be part of the signal. */ + if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return ports; + /* It must have the same scope as the task. */ + if (scope != ivl_signal_scope(lsig)) return ports; + /* It must be an input or inout port of the task. */ + sig_name = ivl_signal_basename(lsig); + for (idx = 0; idx < ports; idx += 1) { + ivl_signal_t port = ivl_scope_port(scope, idx); + ivl_signal_port_t port_type = ivl_signal_port(port); + if ((port_type != IVL_SIP_INPUT) && + (port_type != IVL_SIP_INOUT)) continue; + if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break; + } + return idx; +} + +/* + * Check to see if the statement R-value is a port in the given scope. + * If it is return the zero based port number. + */ +static unsigned utask_out_port_idx(ivl_scope_t scope, ivl_statement_t stmt) +{ + unsigned idx, ports = ivl_scope_ports(scope); + ivl_expr_t rval = ivl_stmt_rval(stmt); + ivl_signal_t rsig = 0; + ivl_expr_type_t expr_type = ivl_expr_type(rval); + const char *sig_name; + /* We can have a simple signal. */ + if (expr_type == IVL_EX_SIGNAL) { + rsig = ivl_expr_signal(rval); + /* Or a simple select of a simple signal. */ + } else if (expr_type == IVL_EX_SELECT) { + ivl_expr_t expr = ivl_expr_oper1(rval); + /* We must have a zero select base. */ + if (ivl_expr_oper2(rval)) return ports; + /* We must be selecting a signal. */ + if (ivl_expr_type(expr) != IVL_EX_SIGNAL) return ports; + rsig = ivl_expr_signal(expr); + } else return ports; + /* The R-value must have the same scope as the task. */ + if (scope != ivl_signal_scope(rsig)) return ports; + /* It must not be an array element. */ + if (ivl_signal_dimensions(rsig)) return ports; + /* It must be an output or inout port of the task. */ + sig_name = ivl_signal_basename(rsig); + for (idx = 0; idx < ports; idx += 1) { + ivl_signal_t port = ivl_scope_port(scope, idx); + ivl_signal_port_t port_type = ivl_signal_port(port); + if ((port_type != IVL_SIP_OUTPUT) && + (port_type != IVL_SIP_INOUT)) continue; + if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break; + } + return idx; +} + +/* + * Structure to hold the port information as we extract it from the block. + */ +typedef struct port_expr_s { + ivl_signal_port_t type; + union { + ivl_statement_t lval; + ivl_expr_t rval; + }; +} *port_expr_t; + +/* + * Icarus encodes a user task call with arguments as: + * begin + * = + * ... + * = + * + * = + * ... + * = + * end + * This routine looks for that pattern and translates it into the + * appropriate task call. It returns true (1) if it successfully + * translated the block to a task call, otherwise it returns false + * (0) to indicate the block needs to be emitted. + */ +static unsigned is_utask_call_with_args(ivl_scope_t scope, + ivl_statement_t stmt) +{ + unsigned idx, ports, task_idx = 0; + unsigned count = ivl_stmt_block_count(stmt); + unsigned lineno = ivl_stmt_lineno(stmt); + ivl_scope_t task_scope = 0; + port_expr_t port_exprs; + /* Check to see if the block is of the basic form first. */ + for (idx = 0; idx < count; idx += 1) { + ivl_statement_t tmp = ivl_stmt_block_stmt(stmt, idx); + if (ivl_statement_type(tmp) == IVL_ST_ASSIGN) continue; + if (ivl_statement_type(tmp) == IVL_ST_UTASK && !task_scope) { + task_idx = idx; + task_scope = ivl_stmt_call(tmp); + assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); + continue; + } + return 0; + } + /* If there is no task call or it takes no argument then return. */ + if (!task_scope) return 0; + ports = ivl_scope_ports(task_scope); + if (ports == 0) return 0; + + /* Allocate space to save the port information and initialize it. */ + port_exprs = (port_expr_t) malloc(sizeof(struct port_expr_s)*ports); + for (idx = 0; idx < ports; idx += 1) { + port_exprs[idx].type = IVL_SIP_NONE; + port_exprs[idx].rval = 0; + } + /* Check that the input arguments are correct. */ + for (idx = 0; idx < task_idx; idx += 1) { + ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); + unsigned port = utask_in_port_idx(task_scope, assign); + if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { + free(port_exprs); + return 0; + } + port_exprs[port].type = IVL_SIP_INPUT; + port_exprs[port].rval = ivl_stmt_rval(assign); + } + /* Check that the output arguments are correct. */ + for (idx = task_idx + 1; idx < count; idx += 1) { + ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); + unsigned port = utask_out_port_idx(task_scope, assign); + if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { + free(port_exprs); + return 0; + } + if (port_exprs[port].type == IVL_SIP_INPUT) { + /* We probably should verify that the current R-value + * matches the new L-value. */ + port_exprs[port].type = IVL_SIP_INOUT; + } else { + port_exprs[port].type = IVL_SIP_OUTPUT; + } + port_exprs[port].lval = assign; + } + /* Check that the task call has the correct line number. */ + if (lineno != ivl_stmt_lineno(ivl_stmt_block_stmt(stmt, task_idx))) { + free(port_exprs); + return 0; + } + + /* Verify that all the ports were defined. */ + for (idx = 0; idx < ports; idx += 1) { + if (port_exprs[idx].type == IVL_SIP_NONE) { + free(port_exprs); + return 0; + } + } + + /* The pattern matched so this block represents a call to a user + * defined task with arguments. */ + show_stmt_file_line(stmt, "User task call (with arguments)."); + return 1; +} + /* * This function draws a statement as vvp assembly. It basically * switches on the statement type and draws code based on the type and @@ -1842,6 +2257,7 @@ static int show_statement(ivl_statement_t net, ivl_scope_t sscope) { const ivl_statement_type_t code = ivl_statement_type(net); int rc = 0; + unsigned saved_file_line = 0; switch (code) { @@ -1860,8 +2276,21 @@ static int show_statement(ivl_statement_t net, ivl_scope_t sscope) case IVL_ST_BLOCK: if (ivl_stmt_block_scope(net)) rc += show_stmt_block_named(net, sscope); - else + else { + /* This block could really represent a single statement. + * If so only emit a single %file_line opcode. */ + if (show_file_line) { + if (is_delayed_or_event_assign(sscope, net) || + is_repeat_event_assign(sscope, net) || + is_wait(sscope, net) || + is_utask_call_with_args(sscope, net)) { + saved_file_line = show_file_line; + show_file_line = 0; + } + } rc += show_stmt_block(net, sscope); + if (saved_file_line) show_file_line = 1; + } break; case IVL_ST_CASE: diff --git a/vpi_user.h b/vpi_user.h index e365c029d..e0d25de96 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -1,7 +1,7 @@ #ifndef __vpi_user_H #define __vpi_user_H /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -373,6 +373,8 @@ typedef struct t_vpi_delay { # define _vpiDelaySelMinimum 1 # define _vpiDelaySelTypical 2 # define _vpiDelaySelMaximum 3 +/* used in vvp/vpi_priv.h 0x1000003 */ +/* used in vvp/vpi_priv.h 0x1000004 */ /* DELAY MODES */ #define vpiNoDelay 1 diff --git a/vvp/Makefile.in b/vvp/Makefile.in index d959f14c3..604f4ce3a 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -67,8 +67,8 @@ V = vpi_modules.o vpi_callback.o vpi_const.o vpi_event.o vpi_iter.o vpi_mcd.o \ vpip_to_dec.o vpip_format.o vvp_vpi.o O = main.o parse.o parse_misc.o lexor.o arith.o array.o bufif.o compile.o \ - concat.o dff.o enum_type.o extend.o npmos.o part.o permaheap.o \ - reduce.o resolv.o \ + concat.o dff.o enum_type.o extend.o file_line.o npmos.o part.o \ + permaheap.o reduce.o resolv.o \ sfunc.o stop.o symbols.o ufunc.o codes.o vthread.o schedule.o \ statistics.o tables.o udp.o vvp_island.o vvp_net.o vvp_net_sig.o \ event.o logic.o delay.o words.o island_tran.o $V diff --git a/vvp/codes.cc b/vvp/codes.cc index 22453127b..d1b4650f8 100644 --- a/vvp/codes.cc +++ b/vvp/codes.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -109,6 +109,8 @@ void codespace_delete(void) vpi_call_delete((cur+idx)->handle); } else if ((cur+idx)->opcode == &of_EXEC_UFUNC) { exec_ufunc_delete((cur+idx)); + } else if ((cur+idx)->opcode == &of_FILE_LINE) { + delete((cur+idx)->handle); } if (count_opcodes == 0) break; } diff --git a/vvp/codes.h b/vvp/codes.h index 32dc25e66..68769b496 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -1,7 +1,7 @@ #ifndef __codes_H #define __codes_H /* - * Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -94,6 +94,7 @@ extern bool of_EVCTL(vthread_t thr, vvp_code_t code); extern bool of_EVCTLC(vthread_t thr, vvp_code_t code); extern bool of_EVCTLI(vthread_t thr, vvp_code_t code); extern bool of_EVCTLS(vthread_t thr, vvp_code_t code); +extern bool of_FILE_LINE(vthread_t thr, vvp_code_t code); extern bool of_FORCE_LINK(vthread_t thr, vvp_code_t code); extern bool of_FORCE_V(vthread_t thr, vvp_code_t code); extern bool of_FORCE_WR(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index e7641f47b..f742a50a5 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -1709,6 +1709,23 @@ void compile_fork(char*label, struct symb_s dest, struct symb_s scope) compile_vpi_lookup(&code->handle, scope.text); } +void compile_file_line(char*label, long file_idx, long lineno, + char*description) +{ + if (label) compile_codelabel(label); + + /* Create an instruction in the code space. */ + vvp_code_t code = codespace_allocate(); + code->opcode = &of_FILE_LINE; + + /* Create a vpiHandle that contains the information. */ + code->handle = vpip_build_file_line(description, file_idx, lineno); + assert(code->handle); + + /* Done with the lexor-allocated name string. */ + delete[] description; +} + void compile_vpi_call(char*label, char*name, bool func_as_task_err, bool func_as_task_warn, long file_idx, long lineno, diff --git a/vvp/compile.h b/vvp/compile.h index 442a696b0..17d21547c 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -1,7 +1,7 @@ #ifndef __compile_H #define __compile_H /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -404,6 +404,9 @@ typedef struct comp_operands_s*comp_operands_t; extern void compile_code(char*label, char*mnem, comp_operands_t opa); extern void compile_disable(char*label, struct symb_s symb); +extern void compile_file_line(char*label, long file_idx, long lineno, + char*description); + extern void compile_vpi_call(char*label, char*name, bool func_as_task_err, bool func_as_task_warn, long file_idx, long lineno, diff --git a/vvp/lexor.lex b/vvp/lexor.lex index b043cf488..5fe395dea 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -204,13 +204,14 @@ static char* strdupnew(char const *str) kind of instruction this really is. The few exceptions (that have exceptional parameter requirements) are listed first. */ -"%vpi_call" { return K_vpi_call; } +"%vpi_call" { return K_vpi_call; } "%vpi_call/w" { return K_vpi_call_w; } "%vpi_call/i" { return K_vpi_call_i; } -"%vpi_func" { return K_vpi_func; } +"%vpi_func" { return K_vpi_func; } "%vpi_func/r" { return K_vpi_func_r; } -"%disable" { return K_disable; } -"%fork" { return K_fork; } +"%disable" { return K_disable; } +"%fork" { return K_fork; } +"%file_line" { return K_file_line; } /* Handle the specialized variable access functions. */ diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index fc3b62106..c51f3092a 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * */ @@ -385,6 +385,18 @@ that this information has been cleared. You can get an assert if this information is not managed correctly. +* %file_line + +This command emits the provided file and line information along with +the description when it is executed. The output is sent to stderr and +the format of the output is: + :: + is the unsigned numeric file index. + is the unsigned line number. + is a string, if string is 0 then the following default +message is used: "Procedural tracing.". + + * %force/v