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; } 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_; diff --git a/tgt-vlog95/expr.c b/tgt-vlog95/expr.c index 32b3001fb..a28b32d91 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); @@ -321,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. */ @@ -434,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 { @@ -587,19 +619,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..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); @@ -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, ") : -("); @@ -1008,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, "("); diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index a0354c2c1..a633df717 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -627,6 +627,163 @@ 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; +} + +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 @@ -801,6 +958,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); } @@ -810,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; 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); } } 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