860 lines
29 KiB
C
860 lines
29 KiB
C
/*
|
|
* Copyright (C) 2011-2012 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);
|
|
return;
|
|
}
|
|
}
|
|
emit_expr(scope, expr, 0);
|
|
}
|
|
|
|
/*
|
|
* 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 (%u) 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;
|
|
return 0;
|
|
} 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 0;
|
|
}
|
|
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;
|
|
}
|
|
|
|
void emit_scaled_range(ivl_scope_t scope, ivl_expr_t expr, unsigned width,
|
|
int msb, int lsb)
|
|
{
|
|
if (msb >= lsb) {
|
|
int rtype;
|
|
int64_t value = get_valid_int64_from_number(expr, &rtype,
|
|
"range value");
|
|
if (rtype) return;
|
|
value += lsb;
|
|
fprintf(vlog_out, "[%"PRId64":%"PRId64"]",
|
|
value + (int64_t)(width - 1), value);
|
|
} else {
|
|
int rtype;
|
|
int64_t value = get_valid_int64_from_number(expr, &rtype,
|
|
"range value");
|
|
if (rtype) return;
|
|
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_valid_int64_from_number(expr, &rtype,
|
|
"value");
|
|
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);
|
|
} else {
|
|
if (is_scaled_expr(expr, msb, lsb)) {
|
|
emit_expr(scope, ivl_expr_oper1(expr), 0);
|
|
}
|
|
}
|
|
} else {
|
|
if (ivl_expr_type(expr) == IVL_EX_NUMBER) {
|
|
int rtype;
|
|
int64_t value = get_valid_int64_from_number(expr, &rtype,
|
|
"value");
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
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)
|
|
{
|
|
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. For this
|
|
* case 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.
|
|
*/
|
|
ivl_scope_t get_module_scope(ivl_scope_t scope)
|
|
{
|
|
|
|
while (ivl_scope_type(scope) != IVL_SCT_MODULE) {
|
|
ivl_scope_t pscope = ivl_scope_parent(scope);
|
|
assert(pscope);
|
|
scope = pscope;
|
|
}
|
|
return scope;
|
|
}
|
|
|
|
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);
|
|
}
|
|
/* Print the base scope. */
|
|
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 = get_module_scope(scope);
|
|
ivl_scope_t 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, ".");
|
|
}
|
|
/* Print the base scope. */
|
|
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 = get_module_scope(scope);
|
|
ivl_scope_t 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);
|
|
}
|