iverilog/tgt-vlog95/misc.c

990 lines
33 KiB
C

/*
* Copyright (C) 2011-2020 Cary R. (cygcary@yahoo.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
# include <ctype.h>
# include <stdlib.h>
# include <string.h>
# include "config.h"
# include "vlog95_priv.h"
# include "ivl_alloc.h"
/*
* Emit a constant delay that has been rescaled to the given scopes timescale.
*/
void emit_scaled_delay(ivl_scope_t scope, uint64_t delay)
{
int scale = ivl_scope_time_units(scope) - sim_precision;
int pre = ivl_scope_time_units(scope) - ivl_scope_time_precision(scope);
char *frac;
unsigned real_dly = 0;
assert(scale >= 0);
assert(pre >= 0);
assert(scale >= pre);
frac = (char *)malloc(pre+1);
frac[pre] = 0;
for (/* none */; scale > 0; scale -= 1) {
if (scale > pre) {
assert((delay % 10) == 0);
} else {
frac[scale-1] = (delay % 10) + '0';
if (frac[scale-1] != '0') {
real_dly = 1;
} else if (!real_dly) {
frac[scale-1] = 0;
}
}
delay /= 10;
}
if (real_dly) {
fprintf(vlog_out, "%"PRIu64".%s", delay, frac);
} else {
if (delay & 0xffffffff80000000) {
fprintf(vlog_out, "(64'd%"PRIu64")", delay);
} else {
fprintf(vlog_out, "%"PRIu64, delay);
}
}
free(frac);
}
static void emit_delay(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt)
{
/* A delay in a continuous assignment can also be a continuous
* assignment expression. */
if (ivl_expr_type(expr) == IVL_EX_SIGNAL) {
ivl_signal_t sig = ivl_expr_signal(expr);
if (ivl_signal_local(sig)) {
assert(! is_stmt);
emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0), 0, 0);
return;
}
}
emit_expr(scope, expr, 0, 0, 0, 1);
}
/*
* Check to see if the bit based expression is of the form (expr) * <scale>
*/
static unsigned check_scaled_expr(ivl_expr_t expr, uint64_t scale,
const char *msg, unsigned must_match)
{
uint64_t scale_val;
int rtype;
if ((ivl_expr_type(expr) != IVL_EX_BINARY) ||
(ivl_expr_opcode(expr) != '*') ||
(ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_NUMBER)) {
fprintf(stderr, "%s:%u: vlog95 error: %s expression/value "
"cannot be scaled.\n ",
ivl_expr_file(expr), ivl_expr_lineno(expr), msg);
vlog_errors += 1;
return 0;
}
scale_val = get_uint64_from_number(ivl_expr_oper2(expr), &rtype);
if (rtype > 0) {
fprintf(stderr, "%s:%u: vlog95 error: %s expression/value "
"scale coefficient was greater than 64 bits "
"(%d).\n", ivl_expr_file(expr),
ivl_expr_lineno(expr), msg, rtype);
vlog_errors += 1;
return 0;
}
if (rtype < 0) {
fprintf(stderr, "%s:%u: vlog95 error: %s expression/value "
"scale coefficient has an undefined bit.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), msg);
vlog_errors += 1;
return 0;
}
if (scale != scale_val) {
if (must_match) {
fprintf(stderr, "%s:%u: vlog95 error: %s expression/value "
"scale coefficient did not match expected "
"value (%"PRIu64" != %"PRIu64").\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
msg, scale, scale_val);
vlog_errors += 1;
return 0;
}
return 2;
}
/* Yes, this expression is of the correct form. */
return 1;
}
/*
* Check to see if the real expression is of the form (expr) * <scale>
*/
static unsigned check_scaled_real_expr(ivl_expr_t expr, double scale)
{
double scale_val;
if ((ivl_expr_type(expr) != IVL_EX_BINARY) ||
(ivl_expr_opcode(expr) != '*') ||
(ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_REALNUM)) {
fprintf(stderr, "%s:%u: vlog95 error: Variable real time unit "
" expression/value cannot be scaled.\n ",
ivl_expr_file(expr), ivl_expr_lineno(expr));
vlog_errors += 1;
return 0;
}
scale_val = ivl_expr_dvalue(ivl_expr_oper2(expr));
if (scale != scale_val) {
fprintf(stderr, "%s:%u: vlog95 error: Variable real time unit "
"expression/value scale coefficient did not "
"match expected value (%g != %g).\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
scale, scale_val);
vlog_errors += 1;
return 0;
}
/* Yes, this expression is of the correct form. */
return 1;
}
/*
* Emit a constant or variable delay that has been rescaled to the given
* scopes timescale.
*/
void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt)
{
ivl_expr_type_t type = ivl_expr_type(expr);
if (type == IVL_EX_DELAY) {
emit_scaled_delay(scope, ivl_expr_delay_val(expr));
} else if (type == IVL_EX_NUMBER) {
assert(! ivl_expr_signed(expr));
int rtype;
uint64_t value = get_uint64_from_number(expr, &rtype);
if (rtype > 0) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Time value is "
"greater than 64 bits (%d) and cannot be "
"safely represented.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
rtype);
vlog_errors += 1;
return;
}
if (rtype < 0) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Time value has an "
"undefined bit and cannot be represented.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr));
vlog_errors += 1;
return;
}
emit_scaled_delay(scope, value);
} else {
int exponent = ivl_scope_time_units(scope) - sim_precision;
assert(exponent >= 0);
if ((exponent == 0) && (type == IVL_EX_SIGNAL)) {
emit_delay(scope, expr, is_stmt);
/* A real delay variable is not scaled by the compiler. */
} else if (type == IVL_EX_SIGNAL) {
if (is_stmt) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Only continuous "
"assignment delay variables are scaled "
"at run time.\n", ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
return;
}
emit_delay(scope, expr, is_stmt);
} else {
uint64_t iscale = 1;
unsigned rtn;
assert(! ivl_expr_signed(expr));
/* Calculate the integer time scaling coefficient. */
while (exponent > 0) {
iscale *= 10;
exponent -= 1;
}
/* Check to see if this is an integer time value. */
rtn = check_scaled_expr(expr, iscale, "Variable time", 0);
/* This may be a scaled real value. */
if (rtn == 2){
ivl_expr_t tmp_expr;
uint64_t rprec = 1;
/* This could be a scaled real time so calculate
* the real time scaling coefficients and check
* that the expression matches (statements only). */
exponent = ivl_scope_time_precision(scope) -
sim_precision;
assert(exponent >= 0);
while (exponent > 0) {
rprec *= 10;
exponent -= 1;
}
/* Verify that the precision scaling is correct. */
if (! check_scaled_expr(expr, rprec,
"Variable real time prec.",
1)) {
fprintf(vlog_out, "<invalid>");
return;
}
/* Verify that the left operator is a real to
* integer cast. */
tmp_expr = ivl_expr_oper1(expr);
if ((ivl_expr_type(tmp_expr) != IVL_EX_UNARY) ||
(ivl_expr_opcode(tmp_expr) != 'v')) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Real time "
"value does not have a cast to "
"integer.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
return;
}
/* Check that the cast value is scaled correctly. */
assert(iscale >= rprec);
tmp_expr = ivl_expr_oper1(tmp_expr);
assert(ivl_expr_value(tmp_expr) == IVL_VT_REAL);
if (! check_scaled_real_expr(tmp_expr, iscale/rprec)) {
fprintf(vlog_out, "<invalid>");
return;
}
assert(is_stmt);
emit_delay(scope, ivl_expr_oper1(tmp_expr), is_stmt);
return;
} else if (rtn == 1) {
emit_delay(scope, ivl_expr_oper1(expr), is_stmt);
return;
}
fprintf(vlog_out, "<invalid>");
}
}
}
static int64_t get_valid_int64_from_number(ivl_expr_t expr, int *rtype,
const char *msg)
{
int64_t value = get_int64_from_number(expr, rtype);
if (*rtype > 0) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Scaled %s is greater than "
"64 bits (%d) and cannot be safely represented.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
msg, *rtype);
vlog_errors += 1;
} else if (*rtype < 0) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Scaled %s has an undefined "
"bit and cannot be represented.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), msg);
vlog_errors += 1;
}
return value;
}
// HERE: Probably need to pass in a msg string to make this work with
// indexed part selects.
static unsigned is_scaled_expr(ivl_expr_t expr, int msb, int lsb)
{
int64_t scale_val;
int rtype;
/* This is as easy as removing the addition/subtraction that was
* added to scale the value to be zero based, but we need to verify
* that the scaling value is correct first. */
if (msb > lsb) {
if ((ivl_expr_type(expr) != IVL_EX_BINARY) ||
((ivl_expr_opcode(expr) != '+') &&
(ivl_expr_opcode(expr) != '-')) ||
(ivl_expr_type(ivl_expr_oper2(expr)) != IVL_EX_NUMBER)) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Scaled "
"expression value cannot be scaled.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
return 0;
}
scale_val = get_valid_int64_from_number(
ivl_expr_oper2(expr), &rtype,
"expression value scale coefficient");
} else {
if ((ivl_expr_type(expr) != IVL_EX_BINARY) ||
((ivl_expr_opcode(expr) != '+') &&
(ivl_expr_opcode(expr) != '-')) ||
(ivl_expr_type(ivl_expr_oper1(expr)) != IVL_EX_NUMBER)) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Scaled "
"expression value cannot be scaled.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
return 0;
}
scale_val = get_valid_int64_from_number(
ivl_expr_oper1(expr), &rtype,
"expression value scale coefficient");
}
if (rtype) return 0;
if (ivl_expr_opcode(expr) == '+') scale_val *= -1;
if (lsb != scale_val) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Scaled expression value "
"scaling coefficient did not match expected "
"value (%d != %"PRIu64").\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
lsb, scale_val);
vlog_errors += 1;
return 0;
}
return 1;
}
static int64_t get_in_range_int64_from_number(ivl_expr_t expr, int *rtype,
const char *msg)
{
int64_t value = get_int64_from_number(expr, rtype);
if (*rtype > 0) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Scaled %s is greater than "
"64 bits (%d) and cannot be safely represented.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
msg, *rtype);
vlog_errors += 1;
}
return value;
}
void emit_scaled_range(ivl_scope_t scope, ivl_expr_t expr, unsigned width,
int msb, int lsb)
{
int rtype;
int64_t value = get_in_range_int64_from_number(expr, &rtype,
"range value");
(void)scope; /* Parameter is not used. */
if (rtype < 0) fprintf(vlog_out, "[1'bx:1'bx]");
if (rtype) return;
if (msb >= lsb) {
value += lsb;
fprintf(vlog_out, "[%"PRId64":%"PRId64"]",
value + (int64_t)(width - 1), value);
} else {
value = (int64_t)lsb - value;
fprintf(vlog_out, "[%"PRId64":%"PRId64"]",
value - (int64_t)(width - 1), value);
}
}
void emit_scaled_expr(ivl_scope_t scope, ivl_expr_t expr, int msb, int lsb)
{
if (msb >= lsb) {
if (ivl_expr_type(expr) == IVL_EX_NUMBER) {
int rtype;
int64_t value = get_in_range_int64_from_number(expr, &rtype,
"value");
if (rtype < 0) fprintf(vlog_out, "1'bx");
if (rtype) return;
value += lsb;
fprintf(vlog_out, "%"PRId64, value);
} else if (lsb == 0) {
/* If the LSB is zero then there is no scale. */
emit_expr(scope, expr, 0, 0, 0, 1);
} else {
if (is_scaled_expr(expr, msb, lsb)) {
emit_expr(scope, ivl_expr_oper1(expr), 0, 0, 0, 1);
}
}
} else {
if (ivl_expr_type(expr) == IVL_EX_NUMBER) {
int rtype;
int64_t value = get_in_range_int64_from_number(expr, &rtype,
"value");
if (rtype < 0) fprintf(vlog_out, "1'bx");
if (rtype) return;
value = (int64_t)lsb - value;
fprintf(vlog_out, "%"PRId64, value);
} else {
if (is_scaled_expr(expr, msb, lsb)) {
emit_expr(scope, ivl_expr_oper2(expr), 0, 0, 0, 1);
}
}
}
}
static unsigned find_signal_in_nexus(ivl_scope_t scope, ivl_nexus_t nex)
{
ivl_signal_t use_sig = 0;
unsigned is_driver = 0;
unsigned is_array = 0;
int64_t array_idx = 0;
unsigned idx, count = ivl_nexus_ptrs(nex);
for (idx = 0; idx < count; idx += 1) {
ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx);
ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr);
if (! sig) continue;
if (ivl_signal_local(sig)) {
/* If the local signal is another receiver skip it. */
if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) &&
(ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) {
continue;
}
assert(0);
}
/* We have a signal that can be used to find the name. */
if (scope == ivl_signal_scope(sig)) {
if (use_sig) {
/* Swap a receiver for a driver. */
if (is_driver &&
(ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) &&
(ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ)) {
use_sig = sig;
is_driver = 0;
if (ivl_signal_dimensions(sig) > 0) {
is_array = 1;
array_idx = ivl_nexus_ptr_pin(nex_ptr);
array_idx += ivl_signal_array_base(sig);
}
continue;
}
// HERE: Which one should we use? For now it's the first one found.
// I believe this needs to be solved (see the inout.v test).
fprintf(stderr, "%s:%u: vlog95 warning: Duplicate "
"name (%s",
ivl_signal_file(sig),
ivl_signal_lineno(sig),
ivl_signal_basename(sig));
if (ivl_signal_dimensions(sig) > 0) {
int64_t tmp_idx = ivl_nexus_ptr_pin(nex_ptr);
tmp_idx += ivl_signal_array_base(sig);
fprintf(stderr, "[%"PRId64"]", tmp_idx);
}
fprintf(stderr, ") found for nexus (%s",
ivl_signal_basename(use_sig));
if (is_array) fprintf(stderr, "[%"PRId64"]", array_idx);
fprintf(stderr, ")\n");
} else {
use_sig = sig;
/* This signal is a driver. */
if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) ||
(ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) {
is_driver = 1;
}
if (ivl_signal_dimensions(sig) > 0) {
is_array = 1;
array_idx = ivl_nexus_ptr_pin(nex_ptr);
array_idx += ivl_signal_array_base(sig);
}
}
}
}
if (use_sig) {
emit_id(ivl_signal_basename(use_sig));
if (is_array) fprintf(vlog_out, "[%"PRId64"]", array_idx);
return 1;
}
return 0;
}
static void emit_number_as_string(ivl_net_const_t net_const)
{
const char *bits = ivl_const_bits(net_const);
unsigned count = ivl_const_width(net_const);
int idx;
assert((count % 8) == 0);
fprintf(vlog_out, "\"");
for (idx = (int)count-1; idx >= 0; idx -= 8) {
unsigned bit;
char val = 0;
for (bit = 0; bit < 8; bit += 1) {
val |= (bits[idx-bit] == '1') ? 1 << (7-bit) : 0x00;
}
/* Skip any NULL bytes. */
if (val == 0) continue;
/* Print some values that can be escaped. */
if (val == '"') fprintf(vlog_out, "\\\"");
else if (val == '\\') fprintf(vlog_out, "\\\\");
else if (val == '\n') fprintf(vlog_out, "\\n");
else if (val == '\t') fprintf(vlog_out, "\\t");
/* Print the printable characters. */
else if (isprint((int)val)) fprintf(vlog_out, "%c", val);
/* Print the non-printable characters as an octal escape. */
else fprintf(vlog_out, "\\%03o", val);
}
fprintf(vlog_out, "\"");
}
static unsigned emit_as_input(ivl_scope_t scope, ivl_net_const_t net_const)
{
ivl_scope_t const_scope = ivl_const_scope(net_const);
ivl_scope_t parent = ivl_scope_parent(scope);
/* Look to see if the constant scope is a parent of this scope. */
while (parent) {
if (parent == const_scope) break;
parent = ivl_scope_parent(parent);
}
/* If the constant scope is a parent then look for an input in
* this scope and use that for the name. */
if (parent) {
ivl_nexus_t nex = ivl_const_nex(net_const);
unsigned idx, count = ivl_nexus_ptrs(nex);
for (idx = 0; idx < count; idx += 1) {
ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx);
ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr);
if (sig && (ivl_signal_port(sig) == IVL_SIP_INPUT)) {
emit_id(ivl_signal_basename(sig));
return 1;
}
}
}
return 0;
}
void emit_const_nexus(ivl_scope_t scope, ivl_net_const_t net_const)
{
ivl_scope_t const_scope = ivl_const_scope(net_const);
unsigned idx, count, lineno;
const char *file;
count = ivl_scope_params(const_scope);
file = ivl_const_file(net_const);
lineno = ivl_const_lineno(net_const);
/* Look to see if the constant matches a parameter in its scope. */
for (idx = 0; idx < count; idx += 1) {
ivl_parameter_t par = ivl_scope_param(const_scope, idx);
if (lineno != ivl_parameter_lineno(par)) continue;
if (strcmp(file, ivl_parameter_file(par)) == 0) {
/* Check that the appropriate expression bits match the
* original parameter bits. */
// HERE: Verify that the values match and then print the name.
// Does this work with out of scope references? Check real parameters.
emit_id(ivl_parameter_basename(par));
return;
}
}
/* If the scopes don't match then we assume this is an empty port. */
if (const_scope != scope) {
/* This constant could really be from an input port. */
if (emit_as_input(scope, net_const)) return;
fprintf(vlog_out, "/* Empty */");
return;
}
switch (ivl_const_type(net_const)) {
case IVL_VT_LOGIC:
case IVL_VT_BOOL:
emit_number(ivl_const_bits(net_const),
ivl_const_width(net_const),
ivl_const_signed(net_const),
ivl_const_file(net_const),
ivl_const_lineno(net_const));
break;
case IVL_VT_STRING:
emit_number_as_string(net_const);
break;
case IVL_VT_REAL:
emit_real_number(ivl_const_real(net_const));
break;
default:
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Unknown constant type "
"(%d).\n",
ivl_const_file(net_const),
ivl_const_lineno(net_const),
(int)ivl_const_type(net_const));
vlog_errors += 1;
break;
}
}
static unsigned find_const_nexus(ivl_scope_t scope, ivl_nexus_t nex)
{
unsigned idx, count;
count = ivl_nexus_ptrs(nex);
for (idx = 0; idx < count; idx += 1) {
ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx);
ivl_net_const_t net_const = ivl_nexus_ptr_con(nex_ptr);
// HERE: Do we need to check for duplicates?
if (net_const) {
assert(! ivl_nexus_ptr_pin(nex_ptr));
emit_const_nexus(scope, net_const);
return 1;
}
}
return 0;
}
static unsigned find_driving_signal(ivl_scope_t scope, ivl_nexus_t nex)
{
ivl_signal_t sig = 0;
unsigned is_array = 0;
int64_t array_idx = 0;
unsigned idx, count = ivl_nexus_ptrs(nex);
for (idx = 0; idx < count; idx += 1) {
ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx);
ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr);
if (! t_sig) continue;
if (ivl_signal_local(t_sig)) continue;
/* An output can be used if it is driven by this nexus. */
if ((ivl_nexus_ptr_drive1(nex_ptr) == IVL_DR_HiZ) &&
(ivl_nexus_ptr_drive0(nex_ptr) == IVL_DR_HiZ) &&
(ivl_signal_port(t_sig) != IVL_SIP_OUTPUT)) {
continue;
}
/* We have a signal that can be used to find the name. */
if (sig) {
// HERE: Which one should we use? For now it's the first one found.
// I believe this needs to be solved (see above).
fprintf(stderr, "%s:%u: vlog95 warning: Duplicate name (%s",
ivl_signal_file(t_sig), ivl_signal_lineno(t_sig),
ivl_signal_basename(t_sig));
if (ivl_signal_dimensions(t_sig) > 0) {
int64_t tmp_idx = ivl_nexus_ptr_pin(nex_ptr);
tmp_idx += ivl_signal_array_base(t_sig);
fprintf(stderr, "[%"PRId64"]", tmp_idx);
}
fprintf(stderr, ") found for nexus (%s",
ivl_signal_basename(sig));
if (is_array) fprintf(stderr, "[%"PRId64"]", array_idx);
fprintf(stderr, ")\n");
} else {
sig = t_sig;
if (ivl_signal_dimensions(sig) > 0) {
is_array = 1;
array_idx = ivl_nexus_ptr_pin(nex_ptr);
array_idx += ivl_signal_array_base(sig);
}
}
}
if (sig) {
emit_scope_call_path(scope, ivl_signal_scope(sig));
emit_id(ivl_signal_basename(sig));
if (is_array) fprintf(vlog_out, "[%"PRId64"]", array_idx);
return 1;
}
return 0;
}
static unsigned is_local_input(ivl_scope_t scope, ivl_nexus_t nex)
{
ivl_signal_t sig = 0;
unsigned idx, count = ivl_nexus_ptrs(nex);
(void)scope; /* Parameter is not used. */
for (idx = 0; idx < count; idx += 1) {
ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx);
ivl_signal_t t_sig = ivl_nexus_ptr_sig(nex_ptr);
if (! t_sig) continue;
if (! ivl_signal_local(t_sig)) continue;
if (ivl_signal_port(t_sig) != IVL_SIP_INPUT) continue;
assert(! sig);
assert(ivl_signal_dimensions(t_sig) == 0);
sig = t_sig;
}
if (sig) {
fprintf(vlog_out, "ivlog%s", ivl_signal_basename(sig));
return 1;
}
return 0;
}
// HERE: Does this work correctly with an array reference created from @*?
void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD)
{
unsigned idx;
ivl_scope_t mod_scope;
/* First look in the local scope for the nexus name. */
if (find_signal_in_nexus(scope, nex)) return;
/* If the signal was not found in the passed scope then look in
* the module scope if the passed scope was not the module scope. */
mod_scope = get_module_scope(scope);
if (mod_scope != scope) {
if (find_signal_in_nexus(mod_scope, nex)) return;
}
/* Look to see if this is a up/down reference. */
if (allow_UD && find_driving_signal(scope, nex)) return;
/* If there is no signals driving this then look for a constant. */
if (find_const_nexus(scope, nex)) return;
/* Module inputs that are split (arg[7:4], arg[3:0]) need to use
* the local signal names. */
if (is_local_input(scope, nex)) return;
// HERE: Need to check arr[var]? Can this be rebuilt?
// Then look for down scopes and then any scope. For all this warn if
// multiples are found in a given scope. This all needs to be before
// the constant code.
/* It is possible that the nexus does not have a name. First check
if it drives another nexus through a transparent buffer. */
for (idx = 0; idx < ivl_nexus_ptrs(nex); idx += 1) {
ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx);
ivl_net_logic_t nlogic = ivl_nexus_ptr_log(nex_ptr);
if (nlogic && ivl_logic_type(nlogic) == IVL_LO_BUFT
&& ivl_logic_pin(nlogic, 1) == nex) {
emit_name_of_nexus(scope, ivl_logic_pin(nlogic, 0), allow_UD);
return;
}
}
/* If not, do not print an actual name. */
fprintf(vlog_out, "/* Empty */");
// dump_nexus_information(scope, nex);
}
/*
* This function traverses the scope tree looking for the enclosing module
* scope. When it is found the module scope is returned. As far as this
* translation is concerned a package is a special form of a module
* definition and a class is also a top level scope. In SystemVerilog,
* tasks and functions can also be top level scopes - we create a wrapper
* module for these later.
*/
ivl_scope_t get_module_scope(ivl_scope_t scope)
{
while ((ivl_scope_type(scope) != IVL_SCT_MODULE) &&
(ivl_scope_type(scope) != IVL_SCT_PACKAGE) &&
(ivl_scope_type(scope) != IVL_SCT_CLASS)) {
ivl_scope_t pscope = ivl_scope_parent(scope);
if (pscope == 0) {
if (ivl_scope_type(scope) == IVL_SCT_TASK)
break;
if (ivl_scope_type(scope) == IVL_SCT_FUNCTION)
break;
}
assert(pscope);
scope = pscope;
}
return scope;
}
/*
* A package is emitted as a module with a special name. This routine
* calculates the name for the package. The returned string must be freed
* by the calling routine.
*/
char * get_package_name(ivl_scope_t scope)
{
char *package_name;
const char *name = ivl_scope_basename(scope);
package_name = (char *)malloc(strlen(name)+13);
strcpy(package_name, "ivl_package_");
strcat(package_name, name);
return package_name;
}
static void emit_scope_piece(ivl_scope_t scope, ivl_scope_t call_scope)
{
ivl_scope_t parent = ivl_scope_parent(call_scope);
/* If we are not at the top of the scope (parent != 0) and the two
* scopes do not match then print the parent scope. */
if ((parent != 0) && (scope != parent)) {
emit_scope_piece(scope, parent);
}
/* If the scope is a package then add the special part of the name. */
if (ivl_scope_type(call_scope) == IVL_SCT_PACKAGE) {
char *package_name = get_package_name(call_scope);
emit_id(package_name);
free(package_name);
/* Print the base scope. */
} else emit_id(ivl_scope_basename(call_scope));
fprintf(vlog_out, ".");
}
/*
* This routine emits the appropriate string to call the call_scope from the
* given scope. If the module scopes for the two match then do nothing. If
* the module scopes are different, but the call_scope begins with the
* entire module scope of scope then we can trim the top off the call_scope
* (it is a sub-scope of the module that contains scope). Otherwise we need
* to print the entire path of call_scope.
*/
void emit_scope_module_path(ivl_scope_t scope, ivl_scope_t call_scope)
{
ivl_scope_t mod_scope = get_module_scope(scope);
ivl_scope_t call_mod_scope = get_module_scope(call_scope);
if (mod_scope == call_mod_scope) return;
emit_scope_piece(mod_scope, call_mod_scope);
}
/* This is the same as emit_scope_module_path() except we need to add down
* references for variables, etc. */
void emit_scope_call_path(ivl_scope_t scope, ivl_scope_t call_scope)
{
ivl_scope_t mod_scope, call_mod_scope;
if (scope == call_scope) return;
mod_scope = get_module_scope(scope);
call_mod_scope = get_module_scope(call_scope);
if (mod_scope != call_mod_scope) {
emit_scope_piece(mod_scope, call_mod_scope);
} else if (scope != call_scope) {
ivl_scope_t parent;
/* We only emit a scope path if the scope is a parent of the
* call scope. */
for (parent = ivl_scope_parent(call_scope);
parent != 0;
parent = ivl_scope_parent(parent)) {
if (parent == scope) {
emit_scope_piece(scope, call_scope);
return;
}
}
}
}
static void emit_scope_path_piece(ivl_scope_t scope, ivl_scope_t call_scope)
{
ivl_scope_t parent = ivl_scope_parent(call_scope);
/* If we are not at the top of the scope (parent != 0) and the two
* scopes do not match then print the parent scope. */
if ((parent != 0) && (scope != parent)) {
emit_scope_path_piece(scope, parent);
fprintf(vlog_out, ".");
}
/* If the scope is a package then add the special part of the name. */
if (ivl_scope_type(call_scope) == IVL_SCT_PACKAGE) {
char *package_name = get_package_name(call_scope);
emit_id(package_name);
free(package_name);
/* Print the base scope. */
} else emit_id(ivl_scope_basename(call_scope));
}
/*
* This routine emits the appropriate string to call the call_scope from the
* given scope. If the module scopes for the two match then just return the
* base name of the call_scope. If the module scopes are different, but the
* call_scope begins with the entire module scope of scope then we can trim
* the top off the call_scope (it is a sub-scope of the module that contains
* scope). Otherwise we need to print the entire path of call_scope.
*/
void emit_scope_path(ivl_scope_t scope, ivl_scope_t call_scope)
{
ivl_scope_t mod_scope, call_mod_scope;
/* Check to see if this is a root scope task or function. */
if (ivl_scope_parent(call_scope) == 0) {
fprintf(vlog_out, "ivl_root_scope_%s.",
ivl_scope_basename(call_scope));
mod_scope = 0;
call_mod_scope = 0;
} else {
mod_scope = get_module_scope(scope);
call_mod_scope = get_module_scope(call_scope);
}
if (mod_scope == call_mod_scope) {
emit_id(ivl_scope_basename(call_scope));
} else {
emit_scope_path_piece(mod_scope, call_scope);
}
}
static unsigned is_escaped(const char *id)
{
assert(id);
/* The first digit must be alpha or '_' to be a normal id. */
if (isalpha((int)id[0]) || id[0] == '_') {
unsigned idx;
for (idx = 1; id[idx] != '\0'; idx += 1) {
if (! (isalnum((int)id[idx]) ||
id[idx] == '_' || id[idx] == '$')) {
return 1;
}
}
/* Any Verilog keyword should also be escaped. */
// HERE: Create a keyword.gperf file to do this check.
if ((strcmp(id, "input") == 0) ||
(strcmp(id, "output") == 0) ) return 1;
/* We looked at all the digits, so this is a normal id. */
return 0;
}
return 1;
}
void emit_id(const char *id)
{
if (is_escaped(id)) fprintf(vlog_out, "\\%s ", id);
else fprintf(vlog_out, "%s", id);
}
/*
* Get the correct MSB and LSB for a signal.
*/
void get_sig_msb_lsb(ivl_signal_t sig, int *msb, int *lsb)
{
switch (ivl_signal_packed_dimensions(sig)) {
/* For a scalar we use zero for both the MSB and LSB. */
case 0:
*msb = 0;
*lsb = 0;
break;
case 1:
/* For a vector we use the real MSB and LSB. */
*msb = ivl_signal_packed_msb(sig, 0);
*lsb = ivl_signal_packed_lsb(sig, 0);
break;
/* For a packed vector we use the normalized MSB and LSB. */
default:
*msb = ivl_signal_width(sig) - 1;
*lsb = 0;
break;
}
}
const char*get_time_const(int time_value)
{
switch (time_value) {
case 2: return "100s";
case 1: return "10s";
case 0: return "1s";
case -1: return "100ms";
case -2: return "10ms";
case -3: return "1ms";
case -4: return "100us";
case -5: return "10us";
case -6: return "1us";
case -7: return "100ns";
case -8: return "10ns";
case -9: return "1ns";
case -10: return "100ps";
case -11: return "10ps";
case -12: return "1ps";
case -13: return "100fs";
case -14: return "10fs";
case -15: return "1fs";
default:
fprintf(stderr, "Invalid time constant value %d.\n", time_value);
return "N/A";
}
}