static void draw_ufunc_preamble(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); unsigned idx; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%alloc S_%p;\n", def); } /* evaluate the expressions and send the results to the function ports. */ assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_signal_t port = ivl_scope_port(def, idx+1); draw_function_argument(port, ivl_expr_parm(expr, idx)); } /* Call the function */ fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); fprintf(vvp_out, " %%join;\n"); }
static void draw_ufunc_preamble(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); unsigned idx; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%alloc S_%p;\n", def); } /* Evaluate the expressions and send the results to the function ports. Do this in two passes - evaluate, then send - this avoids the function input variables being overwritten if the same (non-automatic) function is called in one of the exressions. */ assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_signal_t port = ivl_scope_port(def, idx+1); draw_eval_function_argument(port, ivl_expr_parm(expr, idx)); } for (idx = ivl_expr_parms(expr) ; idx > 0 ; idx -= 1) { ivl_signal_t port = ivl_scope_port(def, idx); draw_send_function_argument(port); } /* Call the function */ fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); fprintf(vvp_out, " %%join;\n"); }
void draw_ufunc_real(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); unsigned idx; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%alloc S_%p;\n", def); } assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_signal_t port = ivl_scope_port(def, idx+1); draw_function_argument(port, ivl_expr_parm(expr, idx)); } /* Call the function */ fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); fprintf(vvp_out, " %%join;\n"); /* Return value signal cannot be an array. */ assert(ivl_signal_dimensions(retval) == 0); /* Load the result into a word. */ fprintf(vvp_out, " %%load/real v%p_0;\n", retval); /* If this is an automatic function, free the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%free S_%p;\n", def); } }
/* * Check to see if the statement R-value is a port in the given scope. * If it is return the zero based port number. */ static unsigned utask_out_port_idx(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports = ivl_scope_ports(scope); ivl_expr_t rval = ivl_stmt_rval(stmt); ivl_signal_t rsig = 0; ivl_expr_type_t expr_type = ivl_expr_type(rval); const char *sig_name; /* We can have a simple signal. */ if (expr_type == IVL_EX_SIGNAL) { rsig = ivl_expr_signal(rval); /* Or a simple select of a simple signal. */ } else if (expr_type == IVL_EX_SELECT) { ivl_expr_t expr = ivl_expr_oper1(rval); /* We must have a zero select base. */ if (ivl_expr_oper2(rval)) return ports; /* We must be selecting a signal. */ if (ivl_expr_type(expr) != IVL_EX_SIGNAL) return ports; rsig = ivl_expr_signal(expr); } else return ports; /* The R-value must have the same scope as the task. */ if (scope != ivl_signal_scope(rsig)) return ports; /* It must not be an array element. */ if (ivl_signal_dimensions(rsig)) return ports; /* It must be an output or inout port of the task. */ sig_name = ivl_signal_basename(rsig); for (idx = 0; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_OUTPUT) && (port_type != IVL_SIP_INOUT)) continue; if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break; } return idx; }
/* * Check to see if the statement L-value is a port in the given scope. * If it is return the zero based port number. */ static unsigned utask_in_port_idx(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports = ivl_scope_ports(scope); ivl_lval_t lval = ivl_stmt_lval(stmt, 0); ivl_signal_t lsig = ivl_lval_sig(lval); const char *sig_name; /* The L-value must be a single signal. */ if (ivl_stmt_lvals(stmt) != 1) return ports; /* It must not have an array select. */ if (ivl_lval_idx(lval)) return ports; /* It must not have a non-zero base. */ if (ivl_lval_part_off(lval)) return ports; /* It must not be part of the signal. */ if (ivl_lval_width(lval) != ivl_signal_width(lsig)) return ports; /* It must have the same scope as the task. */ if (scope != ivl_signal_scope(lsig)) return ports; /* It must be an input or inout port of the task. */ sig_name = ivl_signal_basename(lsig); for (idx = 0; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_INPUT) && (port_type != IVL_SIP_INOUT)) continue; if (strcmp(sig_name, ivl_signal_basename(port)) == 0) break; } return idx; }
static void draw_ufunc_preamble(ivl_expr_t expr) { ivl_scope_t def = ivl_expr_def(expr); unsigned idx; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%alloc S_%p;\n", def); } /* Evaluate the expressions and send the results to the function ports. Do this in two passes - evaluate, then send - this avoids the function input variables being overwritten if the same (non-automatic) function is called in one of the expressions. */ assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_signal_t port = ivl_scope_port(def, idx+1); draw_eval_function_argument(port, ivl_expr_parm(expr, idx)); } for (idx = ivl_expr_parms(expr) ; idx > 0 ; idx -= 1) { ivl_signal_t port = ivl_scope_port(def, idx); draw_send_function_argument(port); } /* Call the function */ switch (ivl_expr_value(expr)) { case IVL_VT_VOID: fprintf(vvp_out, " %%callf/void TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); break; case IVL_VT_REAL: fprintf(vvp_out, " %%callf/real TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); break; case IVL_VT_BOOL: case IVL_VT_LOGIC: fprintf(vvp_out, " %%callf/vec4 TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); break; case IVL_VT_STRING: fprintf(vvp_out, " %%callf/str TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); break; case IVL_VT_CLASS: case IVL_VT_DARRAY: case IVL_VT_QUEUE: fprintf(vvp_out, " %%callf/obj TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); break; default: fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); fprintf(vvp_out, " %%join;\n"); break; } }
/* * A user defined task call with arguments is generated as a block with * input assignments, a simple call and then output assignments. This is * handled by the is_utask_call_with_args() routine above. */ static void emit_stmt_utask(ivl_scope_t scope, ivl_statement_t stmt) { ivl_scope_t task_scope = ivl_stmt_call(stmt); assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); assert(ivl_scope_ports(task_scope) == 0); fprintf(vlog_out, "%*c", get_indent(), ' '); emit_scope_path(scope, task_scope); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); }
static void draw_lpm_ufunc(ivl_lpm_t net) { unsigned idx; ivl_scope_t def = ivl_lpm_define(net); const char*dly = draw_lpm_output_delay(net); fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly, vvp_mangle_id(ivl_scope_name(def)), ivl_lpm_width(net)); /* Print all the net signals that connect to the input of the function. */ for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) { fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net, idx))); } assert((ivl_lpm_size(net)+1) == ivl_scope_ports(def)); /* Now print all the variables in the function scope that receive the input values given in the previous list. */ for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 1) { ivl_signal_t psig = ivl_scope_port(def, idx+1); if (idx == 0) fprintf(vvp_out, " ("); else fprintf(vvp_out, ", "); assert(ivl_signal_dimensions(psig) == 0); fprintf(vvp_out, "v%p_0", psig); } fprintf(vvp_out, ")"); /* Now print the reference to the signal from which the result is collected. */ { ivl_signal_t psig = ivl_scope_port(def, 0); assert(ivl_lpm_width(net) == ivl_signal_width(psig)); assert(ivl_signal_dimensions(psig) == 0); fprintf(vvp_out, " v%p_0", psig); } /* Finally, print the scope identifier. */ fprintf(vvp_out, " S_%p;\n", def); }
struct vector_info draw_ufunc_expr(ivl_expr_t expr, unsigned wid) { unsigned idx; unsigned swid = ivl_expr_width(expr); ivl_scope_t def = ivl_expr_def(expr); ivl_signal_t retval = ivl_scope_port(def, 0); struct vector_info res; unsigned load_wid; /* If this is an automatic function, allocate the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%alloc S_%p;\n", def); } /* evaluate the expressions and send the results to the function ports. */ assert(ivl_expr_parms(expr) == (ivl_scope_ports(def)-1)); for (idx = 0 ; idx < ivl_expr_parms(expr) ; idx += 1) { ivl_signal_t port = ivl_scope_port(def, idx+1); draw_function_argument(port, ivl_expr_parm(expr, idx)); } /* Call the function */ fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); fprintf(vvp_out, ", S_%p;\n", def); fprintf(vvp_out, " %%join;\n"); /* Fresh basic block starts after the join. */ clear_expression_lookaside(); /* The return value is in a signal that has the name of the expression. Load that into the thread and return the vector result. */ res.base = allocate_vector(wid); res.wid = wid; if (res.base == 0) { fprintf(stderr, "%s:%u: vvp.tgt error: " "Unable to allocate %u thread bits for function result.\n", ivl_expr_file(expr), ivl_expr_lineno(expr), wid); vvp_errors += 1; return res; } assert(res.base != 0); load_wid = swid; if (load_wid > ivl_signal_width(retval)) load_wid = ivl_signal_width(retval); assert(ivl_signal_dimensions(retval) == 0); fprintf(vvp_out, " %%load/v %u, v%p_0, %u;\n", res.base, retval, load_wid); /* Pad the signal value with zeros. */ if (load_wid < wid) pad_expr_in_place(expr, res, swid); /* If this is an automatic function, free the local storage. */ if (ivl_scope_is_auto(def)) { fprintf(vvp_out, " %%free S_%p;\n", def); } return res; }
/* * Icarus encodes a user task call with arguments as: * begin * <input 1> = <arg> * ... * <input n> = <arg> * <task_call> * <arg> = <output 1> * ... * <arg> = <output n> * end * This routine looks for that pattern and translates it into the * appropriate task call. It returns true (1) if it successfully * translated the block to a task call, otherwise it returns false * (0) to indicate the block needs to be emitted. */ static unsigned is_utask_call_with_args(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports, task_idx = 0; unsigned count = ivl_stmt_block_count(stmt); unsigned lineno = ivl_stmt_lineno(stmt); ivl_scope_t task_scope = 0; port_expr_t port_exprs; /* Check to see if the block is of the basic form first. */ for (idx = 0; idx < count; idx += 1) { ivl_statement_t tmp = ivl_stmt_block_stmt(stmt, idx); if (ivl_statement_type(tmp) == IVL_ST_ASSIGN) continue; if (ivl_statement_type(tmp) == IVL_ST_UTASK && !task_scope) { task_idx = idx; task_scope = ivl_stmt_call(tmp); assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); continue; } return 0; } /* If there is no task call or it takes no argument then return. */ if (!task_scope) return 0; ports = ivl_scope_ports(task_scope); if (ports == 0) return 0; /* Allocate space to save the port information and initialize it. */ port_exprs = (port_expr_t) malloc(sizeof(struct port_expr_s)*ports); for (idx = 0; idx < ports; idx += 1) { port_exprs[idx].type = IVL_SIP_NONE; port_exprs[idx].expr.rval = 0; } /* Check that the input arguments are correct. */ for (idx = 0; idx < task_idx; idx += 1) { ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); unsigned port = utask_in_port_idx(task_scope, assign); if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { free(port_exprs); return 0; } port_exprs[port].type = IVL_SIP_INPUT; port_exprs[port].expr.rval = ivl_stmt_rval(assign); } /* Check that the output arguments are correct. */ for (idx = task_idx + 1; idx < count; idx += 1) { ivl_statement_t assign = ivl_stmt_block_stmt(stmt, idx); unsigned port = utask_out_port_idx(task_scope, assign); if ((port == ports) || (lineno != ivl_stmt_lineno(assign))) { free(port_exprs); return 0; } if (port_exprs[port].type == IVL_SIP_INPUT) { port_exprs[port].type = IVL_SIP_INOUT; // HERE: We probably should verify that the current R-value matches the // new L-value. } else { port_exprs[port].type = IVL_SIP_OUTPUT; } port_exprs[port].expr.lval = assign; } /* Check that the task call has the correct line number. */ if (lineno != ivl_stmt_lineno(ivl_stmt_block_stmt(stmt, task_idx))) { free(port_exprs); return 0; } /* Verify that all the ports were defined. */ for (idx = 0; idx < ports; idx += 1) { if (port_exprs[idx].type == IVL_SIP_NONE) { free(port_exprs); return 0; } } /* Now that we have the arguments figured out, print the task call. */ fprintf(vlog_out, "%*c", get_indent(), ' '); emit_scope_path(scope, task_scope); fprintf(vlog_out, "("); emit_port(scope, port_exprs[0]); for (idx = 1; idx < ports; idx += 1) { fprintf(vlog_out, ", "); emit_port(scope, port_exprs[idx]); } free(port_exprs); fprintf(vlog_out, ");"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); return 1; }