/* * This function assigns a value to a real .variable. This is destined * for /dev/null when typed ivl_signal_t takes over all the real * variable support. */ static int show_stmt_assign_sig_real(ivl_statement_t net) { ivl_lval_t lval; ivl_signal_t var; assert(ivl_stmt_opcode(net) == 0); draw_eval_real(ivl_stmt_rval(net)); assert(ivl_stmt_lvals(net) == 1); lval = ivl_stmt_lval(net, 0); var = ivl_lval_sig(lval); assert(var != 0); if (ivl_signal_dimensions(var) == 0) { fprintf(vvp_out, " %%store/real v%p_0;\n", var); return 0; } // For now, only support 1-dimensional arrays. assert(ivl_signal_dimensions(var) == 1); ivl_expr_t word_ex = ivl_lval_idx(lval); int word_ix = allocate_word(); /* If the word index is a constant, then we can write directly to the word and save the index calculation. Out-of-bounds and undefined indices are converted to a canonical index of 'bx during elaboration, and we don't try to optimise that case. */ if (word_ex && number_is_immediate(word_ex, IMM_WID, 0) && !number_is_unknown(word_ex)) { unsigned long use_word = get_number_immediate(word_ex); assert(use_word < ivl_signal_array_count(var)); fprintf(vvp_out, " %%ix/load %u, %lu, 0;\n", word_ix, use_word); fprintf(vvp_out, " %%store/reala v%p, %d;\n", var, word_ix); } else { unsigned do_store = transient_id++; unsigned end_store = transient_id++; draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%jmp/0 t_%u, 4;\n", do_store); fprintf(vvp_out, " %%pop/real 1;\n"); fprintf(vvp_out, " %%jmp t_%u;\n", end_store); fprintf(vvp_out, "t_%u ;\n", do_store); fprintf(vvp_out, " %%store/reala v%p, %d;\n", var, word_ix); fprintf(vvp_out, "t_%u ;\n", end_store); } clr_word(word_ix); return 0; }
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 draw_lpm_ufunc(ivl_lpm_t net) { unsigned idx; ivl_scope_t def = ivl_lpm_define(net); const char*dly = draw_lpm_output_delay(net); fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly, vvp_mangle_id(ivl_scope_name(def)), ivl_lpm_width(net)); /* Print all the net signals that connect to the input of the function. */ for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) { fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net, idx))); } assert((ivl_lpm_size(net)+1) == ivl_scope_ports(def)); /* Now print all the variables in the function scope that receive the input values given in the previous list. */ for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) { ivl_signal_t psig = ivl_scope_port(def, idx+1); if (idx == 0) fprintf(vvp_out, " ("); else fprintf(vvp_out, ", "); assert(ivl_signal_dimensions(psig) == 0); fprintf(vvp_out, "v%p_0", psig); } fprintf(vvp_out, ")"); /* Now print the reference to the signal from which the result is collected. */ { ivl_signal_t psig = ivl_scope_port(def, 0); assert(ivl_lpm_width(net) == ivl_signal_width(psig)); assert(ivl_signal_dimensions(psig) == 0); fprintf(vvp_out, " v%p_0", psig); } /* Finally, print the scope identifier. */ fprintf(vvp_out, " S_%p;\n", def); }
static void function_argument_real(ivl_signal_t port, ivl_expr_t expr) { /* ports cannot be arrays. */ assert(ivl_signal_dimensions(port) == 0); draw_eval_real(expr); }
/* * 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; }
void draw_ufunc_real(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); unsigned idx; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%alloc S_%p;\n", def); } assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_signal_t port = ivl_scope_port(def, idx+1); draw_function_argument(port, ivl_expr_parm(expr, idx)); } /* Call the function */ 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"); /* Return value signal cannot be an array. */ assert(ivl_signal_dimensions(retval) == 0); /* Load the result into a word. */ fprintf(vvp_out, " %%load/real v%p_0;\n", retval); /* If this is an automatic function, free the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%free S_%p;\n", def); } }
static void function_argument_real(ivl_signal_t port, ivl_expr_t expr) { /* ports cannot be arrays. */ assert(ivl_signal_dimensions(port) == 0); draw_eval_real(expr); fprintf(vvp_out, " %%store/real v%p_0;\n", port); }
static void show_array_expression(ivl_expr_t net, unsigned ind) { ivl_signal_t sig = ivl_expr_signal(net); const char*name = ivl_signal_basename(sig); unsigned width = ivl_signal_width(sig); const char*vt = vt_type_string(net); fprintf(out, "%*sArray: %s, word_count=%u (%u dimensions), width=%u, type=%s\n", ind, "", name, ivl_signal_array_count(sig), ivl_signal_dimensions(sig), width, vt); }
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_call_path(scope, ivl_signal_scope(sig)); emit_id(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, "]"); } }
void draw_ufunc_vec4(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call function code. */ draw_ufunc_preamble(expr); assert(ivl_signal_dimensions(retval) == 0); fprintf(vvp_out, " %%load/vec4 v%p_0;\n", retval); draw_ufunc_epilogue(expr); }
const char*draw_input_from_net(ivl_nexus_t nex) { static char result[32]; unsigned word; ivl_signal_t sig = signal_of_nexus(nex, &word); if (sig == 0) return draw_net_input(nex); if (ivl_signal_type(sig)==IVL_SIT_REG && ivl_signal_dimensions(sig)>0) return draw_net_input(nex); snprintf(result, sizeof result, "v%p_%u", sig, word); return result; }
static void function_argument_logic(ivl_signal_t port, ivl_expr_t expr) { unsigned ewidth, pwidth; /* ports cannot be arrays. */ assert(ivl_signal_dimensions(port) == 0); ewidth = ivl_expr_width(expr); pwidth = ivl_signal_width(port); draw_eval_vec4(expr); if (ewidth < pwidth) fprintf(vvp_out, " %%pad/u %u;\n", pwidth); }
void draw_ufunc_string(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); /* Return value signal cannot be an array. */ assert(ivl_signal_dimensions(retval) == 0); /* Load the result into a word. */ fprintf(vvp_out, " %%load/str v%p_0;\n", retval); draw_ufunc_epilogue(expr); }
static void show_signal_expression(ivl_expr_t net, unsigned ind) { unsigned width = ivl_expr_width(net); const char*sign = ivl_expr_signed(net)? "signed" : "unsigned"; const char*vt = vt_type_string(net); ivl_expr_t word = ivl_expr_oper1(net); ivl_signal_t sig = ivl_expr_signal(net); const char*vt_sig = data_type_string(ivl_signal_data_type(sig)); unsigned dimensions = ivl_signal_dimensions(sig); unsigned word_count = ivl_signal_array_count(sig); if (dimensions == 0 && word_count != 1) { fprintf(out, "%*sERROR: Word count = %u for non-array object\n", ind, "", word_count); stub_errors += 1; } fprintf(out, "%*s<signal=%s, words=%u, width=%u, %s type=%s (%s)>\n", ind, "", ivl_expr_name(net), word_count, width, sign, vt, vt_sig); /* If the expression refers to a signal array, then there must also be a word select expression, and if the signal is not an array, there must NOT be a word expression. */ if (dimensions == 0 && word != 0) { fprintf(out, "%*sERROR: Unexpected word expression\n", ind+2, ""); stub_errors += 1; } if (dimensions >= 1 && word == 0) { fprintf(out, "%*sERROR: Missing word expression\n", ind+2, ""); stub_errors += 1; } /* If this is not an array, then the expression with must match the signal width. We have IVL_EX_SELECT expressions for casting signal widths. */ if (dimensions == 0 && ivl_signal_width(sig) != width) { fprintf(out, "%*sERROR: Expression width (%u) doesn't match ivl_signal_width(sig)=%u\n", ind+2, "", width, ivl_signal_width(sig)); stub_errors += 1; } if (word != 0) { fprintf(out, "%*sAddress-0 word address:\n", ind+2, ""); show_expression(word, ind+2); } }
struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid) { unsigned swid = ivl_expr_width(expr); ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); struct vector_info res; unsigned load_wid; /* Take in arguments to function and call function code. */ draw_ufunc_preamble(expr); /* Fresh basic block starts after the join. */ clear_expression_lookaside(); /* The return value is in a signal that has the name of the expression. Load that into the thread and return the vector result. */ res.base = allocate_vector(wid); res.wid = wid; if (res.base == 0) { fprintf(stderr, "%s:%u: vvp.tgt error: " "Unable to allocate %u thread bits for function result.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), wid); vvp_errors += 1; return res; } assert(res.base != 0); load_wid = swid; if (load_wid > ivl_signal_width(retval)) load_wid = ivl_signal_width(retval); assert(ivl_signal_dimensions(retval) == 0); fprintf(vvp_out, " %%load/v %u, v%p_0, %u;\n", res.base, retval, load_wid); /* Pad the signal value with zeros. */ if (load_wid < wid) pad_expr_in_place(expr, res, swid); draw_ufunc_epilogue(expr); return res; }
static void draw_signal_vec4(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); /* Handle the simple case, a signal expression that is a simple vector, no array dimensions. */ if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%load/vec4 v%p_0;\n", sig); return; } /* calculate the array index... */ int addr_index = allocate_word(); draw_eval_expr_into_integer(ivl_expr_oper1(expr), addr_index); fprintf(vvp_out, " %%load/vec4a v%p, %d;\n", sig, addr_index); clr_word(addr_index); }
static void emit_stmt_lval_name(ivl_scope_t scope, ivl_lval_t lval, ivl_signal_t sig) { ivl_expr_t array_idx = ivl_lval_idx(lval); emit_scope_call_path(scope, ivl_signal_scope(sig)); emit_id(ivl_signal_basename(sig)); 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, array_idx, msb, lsb); fprintf(vlog_out, "]"); } }
static int is_fixed_memory_word(ivl_expr_t net) { ivl_signal_t sig; if (ivl_expr_type(net) != IVL_EX_SIGNAL) return 0; sig = ivl_expr_signal(net); if (ivl_signal_dimensions(sig) == 0) return 1; if (ivl_signal_type(sig) == IVL_SIT_REG) return 0; if (number_is_immediate(ivl_expr_oper1(net), IMM_WID, 0)) return 1; 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 int eval_object_signal(ivl_expr_t expr) { ivl_signal_t sig = ivl_expr_signal(expr); /* Simple case: This is a simple variable. Generate a load statement to load the string into the stack. */ if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); return 0; } /* There is a word select expression, so load the index into a register and load from the array. */ ivl_expr_t word_ex = ivl_expr_oper1(expr); int word_ix = allocate_word(); draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%load/obja v%p, %d;\n", sig, word_ix); clr_word(word_ix); return 0; }
static void function_argument_logic(ivl_signal_t port, ivl_expr_t expr) { struct vector_info res; unsigned ewidth, pwidth; /* ports cannot be arrays. */ assert(ivl_signal_dimensions(port) == 0); ewidth = ivl_expr_width(expr); pwidth = ivl_signal_width(port); /* Just like a normal assignment the function arguments need to * be evaluated at either their width or the argument width if * it is larger. */ if (ewidth < pwidth) ewidth = pwidth; res = draw_eval_expr_wid(expr, ewidth, 0); /* We could have extra bits so only select the ones we need. */ fprintf(vvp_out, " %%set/v v%p_0, %u, %u;\n", port, res.base, pwidth); clr_vector(res); }
/* * 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" : ""); } }
static void draw_logic_in_scope(ivl_net_logic_t lptr) { unsigned pdx; const char*ltype = "?"; const char*lcasc = 0; char identity_val = '0'; int need_delay_flag = ivl_logic_delay(lptr,0)? 1 : 0; unsigned vector_width = width_of_nexus(ivl_logic_pin(lptr, 0)); ivl_drive_t str0, str1; int level; int ninp = ivl_logic_pins(lptr) - 1; typedef const char*const_charp; const_charp*input_strings = calloc(ninp, sizeof(const_charp)); for (pdx = 0 ; pdx < ninp ; pdx += 1) { ivl_nexus_t nex = ivl_logic_pin(lptr, pdx+1); if (nex == 0) { /* Only UDPs can have unconnected inputs. */ assert(ivl_logic_type(lptr) == IVL_LO_UDP); input_strings[pdx] = 0; } else { input_strings[pdx] = draw_net_input(nex); } } switch (ivl_logic_type(lptr)) { case IVL_LO_UDP: free(input_strings); draw_udp_in_scope(lptr); return; case IVL_LO_BUFZ: { /* Draw bufz objects, but only if the gate cannot be elided. If I can elide it, then the draw_nex_input will take care of it for me. */ ivl_nexus_ptr_t nptr = ivl_logic_pin_ptr(lptr,0); ltype = "BUFZ"; if (can_elide_bufz(lptr, nptr)) return; break; } case IVL_LO_PULLDOWN: case IVL_LO_PULLUP: /* Skip pullup and pulldown objects. Things that have pull objects as inputs will instead generate the appropriate C<?> symbol. */ free(input_strings); return; case IVL_LO_AND: ltype = "AND"; identity_val = '1'; break; case IVL_LO_BUF: ltype = "BUF"; break; case IVL_LO_BUFIF0: ltype = "BUFIF0"; break; case IVL_LO_BUFIF1: ltype = "BUFIF1"; break; case IVL_LO_NAND: ltype = "NAND"; lcasc = "AND"; identity_val = '1'; break; case IVL_LO_NOR: ltype = "NOR"; lcasc = "OR"; break; case IVL_LO_NOT: ltype = "NOT"; break; case IVL_LO_OR: ltype = "OR"; break; case IVL_LO_XNOR: ltype = "XNOR"; lcasc = "XOR"; break; case IVL_LO_XOR: ltype = "XOR"; break; case IVL_LO_CMOS: ltype = "CMOS"; break; case IVL_LO_PMOS: ltype = "PMOS"; break; case IVL_LO_NMOS: ltype = "NMOS"; break; case IVL_LO_RCMOS: ltype = "RCMOS"; break; case IVL_LO_RPMOS: ltype = "RPMOS"; break; case IVL_LO_RNMOS: ltype = "RNMOS"; break; case IVL_LO_NOTIF0: ltype = "NOTIF0"; break; case IVL_LO_NOTIF1: ltype = "NOTIF1"; break; default: fprintf(stderr, "vvp.tgt: error: Unhandled logic type: %u\n", ivl_logic_type(lptr)); ltype = "?"; break; } { ivl_nexus_t nex = ivl_logic_pin(lptr, 0); ivl_nexus_ptr_t nptr = 0; unsigned idx; for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { nptr = ivl_nexus_ptr(nex,idx); if (ivl_nexus_ptr_log(nptr) != lptr) continue; if (ivl_nexus_ptr_pin(nptr) != 0) continue; break; } str0 = ivl_nexus_ptr_drive0(nptr); str1 = ivl_nexus_ptr_drive1(nptr); } if (!lcasc) lcasc = ltype; /* Get all the input label that I will use for parameters to the functor that I create later. */ ninp = ivl_logic_pins(lptr) - 1; input_strings = calloc(ninp, sizeof(char*)); for (pdx = 0 ; pdx < ninp ; pdx += 1) input_strings[pdx] = draw_net_input(ivl_logic_pin(lptr, pdx+1)); level = 0; ninp = ivl_logic_pins(lptr) - 1; while (ninp) { int inst; for (inst = 0; inst < ninp; inst += 4) { if (ninp > 4) fprintf(vvp_out, "L_%p/%d/%d .functor %s %u", lptr, level, inst, lcasc, vector_width); else { fprintf(vvp_out, "L_%p%s .functor %s %u", lptr, need_delay_flag? "/d" : "", ltype, vector_width); if (str0 != IVL_DR_STRONG || str1 != IVL_DR_STRONG) fprintf(vvp_out, " [%u %u]", str0, str1); } for (pdx = inst; pdx < ninp && pdx < inst+4 ; pdx += 1) { if (level) { fprintf(vvp_out, ", L_%p/%d/%d", lptr, level - 1, pdx*4); } else { fprintf(vvp_out, ", %s", input_strings[pdx]); } } for ( ; pdx < inst+4 ; pdx += 1) { unsigned wdx; fprintf(vvp_out, ", C4<"); for (wdx = 0 ; wdx < vector_width ; wdx += 1) fprintf(vvp_out, "%c", identity_val); fprintf(vvp_out, ">"); } fprintf(vvp_out, ";\n"); } if (ninp > 4) ninp = (ninp+3) / 4; else ninp = 0; level += 1; } /* Free the array of char*. The strings themselves are persistent, held by the ivl_nexus_t objects. */ free(input_strings); /* If there are delays, then draw the delay functor to carry that delay. This is the final output. */ if (need_delay_flag) { ivl_expr_t rise_exp = ivl_logic_delay(lptr,0); ivl_expr_t fall_exp = ivl_logic_delay(lptr,1); ivl_expr_t decay_exp = ivl_logic_delay(lptr,2); if (number_is_immediate(rise_exp,64,0) && number_is_immediate(fall_exp,64,0) && number_is_immediate(decay_exp,64,0)) { fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", lptr, get_number_immediate(rise_exp), get_number_immediate(rise_exp), get_number_immediate(rise_exp), lptr); } else { ivl_signal_t sig; assert(ivl_expr_type(rise_exp) == IVL_EX_SIGNAL); assert(ivl_expr_type(fall_exp) == IVL_EX_SIGNAL); assert(ivl_expr_type(decay_exp) == IVL_EX_SIGNAL); fprintf(vvp_out, "L_%p .delay L_%p/d", lptr, lptr); sig = ivl_expr_signal(rise_exp); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0", sig); sig = ivl_expr_signal(fall_exp); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0", sig); sig = ivl_expr_signal(decay_exp); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0;\n", sig); } } }
struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid) { unsigned idx; unsigned swid = ivl_expr_width(expr); ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); struct vector_info res; unsigned load_wid; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%alloc S_%p;\n", def); } /* evaluate the expressions and send the results to the function ports. */ assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_signal_t port = ivl_scope_port(def, idx+1); draw_function_argument(port, ivl_expr_parm(expr, idx)); } /* Call the function */ 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"); /* Fresh basic block starts after the join. */ clear_expression_lookaside(); /* The return value is in a signal that has the name of the expression. Load that into the thread and return the vector result. */ res.base = allocate_vector(wid); res.wid = wid; if (res.base == 0) { fprintf(stderr, "%s:%u: vvp.tgt error: " "Unable to allocate %u thread bits for function result.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), wid); vvp_errors += 1; return res; } assert(res.base != 0); load_wid = swid; if (load_wid > ivl_signal_width(retval)) load_wid = ivl_signal_width(retval); assert(ivl_signal_dimensions(retval) == 0); fprintf(vvp_out, " %%load/v %u, v%p_0, %u;\n", res.base, retval, load_wid); /* Pad the signal value with zeros. */ if (load_wid < wid) pad_expr_in_place(expr, res, swid); /* If this is an automatic function, free the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%free S_%p;\n", def); } return res; }
static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) { unsigned nptr_pin = ivl_nexus_ptr_pin(nptr); ivl_net_const_t cptr; ivl_net_logic_t lptr; ivl_signal_t sptr; ivl_lpm_t lpm; lptr = ivl_nexus_ptr_log(nptr); if (lptr && ((ivl_logic_type(lptr)==IVL_LO_BUFZ)||(ivl_logic_type(lptr)==IVL_LO_BUFT)) && (nptr_pin == 0)) do { if (! can_elide_bufz(lptr, nptr)) break; return strdup(draw_net_input(ivl_logic_pin(lptr, 1))); } while(0); if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLDOWN)) { return draw_net_pull(lptr, ivl_nexus_ptr_drive0(nptr), "0"); } if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) { return draw_net_pull(lptr, ivl_nexus_ptr_drive1(nptr), "1"); } if (lptr && (nptr_pin == 0)) { char tmp[128]; snprintf(tmp, sizeof tmp, "L_%p", lptr); return strdup(tmp); } sptr = ivl_nexus_ptr_sig(nptr); if (sptr && (ivl_signal_type(sptr) == IVL_SIT_REG)) { char tmp[128]; /* Input is a .var. This device may be a non-zero pin because it may be an array of reg vectors. */ snprintf(tmp, sizeof tmp, "v%p_%u", sptr, nptr_pin); if (ivl_signal_dimensions(sptr) > 0) { fprintf(vvp_out, "v%p_%u .array/port v%p, %u;\n", sptr, nptr_pin, sptr, nptr_pin); } return strdup(tmp); } cptr = ivl_nexus_ptr_con(nptr); if (cptr) { char tmp[64]; char *result = 0; ivl_expr_t d_rise, d_fall, d_decay; unsigned dly_width = 0; char *dly; /* Constants should have exactly 1 pin, with a literal value. */ assert(nptr_pin == 0); switch (ivl_const_type(cptr)) { case IVL_VT_LOGIC: case IVL_VT_BOOL: case IVL_VT_STRING: if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG)) { result = draw_C4_to_string(cptr); } else { result = draw_C8_to_string(cptr, ivl_nexus_ptr_drive0(nptr), ivl_nexus_ptr_drive1(nptr)); } dly_width = ivl_const_width(cptr); break; case IVL_VT_REAL: result = draw_Cr_to_string(ivl_const_real(cptr)); dly_width = 0; break; default: assert(0); break; } d_rise = ivl_const_delay(cptr, 0); d_fall = ivl_const_delay(cptr, 1); d_decay = ivl_const_delay(cptr, 2); dly = ""; if (d_rise != 0) { draw_delay(cptr, dly_width, 0, d_rise, d_fall, d_decay); dly = "/d"; } fprintf(vvp_out, "L_%p%s .functor BUFT 1, %s, C4<0>, C4<0>, C4<0>;\n", cptr, dly, result); free(result); snprintf(tmp, sizeof tmp, "L_%p", cptr); return strdup(tmp); } lpm = ivl_nexus_ptr_lpm(nptr); if (lpm) switch (ivl_lpm_type(lpm)) { case IVL_LPM_FF: case IVL_LPM_LATCH: case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_INT: case IVL_LPM_CAST_REAL: case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_WEQ: case IVL_LPM_CMP_WNE: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: case IVL_LPM_SFUNC: case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: case IVL_LPM_SIGN_EXT: case IVL_LPM_SUB: case IVL_LPM_MULT: case IVL_LPM_MUX: case IVL_LPM_POW: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_UFUNC: case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */ case IVL_LPM_REPEAT: case IVL_LPM_SUBSTITUTE: if (ivl_lpm_q(lpm) == nex) { char tmp[128]; snprintf(tmp, sizeof tmp, "L_%p", lpm); return strdup(tmp); } break; } fprintf(stderr, "vvp.tgt error: no input to nexus.\n"); assert(0); return strdup("C<z>"); }
static void get_vec_from_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice, unsigned bit, unsigned wid) { ivl_signal_t sig = ivl_lval_sig(lval); ivl_expr_t part_off_ex = ivl_lval_part_off(lval); unsigned long part_off = 0; /* Although Verilog doesn't support it, we'll handle here the case of an l-value part select of an array word if the address is constant. */ ivl_expr_t word_ix = ivl_lval_idx(lval); unsigned long use_word = 0; if (part_off_ex == 0) { part_off = 0; } else if (number_is_immediate(part_off_ex, IMM_WID, 0) && !number_is_unknown(part_off_ex)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } /* If the word index is a constant expression, then evaluate it to select the word, and pay no further heed to the expression itself. */ if (word_ix && number_is_immediate(word_ix, IMM_WID, 0)) { assert(! number_is_unknown(word_ix)); use_word = get_number_immediate(word_ix); word_ix = 0; } if (ivl_lval_mux(lval)) part_off_ex = ivl_lval_mux(lval); if (ivl_signal_dimensions(sig)==0 && part_off_ex==0 && word_ix==0 && part_off==0 && wid==ivl_signal_width(sig)) { slice->type = SLICE_SIMPLE_VECTOR; slice->u_.simple_vector.use_word = use_word; fprintf(vvp_out, " %%load/v %u, v%p_%lu, %u;\n", bit, sig, use_word, wid); } else if (ivl_signal_dimensions(sig)==0 && part_off_ex==0 && word_ix==0) { assert(use_word == 0); slice->type = SLICE_PART_SELECT_STATIC; slice->u_.part_select_static.part_off = part_off; fprintf(vvp_out, " %%ix/load 1, %lu, 0;\n", part_off); fprintf(vvp_out, " %%load/x1p %u, v%p_0, %u;\n", bit, sig, wid); } else if (ivl_signal_dimensions(sig)==0 && part_off_ex!=0 && word_ix==0) { unsigned skip_set = transient_id++; unsigned out_set = transient_id++; assert(use_word == 0); assert(part_off == 0); slice->type = SLICE_PART_SELECT_DYNAMIC; draw_eval_expr_into_integer(part_off_ex, 1); slice->u_.part_select_dynamic.word_idx_reg = allocate_word(); slice->u_.part_select_dynamic.x_flag = allocate_vector(1); fprintf(vvp_out, " %%mov %u, %u, 1;\n", slice->u_.part_select_dynamic.x_flag, 4); fprintf(vvp_out, " %%mov/wu %d, %d;\n", slice->u_.part_select_dynamic.word_idx_reg, 1); fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); fprintf(vvp_out, " %%load/x1p %u, v%p_0, %u;\n", bit, sig, wid); fprintf(vvp_out, " %%jmp t_%u;\n", out_set); fprintf(vvp_out, "t_%u ;\n", skip_set); fprintf(vvp_out, " %%mov %u, 2, %u;\n", bit, wid); fprintf(vvp_out, "t_%u ;\n", out_set); } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { slice->type = SLICE_MEMORY_WORD_STATIC; slice->u_.memory_word_static.use_word = use_word; if (use_word < ivl_signal_array_count(sig)) { fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); fprintf(vvp_out, " %%load/av %u, v%p, %u;\n", bit, sig, wid); } else { fprintf(vvp_out, " %%mov %u, 2, %u; OUT OF BOUNDS\n", bit, wid); } } else if (ivl_signal_dimensions(sig) > 0 && word_ix != 0) { unsigned skip_set = transient_id++; unsigned out_set = transient_id++; slice->type = SLICE_MEMORY_WORD_DYNAMIC; draw_eval_expr_into_integer(word_ix, 3); slice->u_.memory_word_dynamic.word_idx_reg = allocate_word(); slice->u_.memory_word_dynamic.x_flag = allocate_vector(1); fprintf(vvp_out, " %%mov/wu %d, 3;\n", slice->u_.memory_word_dynamic.word_idx_reg); fprintf(vvp_out, " %%mov %u, 4, 1;\n", slice->u_.memory_word_dynamic.x_flag); fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); fprintf(vvp_out, " %%ix/load 1, 0, 0;\n"); fprintf(vvp_out, " %%load/av %u, v%p, %u;\n", bit, sig, wid); fprintf(vvp_out, " %%jmp t_%u;\n", out_set); fprintf(vvp_out, "t_%u ;\n", skip_set); fprintf(vvp_out, " %%mov %u, 2, %u;\n", bit, wid); fprintf(vvp_out, "t_%u ;\n", out_set); } else { assert(0); } }
static void draw_binary_vec4_compare_class(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); ivl_expr_t re = ivl_expr_oper2(expr); if (ivl_expr_type(le) == IVL_EX_NULL) { ivl_expr_t tmp = le; le = re; re = tmp; } /* Special case: If both operands are null, then the expression has a constant value. */ if (ivl_expr_type(le)==IVL_EX_NULL && ivl_expr_type(re)==IVL_EX_NULL) { switch (ivl_expr_opcode(expr)) { case 'e': /* == */ fprintf(vvp_out, " %%pushi/vec4 1, 0, 1;\n"); break; case 'n': /* != */ fprintf(vvp_out, " %%pushi/vec4 0, 0, 1;\n"); break; default: assert(0); break; } return; } /* A signal/variable is compared to null. Implement this with the %test_nul statement, which peeks at the variable contents directly. */ if (ivl_expr_type(re)==IVL_EX_NULL && ivl_expr_type(le)==IVL_EX_SIGNAL) { ivl_signal_t sig= ivl_expr_signal(le); if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%test_nul v%p_0;\n", sig); } else { ivl_expr_t word_ex = ivl_expr_oper1(le); int word_ix = allocate_word(); draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%test_nul/a v%p, %d;\n", sig, word_ix); clr_word(word_ix); } if (ivl_expr_opcode(expr) == 'n') fprintf(vvp_out, " %%flag_inv 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); return; } if (ivl_expr_type(re)==IVL_EX_NULL && ivl_expr_type(le)==IVL_EX_PROPERTY) { ivl_signal_t sig = ivl_expr_signal(le); unsigned pidx = ivl_expr_property_idx(le); ivl_expr_t idx_expr = ivl_expr_oper1(le); int idx = 0; /* If the property has an array index, then evaluate it into an index register. */ if ( idx_expr ) { idx = allocate_word(); draw_eval_expr_into_integer(idx_expr, idx); } fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%test_nul/prop %u, %d;\n", pidx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); if (ivl_expr_opcode(expr) == 'n') fprintf(vvp_out, " %%flag_inv 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (idx != 0) clr_word(idx); return; } fprintf(stderr, "SORRY: Compare class handles not implemented\n"); fprintf(vvp_out, " ; XXXX compare class handles. re-type=%d, le-type=%d\n", ivl_expr_type(re), ivl_expr_type(le)); vvp_errors += 1; }
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; }
static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr) { unsigned nptr_pin = ivl_nexus_ptr_pin(nptr); ivl_net_const_t cptr; ivl_net_logic_t lptr; ivl_signal_t sptr; ivl_lpm_t lpm; lptr = ivl_nexus_ptr_log(nptr); if (lptr && ((ivl_logic_type(lptr)==IVL_LO_BUFZ)||(ivl_logic_type(lptr)==IVL_LO_BUFT)) && (nptr_pin == 0)) do { if (! can_elide_bufz(lptr, nptr)) break; return strdup(draw_net_input(ivl_logic_pin(lptr, 1))); } while(0); /* If this is a pulldown device, then there is a single pin that drives a constant value to the entire width of the vector. The driver normally drives a pull0 value, so a C8<> constant is appropriate, but if the drive is really strong, then we can draw a C4<> constant instead. */ if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLDOWN)) { if (ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) { size_t result_len = ivl_logic_width(lptr) + 5; char*result = malloc(result_len); char*dp = result; strcpy(dp, "C4<"); dp += strlen(dp); str_repeat(dp, "0", ivl_logic_width(lptr)); dp += ivl_logic_width(lptr); *dp++ = '>'; *dp = 0; assert(dp >= result); assert((unsigned)(dp - result) <= result_len); return result; } else { char val[4]; size_t result_len = 3*ivl_logic_width(lptr) + 5; char*result = malloc(result_len); char*dp = result; val[0] = "01234567"[ivl_nexus_ptr_drive0(nptr)]; val[1] = val[0]; val[2] = '0'; val[3] = 0; strcpy(dp, "C8<"); dp += strlen(dp); str_repeat(dp, val, ivl_logic_width(lptr)); dp += 3*ivl_logic_width(lptr); *dp++ = '>'; *dp = 0; assert(dp >= result); assert((unsigned)(dp - result) <= result_len); return result; } } if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) { char*result; char tmp[32]; if (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG) { size_t result_len = 5 + ivl_logic_width(lptr); result = malloc(result_len); char*dp = result; strcpy(dp, "C4<"); dp += strlen(dp); str_repeat(dp, "1", ivl_logic_width(lptr)); dp += ivl_logic_width(lptr); *dp++ = '>'; *dp = 0; assert(dp >= result); assert((unsigned)(dp - result) <= result_len); } else { char val[4]; size_t result_len = 5 + 3*ivl_logic_width(lptr); result = malloc(result_len); char*dp = result; val[0] = "01234567"[ivl_nexus_ptr_drive1(nptr)]; val[1] = val[0]; val[2] = '1'; val[3] = 0; strcpy(dp, "C8<"); dp += strlen(dp); str_repeat(dp, val, ivl_logic_width(lptr)); dp += 3*ivl_logic_width(lptr); *dp++ = '>'; *dp = 0; assert(dp >= result); assert((unsigned)(dp - result) <= result_len); } /* Make the constant an argument to a BUFZ, which is what we use to drive the PULLed value. */ fprintf(vvp_out, "L_%p .functor BUFT 1, %s, C4<0>, C4<0>, C4<0>;\n", lptr, result); snprintf(tmp, sizeof tmp, "L_%p", lptr); result = realloc(result, strlen(tmp)+1); strcpy(result, tmp); return result; } if (lptr && (nptr_pin == 0)) { char tmp[128]; snprintf(tmp, sizeof tmp, "L_%p", lptr); return strdup(tmp); } sptr = ivl_nexus_ptr_sig(nptr); if (sptr && (ivl_signal_type(sptr) == IVL_SIT_REG)) { char tmp[128]; /* Input is a .var. This device may be a non-zero pin because it may be an array of reg vectors. */ snprintf(tmp, sizeof tmp, "v%p_%u", sptr, nptr_pin); if (ivl_signal_dimensions(sptr) > 0) { fprintf(vvp_out, "v%p_%u .array/port v%p, %u;\n", sptr, nptr_pin, sptr, nptr_pin); } return strdup(tmp); } cptr = ivl_nexus_ptr_con(nptr); if (cptr) { char *result = 0; ivl_expr_t d_rise, d_fall, d_decay; unsigned dly_width = 0; /* Constants should have exactly 1 pin, with a literal value. */ assert(nptr_pin == 0); switch (ivl_const_type(cptr)) { case IVL_VT_LOGIC: case IVL_VT_BOOL: case IVL_VT_STRING: if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG)) { result = draw_C4_to_string(cptr); } else { result = draw_C8_to_string(cptr, ivl_nexus_ptr_drive0(nptr), ivl_nexus_ptr_drive1(nptr)); } dly_width = ivl_const_width(cptr); break; case IVL_VT_REAL: result = draw_Cr_to_string(ivl_const_real(cptr)); dly_width = 0; break; default: assert(0); break; } d_rise = ivl_const_delay(cptr, 0); d_fall = ivl_const_delay(cptr, 1); d_decay = ivl_const_delay(cptr, 2); /* We have a delayed constant, so we need to build some code. */ if (d_rise != 0) { char tmp[128]; fprintf(vvp_out, "L_%p/d .functor BUFT 1, %s, " "C4<0>, C4<0>, C4<0>;\n", cptr, result); free(result); /* Is this a fixed or variable delay? */ if (number_is_immediate(d_rise, 64, 0) && number_is_immediate(d_fall, 64, 0) && number_is_immediate(d_decay, 64, 0)) { assert(! number_is_unknown(d_rise)); assert(! number_is_unknown(d_fall)); assert(! number_is_unknown(d_decay)); fprintf(vvp_out, "L_%p .delay %u " "(%" PRIu64 ",%" PRIu64 ",%" PRIu64 ") L_%p/d;\n", cptr, dly_width, get_number_immediate64(d_rise), get_number_immediate64(d_fall), get_number_immediate64(d_decay), cptr); } else { ivl_signal_t sig; // We do not currently support calculating the decay // from the rise and fall variable delays. assert(d_decay != 0); assert(ivl_expr_type(d_rise) == IVL_EX_SIGNAL); assert(ivl_expr_type(d_fall) == IVL_EX_SIGNAL); assert(ivl_expr_type(d_decay) == IVL_EX_SIGNAL); fprintf(vvp_out, "L_%p .delay %u L_%p/d", cptr, dly_width, cptr); sig = ivl_expr_signal(d_rise); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0", sig); sig = ivl_expr_signal(d_fall); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0", sig); sig = ivl_expr_signal(d_decay); assert(ivl_signal_dimensions(sig) == 0); fprintf(vvp_out, ", v%p_0;\n", sig); } snprintf(tmp, sizeof tmp, "L_%p", cptr); result = strdup(tmp); } else { char tmp[64]; fprintf(vvp_out, "L_%p .functor BUFT 1, %s, " "C4<0>, C4<0>, C4<0>;\n", cptr, result); free(result); snprintf(tmp, sizeof tmp, "L_%p", cptr); result = strdup(tmp); } return result; } lpm = ivl_nexus_ptr_lpm(nptr); if (lpm) switch (ivl_lpm_type(lpm)) { case IVL_LPM_FF: case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_INT: case IVL_LPM_CAST_REAL: case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: case IVL_LPM_RE_AND: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_XNOR: case IVL_LPM_SFUNC: case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: case IVL_LPM_SIGN_EXT: case IVL_LPM_SUB: case IVL_LPM_MULT: case IVL_LPM_MUX: case IVL_LPM_POW: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_UFUNC: case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */ case IVL_LPM_REPEAT: if (ivl_lpm_q(lpm) == nex) { char tmp[128]; snprintf(tmp, sizeof tmp, "L_%p", lpm); return strdup(tmp); } break; } fprintf(stderr, "vvp.tgt error: no input to nexus.\n"); assert(0); return strdup("C<z>"); }
static void set_vec_to_lval_slice(ivl_lval_t lval, unsigned bit, unsigned wid) { ivl_signal_t sig = ivl_lval_sig(lval); ivl_expr_t part_off_ex = ivl_lval_part_off(lval); unsigned long part_off = 0; /* Although Verilog doesn't support it, we'll handle here the case of an l-value part select of an array word if the address is constant. */ ivl_expr_t word_ix = ivl_lval_idx(lval); unsigned long use_word = 0; if (part_off_ex == 0) { part_off = 0; } else if (number_is_immediate(part_off_ex, IMM_WID, 0) && !number_is_unknown(part_off_ex)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } /* If the word index is a constant expression, then evaluate it to select the word, and pay no further heed to the expression itself. Out-of-bounds and undefined indices are converted to a canonical index of 'bx during elaboration, and we don't try to optimise that case. */ if (word_ix && number_is_immediate(word_ix, IMM_WID, 0) && !number_is_unknown(word_ix)) { use_word = get_number_immediate(word_ix); assert(use_word < ivl_signal_array_count(sig)); word_ix = 0; } if (ivl_lval_mux(lval)) part_off_ex = ivl_lval_mux(lval); if (part_off_ex && ivl_signal_dimensions(sig) == 0) { unsigned skip_set = transient_id++; /* There is a mux expression, so this must be a write to a bit-select l-val. Presumably, the x0 index register has been loaded wit the result of the evaluated part select base expression. */ assert(!word_ix); draw_eval_expr_into_integer(part_off_ex, 0); fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set); fprintf(vvp_out, " %%set/x0 v%p_%lu, %u, %u;\n", sig, use_word, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); /* save_signal width of 0 CLEARS the signal from the lookaside. */ save_signal_lookaside(bit, sig, use_word, 0); } else if (part_off_ex && ivl_signal_dimensions(sig) > 0) { /* Here we have a part select write into an array word. */ 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); } else { 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", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); } else if ((part_off>0 || ivl_lval_width(lval)!=ivl_signal_width(sig)) && ivl_signal_dimensions(sig) > 0) { /* Here we have a part select write into an array word. */ 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); } else { 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", sig, bit, wid); if (word_ix) /* Only need this label if word_ix is set. */ fprintf(vvp_out, "t_%u ;\n", skip_set); } else if (part_off>0 || ivl_lval_width(lval)!=ivl_signal_width(sig)) { /* There is no mux expression, but a constant part offset. Load that into index x0 and generate a vector set instruction. */ assert(ivl_lval_width(lval) == wid); /* If the word index is a constant, then we can write directly to the word and save the index calculation. */ if (word_ix == 0) { fprintf(vvp_out, " %%ix/load 0, %lu, 0;\n", part_off); fprintf(vvp_out, " %%set/x0 v%p_%lu, %u, %u;\n", sig, use_word, bit, wid); } else { 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", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); } /* save_signal width of 0 CLEARS the signal from the lookaside. */ save_signal_lookaside(bit, sig, use_word, 0); } else if (ivl_signal_dimensions(sig) > 0) { /* If the word index is a constant, then we can write directly to the word and save the index calculation. */ if (word_ix == 0) { 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 { 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", sig, bit, wid); fprintf(vvp_out, "t_%u ;\n", skip_set); } /* save_signal width of 0 CLEARS the signal from the lookaside. */ save_signal_lookaside(bit, sig, use_word, 0); } else { fprintf(vvp_out, " %%set/v v%p_%lu, %u, %u;\n", sig, use_word, bit, wid); /* save_signal width of 0 CLEARS the signal from the lookaside. */ save_signal_lookaside(bit, sig, use_word, 0); } }