From 890589191cfbb25f722982da603245397c2b2d95 Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 14 Feb 2013 18:55:34 -0800 Subject: [PATCH 1/8] When duplicating a select keep the sign information Make the duplicate select signed if the original was signed. --- dup_expr.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dup_expr.cc b/dup_expr.cc index e3d9cd552..084e433c2 100644 --- a/dup_expr.cc +++ b/dup_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2013 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 @@ -207,6 +207,7 @@ NetESelect* NetESelect::dup_expr() const base_? base_->dup_expr() : 0, expr_width(), sel_type_); ivl_assert(*this, tmp); + tmp->cast_signed(has_sign()); tmp->set_line(*this); return tmp; } From 8808cd9514355ca1b7e25b8b0fe270321c22bffb Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 15 Feb 2013 11:49:10 -0800 Subject: [PATCH 2/8] For a LPM constant pass the sign information and one other fix For a LPM constant we need to pass the sign information so that the vlog95 code generator can create the correct constant. Also when using the sub_net_from() routine the numeric constant should have the same sign as the signal so that it can also be displayed correctly. --- netmisc.cc | 1 + t-dll.cc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/netmisc.cc b/netmisc.cc index 783c9a506..cbd31f4c3 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -49,6 +49,7 @@ NetNet* sub_net_from(Design*des, NetScope*scope, long val, NetNet*sig) } else { verinum zero ((int64_t)val); zero = pad_to_width(zero, sig->vector_width()); + zero.has_sign(sig->get_signed()); NetConst*zero_obj = new NetConst(scope, scope->local_symbol(), zero); zero_obj->set_line(*sig); des->add_node(zero_obj); diff --git a/t-dll.cc b/t-dll.cc index f42d8bf30..aefdd3601 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2013 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 @@ -2166,7 +2166,7 @@ bool dll_target::net_const(const NetConst*net) assert(net->pin_count() == 1); obj->width_ = net->width(); - obj->signed_ = 0; + obj->signed_ = net->value().has_sign(); if (obj->width_ <= sizeof(obj->b.bit_)) { bits = obj->b.bit_; From 51112131ad84b3a10f43b88b2cabb172537f2d34 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 15 Feb 2013 19:21:11 -0800 Subject: [PATCH 3/8] vlog95: Emit if using signed and add the number of errors to the output In the starting comment say if the conversion was done with signed support. If there were errors then add a comment at the end of the file that says how many errors occurred and add a line after this that prevents the file from running with out an error. This requires the user to see that errors occurred and remove the offending line if they want to use the file anyway. --- tgt-vlog95/vlog95.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/tgt-vlog95/vlog95.c b/tgt-vlog95/vlog95.c index be00636f5..dcd893570 100644 --- a/tgt-vlog95/vlog95.c +++ b/tgt-vlog95/vlog95.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-2013 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,7 +30,7 @@ static const char*version_string = "Icarus Verilog VLOG95 Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (C) 2010-2012 Cary R. (cygcary@yahoo.com)\n\n" +"Copyright (C) 2010-2013 Cary R. (cygcary@yahoo.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" @@ -175,8 +175,9 @@ int target_design(ivl_design_t des) fprintf(vlog_out, " * 1364-1995 Verilog generated by Icarus Verilog " "VLOG95 Code Generator,\n"); fprintf(vlog_out, " * Version: " VERSION " (" VERSION_TAG ")\n"); - fprintf(vlog_out, " * Converted using %s delays.\n", - ivl_design_delay_sel(des)); + fprintf(vlog_out, " * Converted using %s delays and %s signed support.\n", + ivl_design_delay_sel(des), + allow_signed ? "with" : "without"); fprintf(vlog_out, " */\n"); sim_precision = ivl_design_time_precision(des); @@ -193,6 +194,25 @@ int target_design(ivl_design_t des) /* Emit any UDPs that are Icarus generated (D-FF). */ emit_icarus_generated_udps(); + /* If there were errors then add this information to the output. */ + if (vlog_errors) { + fprintf(vlog_out, "\n"); + fprintf(vlog_out, "/*\n"); + if (vlog_errors == 1) { + fprintf(vlog_out, " * There was 1 error during " + "translation.\n"); + } else { + fprintf(vlog_out, " * There were %d errors during " + "translation.\n", + vlog_errors); + } + fprintf(vlog_out, " */\n"); + /* Add something that makes the file invalid to make sure + * the user knows there were errors. */ + fprintf(vlog_out, "\n"); + } + fclose(vlog_out); /* A do nothing call to prevent warnings about this routine not From c4386da666c8cb170150a7005eb8bb021ed8d2b4 Mon Sep 17 00:00:00 2001 From: Cary R Date: Fri, 15 Feb 2013 19:29:29 -0800 Subject: [PATCH 4/8] vlog95: Emit initial blocks for tasks or functions. In SystemVerilog a task or function can initialize a variable in a task or function. In Icarus this is done by creating an initial block that does the assignment. We can translate this by emitting the initial block in the enclosing module scope. This is not 100% correct since SystemVerilog requires the initialization to be done before the other initial/always blocks are processed. For SystemVerilog the current Icarus behavior is incorrect, but even if it had a new process type that ran before the other ones the best I can do for vlog95 is emit it before the normal module processes which currently works. --- tgt-vlog95/scope.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index a0354c2c1..a08ee3792 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -627,6 +627,44 @@ static void emit_named_block_scope(ivl_scope_t scope) fprintf(vlog_out, " */\n"); } +/* + * In SystemVerilog a task or function can have a process to initialize + * variables. In reality SystemVerilog requires this to be before the + * initial/always blocks are processed, but that's not how it is currently + * implemented in Icarus! + */ +static int find_tf_process(ivl_process_t proc, ivl_scope_t scope) +{ + if (scope == ivl_process_scope(proc)) { + ivl_scope_t mod_scope = scope; + /* A task or function can only have initial processes that + * are used to set local variables. */ + assert(ivl_process_type(proc) == IVL_PR_INITIAL); + /* Find the module scope for this task/function. */ + while (ivl_scope_type(mod_scope) != IVL_SCT_MODULE) { + mod_scope = ivl_scope_parent(mod_scope); + assert(mod_scope); + } + /* Emit the process in the module scope since that is where + * this all started. */ + emit_process(mod_scope, proc); + } + return 0; +} + +/* + * Emit any initial blocks for the tasks or functions in a module. + */ +static int emit_tf_process(ivl_scope_t scope, ivl_scope_t parent) +{ + ivl_scope_type_t sc_type = ivl_scope_type(scope); + if ((sc_type == IVL_SCT_FUNCTION) || (sc_type == IVL_SCT_TASK)) { + /* Output the initial/always blocks for this module. */ + ivl_design_process(design, (ivl_process_f)find_tf_process, scope); + } + return 0; +} + /* * This search method may be slow for a large structural design with a * large number of gate types. That's not what this converter was built @@ -801,6 +839,10 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) emit_tran(scope, ivl_scope_switch(scope, idx)); } + /* Output any initial blocks for tasks or functions defined + * in this module. Used to initialize local variables. */ + ivl_scope_children(scope, (ivl_scope_f*) emit_tf_process, scope); + /* Output the initial/always blocks for this module. */ ivl_design_process(design, (ivl_process_f)find_process, scope); } From 27b8738d0618acc7a813731589f1f68935aaae02 Mon Sep 17 00:00:00 2001 From: Cary R Date: Tue, 19 Feb 2013 20:02:05 -0800 Subject: [PATCH 5/8] vlog95: convert real Verilog-A abs/min/max to $abs/$min/$max Converting these Verilog-A functions to use the $ version allows nan to be handled correct. This still needs to be done for the LPM code. --- tgt-vlog95/expr.c | 60 ++++++++++++++++++++++++++---------------- tgt-vlog95/logic_lpm.c | 5 ++-- 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 32b3001fb..70b34b385 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -176,19 +176,29 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) vlog_errors += 1; } break; - /* Convert Verilog-A min() or max() functions. This only works - * when the arguments have no side effect. */ + /* Convert the Verilog-A min() or max() functions. */ case 'm': case 'M': - fprintf(vlog_out, "(("); - emit_expr(scope, ivl_expr_oper1(expr), wid); - fprintf(vlog_out, ") %s (", oper); - emit_expr(scope, ivl_expr_oper2(expr), wid); - fprintf(vlog_out, ") ? ("); - emit_expr(scope, ivl_expr_oper1(expr), wid); - fprintf(vlog_out, ") : ("); - emit_expr(scope, ivl_expr_oper2(expr), wid); - fprintf(vlog_out, "))"); + if (ivl_expr_value(expr) == IVL_VT_REAL) { + /* For a real expression use the $min()/$max() function. */ + if (ivl_expr_opcode(expr) == 'm') fprintf(vlog_out, "$min("); + else fprintf(vlog_out, "$max("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ","); + emit_expr(scope, ivl_expr_oper2(expr), wid); + fprintf(vlog_out, ")"); + } else { + /* This only works when the argument has no side effect. */ + fprintf(vlog_out, "(("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ") %s (", oper); + emit_expr(scope, ivl_expr_oper2(expr), wid); + fprintf(vlog_out, ") ? ("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ") : ("); + emit_expr(scope, ivl_expr_oper2(expr), wid); + fprintf(vlog_out, "))"); + } break; default: emit_expr(scope, ivl_expr_oper1(expr), wid); @@ -587,19 +597,23 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) ivl_expr_lineno(expr)); vlog_errors += 1; break; - /* Convert Verilog-A abs() function. This only works when the - * argument has no side effect. */ + /* Convert the Verilog-A abs() function. */ case 'm': - fprintf(vlog_out, "(("); - emit_expr(scope, ivl_expr_oper1(expr), wid); - fprintf(vlog_out, ") > "); - if (ivl_expr_value(expr) == IVL_VT_REAL) fprintf(vlog_out, "0.0"); - else fprintf(vlog_out, "0"); - fprintf(vlog_out, " ? ("); - emit_expr(scope, ivl_expr_oper1(expr), wid); - fprintf(vlog_out, ") : -("); - emit_expr(scope, ivl_expr_oper1(expr), wid); - fprintf(vlog_out, "))"); + if (ivl_expr_value(expr) == IVL_VT_REAL) { + /* For a real expression use the $abs() function. */ + fprintf(vlog_out, "$abs("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ")"); + } else { + /* This only works when the argument has no side effect. */ + fprintf(vlog_out, "(("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ") > 0 ? ("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, ") : -("); + emit_expr(scope, ivl_expr_oper1(expr), wid); + fprintf(vlog_out, "))"); + } break; default: fprintf(vlog_out, ""); diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 40be840b3..8d55e133e 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -818,13 +818,14 @@ static void emit_lpm_func(ivl_scope_t scope, ivl_lpm_t lpm) static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) { switch (ivl_lpm_type(lpm)) { - /* Convert Verilog-A abs() function. This only works when the + /* Convert the Verilog-A abs() function. This only works when the * argument has no side effect. */ case IVL_LPM_ABS: +// HERE: If this is a real net then use the $abs() function to get nan to +// work correctly. See the expr code. fprintf(vlog_out, "(("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); fprintf(vlog_out, ") > "); -// HERE: If this is a real net then use 0.0. See the expr code. fprintf(vlog_out, "0 ? ("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); fprintf(vlog_out, ") : -("); From 2e86c208d1dc630bdb677d676962a40c43883077 Mon Sep 17 00:00:00 2001 From: Cary R Date: Wed, 20 Feb 2013 22:07:00 -0800 Subject: [PATCH 6/8] vlog95: Add support for variable packed array accesses --- tgt-vlog95/expr.c | 44 +++++++++++++++++++++++++++++++++----------- tgt-vlog95/stmt.c | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 70b34b385..a28b32d91 100644 --- a/tgt-vlog95/expr.c +++ b/tgt-vlog95/expr.c @@ -331,6 +331,27 @@ static void emit_select_name(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) } } +/* + * Emit a packed array access as a concatenation of bit selects. + */ +static void emit_expr_packed(ivl_scope_t scope, ivl_expr_t sig_expr, + ivl_expr_t sel_expr, unsigned wid) +{ + unsigned idx; + assert(wid > 0); + fprintf(vlog_out, "{"); + for (idx = wid - 1; idx > 0; idx -= 1) { + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_expr(scope, sel_expr, 0); + fprintf(vlog_out, " + %u], ", idx); + } + emit_select_name(scope, sig_expr, wid); + fprintf(vlog_out, "["); + emit_expr(scope, sel_expr, 0); + fprintf(vlog_out, "]}"); +} + /* * Emit an indexed part select as a concatenation of bit selects. */ @@ -444,18 +465,19 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid) fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); + } else 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 if (sel_type == IVL_SEL_OTHER) { + /* A packed array access. */ + assert(lsb == 0); + assert(msb >= 0); + emit_expr_packed(scope, sig_expr, sel_expr, width); } else { - 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. */ - assert(sel_type != IVL_SEL_OTHER); - emit_expr_ips(scope, sig_expr, sel_expr, - sel_type, width, msb, lsb); - } + /* An indexed part select. */ + emit_expr_ips(scope, sig_expr, sel_expr, sel_type, + width, msb, lsb); } } } else { diff --git a/tgt-vlog95/stmt.c b/tgt-vlog95/stmt.c index e58918b1d..0779829a6 100644 --- a/tgt-vlog95/stmt.c +++ b/tgt-vlog95/stmt.c @@ -105,6 +105,25 @@ static void emit_stmt_lval_name(ivl_scope_t scope, ivl_lval_t lval, } } +static void emit_stmt_lval_packed(ivl_scope_t scope, ivl_lval_t lval, + ivl_signal_t sig, ivl_expr_t sel_expr, + unsigned wid) +{ + unsigned idx; + assert(wid > 0); + fprintf(vlog_out, "{"); + for (idx = wid - 1; idx > 0; idx -= 1) { + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_expr(scope, sel_expr, 0); + fprintf(vlog_out, " + %u], ", idx); + } + emit_stmt_lval_name(scope, lval, sig); + fprintf(vlog_out, "["); + emit_expr(scope, sel_expr, 0); + fprintf(vlog_out, "]}"); +} + static void emit_stmt_lval_ips(ivl_scope_t scope, ivl_lval_t lval, ivl_signal_t sig, ivl_expr_t sel_expr, ivl_select_type_t sel_type, @@ -245,17 +264,18 @@ static void emit_stmt_lval_piece(ivl_scope_t scope, ivl_lval_t lval) fprintf(vlog_out, "["); emit_scaled_expr(scope, sel_expr, msb, lsb); fprintf(vlog_out, "]"); - } else { + } else if (ivl_expr_type(sel_expr) == IVL_EX_NUMBER) { /* 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); + emit_stmt_lval_name(scope, lval, sig); + emit_scaled_range(scope, sel_expr, width, msb, lsb); + } else if (sel_type == IVL_SEL_OTHER) { + assert(lsb == 0); + assert(msb >= 0); + emit_stmt_lval_packed(scope, lval, sig, sel_expr, width); + } else { /* An indexed part select. */ - } else { - assert(sel_type != IVL_SEL_OTHER); - emit_stmt_lval_ips(scope, lval, sig, sel_expr, sel_type, - width, msb, lsb); - } + emit_stmt_lval_ips(scope, lval, sig, sel_expr, sel_type, + width, msb, lsb); } } From 69705ef32b8c2e778c52b95e3aec0a9cf3d0f84d Mon Sep 17 00:00:00 2001 From: Cary R Date: Thu, 21 Feb 2013 19:28:38 -0800 Subject: [PATCH 7/8] vlog95: a sign extend can have an out of scope reference. If modules instantiations were always handled correctly this would not be needed, but for now looking for the nexus driver outside the scope is needed to make some of the tests work. --- tgt-vlog95/logic_lpm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 8d55e133e..343834f00 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -465,7 +465,7 @@ void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD) assert(! must_be_sig); // HERE: I think we need special input code like the following. #if 0 - /* If these is a signal in this scope that is also driven by + /* If there is a signal in this scope that is also driven by * the LPM then use the signal instead. */ sig = find_local_signal(scope, ivl_lpm_q(lpm), &word); if (sig) emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), 0); @@ -1009,9 +1009,12 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm) fprintf(vlog_out, ")"); break; case IVL_LPM_SIGN_EXT: +// HERE: pr1002 and one other test fails if this assert is used. A more +// robust method is needed to make sure things work as expected. // assert(! sign_extend); sign_extend = 1; - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1); + sign_extend = 0; break; case IVL_LPM_SUB: fprintf(vlog_out, "("); From 1b36aa5560294597f3985ec54aec15a096e9310f Mon Sep 17 00:00:00 2001 From: Cary R Date: Sat, 23 Feb 2013 14:24:17 -0800 Subject: [PATCH 8/8] vlog95: Add code to output specify block path delays. There are limitations with this since the compiler does not pass all the information that was present in the original source, but it is enough to get a valid simulation result. It is not enough for timing analysis! specparams are also incorrectly translated to parameters so they show up in the wrong place. --- tgt-vlog95/scope.c | 124 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index a08ee3792..a633df717 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -665,6 +665,125 @@ static int emit_tf_process(ivl_scope_t scope, ivl_scope_t parent) return 0; } +static void emit_path_delay(ivl_scope_t scope, ivl_delaypath_t dpath) +{ + unsigned idx, count = 6; + uint64_t pdlys [12]; + pdlys[0] = ivl_path_delay(dpath, IVL_PE_01); + pdlys[1] = ivl_path_delay(dpath, IVL_PE_10); + pdlys[2] = ivl_path_delay(dpath, IVL_PE_0z); + pdlys[3] = ivl_path_delay(dpath, IVL_PE_z1); + pdlys[4] = ivl_path_delay(dpath, IVL_PE_1z); + pdlys[5] = ivl_path_delay(dpath, IVL_PE_z0); + pdlys[6] = ivl_path_delay(dpath, IVL_PE_0x); + pdlys[7] = ivl_path_delay(dpath, IVL_PE_x1); + pdlys[8] = ivl_path_delay(dpath, IVL_PE_1x); + pdlys[9] = ivl_path_delay(dpath, IVL_PE_x0); + pdlys[10] = ivl_path_delay(dpath, IVL_PE_xz); + pdlys[11] = ivl_path_delay(dpath, IVL_PE_zx); + /* If the first six pdlys match then this may be a 1 delay form. */ + if ((pdlys[0] == pdlys[1]) && + (pdlys[0] == pdlys[2]) && + (pdlys[0] == pdlys[3]) && + (pdlys[0] == pdlys[4]) && + (pdlys[0] == pdlys[5])) count = 1; + /* Check to see if only a rise and fall value are given for the first + * six pdlys. */ + else if ((pdlys[0] == pdlys[2]) && + (pdlys[0] == pdlys[3]) && + (pdlys[1] == pdlys[4]) && + (pdlys[1] == pdlys[5])) count = 2; + /* Check to see if a rise, fall and high-Z value are given for the + * first six pdlys. */ + else if ((pdlys[0] == pdlys[3]) && + (pdlys[1] == pdlys[5]) && + (pdlys[2] == pdlys[4])) count = 3; + /* Now check to see if the 'bx related pdlys match the reduced + * delay form. If not then this is a twelve delay value. */ + if ((pdlys[6] != ((pdlys[0] < pdlys[2]) ? pdlys[0] : pdlys[2])) || + (pdlys[8] != ((pdlys[1] < pdlys[4]) ? pdlys[1] : pdlys[4])) || + (pdlys[11] != ((pdlys[3] < pdlys[5]) ? pdlys[3] : pdlys[5])) || + (pdlys[7] != ((pdlys[0] > pdlys[3]) ? pdlys[0] : pdlys[3])) || + (pdlys[9] != ((pdlys[1] > pdlys[5]) ? pdlys[1] : pdlys[5])) || + (pdlys[10] != ((pdlys[2] > pdlys[4]) ? pdlys[2] : pdlys[4]))) { + count = 12; + } + emit_scaled_delay(scope, pdlys[0]); + for(idx = 1; idx < count; idx += 1) { + fprintf(vlog_out, ", "); + emit_scaled_delay(scope, pdlys[idx]); + } +} + +static void emit_specify_paths(ivl_scope_t scope, ivl_signal_t sig) +{ + unsigned idx, count = ivl_signal_npath(sig); + for(idx = 0; idx < count; idx += 1) { + ivl_delaypath_t dpath = ivl_signal_path(sig, idx); + ivl_nexus_t cond = ivl_path_condit(dpath); + ivl_nexus_t source = ivl_path_source(dpath); + unsigned has_edge = 0; + fprintf(vlog_out, "%*c", indent, ' '); + if (cond) { + fprintf(vlog_out, "if ("); + emit_nexus_as_ca(scope, cond, 0); + fprintf(vlog_out, ") "); + } else if (ivl_path_is_condit(dpath)) { + fprintf(vlog_out, "ifnone "); + } + fprintf(vlog_out, "("); + if (ivl_path_source_posedge(dpath)) { + fprintf(vlog_out, "posedge "); + has_edge = 1; + } + if (ivl_path_source_negedge(dpath)) { + fprintf(vlog_out, "negedge "); + has_edge = 1; + } + emit_nexus_as_ca(scope, source, 0); + fprintf(vlog_out, " =>"); + /* The compiler does not keep the source expression for an edge + * sensitive path so add a constant to get the syntax right. */ + if (has_edge) { + fprintf(vlog_out, "(%s : 1'bx /* Missing */)", + ivl_signal_basename(sig)); + } else { + fprintf(vlog_out, "%s", ivl_signal_basename(sig)); + } + fprintf(vlog_out, ") = ("); + emit_path_delay(scope, dpath); + fprintf(vlog_out, ");\n"); + } +} + +/* + * The path delay information from the specify block is attached to the + * output ports. + */ +static void emit_specify(ivl_scope_t scope) +{ + unsigned word, idx, count = ivl_scope_ports(scope); + unsigned need_specify = 0; + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_t nex = ivl_scope_mod_port(scope, idx); + ivl_signal_t port = get_port_from_nexus(scope, nex, &word); +// HERE: Do we need to use word? See emit_module_port_def(). + assert(port); + if (ivl_signal_npath(port)) { + if (! need_specify) { + fprintf(vlog_out, "\n%*cspecify\n", indent, ' '); + need_specify = 1; + indent += indent_incr; + } + emit_specify_paths(scope, port); + } + } + if (need_specify) { + indent -= indent_incr; + fprintf(vlog_out, "%*cendspecify\n", indent, ' '); + } +} + /* * This search method may be slow for a large structural design with a * large number of gate types. That's not what this converter was built @@ -852,9 +971,12 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent) emit_stmt(scope, ivl_scope_def(scope)); } - /* Now print out any sub-scopes. */ + /* Print any sub-scopes. */ ivl_scope_children(scope, (ivl_scope_f*) emit_scope, scope); + /* And finally print a specify block when needed. */ + if (sc_type == IVL_SCT_MODULE) emit_specify(scope); + /* Output the scope ending. */ assert(indent >= indent_incr); indent -= indent_incr;