mirror of
https://github.com/steveicarus/iverilog.git
synced 2026-08-29 01:03:29 +02:00
A BUFT is needed to allow a force to override the pull value. This had already been done for pullup devices, so factor out the common code and use for both pullup and pulldown.
866 lines
24 KiB
C
866 lines
24 KiB
C
/*
|
|
* Copyright (c) 2001-2012 Stephen Williams ([email protected])
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
# include "vvp_priv.h"
|
|
# include <stdlib.h>
|
|
# include <math.h>
|
|
# include <string.h>
|
|
# include <inttypes.h>
|
|
# include <limits.h>
|
|
# include <assert.h>
|
|
# include "ivl_alloc.h"
|
|
|
|
#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;
|
|
if (stype == IVL_SIT_UWIRE) return IVL_SIT_UWIRE;
|
|
out = stype;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
static ivl_variable_type_t signal_data_type_of_nexus(ivl_nexus_t nex)
|
|
{
|
|
unsigned idx;
|
|
ivl_variable_type_t out = IVL_VT_NO_TYPE;
|
|
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
|
ivl_variable_type_t vtype;
|
|
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx);
|
|
ivl_signal_t sig = ivl_nexus_ptr_sig(ptr);
|
|
if (sig == 0) continue;
|
|
|
|
vtype = ivl_signal_data_type(sig);
|
|
if (out == IVL_VT_NO_TYPE && vtype == IVL_VT_BOOL) {
|
|
out = vtype;
|
|
continue;
|
|
}
|
|
if (out != IVL_VT_LOGIC && vtype == IVL_VT_LOGIC) {
|
|
out = vtype;
|
|
continue;
|
|
}
|
|
if (vtype == IVL_VT_REAL) {
|
|
out = vtype;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 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);
|
|
assert((unsigned)(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);
|
|
assert((unsigned)(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)
|
|
{
|
|
ivl_net_logic_t logic;
|
|
|
|
if (ivl_nexus_ptr_drive0(nptr) != IVL_DR_STRONG)
|
|
return 1;
|
|
if (ivl_nexus_ptr_drive1(nptr) != IVL_DR_STRONG)
|
|
return 1;
|
|
|
|
logic = ivl_nexus_ptr_log(nptr);
|
|
if (logic != 0) {
|
|
/* These logic gates are able to generate unusual
|
|
strength values and so their outputs are considered
|
|
strength aware. */
|
|
if (ivl_logic_type(logic) == IVL_LO_BUFIF0)
|
|
return 1;
|
|
if (ivl_logic_type(logic) == IVL_LO_BUFIF1)
|
|
return 1;
|
|
if (ivl_logic_type(logic) == IVL_LO_PMOS)
|
|
return 1;
|
|
if (ivl_logic_type(logic) == IVL_LO_NMOS)
|
|
return 1;
|
|
if (ivl_logic_type(logic) == 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 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, 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;
|
|
}
|
|
|
|
/*
|
|
* 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)||(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 *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_island_port(ivl_island_t island, int island_input_flag,
|
|
ivl_nexus_t nex, struct vvp_nexus_data*nex_data,
|
|
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);
|
|
}
|
|
|
|
snprintf(result, sizeof result, "p%p", nex);
|
|
assert(nex_data->island == 0);
|
|
nex_data->island = island;
|
|
assert(nex_data->island_input == 0);
|
|
nex_data->island_input = strdup(result);
|
|
|
|
if (island_input_flag) {
|
|
fprintf(vvp_out, "p%p .import I%p, %s;\n", nex, island, src);
|
|
return strdup(src);
|
|
} else {
|
|
fprintf(vvp_out, "p%p .port I%p, %s;\n", nex, island, src);
|
|
return strdup(nex_data->island_input);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This routine is called to display an error message when a uwire or
|
|
* wire real has multiple drivers.
|
|
*/
|
|
typedef enum mdriver_type_e {
|
|
MDRV_UWIRE = 0,
|
|
MDRV_REAL = 1
|
|
} mdriver_type_t;
|
|
|
|
static void display_multi_driver_error(ivl_nexus_t nex, unsigned ndrivers,
|
|
mdriver_type_t type)
|
|
{
|
|
unsigned idx;
|
|
unsigned scope_len = UINT_MAX;
|
|
ivl_signal_t sig = 0;
|
|
/* Find the signal. */
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
|
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx);
|
|
ivl_signal_t tsig = ivl_nexus_ptr_sig(ptr);
|
|
if (tsig != 0) {
|
|
ivl_scope_t scope;
|
|
unsigned len;
|
|
if (ivl_signal_local(tsig)) continue;
|
|
|
|
/* If this is not a local signal then find the signal
|
|
* that has the shortest scope (is the furthest up
|
|
* the hierarchy). */
|
|
scope = ivl_signal_scope(tsig);
|
|
assert(scope);
|
|
len = strlen(ivl_scope_name(scope));
|
|
if (len < scope_len) {
|
|
scope_len = len;
|
|
sig = tsig;
|
|
}
|
|
}
|
|
}
|
|
assert(sig);
|
|
|
|
fprintf(stderr, "%s:%u: vvp.tgt error: ",
|
|
ivl_signal_file(sig), ivl_signal_lineno(sig));
|
|
switch (type) {
|
|
case MDRV_UWIRE:
|
|
if (ivl_signal_type(sig) != IVL_SIT_UWIRE) {
|
|
fprintf(stderr, "(implicit) ");
|
|
}
|
|
fprintf(stderr, "uwire");
|
|
break;
|
|
|
|
case MDRV_REAL:
|
|
assert(ivl_signal_type(sig) == IVL_SIT_TRI);
|
|
if (ivl_signal_data_type(sig) != IVL_VT_REAL) {
|
|
fprintf(stderr, "(implicit) ");
|
|
}
|
|
fprintf(stderr, "wire real");
|
|
break;
|
|
|
|
default:
|
|
assert(0);;
|
|
}
|
|
fprintf(stderr, " \"%s\" must have a single driver, found (%u).\n",
|
|
ivl_signal_basename(sig), ndrivers);
|
|
vvp_errors += 1;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
static ivl_nexus_ptr_t *drivers = 0x0;
|
|
static unsigned adrivers = 0;
|
|
|
|
void EOC_cleanup_drivers()
|
|
{
|
|
free(drivers);
|
|
drivers = NULL;
|
|
adrivers = 0;
|
|
}
|
|
|
|
static void draw_net_input_x(ivl_nexus_t nex,
|
|
struct vvp_nexus_data*nex_data)
|
|
{
|
|
ivl_island_t island = 0;
|
|
int island_input_flag = -1;
|
|
ivl_signal_type_t res;
|
|
char result[512];
|
|
unsigned idx;
|
|
char**driver_labels;
|
|
unsigned ndrivers = 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:
|
|
case IVL_SIT_UWIRE:
|
|
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: %d\n", res);
|
|
assert(0);
|
|
resolv_type = "tri";
|
|
break;
|
|
}
|
|
|
|
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
|
ivl_switch_t sw = 0;
|
|
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. If this nexus is an output from any
|
|
switches in the island, then set island_input_flag to
|
|
false. Save the island cookie. */
|
|
if ( (sw = ivl_nexus_ptr_switch(nptr)) ) {
|
|
assert(island == 0 || island == ivl_switch_island(sw));
|
|
island = ivl_switch_island(sw);
|
|
if (nex == ivl_switch_a(sw)) {
|
|
nex_flags |= VVP_NEXUS_DATA_STR;
|
|
island_input_flag = 0;
|
|
} else if (nex == ivl_switch_b(sw)) {
|
|
nex_flags |= VVP_NEXUS_DATA_STR;
|
|
island_input_flag = 0;
|
|
} else if (island_input_flag == -1) {
|
|
assert(nex == ivl_switch_enable(sw));
|
|
island_input_flag = 1;
|
|
}
|
|
}
|
|
|
|
/* 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));
|
|
}
|
|
drivers[ndrivers] = nptr;
|
|
ndrivers += 1;
|
|
}
|
|
|
|
if (island_input_flag < 0)
|
|
island_input_flag = 0;
|
|
|
|
/* Save the nexus driver count in the nex_data. */
|
|
assert(nex_data);
|
|
nex_data->drivers_count = ndrivers;
|
|
nex_data->flags |= nex_flags;
|
|
|
|
/* If the nexus has no drivers, then send a constant HiZ or
|
|
0.0 into the net. */
|
|
if (ndrivers == 0) {
|
|
/* For real nets put 0.0. */
|
|
if (signal_data_type_of_nexus(nex) == IVL_VT_REAL) {
|
|
nex_private = draw_Cr_to_string(0.0);
|
|
} else {
|
|
unsigned jdx, 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:
|
|
case IVL_SIT_UWIRE:
|
|
for (jdx = 0 ; jdx < wid ; jdx += 1)
|
|
*tmp++ = 'z';
|
|
break;
|
|
case IVL_SIT_TRI0:
|
|
for (jdx = 0 ; jdx < wid ; jdx += 1)
|
|
*tmp++ = '0';
|
|
break;
|
|
case IVL_SIT_TRI1:
|
|
for (jdx = 0 ; jdx < wid ; jdx += 1)
|
|
*tmp++ = '1';
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
*tmp++ = '>';
|
|
*tmp = 0;
|
|
|
|
/* Create an "open" driver to hold the HiZ. We
|
|
need to do this so that .nets have something to
|
|
hang onto. */
|
|
char buf[64];
|
|
snprintf(buf, sizeof buf, "o%p", nex);
|
|
fprintf(vvp_out, "%s .functor BUFZ %u, %s; HiZ drive\n",
|
|
buf, wid, nex_private);
|
|
nex_private = realloc(nex_private, strlen(buf)+1);
|
|
strcpy(nex_private, buf);
|
|
}
|
|
|
|
if (island) {
|
|
char*tmp2 = draw_island_port(island, island_input_flag, nex, nex_data, nex_private);
|
|
free(nex_private);
|
|
nex_private = tmp2;
|
|
}
|
|
assert(nex_data->net_input == 0);
|
|
nex_data->net_input = nex_private;
|
|
return;
|
|
}
|
|
|
|
/* A uwire is a tri with only one driver. */
|
|
if (res == IVL_SIT_UWIRE) {
|
|
if (ndrivers > 1) {
|
|
display_multi_driver_error(nex, ndrivers, MDRV_UWIRE);
|
|
}
|
|
res = IVL_SIT_TRI;
|
|
}
|
|
|
|
/* 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, island_input_flag, nex, nex_data, nex_private);
|
|
free(nex_private);
|
|
nex_private = tmp;
|
|
}
|
|
assert(nex_data->net_input == 0);
|
|
nex_data->net_input = nex_private;
|
|
return;
|
|
}
|
|
|
|
/* We currently only support one driver on real nets. */
|
|
if (ndrivers > 1 && signal_data_type_of_nexus(nex) == IVL_VT_REAL) {
|
|
display_multi_driver_error(nex, ndrivers, MDRV_REAL);
|
|
}
|
|
|
|
driver_labels = malloc(ndrivers * sizeof(char*));
|
|
for (idx = 0; idx < ndrivers; idx += 1) {
|
|
driver_labels[idx] = draw_net_input_drive(nex, drivers[idx]);
|
|
}
|
|
fprintf(vvp_out, "RS_%p .resolv %s", nex, resolv_type);
|
|
for (idx = 0; idx < ndrivers; idx += 1) {
|
|
fprintf(vvp_out, ", %s", driver_labels[idx]);
|
|
free(driver_labels[idx]);
|
|
}
|
|
fprintf(vvp_out, ";\n");
|
|
free(driver_labels);
|
|
|
|
snprintf(result, sizeof result, "RS_%p", nex);
|
|
|
|
if (island)
|
|
nex_private = draw_island_port(island, island_input_flag, nex, nex_data, result);
|
|
else
|
|
nex_private = strdup(result);
|
|
|
|
assert(nex_data->net_input == 0);
|
|
nex_data->net_input = 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);
|
|
draw_net_input_x(nex, nex_data);
|
|
|
|
return nex_data->net_input;
|
|
}
|
|
|
|
const char*draw_island_net_input(ivl_island_t island, 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->island_input) {
|
|
assert(nex_data->island == island);
|
|
return nex_data->island_input;
|
|
}
|
|
|
|
if (nex_data == 0) {
|
|
nex_data = new_nexus_data();
|
|
ivl_nexus_set_private(nex, nex_data);
|
|
}
|
|
|
|
assert(nex_data->net_input == 0);
|
|
draw_net_input_x(nex, nex_data);
|
|
|
|
assert(nex_data->island == island);
|
|
assert(nex_data->island_input);
|
|
|
|
return nex_data->island_input;
|
|
}
|