vlog95: Major rework of nexus emitting code, etc.

Rework the nexus emitting code to correctly translate most I/O ports.
Fix a few other expression issues uncovered when port translation was
done correctly. Ignore and warn that the SV ++/-- operators and enum
types are not translatable. More updates of the nexus debug code.
This commit is contained in:
Cary R 2012-01-02 11:28:50 -08:00
parent a433fa3fcd
commit dab982f39b
10 changed files with 602 additions and 177 deletions

View File

@ -36,7 +36,7 @@ void emit_event(ivl_scope_t scope, ivl_statement_t stmt)
for (idx = 0; idx < count; idx += 1) {
if (first) first = 0;
else fprintf(vlog_out, " or ");
emit_name_of_nexus(scope, ivl_event_any(event, idx));
emit_nexus_as_ca(scope, ivl_event_any(event, idx), 0);
}
/* Check for positive edge events. */
@ -46,7 +46,7 @@ void emit_event(ivl_scope_t scope, ivl_statement_t stmt)
if (first) first = 0;
else fprintf(vlog_out, " or ");
fprintf(vlog_out, "posedge ");
emit_name_of_nexus(scope, ivl_event_pos(event, idx));
emit_nexus_as_ca(scope, ivl_event_pos(event, idx), 0);
}
/* Check for negative edge events. */
@ -56,7 +56,7 @@ void emit_event(ivl_scope_t scope, ivl_statement_t stmt)
if (first) first = 0;
else fprintf(vlog_out, " or ");
fprintf(vlog_out, "negedge ");
emit_name_of_nexus(scope, ivl_event_neg(event, idx));
emit_nexus_as_ca(scope, ivl_event_neg(event, idx), 0);
}
/* We have a named event if there were no edge events. */

View File

@ -79,7 +79,7 @@ static void emit_expr_array(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
{
char *oper = "<invalid>";
switch(ivl_expr_opcode(expr)) {
switch (ivl_expr_opcode(expr)) {
case '+': oper = "+"; break;
case '-': oper = "-"; break;
case '*': oper = "*"; break;
@ -108,7 +108,7 @@ static void emit_expr_binary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
}
fprintf(vlog_out, "(");
switch(ivl_expr_opcode(expr)) {
switch (ivl_expr_opcode(expr)) {
case '%':
if (ivl_expr_value(expr) == IVL_VT_REAL) {
fprintf(stderr, "%s:%u: vlog95 error: Real modulus operator "
@ -427,6 +427,18 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
} else {
// HERE: Should this sign extend if the expression is signed?
emit_expr(scope, sig_expr, wid);
/* Select part of a signal when needed. */
if ((ivl_expr_type(sig_expr) == IVL_EX_SIGNAL) &&
(ivl_expr_width(expr) < ivl_expr_width(sig_expr))) {
ivl_signal_t sig = ivl_expr_signal(sig_expr);
int msb = ivl_signal_msb(sig);
int lsb = ivl_signal_lsb(sig);
int64_t value = lsb;
unsigned e_wid = ivl_expr_width(expr) - 1;
if (msb >= lsb) value += e_wid;
else value -= e_wid;
fprintf(vlog_out, "[%"PRId64":%u]", value, lsb);
}
}
}
@ -508,6 +520,46 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
/* A cast is a noop. */
emit_expr(scope, ivl_expr_oper1(expr), wid);
break;
case 'I':
fprintf(vlog_out, "(++");
emit_expr(scope, ivl_expr_oper1(expr), wid);
fprintf(vlog_out, ")");
fprintf(stderr, "%s:%u: vlog95 sorry: Pre-increment "
"operator is not currently translated.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
break;
case 'i':
fprintf(vlog_out, "(");
emit_expr(scope, ivl_expr_oper1(expr), wid);
fprintf(vlog_out, "++)");
fprintf(stderr, "%s:%u: vlog95 sorry: Pre-increment "
"operator is not currently translated.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
break;
case 'D':
fprintf(vlog_out, "(--");
emit_expr(scope, ivl_expr_oper1(expr), wid);
fprintf(vlog_out, ")");
fprintf(stderr, "%s:%u: vlog95 sorry: Pre-decrement "
"operator is not currently translated.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
break;
case 'd':
fprintf(vlog_out, "(");
emit_expr(scope, ivl_expr_oper1(expr), wid);
fprintf(vlog_out, "--)");
fprintf(stderr, "%s:%u: vlog95 sorry: Pre-decrement "
"operator is not currently translated.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
break;
default:
fprintf(vlog_out, "<unknown>");
emit_expr(scope, ivl_expr_oper1(expr), wid);
@ -516,13 +568,14 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
ivl_expr_file(expr),
ivl_expr_lineno(expr),
ivl_expr_opcode(expr));
vlog_errors += 1;
break;
}
}
void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
{
switch(ivl_expr_type(expr)) {
switch (ivl_expr_type(expr)) {
case IVL_EX_ARRAY:
emit_expr_array(scope, expr, wid);
break;
@ -535,6 +588,14 @@ void emit_expr(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
case IVL_EX_DELAY:
emit_expr_delay(scope, expr, wid);
break;
case IVL_EX_ENUMTYPE:
fprintf(vlog_out, "<enum>");
fprintf(stderr, "%s:%u: vlog95 error: Enum expressions "
"are not supported.\n",
ivl_expr_file(expr),
ivl_expr_lineno(expr));
vlog_errors += 1;
break;
case IVL_EX_EVENT:
emit_expr_event(scope, expr, wid);
break;

File diff suppressed because it is too large Load Diff

View File

@ -70,7 +70,7 @@ static void emit_delay(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt)
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));
emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0), 0);
return;
}
}
@ -412,9 +412,8 @@ static unsigned find_signal_in_nexus(ivl_scope_t scope, ivl_nexus_t nex)
unsigned is_driver = 0;
unsigned is_array = 0;
int64_t array_idx = 0;
unsigned idx, count;
unsigned idx, count = ivl_nexus_ptrs(nex);
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);
@ -501,7 +500,7 @@ static void emit_number_as_string(ivl_net_const_t net_const)
/* Skip any NULL bytes. */
if (val == 0) continue;
/* Print some values that must be escapped. */
/* Print some values that must be escaped. */
if (val == '"') fprintf(vlog_out, "\\\"");
else if (val == '\\') fprintf(vlog_out, "\\\\");
/* Print the printable characters. */
@ -616,8 +615,84 @@ static unsigned find_const_nexus(ivl_scope_t scope, ivl_nexus_t nex)
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)
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. */
@ -630,9 +705,16 @@ void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex)
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
@ -641,7 +723,7 @@ void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex)
/* 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);
// dump_nexus_information(scope, nex);
}
/*
@ -758,6 +840,10 @@ static unsigned is_escaped(const char *id)
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;
}

View File

@ -61,6 +61,8 @@ static int32_t get_int32_from_bits(const char *bits, unsigned nbits,
}
}
/* Sign extend as needed. */
// HERE: Need to emit 1 instead of -1 for some of the constants.
// if (is_signed && (nbits > 1) && (msb == '1') && (trim_wid < 32U)) {
if (is_signed && (msb == '1') && (trim_wid < 32U)) {
value |= ~(((int32_t)1 << trim_wid) - (int32_t)1);
}

View File

@ -204,7 +204,7 @@ void emit_net_def(ivl_scope_t scope, ivl_signal_t sig)
ivl_signal_lineno(sig), ivl_signal_basename(sig));
vlog_errors += 1;
} else {
switch(ivl_signal_type(sig)) {
switch (ivl_signal_type(sig)) {
case IVL_SIT_TRI:
case IVL_SIT_UWIRE:
// HERE: Need to add support for supply nets. Probably supply strength
@ -363,19 +363,21 @@ static void emit_module_ports(ivl_scope_t scope)
if (count == 0) return;
fprintf(vlog_out, "(");
emit_nexus_as_ca(scope, ivl_scope_mod_port(scope, 0));
emit_nexus_as_ca(scope, ivl_scope_mod_port(scope, 0), 0);
for (idx = 1; idx < count; idx += 1) {
fprintf(vlog_out, ", ");
emit_nexus_as_ca(scope, ivl_scope_mod_port(scope, idx));
emit_nexus_as_ca(scope, ivl_scope_mod_port(scope, idx), 0);
}
fprintf(vlog_out, ")");
}
static ivl_signal_t get_port_from_nexus(ivl_scope_t scope, ivl_nexus_t nex)
static ivl_signal_t get_port_from_nexus(ivl_scope_t scope, ivl_nexus_t nex,
unsigned *word)
{
assert(nex);
unsigned idx, count = ivl_nexus_ptrs(nex);
ivl_signal_t sig = 0;
*word = 0;
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);
@ -383,6 +385,7 @@ static ivl_signal_t get_port_from_nexus(ivl_scope_t scope, ivl_nexus_t nex)
if (ivl_signal_scope(t_sig) != scope) continue;
assert(! sig);
sig = t_sig;
*word = ivl_nexus_ptr_pin(nex_ptr);
}
}
return sig;
@ -475,7 +478,12 @@ static void emit_port(ivl_signal_t port)
}
emit_sig_type(port);
fprintf(vlog_out, " ");
emit_id(ivl_signal_basename(port));
/* Split port (arg[7:4],arg[3:0]) are generated using local signals. */
if (ivl_signal_local(port)) {
fprintf(vlog_out, "ivlog%s", ivl_signal_basename(port));
} else {
emit_id(ivl_signal_basename(port));
}
fprintf(vlog_out, ";");
emit_sig_file_line(port);
fprintf(vlog_out, "\n");
@ -483,10 +491,11 @@ static void emit_port(ivl_signal_t port)
static void emit_module_port_defs(ivl_scope_t scope)
{
unsigned idx, count = ivl_scope_ports(scope);
unsigned word, idx, count = ivl_scope_ports(scope);
for (idx = 0; idx < count; idx += 1) {
ivl_nexus_t nex = ivl_scope_mod_port(scope, idx);
ivl_signal_t port = get_port_from_nexus(scope, nex);
ivl_signal_t port = get_port_from_nexus(scope, nex, &word);
// HERE: Do we need to use word?
if (port) emit_port(port);
else {
fprintf(vlog_out, "<missing>");
@ -502,15 +511,17 @@ static void emit_module_port_defs(ivl_scope_t scope)
static void emit_module_call_expr(ivl_scope_t scope, unsigned idx)
{
unsigned word;
ivl_nexus_t nex = ivl_scope_mod_port(scope, idx);
ivl_signal_t port = get_port_from_nexus(scope, nex);
ivl_signal_t port = get_port_from_nexus(scope, nex, &word);
/* For an input port we need to emit the driving expression. */
if (ivl_signal_port(port) == IVL_SIP_INPUT) {
emit_nexus_port_driver_as_ca(ivl_scope_parent(scope),
ivl_signal_nex(port, 0));
ivl_signal_nex(port, word));
/* For an output we need to emit the signal the output is driving. */
} else {
emit_nexus_as_ca(ivl_scope_parent(scope), ivl_signal_nex(port, 0));
emit_nexus_as_ca(ivl_scope_parent(scope),
ivl_signal_nex(port, word), 0);
}
}
@ -536,6 +547,70 @@ static void emit_task_func_port_defs(ivl_scope_t scope)
if (count) fprintf(vlog_out, "\n");
}
/*
* Look at all the processes to see if we can find one with the expected
* scope. If we don't find one then we can assume the block only has
* variable definitions and needs to be emitted here in the scope code.
*/
static int no_stmts_in_process(ivl_process_t proc, ivl_scope_t scope)
{
ivl_statement_t stmt = ivl_process_stmt(proc);
switch (ivl_statement_type(stmt)) {
case IVL_ST_BLOCK:
case IVL_ST_FORK:
if (ivl_stmt_block_scope(stmt) == scope) return 1;
default: /* Do nothing. */ ;
}
return 0;
}
/*
* If a named block has no statements then we may need to emit it here if
* there are variable definitions in the scope. We translate all this to
* an initial and named begin since that is enough to hold the variables.
*/
static void emit_named_block_scope(ivl_scope_t scope)
{
unsigned idx, count = ivl_scope_events(scope);
unsigned named_ev = 0;
/* If there are no parameters, signals or named events then skip
* this block. */
for (idx = 0; idx < count; idx += 1) {
ivl_event_t event = ivl_scope_event(scope, idx);
/* If this event has any type of edge sensitivity then it is
* not a named event. */
if (ivl_event_nany(event)) continue;
if (ivl_event_npos(event)) continue;
if (ivl_event_nneg(event)) continue;
named_ev = 1;
break;
}
if ((ivl_scope_params(scope) == 0) && (ivl_scope_sigs(scope) == 0) &&
(named_ev == 0)) return;
/* Currently we only need to emit a named block for the variables
* if the parent scope is a module. This gets much more complicated
* if this is not true. */
if (ivl_scope_type(ivl_scope_parent(scope)) != IVL_SCT_MODULE) return;
/* Scan all the processes looking for one that matches this scope.
* If a match is found then this named block was already emitted by
* the process code. */
if (ivl_design_process(design, (ivl_process_f)no_stmts_in_process,
scope)) return;
/* A match was not found so emit the named block here to get the
* variable definitions. */
fprintf(vlog_out, "\n%*cinitial begin: ", indent, ' ');
emit_id(ivl_scope_tname(scope));
emit_scope_file_line(scope);
fprintf(vlog_out, "\n");
indent += indent_incr;
emit_scope_variables(scope);
indent -= indent_incr;
fprintf(vlog_out, "%*cend /* ", indent, ' ');
emit_id(ivl_scope_tname(scope));
fprintf(vlog_out, " */\n");
}
/*
* This search method may be slow for a large structural design with a
* large number of gate types. That's not what this converter was built
@ -652,6 +727,7 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent)
case IVL_SCT_BEGIN:
case IVL_SCT_FORK:
assert(indent != 0);
emit_named_block_scope(scope);
return 0; /* A named begin/fork is handled in line. */
default:
fprintf(stderr, "%s:%u: vlog95 error: Unsupported scope type "

View File

@ -780,7 +780,7 @@ static void emit_stmt_case(ivl_scope_t scope, ivl_statement_t stmt)
{
char *case_type;
unsigned idx, default_case, count = ivl_stmt_case_count(stmt);
switch(ivl_statement_type(stmt)) {
switch (ivl_statement_type(stmt)) {
case IVL_ST_CASE:
case IVL_ST_CASER:
case_type = "case";
@ -1047,7 +1047,7 @@ static void emit_stmt_while(ivl_scope_t scope, ivl_statement_t stmt)
void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt)
{
switch(ivl_statement_type(stmt)) {
switch (ivl_statement_type(stmt)) {
case IVL_ST_NOOP:
/* If this is a statement termination then just finish the
* statement, otherwise print an empty begin/end pair. */
@ -1159,7 +1159,8 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt)
void emit_process(ivl_scope_t scope, ivl_process_t proc)
{
ivl_statement_t stmt;
ivl_statement_t stmt = ivl_process_stmt(proc);
if (ivl_statement_type(stmt) == IVL_ST_NOOP) return;
fprintf(vlog_out, "\n%*c", get_indent(), ' ');
switch (ivl_process_type(proc)) {
case IVL_PR_INITIAL:
@ -1181,7 +1182,6 @@ void emit_process(ivl_scope_t scope, ivl_process_t proc)
ivl_process_file(proc),
ivl_process_lineno(proc));
}
stmt = ivl_process_stmt(proc);
if (ivl_statement_type(stmt) == IVL_ST_NOOP) {
fprintf(vlog_out, " begin\n%*cend\n", get_indent(), ' ');
} else {

View File

@ -25,7 +25,7 @@
static void emit_entry(ivl_udp_t udp, char entry, unsigned *rerun)
{
const char *value = 0;
switch(entry) {
switch (entry) {
case '0':
value = " 0 ";
break;

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2011 Cary R. (cygcary@yahoo.com)
* Copyright (C) 2010-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
@ -30,7 +30,7 @@
static const char*version_string =
"Icarus Verilog VLOG95 Code Generator " VERSION " (" VERSION_TAG ")\n\n"
"Copyright (C) 2010-2011 Cary R. (cygcary@yahoo.com)\n\n"
"Copyright (C) 2010-2012 Cary R. (cygcary@yahoo.com)\n\n"
" This program is free software; you can redistribute it and/or modify\n"
" it under the terms of the GNU General Public License as published by\n"
" the Free Software Foundation; either version 2 of the License, or\n"
@ -195,6 +195,10 @@ int target_design(ivl_design_t des)
fclose(vlog_out);
/* A do nothing call to prevent warnings about this routine not
* being used. */
dump_nexus_information(0, 0);
return vlog_errors;
}

View File

@ -85,8 +85,10 @@ extern void emit_scope_path(ivl_scope_t scope, ivl_scope_t call_scope);
extern void emit_scope_variables(ivl_scope_t scope);
extern void emit_scope_call_path(ivl_scope_t scope, ivl_scope_t call_scope);
extern void emit_scope_module_path(ivl_scope_t scope, ivl_scope_t call_scope);
extern void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex);
extern void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex);
extern void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex,
unsigned allow_UD);
extern void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex,
unsigned allow_UD);
extern void emit_nexus_port_driver_as_ca(ivl_scope_t scope, ivl_nexus_t nex);
extern void emit_const_nexus(ivl_scope_t scope, ivl_net_const_t const_net);
extern void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig);