/* IVL_LPM_CMP_EEQ/NEE * This LPM node supports two-input compare. The output width is * actually always 1, the lpm_width is the expected width of the inputs. */ static void show_lpm_cmp_eeq(ivl_lpm_t net) { const char*str = (ivl_lpm_type(net) == IVL_LPM_CMP_EEQ)? "EEQ" : "NEE"; unsigned width = ivl_lpm_width(net); fprintf(out, " LPM_CMP_%s %s: <width=%u>\n", str, ivl_lpm_basename(net), width); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " A: %p\n", ivl_lpm_data(net,0)); fprintf(out, " B: %p\n", ivl_lpm_data(net,1)); check_cmp_widths(net); }
/* * The reduction operators have similar characteristics and are * displayed here. */ static void show_lpm_re(ivl_lpm_t net) { ivl_nexus_t nex; const char*type = "?"; unsigned width = ivl_lpm_width(net); switch (ivl_lpm_type(net)) { case IVL_LPM_RE_AND: type = "AND"; break; case IVL_LPM_RE_NAND: type = "NAND"; break; case IVL_LPM_RE_OR: type = "OR"; break; case IVL_LPM_RE_NOR: type = "NOR"; case IVL_LPM_RE_XOR: type = "XOR"; break; case IVL_LPM_RE_XNOR: type = "XNOR"; default: break; } fprintf(out, " LPM_RE_%s: %s <width=%u>\n", type, ivl_lpm_name(net),width); nex = ivl_lpm_q(net); fprintf(out, " Q: %p\n", nex); nex = ivl_lpm_data(net, 0); fprintf(out, " D: %p\n", nex); nex = ivl_lpm_q(net); if (1 != width_of_nexus(nex)) { fprintf(out, " ERROR: Width of Q is %u, expecting 1\n", width_of_nexus(nex)); stub_errors += 1; } nex = ivl_lpm_data(net, 0); if (width != width_of_nexus(nex)) { fprintf(out, " ERROR: Width of input is %u, expecting %u\n", width_of_nexus(nex), width); stub_errors += 1; } }
static void draw_lpm_shiftl(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); const char* signed_flag = ivl_lpm_signed(net)? "s" : ""; const char*dly = draw_lpm_output_delay(net); if (ivl_lpm_type(net) == IVL_LPM_SHIFTR) fprintf(vvp_out, "L_%p%s .shift/r%s %u", net, dly, signed_flag, width); else fprintf(vvp_out, "L_%p%s .shift/l %u", net, dly, width); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net, 0))); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net, 1))); fprintf(vvp_out, ";\n"); }
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 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 lpm_show_add(ivl_lpm_t net) { unsigned idx; unsigned cell_width; char cellname[32]; edif_cell_t cell; edif_cellref_t ref; edif_joint_t jnt; const char*type = "ADD"; if (ivl_lpm_type(net) == IVL_LPM_SUB) type = "SUB"; /* Figure out the width of the cell. Normally, it is the LPM width known by IVL. But if the top data input bits are unconnected, then we really have a width one less, and we can use the cout to fill out the output width. */ cell_width = ivl_lpm_width(net); if ( (ivl_lpm_data(net,cell_width-1) == 0) && (ivl_lpm_datab(net,cell_width-1) == 0) ) cell_width -= 1; /* Find the correct ADD/SUB device in the library, search by name. If the device is not there, then create it and put it in the library. */ sprintf(cellname, "%s%u", type, cell_width); cell = edif_xlibrary_findcell(xlib, cellname); if (cell == 0) { unsigned pins = cell_width * 3 + 1; cell = edif_xcell_create(xlib, strdup(cellname), pins); for (idx = 0 ; idx < cell_width ; idx += 1) { sprintf(cellname, "Result%u", idx); edif_cell_portconfig(cell, idx*3+0, strdup(cellname), IVL_SIP_OUTPUT); sprintf(cellname, "DataA%u", idx); edif_cell_portconfig(cell, idx*3+1, strdup(cellname), IVL_SIP_INPUT); sprintf(cellname, "DataB%u", idx); edif_cell_portconfig(cell, idx*3+2, strdup(cellname), IVL_SIP_INPUT); } edif_cell_portconfig(cell, pins-1, "Cout", IVL_SIP_OUTPUT); edif_cell_pstring(cell, "LPM_Type", "LPM_ADD_SUB"); edif_cell_pstring(cell, "LPM_Direction", type); edif_cell_pinteger(cell, "LPM_Width", ivl_lpm_width(net)); } ref = edif_cellref_create(edf, cell); /* Connect the pins of the instance to the nexa. Access the cell pins by name. */ for (idx = 0 ; idx < cell_width ; idx += 1) { unsigned pin; sprintf(cellname, "Result%u", idx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, ref, pin); sprintf(cellname, "DataA%u", idx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, ref, pin); sprintf(cellname, "DataB%u", idx); pin = edif_cell_port_byname(cell, cellname); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx)); edif_add_to_joint(jnt, ref, pin); } if (cell_width < ivl_lpm_width(net)) { unsigned pin = edif_cell_port_byname(cell, "Cout"); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, cell_width)); edif_add_to_joint(jnt, ref, pin); } }
// Build the design hierarchy. int build_hierarchy(ivl_scope_t scope, void* cd) { int return_code; unsigned i, j; indent(); fprintf(output, "(scope "); quoted_string(ivl_scope_tname(scope)); fprintf(output, " "); quoted_string(ivl_scope_basename(scope)); fprintf(output, " (\n"); // Constants (root scope only) if (! level) for (i = 0; i < ivl_design_consts(design); i++) { ivl_net_const_t constant = ivl_design_const(design, i); const char* bits = ivl_const_bits(constant); unsigned pins = ivl_const_pins(constant); unsigned id = new_id(); indent(); fprintf(output, " (const %i \"", id); for (j = pins - 1; j < pins; j--) fprintf(output, "%c", bits[j]); fprintf(output, "\")\n"); for (j = 0; j < pins; j++) create_bit_select(id_of_nexus(ivl_const_pin(constant, j), 1), pins, j, id); } // Parameters for (i = 0; i < ivl_scope_params(scope); i++) { ivl_parameter_t param = ivl_scope_param(scope, i); ivl_expr_t param_value = ivl_parameter_expr(param); unsigned width = ivl_expr_width(param_value); const char* bits; unsigned id = new_id(); indent(); fprintf(output, " (const %i \"", id); switch (ivl_expr_type(param_value)) { case IVL_EX_STRING : bits = ivl_expr_string(param_value); break; case IVL_EX_NUMBER : bits = ivl_expr_bits(param_value); break; default : fprintf(output, "** ERROR: Unknown parameter type."); return -1; } for (j = width - 1; j < width; j--) fprintf(output, "%c", bits[j]); fprintf(output, "\")\n"); indent(); fprintf(output, " (name %i ", new_id()); quoted_string(ivl_parameter_basename(param)); fprintf(output, " %i %i)\n", width, id); } // Signals for (i = 0; i < ivl_scope_sigs(scope); i++) { ivl_signal_t sig = ivl_scope_sig(scope, i); unsigned pins = ivl_signal_pins(sig); ivl_signal_port_t type = ivl_signal_port(sig); const char* name = ivl_signal_basename(sig); unsigned id; if (! level && type == IVL_SIP_INPUT) { id = new_id(); fprintf(output, " (input %i \"%s\" %i)\n", id, name, pins); for (j = 0; j < pins; j++) create_bit_select(id_of_nexus(ivl_signal_pin(sig, j), 1), pins, j, id); } else if (! level && type == IVL_SIP_INOUT) { printf("** ERROR: Inout ports not supported.\n"); } else if (! level && type == IVL_SIP_OUTPUT) { id = id_of_nexus(ivl_signal_pin(sig, 0), 0); for (j = 1; j < pins; j++) { id = create_bit_concat(id, j, ivl_signal_pin(sig, j)); } fprintf(output, " (output %i \"%s\" %i %i)\n", new_id(), name, pins, id); } else { id = id_of_nexus(ivl_signal_pin(sig, 0), 0); for (j = 1; j < pins; j++) { id = create_bit_concat(id, j, ivl_signal_pin(sig, j)); } indent(); fprintf(output, " (name %i ", new_id()); //XXX Why is "_s22" getting named? quoted_string(name); fprintf(output, " %i %i)\n", pins, id); } } // Logic for (i = 0; i < ivl_scope_logs(scope); i++) { unsigned id; ivl_net_logic_t log = ivl_scope_log(scope, i); switch (ivl_logic_type(log)) { case IVL_LO_BUF: indent(); fprintf(output, " (buf %i 1 %i)\n", id_of_nexus(ivl_logic_pin(log, 0), 1), id_of_nexus(ivl_logic_pin(log, 1), 0)); break; case IVL_LO_NOT: indent(); fprintf(output, " (not %i 1 %i)\n", id_of_nexus(ivl_logic_pin(log, 0), 1), id_of_nexus(ivl_logic_pin(log, 1), 0)); break; case IVL_LO_AND: indent(); create_multi_gate("and ", id_of_nexus(ivl_logic_pin(log, 0), 1), log); break; case IVL_LO_NAND: id = new_id(); indent(); create_multi_gate("and ", id, log); indent(); fprintf(output, " (not %i 1 %i)\n", id_of_nexus(ivl_logic_pin(log, 0), 1), id); break; case IVL_LO_XOR: indent(); create_multi_gate("xor ", id_of_nexus(ivl_logic_pin(log, 0), 1), log); break; case IVL_LO_XNOR: id = new_id(); indent(); create_multi_gate("xor ", id, log); indent(); fprintf(output, " (not %i 1 %i)\n", id_of_nexus(ivl_logic_pin(log, 0), 1), id); break; case IVL_LO_OR: indent(); create_multi_gate("or ", id_of_nexus(ivl_logic_pin(log, 0), 1), log); break; case IVL_LO_NOR: id = new_id(); indent(); create_multi_gate("or ", id, log); indent(); fprintf(output, " (not %i 1 %i)\n", id_of_nexus(ivl_logic_pin(log, 0), 1), id); break; default: printf("** ERROR: Unsupported logic type: %i.\n", ivl_logic_type(log)); return -1; } } // LPMs for (i = 0; i < ivl_scope_lpms(scope); i++) { ivl_lpm_t lpm = ivl_scope_lpm(scope, i); ivl_lpm_type_t lpm_t = ivl_lpm_type(lpm); unsigned width = ivl_lpm_width(lpm); unsigned selects; unsigned size; unsigned id, id1, id2, id3; switch (lpm_t) { case IVL_LPM_ADD: id = new_id(); id1 = create_concat_lpm_data(lpm); id2 = create_concat_lpm_datab(lpm); indent(); fprintf(output, " (add %i %i %i %i)\n", id, width, id1, id2); create_split_lpm_q(lpm, id); break; case IVL_LPM_SUB: id = new_id(); id1 = create_concat_lpm_data(lpm); id2 = create_concat_lpm_datab(lpm); indent(); fprintf(output, " (sub %i %i %i %i)\n", id, width, id1, id2); create_split_lpm_q(lpm, id); break; case IVL_LPM_MULT: id = new_id(); id1 = create_concat_lpm_data(lpm); id2 = create_concat_lpm_datab(lpm); indent(); fprintf(output, " (mul %i %i %i %i)\n", id, width, id1, id2); create_split_lpm_q(lpm, id); break; case IVL_LPM_CMP_EQ: id1 = create_concat_lpm_data(lpm); id2 = create_concat_lpm_datab(lpm); indent(); fprintf(output, " (eq %i %i %i %i)\n", id_of_nexus(ivl_lpm_q(lpm, 0), 1), width, id1, id2); break; case IVL_LPM_CMP_NE: id1 = create_concat_lpm_data(lpm); id2 = create_concat_lpm_datab(lpm); id = new_id(); indent(); fprintf(output, " (eq %i %i %i %i)\n", id, width, id1, id2); indent(); fprintf(output, " (not %i 1 %i)\n", id_of_nexus(ivl_lpm_q(lpm, 0), 1), id); break; case IVL_LPM_CMP_GT: // XXX Check for signed. id1 = create_concat_lpm_data(lpm); id2 = create_concat_lpm_datab(lpm); indent(); fprintf(output, " (lt %i %i %i %i)\n", id_of_nexus(ivl_lpm_q(lpm, 0), 1), width, id2, id1); break; case IVL_LPM_CMP_GE: // XXX Check for signed. id1 = create_concat_lpm_data(lpm); id2 = create_concat_lpm_datab(lpm); id = new_id(); indent(); fprintf(output, " (lt %i %i %i %i)\n", id, width, id1, id2); indent(); fprintf(output, " (not %i 1 %i)\n", id_of_nexus(ivl_lpm_q(lpm, 0), 1), id); break; case IVL_LPM_FF: { ivl_nexus_t async_clr = ivl_lpm_async_clr(lpm); ivl_nexus_t async_set = ivl_lpm_async_set(lpm); ivl_nexus_t sync_clr = ivl_lpm_sync_clr(lpm); ivl_nexus_t sync_set = ivl_lpm_sync_set(lpm); ivl_nexus_t clk = ivl_lpm_clk(lpm); ivl_nexus_t enable = ivl_lpm_enable(lpm); if (async_set || sync_set) { perror("** ERROR: Does not support registers with async or sync sets.\n"); return -1; } id = new_id(); id1 = create_concat_lpm_data(lpm); if (enable) { id2 = new_id(); indent(); fprintf(output, " (mux %i %i %i %i %i)\n", id2, width, id_of_nexus(enable, 0), id, id1); id1 = id2; } if (sync_clr) { id2 = new_id(); id3 = new_id(); indent(); fprintf(output, " (const %i \"", id3); for (j = 0; j < width; j++) fprintf(output, "0"); fprintf(output, "\")\n"); indent(); fprintf(output, " (mux %i %i %i %i %i)\n", id2, width, id_of_nexus(sync_clr, 0), id1, id3); id1 = id2; } // XXX Default to posedge sensitivity. if (async_clr) { indent(); fprintf(output, " (ffc %i %i %i %i %i)\n", id, width, id_of_nexus(async_clr, 0), id_of_nexus(clk, 0), id1); } else { indent(); fprintf(output, " (ff %i %i %i %i)\n", id, width, id_of_nexus(clk, 0), id1); } create_split_lpm_q(lpm, id); } break; case IVL_LPM_MUX: { unsigned t = 1; selects = ivl_lpm_selects(lpm); size = ivl_lpm_size(lpm); for (j = 0; j < selects; j++) t = t * 2; assert(t == size); // General case. id = create_mux(lpm, selects, 0); create_split_lpm_q(lpm, id); } break; default: perror("** ERROR: Unsupported LPM type.\n"); return -1; } } level = level + 1; return_code = ivl_scope_children(scope, build_hierarchy, 0); level = level - 1; if (! level) delete_nexus_table(nexus_table); indent(); fprintf(output, "))\n"); return return_code; }
/* * This function generates ADD/SUB devices for Virtex devices, * based on the documented implementations of ADD8/ADD16, etc., from * the Libraries Guide. * * Each slice of the ADD/SUB device is made from a LUT2 device, an * XORCY device that mixes with the LUT2 to make a full adder, and a * MUXCY_L to propagate the carry. The most significant slice does not * have a carry to propagate, so has no MUXCY_L. * * If the device is a wide adder, then the LUT2 devices are configured * to implement an XOR function and a zero is pumped into the least * significant carry input. * * If the device is really an adder, then the input is turned into an * XNOR, which takes a 1-s complement of the B input. Pump a 1 into * the LSB carry input to finish converting the B input into the 2s * complement. */ void virtex_add(ivl_lpm_t net) { const char*ha_init = 0; edif_cellref_t lut, xorcy, muxcy, pad; edif_joint_t jnt; unsigned idx; if (ivl_lpm_width(net) < 2) { xilinx_add(net); return; } switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: ha_init = "6"; break; case IVL_LPM_SUB: ha_init = "9"; break; default: assert(0); } assert(ivl_lpm_width(net) > 1); lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); xorcy = edif_cellref_create(edf, xilinx_cell_xorcy(xlib)); muxcy = edif_cellref_create(edf, xilinx_cell_muxcy_l(xlib)); edif_cellref_pstring(lut, "INIT", ha_init); /* The bottom carry-in takes a constant that primes the add or subtract. */ switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: pad = edif_cellref_create(edf, cell_0); break; case IVL_LPM_SUB: pad = edif_cellref_create(edf, cell_1); break; default: assert(0); } jnt = edif_joint_create(edf); edif_add_to_joint(jnt, pad, 0); edif_add_to_joint(jnt, muxcy, MUXCY_CI); edif_add_to_joint(jnt, xorcy, XORCY_CI); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, xorcy, XORCY_O); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, xorcy, XORCY_LI); edif_add_to_joint(jnt, muxcy, MUXCY_S); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I0); edif_add_to_joint(jnt, muxcy, MUXCY_DI); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I1); for (idx = 1 ; idx < ivl_lpm_width(net) ; idx += 1) { edif_cellref_t muxcy0 = muxcy; lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); xorcy = edif_cellref_create(edf, xilinx_cell_xorcy(xlib)); edif_cellref_pstring(lut, "INIT", ha_init); /* If this is the last bit, then there is no further propagation in the carry chain, and I can skip the carry mux MUXCY. */ if ((idx+1) < ivl_lpm_width(net)) muxcy = edif_cellref_create(edf, xilinx_cell_muxcy_l(xlib)); else muxcy = 0; jnt = edif_joint_create(edf); edif_add_to_joint(jnt, muxcy0, MUXCY_O); edif_add_to_joint(jnt, xorcy, XORCY_CI); if (muxcy) edif_add_to_joint(jnt, muxcy, MUXCY_CI); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, idx)); edif_add_to_joint(jnt, xorcy, XORCY_O); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, xorcy, XORCY_LI); if (muxcy) edif_add_to_joint(jnt, muxcy, MUXCY_S); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, lut, LUT_I0); if (muxcy) edif_add_to_joint(jnt, muxcy, MUXCY_DI); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx)); edif_add_to_joint(jnt, lut, LUT_I1); } }
/* * This method handles both == and != operators, the identity * comparison operators. * * If the identity compare is applied to small enough input vectors, * it is shoved into a single LUT. Otherwise, it is strung out into a * row of LUT devices chained together by carry muxes. The output of * the comparison is the output of the last mux. * * When the compare is small, a LUT is generated with the appropriate * truth table to cause an == or != result. * * When the compare is too wide for a single LUT, then it is made into * a chain connected by a string of carry mux devices. Each LUT * implements == for up to two pairs of bits, even if the final output * is supposed to be !=. The LUT output is connected to an associated * MUX select input. The CO output of each muxcy is passed up to the * next higher order bits of the compare. * * For identity == compare, a != output from the LUT selects the DI * input of the muxcy, generating a 0 output that is passed up. Since * the next higher muxcy now gets a 0 input to both DI and CI, the * output of the next higher muxcy is guaranteed to be 0, and so on to * the final output of the carry chain. If the output from a LUT is ==, * then the CI input of the muxcy is selected and the truth of this * level depends on lower order bits. The least significant muxcy is * connected to GND and VCC so that its CO follows the least * significant LUT. * * Identity != is the same as == except that the output is * inverted. To get that effect without putting an inverter on the * output of the top muxcy pin CO (which would cost a LUT) the DI * inputs are all connected to VCC instead of GND, and the CI of the * least significant muxcy is connected to GND instead of VCC. The LUT * expressions for the chained compare are configured for ==, with the * changed CI/DI inputs performing the inversion. */ void virtex_eq(ivl_lpm_t net) { edif_cellref_t lut, mux, mux_prev; edif_joint_t jnt, jnt_di; unsigned idx; /* True if I'm implementing CMP_EQ instead of CMP_NE */ int eq = 1; assert(ivl_lpm_width(net) >= 1); if (ivl_lpm_type(net) == IVL_LPM_CMP_NE) eq = 0; switch (ivl_lpm_width(net)) { case 1: lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); edif_cellref_pstring(lut, "INIT", eq? "9" : "6"); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I1); return; case 2: lut = edif_cellref_create(edf, xilinx_cell_lut4(xlib)); edif_cellref_pstring(lut, "INIT", eq? "9009" : "6FF6"); jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, lut, LUT_O); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 0)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 0)); edif_add_to_joint(jnt, lut, LUT_I1); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, 1)); edif_add_to_joint(jnt, lut, LUT_I2); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, 1)); edif_add_to_joint(jnt, lut, LUT_I3); return; default: { edif_cellref_t di; di = edif_cellref_create(edf, eq? cell_0 : cell_1); jnt_di = edif_joint_create(edf); edif_add_to_joint(jnt_di, di, 0); } mux_prev = 0; for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 2) { int subwid = 2; if ((idx + 1) == ivl_lpm_width(net)) subwid = 1; mux = edif_cellref_create(edf, xilinx_cell_muxcy(xlib)); if (subwid == 2) { lut = edif_cellref_create(edf, xilinx_cell_lut4(xlib)); edif_cellref_pstring(lut, "INIT", "9009"); } else { lut = edif_cellref_create(edf, xilinx_cell_lut2(xlib)); edif_cellref_pstring(lut, "INIT", "9"); } jnt = edif_joint_create(edf); edif_add_to_joint(jnt, lut, LUT_O); edif_add_to_joint(jnt, mux, MUXCY_S); jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx)); edif_add_to_joint(jnt, lut, LUT_I0); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx)); edif_add_to_joint(jnt, lut, LUT_I1); if (subwid > 1) { jnt = edif_joint_of_nexus(edf, ivl_lpm_data(net, idx+1)); edif_add_to_joint(jnt, lut, LUT_I2); jnt = edif_joint_of_nexus(edf, ivl_lpm_datab(net, idx+1)); edif_add_to_joint(jnt, lut, LUT_I3); } edif_add_to_joint(jnt_di, mux, MUXCY_DI); if (mux_prev) { jnt = edif_joint_create(edf); edif_add_to_joint(jnt, mux, MUXCY_CI); edif_add_to_joint(jnt, mux_prev, MUXCY_O); } else { edif_cellref_t ci; ci = edif_cellref_create(edf, eq? cell_1 : cell_0); jnt = edif_joint_create(edf); edif_add_to_joint(jnt, ci, 0); edif_add_to_joint(jnt, mux, MUXCY_CI); } mux_prev = mux; } jnt = edif_joint_of_nexus(edf, ivl_lpm_q(net, 0)); edif_add_to_joint(jnt, mux_prev, MUXCY_O); return; } }
/* * The generic == comparator uses EQN records to generate 2-bit * comparators, that are then connected together by a wide AND gate. */ static void generic_show_cmp_eq(ivl_lpm_t net) { ivl_nexus_t nex; unsigned idx; char name[1024]; /* Make this many dual pair comparators, and */ unsigned deqn = ivl_lpm_width(net) / 2; /* Make this many single pair comparators. */ unsigned seqn = ivl_lpm_width(net) % 2; xnf_mangle_lpm_name(net, name, sizeof name); for (idx = 0 ; idx < deqn ; idx += 1) { fprintf(xnf, "SYM, %s/CD%u, EQN, " "EQN=(~((I0 @ I1) + (I2 @ I3)))\n", name, idx); fprintf(xnf, " PIN, O, O, %s/CDO%u\n", name, idx); nex = ivl_lpm_data(net, 2*idx); xnf_draw_pin(nex, "I0", 'I'); nex = ivl_lpm_datab(net, 2*idx); xnf_draw_pin(nex, "I1", 'I'); nex = ivl_lpm_data(net, 2*idx+1); xnf_draw_pin(nex, "I2", 'I'); nex = ivl_lpm_datab(net, 2*idx+1); xnf_draw_pin(nex, "I3", 'I'); fprintf(xnf, "END\n"); } if (seqn != 0) { fprintf(xnf, "SYM, %s/CT, XNOR, LIBVER=2.0.0\n", name); fprintf(xnf, " PIN, O, O, %s/CTO\n", name); nex = ivl_lpm_data(net, 2*deqn); xnf_draw_pin(nex, "I0", 'I'); nex = ivl_lpm_datab(net, 2*deqn); xnf_draw_pin(nex, "I1", 'I'); fprintf(xnf, "END\n"); } if (ivl_lpm_type(net) == IVL_LPM_CMP_EQ) fprintf(xnf, "SYM, %s/OUT, AND, LIBVER=2.0.0\n", name); else fprintf(xnf, "SYM, %s/OUT, NAND, LIBVER=2.0.0\n", name); nex = ivl_lpm_q(net, 0); xnf_draw_pin(nex, "O", 'O'); for (idx = 0 ; idx < deqn ; idx += 1) fprintf(xnf, " PIN, I%u, I, %s/CDO%u\n", idx, name, idx); for (idx = 0 ; idx < seqn ; idx += 1) fprintf(xnf, " PIN, I%u, I, %s/CTO\n", deqn+idx, name); fprintf(xnf, "END\n"); }
static void draw_lpm_in_scope(ivl_lpm_t net) { switch (ivl_lpm_type(net)) { case IVL_LPM_ABS: draw_lpm_abs(net); return; case IVL_LPM_CAST_INT: draw_lpm_cast_int(net); return; case IVL_LPM_CAST_REAL: draw_lpm_cast_real(net); return; case IVL_LPM_ADD: case IVL_LPM_SUB: case IVL_LPM_MULT: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_POW: draw_lpm_add(net); return; case IVL_LPM_ARRAY: draw_lpm_array(net); return; case IVL_LPM_PART_VP: draw_lpm_part(net); return; case IVL_LPM_PART_PV: draw_lpm_part_pv(net); return; case IVL_LPM_CONCAT: draw_lpm_concat(net); return; case IVL_LPM_FF: draw_lpm_ff(net); return; 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: draw_lpm_cmp(net); return; case IVL_LPM_MUX: draw_lpm_mux(net); return; case IVL_LPM_RE_AND: draw_lpm_re(net, "and"); return; case IVL_LPM_RE_OR: draw_lpm_re(net, "or"); return; case IVL_LPM_RE_XOR: draw_lpm_re(net, "xor"); return; case IVL_LPM_RE_NAND: draw_lpm_re(net, "nand"); return; case IVL_LPM_RE_NOR: draw_lpm_re(net, "nor"); return; case IVL_LPM_RE_XNOR: draw_lpm_re(net, "xnor"); return; case IVL_LPM_REPEAT: draw_lpm_repeat(net); return; case IVL_LPM_SHIFTL: case IVL_LPM_SHIFTR: draw_lpm_shiftl(net); return; case IVL_LPM_SIGN_EXT: draw_lpm_sign_ext(net); return; case IVL_LPM_SFUNC: draw_lpm_sfunc(net); return; case IVL_LPM_UFUNC: draw_lpm_ufunc(net); return; default: fprintf(stderr, "XXXX LPM not supported: %s.%s\n", ivl_scope_name(ivl_lpm_scope(net)), ivl_lpm_basename(net)); } }
static void draw_lpm_cmp(ivl_lpm_t net) { const char*src_table[2]; unsigned width; const char*type = ""; const char*signed_string = ivl_lpm_signed(net)? ".s" : ""; ivl_variable_type_t dta = data_type_of_nexus(ivl_lpm_data(net,0)); ivl_variable_type_t dtb = data_type_of_nexus(ivl_lpm_data(net,1)); ivl_variable_type_t dtc = IVL_VT_LOGIC; const char*dly; if (dta == IVL_VT_REAL || dtb == IVL_VT_REAL) dtc = IVL_VT_REAL; width = ivl_lpm_width(net); switch (ivl_lpm_type(net)) { case IVL_LPM_CMP_EEQ: assert(dtc != IVL_VT_REAL); /* Should never get here! */ type = "eeq"; signed_string = ""; break; case IVL_LPM_CMP_EQ: if (dtc == IVL_VT_REAL) type = "eq.r"; else type = "eq"; signed_string = ""; break; case IVL_LPM_CMP_GE: if (dtc == IVL_VT_REAL) { type = "ge.r"; signed_string = ""; } else type = "ge"; break; case IVL_LPM_CMP_GT: if (dtc == IVL_VT_REAL) { type = "gt.r"; signed_string = ""; } else type = "gt"; break; case IVL_LPM_CMP_NE: if (dtc == IVL_VT_REAL) type = "ne.r"; else type = "ne"; signed_string = ""; break; case IVL_LPM_CMP_NEE: assert(dtc != IVL_VT_REAL); /* Should never get here! */ type = "nee"; signed_string = ""; break; default: assert(0); } draw_lpm_data_inputs(net, 0, 2, src_table); dly = draw_lpm_output_delay(net); fprintf(vvp_out, "L_%p%s .cmp/%s%s %u, %s, %s;\n", net, dly, type, signed_string, width, src_table[0], src_table[1]); }
static void draw_lpm_add(ivl_lpm_t net) { const char*src_table[2]; unsigned width; const char*type = ""; ivl_variable_type_t dta = data_type_of_nexus(ivl_lpm_data(net,0)); ivl_variable_type_t dtb = data_type_of_nexus(ivl_lpm_data(net,1)); ivl_variable_type_t dto = IVL_VT_LOGIC; const char*dly; if (dta == IVL_VT_REAL || dtb == IVL_VT_REAL) dto = IVL_VT_REAL; width = ivl_lpm_width(net); switch (ivl_lpm_type(net)) { case IVL_LPM_ADD: if (dto == IVL_VT_REAL) type = "sum.r"; else type = "sum"; break; case IVL_LPM_SUB: if (dto == IVL_VT_REAL) type = "sub.r"; else type = "sub"; break; case IVL_LPM_MULT: if (dto == IVL_VT_REAL) type = "mult.r"; else type = "mult"; break; case IVL_LPM_DIVIDE: if (dto == IVL_VT_REAL) type = "div.r"; else if (ivl_lpm_signed(net)) type = "div.s"; else type = "div"; break; case IVL_LPM_MOD: if (dto == IVL_VT_REAL) type = "mod.r"; else if (ivl_lpm_signed(net)) type = "mod.s"; else type = "mod"; break; case IVL_LPM_POW: if (dto == IVL_VT_REAL) type = "pow.r"; else if (ivl_lpm_signed(net)) type = "pow.s"; else type = "pow"; break; default: assert(0); } draw_lpm_data_inputs(net, 0, 2, src_table); dly = draw_lpm_output_delay(net); fprintf(vvp_out, "L_%p%s .arith/%s %u, %s, %s;\n", net, dly, type, width, src_table[0], src_table[1]); }
static void show_lpm(ivl_lpm_t net) { switch (ivl_lpm_type(net)) { case IVL_LPM_ABS: show_lpm_abs(net); break; case IVL_LPM_ADD: show_lpm_add(net); break; case IVL_LPM_ARRAY: show_lpm_array(net); break; case IVL_LPM_CAST_INT: show_lpm_cast_int(net); break; case IVL_LPM_CAST_REAL: show_lpm_cast_real(net); break; case IVL_LPM_DIVIDE: show_lpm_divide(net); break; case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_NEE: show_lpm_cmp_eeq(net); break; case IVL_LPM_FF: show_lpm_ff(net); break; case IVL_LPM_CMP_GE: show_lpm_cmp_ge(net); break; case IVL_LPM_CMP_GT: show_lpm_cmp_gt(net); break; case IVL_LPM_CMP_NE: show_lpm_cmp_ne(net); break; case IVL_LPM_CONCAT: show_lpm_concat(net); break; case IVL_LPM_RE_AND: case IVL_LPM_RE_NAND: case IVL_LPM_RE_NOR: case IVL_LPM_RE_OR: case IVL_LPM_RE_XOR: case IVL_LPM_RE_XNOR: show_lpm_re(net); break; case IVL_LPM_SHIFTL: show_lpm_shift(net, "L"); break; case IVL_LPM_SIGN_EXT: show_lpm_sign_ext(net); break; case IVL_LPM_SHIFTR: show_lpm_shift(net, "R"); break; case IVL_LPM_SUB: show_lpm_sub(net); break; case IVL_LPM_MOD: show_lpm_mod(net); break; case IVL_LPM_MULT: show_lpm_mult(net); break; case IVL_LPM_MUX: show_lpm_mux(net); break; case IVL_LPM_PART_VP: case IVL_LPM_PART_PV: show_lpm_part(net); break; case IVL_LPM_REPEAT: show_lpm_repeat(net); break; case IVL_LPM_SFUNC: show_lpm_sfunc(net); break; case IVL_LPM_UFUNC: show_lpm_ufunc(net); break; default: fprintf(out, " LPM(%d) %s: <width=%u, signed=%d>\n", ivl_lpm_type(net), ivl_lpm_basename(net), ivl_lpm_width(net), ivl_lpm_signed(net)); } }
static void show_lpm_part(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); unsigned base = ivl_lpm_base(net); ivl_nexus_t sel = ivl_lpm_data(net,1); const char*part_type_string = ""; switch (ivl_lpm_type(net)) { case IVL_LPM_PART_VP: part_type_string = "VP"; break; case IVL_LPM_PART_PV: part_type_string = "PV"; break; default: break; } fprintf(out, " LPM_PART_%s %s: <width=%u, base=%u, signed=%d>\n", part_type_string, ivl_lpm_basename(net), width, base, ivl_lpm_signed(net)); fprintf(out, " O: %p\n", ivl_lpm_q(net)); fprintf(out, " I: %p\n", ivl_lpm_data(net,0)); if (sel != 0) { fprintf(out, " S: %p\n", sel); if (base != 0) { fprintf(out, " ERROR: Part select has base AND selector\n"); stub_errors += 1; } } /* The compiler must assure that the base plus the part select width fits within the input to the part select. */ switch (ivl_lpm_type(net)) { case IVL_LPM_PART_VP: if (width_of_nexus(ivl_lpm_data(net,0)) < (width+base)) { fprintf(out, " ERROR: Part select is out of range." " Data nexus width=%u, width+base=%u\n", width_of_nexus(ivl_lpm_data(net,0)), width+base); stub_errors += 1; } if (width_of_nexus(ivl_lpm_q(net)) != width) { fprintf(out, " ERROR: Part select input mismatch." " Nexus width=%u, expect width=%u\n", width_of_nexus(ivl_lpm_q(net)), width); stub_errors += 1; } break; case IVL_LPM_PART_PV: if (width_of_nexus(ivl_lpm_q(net)) < (width+base)) { fprintf(out, " ERROR: Part select is out of range." " Target nexus width=%u, width+base=%u\n", width_of_nexus(ivl_lpm_q(net)), width+base); stub_errors += 1; } if (width_of_nexus(ivl_lpm_data(net,0)) != width) { fprintf(out, " ERROR: Part select input mismatch." " Nexus width=%u, expect width=%u\n", width_of_nexus(ivl_lpm_data(net,0)), width); stub_errors += 1; } break; default: assert(0); } }