vlog95: Fix pull gate bug, emit local expr sigs as CA, etc.

This patch fixes the following bugs in the vlog95 converter:

When emitting a signal as an expression, if the signal is local then
emit the expression that is driving the local signal.

The casts need to emit the expression.

When emitting pull devices they have different default strength rules
(pull is the default).

For compatibility pull devices should only have a single output net.

Extend the code that searches for the <var> = <delay> <value> pattern
to also check for an <event>. The previous event code only looks for
the repeat event pattern.
This commit is contained in:
Cary R 2011-02-10 20:42:51 -08:00 committed by Stephen Williams
parent dfa6370187
commit 88cec4d6f6
5 changed files with 97 additions and 55 deletions

View File

@ -351,14 +351,18 @@ static void emit_expr_sfunc(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
static void emit_expr_signal(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
{
ivl_signal_t sig = ivl_expr_signal(expr);
emit_scope_module_path(scope, ivl_signal_scope(sig));
fprintf(vlog_out, "%s", ivl_signal_basename(sig));
if (ivl_signal_dimensions(sig)) {
int lsb = ivl_signal_array_base(sig);
int msb = lsb + ivl_signal_array_count(sig);
fprintf(vlog_out, "[");
emit_scaled_expr(scope, ivl_expr_oper1(expr), msb, lsb);
fprintf(vlog_out, "]");
if (ivl_signal_local(sig)) {
emit_nexus_as_ca(scope, ivl_signal_nex(sig, 0));
} else {
emit_scope_module_path(scope, ivl_signal_scope(sig));
fprintf(vlog_out, "%s", ivl_signal_basename(sig));
if (ivl_signal_dimensions(sig)) {
int lsb = ivl_signal_array_base(sig);
int msb = lsb + ivl_signal_array_count(sig);
fprintf(vlog_out, "[");
emit_scaled_expr(scope, ivl_expr_oper1(expr), msb, lsb);
fprintf(vlog_out, "]");
}
}
}
@ -411,6 +415,7 @@ static void emit_expr_unary(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
case 'i':
case 'r':
/* A cast is a noop. */
emit_expr(scope, ivl_expr_oper1(expr), wid);
break;
default:
fprintf(vlog_out, "<unknown>");

View File

@ -54,30 +54,45 @@ static void emit_strength(ivl_drive_t drive1, ivl_drive_t drive0,
const char *file, unsigned lineno)
{
assert(strength_type <= 2);
if ((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG)) {
if ((strength_type == 2) &&
((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG))) {
fprintf(vlog_out, " (");
if (strength_type > 0) {
if (emit_drive(drive1)) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s "
"1 drive (%d)\n", file, lineno,
type, (int)drive1);
vlog_errors += 1;
}
fprintf(vlog_out, "1");
if (emit_drive(drive1)) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s "
"1 drive (%d)\n", file, lineno,
type, (int)drive1);
vlog_errors += 1;
}
if (strength_type == 2) fprintf(vlog_out, ", ");
if ((strength_type & 0x01) == 0) {
if (emit_drive(drive0)) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s "
"0 drive (%d)\n", file, lineno,
type, (int)drive1);
vlog_errors += 1;
}
fprintf(vlog_out, "0");
fprintf(vlog_out, "1, ");
if (emit_drive(drive0)) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s "
"0 drive (%d)\n", file, lineno,
type, (int)drive0);
vlog_errors += 1;
}
fprintf(vlog_out, ")");
fprintf(vlog_out, "0)");
} else if ((strength_type == 1) && (drive1 != IVL_DR_PULL)) {
fprintf(vlog_out, " (");
if (emit_drive(drive1)) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s "
"1 drive (%d)\n", file, lineno,
type, (int)drive1);
vlog_errors += 1;
}
fprintf(vlog_out, "1)");
} else if ((strength_type == 0) && (drive0 != IVL_DR_PULL)) {
fprintf(vlog_out, " (");
if (emit_drive(drive0)) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Unsupported %s "
"0 drive (%d)\n", file, lineno,
type, (int)drive0);
vlog_errors += 1;
}
fprintf(vlog_out, "0)");
}
}
@ -108,28 +123,28 @@ static void emit_delay(ivl_scope_t scope, ivl_expr_t rise, ivl_expr_t fall,
/* If all three delays match then we only have a single delay. */
if ((rise == fall) && (rise == decay)) {
fprintf(vlog_out, " #(");
emit_scaled_delayx(scope, rise);
emit_scaled_delayx(scope, rise, 0);
fprintf(vlog_out, ")");
return;
}
/* If we have a gate that only supports two delays then print them. */
if (dly_count == 2) {
fprintf(vlog_out, " #(");
emit_scaled_delayx(scope, rise);
emit_scaled_delayx(scope, rise, 0);
fprintf(vlog_out, ", ");
emit_scaled_delayx(scope, fall);
emit_scaled_delayx(scope, fall, 0);
fprintf(vlog_out, ")");
return;
}
/* What's left is a gate that supports three delays. */
fprintf(vlog_out, " #(");
emit_scaled_delayx(scope, rise);
emit_scaled_delayx(scope, rise, 0);
fprintf(vlog_out, ", ");
emit_scaled_delayx(scope, fall);
emit_scaled_delayx(scope, fall, 0);
if (decay) {
fprintf(vlog_out, ", ");
emit_scaled_delayx(scope, decay);
emit_scaled_delayx(scope, decay, 0);
}
fprintf(vlog_out, ")");
}
@ -215,7 +230,7 @@ static ivl_nexus_t get_lpm_output(ivl_scope_t scope, ivl_lpm_t lpm)
static void emit_logic_as_ca(ivl_scope_t scope, ivl_net_logic_t nlogic);
static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm);
static void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex)
void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex)
{
/* If there is no nexus then there is nothing to print. */
if (! nex) return;
@ -857,11 +872,13 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic)
case IVL_LO_PULLDOWN:
fprintf(vlog_out, "pulldown");
dly_count = 0;
outputs = 0;
strength_type = 0;
break;
case IVL_LO_PULLUP:
fprintf(vlog_out, "pullup");
dly_count = 0;
outputs = 0;
strength_type = 1;
break;
case IVL_LO_RCMOS:
@ -919,7 +936,13 @@ void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic)
emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, idx));
fprintf(vlog_out, ", ");
}
emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, count));
if (strength_type == 2) {
emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, idx));
} else {
/* A pull gate only has a single output connection. */
assert(count == 0);
emit_name_of_logic_nexus(scope, nlogic, ivl_logic_pin(nlogic, idx));
}
fprintf(vlog_out, ");");
emit_logic_file_line(nlogic);
fprintf(vlog_out, "\n");
@ -990,7 +1013,6 @@ void emit_tran(ivl_scope_t scope, ivl_switch_t tran)
}
fprintf(vlog_out, ");");
if (emit_file_line) {
assert(ivl_switch_lineno(tran));
fprintf(vlog_out, " /* %s:%u */",
ivl_switch_file(tran),
ivl_switch_lineno(tran));

View File

@ -65,7 +65,7 @@ void emit_scaled_delay(ivl_scope_t scope, uint64_t delay)
* 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)
void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr, unsigned is_stmt)
{
if (ivl_expr_type(expr) == IVL_EX_NUMBER) {
assert(! ivl_expr_signed(expr));
@ -109,6 +109,9 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr)
emit_expr(scope, expr, 0);
return;
} else {
// HERE: If we have a statement delay then a real variable delay has already
// been encoded as int((real expr) * tu_tp_scale) * tp_sim_scale. So
// we need to look for that pattern as well.
uint64_t scale_val, scale = 1;
int rtype;
assert(! ivl_expr_signed(expr));
@ -154,7 +157,7 @@ void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr)
scale *= 10;
exponent -= 1;
}
if (scale != scale_val) {
if (scale != scale_val) {
fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Variable time "
"expression/value scale coefficient "

View File

@ -150,18 +150,20 @@ static unsigned emit_stmt_lval(ivl_scope_t scope, ivl_statement_t stmt)
}
/*
* Icarus translated <var> = <delay> <value> into
* Icarus translated <var> = <delay or event> <value> into
* begin
* <tmp> = <value>;
* <delay> <var> = <tmp>;
* <delay or event> <var> = <tmp>;
* end
* This routine looks for this pattern and turns it back into the
* appropriate blocking assignment.
*/
static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt)
static unsigned find_delayed_or_event_assign(ivl_scope_t scope,
ivl_statement_t stmt)
{
unsigned wid;
ivl_statement_t assign, delay, delayed_assign;
ivl_statement_type_t delay_type;
ivl_lval_t lval;
ivl_expr_t rval;
ivl_signal_t lsig, rsig;
@ -173,7 +175,9 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt)
if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0;
/* The second must be a delayx. */
delay = ivl_stmt_block_stmt(stmt, 1);
if (ivl_statement_type(delay) != IVL_ST_DELAYX) return 0;
delay_type = ivl_statement_type(delay);
if ((delay_type != IVL_ST_DELAYX) &&
(delay_type != IVL_ST_WAIT)) return 0;
/* The statement for the delayx must be an assign. */
delayed_assign = ivl_stmt_sub_stmt(delay);
if (ivl_statement_type(delayed_assign) != IVL_ST_ASSIGN) return 0;
@ -201,8 +205,14 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt)
// HERE: Do we need to calculate the width? The compiler should have already
// done this for us.
wid = emit_stmt_lval(scope, delayed_assign);
fprintf(vlog_out, " = #(");
emit_scaled_delayx(scope, ivl_stmt_delay_expr(delay));
fprintf(vlog_out, " = ");
if (delay_type == IVL_ST_DELAYX) {
fprintf(vlog_out, "#(");
emit_scaled_delayx(scope, ivl_stmt_delay_expr(delay), 1);
} else {
fprintf(vlog_out, "@(");
emit_event(scope, delay);
}
fprintf(vlog_out, ") ");
emit_expr(scope, ivl_stmt_rval(assign), wid);
fprintf(vlog_out, ";");
@ -213,16 +223,17 @@ static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt)
}
/*
* Icarus translated <var> = <event> <value> into
* Icarus translated <var> = repeat(<count>) <event> <value> into
* begin
* <tmp> = <value>;
* <event>;
* repeat(<count>) <event>;
* <var> = <tmp>;
* end
* This routine looks for this pattern and turns it back into the
* appropriate blocking assignment.
*/
static unsigned find_event_assign(ivl_scope_t scope, ivl_statement_t stmt)
static unsigned find_repeat_event_assign(ivl_scope_t scope,
ivl_statement_t stmt)
{
unsigned wid;
ivl_statement_t assign, event, event_assign, repeat = 0;
@ -237,8 +248,7 @@ static unsigned find_event_assign(ivl_scope_t scope, ivl_statement_t stmt)
if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0;
/* The second must be a repeat with an event or an event. */
event = ivl_stmt_block_stmt(stmt, 1);
if (ivl_statement_type(event) != IVL_ST_REPEAT &&
ivl_statement_type(event) != IVL_ST_WAIT) return 0;
if (ivl_statement_type(event) != IVL_ST_REPEAT) return 0;
if (ivl_statement_type(event) == IVL_ST_REPEAT) {
repeat = event;
event = ivl_stmt_sub_stmt(repeat);
@ -704,7 +714,7 @@ static void emit_stmt_delay(ivl_scope_t scope, ivl_statement_t stmt)
static void emit_stmt_delayx(ivl_scope_t scope, ivl_statement_t stmt)
{
fprintf(vlog_out, "%*c#(", get_indent(), ' ');
emit_scaled_delayx(scope, ivl_stmt_delay_expr(stmt));
emit_scaled_delayx(scope, ivl_stmt_delay_expr(stmt), 1);
fprintf(vlog_out, ")");
emit_stmt_file_line(stmt);
single_indent = 1;
@ -880,8 +890,8 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt)
if (ivl_stmt_block_scope(stmt)) {
emit_stmt_block_named(scope, stmt);
} else {
if (find_delayed_assign(scope, stmt)) break;
if (find_event_assign(scope, stmt)) break;
if (find_delayed_or_event_assign(scope, stmt)) break;
if (find_repeat_event_assign(scope, stmt)) break;
if (find_wait(scope, stmt)) break;
emit_stmt_block(scope, stmt);
}

View File

@ -69,7 +69,8 @@ extern void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt);
extern void emit_tran(ivl_scope_t scope, ivl_switch_t tran);
extern void emit_scaled_delay(ivl_scope_t scope, uint64_t delay);
extern void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr);
extern void emit_scaled_delayx(ivl_scope_t scope, ivl_expr_t expr,
unsigned is_stmt);
extern void emit_scaled_expr(ivl_scope_t scope, ivl_expr_t expr,
int msb, int lsb);
extern void emit_scaled_range(ivl_scope_t scope, ivl_expr_t expr,
@ -78,6 +79,7 @@ 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_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_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);
extern void emit_icarus_generated_udps();