diff --git a/tgt-stub/switches.c b/tgt-stub/switches.c index 79dac554a..6f6d3c9f9 100644 --- a/tgt-stub/switches.c +++ b/tgt-stub/switches.c @@ -54,7 +54,7 @@ void show_switch(ivl_switch_t net) break; } - fprintf(out, "\n"); + fprintf(out, " island=%p\n", ivl_switch_island(net)); ivl_nexus_t nex = ivl_switch_a(net); const char*nex_name = nex? ivl_nexus_name(nex) : ""; diff --git a/tgt-vvp/Makefile.in b/tgt-vvp/Makefile.in index 79664e616..323212332 100644 --- a/tgt-vvp/Makefile.in +++ b/tgt-vvp/Makefile.in @@ -51,7 +51,8 @@ dep: $(CC) $(CPPFLAGS) $(CFLAGS) -MD -c $< -o $*.o mv $*.d dep -O = vvp.o draw_mux.o draw_switch.o draw_ufunc.o draw_vpi.o eval_bool.o eval_expr.o \ +O = vvp.o draw_mux.o draw_net_input.o draw_switch.o draw_ufunc.o draw_vpi.o \ +eval_bool.o eval_expr.o \ eval_real.o modpath.o vector.o \ vvp_process.o vvp_scope.o diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c new file mode 100644 index 000000000..03707be13 --- /dev/null +++ b/tgt-vvp/draw_net_input.c @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2001-2008 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "vvp_priv.h" +#ifdef HAVE_MALLOC_H +# include +#endif +# include +# include +# include +# include +# include + +#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */ +#define snprintf _snprintf +#endif + +static ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex) +{ + unsigned idx; + ivl_signal_type_t out = IVL_SIT_TRI; + + for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { + ivl_signal_type_t stype; + ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); + ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); + if (sig == 0) + continue; + + stype = ivl_signal_type(sig); + if (stype == IVL_SIT_REG) + continue; + if (stype == IVL_SIT_TRI) + continue; + if (stype == IVL_SIT_NONE) + continue; + out = stype; + } + + return out; +} + +static void draw_C4_repeated_constant(char bit_char, unsigned width) +{ + unsigned idx; + + fprintf(vvp_out, "C4<"); + for (idx = 0 ; idx < width ; idx += 1) + fprintf(vvp_out, "%c", bit_char); + + fprintf(vvp_out, ">"); +} + +static char* draw_C4_to_string(ivl_net_const_t cptr) +{ + const char*bits = ivl_const_bits(cptr); + unsigned idx; + + size_t result_len = 5 + ivl_const_width(cptr); + char*result = malloc(result_len); + char*dp = result; + strcpy(dp, "C4<"); + dp += strlen(dp); + + for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { + char bitchar = bits[ivl_const_width(cptr)-idx-1]; + *dp++ = bitchar; + assert((dp - result) < result_len); + } + + strcpy(dp, ">"); + return result; +} + +static char* draw_C8_to_string(ivl_net_const_t cptr, + ivl_drive_t dr0, ivl_drive_t dr1) +{ + size_t nresult = 5 + 3*ivl_const_width(cptr); + char*result = malloc(nresult); + const char*bits = ivl_const_bits(cptr); + unsigned idx; + + char dr0c = "01234567"[dr0]; + char dr1c = "01234567"[dr1]; + char*dp = result; + + strcpy(dp, "C8<"); + dp += strlen(dp); + + for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { + switch (bits[ivl_const_width(cptr)-idx-1]) { + case '0': + *dp++ = dr0c; + *dp++ = dr0c; + *dp++ = '0'; + break; + case '1': + *dp++ = dr1c; + *dp++ = dr1c; + *dp++ = '1'; + break; + case 'x': + case 'X': + *dp++ = dr0c; + *dp++ = dr1c; + *dp++ = 'x'; + break; + case 'z': + case 'Z': + *dp++ = '0'; + *dp++ = '0'; + *dp++ = 'z'; + break; + default: + assert(0); + break; + } + assert(dp - result < nresult); + } + + strcpy(dp, ">"); + return result; +} + +static struct vvp_nexus_data*new_nexus_data() +{ + struct vvp_nexus_data*data = calloc(1, sizeof(struct vvp_nexus_data)); + return data; +} + +static int nexus_drive_is_strength_aware(ivl_nexus_ptr_t nptr) +{ + if (ivl_nexus_ptr_drive0(nptr) != IVL_DR_STRONG) + return 1; + if (ivl_nexus_ptr_drive1(nptr) != IVL_DR_STRONG) + return 1; + + ivl_net_logic_t log = ivl_nexus_ptr_log(nptr); + if (log != 0) { + /* These logic gates are able to generate unusual + strength values and so their outputs are considered + strength aware. */ + if (ivl_logic_type(log) == IVL_LO_BUFIF0) + return 1; + if (ivl_logic_type(log) == IVL_LO_BUFIF1) + return 1; + if (ivl_logic_type(log) == IVL_LO_PMOS) + return 1; + if (ivl_logic_type(log) == IVL_LO_NMOS) + return 1; + if (ivl_logic_type(log) == IVL_LO_CMOS) + return 1; + } + + return 0; +} + +/* + * Given a nexus, look for a signal that has module delay + * paths. Return that signal. (There should be no more than 1.) If we + * don't find any, then return nil. + */ +static ivl_signal_t find_modpath(ivl_nexus_t nex) +{ + unsigned idx; + for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { + ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx); + ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); + if (sig == 0) + continue; + if (ivl_signal_npath(sig) == 0) + continue; + + return sig; + } + + return 0; +} + +static void str_repeat(char*buf, const char*str, unsigned rpt) +{ + unsigned idx; + size_t len = strlen(str); + for (idx = 0 ; idx < rpt ; idx += 1) { + strcpy(buf, str); + buf += len; + } +} + +/* + * This function takes a nexus and looks for an input functor. It then + * draws to the output a string that represents that functor. What we + * are trying to do here is find the input to the net that is attached + * to this nexus. + */ + +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) && (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) <= 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) <= result_len); + return result; + } + } + + if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) { + if (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG) { + size_t result_len = 5 + ivl_logic_width(lptr); + char*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) <= result_len); + return result; + } else { + char val[4]; + size_t result_len = 5 + 3*ivl_logic_width(lptr); + char*result = malloc(result_len); + char*dp = result; + + val[0] = "01234567"[ivl_nexus_ptr_drive0(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) <= result_len); + 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_array_count(sptr) > 1) { + 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) { + /* Constants should have exactly 1 pin, with a literal value. */ + assert(nptr_pin == 0); + char *result = 0; + + switch (ivl_const_type(cptr)) { + case IVL_VT_LOGIC: + case IVL_VT_BOOL: + 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)); + } + break; + + case IVL_VT_REAL: + result = draw_Cr_to_string(ivl_const_real(cptr)); + break; + + default: + assert(0); + break; + } + + ivl_expr_t d_rise = ivl_const_delay(cptr, 0); + ivl_expr_t d_fall = ivl_const_delay(cptr, 1); + ivl_expr_t d_decay = ivl_const_delay(cptr, 2); + + /* We have a delayed constant, so we need to build some code. */ + if (d_rise != 0) { + assert(number_is_immediate(d_rise, 64)); + assert(number_is_immediate(d_fall, 64)); + assert(number_is_immediate(d_decay, 64)); + + fprintf(vvp_out, "L_%p/d .functor BUFZ 1, %s, " + "C4<0>, C4<0>, C4<0>;\n", cptr, result); + + fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", + cptr, get_number_immediate(d_rise), + get_number_immediate(d_rise), + get_number_immediate(d_rise), cptr); + + free(result); + char tmp[128]; + 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_CONCAT: + 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, 0) == nex) { + char tmp[128]; + snprintf(tmp, sizeof tmp, "L_%p", lpm); + return strdup(tmp); + } + break; + + case IVL_LPM_PART_BI: + if (ivl_lpm_q(lpm, 0) == nex) { + char tmp[128]; + snprintf(tmp, sizeof tmp, "L_%p/P", lpm); + return strdup(tmp); + } else if (ivl_lpm_data(lpm,0) == nex) { + char tmp[128]; + snprintf(tmp, sizeof tmp, "L_%p/V", lpm); + return strdup(tmp); + } + break; + } + + fprintf(stderr, "internal error: no input to nexus %s\n", + ivl_nexus_name(nex)); + assert(0); + return strdup("C"); +} + +static char* draw_island_port(ivl_island_t island, ivl_nexus_t nex, const char*src) +{ + char result[64]; + if (ivl_island_flag_test(island,0) == 0) { + fprintf(vvp_out, "I%p .island tran;\n", island); + ivl_island_flag_set(island,0,1); + } + + fprintf(vvp_out, "p%p .port I%p, %s;\n", nex, island, src); + snprintf(result, sizeof result, "p%p", nex); + return strdup(result); +} + +/* + * This function draws the input to a net into a string. What that + * means is that it returns a static string that can be used to + * represent a resolved driver to a nexus. If there are multiple + * drivers to the nexus, then it writes out the resolver declarations + * needed to perform strength resolution. + * + * The string that this returns is malloced, and that means that the + * caller must free the string or store it permanently. This function + * does *not* check for a previously calculated string. Use the + * draw_net_input for the general case. + */ + /* Omit LPMPART_BI device pin-data(0) drivers. */ +# define OMIT_PART_BI_DATA 0x0001 + +char* draw_net_input_x(ivl_nexus_t nex, + ivl_nexus_ptr_t omit_ptr, int omit_flags, + struct vvp_nexus_data*nex_data) +{ + ivl_island_t island = 0; + ivl_signal_type_t res; + char result[512]; + unsigned idx; + int level; + unsigned ndrivers = 0; + static ivl_nexus_ptr_t *drivers = 0x0; + static unsigned adrivers = 0; + + const char*resolv_type; + + char*nex_private = 0; + + /* Accumulate nex_data flags. */ + int nex_flags = 0; + + res = signal_type_of_nexus(nex); + switch (res) { + case IVL_SIT_TRI: + resolv_type = "tri"; + break; + case IVL_SIT_TRI0: + resolv_type = "tri0"; + nex_flags |= VVP_NEXUS_DATA_STR; + break; + case IVL_SIT_TRI1: + resolv_type = "tri1"; + nex_flags |= VVP_NEXUS_DATA_STR; + break; + case IVL_SIT_TRIAND: + resolv_type = "triand"; + break; + case IVL_SIT_TRIOR: + resolv_type = "trior"; + break; + default: + fprintf(stderr, "vvp.tgt: Unsupported signal type: %u\n", res); + assert(0); + resolv_type = "tri"; + break; + } + + + for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { + ivl_lpm_t lpm_tmp; + ivl_switch_t sw; + ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx); + + /* If this object is part of an island, then we'll be + making a port. Save the island cookie. */ + if ( (sw = ivl_nexus_ptr_switch(nptr)) ) + island = ivl_switch_island(sw); + + /* If we are supposed to skip LPM_PART_BI data pins, + check that this driver is that. */ + if ((omit_flags&OMIT_PART_BI_DATA) + && (lpm_tmp = ivl_nexus_ptr_lpm(nptr)) + && (nex == ivl_lpm_data(lpm_tmp,0))) + continue; + + if (nptr == omit_ptr) + continue; + + /* Skip input only pins. */ + if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_HiZ) + && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_HiZ)) + continue; + + /* Mark the strength-aware flag if the driver can + generate values other than the standard "6" + strength. */ + if (nexus_drive_is_strength_aware(nptr)) + nex_flags |= VVP_NEXUS_DATA_STR; + + /* Save this driver. */ + if (ndrivers >= adrivers) { + adrivers += 4; + drivers = realloc(drivers, adrivers*sizeof(ivl_nexus_ptr_t)); + assert(drivers); + } + drivers[ndrivers] = nptr; + ndrivers += 1; + } + + /* If the caller is collecting nexus information, then save + the nexus driver count in the nex_data. */ + if (nex_data) { + nex_data->drivers_count = ndrivers; + nex_data->flags |= nex_flags; + } + + /* If the nexus has no drivers, then send a constant HiZ into + the net. */ + if (ndrivers == 0) { + unsigned idx, wid = width_of_nexus(nex); + char*tmp = malloc(wid + 5); + nex_private = tmp; + strcpy(tmp, "C4<"); + tmp += strlen(tmp); + switch (res) { + case IVL_SIT_TRI: + for (idx = 0 ; idx < wid ; idx += 1) + *tmp++ = 'z'; + break; + case IVL_SIT_TRI0: + for (idx = 0 ; idx < wid ; idx += 1) + *tmp++ = '0'; + break; + case IVL_SIT_TRI1: + for (idx = 0 ; idx < wid ; idx += 1) + *tmp++ = '1'; + break; + default: + assert(0); + } + *tmp++ = '>'; + *tmp = 0; + return nex_private; + } + + + /* If the nexus has exactly one driver, then simply draw + it. Note that this will *not* work if the nexus is not a + TRI type nexus. */ + if (ndrivers == 1 && res == IVL_SIT_TRI) { + ivl_signal_t path_sig = find_modpath(nex); + if (path_sig) { + char*nex_str = draw_net_input_drive(nex, drivers[0]); + char modpath_label[64]; + snprintf(modpath_label, sizeof modpath_label, + "V_%p/m", path_sig); + nex_private = strdup(modpath_label); + draw_modpath(path_sig, nex_str); + + } else { + nex_private = draw_net_input_drive(nex, drivers[0]); + } + if (island) { + char*tmp = draw_island_port(island, nex, nex_private); + free(nex_private); + nex_private = tmp; + } + return nex_private; + } + + level = 0; + while (ndrivers) { + unsigned int inst; + for (inst = 0; inst < ndrivers; inst += 4) { + if (ndrivers > 4) + fprintf(vvp_out, "RS_%p/%d/%d .resolv tri", + nex, level, inst); + else + fprintf(vvp_out, "RS_%p .resolv %s", + nex, resolv_type); + + for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) { + if (level) { + fprintf(vvp_out, ", RS_%p/%d/%d", + nex, level - 1, idx*4); + } else { + char*drive = draw_net_input_drive(nex, drivers[idx]); + fprintf(vvp_out, ", %s", drive); + free(drive); + } + } + for ( ; idx < inst+4 ; idx += 1) { + fprintf(vvp_out, ", "); + draw_C4_repeated_constant('z',width_of_nexus(nex)); + } + + fprintf(vvp_out, ";\n"); + } + if (ndrivers > 4) + ndrivers = (ndrivers+3) / 4; + else + ndrivers = 0; + level += 1; + } + + snprintf(result, sizeof result, "RS_%p", nex); + + if (island) + nex_private = draw_island_port(island, nex, result); + else + nex_private = strdup(result); + + return nex_private; +} + +/* + * Get a cached description of the nexus input, or create one if this + * nexus has not been cached yet. This is a wrapper for the common + * case call to draw_net_input_x. + */ +const char*draw_net_input(ivl_nexus_t nex) +{ + struct vvp_nexus_data*nex_data = (struct vvp_nexus_data*) + ivl_nexus_get_private(nex); + + /* If this nexus already has a label, then its input is + already figured out. Just return the existing label. */ + if (nex_data && nex_data->net_input) + return nex_data->net_input; + + if (nex_data == 0) { + nex_data = new_nexus_data(); + ivl_nexus_set_private(nex, nex_data); + } + + assert(nex_data->net_input == 0); + nex_data->net_input = draw_net_input_x(nex, 0, 0, nex_data); + + return nex_data->net_input; +} + diff --git a/tgt-vvp/draw_switch.c b/tgt-vvp/draw_switch.c index 0cbc88e3d..93f3ff13a 100644 --- a/tgt-vvp/draw_switch.c +++ b/tgt-vvp/draw_switch.c @@ -25,9 +25,52 @@ # include # include +static void draw_tran_island(ivl_island_t island) +{ + fprintf(vvp_out, "I%p .island tran;\n", island); + ivl_island_flag_set(island, 0, 1); +} + void draw_switch_in_scope(ivl_switch_t sw) { - fprintf(stderr, "%s:%u: sorry: vvp target does not support switch modeling.\n", - ivl_switch_file(sw), ivl_switch_lineno(sw)); - vvp_errors += 1; + ivl_island_t island = ivl_switch_island(sw); + if (ivl_island_flag_test(island, 0) == 0) + draw_tran_island(island); + + ivl_nexus_t nex_a = ivl_switch_a(sw); + assert(nex_a); + const char*str_a = draw_net_input(nex_a); + + ivl_nexus_t nex_b = ivl_switch_b(sw); + assert(nex_b); + const char*str_b = draw_net_input(nex_b); + + ivl_nexus_t enable = ivl_switch_enable(sw); + const char*str_e = 0; + if (enable) + str_e = draw_net_input(enable); + + + switch (ivl_switch_type(sw)) { + case IVL_SW_TRAN: + fprintf(vvp_out, " .tran "); + break; + case IVL_SW_TRANIF0: + fprintf(vvp_out, " .tranif0 "); + break; + case IVL_SW_TRANIF1: + fprintf(vvp_out, " .tranif1 "); + break; + default: + fprintf(stderr, "%s:%u: sorry: vvp target does not support switch modeling.\n", + ivl_switch_file(sw), ivl_switch_lineno(sw)); + vvp_errors += 1; + return; + } + + fprintf(vvp_out, " I%p, %s %s", island, str_a, str_b); + if (enable) + fprintf(vvp_out, ", %s", str_e); + + fprintf(vvp_out, ";\n"); } diff --git a/tgt-vvp/vvp_priv.h b/tgt-vvp/vvp_priv.h index 0ccf36343..cb4406bf3 100644 --- a/tgt-vvp/vvp_priv.h +++ b/tgt-vvp/vvp_priv.h @@ -1,7 +1,7 @@ #ifndef __vvp_priv_H #define __vvp_priv_H /* - * Copyright (c) 2001-2005 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -51,6 +51,8 @@ struct vector_info { extern const char *vvp_mangle_id(const char *); extern const char *vvp_mangle_name(const char *); +extern char* draw_Cr_to_string(double value); + /* * This generates a string from a signal that uniquely identifies * that signal with letters that can be used in a label. @@ -65,6 +67,8 @@ extern const char* vvp_signal_label(ivl_signal_t sig); extern unsigned width_of_nexus(ivl_nexus_t nex); extern ivl_variable_type_t data_type_of_nexus(ivl_nexus_t nex); +extern int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr); + /* * This function draws a process (initial or always) into the output * file. It normally returns 0, but returns !0 of there is some sort @@ -115,6 +119,19 @@ extern int draw_vpi_rfunc_call(ivl_expr_t exp); */ extern void draw_switch_in_scope(ivl_switch_t sw); +/* Draw_net_input and friends uses this. */ +struct vvp_nexus_data { + /* draw_net_input uses this */ + const char*net_input; + unsigned drivers_count; + int flags; + /* draw_net_in_scope uses these to identify the controlling word. */ + ivl_signal_t net; + unsigned net_word; +}; +#define VVP_NEXUS_DATA_STR 0x0001 + + /* * Given a nexus, draw a string that represents the functor output * that feeds the nexus. This function can be used to get the input to @@ -125,6 +142,17 @@ extern void draw_switch_in_scope(ivl_switch_t sw); */ extern const char* draw_net_input(ivl_nexus_t nex); +/* + * See draw_net_input.c for details on draw_net_input_x. (It would be + * nice if this can be made private.) + */ + /* Omit LPMPART_BI device pin-data(0) drivers. */ +# define OMIT_PART_BI_DATA 0x0001 +struct vvp_nexus_data; +extern char* draw_net_input_x(ivl_nexus_t nex, + ivl_nexus_ptr_t omit_ptr, int omit_flags, + struct vvp_nexus_data*nex_data); + /* * This function is different from draw_net_input in that it will * return a reference to a net as its first choice. This reference diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 9499efe4d..9a18239a2 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -31,23 +31,6 @@ #define snprintf _snprintf #endif -struct vvp_nexus_data { - /* draw_net_input uses this */ - const char*net_input; - unsigned drivers_count; - int flags; - /* draw_net_in_scope uses these to identify the controlling word. */ - ivl_signal_t net; - unsigned net_word; -}; -#define VVP_NEXUS_DATA_STR 0x0001 - - -static struct vvp_nexus_data*new_nexus_data() -{ - struct vvp_nexus_data*data = calloc(1, sizeof(struct vvp_nexus_data)); - return data; -} /* * Escape non-symbol characters in ids, and quotes in strings. @@ -147,27 +130,6 @@ const char *vvp_mangle_name(const char *id) return out; } -static void draw_C4_repeated_constant(char bit_char, unsigned width) -{ - unsigned idx; - - fprintf(vvp_out, "C4<"); - for (idx = 0 ; idx < width ; idx += 1) - fprintf(vvp_out, "%c", bit_char); - - fprintf(vvp_out, ">"); -} - -static void str_repeat(char*buf, const char*str, unsigned rpt) -{ - unsigned idx; - size_t len = strlen(str); - for (idx = 0 ; idx < rpt ; idx += 1) { - strcpy(buf, str); - buf += len; - } -} - /* REMOVE ME: vvp_signal_label should not be used. DEAD CODE * Given a signal, generate a string name that is suitable for use as * a label. The only rule is that the same signal will always have the @@ -198,31 +160,6 @@ ivl_signal_t signal_of_nexus(ivl_nexus_t nex, unsigned*word) return 0; } -ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex) -{ - unsigned idx; - ivl_signal_type_t out = IVL_SIT_TRI; - - for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { - ivl_signal_type_t stype; - ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx); - ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); - if (sig == 0) - continue; - - stype = ivl_signal_type(sig); - if (stype == IVL_SIT_REG) - continue; - if (stype == IVL_SIT_TRI) - continue; - if (stype == IVL_SIT_NONE) - continue; - out = stype; - } - - return out; -} - unsigned width_of_nexus(ivl_nexus_t nex) { unsigned idx; @@ -332,7 +269,7 @@ const char*drive_string(ivl_drive_t drive) * gate will be generated for this node. Otherwise, the code generator * will connect its input to its output and skip the gate. */ -static int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr) +int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr) { ivl_nexus_t in_n; unsigned idx; @@ -380,100 +317,7 @@ static int can_elide_bufz(ivl_net_logic_t net, ivl_nexus_ptr_t nptr) return 1; } -/* - * Given a nexus, look for a signal that has module delay - * paths. Return that signal. (There should be no more than 1.) If we - * don't find any, then return nil. - */ -static ivl_signal_t find_modpath(ivl_nexus_t nex) -{ - unsigned idx; - for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { - ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex,idx); - ivl_signal_t sig = ivl_nexus_ptr_sig(ptr); - if (sig == 0) - continue; - if (ivl_signal_npath(sig) == 0) - continue; - - return sig; - } - - return 0; -} - -static char* draw_C4_to_string(ivl_net_const_t cptr) -{ - const char*bits = ivl_const_bits(cptr); - unsigned idx; - - size_t result_len = 5 + ivl_const_width(cptr); - char*result = malloc(result_len); - char*dp = result; - strcpy(dp, "C4<"); - dp += strlen(dp); - - for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { - char bitchar = bits[ivl_const_width(cptr)-idx-1]; - *dp++ = bitchar; - assert((dp - result) < result_len); - } - - strcpy(dp, ">"); - return result; -} - -static char* draw_C8_to_string(ivl_net_const_t cptr, - ivl_drive_t dr0, ivl_drive_t dr1) -{ - size_t nresult = 5 + 3*ivl_const_width(cptr); - char*result = malloc(nresult); - const char*bits = ivl_const_bits(cptr); - unsigned idx; - - char dr0c = "01234567"[dr0]; - char dr1c = "01234567"[dr1]; - char*dp = result; - - strcpy(dp, "C8<"); - dp += strlen(dp); - - for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) { - switch (bits[ivl_const_width(cptr)-idx-1]) { - case '0': - *dp++ = dr0c; - *dp++ = dr0c; - *dp++ = '0'; - break; - case '1': - *dp++ = dr1c; - *dp++ = dr1c; - *dp++ = '1'; - break; - case 'x': - case 'X': - *dp++ = dr0c; - *dp++ = dr1c; - *dp++ = 'x'; - break; - case 'z': - case 'Z': - *dp++ = '0'; - *dp++ = '0'; - *dp++ = 'z'; - break; - default: - assert(0); - break; - } - assert(dp - result < nresult); - } - - strcpy(dp, ">"); - return result; -} - -static char* draw_Cr_to_string(double value) +char* draw_Cr_to_string(double value) { char tmp[256]; @@ -508,488 +352,6 @@ static char* draw_Cr_to_string(double value) return strdup(tmp); } -/* - * This function takes a nexus and looks for an input functor. It then - * draws to the output a string that represents that functor. What we - * are trying to do here is find the input to the net that is attached - * to this nexus. - */ - -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) && (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) <= 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) <= result_len); - return result; - } - } - - if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) { - if (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG) { - size_t result_len = 5 + ivl_logic_width(lptr); - char*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) <= result_len); - return result; - } else { - char val[4]; - size_t result_len = 5 + 3*ivl_logic_width(lptr); - char*result = malloc(result_len); - char*dp = result; - - val[0] = "01234567"[ivl_nexus_ptr_drive0(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) <= result_len); - 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_array_count(sptr) > 1) { - 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) { - /* Constants should have exactly 1 pin, with a literal value. */ - assert(nptr_pin == 0); - char *result = 0; - - switch (ivl_const_type(cptr)) { - case IVL_VT_LOGIC: - case IVL_VT_BOOL: - 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)); - } - break; - - case IVL_VT_REAL: - result = draw_Cr_to_string(ivl_const_real(cptr)); - break; - - default: - assert(0); - break; - } - - ivl_expr_t d_rise = ivl_const_delay(cptr, 0); - ivl_expr_t d_fall = ivl_const_delay(cptr, 1); - ivl_expr_t d_decay = ivl_const_delay(cptr, 2); - - /* We have a delayed constant, so we need to build some code. */ - if (d_rise != 0) { - assert(number_is_immediate(d_rise, 64)); - assert(number_is_immediate(d_fall, 64)); - assert(number_is_immediate(d_decay, 64)); - - fprintf(vvp_out, "L_%p/d .functor BUFZ 1, %s, " - "C4<0>, C4<0>, C4<0>;\n", cptr, result); - - fprintf(vvp_out, "L_%p .delay (%lu,%lu,%lu) L_%p/d;\n", - cptr, get_number_immediate(d_rise), - get_number_immediate(d_rise), - get_number_immediate(d_rise), cptr); - - free(result); - char tmp[128]; - 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_CONCAT: - 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, 0) == nex) { - char tmp[128]; - snprintf(tmp, sizeof tmp, "L_%p", lpm); - return strdup(tmp); - } - break; - - case IVL_LPM_PART_BI: - if (ivl_lpm_q(lpm, 0) == nex) { - char tmp[128]; - snprintf(tmp, sizeof tmp, "L_%p/P", lpm); - return strdup(tmp); - } else if (ivl_lpm_data(lpm,0) == nex) { - char tmp[128]; - snprintf(tmp, sizeof tmp, "L_%p/V", lpm); - return strdup(tmp); - } - break; - } - - fprintf(stderr, "internal error: no input to nexus %s\n", - ivl_nexus_name(nex)); - assert(0); - return strdup("C"); -} - -static int nexus_drive_is_strength_aware(ivl_nexus_ptr_t nptr) -{ - if (ivl_nexus_ptr_drive0(nptr) != IVL_DR_STRONG) - return 1; - if (ivl_nexus_ptr_drive1(nptr) != IVL_DR_STRONG) - return 1; - - ivl_net_logic_t log = ivl_nexus_ptr_log(nptr); - if (log != 0) { - /* These logic gates are able to generate unusual - strength values and so their outputs are considered - strength aware. */ - if (ivl_logic_type(log) == IVL_LO_BUFIF0) - return 1; - if (ivl_logic_type(log) == IVL_LO_BUFIF1) - return 1; - if (ivl_logic_type(log) == IVL_LO_PMOS) - return 1; - if (ivl_logic_type(log) == IVL_LO_NMOS) - return 1; - if (ivl_logic_type(log) == IVL_LO_CMOS) - return 1; - } - - return 0; -} - -/* - * This function draws the input to a net into a string. What that - * means is that it returns a static string that can be used to - * represent a resolved driver to a nexus. If there are multiple - * drivers to the nexus, then it writes out the resolver declarations - * needed to perform strength resolution. - * - * The string that this returns is malloced, and that means that the - * caller must free the string or store it permanently. This function - * does *not* check for a previously calculated string. Use the - * draw_net_input for the general case. - */ - /* Omit LPMPART_BI device pin-data(0) drivers. */ -# define OMIT_PART_BI_DATA 0x0001 - -static char* draw_net_input_x(ivl_nexus_t nex, - ivl_nexus_ptr_t omit_ptr, int omit_flags, - struct vvp_nexus_data*nex_data) -{ - ivl_signal_type_t res; - char result[512]; - unsigned idx; - int level; - unsigned ndrivers = 0; - static ivl_nexus_ptr_t *drivers = 0x0; - static unsigned adrivers = 0; - - const char*resolv_type; - - char*nex_private = 0; - - /* Accumulate nex_data flags. */ - int nex_flags = 0; - - res = signal_type_of_nexus(nex); - switch (res) { - case IVL_SIT_TRI: - resolv_type = "tri"; - break; - case IVL_SIT_TRI0: - resolv_type = "tri0"; - nex_flags |= VVP_NEXUS_DATA_STR; - break; - case IVL_SIT_TRI1: - resolv_type = "tri1"; - nex_flags |= VVP_NEXUS_DATA_STR; - break; - case IVL_SIT_TRIAND: - resolv_type = "triand"; - break; - case IVL_SIT_TRIOR: - resolv_type = "trior"; - break; - default: - fprintf(stderr, "vvp.tgt: Unsupported signal type: %u\n", res); - assert(0); - resolv_type = "tri"; - break; - } - - - for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) { - ivl_lpm_t lpm_tmp; - ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx); - - /* If we are supposed to skip LPM_PART_BI data pins, - check that this driver is that. */ - if ((omit_flags&OMIT_PART_BI_DATA) - && (lpm_tmp = ivl_nexus_ptr_lpm(nptr)) - && (nex == ivl_lpm_data(lpm_tmp,0))) - continue; - - if (nptr == omit_ptr) - continue; - - /* Skip input only pins. */ - if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_HiZ) - && (ivl_nexus_ptr_drive1(nptr) == IVL_DR_HiZ)) - continue; - - /* Mark the strength-aware flag if the driver can - generate values other than the standard "6" - strength. */ - if (nexus_drive_is_strength_aware(nptr)) - nex_flags |= VVP_NEXUS_DATA_STR; - - /* Save this driver. */ - if (ndrivers >= adrivers) { - adrivers += 4; - drivers = realloc(drivers, adrivers*sizeof(ivl_nexus_ptr_t)); - assert(drivers); - } - drivers[ndrivers] = nptr; - ndrivers += 1; - } - - /* If the caller is collecting nexus information, then save - the nexus driver count in the nex_data. */ - if (nex_data) { - nex_data->drivers_count = ndrivers; - nex_data->flags |= nex_flags; - } - - /* If the nexus has no drivers, then send a constant HiZ into - the net. */ - if (ndrivers == 0) { - unsigned idx, wid = width_of_nexus(nex); - char*tmp = malloc(wid + 5); - nex_private = tmp; - strcpy(tmp, "C4<"); - tmp += strlen(tmp); - switch (res) { - case IVL_SIT_TRI: - for (idx = 0 ; idx < wid ; idx += 1) - *tmp++ = 'z'; - break; - case IVL_SIT_TRI0: - for (idx = 0 ; idx < wid ; idx += 1) - *tmp++ = '0'; - break; - case IVL_SIT_TRI1: - for (idx = 0 ; idx < wid ; idx += 1) - *tmp++ = '1'; - break; - default: - assert(0); - } - *tmp++ = '>'; - *tmp = 0; - return nex_private; - } - - - /* If the nexus has exactly one driver, then simply draw - it. Note that this will *not* work if the nexus is not a - TRI type nexus. */ - if (ndrivers == 1 && res == IVL_SIT_TRI) { - ivl_signal_t path_sig = find_modpath(nex); - if (path_sig) { - char*nex_str = draw_net_input_drive(nex, drivers[0]); - char modpath_label[64]; - snprintf(modpath_label, sizeof modpath_label, - "V_%p/m", path_sig); - nex_private = strdup(modpath_label); - draw_modpath(path_sig, nex_str); - - } else { - nex_private = draw_net_input_drive(nex, drivers[0]); - } - return nex_private; - } - - level = 0; - while (ndrivers) { - unsigned int inst; - for (inst = 0; inst < ndrivers; inst += 4) { - if (ndrivers > 4) - fprintf(vvp_out, "RS_%p/%d/%d .resolv tri", - nex, level, inst); - else - fprintf(vvp_out, "RS_%p .resolv %s", - nex, resolv_type); - - for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) { - if (level) { - fprintf(vvp_out, ", RS_%p/%d/%d", - nex, level - 1, idx*4); - } else { - char*drive = draw_net_input_drive(nex, drivers[idx]); - fprintf(vvp_out, ", %s", drive); - free(drive); - } - } - for ( ; idx < inst+4 ; idx += 1) { - fprintf(vvp_out, ", "); - draw_C4_repeated_constant('z',width_of_nexus(nex)); - } - - fprintf(vvp_out, ";\n"); - } - if (ndrivers > 4) - ndrivers = (ndrivers+3) / 4; - else - ndrivers = 0; - level += 1; - } - - sprintf(result, "RS_%p", nex); - nex_private = strdup(result); - return nex_private; -} - -/* - * Get a cached description of the nexus input, or create one if this - * nexus has not been cached yet. This is a wrapper for the common - * case call to draw_net_input_x. - */ -const char*draw_net_input(ivl_nexus_t nex) -{ - struct vvp_nexus_data*nex_data = (struct vvp_nexus_data*) - ivl_nexus_get_private(nex); - - /* If this nexus already has a label, then its input is - already figured out. Just return the existing label. */ - if (nex_data && nex_data->net_input) - return nex_data->net_input; - - if (nex_data == 0) { - nex_data = new_nexus_data(); - ivl_nexus_set_private(nex, nex_data); - } - - assert(nex_data->net_input == 0); - nex_data->net_input = draw_net_input_x(nex, 0, 0, nex_data); - - return nex_data->net_input; -} - const char*draw_input_from_net(ivl_nexus_t nex) { static char result[32];