2001-03-21 02:49:43 +01:00
|
|
|
/*
|
2005-02-08 01:12:36 +01:00
|
|
|
* Copyright (c) 2001-2005 Stephen Williams (steve@icarus.com)
|
2001-03-21 02:49:43 +01:00
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2002-08-12 03:34:58 +02:00
|
|
|
#ifdef HAVE_CVS_IDENT
|
2005-02-12 07:25:15 +01:00
|
|
|
#ident "$Id: vvp_scope.c,v 1.117 2005/02/12 06:25:15 steve Exp $"
|
2001-03-21 02:49:43 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
# include "vvp_priv.h"
|
|
|
|
|
# include <assert.h>
|
2001-09-15 20:27:04 +02:00
|
|
|
#ifdef HAVE_MALLOC_H
|
2001-04-24 04:23:58 +02:00
|
|
|
# include <malloc.h>
|
2001-09-15 20:27:04 +02:00
|
|
|
#endif
|
|
|
|
|
# include <stdlib.h>
|
2001-05-06 02:01:02 +02:00
|
|
|
# include <string.h>
|
2001-03-21 02:49:43 +01:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
/*
|
|
|
|
|
* Escape non-symbol chararacters in ids, and quotes in strings.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
inline static char hex_digit(unsigned i)
|
|
|
|
|
{
|
|
|
|
|
i &= 0xf;
|
|
|
|
|
return i>=10 ? i-10+'A' : i+'0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *vvp_mangle_id(const char *id)
|
|
|
|
|
{
|
|
|
|
|
static char *out = 0x0;
|
|
|
|
|
static size_t out_len;
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
int nesc = 0;
|
|
|
|
|
int iout = 0;
|
|
|
|
|
const char *inp = id;
|
|
|
|
|
|
|
|
|
|
const char nosym[] = "!\"#%&'()*+,-/:;<=>?@[\\]^`{|}~";
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
char *se = strpbrk(inp, nosym);
|
|
|
|
|
if (!se)
|
|
|
|
|
return id;
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
do {
|
|
|
|
|
int n = se - inp;
|
2003-12-19 02:27:10 +01:00
|
|
|
unsigned int nlen = strlen(id) + 4*(++nesc) + 1;
|
2001-06-18 05:10:34 +02:00
|
|
|
if (out_len < nlen) {
|
2004-09-11 01:13:05 +02:00
|
|
|
out = realloc(out, nlen);
|
2001-06-18 05:10:34 +02:00
|
|
|
assert(out);
|
|
|
|
|
out_len = nlen;
|
|
|
|
|
}
|
|
|
|
|
if (n) {
|
|
|
|
|
strncpy(out+iout, inp, n);
|
|
|
|
|
iout += n;
|
|
|
|
|
}
|
|
|
|
|
inp += n+1;
|
|
|
|
|
out[iout++] = '\\';
|
|
|
|
|
switch (*se) {
|
|
|
|
|
case '\\':
|
|
|
|
|
case '/':
|
|
|
|
|
case '<':
|
|
|
|
|
case '>':
|
|
|
|
|
out[iout++] = *se;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
out[iout++] = 'x';
|
|
|
|
|
out[iout++] = hex_digit(*se >> 4);
|
|
|
|
|
out[iout++] = hex_digit(*se);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
se = strpbrk(inp, nosym);
|
|
|
|
|
} while (se);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
strcpy(out+iout, inp);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *vvp_mangle_name(const char *id)
|
|
|
|
|
{
|
|
|
|
|
static char *out = 0x0;
|
|
|
|
|
static size_t out_len;
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
int nesc = 0;
|
|
|
|
|
int iout = 0;
|
|
|
|
|
const char *inp = id;
|
|
|
|
|
|
|
|
|
|
const char nosym[] = "\"\\";
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
char *se = strpbrk(inp, nosym);
|
|
|
|
|
if (!se)
|
|
|
|
|
return id;
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
do {
|
|
|
|
|
int n = se - inp;
|
2003-12-19 02:27:10 +01:00
|
|
|
unsigned int nlen = strlen(id) + 2*(++nesc) + 1;
|
2001-06-18 05:10:34 +02:00
|
|
|
if (out_len < nlen) {
|
2004-09-11 01:13:05 +02:00
|
|
|
out = realloc(out, nlen);
|
2001-06-18 05:10:34 +02:00
|
|
|
assert(out);
|
|
|
|
|
out_len = nlen;
|
|
|
|
|
}
|
|
|
|
|
if (n) {
|
|
|
|
|
strncpy(out+iout, inp, n);
|
|
|
|
|
iout += n;
|
|
|
|
|
}
|
|
|
|
|
inp += n+1;
|
|
|
|
|
out[iout++] = '\\';
|
|
|
|
|
out[iout++] = *se;
|
|
|
|
|
|
|
|
|
|
se = strpbrk(inp, nosym);
|
|
|
|
|
} while (se);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
strcpy(out+iout, inp);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-08 01:12:36 +01:00
|
|
|
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, ">");
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-12 07:25:15 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-04 00:30:48 +02:00
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
* same label. The result is stored in static memory, so remember to
|
|
|
|
|
* copy it out.
|
|
|
|
|
*/
|
|
|
|
|
const char* vvp_signal_label(ivl_signal_t sig)
|
|
|
|
|
{
|
|
|
|
|
static char buf[32];
|
|
|
|
|
sprintf(buf, "$%p", sig);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-26 22:15:58 +01:00
|
|
|
const char* vvp_word_label(ivl_variable_t sig)
|
|
|
|
|
{
|
|
|
|
|
static char buf[32];
|
|
|
|
|
sprintf(buf, "$%p", sig);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-04 20:28:14 +02:00
|
|
|
/*
|
|
|
|
|
* This makes a string suitable for use as a label for memories.
|
|
|
|
|
*/
|
|
|
|
|
const char* vvp_memory_label(ivl_memory_t mem)
|
|
|
|
|
{
|
|
|
|
|
static char buf[32];
|
|
|
|
|
sprintf(buf, "$%p", mem);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
2001-12-15 03:13:33 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-30 00:52:09 +01:00
|
|
|
unsigned width_of_nexus(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)
|
|
|
|
|
return ivl_signal_width(sig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-01-12 05:03:39 +01:00
|
|
|
ivl_nexus_ptr_t ivl_logic_pin_ptr(ivl_net_logic_t net, unsigned pin)
|
|
|
|
|
{
|
|
|
|
|
ivl_nexus_t nex = ivl_logic_pin(net, pin);
|
|
|
|
|
unsigned idx;
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
|
|
|
|
ivl_nexus_ptr_t ptr = ivl_nexus_ptr(nex, idx);
|
|
|
|
|
ivl_net_logic_t tmp = ivl_nexus_ptr_log(ptr);
|
|
|
|
|
if (tmp == 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (tmp != net)
|
|
|
|
|
continue;
|
|
|
|
|
if (ivl_nexus_ptr_pin(ptr) != pin)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
assert(0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-06 04:15:43 +01:00
|
|
|
const char*drive_string(ivl_drive_t drive)
|
|
|
|
|
{
|
|
|
|
|
switch (drive) {
|
|
|
|
|
case IVL_DR_HiZ:
|
|
|
|
|
return "";
|
|
|
|
|
case IVL_DR_SMALL:
|
|
|
|
|
return "sm";
|
|
|
|
|
case IVL_DR_MEDIUM:
|
|
|
|
|
return "me";
|
|
|
|
|
case IVL_DR_WEAK:
|
|
|
|
|
return "we";
|
|
|
|
|
case IVL_DR_LARGE:
|
|
|
|
|
return "la";
|
|
|
|
|
case IVL_DR_PULL:
|
|
|
|
|
return "pu";
|
|
|
|
|
case IVL_DR_STRONG:
|
|
|
|
|
return "";
|
|
|
|
|
case IVL_DR_SUPPLY:
|
|
|
|
|
return "su";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-03-21 02:49:43 +01:00
|
|
|
/*
|
2001-03-25 05:25:43 +02:00
|
|
|
* The draw_scope function draws the major functional items within a
|
|
|
|
|
* scope. This includes the scopes themselves, of course. All the
|
|
|
|
|
* other functions in this file are in support of that task.
|
2001-03-21 02:49:43 +01:00
|
|
|
*/
|
2001-03-25 05:25:43 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
|
2001-05-06 02:01:02 +02:00
|
|
|
/*
|
|
|
|
|
* NEXUS
|
|
|
|
|
* ivl builds up the netlist into objects connected together by
|
|
|
|
|
* ivl_nexus_t objects. The nexus receives all the drivers of the
|
|
|
|
|
* point in the net and resolves the value. The result is then sent to
|
|
|
|
|
* all the nets that are connected to the nexus. The nets, then, are
|
|
|
|
|
* read to get the value of the nexus.
|
|
|
|
|
*
|
|
|
|
|
* NETS
|
|
|
|
|
* Nets are interesting and special, because a nexus may be connected
|
|
|
|
|
* to several of them at once. This can happen, for example, as an
|
|
|
|
|
* artifact of module port connects, where the inside and the outside
|
|
|
|
|
* of the module are connected through an in-out port. (In fact, ivl
|
|
|
|
|
* will simply connect signals that are bound through a port, because
|
|
|
|
|
* the input/output/inout properties are enforced as compile time.)
|
|
|
|
|
*
|
|
|
|
|
* This case is handled by choosing one to receive the value of the
|
|
|
|
|
* nexus. This one then feeds to another net at the nexus, and so
|
|
|
|
|
* on. The last net is selected as the output of the nexus.
|
|
|
|
|
*/
|
2004-09-25 23:04:25 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This tests a bufz device against an output receiver, and determines
|
|
|
|
|
* if the device can be skipped. If this function returns true, then a
|
|
|
|
|
* 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)
|
|
|
|
|
{
|
|
|
|
|
ivl_nexus_t in_n;
|
|
|
|
|
unsigned idx;
|
|
|
|
|
|
|
|
|
|
/* These are the drives we expect. */
|
|
|
|
|
ivl_drive_t dr0 = ivl_nexus_ptr_drive0(nptr);
|
|
|
|
|
ivl_drive_t dr1 = ivl_nexus_ptr_drive1(nptr);
|
|
|
|
|
int drive_count = 0;
|
|
|
|
|
|
|
|
|
|
/* If the gate carries a delay, it must remain. */
|
|
|
|
|
if (ivl_logic_delay(net, 0) != 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* If the input is connected to the output, then do not elide
|
|
|
|
|
the gate. This is some sort of cycle. */
|
|
|
|
|
if (ivl_logic_pin(net, 0) == ivl_logic_pin(net, 1))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
in_n = ivl_logic_pin(net, 1);
|
|
|
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(in_n) ; idx += 1) {
|
|
|
|
|
ivl_nexus_ptr_t in_np = ivl_nexus_ptr(in_n, idx);
|
|
|
|
|
if (ivl_nexus_ptr_log(in_np) == net)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* If the driver for the source does not match the
|
|
|
|
|
expected drive, then we need to keep the bufz. This
|
|
|
|
|
test also catches the case that the input device is
|
|
|
|
|
really also an input, as that device will have a
|
|
|
|
|
drive of HiZ. We need to keep BUFZ devices in that
|
|
|
|
|
case in order to prevent back-flow of data. */
|
|
|
|
|
if (ivl_nexus_ptr_drive0(in_np) != dr0)
|
|
|
|
|
return 0;
|
|
|
|
|
if (ivl_nexus_ptr_drive1(in_np) != dr1)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
drive_count += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the BUFZ input has multiple drivers on its input, then
|
|
|
|
|
we need to keep this device in order to hide the
|
|
|
|
|
resolution. */
|
|
|
|
|
if (drive_count != 1)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-12 07:25:15 +01:00
|
|
|
static void draw_C4_to_string(char*result, size_t nresult,
|
|
|
|
|
ivl_net_const_t cptr)
|
|
|
|
|
{
|
|
|
|
|
const char*bits = ivl_const_bits(cptr);
|
|
|
|
|
unsigned idx;
|
|
|
|
|
|
|
|
|
|
char*dp = result;
|
|
|
|
|
strcpy(dp, "C4<");
|
|
|
|
|
dp += strlen(dp);
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < ivl_const_width(cptr) ; idx += 1) {
|
|
|
|
|
switch (bits[ivl_const_width(cptr)-idx-1]) {
|
|
|
|
|
case '0':
|
|
|
|
|
*dp++ = '0';
|
|
|
|
|
break;
|
|
|
|
|
case '1':
|
|
|
|
|
*dp++ = '1';
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
*dp++ = bits[idx];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
assert(dp - result < nresult);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strcpy(dp, ">");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void draw_C8_to_string(char*result, size_t nresult,
|
|
|
|
|
ivl_net_const_t cptr,
|
|
|
|
|
ivl_drive_t dr0, ivl_drive_t dr1)
|
|
|
|
|
{
|
|
|
|
|
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, ">");
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 05:25:43 +02:00
|
|
|
/*
|
|
|
|
|
* This function takes a nexus and looks for an input functor. It then
|
2001-05-06 02:01:02 +02:00
|
|
|
* 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.
|
2001-03-25 05:25:43 +02:00
|
|
|
*/
|
2001-05-12 05:31:01 +02:00
|
|
|
|
|
|
|
|
static const char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
|
2001-03-25 05:25:43 +02:00
|
|
|
{
|
2001-05-12 05:31:01 +02:00
|
|
|
static char result[2048];
|
|
|
|
|
unsigned idx;
|
|
|
|
|
unsigned nptr_pin = ivl_nexus_ptr_pin(nptr);
|
2001-04-30 01:16:31 +02:00
|
|
|
ivl_net_const_t cptr;
|
2001-03-25 05:25:43 +02:00
|
|
|
ivl_net_logic_t lptr;
|
|
|
|
|
ivl_signal_t sptr;
|
2001-04-26 07:12:02 +02:00
|
|
|
ivl_lpm_t lpm;
|
2001-03-25 05:25:43 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
lptr = ivl_nexus_ptr_log(nptr);
|
2002-01-12 05:03:39 +01:00
|
|
|
if (lptr && (ivl_logic_type(lptr) == IVL_LO_BUFZ) && (nptr_pin == 0))
|
|
|
|
|
do {
|
2004-09-25 23:04:25 +02:00
|
|
|
if (! can_elide_bufz(lptr, nptr))
|
2002-07-18 04:06:37 +02:00
|
|
|
break;
|
|
|
|
|
|
2002-01-12 05:03:39 +01:00
|
|
|
return draw_net_input(ivl_logic_pin(lptr, 1));
|
|
|
|
|
} while(0);
|
2001-04-30 01:16:31 +02:00
|
|
|
|
2005-02-12 07:25:15 +01:00
|
|
|
/* 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. */
|
2001-05-12 05:31:01 +02:00
|
|
|
if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLDOWN)) {
|
2005-02-12 07:25:15 +01:00
|
|
|
if (ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG) {
|
|
|
|
|
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) <= sizeof result);
|
|
|
|
|
return result;
|
|
|
|
|
} else {
|
|
|
|
|
char val[4];
|
|
|
|
|
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) <= sizeof result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2001-05-12 05:31:01 +02:00
|
|
|
}
|
2001-03-25 05:25:43 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
if (lptr && (ivl_logic_type(lptr) == IVL_LO_PULLUP)) {
|
2005-02-12 07:25:15 +01:00
|
|
|
if (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG) {
|
|
|
|
|
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) <= sizeof result);
|
|
|
|
|
return result;
|
|
|
|
|
} else {
|
|
|
|
|
char val[4];
|
|
|
|
|
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) <= sizeof result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2001-05-12 05:31:01 +02:00
|
|
|
}
|
2001-04-30 01:16:31 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
if (lptr && (nptr_pin == 0)) {
|
2003-03-06 01:27:09 +01:00
|
|
|
sprintf(result, "L_%p", lptr);
|
2001-05-12 05:31:01 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
2001-04-30 01:16:31 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
sptr = ivl_nexus_ptr_sig(nptr);
|
|
|
|
|
if (sptr && (ivl_signal_type(sptr) == IVL_SIT_REG)) {
|
2004-12-11 03:31:25 +01:00
|
|
|
/* Input is a .var. Note that these devices have only
|
|
|
|
|
exactly one pin (that carries a vector) so nptr_pin
|
|
|
|
|
must be 0. */
|
|
|
|
|
assert(nptr_pin == 0);
|
|
|
|
|
sprintf(result, "V_%s", vvp_signal_label(sptr));
|
2001-05-12 05:31:01 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
2001-04-30 01:16:31 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
cptr = ivl_nexus_ptr_con(nptr);
|
|
|
|
|
if (cptr) {
|
2005-02-12 07:25:15 +01:00
|
|
|
/* Constants should have exactly 1 pin, with a vector value. */
|
2004-12-11 03:31:25 +01:00
|
|
|
assert(nptr_pin == 0);
|
|
|
|
|
|
2005-02-12 07:25:15 +01:00
|
|
|
if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_STRONG)
|
|
|
|
|
&& (ivl_nexus_ptr_drive1(nptr) == IVL_DR_STRONG)) {
|
2004-12-11 03:31:25 +01:00
|
|
|
|
2005-02-12 07:25:15 +01:00
|
|
|
draw_C4_to_string(result, sizeof(result), cptr);
|
2002-01-06 04:15:43 +01:00
|
|
|
|
2005-02-12 07:25:15 +01:00
|
|
|
} else {
|
|
|
|
|
draw_C8_to_string(result, sizeof(result), cptr,
|
|
|
|
|
ivl_nexus_ptr_drive0(nptr),
|
|
|
|
|
ivl_nexus_ptr_drive1(nptr));
|
|
|
|
|
}
|
2001-05-12 05:31:01 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
2001-03-25 05:25:43 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
lpm = ivl_nexus_ptr_lpm(nptr);
|
|
|
|
|
if (lpm) switch (ivl_lpm_type(lpm)) {
|
2001-04-30 01:16:31 +02:00
|
|
|
|
2002-09-17 07:37:45 +02:00
|
|
|
case IVL_LPM_FF:
|
2001-05-12 05:31:01 +02:00
|
|
|
case IVL_LPM_MUX:
|
|
|
|
|
for (idx = 0 ; idx < ivl_lpm_width(lpm) ; idx += 1)
|
|
|
|
|
if (ivl_lpm_q(lpm, idx) == nex) {
|
2003-02-25 04:40:45 +01:00
|
|
|
sprintf(result, "L_%s.%s/%u",
|
|
|
|
|
vvp_mangle_id(ivl_scope_name(ivl_lpm_scope(lpm))),
|
|
|
|
|
vvp_mangle_id(ivl_lpm_basename(lpm)), idx);
|
|
|
|
|
return result;
|
2001-05-12 05:31:01 +02:00
|
|
|
}
|
2001-06-15 06:14:18 +02:00
|
|
|
break;
|
2001-03-25 05:25:43 +02:00
|
|
|
|
2001-06-16 04:41:41 +02:00
|
|
|
case IVL_LPM_RAM:
|
2001-06-07 04:12:43 +02:00
|
|
|
case IVL_LPM_ADD:
|
2005-01-09 21:16:00 +01:00
|
|
|
case IVL_LPM_CONCAT:
|
2005-01-22 02:06:55 +01:00
|
|
|
case IVL_LPM_CMP_EEQ:
|
2005-01-22 17:22:13 +01:00
|
|
|
case IVL_LPM_CMP_EQ:
|
2005-01-16 05:20:32 +01:00
|
|
|
case IVL_LPM_CMP_GE:
|
|
|
|
|
case IVL_LPM_CMP_GT:
|
2005-01-22 17:22:13 +01:00
|
|
|
case IVL_LPM_CMP_NE:
|
|
|
|
|
case IVL_LPM_CMP_NEE:
|
2005-02-03 05:56:20 +01:00
|
|
|
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:
|
2001-07-06 06:48:04 +02:00
|
|
|
case IVL_LPM_SHIFTL:
|
|
|
|
|
case IVL_LPM_SHIFTR:
|
2001-06-07 05:09:37 +02:00
|
|
|
case IVL_LPM_SUB:
|
2001-06-17 01:45:05 +02:00
|
|
|
case IVL_LPM_MULT:
|
2001-10-16 04:19:26 +02:00
|
|
|
case IVL_LPM_DIVIDE:
|
2002-01-03 05:19:01 +01:00
|
|
|
case IVL_LPM_MOD:
|
2002-03-09 03:10:22 +01:00
|
|
|
case IVL_LPM_UFUNC:
|
2005-01-09 21:16:00 +01:00
|
|
|
case IVL_LPM_PART_VP:
|
2005-01-22 02:06:55 +01:00
|
|
|
case IVL_LPM_PART_PV: /* NOTE: This is only a partial driver. */
|
2005-02-08 01:12:36 +01:00
|
|
|
case IVL_LPM_REPEAT:
|
2004-12-11 03:31:25 +01:00
|
|
|
if (ivl_lpm_q(lpm, 0) == nex) {
|
|
|
|
|
sprintf(result, "L_%p", lpm);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2001-06-07 04:12:43 +02:00
|
|
|
|
2001-06-15 06:14:18 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
}
|
2001-04-30 01:16:31 +02:00
|
|
|
|
2001-06-17 01:45:05 +02:00
|
|
|
fprintf(stderr, "internal error: no input to nexus %s\n",
|
|
|
|
|
ivl_nexus_name(nex));
|
2001-05-12 05:31:01 +02:00
|
|
|
assert(0);
|
2001-06-17 01:45:05 +02:00
|
|
|
return "C<z>";
|
2001-05-12 05:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
2001-08-10 02:40:45 +02:00
|
|
|
/*
|
|
|
|
|
* This function draws the input to a net. 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.
|
|
|
|
|
*
|
2001-10-24 05:43:45 +02:00
|
|
|
* The string that this returns is bound to the nexus, so the pointer
|
|
|
|
|
* remains valid.
|
2001-08-10 02:40:45 +02:00
|
|
|
*/
|
2001-11-01 05:26:57 +01:00
|
|
|
const char* draw_net_input(ivl_nexus_t nex)
|
2001-05-12 05:31:01 +02:00
|
|
|
{
|
2001-12-15 03:13:33 +01:00
|
|
|
ivl_signal_type_t res;
|
2001-08-10 02:40:45 +02:00
|
|
|
char result[512];
|
2001-05-12 05:31:01 +02:00
|
|
|
unsigned idx;
|
2001-07-16 20:31:49 +02:00
|
|
|
int level;
|
2001-05-12 05:31:01 +02:00
|
|
|
unsigned ndrivers = 0;
|
2001-07-18 04:44:39 +02:00
|
|
|
static ivl_nexus_ptr_t *drivers = 0x0;
|
|
|
|
|
static unsigned adrivers = 0;
|
2001-05-12 05:31:01 +02:00
|
|
|
|
2001-12-15 03:13:33 +01:00
|
|
|
const char*resolv_type;
|
|
|
|
|
|
2001-08-10 02:40:45 +02:00
|
|
|
/* If this nexus already has a label, then its input is
|
|
|
|
|
already figured out. Just return the existing label. */
|
2001-09-14 06:15:46 +02:00
|
|
|
char*nex_private = (char*)ivl_nexus_get_private(nex);
|
2001-08-10 02:40:45 +02:00
|
|
|
if (nex_private)
|
|
|
|
|
return nex_private;
|
|
|
|
|
|
2001-12-15 03:13:33 +01:00
|
|
|
res = signal_type_of_nexus(nex);
|
|
|
|
|
switch (res) {
|
|
|
|
|
case IVL_SIT_TRI:
|
|
|
|
|
resolv_type = "tri";
|
|
|
|
|
break;
|
|
|
|
|
case IVL_SIT_TRI0:
|
|
|
|
|
resolv_type = "tri0";
|
|
|
|
|
break;
|
|
|
|
|
case IVL_SIT_TRI1:
|
|
|
|
|
resolv_type = "tri1";
|
2003-07-30 03:13:28 +02:00
|
|
|
break;
|
|
|
|
|
case IVL_SIT_TRIAND:
|
|
|
|
|
resolv_type = "triand";
|
|
|
|
|
break;
|
|
|
|
|
case IVL_SIT_TRIOR:
|
|
|
|
|
resolv_type = "trior";
|
2001-12-15 03:13:33 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Catch the special cases that the nets are supply
|
|
|
|
|
nets. Drive constant values uncomditionally. */
|
|
|
|
|
case IVL_SIT_SUPPLY0:
|
|
|
|
|
nex_private = "C<su0>";
|
|
|
|
|
ivl_nexus_set_private(nex, nex_private);
|
|
|
|
|
return nex_private;
|
|
|
|
|
case IVL_SIT_SUPPLY1:
|
|
|
|
|
nex_private = "C<su1>";
|
|
|
|
|
ivl_nexus_set_private(nex, nex_private);
|
|
|
|
|
return nex_private;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "vvp.tgt: Unsupported signal type: %u\n", res);
|
|
|
|
|
assert(0);
|
|
|
|
|
resolv_type = "tri";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2001-08-10 02:40:45 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
|
|
|
|
ivl_nexus_ptr_t nptr = ivl_nexus_ptr(nex, idx);
|
2001-04-30 01:16:31 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
/* Skip input only pins. */
|
|
|
|
|
if ((ivl_nexus_ptr_drive0(nptr) == IVL_DR_HiZ)
|
|
|
|
|
&& (ivl_nexus_ptr_drive1(nptr) == IVL_DR_HiZ))
|
2001-04-30 02:00:27 +02:00
|
|
|
continue;
|
2001-04-26 07:12:02 +02:00
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
/* Save this driver. */
|
2001-07-18 04:44:39 +02:00
|
|
|
if (ndrivers >= adrivers) {
|
|
|
|
|
adrivers += 4;
|
2004-09-11 01:13:05 +02:00
|
|
|
drivers = realloc(drivers, adrivers*sizeof(ivl_nexus_ptr_t));
|
2001-07-18 04:44:39 +02:00
|
|
|
assert(drivers);
|
|
|
|
|
}
|
2001-05-12 05:31:01 +02:00
|
|
|
drivers[ndrivers] = nptr;
|
|
|
|
|
ndrivers += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the nexus has no drivers, then send a constant HiZ into
|
|
|
|
|
the net. */
|
2001-08-10 02:40:45 +02:00
|
|
|
if (ndrivers == 0) {
|
2005-02-10 05:55:45 +01:00
|
|
|
unsigned idx, wid = width_of_nexus(nex);
|
|
|
|
|
char*tmp = malloc(wid + 5);
|
|
|
|
|
nex_private = tmp;
|
|
|
|
|
strcpy(tmp, "C4<");
|
|
|
|
|
tmp += strlen(tmp);
|
2001-12-15 03:13:33 +01:00
|
|
|
switch (res) {
|
|
|
|
|
case IVL_SIT_TRI:
|
2005-02-10 05:55:45 +01:00
|
|
|
for (idx = 0 ; idx < wid ; idx += 1)
|
|
|
|
|
*tmp++ = 'z';
|
2001-12-15 03:13:33 +01:00
|
|
|
break;
|
|
|
|
|
case IVL_SIT_TRI0:
|
2005-02-10 05:55:45 +01:00
|
|
|
for (idx = 0 ; idx < wid ; idx += 1)
|
|
|
|
|
*tmp++ = '0';
|
2001-12-15 03:13:33 +01:00
|
|
|
break;
|
|
|
|
|
case IVL_SIT_TRI1:
|
2005-02-10 05:55:45 +01:00
|
|
|
for (idx = 0 ; idx < wid ; idx += 1)
|
|
|
|
|
*tmp++ = '1';
|
2001-12-15 03:13:33 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
2005-02-10 05:55:45 +01:00
|
|
|
*tmp++ = '>';
|
|
|
|
|
*tmp = 0;
|
2001-08-10 02:40:45 +02:00
|
|
|
ivl_nexus_set_private(nex, nex_private);
|
|
|
|
|
return nex_private;
|
|
|
|
|
}
|
2001-04-26 07:12:02 +02:00
|
|
|
|
2001-04-30 01:16:31 +02:00
|
|
|
|
2001-12-15 03:13:33 +01:00
|
|
|
/* 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) {
|
2001-08-10 02:40:45 +02:00
|
|
|
nex_private = strdup(draw_net_input_drive(nex, drivers[0]));
|
|
|
|
|
ivl_nexus_set_private(nex, nex_private);
|
|
|
|
|
return nex_private;
|
|
|
|
|
}
|
2001-04-26 07:12:02 +02:00
|
|
|
|
2001-07-16 20:31:49 +02:00
|
|
|
level = 0;
|
|
|
|
|
while (ndrivers) {
|
2003-12-19 02:27:10 +01:00
|
|
|
unsigned int inst;
|
2001-07-16 20:31:49 +02:00
|
|
|
for (inst = 0; inst < ndrivers; inst += 4) {
|
|
|
|
|
if (ndrivers > 4)
|
2004-10-04 03:10:51 +02:00
|
|
|
fprintf(vvp_out, "RS_%p/%d/%d .resolv tri",
|
2003-03-04 00:05:49 +01:00
|
|
|
nex, level, inst);
|
2004-10-04 03:10:51 +02:00
|
|
|
else
|
|
|
|
|
fprintf(vvp_out, "RS_%p .resolv %s",
|
2003-03-04 00:05:49 +01:00
|
|
|
nex, resolv_type);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-07-16 20:31:49 +02:00
|
|
|
for (idx = inst; idx < ndrivers && idx < inst+4; idx += 1) {
|
|
|
|
|
if (level) {
|
2003-03-04 00:05:49 +01:00
|
|
|
fprintf(vvp_out, ", RS_%p/%d/%d",
|
|
|
|
|
nex, level - 1, idx*4);
|
2001-07-16 20:31:49 +02:00
|
|
|
} else {
|
|
|
|
|
fprintf(vvp_out, ", %s",
|
|
|
|
|
draw_net_input_drive(nex, drivers[idx]));
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-02-08 01:12:36 +01:00
|
|
|
for ( ; idx < inst+4 ; idx += 1) {
|
|
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
draw_C4_repeated_constant('z',width_of_nexus(nex));
|
|
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-07-16 20:31:49 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
if (ndrivers > 4)
|
|
|
|
|
ndrivers = (ndrivers+3) / 4;
|
|
|
|
|
else
|
|
|
|
|
ndrivers = 0;
|
|
|
|
|
level += 1;
|
|
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2003-03-04 00:05:49 +01:00
|
|
|
sprintf(result, "RS_%p", nex);
|
2001-08-10 02:40:45 +02:00
|
|
|
nex_private = strdup(result);
|
|
|
|
|
ivl_nexus_set_private(nex, nex_private);
|
|
|
|
|
return nex_private;
|
2001-03-25 05:25:43 +02:00
|
|
|
}
|
|
|
|
|
|
2001-05-12 05:31:01 +02:00
|
|
|
|
|
|
|
|
|
2001-05-06 02:01:02 +02:00
|
|
|
/*
|
2001-08-10 02:40:45 +02:00
|
|
|
* This function looks at the nexus in search of the net to attach
|
2001-05-06 02:01:02 +02:00
|
|
|
* functor inputs to. Sort the signals in the nexus by name, and
|
|
|
|
|
* choose the lexically earliest one.
|
|
|
|
|
*/
|
2002-07-08 06:04:07 +02:00
|
|
|
void draw_input_from_net(ivl_nexus_t nex)
|
2001-05-06 02:01:02 +02:00
|
|
|
{
|
2001-08-10 02:40:45 +02:00
|
|
|
const char*nex_private = (const char*)ivl_nexus_get_private(nex);
|
|
|
|
|
if (nex_private == 0)
|
|
|
|
|
nex_private = draw_net_input(nex);
|
|
|
|
|
assert(nex_private);
|
|
|
|
|
fprintf(vvp_out, "%s", nex_private);
|
2001-05-06 02:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
2001-03-25 05:25:43 +02:00
|
|
|
/*
|
|
|
|
|
* This function draws a reg/int/variable in the scope. This is a very
|
|
|
|
|
* simple device to draw as there are no inputs to connect so no need
|
|
|
|
|
* to scan the nexus.
|
|
|
|
|
*/
|
|
|
|
|
static void draw_reg_in_scope(ivl_signal_t sig)
|
|
|
|
|
{
|
2003-08-23 01:14:26 +02:00
|
|
|
int msb = ivl_signal_msb(sig);
|
|
|
|
|
int lsb = ivl_signal_lsb(sig);
|
2001-03-25 05:25:43 +02:00
|
|
|
|
2002-06-21 06:59:35 +02:00
|
|
|
const char*signed_flag = ivl_signal_integer(sig) ? "/i" :
|
|
|
|
|
ivl_signal_signed(sig)? "/s" : "";
|
2001-04-05 03:38:24 +02:00
|
|
|
|
|
|
|
|
fprintf(vvp_out, "V_%s .var%s \"%s\", %d, %d;\n",
|
2002-08-04 00:30:48 +02:00
|
|
|
vvp_signal_label(sig), signed_flag,
|
2001-06-18 05:10:34 +02:00
|
|
|
vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb);
|
2001-05-06 02:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
2001-03-25 05:25:43 +02:00
|
|
|
/*
|
|
|
|
|
* This function draws a net. This is a bit more complicated as we
|
|
|
|
|
* have to find an appropriate functor to connect to the input.
|
|
|
|
|
*/
|
|
|
|
|
static void draw_net_in_scope(ivl_signal_t sig)
|
|
|
|
|
{
|
2003-08-23 01:14:26 +02:00
|
|
|
int msb = ivl_signal_msb(sig);
|
|
|
|
|
int lsb = ivl_signal_lsb(sig);
|
2001-10-24 05:43:45 +02:00
|
|
|
typedef const char*const_charp;
|
2004-12-11 03:31:25 +01:00
|
|
|
const char* arg;
|
2001-03-25 05:25:43 +02:00
|
|
|
|
2001-04-05 03:38:24 +02:00
|
|
|
const char*signed_flag = ivl_signal_signed(sig)? "/s" : "";
|
|
|
|
|
|
2002-07-05 23:26:17 +02:00
|
|
|
/* Skip the local signal. */
|
|
|
|
|
if (ivl_signal_local(sig))
|
|
|
|
|
return;
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
/* Connect the pin of the signal to something. */
|
|
|
|
|
{
|
|
|
|
|
ivl_nexus_t nex = ivl_signal_nex(sig);
|
|
|
|
|
arg = draw_net_input(nex);
|
2001-03-25 05:25:43 +02:00
|
|
|
}
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
fprintf(vvp_out, "V_%s .net%s \"%s\", %d, %d, %s;\n",
|
2002-08-04 00:30:48 +02:00
|
|
|
vvp_signal_label(sig), signed_flag,
|
2004-12-11 03:31:25 +01:00
|
|
|
vvp_mangle_name(ivl_signal_basename(sig)), msb, lsb, arg);
|
2001-05-12 05:31:01 +02:00
|
|
|
|
2001-03-25 05:25:43 +02:00
|
|
|
}
|
|
|
|
|
|
2001-12-06 04:31:24 +01:00
|
|
|
static void draw_delay(ivl_net_logic_t lptr)
|
|
|
|
|
{
|
|
|
|
|
unsigned d0 = ivl_logic_delay(lptr, 0);
|
|
|
|
|
unsigned d1 = ivl_logic_delay(lptr, 1);
|
|
|
|
|
unsigned d2 = ivl_logic_delay(lptr, 2);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-12-06 04:31:24 +01:00
|
|
|
if (d0 == 0 && d1 == 0 && d2 == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (d0 == d1 && d1 == d2)
|
|
|
|
|
fprintf(vvp_out, " (%d)", d0);
|
|
|
|
|
else
|
|
|
|
|
fprintf(vvp_out, " (%d,%d,%d)", d0, d1, d2);
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-24 04:23:58 +02:00
|
|
|
static void draw_udp_def(ivl_udp_t udp)
|
|
|
|
|
{
|
|
|
|
|
unsigned init;
|
2003-12-19 02:27:10 +01:00
|
|
|
unsigned i;
|
2001-04-24 04:23:58 +02:00
|
|
|
|
|
|
|
|
switch (ivl_udp_init(udp))
|
|
|
|
|
{
|
|
|
|
|
case '0':
|
|
|
|
|
init = 0;
|
|
|
|
|
break;
|
|
|
|
|
case '1':
|
|
|
|
|
init = 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
init = 2;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-24 04:59:52 +02:00
|
|
|
if (ivl_udp_sequ(udp))
|
2004-10-04 03:10:51 +02:00
|
|
|
fprintf(vvp_out,
|
2001-04-24 04:59:52 +02:00
|
|
|
"UDP_%s .udp/sequ \"%s\", %d, %d",
|
2001-06-18 05:10:34 +02:00
|
|
|
vvp_mangle_id(ivl_udp_name(udp)),
|
|
|
|
|
vvp_mangle_name(ivl_udp_name(udp)),
|
2001-04-24 04:59:52 +02:00
|
|
|
ivl_udp_nin(udp),
|
|
|
|
|
init );
|
|
|
|
|
else
|
2004-10-04 03:10:51 +02:00
|
|
|
fprintf(vvp_out,
|
2001-04-24 04:59:52 +02:00
|
|
|
"UDP_%s .udp/comb \"%s\", %d",
|
2001-06-18 05:10:34 +02:00
|
|
|
vvp_mangle_id(ivl_udp_name(udp)),
|
|
|
|
|
vvp_mangle_name(ivl_udp_name(udp)),
|
2001-04-24 04:59:52 +02:00
|
|
|
ivl_udp_nin(udp));
|
2001-04-24 04:23:58 +02:00
|
|
|
|
|
|
|
|
for (i=0; i<ivl_udp_rows(udp); i++)
|
|
|
|
|
fprintf(vvp_out, "\n ,\"%s\"", ivl_udp_row(udp, i) );
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void draw_udp_in_scope(ivl_net_logic_t lptr)
|
|
|
|
|
{
|
|
|
|
|
unsigned pdx;
|
|
|
|
|
|
|
|
|
|
ivl_udp_t udp = ivl_logic_udp(lptr);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-24 04:23:58 +02:00
|
|
|
static ivl_udp_t *udps = 0x0;
|
|
|
|
|
static int nudps = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i=0; i<nudps; i++)
|
|
|
|
|
if (udps[i] == udp)
|
|
|
|
|
break;
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-24 04:23:58 +02:00
|
|
|
if (i >= nudps)
|
|
|
|
|
{
|
2004-09-11 01:13:05 +02:00
|
|
|
udps = realloc(udps, (nudps+1)*sizeof(ivl_udp_t));
|
2001-04-24 04:23:58 +02:00
|
|
|
assert(udps);
|
|
|
|
|
udps[nudps++] = udp;
|
|
|
|
|
draw_udp_def(udp);
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-06 01:27:09 +01:00
|
|
|
fprintf(vvp_out, "L_%p .udp", lptr);
|
2004-10-04 03:10:51 +02:00
|
|
|
fprintf(vvp_out, " UDP_%s",
|
2001-06-18 05:10:34 +02:00
|
|
|
vvp_mangle_id(ivl_udp_name(udp)));
|
2001-12-06 04:31:24 +01:00
|
|
|
draw_delay(lptr);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2003-05-13 03:56:15 +02:00
|
|
|
for (pdx = 1 ; pdx < ivl_logic_pins(lptr) ; pdx += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_logic_pin(lptr, pdx);
|
|
|
|
|
|
|
|
|
|
/* Unlike other logic gates, primitives may have unconnected
|
|
|
|
|
inputs. The proper behavior is to attach a HiZ to the
|
|
|
|
|
port. */
|
|
|
|
|
if (nex == 0) {
|
|
|
|
|
fprintf(vvp_out, ", C<z>");
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
draw_input_from_net(nex);
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-24 04:23:58 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-27 08:27:40 +02:00
|
|
|
static void draw_logic_in_scope(ivl_net_logic_t lptr)
|
|
|
|
|
{
|
2001-06-18 05:10:34 +02:00
|
|
|
unsigned pdx;
|
2001-03-27 08:27:40 +02:00
|
|
|
const char*ltype = "?";
|
2001-06-18 05:10:34 +02:00
|
|
|
const char*lcasc = 0x0;
|
2001-05-02 06:05:16 +02:00
|
|
|
char identity_val = '0';
|
2001-12-14 03:05:13 +01:00
|
|
|
|
2004-12-30 00:52:09 +01:00
|
|
|
unsigned vector_width = width_of_nexus(ivl_logic_pin(lptr, 0));
|
|
|
|
|
|
2001-12-14 03:05:13 +01:00
|
|
|
ivl_drive_t str0, str1;
|
|
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
int level;
|
2001-10-24 05:43:45 +02:00
|
|
|
int ninp = ivl_logic_pins(lptr) - 1;
|
|
|
|
|
typedef const char*const_charp;
|
|
|
|
|
const_charp*input_strings = calloc(ninp, sizeof(const_charp));
|
2001-04-24 04:23:58 +02:00
|
|
|
|
2003-05-13 03:56:15 +02:00
|
|
|
for (pdx = 0 ; pdx < ninp ; pdx += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_logic_pin(lptr, pdx+1);
|
|
|
|
|
if (nex == 0) {
|
|
|
|
|
/* Only UDPs can have unconnected inputs. */
|
|
|
|
|
assert(ivl_logic_type(lptr) == IVL_LO_UDP);
|
|
|
|
|
input_strings[pdx] = 0;
|
|
|
|
|
} else {
|
|
|
|
|
input_strings[pdx] = draw_net_input(nex);
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-24 04:23:58 +02:00
|
|
|
switch (ivl_logic_type(lptr)) {
|
|
|
|
|
|
|
|
|
|
case IVL_LO_UDP:
|
2003-05-13 03:56:15 +02:00
|
|
|
free(input_strings);
|
2001-04-24 04:23:58 +02:00
|
|
|
draw_udp_in_scope(lptr);
|
|
|
|
|
return;
|
|
|
|
|
|
2002-01-12 05:03:39 +01:00
|
|
|
case IVL_LO_BUFZ: {
|
2004-09-25 23:04:25 +02:00
|
|
|
/* Draw bufz objects, but only if the gate cannot
|
|
|
|
|
be elided. If I can elide it, then the
|
|
|
|
|
draw_nex_input will take care of it for me. */
|
2002-01-12 05:03:39 +01:00
|
|
|
ivl_nexus_ptr_t nptr = ivl_logic_pin_ptr(lptr,0);
|
|
|
|
|
|
|
|
|
|
ltype = "BUFZ";
|
|
|
|
|
|
2004-09-25 23:04:25 +02:00
|
|
|
if (can_elide_bufz(lptr, nptr))
|
|
|
|
|
return;
|
2002-01-12 05:03:39 +01:00
|
|
|
|
2004-09-25 23:04:25 +02:00
|
|
|
break;
|
2002-01-12 05:03:39 +01:00
|
|
|
}
|
2001-03-27 08:27:40 +02:00
|
|
|
|
2001-04-30 01:16:31 +02:00
|
|
|
case IVL_LO_PULLDOWN:
|
|
|
|
|
case IVL_LO_PULLUP:
|
|
|
|
|
/* Skip pullup and pulldown objects. Things that have
|
|
|
|
|
pull objects as inputs will instead generate the
|
|
|
|
|
appropriate C<?> symbol. */
|
2003-05-13 03:56:15 +02:00
|
|
|
free(input_strings);
|
2001-04-30 01:16:31 +02:00
|
|
|
return;
|
|
|
|
|
|
2001-03-27 08:27:40 +02:00
|
|
|
case IVL_LO_AND:
|
|
|
|
|
ltype = "AND";
|
2001-05-02 06:05:16 +02:00
|
|
|
identity_val = '1';
|
2001-03-27 08:27:40 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-04-01 23:34:48 +02:00
|
|
|
case IVL_LO_BUF:
|
|
|
|
|
ltype = "BUF";
|
|
|
|
|
break;
|
|
|
|
|
|
2001-04-30 01:16:31 +02:00
|
|
|
case IVL_LO_BUFIF0:
|
|
|
|
|
ltype = "BUFIF0";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_LO_BUFIF1:
|
|
|
|
|
ltype = "BUFIF1";
|
|
|
|
|
break;
|
|
|
|
|
|
2001-04-21 04:04:01 +02:00
|
|
|
case IVL_LO_NAND:
|
|
|
|
|
ltype = "NAND";
|
2001-06-18 05:10:34 +02:00
|
|
|
lcasc = "AND";
|
2001-05-02 06:05:16 +02:00
|
|
|
identity_val = '1';
|
2001-04-21 04:04:01 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-03-27 08:27:40 +02:00
|
|
|
case IVL_LO_NOR:
|
|
|
|
|
ltype = "NOR";
|
2001-06-18 05:10:34 +02:00
|
|
|
lcasc = "OR";
|
2001-03-27 08:27:40 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_LO_NOT:
|
|
|
|
|
ltype = "NOT";
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_LO_OR:
|
|
|
|
|
ltype = "OR";
|
|
|
|
|
break;
|
|
|
|
|
|
2001-04-21 04:04:01 +02:00
|
|
|
case IVL_LO_XNOR:
|
|
|
|
|
ltype = "XNOR";
|
2001-06-18 05:10:34 +02:00
|
|
|
lcasc = "XOR";
|
2001-04-21 04:04:01 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-04-15 18:37:48 +02:00
|
|
|
case IVL_LO_XOR:
|
|
|
|
|
ltype = "XOR";
|
|
|
|
|
break;
|
|
|
|
|
|
2001-08-03 19:06:10 +02:00
|
|
|
case IVL_LO_PMOS:
|
2001-10-09 04:28:44 +02:00
|
|
|
ltype = "PMOS";
|
2001-08-03 19:06:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_LO_NMOS:
|
2001-10-09 04:28:44 +02:00
|
|
|
ltype = "NMOS";
|
2001-08-03 19:06:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_LO_RPMOS:
|
2001-10-18 19:30:25 +02:00
|
|
|
ltype = "RPMOS";
|
2001-08-03 19:06:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_LO_RNMOS:
|
2001-10-18 19:30:25 +02:00
|
|
|
ltype = "RNMOS";
|
2001-08-03 19:06:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_LO_NOTIF0:
|
2001-12-14 07:03:34 +01:00
|
|
|
ltype = "NOTIF0";
|
2001-08-03 19:06:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IVL_LO_NOTIF1:
|
2001-12-14 07:03:34 +01:00
|
|
|
ltype = "NOTIF1";
|
2001-08-03 19:06:10 +02:00
|
|
|
break;
|
|
|
|
|
|
2001-03-27 08:27:40 +02:00
|
|
|
default:
|
2001-04-30 01:16:31 +02:00
|
|
|
fprintf(stderr, "vvp.tgt: error: Unhandled logic type: %u\n",
|
|
|
|
|
ivl_logic_type(lptr));
|
2001-03-27 08:27:40 +02:00
|
|
|
ltype = "?";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2001-12-14 03:05:13 +01:00
|
|
|
{ ivl_nexus_t nex = ivl_logic_pin(lptr, 0);
|
|
|
|
|
ivl_nexus_ptr_t nptr = 0;
|
|
|
|
|
unsigned idx;
|
|
|
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
|
|
|
|
nptr = ivl_nexus_ptr(nex,idx);
|
|
|
|
|
if (ivl_nexus_ptr_log(nptr) != lptr)
|
|
|
|
|
continue;
|
|
|
|
|
if (ivl_nexus_ptr_pin(nptr) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
str0 = ivl_nexus_ptr_drive0(nptr);
|
|
|
|
|
str1 = ivl_nexus_ptr_drive1(nptr);
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
if (!lcasc)
|
|
|
|
|
lcasc = ltype;
|
|
|
|
|
|
2001-10-24 05:43:45 +02:00
|
|
|
/* Get all the input label that I will use for parameters to
|
|
|
|
|
the functor that I create later. */
|
|
|
|
|
ninp = ivl_logic_pins(lptr) - 1;
|
|
|
|
|
input_strings = calloc(ninp, sizeof(char*));
|
|
|
|
|
for (pdx = 0 ; pdx < ninp ; pdx += 1)
|
|
|
|
|
input_strings[pdx] = draw_net_input(ivl_logic_pin(lptr, pdx+1));
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
level = 0;
|
|
|
|
|
ninp = ivl_logic_pins(lptr) - 1;
|
|
|
|
|
while (ninp) {
|
|
|
|
|
int inst;
|
|
|
|
|
for (inst = 0; inst < ninp; inst += 4) {
|
|
|
|
|
if (ninp > 4)
|
2004-10-04 03:10:51 +02:00
|
|
|
fprintf(vvp_out, "L_%p/%d/%d .functor %s",
|
2003-03-06 01:27:09 +01:00
|
|
|
lptr, level, inst, lcasc);
|
2001-12-06 04:31:24 +01:00
|
|
|
else {
|
2004-10-04 03:10:51 +02:00
|
|
|
fprintf(vvp_out, "L_%p .functor %s",
|
2003-03-06 01:27:09 +01:00
|
|
|
lptr, ltype);
|
2001-12-14 03:05:13 +01:00
|
|
|
|
2001-12-06 04:31:24 +01:00
|
|
|
draw_delay(lptr);
|
2001-12-14 03:05:13 +01:00
|
|
|
|
|
|
|
|
if (str0 != IVL_DR_STRONG || str1 != IVL_DR_STRONG)
|
|
|
|
|
fprintf(vvp_out, " [%u %u]", str0, str1);
|
|
|
|
|
|
2001-12-06 04:31:24 +01:00
|
|
|
}
|
2001-06-18 05:10:34 +02:00
|
|
|
for (pdx = inst; pdx < ninp && pdx < inst+4 ; pdx += 1) {
|
|
|
|
|
if (level) {
|
2003-03-06 01:27:09 +01:00
|
|
|
fprintf(vvp_out, ", L_%p/%d/%d",
|
|
|
|
|
lptr, level - 1, pdx*4);
|
2001-06-18 05:10:34 +02:00
|
|
|
} else {
|
2001-10-24 05:43:45 +02:00
|
|
|
fprintf(vvp_out, ", %s", input_strings[pdx]);
|
2001-06-18 05:10:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for ( ; pdx < inst+4 ; pdx += 1) {
|
2004-12-30 00:52:09 +01:00
|
|
|
unsigned wdx;
|
|
|
|
|
fprintf(vvp_out, ", C4<");
|
|
|
|
|
for (wdx = 0 ; wdx < vector_width ; wdx += 1)
|
|
|
|
|
fprintf(vvp_out, "%c", identity_val);
|
|
|
|
|
fprintf(vvp_out, ">");
|
2001-06-18 05:10:34 +02:00
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
if (ninp > 4)
|
|
|
|
|
ninp = (ninp+3) / 4;
|
|
|
|
|
else
|
|
|
|
|
ninp = 0;
|
2001-07-09 17:38:35 +02:00
|
|
|
level += 1;
|
2001-05-02 06:05:16 +02:00
|
|
|
}
|
2001-10-24 05:43:45 +02:00
|
|
|
|
|
|
|
|
/* Free the array of char*. The strings themselves are
|
|
|
|
|
persistent, held by the ivl_nexus_t objects. */
|
|
|
|
|
free(input_strings);
|
2001-03-27 08:27:40 +02:00
|
|
|
}
|
|
|
|
|
|
2001-03-28 08:07:39 +02:00
|
|
|
static void draw_event_in_scope(ivl_event_t obj)
|
|
|
|
|
{
|
2001-04-01 03:48:21 +02:00
|
|
|
unsigned nany = ivl_event_nany(obj);
|
|
|
|
|
unsigned nneg = ivl_event_nneg(obj);
|
|
|
|
|
unsigned npos = ivl_event_npos(obj);
|
2001-04-14 07:11:49 +02:00
|
|
|
|
2001-05-03 06:55:46 +02:00
|
|
|
unsigned cnt = 0;
|
|
|
|
|
|
|
|
|
|
/* Figure out how many probe functors are needed. */
|
|
|
|
|
if (nany > 0)
|
|
|
|
|
cnt += (nany+3) / 4;
|
|
|
|
|
|
|
|
|
|
if (nneg > 0)
|
|
|
|
|
cnt += (nneg+3) / 4;
|
|
|
|
|
|
|
|
|
|
if (npos > 0)
|
|
|
|
|
cnt += (npos+3) / 4;
|
|
|
|
|
|
|
|
|
|
if (cnt == 0) {
|
|
|
|
|
/* If none are needed, then this is a named event. The
|
|
|
|
|
code needed is easy. */
|
2003-03-06 02:17:46 +01:00
|
|
|
fprintf(vvp_out, "E_%p .event \"%s\";\n", obj,
|
2001-06-18 05:10:34 +02:00
|
|
|
vvp_mangle_name(ivl_event_basename(obj)));
|
2001-03-28 08:07:39 +02:00
|
|
|
|
2001-05-03 06:55:46 +02:00
|
|
|
} else if (cnt > 1) {
|
2003-10-10 01:45:03 +02:00
|
|
|
/* There are a bunch of events that need to be event/or
|
|
|
|
|
combined. */
|
2001-04-14 07:11:49 +02:00
|
|
|
unsigned idx;
|
2001-05-03 06:55:46 +02:00
|
|
|
unsigned ecnt = 0;
|
2001-04-14 07:11:49 +02:00
|
|
|
|
2001-05-03 06:55:46 +02:00
|
|
|
for (idx = 0 ; idx < nany ; idx += 4, ecnt += 1) {
|
2001-04-14 07:11:49 +02:00
|
|
|
unsigned sub, top;
|
|
|
|
|
|
2003-03-06 02:17:46 +01:00
|
|
|
fprintf(vvp_out, "E_%p/%u .event edge", obj, ecnt);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-14 07:11:49 +02:00
|
|
|
top = idx + 4;
|
|
|
|
|
if (nany < top)
|
|
|
|
|
top = nany;
|
|
|
|
|
for (sub = idx ; sub < top ; sub += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_event_any(obj, sub);
|
|
|
|
|
fprintf(vvp_out, ", ");
|
2001-05-06 02:01:02 +02:00
|
|
|
draw_input_from_net(nex);
|
2001-04-14 07:11:49 +02:00
|
|
|
}
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-03 06:55:46 +02:00
|
|
|
for (idx = 0 ; idx < nneg ; idx += 4, ecnt += 1) {
|
|
|
|
|
unsigned sub, top;
|
|
|
|
|
|
2003-03-06 02:17:46 +01:00
|
|
|
fprintf(vvp_out, "E_%p/%u .event negedge", obj, ecnt);
|
2001-05-03 06:55:46 +02:00
|
|
|
|
|
|
|
|
top = idx + 4;
|
|
|
|
|
if (nneg < top)
|
|
|
|
|
top = nneg;
|
|
|
|
|
for (sub = idx ; sub < top ; sub += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_event_neg(obj, sub);
|
|
|
|
|
fprintf(vvp_out, ", ");
|
2001-05-06 02:01:02 +02:00
|
|
|
draw_input_from_net(nex);
|
2001-05-03 06:55:46 +02:00
|
|
|
}
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < npos ; idx += 4, ecnt += 1) {
|
|
|
|
|
unsigned sub, top;
|
|
|
|
|
|
2003-03-06 02:17:46 +01:00
|
|
|
fprintf(vvp_out, "E_%p/%u .event posedge", obj, ecnt);
|
2001-05-03 06:55:46 +02:00
|
|
|
|
|
|
|
|
top = idx + 4;
|
|
|
|
|
if (npos < top)
|
|
|
|
|
top = npos;
|
|
|
|
|
for (sub = idx ; sub < top ; sub += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_event_pos(obj, sub);
|
|
|
|
|
fprintf(vvp_out, ", ");
|
2001-05-06 02:01:02 +02:00
|
|
|
draw_input_from_net(nex);
|
2001-05-03 06:55:46 +02:00
|
|
|
}
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(ecnt == cnt);
|
|
|
|
|
|
2003-03-06 02:17:46 +01:00
|
|
|
fprintf(vvp_out, "E_%p .event/or", obj);
|
|
|
|
|
fprintf(vvp_out, " E_%p/0", obj);
|
2001-04-14 07:11:49 +02:00
|
|
|
|
2001-05-03 06:55:46 +02:00
|
|
|
for (idx = 1 ; idx < cnt ; idx += 1)
|
2003-03-06 02:17:46 +01:00
|
|
|
fprintf(vvp_out, ", E_%p/%u", obj, idx);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-14 07:11:49 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-03-28 08:07:39 +02:00
|
|
|
} else {
|
2003-10-10 01:45:03 +02:00
|
|
|
unsigned num_input_strings = nany + nneg + npos;
|
2001-03-28 08:07:39 +02:00
|
|
|
unsigned idx;
|
2003-10-10 01:45:03 +02:00
|
|
|
const char* input_strings[4];
|
|
|
|
|
const char*edge = 0;
|
2001-04-01 03:48:21 +02:00
|
|
|
|
|
|
|
|
if (nany > 0) {
|
|
|
|
|
assert((nneg + npos) == 0);
|
|
|
|
|
assert(nany <= 4);
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2003-10-10 01:45:03 +02:00
|
|
|
edge = "edge";
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-01 03:48:21 +02:00
|
|
|
for (idx = 0 ; idx < nany ; idx += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_event_any(obj, idx);
|
2003-10-10 01:45:03 +02:00
|
|
|
input_strings[idx] = draw_net_input(nex);
|
2001-04-01 03:48:21 +02:00
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-01 03:48:21 +02:00
|
|
|
} else if (nneg > 0) {
|
|
|
|
|
assert((nany + npos) == 0);
|
2003-10-10 01:45:03 +02:00
|
|
|
edge = "negedge";
|
2001-04-01 03:48:21 +02:00
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < nneg ; idx += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_event_neg(obj, idx);
|
2003-10-10 01:45:03 +02:00
|
|
|
input_strings[idx] = draw_net_input(nex);
|
2001-04-01 03:48:21 +02:00
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-01 03:48:21 +02:00
|
|
|
} else {
|
|
|
|
|
assert((nany + nneg) == 0);
|
2003-10-10 01:45:03 +02:00
|
|
|
edge = "posedge";
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-04-01 03:48:21 +02:00
|
|
|
for (idx = 0 ; idx < npos ; idx += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_event_pos(obj, idx);
|
2003-10-10 01:45:03 +02:00
|
|
|
input_strings[idx] = draw_net_input(nex);
|
2001-04-01 03:48:21 +02:00
|
|
|
}
|
2001-03-28 08:07:39 +02:00
|
|
|
}
|
2003-10-10 01:45:03 +02:00
|
|
|
|
|
|
|
|
fprintf(vvp_out, "E_%p .event %s", obj, edge);
|
|
|
|
|
for (idx = 0 ; idx < num_input_strings ; idx += 1)
|
|
|
|
|
fprintf(vvp_out, ", %s", input_strings[idx]);
|
|
|
|
|
|
2001-03-28 08:07:39 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-16 04:41:41 +02:00
|
|
|
inline static void draw_lpm_ram(ivl_lpm_t net)
|
|
|
|
|
{
|
|
|
|
|
unsigned idx;
|
|
|
|
|
unsigned width = ivl_lpm_width(net);
|
|
|
|
|
unsigned awidth = ivl_lpm_selects(net);
|
|
|
|
|
ivl_memory_t mem = ivl_lpm_memory(net);
|
|
|
|
|
ivl_nexus_t clk = ivl_lpm_clk(net);
|
|
|
|
|
ivl_nexus_t pin;
|
|
|
|
|
|
|
|
|
|
if (clk) {
|
2003-03-13 07:07:11 +01:00
|
|
|
fprintf(vvp_out, "CLK_%p .event posedge, ", net);
|
2001-10-22 01:38:16 +02:00
|
|
|
draw_input_from_net(clk);
|
2001-06-16 04:41:41 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-13 07:07:11 +01:00
|
|
|
fprintf(vvp_out, "L_%p .mem/port", net);
|
2004-10-04 03:10:51 +02:00
|
|
|
fprintf(vvp_out,
|
|
|
|
|
" M_%s, %d,0, %d,\n ",
|
2002-08-04 20:28:14 +02:00
|
|
|
vvp_memory_label(mem),
|
2001-06-16 04:41:41 +02:00
|
|
|
width-1,
|
|
|
|
|
awidth);
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < awidth ; idx += 1) {
|
|
|
|
|
pin = ivl_lpm_select(net, idx);
|
|
|
|
|
if (idx) fprintf(vvp_out, ", ");
|
|
|
|
|
draw_input_from_net(pin);
|
|
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-16 04:41:41 +02:00
|
|
|
if (clk) {
|
2003-03-13 07:07:11 +01:00
|
|
|
fprintf(vvp_out, ",\n CLK_%p, ", net);
|
2001-06-16 04:41:41 +02:00
|
|
|
pin = ivl_lpm_enable(net);
|
|
|
|
|
if (pin)
|
|
|
|
|
draw_input_from_net(pin);
|
|
|
|
|
else
|
|
|
|
|
fprintf(vvp_out, "C<1>");
|
|
|
|
|
for (idx=0; idx<width; idx++) {
|
|
|
|
|
pin = ivl_lpm_data(net, idx);
|
|
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
draw_input_from_net(pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2001-06-16 04:41:41 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-15 06:14:18 +02:00
|
|
|
static void draw_lpm_arith_a_b_inputs(ivl_lpm_t net)
|
|
|
|
|
{
|
|
|
|
|
unsigned width = ivl_lpm_width(net);
|
|
|
|
|
unsigned idx;
|
2004-12-11 03:31:25 +01:00
|
|
|
|
|
|
|
|
assert(width > 0);
|
|
|
|
|
ivl_nexus_t nex = ivl_lpm_data(net, 0);
|
|
|
|
|
ivl_signal_t sig = 0;
|
|
|
|
|
|
|
|
|
|
ivl_nexus_ptr_t np;
|
|
|
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
|
|
|
|
np = ivl_nexus_ptr(nex,idx);
|
|
|
|
|
sig = ivl_nexus_ptr_sig(np);
|
|
|
|
|
if (sig != 0)
|
|
|
|
|
break;
|
2001-06-15 06:14:18 +02:00
|
|
|
}
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
assert(sig != 0);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ", V_%s", vvp_signal_label(sig));
|
|
|
|
|
|
|
|
|
|
sig = 0;
|
2005-01-16 05:20:32 +01:00
|
|
|
nex = ivl_lpm_data(net, 1);
|
2004-12-11 03:31:25 +01:00
|
|
|
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
|
|
|
|
|
np = ivl_nexus_ptr(nex,idx);
|
|
|
|
|
sig = ivl_nexus_ptr_sig(np);
|
|
|
|
|
if (sig != 0)
|
|
|
|
|
break;
|
2001-06-15 06:14:18 +02:00
|
|
|
}
|
2004-12-11 03:31:25 +01:00
|
|
|
|
|
|
|
|
assert(sig != 0);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ", V_%s", vvp_signal_label(sig));
|
2001-06-15 06:14:18 +02:00
|
|
|
}
|
|
|
|
|
|
2005-01-12 04:16:35 +01:00
|
|
|
/*
|
|
|
|
|
* This function draws any functors needed to calculate the input to
|
|
|
|
|
* this nexus, and leaves in the data array strings that can be used
|
2005-01-12 06:31:50 +01:00
|
|
|
* as functor arguments. The strings are from the draw_net_input
|
|
|
|
|
* function, which in turn returns nexus names, so the strings are
|
|
|
|
|
* safe to pass around.
|
2005-01-12 04:16:35 +01:00
|
|
|
*/
|
|
|
|
|
static void draw_lpm_data_inputs(ivl_lpm_t net, unsigned base,
|
|
|
|
|
unsigned ndata, const char**src_table)
|
2004-12-30 00:52:09 +01:00
|
|
|
{
|
|
|
|
|
unsigned idx;
|
|
|
|
|
for (idx = 0 ; idx < ndata ; idx += 1) {
|
2005-01-10 02:42:59 +01:00
|
|
|
ivl_nexus_t nex = ivl_lpm_data(net, base+idx);
|
2005-01-12 04:16:35 +01:00
|
|
|
src_table[idx] = draw_net_input(nex);
|
2004-12-30 00:52:09 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-07 04:12:43 +02:00
|
|
|
static void draw_lpm_add(ivl_lpm_t net)
|
|
|
|
|
{
|
2005-01-12 06:31:50 +01:00
|
|
|
const char*src_table[2];
|
2001-06-15 06:14:18 +02:00
|
|
|
unsigned width;
|
2001-06-07 05:09:37 +02:00
|
|
|
const char*type = "";
|
2001-06-07 04:12:43 +02:00
|
|
|
|
|
|
|
|
width = ivl_lpm_width(net);
|
|
|
|
|
|
2001-06-07 05:09:37 +02:00
|
|
|
switch (ivl_lpm_type(net)) {
|
|
|
|
|
case IVL_LPM_ADD:
|
|
|
|
|
type = "sum";
|
|
|
|
|
break;
|
|
|
|
|
case IVL_LPM_SUB:
|
|
|
|
|
type = "sub";
|
|
|
|
|
break;
|
2001-06-17 01:45:05 +02:00
|
|
|
case IVL_LPM_MULT:
|
|
|
|
|
type = "mult";
|
|
|
|
|
break;
|
2001-10-16 04:19:26 +02:00
|
|
|
case IVL_LPM_DIVIDE:
|
2004-06-30 04:16:26 +02:00
|
|
|
if (ivl_lpm_signed(net))
|
|
|
|
|
type = "div.s";
|
|
|
|
|
else
|
|
|
|
|
type = "div";
|
2001-10-16 04:19:26 +02:00
|
|
|
break;
|
2002-01-03 05:19:01 +01:00
|
|
|
case IVL_LPM_MOD:
|
|
|
|
|
type = "mod";
|
|
|
|
|
break;
|
2001-06-07 05:09:37 +02:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-12 06:31:50 +01:00
|
|
|
draw_lpm_data_inputs(net, 0, 2, src_table);
|
|
|
|
|
fprintf(vvp_out, "L_%p .arith/%s %u, %s, %s;\n",
|
|
|
|
|
net, type, width, src_table[0], src_table[1]);
|
2001-06-15 06:14:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void draw_lpm_cmp(ivl_lpm_t net)
|
|
|
|
|
{
|
2005-01-16 05:20:32 +01:00
|
|
|
const char*src_table[2];
|
2001-06-15 06:14:18 +02:00
|
|
|
unsigned width;
|
|
|
|
|
const char*type = "";
|
2003-04-11 07:18:08 +02:00
|
|
|
const char*signed_string = ivl_lpm_signed(net)? ".s" : "";
|
2001-06-15 06:14:18 +02:00
|
|
|
|
|
|
|
|
width = ivl_lpm_width(net);
|
|
|
|
|
|
|
|
|
|
switch (ivl_lpm_type(net)) {
|
2005-01-22 02:06:55 +01:00
|
|
|
case IVL_LPM_CMP_EEQ:
|
|
|
|
|
type = "eeq";
|
|
|
|
|
signed_string = "";
|
|
|
|
|
break;
|
2005-01-22 17:22:13 +01:00
|
|
|
case IVL_LPM_CMP_EQ:
|
|
|
|
|
type = "eq";
|
|
|
|
|
signed_string = "";
|
|
|
|
|
break;
|
2001-06-15 06:14:18 +02:00
|
|
|
case IVL_LPM_CMP_GE:
|
|
|
|
|
type = "ge";
|
|
|
|
|
break;
|
|
|
|
|
case IVL_LPM_CMP_GT:
|
|
|
|
|
type = "gt";
|
|
|
|
|
break;
|
2005-01-22 17:22:13 +01:00
|
|
|
case IVL_LPM_CMP_NE:
|
|
|
|
|
type = "ne";
|
|
|
|
|
signed_string = "";
|
|
|
|
|
break;
|
2001-06-15 06:14:18 +02:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
2001-06-07 04:12:43 +02:00
|
|
|
}
|
|
|
|
|
|
2005-01-16 05:20:32 +01:00
|
|
|
draw_lpm_data_inputs(net, 0, 2, src_table);
|
|
|
|
|
fprintf(vvp_out, "L_%p .cmp/%s%s %u, %s, %s;\n",
|
|
|
|
|
net, type, signed_string, width,
|
|
|
|
|
src_table[0], src_table[1]);
|
2001-06-07 04:12:43 +02:00
|
|
|
}
|
|
|
|
|
|
2005-01-10 02:42:59 +01:00
|
|
|
/*
|
|
|
|
|
* This function draws the arguments to a .const node using the
|
|
|
|
|
* lpm inputs starting at "start" and for "cnt" inputs. This input
|
|
|
|
|
* count must be <= 4. It is up to the caller to write the header part
|
|
|
|
|
* of the statement, and to organize the data into multiple
|
|
|
|
|
* statements.
|
|
|
|
|
*
|
|
|
|
|
* Return the width of the final concatenation.
|
|
|
|
|
*/
|
2005-01-12 04:16:35 +01:00
|
|
|
static unsigned lpm_concat_inputs(ivl_lpm_t net, unsigned start,
|
|
|
|
|
unsigned cnt, const char*src_table[])
|
2004-12-30 00:52:09 +01:00
|
|
|
{
|
|
|
|
|
unsigned idx;
|
2005-01-10 02:42:59 +01:00
|
|
|
unsigned wid = 0;
|
2004-12-30 00:52:09 +01:00
|
|
|
|
2005-01-10 02:42:59 +01:00
|
|
|
assert(cnt <= 4);
|
2004-12-30 00:52:09 +01:00
|
|
|
|
2005-01-10 02:42:59 +01:00
|
|
|
/* First, draw the [L M N O] part of the statement, the list
|
|
|
|
|
of widths for the .concat statement. */
|
|
|
|
|
fprintf(vvp_out, "[");
|
2004-12-30 00:52:09 +01:00
|
|
|
|
2005-01-10 02:42:59 +01:00
|
|
|
for (idx = 0 ; idx < cnt ; idx += 1) {
|
|
|
|
|
ivl_nexus_t nex = ivl_lpm_data(net, start+idx);
|
2004-12-30 00:52:09 +01:00
|
|
|
unsigned nexus_width = width_of_nexus(nex);
|
|
|
|
|
fprintf(vvp_out, " %u", nexus_width);
|
2005-01-10 02:42:59 +01:00
|
|
|
wid += nexus_width;
|
2004-12-30 00:52:09 +01:00
|
|
|
}
|
|
|
|
|
|
2005-01-10 02:42:59 +01:00
|
|
|
for ( ; idx < 4 ; idx += 1)
|
2005-01-09 21:16:00 +01:00
|
|
|
fprintf(vvp_out, " 0");
|
2004-12-30 00:52:09 +01:00
|
|
|
|
|
|
|
|
fprintf(vvp_out, "]");
|
|
|
|
|
|
2005-01-12 04:16:35 +01:00
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < cnt ; idx += 1) {
|
|
|
|
|
fprintf(vvp_out, ", %s", src_table[idx]);
|
|
|
|
|
}
|
2004-12-30 00:52:09 +01:00
|
|
|
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
2005-01-10 02:42:59 +01:00
|
|
|
return wid;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-04 06:13:57 +01:00
|
|
|
/*
|
|
|
|
|
* Implement the general IVL_LPM_CONCAT using .concat nodes. Use as
|
|
|
|
|
* many nested nodes as necessary to support the desired number of
|
|
|
|
|
* input vectors.
|
|
|
|
|
*/
|
2005-01-10 02:42:59 +01:00
|
|
|
static void draw_lpm_concat(ivl_lpm_t net)
|
|
|
|
|
{
|
2005-01-12 04:16:35 +01:00
|
|
|
const char*src_table[4];
|
2005-01-10 02:42:59 +01:00
|
|
|
unsigned icnt = ivl_lpm_selects(net);
|
|
|
|
|
|
|
|
|
|
if (icnt <= 4) {
|
2005-02-04 06:13:57 +01:00
|
|
|
/* This is the easies case. There are 4 or fewer input
|
|
|
|
|
vectors, so the entire IVL_LPM_CONCAT can be
|
|
|
|
|
implemented with a single .concat node. */
|
2005-01-12 04:16:35 +01:00
|
|
|
draw_lpm_data_inputs(net, 0, icnt, src_table);
|
2005-01-10 02:42:59 +01:00
|
|
|
fprintf(vvp_out, "L_%p .concat ", net);
|
2005-01-12 04:16:35 +01:00
|
|
|
lpm_concat_inputs(net, 0, icnt, src_table);
|
2005-01-10 02:42:59 +01:00
|
|
|
|
|
|
|
|
} else {
|
2005-02-04 06:13:57 +01:00
|
|
|
/* If there are more then 4 inputs, things get more
|
|
|
|
|
complicated. We need to generate a balanced tree of
|
|
|
|
|
.concat nodes to blend the inputs down to a single
|
|
|
|
|
root node, that becomes the output from the
|
|
|
|
|
concatenation. */
|
2005-01-10 02:42:59 +01:00
|
|
|
unsigned idx, depth;
|
|
|
|
|
struct concat_tree {
|
|
|
|
|
unsigned base;
|
|
|
|
|
unsigned wid;
|
|
|
|
|
} *tree;
|
|
|
|
|
|
|
|
|
|
tree = malloc((icnt + 3)/4 * sizeof(struct concat_tree));
|
|
|
|
|
|
2005-02-04 06:13:57 +01:00
|
|
|
/* First, fill in all the leaves with the initial inputs
|
|
|
|
|
to the tree. After this loop, there are (icnt+3)/4
|
|
|
|
|
.concat nodes drawn, that together take all the
|
|
|
|
|
inputs. */
|
2005-01-10 02:42:59 +01:00
|
|
|
for (idx = 0 ; idx < icnt ; idx += 4) {
|
|
|
|
|
unsigned wid = 0;
|
|
|
|
|
unsigned trans = 4;
|
|
|
|
|
if ((idx + trans) > icnt)
|
|
|
|
|
trans = icnt - idx;
|
|
|
|
|
|
2005-01-12 04:16:35 +01:00
|
|
|
draw_lpm_data_inputs(net, idx, trans, src_table);
|
2005-01-10 02:42:59 +01:00
|
|
|
fprintf(vvp_out, "LS_%p_0_%u .concat ", net, idx);
|
2005-01-12 04:16:35 +01:00
|
|
|
wid = lpm_concat_inputs(net, idx, trans, src_table);
|
2005-01-10 02:42:59 +01:00
|
|
|
|
|
|
|
|
tree[idx/4].base = idx;
|
|
|
|
|
tree[idx/4].wid = wid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
icnt = (icnt + 3)/4;
|
2005-02-04 06:13:57 +01:00
|
|
|
|
|
|
|
|
/* Tree now has icnt nodes that are depth=0 concat nodes
|
|
|
|
|
which take in the leaf inputs. The while loop below
|
|
|
|
|
starts and ends with a tree of icnt nodes. Each time
|
|
|
|
|
through, there are 1/4 the nodes we started
|
|
|
|
|
with. Thus, we eventually get down to <=4 nodes, and
|
|
|
|
|
that is when we fall out of the loop. */
|
|
|
|
|
|
|
|
|
|
depth = 1;
|
2005-01-10 02:42:59 +01:00
|
|
|
while (icnt > 4) {
|
2005-02-04 06:13:57 +01:00
|
|
|
for (idx = 0 ; idx < icnt ; idx += 4) {
|
|
|
|
|
unsigned tdx;
|
|
|
|
|
unsigned wid = 0;
|
|
|
|
|
unsigned trans = 4;
|
|
|
|
|
if ((idx+trans) > icnt)
|
|
|
|
|
trans = icnt - idx;
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, "LS_%p_%u_%u .concat [",
|
|
|
|
|
net, depth, idx);
|
|
|
|
|
|
|
|
|
|
for (tdx = 0 ; tdx < trans ; tdx += 1) {
|
|
|
|
|
fprintf(vvp_out, " %u", tree[idx].wid);
|
|
|
|
|
wid += tree[idx].wid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for ( ; tdx < 4 ; tdx += 1)
|
|
|
|
|
fprintf(vvp_out, " 0");
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, "]");
|
|
|
|
|
|
|
|
|
|
for (tdx = 0; tdx < trans ; tdx += 1) {
|
|
|
|
|
fprintf(vvp_out, ", LS_%p_%u_%u", net,
|
|
|
|
|
depth-1, tree[idx+tdx].base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
tree[idx/4].base = idx;
|
|
|
|
|
tree[idx/4].wid = wid;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-10 02:42:59 +01:00
|
|
|
depth += 1;
|
|
|
|
|
icnt = (icnt + 3)/4;
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-04 06:13:57 +01:00
|
|
|
/* Finally, draw the root node that takes in the final
|
|
|
|
|
row of tree nodes and generates a single output. */
|
2005-01-10 02:42:59 +01:00
|
|
|
fprintf(vvp_out, "L_%p .concat [", net);
|
|
|
|
|
for (idx = 0 ; idx < icnt ; idx += 1)
|
|
|
|
|
fprintf(vvp_out, " %u", tree[idx].wid);
|
2005-02-04 06:13:57 +01:00
|
|
|
for ( ; idx < 4 ; idx += 1)
|
|
|
|
|
fprintf(vvp_out, " 0");
|
2005-01-10 02:42:59 +01:00
|
|
|
fprintf(vvp_out, "]");
|
|
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < icnt ; idx += 1)
|
|
|
|
|
fprintf(vvp_out, ", LS_%p_%u_%u",
|
|
|
|
|
net, depth-1, tree[idx].base);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
free(tree);
|
|
|
|
|
}
|
2004-12-30 00:52:09 +01:00
|
|
|
}
|
|
|
|
|
|
2002-09-26 05:18:04 +02:00
|
|
|
/*
|
|
|
|
|
* primitive FD (q, clk, ce, d);
|
|
|
|
|
* output q;
|
|
|
|
|
* reg q;
|
|
|
|
|
* input clk, ce, d;
|
|
|
|
|
* table
|
|
|
|
|
* // clk ce d r s q q+
|
|
|
|
|
* r 1 0 0 0 : ? : 0;
|
|
|
|
|
* r 1 1 0 0 : ? : 1;
|
|
|
|
|
* f 1 ? 0 0 : ? : -;
|
|
|
|
|
* ? 1 ? 0 0 : ? : -;
|
|
|
|
|
* * 0 ? 0 0 : ? : -;
|
|
|
|
|
* ? ? ? 1 ? : ? : 0;
|
|
|
|
|
* ? ? ? 0 1 : ? : 1;
|
|
|
|
|
* endtable
|
|
|
|
|
* endprimitive
|
|
|
|
|
*/
|
2002-09-17 07:37:45 +02:00
|
|
|
static void draw_lpm_ff(ivl_lpm_t net)
|
|
|
|
|
{
|
2002-10-23 06:39:35 +02:00
|
|
|
ivl_expr_t aset_expr = 0;
|
|
|
|
|
const char*aset_bits = 0;
|
|
|
|
|
|
2002-09-17 07:37:45 +02:00
|
|
|
unsigned width, idx;
|
|
|
|
|
|
|
|
|
|
width = ivl_lpm_width(net);
|
|
|
|
|
|
2002-10-23 06:39:35 +02:00
|
|
|
/* Q C CE D RS --> Q+ */
|
2003-02-25 04:40:45 +01:00
|
|
|
fprintf(vvp_out, "L_%s.%s/def .udp/sequ \"DFF\", 5, 2,"
|
2002-10-23 06:39:35 +02:00
|
|
|
" \"?" "r" "1" "0" "00" "0\","
|
|
|
|
|
" \"?" "r" "1" "1" "00" "1\","
|
|
|
|
|
" \"?" "r" "1" "x" "00" "x\","
|
|
|
|
|
" \"0" "r" "x" "0" "00" "0\","
|
|
|
|
|
" \"1" "r" "x" "1" "00" "1\","
|
|
|
|
|
" \"?" "*" "0" "?" "00" "-\","
|
|
|
|
|
" \"?" "_" "?" "?" "00" "-\","
|
|
|
|
|
" \"?" "?" "?" "?" "01" "1\","
|
|
|
|
|
" \"?" "?" "?" "?" "1?" "0\","
|
|
|
|
|
" \"?" "?" "1" "?" "00" "-\","
|
|
|
|
|
" \"?" "?" "?" "?" "00" "-\""
|
2003-02-25 04:40:45 +01:00
|
|
|
";\n",
|
|
|
|
|
vvp_mangle_id(ivl_scope_name(ivl_lpm_scope(net))),
|
|
|
|
|
vvp_mangle_id(ivl_lpm_basename(net)));
|
2002-09-17 07:37:45 +02:00
|
|
|
|
2002-10-23 06:39:35 +02:00
|
|
|
aset_expr = ivl_lpm_aset_value(net);
|
|
|
|
|
if (aset_expr) {
|
|
|
|
|
assert(ivl_expr_width(aset_expr) == width);
|
|
|
|
|
aset_bits = ivl_expr_bits(aset_expr);
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-17 07:37:45 +02:00
|
|
|
for (idx = 0 ; idx < width ; idx += 1) {
|
|
|
|
|
ivl_nexus_t tmp;
|
|
|
|
|
|
2003-02-25 04:40:45 +01:00
|
|
|
fprintf(vvp_out, "L_%s.%s/%u .udp ",
|
|
|
|
|
vvp_mangle_id(ivl_scope_name(ivl_lpm_scope(net))),
|
|
|
|
|
vvp_mangle_id(ivl_lpm_basename(net)), idx);
|
2002-09-17 07:37:45 +02:00
|
|
|
|
2003-02-25 04:40:45 +01:00
|
|
|
fprintf(vvp_out, "L_%s.%s/def, ",
|
|
|
|
|
vvp_mangle_id(ivl_scope_name(ivl_lpm_scope(net))),
|
|
|
|
|
vvp_mangle_id(ivl_lpm_basename(net)));
|
2002-09-17 07:37:45 +02:00
|
|
|
|
|
|
|
|
tmp = ivl_lpm_clk(net);
|
|
|
|
|
draw_input_from_net(tmp);
|
|
|
|
|
|
|
|
|
|
tmp = ivl_lpm_enable(net);
|
|
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
if (tmp)
|
|
|
|
|
draw_input_from_net(tmp);
|
|
|
|
|
else
|
|
|
|
|
fprintf(vvp_out, "C<1>");
|
|
|
|
|
|
|
|
|
|
tmp = ivl_lpm_data(net, idx);
|
|
|
|
|
assert(tmp);
|
|
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
draw_input_from_net(tmp);
|
|
|
|
|
|
2002-10-23 06:39:35 +02:00
|
|
|
/* Connect reset input. This may be the Aclr input, or
|
|
|
|
|
an Aset to zero. */
|
2002-09-26 05:18:04 +02:00
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
tmp = ivl_lpm_async_clr(net);
|
2002-10-23 06:39:35 +02:00
|
|
|
if (tmp) {
|
2002-09-26 05:18:04 +02:00
|
|
|
draw_input_from_net(tmp);
|
2002-10-23 06:39:35 +02:00
|
|
|
} else {
|
|
|
|
|
tmp = ivl_lpm_async_set(net);
|
|
|
|
|
if (aset_bits && (aset_bits[idx] == '0'))
|
|
|
|
|
draw_input_from_net(tmp);
|
|
|
|
|
else
|
|
|
|
|
fprintf(vvp_out, "C<0>");
|
|
|
|
|
}
|
2002-09-26 05:18:04 +02:00
|
|
|
|
|
|
|
|
/* Connect set input */
|
|
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
tmp = ivl_lpm_async_set(net);
|
2002-10-23 06:39:35 +02:00
|
|
|
if (aset_bits && (aset_bits[idx] != '1'))
|
|
|
|
|
tmp = 0;
|
|
|
|
|
|
2002-09-26 05:18:04 +02:00
|
|
|
if (tmp)
|
|
|
|
|
draw_input_from_net(tmp);
|
|
|
|
|
else
|
|
|
|
|
fprintf(vvp_out, "C<0>");
|
|
|
|
|
|
2002-09-17 07:37:45 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-07-06 06:48:04 +02:00
|
|
|
static void draw_lpm_shiftl(ivl_lpm_t net)
|
|
|
|
|
{
|
|
|
|
|
unsigned idx, width, selects;
|
2002-11-21 19:08:09 +01:00
|
|
|
unsigned selwid;
|
2001-07-06 06:48:04 +02:00
|
|
|
|
|
|
|
|
width = ivl_lpm_width(net);
|
|
|
|
|
selects = ivl_lpm_selects(net);
|
2001-07-07 05:01:06 +02:00
|
|
|
|
2002-11-21 19:08:09 +01:00
|
|
|
/* The .shift device can only take as many select inputs as
|
|
|
|
|
the width of the device.
|
|
|
|
|
|
|
|
|
|
XXXX I should make some sort of overflow gate for this? If
|
|
|
|
|
any high bits are set, then the shift is certain to be
|
|
|
|
|
*way* beyond the width of the left shifted value. XXXX */
|
|
|
|
|
selwid = selects;
|
|
|
|
|
if (selwid > width)
|
|
|
|
|
selwid = width;
|
|
|
|
|
|
2001-07-07 05:01:06 +02:00
|
|
|
if (ivl_lpm_type(net) == IVL_LPM_SHIFTR)
|
2003-03-13 07:07:11 +01:00
|
|
|
fprintf(vvp_out, "L_%p .shift/r %u", net, width);
|
2001-07-07 05:01:06 +02:00
|
|
|
else
|
2003-03-13 07:07:11 +01:00
|
|
|
fprintf(vvp_out, "L_%p .shift/l %u", net, width);
|
2001-07-06 06:48:04 +02:00
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < width ; idx += 1) {
|
|
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
draw_input_from_net(ivl_lpm_data(net, idx));
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-21 19:08:09 +01:00
|
|
|
for (idx = 0 ; idx < selwid ; idx += 1) {
|
2001-07-06 06:48:04 +02:00
|
|
|
fprintf(vvp_out, ", ");
|
|
|
|
|
draw_input_from_net(ivl_lpm_select(net, idx));
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-21 19:08:09 +01:00
|
|
|
for (idx = selwid ; idx < width ; idx += 1) {
|
|
|
|
|
fprintf(vvp_out, ", C<0>");
|
|
|
|
|
}
|
|
|
|
|
|
2001-07-06 06:48:04 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
2002-03-09 03:10:22 +01:00
|
|
|
static void draw_lpm_ufunc(ivl_lpm_t net)
|
|
|
|
|
{
|
2002-03-18 01:18:50 +01:00
|
|
|
unsigned idx, bit;
|
|
|
|
|
char comma;
|
|
|
|
|
ivl_scope_t def = ivl_lpm_define(net);
|
|
|
|
|
|
2003-03-13 07:07:11 +01:00
|
|
|
fprintf(vvp_out, "L_%p .ufunc TD_%s, %u", net,
|
2002-03-18 01:18:50 +01:00
|
|
|
ivl_scope_name(def),
|
|
|
|
|
ivl_lpm_width(net));
|
|
|
|
|
|
|
|
|
|
/* Print all the net signals that connect to the input of the
|
|
|
|
|
function. Print them one per line, for convenience. */
|
|
|
|
|
for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) {
|
|
|
|
|
comma = ' ';
|
|
|
|
|
fprintf(vvp_out, ",\n");
|
|
|
|
|
for (bit = 0 ; bit < ivl_lpm_data2_width(net, idx) ; bit += 1) {
|
|
|
|
|
fprintf(vvp_out, "%c ", comma);
|
|
|
|
|
draw_input_from_net(ivl_lpm_data2(net, idx, bit));
|
|
|
|
|
comma = ',';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert((ivl_lpm_size(net)+1) == ivl_scope_ports(def));
|
|
|
|
|
|
|
|
|
|
/* Now print all the variables in the function scope that
|
|
|
|
|
receive the input values given in the previous list. */
|
|
|
|
|
for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) {
|
|
|
|
|
ivl_signal_t psig = ivl_scope_port(def, idx+1);
|
|
|
|
|
comma = ' ';
|
|
|
|
|
|
|
|
|
|
if (idx == 0)
|
|
|
|
|
fprintf(vvp_out, "\n(");
|
|
|
|
|
else
|
|
|
|
|
fprintf(vvp_out, ",\n");
|
|
|
|
|
|
|
|
|
|
for (bit = 0 ; bit < ivl_signal_pins(psig) ; bit += 1) {
|
|
|
|
|
fprintf(vvp_out, "%c V_%s[%u]", comma,
|
2002-08-04 00:30:48 +02:00
|
|
|
vvp_signal_label(psig), bit);
|
2002-03-18 01:18:50 +01:00
|
|
|
comma = ',';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ")\n");
|
|
|
|
|
|
|
|
|
|
{ ivl_signal_t psig = ivl_scope_port(def, 0);
|
|
|
|
|
assert(ivl_lpm_width(net) == ivl_signal_pins(psig));
|
|
|
|
|
|
|
|
|
|
comma = ' ';
|
|
|
|
|
for (idx = 0 ; idx < ivl_lpm_width(net) ; idx += 1) {
|
|
|
|
|
fprintf(vvp_out, "%c V_%s[%u]", comma,
|
2002-08-04 00:30:48 +02:00
|
|
|
vvp_signal_label(psig),
|
2002-03-18 01:18:50 +01:00
|
|
|
idx);
|
|
|
|
|
comma = ',';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
2002-03-09 03:10:22 +01:00
|
|
|
}
|
|
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
/*
|
|
|
|
|
* Handle a PART SELECT device. This has a single input and output.
|
|
|
|
|
*/
|
|
|
|
|
static void draw_lpm_part(ivl_lpm_t net)
|
|
|
|
|
{
|
|
|
|
|
unsigned width, base;
|
|
|
|
|
|
|
|
|
|
width = ivl_lpm_width(net);
|
|
|
|
|
base = ivl_lpm_base(net);
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, "L_%p .part ", net);
|
|
|
|
|
draw_input_from_net(ivl_lpm_data(net, 0));
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ", %u, %u;\n", base, width);
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
/*
|
|
|
|
|
* Handle a PART SELECT PV device. Generate a .part/pv node that
|
|
|
|
|
* includes the part input, and the geometry of the part.
|
|
|
|
|
*/
|
|
|
|
|
static void draw_lpm_part_pv(ivl_lpm_t net)
|
|
|
|
|
{
|
|
|
|
|
unsigned width = ivl_lpm_width(net);
|
|
|
|
|
unsigned base = ivl_lpm_base(net);
|
|
|
|
|
unsigned signal_width = width_of_nexus(ivl_lpm_q(net,0));
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, "L_%p .part/pv ", net);
|
|
|
|
|
draw_input_from_net(ivl_lpm_data(net, 0));
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, ", %u, %u, %u;\n", base, width, signal_width);
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-03 05:56:20 +01:00
|
|
|
/*
|
|
|
|
|
* Draw unary reduction devices.
|
|
|
|
|
*/
|
|
|
|
|
static void draw_lpm_re(ivl_lpm_t net, const char*type)
|
|
|
|
|
{
|
|
|
|
|
fprintf(vvp_out, "L_%p .reduce/%s ", net, type);
|
|
|
|
|
draw_input_from_net(ivl_lpm_data(net,0));
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
2005-02-08 01:12:36 +01:00
|
|
|
static void draw_lpm_repeat(ivl_lpm_t net)
|
|
|
|
|
{
|
|
|
|
|
fprintf(vvp_out, "L_%p .repeat %u, %u, ", net,
|
|
|
|
|
ivl_lpm_width(net), ivl_lpm_size(net));
|
|
|
|
|
draw_input_from_net(ivl_lpm_data(net,0));
|
|
|
|
|
fprintf(vvp_out, ";\n");
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-26 07:12:02 +02:00
|
|
|
static void draw_lpm_in_scope(ivl_lpm_t net)
|
|
|
|
|
{
|
|
|
|
|
switch (ivl_lpm_type(net)) {
|
2001-06-16 04:41:41 +02:00
|
|
|
|
|
|
|
|
case IVL_LPM_RAM:
|
|
|
|
|
draw_lpm_ram(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2001-06-07 04:12:43 +02:00
|
|
|
case IVL_LPM_ADD:
|
2001-06-07 05:09:37 +02:00
|
|
|
case IVL_LPM_SUB:
|
2001-06-17 01:45:05 +02:00
|
|
|
case IVL_LPM_MULT:
|
2001-10-16 04:19:26 +02:00
|
|
|
case IVL_LPM_DIVIDE:
|
2002-01-03 05:19:01 +01:00
|
|
|
case IVL_LPM_MOD:
|
2001-06-07 04:12:43 +02:00
|
|
|
draw_lpm_add(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
case IVL_LPM_PART_VP:
|
2004-12-11 03:31:25 +01:00
|
|
|
draw_lpm_part(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2005-01-09 21:16:00 +01:00
|
|
|
case IVL_LPM_PART_PV:
|
|
|
|
|
draw_lpm_part_pv(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2004-12-30 00:52:09 +01:00
|
|
|
case IVL_LPM_CONCAT:
|
|
|
|
|
draw_lpm_concat(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2002-09-17 07:37:45 +02:00
|
|
|
case IVL_LPM_FF:
|
|
|
|
|
draw_lpm_ff(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2005-01-22 02:06:55 +01:00
|
|
|
case IVL_LPM_CMP_EEQ:
|
2005-01-22 17:22:13 +01:00
|
|
|
case IVL_LPM_CMP_EQ:
|
2001-06-15 06:14:18 +02:00
|
|
|
case IVL_LPM_CMP_GE:
|
|
|
|
|
case IVL_LPM_CMP_GT:
|
2005-01-22 17:22:13 +01:00
|
|
|
case IVL_LPM_CMP_NE:
|
2001-06-15 06:14:18 +02:00
|
|
|
draw_lpm_cmp(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2001-04-26 07:12:02 +02:00
|
|
|
case IVL_LPM_MUX:
|
|
|
|
|
draw_lpm_mux(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2005-02-03 05:56:20 +01:00
|
|
|
case IVL_LPM_RE_AND:
|
|
|
|
|
draw_lpm_re(net, "and");
|
|
|
|
|
return;
|
|
|
|
|
case IVL_LPM_RE_OR:
|
|
|
|
|
draw_lpm_re(net, "or");
|
|
|
|
|
return;
|
|
|
|
|
case IVL_LPM_RE_XOR:
|
|
|
|
|
draw_lpm_re(net, "xor");
|
|
|
|
|
return;
|
|
|
|
|
case IVL_LPM_RE_NAND:
|
|
|
|
|
draw_lpm_re(net, "nand");
|
|
|
|
|
return;
|
|
|
|
|
case IVL_LPM_RE_NOR:
|
|
|
|
|
draw_lpm_re(net, "nor");
|
|
|
|
|
return;
|
|
|
|
|
case IVL_LPM_RE_XNOR:
|
|
|
|
|
draw_lpm_re(net, "xnor");
|
|
|
|
|
return;
|
|
|
|
|
|
2005-02-08 01:12:36 +01:00
|
|
|
case IVL_LPM_REPEAT:
|
|
|
|
|
draw_lpm_repeat(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2001-07-06 06:48:04 +02:00
|
|
|
case IVL_LPM_SHIFTL:
|
2001-07-07 05:01:06 +02:00
|
|
|
case IVL_LPM_SHIFTR:
|
2001-07-06 06:48:04 +02:00
|
|
|
draw_lpm_shiftl(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2002-03-09 03:10:22 +01:00
|
|
|
case IVL_LPM_UFUNC:
|
|
|
|
|
draw_lpm_ufunc(net);
|
|
|
|
|
return;
|
|
|
|
|
|
2001-04-26 07:12:02 +02:00
|
|
|
default:
|
2003-02-25 04:40:45 +01:00
|
|
|
fprintf(stderr, "XXXX LPM not supported: %s.%s\n",
|
|
|
|
|
ivl_scope_name(ivl_lpm_scope(net)), ivl_lpm_basename(net));
|
2001-04-26 07:12:02 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-09 01:59:33 +02:00
|
|
|
|
|
|
|
|
static void draw_mem_in_scope(ivl_memory_t net)
|
|
|
|
|
{
|
|
|
|
|
int root = ivl_memory_root(net);
|
|
|
|
|
int last = root + ivl_memory_size(net) - 1;
|
|
|
|
|
int msb = ivl_memory_width(net) - 1;
|
|
|
|
|
int lsb = 0;
|
|
|
|
|
fprintf(vvp_out, "M_%s .mem \"%s\", %u,%u, %u,%u;\n",
|
2004-10-04 03:10:51 +02:00
|
|
|
vvp_memory_label(net),
|
2001-06-18 05:10:34 +02:00
|
|
|
vvp_mangle_name(ivl_memory_basename(net)),
|
2001-05-09 01:59:33 +02:00
|
|
|
msb, lsb, root, last);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-03-21 02:49:43 +01:00
|
|
|
int draw_scope(ivl_scope_t net, ivl_scope_t parent)
|
|
|
|
|
{
|
|
|
|
|
unsigned idx;
|
2001-10-15 04:58:27 +02:00
|
|
|
const char *type;
|
|
|
|
|
switch (ivl_scope_type(net)) {
|
|
|
|
|
case IVL_SCT_MODULE: type = "module"; break;
|
|
|
|
|
case IVL_SCT_FUNCTION: type = "function"; break;
|
|
|
|
|
case IVL_SCT_TASK: type = "task"; break;
|
|
|
|
|
case IVL_SCT_BEGIN: type = "begin"; break;
|
|
|
|
|
case IVL_SCT_FORK: type = "fork"; break;
|
|
|
|
|
default: type = "?"; assert(0);
|
|
|
|
|
}
|
2001-03-21 02:49:43 +01:00
|
|
|
|
2003-05-29 04:21:45 +02:00
|
|
|
fprintf(vvp_out, "S_%p .scope %s, \"%s\" \"%s\"",
|
|
|
|
|
net, type, vvp_mangle_name(ivl_scope_basename(net)),
|
|
|
|
|
ivl_scope_tname(net));
|
2002-12-21 01:55:57 +01:00
|
|
|
|
2001-06-18 05:10:34 +02:00
|
|
|
if (parent) {
|
2003-03-25 03:15:48 +01:00
|
|
|
fprintf(vvp_out, ", S_%p;\n", parent);
|
2002-12-21 01:55:57 +01:00
|
|
|
} else {
|
|
|
|
|
|
2001-10-15 04:58:27 +02:00
|
|
|
fprintf(vvp_out, ";\n");
|
2002-12-21 01:55:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, " .timescale %d;\n", ivl_scope_time_units(net));
|
2003-03-11 00:40:53 +01:00
|
|
|
|
|
|
|
|
for (idx = 0 ; idx < ivl_scope_params(net) ; idx += 1) {
|
|
|
|
|
ivl_parameter_t par = ivl_scope_param(net, idx);
|
|
|
|
|
ivl_expr_t pex = ivl_parameter_expr(par);
|
|
|
|
|
switch (ivl_expr_type(pex)) {
|
|
|
|
|
case IVL_EX_STRING:
|
|
|
|
|
fprintf(vvp_out, "P_%p .param \"%s\", string, \"%s\";\n",
|
|
|
|
|
par, ivl_parameter_basename(par),
|
|
|
|
|
ivl_expr_string(pex));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 07:59:46 +02:00
|
|
|
/* Scan the scope for logic devices. For each device, draw out
|
|
|
|
|
a functor that connects pin 0 to the output, and the
|
|
|
|
|
remaining pins to inputs. */
|
|
|
|
|
|
2001-03-25 05:25:43 +02:00
|
|
|
for (idx = 0 ; idx < ivl_scope_logs(net) ; idx += 1) {
|
|
|
|
|
ivl_net_logic_t lptr = ivl_scope_log(net, idx);
|
2001-03-27 08:27:40 +02:00
|
|
|
draw_logic_in_scope(lptr);
|
2001-03-25 05:25:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-01-26 22:15:58 +01:00
|
|
|
/* Scan the scope for word variables. */
|
|
|
|
|
for (idx = 0 ; idx < ivl_scope_vars(net) ; idx += 1) {
|
|
|
|
|
ivl_variable_t var = ivl_scope_var(net, idx);
|
|
|
|
|
const char*type = "real";
|
|
|
|
|
|
|
|
|
|
fprintf(vvp_out, "W_%s .word %s, \"%s\";\n",
|
|
|
|
|
vvp_word_label(var), type,
|
|
|
|
|
ivl_variable_name(var));
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-25 07:59:46 +02:00
|
|
|
/* Scan the signals (reg and net) and draw the appropriate
|
|
|
|
|
statements to make the signal function. */
|
|
|
|
|
|
2001-03-21 02:49:43 +01:00
|
|
|
for (idx = 0 ; idx < ivl_scope_sigs(net) ; idx += 1) {
|
|
|
|
|
ivl_signal_t sig = ivl_scope_sig(net, idx);
|
|
|
|
|
|
2001-03-25 05:25:43 +02:00
|
|
|
switch (ivl_signal_type(sig)) {
|
|
|
|
|
case IVL_SIT_REG:
|
|
|
|
|
draw_reg_in_scope(sig);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
draw_net_in_scope(sig);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2001-03-21 02:49:43 +01:00
|
|
|
}
|
|
|
|
|
|
2001-03-28 08:07:39 +02:00
|
|
|
for (idx = 0 ; idx < ivl_scope_events(net) ; idx += 1) {
|
|
|
|
|
ivl_event_t event = ivl_scope_event(net, idx);
|
|
|
|
|
draw_event_in_scope(event);
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-09 01:59:33 +02:00
|
|
|
for (idx = 0 ; idx < ivl_scope_mems(net) ; idx += 1) {
|
|
|
|
|
ivl_memory_t mem = ivl_scope_mem(net, idx);
|
|
|
|
|
draw_mem_in_scope(mem);
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-16 04:41:41 +02:00
|
|
|
for (idx = 0 ; idx < ivl_scope_lpms(net) ; idx += 1) {
|
|
|
|
|
ivl_lpm_t lpm = ivl_scope_lpm(net, idx);
|
|
|
|
|
draw_lpm_in_scope(lpm);
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-02 04:28:12 +02:00
|
|
|
if (ivl_scope_type(net) == IVL_SCT_TASK)
|
|
|
|
|
draw_task_definition(net);
|
|
|
|
|
|
2001-04-06 04:28:02 +02:00
|
|
|
if (ivl_scope_type(net) == IVL_SCT_FUNCTION)
|
|
|
|
|
draw_func_definition(net);
|
2001-04-02 04:28:12 +02:00
|
|
|
|
2001-03-31 21:29:23 +02:00
|
|
|
ivl_scope_children(net, (ivl_scope_f*) draw_scope, net);
|
2001-03-21 02:49:43 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* $Log: vvp_scope.c,v $
|
2005-02-12 07:25:15 +01:00
|
|
|
* Revision 1.117 2005/02/12 06:25:15 steve
|
|
|
|
|
* Draw C4 and C8 constants to account for strength.
|
|
|
|
|
*
|
2005-02-10 05:55:45 +01:00
|
|
|
* Revision 1.116 2005/02/10 04:55:45 steve
|
|
|
|
|
* Get the C4 width right for undriven nexa.
|
|
|
|
|
*
|
2005-02-08 01:12:36 +01:00
|
|
|
* Revision 1.115 2005/02/08 00:12:36 steve
|
|
|
|
|
* Add the NetRepeat node, and code generator support.
|
|
|
|
|
*
|
2005-02-04 06:13:57 +01:00
|
|
|
* Revision 1.114 2005/02/04 05:13:57 steve
|
|
|
|
|
* Support .concat with arbitrary input counts.
|
|
|
|
|
*
|
2005-02-03 05:56:20 +01:00
|
|
|
* Revision 1.113 2005/02/03 04:56:21 steve
|
|
|
|
|
* laborate reduction gates into LPM_RED_ nodes.
|
|
|
|
|
*
|
2005-01-22 17:22:13 +01:00
|
|
|
* Revision 1.112 2005/01/22 16:22:13 steve
|
|
|
|
|
* LPM_CMP_NE/EQ are vectored devices.
|
|
|
|
|
*
|
2005-01-22 02:06:55 +01:00
|
|
|
* Revision 1.111 2005/01/22 01:06:55 steve
|
|
|
|
|
* Change case compare from logic to an LPM node.
|
|
|
|
|
*
|
2005-01-16 05:20:32 +01:00
|
|
|
* Revision 1.110 2005/01/16 04:20:32 steve
|
|
|
|
|
* Implement LPM_COMPARE nodes as two-input vector functors.
|
|
|
|
|
*
|
2005-01-12 06:31:50 +01:00
|
|
|
* Revision 1.109 2005/01/12 05:31:50 steve
|
|
|
|
|
* More robust input code generation for LPM_ADD.
|
|
|
|
|
*
|
2005-01-12 04:16:35 +01:00
|
|
|
* Revision 1.108 2005/01/12 03:16:35 steve
|
|
|
|
|
* More complete drawing of concat inputs.
|
|
|
|
|
*
|
2005-01-10 02:42:59 +01:00
|
|
|
* Revision 1.107 2005/01/10 01:42:59 steve
|
|
|
|
|
* Handle concatenations with up to 16 inputs.
|
|
|
|
|
*
|
2005-01-09 21:16:00 +01:00
|
|
|
* Revision 1.106 2005/01/09 20:16:01 steve
|
|
|
|
|
* Use PartSelect/PV and VP to handle part selects through ports.
|
|
|
|
|
*
|
2004-12-30 00:52:09 +01:00
|
|
|
* Revision 1.105 2004/12/29 23:52:09 steve
|
|
|
|
|
* Generate code for the .concat functors, from NetConcat objects.
|
|
|
|
|
* Generate C<> constants of correct widths for functor arguments.
|
|
|
|
|
*
|
2004-12-11 03:31:25 +01:00
|
|
|
* Revision 1.104 2004/12/11 02:31:29 steve
|
|
|
|
|
* Rework of internals to carry vectors through nexus instead
|
|
|
|
|
* of single bits. Make the ivl, tgt-vvp and vvp initial changes
|
|
|
|
|
* down this path.
|
|
|
|
|
*
|
2004-10-04 03:10:51 +02:00
|
|
|
* Revision 1.103 2004/10/04 01:10:57 steve
|
|
|
|
|
* Clean up spurious trailing white space.
|
2001-03-21 02:49:43 +01:00
|
|
|
*/
|
|
|
|
|
|