From 21d15ceece7ee6f6d54d1030f5b117a46f8e8165 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 18 Jun 2010 16:03:17 -0700 Subject: [PATCH] Fix the compiler and modpath scaling of real delays. A real delay must be scaled and rounded using the local precision before it is finally scaled to the simulation time units. This patch fixes the compiler to do this correctly or generate the correct code for run time calculated delays. Delays in a CA already worked correctly. The run time was also fixed to scale modpath (SDF back annotation) delays correctly. --- PDelays.cc | 17 +++------ dup_expr.cc | 8 ++++ elaborate.cc | 89 ++++++++++++++++++++++++++------------------- expr_synth.cc | 24 +++++++++++- netlist.cc | 26 +++++++++++++ netlist.h | 13 +++++++ netmisc.cc | 16 ++++++++ netmisc.h | 10 ++++- set_width.cc | 4 +- tgt-vvp/eval_expr.c | 25 +++++++++++-- tgt-vvp/eval_real.c | 33 +++++++++++++---- vpi/sys_sdf.c | 4 -- vvp/delay.cc | 7 +++- vvp/vpi_time.cc | 29 +++++++++++---- 14 files changed, 230 insertions(+), 75 deletions(-) diff --git a/PDelays.cc b/PDelays.cc index efbc36861..9ce84eb0f 100644 --- a/PDelays.cc +++ b/PDelays.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2009 Stephen Williams (steve@icarus.com) + * 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 @@ -92,15 +92,10 @@ static NetExpr*calculate_val(Design*des, NetScope*scope, PExpr*expr) units, and return an adjusted value. */ if (NetECReal*tmp = dynamic_cast(dex)) { - verireal fn = tmp->value(); - - int shift = scope->time_unit() - des->get_precision(); - int64_t delay = fn.as_long64(shift); - if (delay < 0) - delay = 0; + uint64_t delay = get_scaled_time_from_real(des, scope, tmp); delete tmp; - NetEConst*tmp2 = new NetEConst(verinum(delay)); + NetEConst*tmp2 = new NetEConst(verinum(delay, 64)); tmp2->set_line(*expr); return tmp2; } @@ -108,11 +103,10 @@ static NetExpr*calculate_val(Design*des, NetScope*scope, PExpr*expr) if (NetEConst*tmp = dynamic_cast(dex)) { verinum fn = tmp->value(); - uint64_t delay = des->scale_to_precision(fn.as_ulong64(), scope); delete tmp; - NetEConst*tmp2 = new NetEConst(verinum(delay)); + NetEConst*tmp2 = new NetEConst(verinum(delay, 64)); tmp2->set_line(*expr); return tmp2; } @@ -132,7 +126,8 @@ static NetExpr* make_delay_nets(Design*des, NetScope*scope, NetExpr*expr) NetNet*sig = expr->synthesize(des, scope, expr); if (sig == 0) { cerr << expr->get_fileline() << ": error: Expression " << *expr - << " is not suitable for delay expression." << endl; + << " is not suitable as a delay expression." << endl; + des->errors += 1; return 0; } diff --git a/dup_expr.cc b/dup_expr.cc index 49eef2dfd..97720703d 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -161,3 +161,11 @@ NetEUReduce* NetEUReduce::dup_expr() const tmp->set_line(*this); return tmp; } + +NetECast* NetECast::dup_expr() const +{ + NetECast*tmp = new NetECast(op_, expr_->dup_expr()); + assert(tmp); + tmp->set_line(*this); + return tmp; +} diff --git a/elaborate.cc b/elaborate.cc index f78b45e2c..0b66ed65a 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -1960,55 +1960,68 @@ static NetExpr*elaborate_delay_expr(PExpr*expr, Design*des, NetScope*scope) units, and return an adjusted NetEConst. */ if (NetECReal*tmp = dynamic_cast(dex)) { - verireal fn = tmp->value(); - - int shift = scope->time_unit() - des->get_precision(); - int64_t delay = fn.as_long64(shift); - if (delay < 0) - delay = 0; + uint64_t delay = get_scaled_time_from_real(des, scope, tmp); delete tmp; - return new NetEConst(verinum(delay)); + NetEConst*tmp2 = new NetEConst(verinum(delay, 64)); + tmp2->set_line(*expr); + return tmp2; } if (NetEConst*tmp = dynamic_cast(dex)) { verinum fn = tmp->value(); - - uint64_t delay = - des->scale_to_precision(fn.as_ulong64(), scope); + uint64_t delay = des->scale_to_precision(fn.as_ulong64(), scope); delete tmp; - return new NetEConst(verinum(delay)); + NetEConst*tmp2 = new NetEConst(verinum(delay, 64)); + tmp2->set_line(*expr); + return tmp2; } /* The expression is not constant, so generate an expanded expression that includes the necessary scale shifts, and return that expression. */ - int shift = scope->time_unit() - des->get_precision(); - if (shift > 0) { - uint64_t scale = 1; - while (shift > 0) { - scale *= 10; - shift -= 1; - } + ivl_assert(*expr, dex); + if (dex->expr_type() == IVL_VT_REAL) { + // Scale the real value. + int shift = scope->time_unit() - scope->time_precision(); + assert(shift >= 0); + double round = 1; + for (int lp = 0; lp < shift; lp += 1) round *= 10.0; - ivl_assert(*expr, dex); - NetExpr*scal_val = new NetEConst(verinum(scale)); + NetExpr*scal_val = new NetECReal(verireal(round)); + scal_val->set_line(*expr); dex = new NetEBMult('*', dex, scal_val); - } + dex->set_line(*expr); - if (shift < 0) { - unsigned long scale = 1; - while (shift < 0) { - scale *= 10; - shift += 1; - } + // Cast this part of the expression to an integer. + dex = new NetECast('i', dex); + dex->set_width(64); + dex->set_line(*expr); - ivl_assert(*expr, dex); - NetExpr*scal_val = new NetEConst(verinum(scale)); - dex = new NetEBDiv('/', dex, scal_val); + // Now scale the integer value. + shift = scope->time_precision() - des->get_precision(); + assert(shift >= 0); + uint64_t scale = 1; + for (int lp = 0; lp < shift; lp += 1) scale *= 10; + + scal_val = new NetEConst(verinum(scale, 64)); + scal_val->set_line(*expr); + dex = new NetEBMult('*', dex, scal_val); + dex->set_width(64); + dex->set_line(*expr); + } else { + int shift = scope->time_unit() - des->get_precision(); + assert(shift >= 0); + uint64_t scale = 1; + for (int lp = 0; lp < shift; lp += 1) scale *= 10; + + NetExpr*scal_val = new NetEConst(verinum(scale, 64)); + scal_val->set_line(*expr); + dex = new NetEBMult('*', dex, scal_val); + dex->set_line(*expr); } return dex; @@ -3901,7 +3914,6 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const "module(s) with no `timescale." << endl; display_ts_dly_warning = false; } - int shift = scope->time_unit() - des->get_precision(); /* Elaborate the delay values themselves. Remember to scale them for the timescale/precision of the scope. */ @@ -3910,17 +3922,18 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const probe_expr_width(des, scope, exp); NetExpr*cur = elab_and_eval(des, scope, exp, 0); - if (NetEConst*cur_con = dynamic_cast (cur)) { - delay_value[idx] = cur_con->value().as_ulong(); - for (int tmp = 0 ; tmp < shift ; tmp += 1) - delay_value[idx] *= 10; + if (NetEConst*con = dynamic_cast (cur)) { + verinum fn = con->value(); + delay_value[idx] = des->scale_to_precision(fn.as_ulong64(), + scope); - } else if (NetECReal*cur_rcon = dynamic_cast(cur)) { - delay_value[idx] = cur_rcon->value().as_long(shift); + } else if (NetECReal*rcon = dynamic_cast(cur)) { + delay_value[idx] = get_scaled_time_from_real(des, scope, + rcon); } else { cerr << get_fileline() << ": error: Path delay value " - << "must be constant." << endl; + << "must be constant (" << *cur << ")." << endl; delay_value[idx] = 0; des->errors += 1; } diff --git a/expr_synth.cc b/expr_synth.cc index 175a59072..e58c2b30d 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2009 Stephen Williams (steve@icarus.com) + * 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 @@ -977,6 +977,28 @@ NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope, NetExpr*root) return osig; } +NetNet* NetECast::synthesize(Design*des, NetScope*scope, NetExpr*root) +{ + NetNet*isig = expr_->synthesize(des, scope, root); + + if (isig == 0) return 0; + + switch (op()) { + case 'i': + isig = cast_to_int(des, scope, isig, isig->vector_width()); + break; + case 'r': + isig = cast_to_real(des, scope, isig); + break; + default: + cerr << get_fileline() << ": internal error: " + << "Unable to synthesize " << *this << "." << endl; + return 0; + } + + return isig; +} + /* * Turn a part/bit select expression into gates. * We know some things about the expression that elaboration enforces diff --git a/netlist.cc b/netlist.cc index 11bccce1b..2e8de81da 100644 --- a/netlist.cc +++ b/netlist.cc @@ -2430,6 +2430,32 @@ ivl_variable_type_t NetEUReduce::expr_type() const return expr_->expr_type(); } +NetECast::NetECast(char op__, NetExpr*ex) +: NetEUnary(op__, ex) +{ +} + +NetECast::~NetECast() +{ +} + +ivl_variable_type_t NetECast::expr_type() const +{ + ivl_variable_type_t ret; + switch (op_) { + case 'i': + ret = IVL_VT_LOGIC; + break; + case 'r': + ret = IVL_VT_REAL; + break; + default: + assert(0); + } + + return ret; +} + NetLogic::NetLogic(NetScope*s, perm_string n, unsigned pins, TYPE t, unsigned wid) : NetNode(s, n, pins), type_(t), width_(wid) diff --git a/netlist.h b/netlist.h index c779a6c84..2619d7316 100644 --- a/netlist.h +++ b/netlist.h @@ -3750,6 +3750,8 @@ class NetETernary : public NetExpr { * N -- Reduction NOR (~|) * X -- Reduction NXOR (~^ or ^~) * m -- abs(x) (i.e. "magnitude") + * i -- Cast from real to integer (vector) + * r -- Cast from integer (vector) to real */ class NetEUnary : public NetExpr { @@ -3805,6 +3807,17 @@ class NetEUReduce : public NetEUnary { virtual ivl_variable_type_t expr_type() const; }; +class NetECast : public NetEUnary { + + public: + NetECast(char op, NetExpr*ex); + ~NetECast(); + + virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); + virtual NetECast* dup_expr() const; + virtual ivl_variable_type_t expr_type() const; +}; + /* * When a signal shows up in an expression, this type represents * it. From this the expression can get any kind of access to the diff --git a/netmisc.cc b/netmisc.cc index 39017dca6..f2644cc56 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -494,3 +494,19 @@ const_bool const_logical(const NetExpr*expr) return C_NON; } + +uint64_t get_scaled_time_from_real(Design*des, NetScope*scope, NetECReal*val) +{ + verireal fn = val->value(); + + int shift = scope->time_unit() - scope->time_precision(); + assert(shift >= 0); + int64_t delay = fn.as_long64(shift); + + + shift = scope->time_precision() - des->get_precision(); + assert(shift >= 0); + for (int lp = 0; lp < shift; lp += 1) delay *= 10; + + return delay; +} diff --git a/netmisc.h b/netmisc.h index 5617a8255..31d97def7 100644 --- a/netmisc.h +++ b/netmisc.h @@ -1,7 +1,7 @@ #ifndef __netmisc_H #define __netmisc_H /* - * Copyright (c) 1999-2009 Stephen Williams (steve@icarus.com) + * 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 @@ -235,4 +235,12 @@ extern bool dly_used_no_timescale; extern bool dly_used_timescale; extern bool display_ts_dly_warning; +/* + * When scaling a real value to a time we need to do some standard + * processing. + */ +extern uint64_t get_scaled_time_from_real(Design*des, + NetScope*scope, + NetECReal*val); + #endif diff --git a/set_width.cc b/set_width.cc index 2e013d438..cdd75b094 100644 --- a/set_width.cc +++ b/set_width.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2008 Stephen Williams (steve@icarus.com) + * 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 @@ -474,6 +474,8 @@ bool NetEUnary::set_width(unsigned w, bool) switch (op_) { case '~': case '-': + case 'r': + case 'i': flag = expr_->set_width(w); expr_width(w); break; diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index ef64879b6..4cdf04642 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -286,8 +286,8 @@ void draw_eval_expr_into_integer(ivl_expr_t expr, unsigned ix) case IVL_VT_REAL: word = draw_eval_real(expr); - clr_word(word); fprintf(vvp_out, " %%cvt/sr %u, %u;\n", ix, word); + clr_word(word); break; default: @@ -2933,7 +2933,7 @@ static struct vector_info draw_unary_expr(ivl_expr_t expr, unsigned wid) struct vector_info res; ivl_expr_t sub = ivl_expr_oper1(expr); const char *rop = 0; - int inv = 0; + int word, inv = 0; switch (ivl_expr_opcode(expr)) { case '&': rop = "and"; break; @@ -3117,15 +3117,32 @@ static struct vector_info draw_unary_expr(ivl_expr_t expr, unsigned wid) } fprintf(vvp_out, " %%cmpi/s %d, 0, %u;\n", res.base, res.wid); - fprintf(vvp_out, " %%jmp/0xz T_%u.%u, 5;\n", thread_count, local_count); + fprintf(vvp_out, " %%jmp/0xz T_%u.%u, 5;\n", thread_count, + local_count); fprintf(vvp_out, " %%inv %d, %u;\n", res.base, res.wid); fprintf(vvp_out, " %%addi %d, 1, %u;\n", res.base, res.wid); fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_count); local_count += 1; break; + case 'i': /* Cast a real value to an integer. */ + assert(ivl_expr_value(sub) == IVL_VT_REAL); + word = draw_eval_real(sub); + res.base = allocate_vector(wid); + res.wid = wid; + fprintf(vvp_out, " %%cvt/vr %u, %u, %u;\n", res.base, word, + res.wid); + clr_word(word); + break; + + case 'r': /* Handled in eval_real.c. */ + fprintf(stderr, "vvp error: integer -> real cast in integer " + "context.\n"); + assert(0); + break; + default: - fprintf(stderr, "vvp error: unhandled unary: %c\n", + fprintf(stderr, "vvp error: unhandled unary operator: %c\n", ivl_expr_opcode(expr)); assert(0); } diff --git a/tgt-vvp/eval_real.c b/tgt-vvp/eval_real.c index a946de4e2..e10eeb85b 100644 --- a/tgt-vvp/eval_real.c +++ b/tgt-vvp/eval_real.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-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 @@ -139,6 +139,7 @@ static int draw_binary_real(ivl_expr_t expr) fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_out); break; } + default: fprintf(stderr, "XXXX draw_binary_real(%c)\n", ivl_expr_opcode(expr)); @@ -444,6 +445,20 @@ static int draw_unary_real(ivl_expr_t expr) } sube = ivl_expr_oper1(expr); + + if (ivl_expr_opcode(expr) == 'r') { /* Cast an integer value to a real. */ + struct vector_info res; + char *suffix = ""; + assert(ivl_expr_value(sube) != IVL_VT_REAL); + res = draw_eval_expr(sube, 1); + if (ivl_expr_signed(sube)) suffix = "/s"; + sub = allocate_word(); + fprintf(vvp_out, " %%cvt/rv%s %d, %u, %u;\n", suffix, sub, + res.base, res.wid); + clr_vector(res); + return sub; + } + sub = draw_eval_real(sube); if (ivl_expr_opcode(expr) == '+') @@ -458,16 +473,20 @@ static int draw_unary_real(ivl_expr_t expr) return res; } - if (ivl_expr_opcode(expr) == 'm') { /* abs(sube) */ + if (ivl_expr_opcode(expr) == 'm') { /* abs() */ fprintf(vvp_out, " %%abs/wr %d, %d;\n", sub, sub); return sub; } - fprintf(vvp_out, "; XXXX unary (%c) on sube in %d\n", - ivl_expr_opcode(expr), sub); - fprintf(stderr, "XXXX evaluate unary (%c) on sube in %d\n", - ivl_expr_opcode(expr), sub); - return 0; + if (ivl_expr_opcode(expr) == 'i') { /* Handled in eval_expr.c. */ + fprintf(stderr, "vvp error: real -> integer cast in real " + "context.\n"); + assert(0); + } + + fprintf(stderr, "vvp error: unhandled real unary operator: %c.\n", + ivl_expr_opcode(expr)); + assert(0); } int draw_eval_real(ivl_expr_t expr) diff --git a/vpi/sys_sdf.c b/vpi/sys_sdf.c index 5990b9000..048c16046 100644 --- a/vpi/sys_sdf.c +++ b/vpi/sys_sdf.c @@ -192,10 +192,6 @@ void sdf_iopath_delays(int vpi_edge, const char*src, const char*dst, delay_vals[idx].type = vpiScaledRealTime; if (delval_list->val[idx].defined) { delay_vals[idx].real = delval_list->val[idx].value; - /* Simulation cannot support negative delays. */ - if (delay_vals[idx].real < 0.0) { - delay_vals[idx].real = 0.0; - } } } diff --git a/vvp/delay.cc b/vvp/delay.cc index 0a85a82dd..32950e80d 100644 --- a/vvp/delay.cc +++ b/vvp/delay.cc @@ -781,7 +781,7 @@ static int modpath_src_free_object( vpiHandle ref ) /* * This routine will put specific dimension of delay[] values - * into a vpiHandle. In this case, he will put + * into a vpiHandle. In this case, we will put * specific delays values in a vpiModPathIn object * */ @@ -817,6 +817,11 @@ static void modpath_src_put_delays (vpiHandle ref, p_vpi_delay delays) tmp[idx] = vpip_timestruct_to_time(delays->da+use_map[0][idx]); } } else { + // You cannot create a modpath with a negative delay so set it + // to zero per 1364-2005 section 14.3.1. + for (idx = 0 ; idx < delays->no_of_delays ; idx += 1) { + if (delays->da[idx].real < 0.0) delays->da[idx].real = 0.0; + } for (idx = 0 ; idx < 12 ; idx += 1) { tmp[idx] = vpip_scaled_real_to_time64(delays->da[use_map[0][idx]].real, src->dest->scope); diff --git a/vvp/vpi_time.cc b/vvp/vpi_time.cc index 43a4e6e00..12f965804 100644 --- a/vvp/vpi_time.cc +++ b/vvp/vpi_time.cc @@ -67,18 +67,33 @@ double vpip_time_to_scaled_real(vvp_time64_t ti, struct __vpiScope*scope) return val; } +/* + * This routine does not currently support negative real delays and it + * does not check for overflow. It is only used for modpath delays and + * they are required to be non-negative. + */ vvp_time64_t vpip_scaled_real_to_time64(double val, struct __vpiScope*scope) { - int units; - if (scope) - units = scope->time_units; - else - units = vpi_time_precision; + int shift = 0; + if (scope) shift = scope->time_units - scope->time_precision; + assert(shift >= 0); - double scale = pow(10.0L, units - vpi_time_precision); + assert(val >= 0); + + // Scale to the local precision and then round away from zero. + double scale = pow(10.0L, shift); val *= scale; - return (vvp_time64_t) val; + vvp_time64_t delay = (vvp_time64_t) (val + 0.5); + + // If needed now scale the value to the simulator precision. + if (scope) { + shift = scope->time_precision - vpi_time_precision; + assert(shift >= 0); + for (int lp = 0; lp < shift; lp += 1) delay *= 10; + } + + return delay; } static int timevar_time_get(int code, vpiHandle ref)