static int show_stmt_repeat(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; unsigned lab_top = local_count++, lab_out = local_count++; ivl_expr_t exp = ivl_stmt_cond_expr(net); struct vector_info cnt = draw_eval_expr(exp, 0); /* Test that 0 < expr */ fprintf(vvp_out, "T_%u.%u %%cmp/u 0, %u, %u;\n", thread_count, lab_top, cnt.base, cnt.wid); clear_expression_lookaside(); fprintf(vvp_out, " %%jmp/0xz T_%u.%u, 5;\n", thread_count, lab_out); /* This adds -1 (all ones in 2's complement) to the count. */ fprintf(vvp_out, " %%add %u, 1, %u;\n", cnt.base, cnt.wid); rc += show_statement(ivl_stmt_sub_stmt(net), sscope); fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out); clear_expression_lookaside(); clr_vector(cnt); return rc; }
static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) { if (ivl_stmt_nevent(net) == 1) { ivl_event_t ev = ivl_stmt_events(net, 0); fprintf(vvp_out, " %%wait E_%p;\n", ev); } else { unsigned idx; static unsigned int cascade_counter = 0; ivl_event_t ev = ivl_stmt_events(net, 0); fprintf(vvp_out, "Ewait_%u .event/or E_%p", cascade_counter, ev); for (idx = 1 ; idx < ivl_stmt_nevent(net) ; idx += 1) { ev = ivl_stmt_events(net, idx); fprintf(vvp_out, ", E_%p", ev); } fprintf(vvp_out, ";\n %%wait Ewait_%u;\n", cascade_counter); cascade_counter += 1; } /* Always clear the expression lookaside after a %wait. Anything can happen while the thread is waiting. */ clear_expression_lookaside(); return show_statement(ivl_stmt_sub_stmt(net), sscope); }
/* * The delayx statement is slightly more complex in that it is * necessary to calculate the delay first. Load the calculated delay * into and index register and use the %delayx instruction to do the * actual delay. */ static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; ivl_expr_t exp = ivl_stmt_delay_expr(net); ivl_statement_t stmt = ivl_stmt_sub_stmt(net); switch (ivl_expr_value(exp)) { case IVL_VT_VECTOR: { struct vector_info del = draw_eval_expr(exp, 0); fprintf(vvp_out, " %%ix/get 0, %u, %u;\n", del.base, del.wid); clr_vector(del); break; } case IVL_VT_REAL: { int word = draw_eval_real(exp); fprintf(vvp_out, " %%cvt/ir 0, %d;\n", word); clr_word(word); break; } default: assert(0); } fprintf(vvp_out, " %%delayx 0;\n"); /* Lots of things can happen during a delay. */ clear_expression_lookaside(); rc += show_statement(stmt, sscope); return rc; }
static void emit_stmt_forever(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*cforever", get_indent(), ' '); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); }
/* * Icarus translated for(<assign>; <cond>; <incr_assign>) <body> into * * begin * <assign>; * while (<cond>) begin * <body> * <incr_assign> * end * end * This routine looks for this pattern and turns it back into the * appropriate for loop. */ static unsigned is_for_loop(ivl_scope_t scope, ivl_statement_t stmt) { unsigned wid; ivl_statement_t assign, while_lp, while_blk, body, incr_assign; /* 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 while. */ while_lp = ivl_stmt_block_stmt(stmt, 1); if (ivl_statement_type(while_lp) != IVL_ST_WHILE) return 0; /* The while statement must be a block. */ while_blk = ivl_stmt_sub_stmt(while_lp); if (ivl_statement_type(while_blk) != IVL_ST_BLOCK) return 0; /* It must not be a named block. */ if (ivl_stmt_block_scope(while_blk)) return 0; /* It must have two elements. */ if (ivl_stmt_block_count(while_blk) != 2) return 0; /* The first block element (the body) can be anything. */ body = ivl_stmt_block_stmt(while_blk, 0); /* The second block element must be the increment assign. */ incr_assign = ivl_stmt_block_stmt(while_blk, 1); if (ivl_statement_type(incr_assign) != IVL_ST_ASSIGN) return 0; /* And finally the for statements must have the same line number * as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_lp)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_blk)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(incr_assign))) { return 0; } /* The pattern matched so generate the appropriate code. */ fprintf(vlog_out, "%*cfor(", get_indent(), ' '); /* Emit the initialization statement. */ // HERE: Do we need to calculate the width? The compiler should have already // done this for us. wid = emit_stmt_lval(scope, assign); fprintf(vlog_out, " = "); emit_expr(scope, ivl_stmt_rval(assign), wid); fprintf(vlog_out, "; "); /* Emit the condition. */ emit_expr(scope, ivl_stmt_cond_expr(while_lp), 0); fprintf(vlog_out, "; "); /* Emit in increment statement. */ // HERE: Do we need to calculate the width? The compiler should have already // done this for us. wid = emit_stmt_lval(scope, incr_assign); fprintf(vlog_out, " = "); emit_expr(scope, ivl_stmt_rval(incr_assign), wid); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); /* Now emit the body. */ single_indent = 1; emit_stmt(scope, body); return 1; }
static void emit_stmt_delay(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*c#", get_indent(), ' '); emit_scaled_delay(scope, ivl_stmt_delay_val(stmt)); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); }
static void emit_stmt_repeat(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*crepeat (", get_indent(), ' '); emit_expr(scope, ivl_stmt_cond_expr(stmt), 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(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), 1); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); }
static void emit_stmt_wait(ivl_scope_t scope, ivl_statement_t stmt) { fprintf(vlog_out, "%*c@(", get_indent(), ' '); emit_event(scope, stmt); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_sub_stmt(stmt)); }
/* * Icarus translated wait(<expr) <stmt> into * begin * while (<expr> !== 1'b1) @(<expr sensitivities>); * <stmt> * end * This routine looks for this pattern and turns it back into a * wait statement. */ static unsigned is_wait(ivl_scope_t scope, ivl_statement_t stmt) { 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 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; /* 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. /* And finally the two statements that represent the wait must * have the same line number as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(while_wait)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(wait))) { return 0; } /* The pattern matched so generate the appropriate code. */ fprintf(vlog_out, "%*cwait(", get_indent(), ' '); emit_expr(scope, ivl_expr_oper1(while_expr), 0); fprintf(vlog_out, ")"); emit_stmt_file_line(stmt); single_indent = 1; emit_stmt(scope, ivl_stmt_block_stmt(stmt, 1)); return 1; }
static int show_stmt_forever(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; ivl_statement_t stmt = ivl_stmt_sub_stmt(net); unsigned lab_top = local_count++; fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_top); rc += show_statement(stmt, sscope); fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top); return rc; }
/* * The delay statement is easy. Simply write a ``%delay <n>'' * instruction to delay the thread, then draw the included statement. * The delay statement comes from verilog code like this: * * ... * #<delay> <stmt>; */ static int show_stmt_delay(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; unsigned long delay = ivl_stmt_delay_val(net); ivl_statement_t stmt = ivl_stmt_sub_stmt(net); fprintf(vvp_out, " %%delay %lu;\n", delay); /* Lots of things can happen during a delay. */ clear_expression_lookaside(); rc += show_statement(stmt, sscope); return rc; }
static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; struct vector_info cvec; unsigned top_label = local_count++; unsigned out_label = local_count++; /* Start the loop. The top of the loop starts a basic block because it can be entered from above or from the bottom of the loop. */ fprintf(vvp_out, "T_%d.%d ;\n", thread_count, top_label); clear_expression_lookaside(); /* Draw the evaluation of the condition expression, and test the result. If the expression evaluates to false, then branch to the out label. */ cvec = draw_eval_expr(ivl_stmt_cond_expr(net), STUFF_OK_XZ|STUFF_OK_47); if (cvec.wid > 1) cvec = reduction_or(cvec); fprintf(vvp_out, " %%jmp/0xz T_%d.%d, %u;\n", thread_count, out_label, cvec.base); if (cvec.base >= 8) clr_vector(cvec); /* Draw the body of the loop. */ rc += show_statement(ivl_stmt_sub_stmt(net), sscope); /* This is the bottom of the loop. branch to the top where the test is repeased, and also draw the out label. */ fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, top_label); fprintf(vvp_out, "T_%d.%d ;\n", thread_count, out_label); clear_expression_lookaside(); return rc; }
static void show_statement(ivl_statement_t net, unsigned ind) { const ivl_statement_type_t code = ivl_statement_type(net); switch (code) { case IVL_ST_ASSIGN: fprintf(out, "%*s", ind, ""); show_assign_lvals(net); fprintf(out, " = "); show_expression(ivl_stmt_rval(net)); fprintf(out, ";\n"); break; case IVL_ST_BLOCK: { unsigned cnt = ivl_stmt_block_count(net); unsigned idx; fprintf(out, "%*sbegin\n", ind, ""); for (idx = 0 ; idx < cnt ; idx += 1) { ivl_statement_t cur = ivl_stmt_block_stmt(net, idx); show_statement(cur, ind+4); } fprintf(out, "%*send\n", ind, ""); break; } case IVL_ST_CONDIT: { ivl_statement_t t = ivl_stmt_cond_true(net); ivl_statement_t f = ivl_stmt_cond_false(net); fprintf(out, "%*sif (", ind, ""); show_expression(ivl_stmt_cond_expr(net)); fprintf(out, ")\n"); if (t) show_statement(t, ind+4); else fprintf(out, "%*s;\n", ind+4, ""); if (f) { fprintf(out, "%*selse\n", ind, ""); show_statement(f, ind+4); } break; } case IVL_ST_DELAY: fprintf(out, "%*s#%lu\n", ind, "", ivl_stmt_delay_val(net)); show_statement(ivl_stmt_sub_stmt(net), ind+2); break; case IVL_ST_NOOP: fprintf(out, "%*s/* noop */;\n", ind, ""); break; case IVL_ST_STASK: if (ivl_stmt_parm_count(net) == 0) { fprintf(out, "%*s%s;\n", ind, "", ivl_stmt_name(net)); } else { unsigned idx; fprintf(out, "%*s%s(", ind, "", ivl_stmt_name(net)); show_expression(ivl_stmt_parm(net, 0)); for (idx = 1 ; idx < ivl_stmt_parm_count(net) ; idx += 1) { fprintf(out, ", "); show_expression(ivl_stmt_parm(net, idx)); } fprintf(out, ");\n"); } break; case IVL_ST_WAIT: fprintf(out, "%*s@(...)\n", ind, ""); show_statement(ivl_stmt_sub_stmt(net), ind+2); break; case IVL_ST_WHILE: fprintf(out, "%*swhile (<?>)\n", ind, ""); show_statement(ivl_stmt_sub_stmt(net), ind+2); break; default: fprintf(out, "%*sunknown statement type (%d)\n", ind, "", code); } }
/* * Icarus translated <var> = repeat(<count>) <event> <value> into * begin * <tmp> = <value>; * repeat(<count>) <event>; * <var> = <tmp>; * end * This routine looks for this pattern and turns it back into the * appropriate blocking assignment. */ static unsigned is_repeat_event_assign(ivl_scope_t scope, ivl_statement_t stmt) { unsigned wid; ivl_statement_t assign, event, event_assign, repeat; ivl_lval_t lval; ivl_expr_t rval; ivl_signal_t lsig, rsig; /* We must have three block elements. */ if (ivl_stmt_block_count(stmt) != 3) 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 repeat with an event or an event. */ repeat = ivl_stmt_block_stmt(stmt, 1); if (ivl_statement_type(repeat) != IVL_ST_REPEAT) return 0; /* The repeat must have an event statement. */ event = ivl_stmt_sub_stmt(repeat); if (ivl_statement_type(event) != IVL_ST_WAIT) return 0; /* The third must be an assign. */ event_assign = ivl_stmt_block_stmt(stmt, 2); if (ivl_statement_type(event_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(event_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); /* The two signals must be the same. */ if (lsig != rsig) return 0; /* And finally the four statements must have the same line number * as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(repeat)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(event_assign))) { return 0; } /* The pattern matched so generate the appropriate code. */ fprintf(vlog_out, "%*c", get_indent(), ' '); wid = emit_stmt_lval(scope, event_assign); fprintf(vlog_out, " ="); if (repeat) { fprintf(vlog_out, " repeat ("); emit_expr(scope, ivl_stmt_cond_expr(repeat), 0); fprintf(vlog_out, ")"); } fprintf(vlog_out, " @("); emit_event(scope, event); fprintf(vlog_out, ") "); emit_expr(scope, ivl_stmt_rval(assign), wid); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); return 1; }
/* * Icarus translated <var> = <delay or event> <value> into * begin * <tmp> = <value>; * <delay or event> <var> = <tmp>; * end * This routine looks for this pattern and turns it back into the * appropriate blocking assignment. */ static unsigned is_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; /* 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); 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; /* 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); /* The two signals must be the same. */ if (lsig != rsig) return 0; /* And finally the three statements must have the same line number * as the block. */ if ((ivl_stmt_lineno(stmt) != ivl_stmt_lineno(assign)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delay)) || (ivl_stmt_lineno(stmt) != ivl_stmt_lineno(delayed_assign))) { return 0; } /* The pattern matched so generate the appropriate code. */ fprintf(vlog_out, "%*c", get_indent(), ' '); wid = emit_stmt_lval(scope, delayed_assign); 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, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); return 1; }