iverilog/tgt-vvp/draw_vpi.c

429 lines
12 KiB
C

/*
* Copyright (c) 2003-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"
# include <string.h>
#ifdef HAVE_MALLOC_H
# include <malloc.h>
#endif
# include <stdlib.h>
# include <assert.h>
#ifdef __MINGW32__ /* MinGW has inconsistent %p output. */
#define snprintf _snprintf
#endif
/*
* Check to see if the expression (number) can be correctly represented
* with a long variable.
*/
static int is_constant_number(ivl_expr_t ex)
{
/* Make sure this matches the return type of constant_number(). */
unsigned lim_wid = 8*sizeof(long);
const char*bits;
char pad_bit = '0';
unsigned idx;
unsigned nbits = ivl_expr_width(ex);
if (ivl_expr_type(ex) != IVL_EX_NUMBER
&& ivl_expr_type(ex) != IVL_EX_ULONG)
return 0;
bits = ivl_expr_bits(ex);
/* For unsigned values the effective MSB and on must be '0'. */
if (!ivl_expr_signed(ex)) lim_wid -= 1;
/* For negative values the pad bit is '1'. */
if (ivl_expr_signed(ex) && bits[nbits-1]=='1') {
pad_bit = '1';
}
/* For the number to fit in the variable all the upper bits must
* match the pad bits. */
for (idx = lim_wid ; idx < nbits ; idx += 1) {
if (bits[idx] != pad_bit) return 0;
}
return 1;
}
/*
* Convert the expression (number) to a long value.
*/
static long get_constant_number(ivl_expr_t ex)
{
long rtn = 0;
switch (ivl_expr_type(ex)) {
case IVL_EX_ULONG:
rtn = (signed)ivl_expr_value(ex);
break;
case IVL_EX_NUMBER: {
unsigned idx;
const char*bits = ivl_expr_bits(ex);
unsigned nbits = ivl_expr_width(ex);
char pad_bit = bits[nbits-1];
/* Define all the bits in the long (negative numbers). */
for (idx = 0 ; idx < 8*sizeof(long) ; idx += 1) {
char bit;
if (idx < nbits) bit = bits[idx];
else bit = pad_bit;
switch (bit) {
case '0':
break;
case '1':
rtn |= 1 << idx;
break;
default:
assert(0);
}
}
break;
}
default:
assert(0);
}
return rtn;
}
static const char* magic_sfuncs[] = {
"$time",
"$stime",
"$realtime",
"$simtime",
0
};
static int is_magic_sfunc(const char*name)
{
int idx;
for (idx = 0 ; magic_sfuncs[idx] ; idx += 1)
if (strcmp(magic_sfuncs[idx],name) == 0)
return 1;
return 0;
}
static int is_fixed_memory_word(ivl_expr_t net)
{
ivl_signal_t sig;
if (ivl_expr_type(net) != IVL_EX_SIGNAL)
return 0;
sig = ivl_expr_signal(net);
if (ivl_signal_dimensions(sig) == 0)
return 1;
if (ivl_signal_type(sig) == IVL_SIT_REG)
return 0;
if (number_is_immediate(ivl_expr_oper1(net), 8*sizeof(unsigned)))
return 1;
return 0;
}
static void draw_vpi_taskfunc_args(const char*call_string,
ivl_statement_t tnet,
ivl_expr_t fnet)
{
unsigned idx;
unsigned parm_count = tnet
? ivl_stmt_parm_count(tnet)
: ivl_expr_parms(fnet);
struct args_info {
char*text;
int vec_flag; /* True if the vec must be released. */
struct vector_info vec;
} *args = calloc(parm_count, sizeof(struct args_info));
char buffer[4096];
ivl_parameter_t par;
/* Figure out how many expressions are going to be evaluated
for this task call. I won't need to evaluate expressions
for items that are VPI objects directly. */
for (idx = 0 ; idx < parm_count ; idx += 1) {
ivl_expr_t expr = tnet
? ivl_stmt_parm(tnet, idx)
: ivl_expr_parm(fnet, idx);
switch (ivl_expr_type(expr)) {
/* These expression types can be handled directly,
with VPI handles of their own. Therefore, skip
them in the process of evaluating expressions. */
case IVL_EX_NONE:
args[idx].text = strdup("\" \"");
continue;
case IVL_EX_ARRAY:
snprintf(buffer, sizeof buffer,
"v%p", ivl_expr_signal(expr));
args[idx].text = strdup(buffer);
continue;
case IVL_EX_NUMBER: {
unsigned bit, wid = ivl_expr_width(expr);
const char*bits = ivl_expr_bits(expr);
char*dp;
snprintf(buffer, sizeof buffer,
"%u'%sb", wid, ivl_expr_signed(expr)? "s" : "");
dp = buffer + strlen(buffer);
for (bit = wid ; bit > 0 ; bit -= 1)
*dp++ = bits[bit-1];
*dp++ = 0;
assert(dp - buffer <= sizeof buffer);
args[idx].text = strdup(buffer);
continue;
}
case IVL_EX_STRING:
if (( par = ivl_expr_parameter(expr) )) {
snprintf(buffer, sizeof buffer, "P_%p", par);
} else {
snprintf(buffer, sizeof buffer, "\"%s\"", ivl_expr_string(expr));
}
args[idx].text = strdup(buffer);
continue;
case IVL_EX_EVENT:
snprintf(buffer, sizeof buffer, "E_%p", ivl_expr_event(expr));
args[idx].text = strdup(buffer);
continue;
case IVL_EX_SCOPE:
snprintf(buffer, sizeof buffer, "S_%p", ivl_expr_scope(expr));
args[idx].text = strdup(buffer);
continue;
case IVL_EX_SFUNC:
if (is_magic_sfunc(ivl_expr_name(expr))) {
snprintf(buffer, sizeof buffer, "%s", ivl_expr_name(expr));
args[idx].text = strdup(buffer);
continue;
}
break;
case IVL_EX_SIGNAL:
/* If the signal node is narrower then the signal
itself, then this is a part select so I'm going
to need to evaluate the expression.
Also, if the signedness of the expression is
different from the signedness of the
signal. This could be caused by a $signed or
$unsigned system function.
If I don't need to do any evaluating, then skip
it as I'll be passing the handle to the signal
itself. */
if (ivl_expr_width(expr) !=
ivl_signal_width(ivl_expr_signal(expr))) {
break;
} else if (ivl_expr_signed(expr) !=
ivl_signal_signed(ivl_expr_signal(expr))) {
break;
} else if (is_fixed_memory_word(expr)) {
/* This is a word of a non-array, or a word
of a net array, so we can address the
word directly. */
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned use_word = 0;
ivl_expr_t word_ex = ivl_expr_oper1(expr);
if (word_ex) {
/* Some array select have been evaluated. */
if (number_is_immediate(word_ex, 8*sizeof(unsigned))) {
use_word = get_number_immediate(word_ex);
word_ex = 0;
}
}
if (word_ex)
break;
assert(word_ex == 0);
snprintf(buffer, sizeof buffer, "v%p_%u", sig, use_word);
args[idx].text = strdup(buffer);
continue;
} else {
/* What's left, this is the work of a var
array. Create the right code to handle
it. */
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned use_word = 0;
ivl_expr_t word_ex = ivl_expr_oper1(expr);
if (word_ex) {
/* Some array select have been evaluated. */
if (number_is_immediate(word_ex, 8*sizeof(unsigned))) {
use_word = get_number_immediate(word_ex);
word_ex = 0;
}
}
if (word_ex) {
struct vector_info av;
av = draw_eval_expr(word_ex, STUFF_OK_XZ);
snprintf(buffer, sizeof buffer,
"&A<v%p, T<%u,%u,u>>", sig, av.base, av.wid);
args[idx].vec = av;
args[idx].vec_flag = 1;
} else {
snprintf(buffer, sizeof buffer,
"&A<v%p, %u>", sig, use_word);
}
args[idx].text = strdup(buffer);
continue;
}
case IVL_EX_SELECT: {
ivl_expr_t vexpr = ivl_expr_oper1(expr);
assert(vexpr);
/* This code is only for signals. */
if (ivl_expr_type(vexpr) != IVL_EX_SIGNAL) break;
/* The signal is part of an array. */
/* Add &APV<> code here when it is finished. */
if (ivl_expr_oper1(vexpr)) break;
ivl_expr_t bexpr = ivl_expr_oper2(expr);
assert(bexpr);
/* This is a constant bit/part select. */
if (is_constant_number(bexpr)) {
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %ld, %u>",
ivl_expr_signal(vexpr),
get_constant_number(bexpr),
ivl_expr_width(expr));
/* This is an indexed bit/part select. */
} else {
struct vector_info rv;
rv = draw_eval_expr(bexpr, STUFF_OK_XZ);
snprintf(buffer, sizeof buffer, "&PV<v%p_0, %u %u, %u>",
ivl_expr_signal(vexpr),
rv.base, rv.wid,
ivl_expr_width(expr));
}
args[idx].text = strdup(buffer);
continue;
}
/* Everything else will need to be evaluated and
passed as a constant to the vpi task. */
default:
break;
}
switch (ivl_expr_value(expr)) {
case IVL_VT_LOGIC:
case IVL_VT_BOOL:
args[idx].vec_flag = 1;
args[idx].vec = draw_eval_expr(expr, 0);
snprintf(buffer, sizeof buffer,
"T<%u,%u,%s>", args[idx].vec.base, args[idx].vec.wid,
ivl_expr_signed(expr)? "s" : "u");
break;
case IVL_VT_REAL:
args[idx].vec_flag = 1;
args[idx].vec.base = draw_eval_real(expr);
args[idx].vec.wid = 0;
snprintf(buffer, sizeof buffer,
"W<%u,r>", args[idx].vec.base);
break;
default:
assert(0);
}
args[idx].text = strdup(buffer);
}
fprintf(vvp_out, "%s", call_string);
for (idx = 0 ; idx < parm_count ; idx += 1) {
fprintf(vvp_out, ", %s", args[idx].text);
free(args[idx].text);
if (args[idx].vec_flag) {
if (args[idx].vec.wid > 0)
clr_vector(args[idx].vec);
else
clr_word(args[idx].vec.base);
}
}
free(args);
fprintf(vvp_out, ";\n");
}
void draw_vpi_task_call(ivl_statement_t tnet)
{
char call_string[1024];
sprintf(call_string, " %%vpi_call %u %u \"%s\"",
ivl_file_table_index(ivl_stmt_file(tnet)),
ivl_stmt_lineno(tnet), ivl_stmt_name(tnet));
draw_vpi_taskfunc_args(call_string, tnet, 0);
}
struct vector_info draw_vpi_func_call(ivl_expr_t fnet, unsigned wid)
{
char call_string[1024];
struct vector_info res;
res.base = allocate_vector(wid);
res.wid = wid;
if (res.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits for system function result.\n",
ivl_expr_file(fnet), ivl_expr_lineno(fnet), wid);
vvp_errors += 1;
}
sprintf(call_string, " %%vpi_func %u %u \"%s\", %u, %u",
ivl_file_table_index(ivl_expr_file(fnet)),
ivl_expr_lineno(fnet), ivl_expr_name(fnet), res.base, res.wid);
draw_vpi_taskfunc_args(call_string, 0, fnet);
return res;
}
int draw_vpi_rfunc_call(ivl_expr_t fnet)
{
char call_string[1024];
int res = allocate_word();
sprintf(call_string, " %%vpi_func/r %u %u \"%s\", %d",
ivl_file_table_index(ivl_expr_file(fnet)),
ivl_expr_lineno(fnet), ivl_expr_name(fnet), res);
draw_vpi_taskfunc_args(call_string, 0, fnet);
return res;
}