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:
parent
b08120e223
commit
05a00ff631
|
|
@ -237,6 +237,8 @@ static void emit_expr_select(ivl_scope_t scope, ivl_expr_t expr, unsigned wid)
|
|||
msb = ivl_signal_msb(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);
|
||||
if (width == 1) {
|
||||
fprintf(vlog_out, "[");
|
||||
|
|
|
|||
|
|
@ -225,28 +225,42 @@ static unsigned emit_drive(ivl_drive_t drive)
|
|||
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 drive0 = ivl_logic_drive0(nlogic);
|
||||
if ((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 gate "
|
||||
"1 drive (%d)\n", ivl_logic_file(nlogic),
|
||||
"1 drive (%d)\n",
|
||||
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 ((strength_type & 0x01) == 0) {
|
||||
if (emit_drive(drive0)) {
|
||||
fprintf(vlog_out, "<invalid>");
|
||||
fprintf(stderr, "%s:%u: vlog95 error: Unsupported gate "
|
||||
"0 drive (%d)\n", ivl_logic_file(nlogic),
|
||||
"0 drive (%d)\n",
|
||||
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;
|
||||
}
|
||||
fprintf(vlog_out, "1, ");
|
||||
emit_drive(drive0);
|
||||
if (emit_drive(drive0)) {
|
||||
fprintf(vlog_out, "<invalid>");
|
||||
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);
|
||||
fprintf(vlog_out, "assign");
|
||||
emit_gate_strength(nlogic);
|
||||
emit_gate_strength(nlogic, 2);
|
||||
emit_delay(scope,
|
||||
ivl_logic_delay(nlogic, 0),
|
||||
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)
|
||||
{
|
||||
unsigned idx, count, dly_count;
|
||||
unsigned idx, count, dly_count, strength_type = 2;
|
||||
fprintf(vlog_out, "%*c", indent, ' ');
|
||||
switch (ivl_logic_type(nlogic)) {
|
||||
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:
|
||||
fprintf(vlog_out, "pulldown");
|
||||
dly_count = 0;
|
||||
strength_type = 0;
|
||||
break;
|
||||
case IVL_LO_PULLUP:
|
||||
fprintf(vlog_out, "pullup");
|
||||
dly_count = 0;
|
||||
strength_type = 1;
|
||||
break;
|
||||
case IVL_LO_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;
|
||||
break;
|
||||
}
|
||||
emit_gate_strength(nlogic);
|
||||
emit_gate_strength(nlogic, strength_type);
|
||||
if (dly_count) emit_delay(scope,
|
||||
ivl_logic_delay(nlogic, 0),
|
||||
ivl_logic_delay(nlogic, 1),
|
||||
|
|
|
|||
|
|
@ -139,6 +139,67 @@ static unsigned emit_stmt_lval(ivl_scope_t scope, ivl_statement_t stmt)
|
|||
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
|
||||
* 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
|
||||
* <tmp> = <value>;
|
||||
* <delay> <var> = <tmp>;
|
||||
* while (<expr> !== 1'b1) @(<expr sensitivities>);
|
||||
* <stmt>
|
||||
* end
|
||||
* This routine looks for this pattern and turns it back into the
|
||||
* appropriate blocking assignment.
|
||||
* This routine looks for this pattern and turns it back into a
|
||||
* 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 assign, delay, delayed_assign;
|
||||
ivl_lval_t lval;
|
||||
ivl_expr_t rval;
|
||||
ivl_signal_t lsig, rsig;
|
||||
|
||||
ivl_statement_t while_wait, wait, wait_stmt;
|
||||
ivl_expr_t while_expr, expr;
|
||||
const char *bits;
|
||||
/* 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 first must be a while. */
|
||||
while_wait = ivl_stmt_block_stmt(stmt, 0);
|
||||
if (ivl_statement_type(while_wait) != IVL_ST_WHILE) return 0;
|
||||
/* That has a wait with a NOOP statement. */
|
||||
wait = ivl_stmt_sub_stmt(while_wait);
|
||||
if (ivl_statement_type(wait) != IVL_ST_WAIT) return 0;
|
||||
wait_stmt = ivl_stmt_sub_stmt(wait);
|
||||
if (ivl_statement_type(wait_stmt) != IVL_ST_NOOP) return 0;
|
||||
/* Check that the while condition has the correct form. */
|
||||
while_expr = ivl_stmt_cond_expr(while_wait);
|
||||
if (ivl_expr_type(while_expr) != IVL_EX_BINARY) return 0;
|
||||
if (ivl_expr_opcode(while_expr) != 'N') return 0;
|
||||
/* And has a second operator that is a constant 1'b1. */
|
||||
expr = ivl_expr_oper2(while_expr);
|
||||
if (ivl_expr_type(expr) != IVL_EX_NUMBER) return 0;
|
||||
if (ivl_expr_width(expr) != 1) return 0;
|
||||
bits = ivl_expr_bits(expr);
|
||||
if (*bits != '1') return 0;
|
||||
// HERE: There is no easy way to verify that the @ sensitivity list
|
||||
// matches the first expression so we don't check for that yet.
|
||||
|
||||
/* 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");
|
||||
|
||||
fprintf(vlog_out, "%*cwait(", get_indent(), ' ');
|
||||
emit_expr(scope, ivl_expr_oper1(while_expr), 0);
|
||||
fprintf(vlog_out, ")");
|
||||
single_indent = 1;
|
||||
emit_stmt(scope, ivl_stmt_block_stmt(stmt, 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -782,6 +828,7 @@ void emit_stmt(ivl_scope_t scope, ivl_statement_t stmt)
|
|||
} else {
|
||||
if (find_delayed_assign(scope, stmt)) break;
|
||||
if (find_event_assign(scope, stmt)) break;
|
||||
if (find_wait(scope, stmt)) break;
|
||||
emit_stmt_block(scope, stmt);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in New Issue