static void draw_udp_in_scope(ivl_net_logic_t lptr) { unsigned pdx; ivl_udp_t udp = ivl_logic_udp(lptr); static ivl_udp_t *udps = 0x0; static int nudps = 0; int i; for (i=0; i<nudps; i++) if (udps[i] == udp) break; if (i >= nudps) { udps = realloc(udps, (nudps+1)*sizeof(ivl_udp_t)); assert(udps); udps[nudps++] = udp; draw_udp_def(udp); } fprintf(vvp_out, "L_%p .udp", lptr); fprintf(vvp_out, " UDP_%s", vvp_mangle_id(ivl_udp_name(udp))); draw_delay(lptr); for (pdx = 1 ; pdx < ivl_logic_pins(lptr) ; pdx += 1) { ivl_nexus_t nex = ivl_logic_pin(lptr, pdx); /* Unlike other logic gates, primitives may have unconnected inputs. The proper behavior is to attach a HiZ to the port. */ if (nex == 0) { assert(ivl_logic_width(lptr) == 1); fprintf(vvp_out, ", C4<z>"); } else { fprintf(vvp_out, ", %s", draw_net_input(nex)); } } fprintf(vvp_out, ";\n"); }
/* * This function draws a BUFT to drive a net pullup or pulldown value. * If the drive strength is strong we can draw a C4<> constant as the * pull value, otherwise we need to draw a C8<> constant. */ static char* draw_net_pull(ivl_net_logic_t lptr, ivl_drive_t drive, const char*level) { char*result; char tmp[32]; if (drive == 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, level, 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"[drive]; val[1] = val[0]; val[2] = level[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); } /* 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; }
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>"); }
/* * All logic gates have inputs and outputs that match exactly in * width. For example, and AND gate with 4 bit inputs generates a 4 * bit output, and all the inputs are 4 bits. */ static void show_logic(ivl_net_logic_t net) { unsigned npins, idx; const char*name = ivl_logic_basename(net); ivl_drive_t drive0 = ivl_logic_drive0(net); ivl_drive_t drive1 = ivl_logic_drive1(net); switch (ivl_logic_type(net)) { case IVL_LO_AND: fprintf(out, " and %s", name); break; case IVL_LO_BUF: fprintf(out, " buf %s", name); break; case IVL_LO_BUFIF0: fprintf(out, " bufif0 %s", name); break; case IVL_LO_BUFIF1: fprintf(out, " bufif1 %s", name); break; case IVL_LO_BUFT: fprintf(out, " buft %s", name); break; case IVL_LO_BUFZ: fprintf(out, " bufz %s", name); break; case IVL_LO_CMOS: fprintf(out, " cmos %s", name); break; case IVL_LO_NAND: fprintf(out, " nand %s", name); break; case IVL_LO_NMOS: fprintf(out, " nmos %s", name); break; case IVL_LO_NOR: fprintf(out, " nor %s", name); break; case IVL_LO_NOT: fprintf(out, " not %s", name); break; case IVL_LO_NOTIF0: fprintf(out, " notif0 %s", name); break; case IVL_LO_NOTIF1: fprintf(out, " notif1 %s", name); break; case IVL_LO_OR: fprintf(out, " or %s", name); break; case IVL_LO_PMOS: fprintf(out, " pmos %s", name); break; case IVL_LO_PULLDOWN: fprintf(out, " pulldown %s", name); break; case IVL_LO_PULLUP: fprintf(out, " pullup %s", name); break; case IVL_LO_RCMOS: fprintf(out, " rcmos %s", name); break; case IVL_LO_RNMOS: fprintf(out, " rnmos %s", name); break; case IVL_LO_RPMOS: fprintf(out, " rpmos %s", name); break; case IVL_LO_XNOR: fprintf(out, " xnor %s", name); break; case IVL_LO_XOR: fprintf(out, " xor %s", name); break; case IVL_LO_UDP: fprintf(out, " primitive<%s> %s", ivl_udp_name(ivl_logic_udp(net)), name); break; default: fprintf(out, " unsupported gate<type=%d> %s", ivl_logic_type(net), name); break; } fprintf(out, " <width=%u>\n", ivl_logic_width(net)); fprintf(out, " <Delays...>\n"); if (ivl_logic_delay(net,0)) { test_expr_is_delay(ivl_logic_delay(net,0)); show_expression(ivl_logic_delay(net,0), 6); } if (ivl_logic_delay(net,1)) { test_expr_is_delay(ivl_logic_delay(net,1)); show_expression(ivl_logic_delay(net,1), 6); } if (ivl_logic_delay(net,2)) { test_expr_is_delay(ivl_logic_delay(net,2)); show_expression(ivl_logic_delay(net,2), 6); } npins = ivl_logic_pins(net); /* Show the pins of the gate. Pin-0 is always the output, and the remaining pins are the inputs. Inputs may be unconnected, but if connected the nexus width must exactly match the gate width. */ for (idx = 0 ; idx < npins ; idx += 1) { ivl_nexus_t nex = ivl_logic_pin(net, idx); fprintf(out, " %d: %p", idx, nex); if (idx == 0) fprintf(out, " <drive0/1 = %u/%u>", drive0, drive1); fprintf(out, "\n"); if (nex == 0) { if (idx == 0) { fprintf(out, " 0: ERROR: Pin 0 must not " "be unconnected\n"); stub_errors += 1; } continue; } if (ivl_logic_width(net) != width_of_nexus(nex)) { fprintf(out, " %d: ERROR: Nexus width is %u\n", idx, width_of_nexus(nex)); stub_errors += 1; } } /* If this is an instance of a UDP, then check that the instantiation is consistent with the definition. */ if (ivl_logic_type(net) == IVL_LO_UDP) { ivl_udp_t udp = ivl_logic_udp(net); if (npins != 1+ivl_udp_nin(udp)) { fprintf(out, " ERROR: UDP %s expects %u inputs\n", ivl_udp_name(udp), ivl_udp_nin(udp)); stub_errors += 1; } /* Add a reference to this udp definition. */ reference_udp_definition(udp); } npins = ivl_logic_attr_cnt(net); for (idx = 0 ; idx < npins ; idx += 1) { ivl_attribute_t cur = ivl_logic_attr_val(net,idx); switch (cur->type) { case IVL_ATT_VOID: fprintf(out, " %s\n", cur->key); break; case IVL_ATT_NUM: fprintf(out, " %s = %ld\n", cur->key, cur->val.num); break; case IVL_ATT_STR: fprintf(out, " %s = %s\n", cur->key, cur->val.str); break; } } }