Add support for tranif devices in the vvp code generator.
The draw_net_input function is modified to account for nexus that is a port of an island. Draw the ports (and the islands if necessary) to the island and use the port output for the nexus instead of the port input. This allows the bi-directional behavior of the port to interpose itself in the data flow. In this process of these changes, the draw_net_input function was reorganized, and all the considerable amount of code for it was moved to a file of its own. (vvp_scope.c is pretty unruly.)
This commit is contained in:
parent
df15a0368c
commit
782bfab29e
|
|
@ -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) : "";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <malloc.h>
|
||||
#endif
|
||||
# include <stdlib.h>
|
||||
# include <math.h>
|
||||
# include <string.h>
|
||||
# include <inttypes.h>
|
||||
# include <assert.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;
|
||||
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<z>");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -25,9 +25,52 @@
|
|||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
|
||||
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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<z>");
|
||||
}
|
||||
|
||||
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];
|
||||
|
|
|
|||
Loading…
Reference in New Issue