vlog95: Convert compiler translation back to wait and strength fixes.

This patch adds the ability to translate the code produced by the
compiler for a wait statement back into a wait statement. It also
reworks the strength emitting code to only emit a single strength
for the pull gates and fixes a bug in the strength emitting code.
This commit is contained in:
Cary R 2011-02-02 13:04:06 -08:00 committed by Stephen Williams
parent b08120e223
commit 05a00ff631
3 changed files with 132 additions and 68 deletions

View File

@ -237,6 +237,8 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
msb = ivl_signal_msb(sig); msb = ivl_signal_msb(sig);
lsb = ivl_signal_lsb(sig); lsb = ivl_signal_lsb(sig);
} }
// HERE: If this is a constant then it was likely a parameter reference.
// We need to find the appropriate parameter and use it instead.
emit_expr(scope, sig_expr, wid); emit_expr(scope, sig_expr, wid);
if (width == 1) { if (width == 1) {
fprintf(vlog_out, "["); fprintf(vlog_out, "[");

View File

@ -225,28 +225,42 @@ static unsigned emit_drive(ivl_drive_t drive)
return 0; return 0;
} }
static void emit_gate_strength(ivl_net_logic_t nlogic) /*
* If the strength type is 2 then emit both strengths. If it is 1 then only
* emit the 1 strength (pullup) and if it is 0 only emit the 0 strength
* (pulldown).
*/
static void emit_gate_strength(ivl_net_logic_t nlogic, unsigned strength_type)
{ {
assert(strength_type <= 2);
ivl_drive_t drive1 = ivl_logic_drive1(nlogic); ivl_drive_t drive1 = ivl_logic_drive1(nlogic);
ivl_drive_t drive0 = ivl_logic_drive0(nlogic); ivl_drive_t drive0 = ivl_logic_drive0(nlogic);
if ((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG)) { if ((drive1 != IVL_DR_STRONG) || (drive0 != IVL_DR_STRONG)) {
fprintf(vlog_out, " ("); fprintf(vlog_out, " (");
if (emit_drive(drive1)) { if (strength_type > 0) {
fprintf(vlog_out, "<invalid>"); if (emit_drive(drive1)) {
fprintf(stderr, "%s:%u: vlog95 error: Unsupported gate " fprintf(vlog_out, "<invalid>");
"1 drive (%d)\n", ivl_logic_file(nlogic), fprintf(stderr, "%s:%u: vlog95 error: Unsupported gate "
ivl_logic_lineno(nlogic), (int)drive1); "1 drive (%d)\n",
vlog_errors += 1; ivl_logic_file(nlogic),
ivl_logic_lineno(nlogic), (int)drive1);
vlog_errors += 1;
}
fprintf(vlog_out, "1");
} }
fprintf(vlog_out, "1, "); if (strength_type == 2) fprintf(vlog_out, ", ");
if (emit_drive(drive0)) { if ((strength_type & 0x01) == 0) {
fprintf(vlog_out, "<invalid>"); if (emit_drive(drive0)) {
fprintf(stderr, "%s:%u: vlog95 error: Unsupported gate " fprintf(vlog_out, "<invalid>");
"0 drive (%d)\n", ivl_logic_file(nlogic), fprintf(stderr, "%s:%u: vlog95 error: Unsupported gate "
ivl_logic_lineno(nlogic), (int)drive0); "0 drive (%d)\n",
vlog_errors += 1; ivl_logic_file(nlogic),
ivl_logic_lineno(nlogic), (int)drive0);
vlog_errors += 1;
}
fprintf(vlog_out, "0");
} }
fprintf(vlog_out, "0)"); fprintf(vlog_out, ")");
} }
} }
@ -264,7 +278,6 @@ static void emit_lpm_strength(ivl_lpm_t lpm)
vlog_errors += 1; vlog_errors += 1;
} }
fprintf(vlog_out, "1, "); fprintf(vlog_out, "1, ");
emit_drive(drive0);
if (emit_drive(drive0)) { if (emit_drive(drive0)) {
fprintf(vlog_out, "<invalid>"); fprintf(vlog_out, "<invalid>");
fprintf(stderr, "%s:%u: vlog95 error: Unsupported LPM " fprintf(stderr, "%s:%u: vlog95 error: Unsupported LPM "
@ -742,7 +755,7 @@ static void emit_bufz(ivl_scope_t scope, ivl_net_logic_t nlogic)
{ {
assert(ivl_logic_pins(nlogic) == 2); assert(ivl_logic_pins(nlogic) == 2);
fprintf(vlog_out, "assign"); fprintf(vlog_out, "assign");
emit_gate_strength(nlogic); emit_gate_strength(nlogic, 2);
emit_delay(scope, emit_delay(scope,
ivl_logic_delay(nlogic, 0), ivl_logic_delay(nlogic, 0),
ivl_logic_delay(nlogic, 1), ivl_logic_delay(nlogic, 1),
@ -757,7 +770,7 @@ static void emit_bufz(ivl_scope_t scope, ivl_net_logic_t nlogic)
static void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic) static void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic)
{ {
unsigned idx, count, dly_count; unsigned idx, count, dly_count, strength_type = 2;
fprintf(vlog_out, "%*c", indent, ' '); fprintf(vlog_out, "%*c", indent, ' ');
switch (ivl_logic_type(nlogic)) { switch (ivl_logic_type(nlogic)) {
case IVL_LO_AND: case IVL_LO_AND:
@ -819,10 +832,12 @@ static void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic)
case IVL_LO_PULLDOWN: case IVL_LO_PULLDOWN:
fprintf(vlog_out, "pulldown"); fprintf(vlog_out, "pulldown");
dly_count = 0; dly_count = 0;
strength_type = 0;
break; break;
case IVL_LO_PULLUP: case IVL_LO_PULLUP:
fprintf(vlog_out, "pullup"); fprintf(vlog_out, "pullup");
dly_count = 0; dly_count = 0;
strength_type = 1;
break; break;
case IVL_LO_RCMOS: case IVL_LO_RCMOS:
fprintf(vlog_out, "rcmos"); fprintf(vlog_out, "rcmos");
@ -855,7 +870,7 @@ static void emit_logic(ivl_scope_t scope, ivl_net_logic_t nlogic)
dly_count = 0; dly_count = 0;
break; break;
} }
emit_gate_strength(nlogic); emit_gate_strength(nlogic, strength_type);
if (dly_count) emit_delay(scope, if (dly_count) emit_delay(scope,
ivl_logic_delay(nlogic, 0), ivl_logic_delay(nlogic, 0),
ivl_logic_delay(nlogic, 1), ivl_logic_delay(nlogic, 1),

View File

@ -139,6 +139,67 @@ static unsigned emit_stmt_lval(ivl_scope_t scope, ivl_statement_t stmt)
return wid; return wid;
} }
/*
* Icarus translated <var> = <delay> <value> into
* begin
* <tmp> = <value>;
* <delay> <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)
{
unsigned wid;
ivl_statement_t assign, delay, delayed_assign;
ivl_lval_t lval;
ivl_expr_t rval;
ivl_signal_t lsig, rsig;
/* We must have two block elements. */
if (ivl_stmt_block_count(stmt) != 2) return 0;
/* The first must be an assign. */
assign = ivl_stmt_block_stmt(stmt, 0);
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;
/* 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;
/* The L-value must be a single signal. */
if (ivl_stmt_lvals(assign) != 1) return 0;
lval = ivl_stmt_lval(assign, 0);
/* It must not have an array select. */
if (ivl_lval_idx(lval)) return 0;
/* It must not have a non-zero base. */
if (ivl_lval_part_off(lval)) return 0;
lsig = ivl_lval_sig(lval);
/* It must not be part of the signal. */
if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0;
/* The R-value must be a single signal. */
rval = ivl_stmt_rval(delayed_assign);
if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0;
/* It must not be an array word. */
if (ivl_expr_oper1(rval)) return 0;
rsig = ivl_expr_signal(rval);
/* And finally the two signals must be the same. */
if (lsig != rsig) return 0;
/* The pattern matched so generate the appropriate code. */
fprintf(vlog_out, "%*c", get_indent(), ' ');
// 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, ") ");
emit_expr(scope, ivl_stmt_rval(assign), wid);
fprintf(vlog_out, ";\n");
return 1;
}
/* /*
* Icarus translated <var> = <event> <value> into * Icarus translated <var> = <event> <value> into
* begin * begin
@ -215,63 +276,48 @@ static unsigned find_event_assign(ivl_scope_t scope, ivl_statement_t stmt)
} }
/* /*
* Icarus translated <var> = <delay> <value> into * Icarus translated wait(<expr) <stmt> into
* begin * begin
* <tmp> = <value>; * while (<expr> !== 1'b1) @(<expr sensitivities>);
* <delay> <var> = <tmp>; * <stmt>
* end * end
* This routine looks for this pattern and turns it back into the * This routine looks for this pattern and turns it back into a
* appropriate blocking assignment. * wait statement.
*/ */
static unsigned find_delayed_assign(ivl_scope_t scope, ivl_statement_t stmt) static unsigned find_wait(ivl_scope_t scope, ivl_statement_t stmt)
{ {
unsigned wid; ivl_statement_t while_wait, wait, wait_stmt;
ivl_statement_t assign, delay, delayed_assign; ivl_expr_t while_expr, expr;
ivl_lval_t lval; const char *bits;
ivl_expr_t rval;
ivl_signal_t lsig, rsig;
/* We must have two block elements. */ /* We must have two block elements. */
if (ivl_stmt_block_count(stmt) != 2) return 0; if (ivl_stmt_block_count(stmt) != 2) return 0;
/* The first must be an assign. */ /* The first must be a while. */
assign = ivl_stmt_block_stmt(stmt, 0); while_wait = ivl_stmt_block_stmt(stmt, 0);
if (ivl_statement_type(assign) != IVL_ST_ASSIGN) return 0; if (ivl_statement_type(while_wait) != IVL_ST_WHILE) return 0;
/* The second must be a delayx. */ /* That has a wait with a NOOP statement. */
delay = ivl_stmt_block_stmt(stmt, 1); wait = ivl_stmt_sub_stmt(while_wait);
if (ivl_statement_type(delay) != IVL_ST_DELAYX) return 0; if (ivl_statement_type(wait) != IVL_ST_WAIT) return 0;
/* The statement for the delayx must be an assign. */ wait_stmt = ivl_stmt_sub_stmt(wait);
delayed_assign = ivl_stmt_sub_stmt(delay); if (ivl_statement_type(wait_stmt) != IVL_ST_NOOP) return 0;
if (ivl_statement_type(delayed_assign) != IVL_ST_ASSIGN) return 0; /* Check that the while condition has the correct form. */
/* The L-value must be a single signal. */ while_expr = ivl_stmt_cond_expr(while_wait);
if (ivl_stmt_lvals(assign) != 1) return 0; if (ivl_expr_type(while_expr) != IVL_EX_BINARY) return 0;
lval = ivl_stmt_lval(assign, 0); if (ivl_expr_opcode(while_expr) != 'N') return 0;
/* It must not have an array select. */ /* And has a second operator that is a constant 1'b1. */
if (ivl_lval_idx(lval)) return 0; expr = ivl_expr_oper2(while_expr);
/* It must not have a non-zero base. */ if (ivl_expr_type(expr) != IVL_EX_NUMBER) return 0;
if (ivl_lval_part_off(lval)) return 0; if (ivl_expr_width(expr) != 1) return 0;
lsig = ivl_lval_sig(lval); bits = ivl_expr_bits(expr);
/* It must not be part of the signal. */ if (*bits != '1') return 0;
if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return 0; // HERE: There is no easy way to verify that the @ sensitivity list
/* The R-value must be a single signal. */ // matches the first expression so we don't check for that yet.
rval = ivl_stmt_rval(delayed_assign);
if (ivl_expr_type(rval) != IVL_EX_SIGNAL) return 0;
/* It must not be an array word. */
if (ivl_expr_oper1(rval)) return 0;
rsig = ivl_expr_signal(rval);
/* And finally the two signals must be the same. */
if (lsig != rsig) return 0;
/* The pattern matched so generate the appropriate code. */ /* The pattern matched so generate the appropriate code. */
fprintf(vlog_out, "%*c", get_indent(), ' '); fprintf(vlog_out, "%*cwait(", get_indent(), ' ');
// HERE: Do we need to calculate the width? The compiler should have already emit_expr(scope, ivl_expr_oper1(while_expr), 0);
// done this for us. fprintf(vlog_out, ")");
wid = emit_stmt_lval(scope, delayed_assign); single_indent = 1;
fprintf(vlog_out, " = #("); emit_stmt(scope, ivl_stmt_block_stmt(stmt, 1));
emit_scaled_delayx(scope, ivl_stmt_delay_expr(delay));
fprintf(vlog_out, ") ");
emit_expr(scope, ivl_stmt_rval(assign), wid);
fprintf(vlog_out, ";\n");
return 1; return 1;
} }
@ -782,6 +828,7 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt)
} else { } else {
if (find_delayed_assign(scope, stmt)) break; if (find_delayed_assign(scope, stmt)) break;
if (find_event_assign(scope, stmt)) break; if (find_event_assign(scope, stmt)) break;
if (find_wait(scope, stmt)) break;
emit_stmt_block(scope, stmt); emit_stmt_block(scope, stmt);
} }
break; break;