static void display_multi_driver_error(ivl_nexus_t nex, unsigned ndrivers, mdriver_type_t type) { unsigned idx; unsigned scope_len = UINT_MAX; ivl_signal_t sig = 0; /* Find the signal. */ for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t tsig = ivl_nexus_ptr_sig(ptr); if (tsig != 0) { ivl_scope_t scope; unsigned len; if (ivl_signal_local(tsig)) continue; /* If this is not a local signal then find the signal * that has the shortest scope (is the furthest up * the hierarchy). */ scope = ivl_signal_scope(tsig); assert(scope); len = strlen(ivl_scope_name(scope)); if (len < scope_len) { scope_len = len; sig = tsig; } } } assert(sig); fprintf(stderr, "%s:%u: vvp.tgt error: ", ivl_signal_file(sig), ivl_signal_lineno(sig)); switch (type) { case MDRV_UWIRE: if (ivl_signal_type(sig) != IVL_SIT_UWIRE) { fprintf(stderr, "(implicit) "); } fprintf(stderr, "uwire"); break; case MDRV_REAL: assert(ivl_signal_type(sig) == IVL_SIT_TRI); if (ivl_signal_data_type(sig) != IVL_VT_REAL) { fprintf(stderr, "(implicit) "); } fprintf(stderr, "wire real"); break; default: assert(0);; } fprintf(stderr, " \"%s\" must have a single driver, found (%u).\n", ivl_signal_basename(sig), ndrivers); vvp_errors += 1; }
static unsigned find_driving_signal(ivl_scope_t scope, ivl_nexus_t nex) { ivl_signal_t sig = 0; unsigned is_array = 0; int64_t array_idx = 0; unsigned idx, count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if (! t_sig) continue; if (ivl_signal_local(t_sig)) continue; /* An output can be used if it is driven by this nexus. */ if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ) && (ivl_signal_port(t_sig) != IVL_SIP_OUTPUT)) { continue; } /* We have a signal that can be used to find the name. */ if (sig) { // HERE: Which one should we use? For now it's the first one found. // I believe this needs to be solved (see above). fprintf(stderr, "%s:%u: vlog95 warning: Duplicate name (%s", ivl_signal_file(t_sig), ivl_signal_lineno(t_sig), ivl_signal_basename(t_sig)); if (ivl_signal_dimensions(t_sig) > 0) { int64_t tmp_idx = ivl_nexus_ptr_pin(nex_ptr); tmp_idx += ivl_signal_array_base(t_sig); fprintf(stderr, "[%"PRId64"]", tmp_idx); } fprintf(stderr, ") found for nexus (%s", ivl_signal_basename(sig)); if (is_array) fprintf(stderr, "[%"PRId64"]", array_idx); fprintf(stderr, ")\n"); } else { sig = t_sig; if (ivl_signal_dimensions(sig) > 0) { is_array = 1; array_idx = ivl_nexus_ptr_pin(nex_ptr); array_idx += ivl_signal_array_base(sig); } } } if (sig) { emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); if (is_array) fprintf(vlog_out, "[%"PRId64"]", array_idx); return 1; } return 0; }
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), 0); return; } } emit_expr(scope, expr, 0); }
/* * This function draws a reg/int/variable in the scope. This is a very * simple device to draw as there are no inputs to connect so no need * to scan the nexus. We do have to account for the possibility that * the device is arrayed, though, by making a node for each array element. */ static void draw_reg_in_scope(ivl_signal_t sig) { int msb = ivl_signal_msb(sig); int lsb = ivl_signal_lsb(sig); const char*datatype_flag = ivl_signal_integer(sig) ? "/i" : ivl_signal_signed(sig)? "/s" : ""; const char*local_flag = ivl_signal_local(sig)? "*" : ""; switch (ivl_signal_data_type(sig)) { case IVL_VT_REAL: datatype_flag = "/real"; break; default: break; } /* If the reg objects are collected into an array, then first write out the .array record to declare the array indices. */ if (ivl_signal_dimensions(sig) > 0) { unsigned word_count = ivl_signal_array_count(sig); int last = ivl_signal_array_base(sig)+word_count-1; int first = ivl_signal_array_base(sig); fprintf(vvp_out, "v%p .array%s \"%s\", %d %d, %d %d;\n", sig, datatype_flag, vvp_mangle_name(ivl_signal_basename(sig)), last, first, msb, lsb); } else { fprintf(vvp_out, "v%p_0 .var%s %s\"%s\", %d %d;%s\n", sig, datatype_flag, local_flag, vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, ivl_signal_local(sig)? " Local signal" : ""); } }
ivl_signal_t signal_of_nexus(ivl_nexus_t nex, unsigned*word) { unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); if (sig == 0) continue; if (ivl_signal_local(sig)) continue; *word = ivl_nexus_ptr_pin(ptr); return sig; } return 0; }
static unsigned is_local_input(ivl_scope_t scope, ivl_nexus_t nex) { ivl_signal_t sig = 0; unsigned idx, count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr); if (! t_sig) continue; if (! ivl_signal_local(t_sig)) continue; if (ivl_signal_port(t_sig) != IVL_SIP_INPUT) continue; assert(! sig); assert(ivl_signal_dimensions(t_sig) == 0); sig = t_sig; } if (sig) { fprintf(vlog_out, "ivlog%s", ivl_signal_basename(sig)); return 1; } return 0; }
static unsigned find_signal_in_nexus(ivl_scope_t scope, ivl_nexus_t nex) { ivl_signal_t use_sig = 0; unsigned is_driver = 0; unsigned is_array = 0; int64_t array_idx = 0; unsigned idx, count = ivl_nexus_ptrs(nex); for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); if (! sig) continue; if (ivl_signal_local(sig)) { /* If the local signal is another receiver skip it. */ if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) { continue; } assert(0); } /* We have a signal that can be used to find the name. */ if (scope == ivl_signal_scope(sig)) { if (use_sig) { /* Swap a receiver for a driver. */ if (is_driver && (ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) && (ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) { use_sig = sig; is_driver = 0; if (ivl_signal_dimensions(sig) > 0) { is_array = 1; array_idx = ivl_nexus_ptr_pin(nex_ptr); array_idx += ivl_signal_array_base(sig); } continue; } // HERE: Which one should we use? For now it's the first one found. // I believe this needs to be solved (see the inout.v test). fprintf(stderr, "%s:%u: vlog95 warning: Duplicate " "name (%s", ivl_signal_file(sig), ivl_signal_lineno(sig), ivl_signal_basename(sig)); if (ivl_signal_dimensions(sig) > 0) { int64_t tmp_idx = ivl_nexus_ptr_pin(nex_ptr); tmp_idx += ivl_signal_array_base(sig); fprintf(stderr, "[%"PRId64"]", tmp_idx); } fprintf(stderr, ") found for nexus (%s", ivl_signal_basename(use_sig)); if (is_array) fprintf(stderr, "[%"PRId64"]", array_idx); fprintf(stderr, ")\n"); } else { use_sig = sig; /* This signal is a driver. */ if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) || (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) { is_driver = 1; } if (ivl_signal_dimensions(sig) > 0) { is_array = 1; array_idx = ivl_nexus_ptr_pin(nex_ptr); array_idx += ivl_signal_array_base(sig); } } } } if (use_sig) { emit_id(ivl_signal_basename(use_sig)); if (is_array) fprintf(vlog_out, "[%"PRId64"]", array_idx); return 1; } return 0; }
/* * This function draws a net. This is a bit more complicated as we * have to find an appropriate functor to connect to the input. */ static void draw_net_in_scope(ivl_signal_t sig) { int msb = ivl_signal_msb(sig); int lsb = ivl_signal_lsb(sig); const char*datatype_flag = ivl_signal_signed(sig)? "/s" : ""; const char*local_flag = ivl_signal_local(sig)? "*" : ""; unsigned iword; switch (ivl_signal_data_type(sig)) { case IVL_VT_REAL: datatype_flag = "/real"; break; default: break; } for (iword = 0 ; iword < ivl_signal_array_count(sig); iword += 1) { unsigned word_count = ivl_signal_array_count(sig); unsigned dimensions = ivl_signal_dimensions(sig); struct vvp_nexus_data*nex_data; /* Connect the pin of the signal to something. */ ivl_nexus_t nex = ivl_signal_nex(sig, iword); const char*driver = draw_net_input(nex); nex_data = (struct vvp_nexus_data*)ivl_nexus_get_private(nex); assert(nex_data); if (nex_data->net == 0) { int strength_aware_flag = 0; const char*vec8 = ""; if (nex_data->flags&VVP_NEXUS_DATA_STR) strength_aware_flag = 1; if (nex_data->drivers_count > 1) vec8 = "8"; if (strength_aware_flag) vec8 = "8"; if (iword == 0 && dimensions > 0) { int last = ivl_signal_array_base(sig) + word_count-1; int first = ivl_signal_array_base(sig); fprintf(vvp_out, "v%p .array \"%s\", %d %d;\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), last, first); } if (dimensions > 0) { /* If this is a word of an array, then use an array reference in place of the net name. */ fprintf(vvp_out, "v%p_%u .net%s%s v%p %u, %d %d, %s;" " %u drivers%s\n", sig, iword, vec8, datatype_flag, sig, iword, msb, lsb, driver, nex_data->drivers_count, strength_aware_flag?", strength-aware":""); } else { /* If this is an isolated word, it uses its own name. */ assert(word_count == 1); fprintf(vvp_out, "v%p_%u .net%s%s %s\"%s\", %d %d, %s;" " %u drivers%s\n", sig, iword, vec8, datatype_flag, local_flag, vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, driver, nex_data->drivers_count, strength_aware_flag?", strength-aware":""); } nex_data->net = sig; nex_data->net_word = iword; } else if (dimensions > 0) { /* In this case, we have an alias to an existing signal array. this typically is an instance of port collapsing that the elaborator combined to discover that the entire array can be collapsed, so the word count for the signal and the alias *must* match. */ if (word_count == ivl_signal_array_count(nex_data->net)) { if (iword == 0) { fprintf(vvp_out, "v%p .array \"%s\", v%p; Alias to %s\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), nex_data->net, ivl_signal_basename(nex_data->net)); } /* An alias for an individual word. */ } else { if (iword == 0) { int first = ivl_signal_array_base(sig); int last = first + word_count-1; fprintf(vvp_out, "v%p .array \"%s\", %d %d;\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), last, first); } fprintf(vvp_out, "v%p_%u .alias%s v%p %u, %d %d, " "v%p_%u; Alias to %s\n", sig, iword, datatype_flag, sig, iword, msb, lsb, nex_data->net, nex_data->net_word, ivl_signal_basename(nex_data->net)); } } else { /* Finally, we may have an alias that is a word connected to another word. Again, this is a case of port collapsing. */ /* For the alias, create a different kind of node that refers to the alias source data instead of holding our own data. */ fprintf(vvp_out, "v%p_%u .alias%s \"%s\", %d %d, v%p_%u;\n", sig, iword, datatype_flag, vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, nex_data->net, nex_data->net_word); } } }
static void show_signal(ivl_signal_t net) { unsigned idx; const char*type = "?"; const char*port = ""; const char*data_type = "?"; const char*sign = ivl_signal_signed(net)? "signed" : "unsigned"; switch (ivl_signal_type(net)) { case IVL_SIT_REG: type = "reg"; break; case IVL_SIT_TRI: type = "tri"; break; case IVL_SIT_TRI0: type = "tri0"; break; case IVL_SIT_TRI1: type = "tri1"; break; case IVL_SIT_UWIRE: type = "uwire"; break; default: break; } switch (ivl_signal_port(net)) { case IVL_SIP_INPUT: port = "input "; break; case IVL_SIP_OUTPUT: port = "output "; break; case IVL_SIP_INOUT: port = "inout "; break; case IVL_SIP_NONE: break; } switch (ivl_signal_data_type(net)) { case IVL_VT_BOOL: data_type = "bool"; break; case IVL_VT_LOGIC: data_type = "logic"; break; case IVL_VT_REAL: data_type = "real"; break; default: data_type = "?data?"; break; } const char*discipline_txt = "NONE"; if (ivl_signal_discipline(net)) { ivl_discipline_t dis = ivl_signal_discipline(net); discipline_txt = ivl_discipline_name(dis); } for (idx = 0 ; idx < ivl_signal_array_count(net) ; idx += 1) { ivl_nexus_t nex = ivl_signal_nex(net, idx); fprintf(out, " %s %s %s%s[%d:%d] %s[word=%u, adr=%d] " "<width=%u%s> <discipline=%s> ", type, sign, port, data_type, ivl_signal_msb(net), ivl_signal_lsb(net), ivl_signal_basename(net), idx, ivl_signal_array_base(net)+idx, ivl_signal_width(net), ivl_signal_local(net)? ", local":"", discipline_txt); if (nex == NULL) { fprintf(out, "nexus=<virtual>\n"); continue; } else { fprintf(out, "nexus=%p\n", nex); } show_nexus_details(net, nex); } for (idx = 0 ; idx < ivl_signal_npath(net) ; idx += 1) { ivl_delaypath_t path = ivl_signal_path(net,idx); ivl_nexus_t nex = ivl_path_source(path); ivl_nexus_t con = ivl_path_condit(path); int posedge = ivl_path_source_posedge(path); int negedge = ivl_path_source_negedge(path); fprintf(out, " path %p", nex); if (posedge) fprintf(out, " posedge"); if (negedge) fprintf(out, " negedge"); if (con) fprintf(out, " (if %p)", con); else if (ivl_path_is_condit(path)) fprintf(out, " (ifnone)"); fprintf(out, " %" PRIu64 ",%" PRIu64 ",%" PRIu64 " %" PRIu64 ",%" PRIu64 ",%" PRIu64 " %" PRIu64 ",%" PRIu64 ",%" PRIu64 " %" PRIu64 ",%" PRIu64 ",%" PRIu64, ivl_path_delay(path, IVL_PE_01), ivl_path_delay(path, IVL_PE_10), ivl_path_delay(path, IVL_PE_0z), ivl_path_delay(path, IVL_PE_z1), ivl_path_delay(path, IVL_PE_1z), ivl_path_delay(path, IVL_PE_z0), ivl_path_delay(path, IVL_PE_0x), ivl_path_delay(path, IVL_PE_x1), ivl_path_delay(path, IVL_PE_1x), ivl_path_delay(path, IVL_PE_x0), ivl_path_delay(path, IVL_PE_xz), ivl_path_delay(path, IVL_PE_zx)); fprintf(out, " scope=%s\n", ivl_scope_name(ivl_path_scope(path))); } for (idx = 0 ; idx < ivl_signal_attr_cnt(net) ; idx += 1) { ivl_attribute_t atr = ivl_signal_attr_val(net, idx); switch (atr->type) { case IVL_ATT_STR: fprintf(out, " %s = %s\n", atr->key, atr->val.str); break; case IVL_ATT_NUM: fprintf(out, " %s = %ld\n", atr->key, atr->val.num); break; case IVL_ATT_VOID: fprintf(out, " %s\n", atr->key); break; } } }