Add ability to read back return value / Add vec4 stacked user functions.
Also: - handle functions as arguments to system tasks. - Cleanup detect of signal as return value.
This commit is contained in:
parent
0c91a6b041
commit
e435a879fc
|
|
@ -191,9 +191,6 @@ void draw_ufunc_vec4(ivl_expr_t expr)
|
||||||
/* Take in arguments to function and call function code. */
|
/* Take in arguments to function and call function code. */
|
||||||
draw_ufunc_preamble(expr);
|
draw_ufunc_preamble(expr);
|
||||||
|
|
||||||
assert(ivl_signal_dimensions(retval) == 0);
|
|
||||||
fprintf(vvp_out, " %%load/vec4 v%p_0;\n", retval);
|
|
||||||
|
|
||||||
draw_ufunc_epilogue(expr);
|
draw_ufunc_epilogue(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,12 @@ static int get_vpi_taskfunc_signal_arg(struct args_info *result,
|
||||||
/* This should never happen since we have IVL_EX_SELECT. */
|
/* This should never happen since we have IVL_EX_SELECT. */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
} else if (signal_is_return_value(ivl_expr_signal(expr))) {
|
||||||
|
/* If the signal is the return value of a function,
|
||||||
|
then this can't be handled as a true signal, so
|
||||||
|
fall back on general expression processing. */
|
||||||
|
return 0;
|
||||||
|
|
||||||
} else if (ivl_expr_signed(expr) !=
|
} else if (ivl_expr_signed(expr) !=
|
||||||
ivl_signal_signed(ivl_expr_signal(expr))) {
|
ivl_signal_signed(ivl_expr_signal(expr))) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -1005,6 +1005,15 @@ static void draw_signal_vec4(ivl_expr_t expr)
|
||||||
{
|
{
|
||||||
ivl_signal_t sig = ivl_expr_signal(expr);
|
ivl_signal_t sig = ivl_expr_signal(expr);
|
||||||
|
|
||||||
|
/* Special Case: If the signal is the return value of the function,
|
||||||
|
then use a different opcode to get the value. */
|
||||||
|
if (signal_is_return_value(sig)) {
|
||||||
|
assert(ivl_signal_dimensions(sig) == 0);
|
||||||
|
fprintf(vvp_out, " %%retload/vec4 0; Load %s (draw_signal_vec4)\n",
|
||||||
|
ivl_signal_basename(sig));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle the simple case, a signal expression that is a
|
/* Handle the simple case, a signal expression that is a
|
||||||
simple vector, no array dimensions. */
|
simple vector, no array dimensions. */
|
||||||
if (ivl_signal_dimensions(sig) == 0) {
|
if (ivl_signal_dimensions(sig) == 0) {
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,17 @@ static void put_vec_to_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice,
|
||||||
ivl_signal_t sig = ivl_lval_sig(lval);
|
ivl_signal_t sig = ivl_lval_sig(lval);
|
||||||
int part_off_idx;
|
int part_off_idx;
|
||||||
|
|
||||||
|
|
||||||
|
/* Special Case: If the l-value signal is named after its scope,
|
||||||
|
and the scope is a function, then this is an assign to a return
|
||||||
|
value and should be handled differently. */
|
||||||
|
if (signal_is_return_value(sig)) {
|
||||||
|
assert(ivl_signal_dimensions(sig) == 0);
|
||||||
|
fprintf(vvp_out, " %%ret/vec4 0; Assign to %s\n",
|
||||||
|
ivl_signal_basename(sig));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* If the slice of the l-value is a BOOL variable, then cast
|
/* If the slice of the l-value is a BOOL variable, then cast
|
||||||
the data to a BOOL vector so that the stores can be valid. */
|
the data to a BOOL vector so that the stores can be valid. */
|
||||||
if (ivl_signal_data_type(sig) == IVL_VT_BOOL) {
|
if (ivl_signal_data_type(sig) == IVL_VT_BOOL) {
|
||||||
|
|
@ -345,6 +356,16 @@ static void store_vec4_to_lval(ivl_statement_t net)
|
||||||
if (lidx+1 < ivl_stmt_lvals(net))
|
if (lidx+1 < ivl_stmt_lvals(net))
|
||||||
fprintf(vvp_out, " %%split/vec4 %u;\n", lwid);
|
fprintf(vvp_out, " %%split/vec4 %u;\n", lwid);
|
||||||
|
|
||||||
|
/* Special Case: If the l-value signal is named after its scope,
|
||||||
|
and the scope is a function, then this is an assign to a return
|
||||||
|
value and should be handled differently. */
|
||||||
|
if (signal_is_return_value(lsig)) {
|
||||||
|
assert(ivl_signal_dimensions(lsig) == 0);
|
||||||
|
fprintf(vvp_out, " %%ret/vec4 0; Assign to %s (store_vec4_to_lval)\n",
|
||||||
|
ivl_signal_basename(lsig));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (word_ex) {
|
if (word_ex) {
|
||||||
/* Handle index into an array */
|
/* Handle index into an array */
|
||||||
int word_index = allocate_word();
|
int word_index = allocate_word();
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,10 @@ extern void draw_eval_expr_into_integer(ivl_expr_t expr, unsigned ix);
|
||||||
*/
|
*/
|
||||||
extern int draw_eval_condition(ivl_expr_t expr);
|
extern int draw_eval_condition(ivl_expr_t expr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return true if the signal is the return value of a function.
|
||||||
|
*/
|
||||||
|
extern int signal_is_return_value(ivl_signal_t sig);
|
||||||
|
|
||||||
extern int number_is_unknown(ivl_expr_t ex);
|
extern int number_is_unknown(ivl_expr_t ex);
|
||||||
extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok);
|
extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok);
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,16 @@ static unsigned is_netlist_signal(ivl_net_logic_t net, ivl_nexus_t nex)
|
||||||
return rtn;
|
return rtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int signal_is_return_value(ivl_signal_t sig)
|
||||||
|
{
|
||||||
|
ivl_scope_t sig_scope = ivl_signal_scope(sig);
|
||||||
|
if (ivl_scope_type(sig_scope) != IVL_SCT_FUNCTION)
|
||||||
|
return 0;
|
||||||
|
if (strcmp(ivl_signal_basename(sig), ivl_scope_basename(sig_scope)) == 0)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This tests a bufz device against an output receiver, and determines
|
* This tests a bufz device against an output receiver, and determines
|
||||||
* if the device can be skipped. If this function returns false, then a
|
* if the device can be skipped. If this function returns false, then a
|
||||||
|
|
@ -458,6 +468,13 @@ static void draw_reg_in_scope(ivl_signal_t sig)
|
||||||
ivl_signal_basename(sig), ivl_signal_scope(sig));
|
ivl_signal_basename(sig), ivl_signal_scope(sig));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ((ivl_signal_data_type(sig)==IVL_VT_LOGIC)
|
||||||
|
&& (ivl_scope_type(ivl_signal_scope(sig))==IVL_SCT_FUNCTION)
|
||||||
|
&& (strcmp(ivl_signal_basename(sig),ivl_scope_basename(ivl_signal_scope(sig))) == 0)) {
|
||||||
|
fprintf(vvp_out, "; Variable %s is vec4 return value of scope S_%p\n",
|
||||||
|
ivl_signal_basename(sig), ivl_signal_scope(sig));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const char *datatype_flag = ivl_signal_integer(sig) ? "/i" :
|
const char *datatype_flag = ivl_signal_integer(sig) ? "/i" :
|
||||||
ivl_signal_signed(sig)? "/s" : "";
|
ivl_signal_signed(sig)? "/s" : "";
|
||||||
|
|
|
||||||
|
|
@ -702,11 +702,14 @@ STRUCTURAL FUNCTION CALLS:
|
||||||
|
|
||||||
The .ufunc statements define a call to a user defined function.
|
The .ufunc statements define a call to a user defined function.
|
||||||
|
|
||||||
<label> .ufunc <flabel>, <wid>,
|
<label> .ufunc/real <flabel>, <wid>,
|
||||||
<isymbols> ( <psymbols> ) <rsymbol> <ssymbol>;
|
<isymbols> ( <psymbols> ) <ssymbol>;
|
||||||
|
|
||||||
|
<label> .ufunc/vec4 <flabel>, <wid>,
|
||||||
|
<isymbols> ( <psymbols> ) <ssymbol>;
|
||||||
|
|
||||||
<label> .ufunc/e <flabel>, <wid>, <trigger>,
|
<label> .ufunc/e <flabel>, <wid>, <trigger>,
|
||||||
<isymbols> ( <psymbols> ) <rsymbol> <ssymbol>;
|
<isymbols> ( <psymbols> ) <ssymbol>;
|
||||||
|
|
||||||
The first variant is used for functions that only need to be called
|
The first variant is used for functions that only need to be called
|
||||||
when one of their inputs changes value. The second variant is used
|
when one of their inputs changes value. The second variant is used
|
||||||
|
|
@ -729,10 +732,6 @@ list. The <psymbols> are variables that represent the input ports for
|
||||||
the function. The ufunc performs an assignment to these variables
|
the function. The ufunc performs an assignment to these variables
|
||||||
before calling the function.
|
before calling the function.
|
||||||
|
|
||||||
The <rsymbol> is the variable within the function where the result
|
|
||||||
will be found when the function code ends. This value is picked up
|
|
||||||
and propagated to the output of the functor.
|
|
||||||
|
|
||||||
The <ssymbol> is the function scope name.
|
The <ssymbol> is the function scope name.
|
||||||
|
|
||||||
THREAD STATEMENTS:
|
THREAD STATEMENTS:
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,9 @@ extern bool of_RELEASE_REG(vthread_t thr, vvp_code_t code);
|
||||||
extern bool of_RELEASE_WR(vthread_t thr, vvp_code_t code);
|
extern bool of_RELEASE_WR(vthread_t thr, vvp_code_t code);
|
||||||
extern bool of_REPLICATE(vthread_t thr, vvp_code_t code);
|
extern bool of_REPLICATE(vthread_t thr, vvp_code_t code);
|
||||||
extern bool of_RET_REAL(vthread_t thr, vvp_code_t code);
|
extern bool of_RET_REAL(vthread_t thr, vvp_code_t code);
|
||||||
|
extern bool of_RET_VEC4(vthread_t thr, vvp_code_t code);
|
||||||
|
extern bool of_RETLOAD_REAL(vthread_t thr, vvp_code_t code);
|
||||||
|
extern bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t code);
|
||||||
extern bool of_SCOPY(vthread_t thr, vvp_code_t code);
|
extern bool of_SCOPY(vthread_t thr, vvp_code_t code);
|
||||||
extern bool of_SET_DAR_OBJ_REAL(vthread_t thr, vvp_code_t code);
|
extern bool of_SET_DAR_OBJ_REAL(vthread_t thr, vvp_code_t code);
|
||||||
extern bool of_SET_DAR_OBJ_STR(vthread_t thr, vvp_code_t code);
|
extern bool of_SET_DAR_OBJ_STR(vthread_t thr, vvp_code_t code);
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,9 @@ static const struct opcode_table_s opcode_table[] = {
|
||||||
{ "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} },
|
{ "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} },
|
||||||
{ "%replicate", of_REPLICATE, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
|
{ "%replicate", of_REPLICATE, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
|
||||||
{ "%ret/real", of_RET_REAL, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
|
{ "%ret/real", of_RET_REAL, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
|
||||||
|
{ "%ret/vec4", of_RET_VEC4, 1,{OA_NUMBER, OA_NONE,OA_NONE} },
|
||||||
|
{ "%retload/real",of_RETLOAD_REAL,1,{OA_NUMBER, OA_NONE,OA_NONE} },
|
||||||
|
{ "%retload/vec4",of_RETLOAD_VEC4,1,{OA_NUMBER, OA_NONE,OA_NONE} },
|
||||||
{ "%scopy", of_SCOPY, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
{ "%scopy", of_SCOPY, 0, {OA_NONE, OA_NONE, OA_NONE} },
|
||||||
{ "%set/dar/obj/real",of_SET_DAR_OBJ_REAL,1,{OA_NUMBER,OA_NONE,OA_NONE} },
|
{ "%set/dar/obj/real",of_SET_DAR_OBJ_REAL,1,{OA_NUMBER,OA_NONE,OA_NONE} },
|
||||||
{ "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} },
|
{ "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} },
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@ static char* strdupnew(char const *str)
|
||||||
".tranif1" { return K_TRANIF1; }
|
".tranif1" { return K_TRANIF1; }
|
||||||
".tranvp" { return K_TRANVP; }
|
".tranvp" { return K_TRANVP; }
|
||||||
".ufunc/real" { return K_UFUNC_REAL; }
|
".ufunc/real" { return K_UFUNC_REAL; }
|
||||||
|
".ufunc/vec4" { return K_UFUNC_VEC4; }
|
||||||
".ufunc/e" { return K_UFUNC_E; }
|
".ufunc/e" { return K_UFUNC_E; }
|
||||||
".var" { return K_VAR; }
|
".var" { return K_VAR; }
|
||||||
".var/cobj" { return K_VAR_COBJECT; }
|
".var/cobj" { return K_VAR_COBJECT; }
|
||||||
|
|
|
||||||
|
|
@ -1016,6 +1016,11 @@ for example to store the return value for a real function, use
|
||||||
"%ret/real 0;". It is up to the function caller to set up the argument
|
"%ret/real 0;". It is up to the function caller to set up the argument
|
||||||
references.
|
references.
|
||||||
|
|
||||||
|
* %retload/vec4 <index>
|
||||||
|
|
||||||
|
Read a value from the indexed function argument. The value is read
|
||||||
|
from the argument and pushed to the appropriate stack.
|
||||||
|
|
||||||
* %set/dar/obj/real <index>
|
* %set/dar/obj/real <index>
|
||||||
* %set/dar/obj/str <index>
|
* %set/dar/obj/str <index>
|
||||||
* %set/dar/obj/vec4 <index>
|
* %set/dar/obj/vec4 <index>
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ static struct __vpiModPath*modpath_dst = 0;
|
||||||
%token K_RESOLV K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS
|
%token K_RESOLV K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS
|
||||||
%token K_SUBSTITUTE
|
%token K_SUBSTITUTE
|
||||||
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP
|
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP
|
||||||
%token K_UFUNC_REAL K_UFUNC_E K_UDP K_UDP_C K_UDP_S
|
%token K_UFUNC_REAL K_UFUNC_VEC4 K_UFUNC_E K_UDP K_UDP_C K_UDP_S
|
||||||
%token K_VAR K_VAR_COBJECT K_VAR_DARRAY
|
%token K_VAR K_VAR_COBJECT K_VAR_DARRAY
|
||||||
%token K_VAR_QUEUE
|
%token K_VAR_QUEUE
|
||||||
%token K_VAR_S K_VAR_STR K_VAR_I K_VAR_R K_VAR_2S K_VAR_2U
|
%token K_VAR_S K_VAR_STR K_VAR_I K_VAR_R K_VAR_2S K_VAR_2U
|
||||||
|
|
@ -257,6 +257,9 @@ statement
|
||||||
| T_LABEL K_UFUNC_REAL T_SYMBOL ',' T_NUMBER ',' symbols '(' symbols ')' T_SYMBOL ';'
|
| T_LABEL K_UFUNC_REAL T_SYMBOL ',' T_NUMBER ',' symbols '(' symbols ')' T_SYMBOL ';'
|
||||||
{ compile_ufunc_real($1, $3, $5, $7.cnt, $7.vect, $9.cnt, $9.vect, $11, 0); }
|
{ compile_ufunc_real($1, $3, $5, $7.cnt, $7.vect, $9.cnt, $9.vect, $11, 0); }
|
||||||
|
|
||||||
|
| T_LABEL K_UFUNC_VEC4 T_SYMBOL ',' T_NUMBER ',' symbols '(' symbols ')' T_SYMBOL ';'
|
||||||
|
{ compile_ufunc_vec4($1, $3, $5, $7.cnt, $7.vect, $9.cnt, $9.vect, $11, 0); }
|
||||||
|
|
||||||
| T_LABEL K_UFUNC_E T_SYMBOL ',' T_NUMBER ',' T_SYMBOL ',' symbols '(' symbols ')' T_SYMBOL ';'
|
| T_LABEL K_UFUNC_E T_SYMBOL ',' T_NUMBER ',' T_SYMBOL ',' symbols '(' symbols ')' T_SYMBOL ';'
|
||||||
{ compile_ufunc_vec4($1, $3, $5, $9.cnt, $9.vect, $11.cnt, $11.vect, $13, $7); }
|
{ compile_ufunc_vec4($1, $3, $5, $9.cnt, $9.vect, $11.cnt, $11.vect, $13, $7); }
|
||||||
|
|
||||||
|
|
|
||||||
124
vvp/ufunc.cc
124
vvp/ufunc.cc
|
|
@ -38,6 +38,30 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class ufunc_real : public ufunc_core {
|
||||||
|
public:
|
||||||
|
ufunc_real(unsigned ow, vvp_net_t*ptr,
|
||||||
|
unsigned nports, vvp_net_t**ports,
|
||||||
|
vvp_code_t start_address,
|
||||||
|
__vpiScope*call_scope,
|
||||||
|
char*scope_label);
|
||||||
|
~ufunc_real();
|
||||||
|
|
||||||
|
void finish_thread();
|
||||||
|
};
|
||||||
|
|
||||||
|
class ufunc_vec4 : public ufunc_core {
|
||||||
|
public:
|
||||||
|
ufunc_vec4(unsigned ow, vvp_net_t*ptr,
|
||||||
|
unsigned nports, vvp_net_t**ports,
|
||||||
|
vvp_code_t start_address,
|
||||||
|
__vpiScope*call_scope,
|
||||||
|
char*scope_label);
|
||||||
|
~ufunc_vec4();
|
||||||
|
|
||||||
|
void finish_thread();
|
||||||
|
};
|
||||||
|
|
||||||
ufunc_core::ufunc_core(unsigned owid, vvp_net_t*ptr,
|
ufunc_core::ufunc_core(unsigned owid, vvp_net_t*ptr,
|
||||||
unsigned nports, vvp_net_t**ports,
|
unsigned nports, vvp_net_t**ports,
|
||||||
vvp_code_t sa, __vpiScope*call_scope__,
|
vvp_code_t sa, __vpiScope*call_scope__,
|
||||||
|
|
@ -114,6 +138,18 @@ void ufunc_core::finish_thread_real_()
|
||||||
thread_ = 0;
|
thread_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ufunc_core::finish_thread_vec4_()
|
||||||
|
{
|
||||||
|
assert(thread_);
|
||||||
|
|
||||||
|
vvp_vector4_t val = vthread_get_vec4_stack(thread_, 0);
|
||||||
|
vthread_pop_vec4(thread_, 1);
|
||||||
|
|
||||||
|
propagate_vec4(val);
|
||||||
|
|
||||||
|
thread_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This method is only called when a trigger event occurs. Just arrange for
|
* This method is only called when a trigger event occurs. Just arrange for
|
||||||
* the function to be called.
|
* the function to be called.
|
||||||
|
|
@ -147,6 +183,24 @@ void ufunc_core::invoke_thread_()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ufunc_vec4::ufunc_vec4(unsigned ow, vvp_net_t*ptr,
|
||||||
|
unsigned nports, vvp_net_t**ports,
|
||||||
|
vvp_code_t start_address,
|
||||||
|
__vpiScope*call_scope,
|
||||||
|
char*scope_label)
|
||||||
|
: ufunc_core(ow, ptr, nports, ports, start_address, call_scope, scope_label)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ufunc_vec4::~ufunc_vec4()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ufunc_vec4::finish_thread()
|
||||||
|
{
|
||||||
|
finish_thread_vec4_();
|
||||||
|
}
|
||||||
|
|
||||||
ufunc_real::ufunc_real(unsigned ow, vvp_net_t*ptr,
|
ufunc_real::ufunc_real(unsigned ow, vvp_net_t*ptr,
|
||||||
unsigned nports, vvp_net_t**ports,
|
unsigned nports, vvp_net_t**ports,
|
||||||
vvp_code_t start_address,
|
vvp_code_t start_address,
|
||||||
|
|
@ -195,8 +249,8 @@ void compile_ufunc_real(char*label, char*code, unsigned wid,
|
||||||
last instruction is the usual %end. So the thread looks
|
last instruction is the usual %end. So the thread looks
|
||||||
like this:
|
like this:
|
||||||
|
|
||||||
%exec_ufunc <core>;
|
%exec_ufunc/real <core>;
|
||||||
%reap_ufunc <core>;
|
%reap_ufunc;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
The %exec_ufunc copies the input values into local regs
|
The %exec_ufunc copies the input values into local regs
|
||||||
|
|
@ -251,8 +305,70 @@ void compile_ufunc_vec4(char*label, char*code, unsigned wid,
|
||||||
unsigned portc, struct symb_s*portv,
|
unsigned portc, struct symb_s*portv,
|
||||||
char*scope_label, char*trigger_label)
|
char*scope_label, char*trigger_label)
|
||||||
{
|
{
|
||||||
// XXXX NOT IMPLEMENTED
|
/* The input argument list and port list must have the same
|
||||||
assert(0);
|
sizes, since internally we will be mapping the inputs list
|
||||||
|
to the ports list. */
|
||||||
|
assert(argc == portc);
|
||||||
|
|
||||||
|
__vpiScope*call_scope = vpip_peek_current_scope();
|
||||||
|
assert(call_scope);
|
||||||
|
|
||||||
|
/* Construct some phantom code that is the thread of the
|
||||||
|
function call. The first instruction, at the start_address
|
||||||
|
of the function, loads the ports and calls the function.
|
||||||
|
The second instruction collects the function result. The
|
||||||
|
last instruction is the usual %end. So the thread looks
|
||||||
|
like this:
|
||||||
|
|
||||||
|
%exec_ufunc/vec4 <core>;
|
||||||
|
%reap_ufunc;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
The %exec_ufunc copies the input values into local regs
|
||||||
|
and runs the function code. The %reap_ufunc then copies
|
||||||
|
the output value to the destination net functor. */
|
||||||
|
|
||||||
|
vvp_code_t exec_code = codespace_allocate();
|
||||||
|
exec_code->opcode = of_EXEC_UFUNC_VEC4;
|
||||||
|
code_label_lookup(exec_code, code, false);
|
||||||
|
|
||||||
|
vvp_code_t reap_code = codespace_allocate();
|
||||||
|
reap_code->opcode = of_REAP_UFUNC;
|
||||||
|
|
||||||
|
vvp_code_t end_code = codespace_allocate();
|
||||||
|
end_code->opcode = &of_END;
|
||||||
|
|
||||||
|
/* Run through the function ports (which are related to but
|
||||||
|
not the same as the input ports) and arrange for their
|
||||||
|
binding. */
|
||||||
|
vvp_net_t**ports = new vvp_net_t*[portc];
|
||||||
|
for (unsigned idx = 0 ; idx < portc ; idx += 1) {
|
||||||
|
functor_ref_lookup(&ports[idx], portv[idx].text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the output functor and attach it to the label. Tell
|
||||||
|
it about the start address of the code stub, and the scope
|
||||||
|
that will contain the execution. */
|
||||||
|
vvp_net_t*ptr = new vvp_net_t;
|
||||||
|
ufunc_core*fcore = new ufunc_vec4(wid, ptr, portc, ports,
|
||||||
|
exec_code, call_scope,
|
||||||
|
scope_label);
|
||||||
|
ptr->fun = fcore;
|
||||||
|
define_functor_symbol(label, ptr);
|
||||||
|
free(label);
|
||||||
|
|
||||||
|
exec_code->ufunc_core_ptr = fcore;
|
||||||
|
reap_code->ufunc_core_ptr = fcore;
|
||||||
|
|
||||||
|
wide_inputs_connect(fcore, argc, argv);
|
||||||
|
|
||||||
|
/* If this function has a trigger event, connect the functor to
|
||||||
|
that event. */
|
||||||
|
if (trigger_label)
|
||||||
|
input_connect(ptr, 0, trigger_label);
|
||||||
|
|
||||||
|
free(argv);
|
||||||
|
free(portv);
|
||||||
}
|
}
|
||||||
#ifdef CHECK_WITH_VALGRIND
|
#ifdef CHECK_WITH_VALGRIND
|
||||||
static map<ufunc_core*, bool> ufunc_map;
|
static map<ufunc_core*, bool> ufunc_map;
|
||||||
|
|
|
||||||
13
vvp/ufunc.h
13
vvp/ufunc.h
|
|
@ -70,6 +70,7 @@ class ufunc_core : public vvp_wide_fun_core {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void finish_thread_real_();
|
void finish_thread_real_();
|
||||||
|
void finish_thread_vec4_();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recv_vec4_from_inputs(unsigned port);
|
void recv_vec4_from_inputs(unsigned port);
|
||||||
|
|
@ -94,16 +95,4 @@ class ufunc_core : public vvp_wide_fun_core {
|
||||||
vvp_code_t code_;
|
vvp_code_t code_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ufunc_real : public ufunc_core {
|
|
||||||
public:
|
|
||||||
ufunc_real(unsigned ow, vvp_net_t*ptr,
|
|
||||||
unsigned nports, vvp_net_t**ports,
|
|
||||||
vvp_code_t start_address,
|
|
||||||
__vpiScope*call_scope,
|
|
||||||
char*scope_label);
|
|
||||||
~ufunc_real();
|
|
||||||
|
|
||||||
void finish_thread();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* IVL_ufunc_H */
|
#endif /* IVL_ufunc_H */
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ struct vthread_s {
|
||||||
// corresponding stack. This is how the %ret/* instructions
|
// corresponding stack. This is how the %ret/* instructions
|
||||||
// get at parent thread arguments.
|
// get at parent thread arguments.
|
||||||
vector<unsigned> args_real;
|
vector<unsigned> args_real;
|
||||||
|
vector<unsigned> args_vec4;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vector<vvp_vector4_t>stack_vec4_;
|
vector<vvp_vector4_t>stack_vec4_;
|
||||||
|
|
@ -143,6 +144,12 @@ struct vthread_s {
|
||||||
assert(use_index >= 1);
|
assert(use_index >= 1);
|
||||||
return stack_vec4_[use_index-1];
|
return stack_vec4_[use_index-1];
|
||||||
}
|
}
|
||||||
|
inline void poke_vec4(unsigned depth, const vvp_vector4_t&val)
|
||||||
|
{
|
||||||
|
assert(depth < stack_vec4_.size());
|
||||||
|
unsigned use_index = stack_vec4_.size()-1-depth;
|
||||||
|
stack_vec4_[use_index] = val;
|
||||||
|
}
|
||||||
inline void pop_vec4(unsigned cnt)
|
inline void pop_vec4(unsigned cnt)
|
||||||
{
|
{
|
||||||
while (cnt > 0) {
|
while (cnt > 0) {
|
||||||
|
|
@ -1348,9 +1355,13 @@ bool of_CALLF_STR(vthread_t thr, vvp_code_t cp)
|
||||||
bool of_CALLF_VEC4(vthread_t thr, vvp_code_t cp)
|
bool of_CALLF_VEC4(vthread_t thr, vvp_code_t cp)
|
||||||
{
|
{
|
||||||
vthread_t child = vthread_new(cp->cptr2, cp->scope);
|
vthread_t child = vthread_new(cp->cptr2, cp->scope);
|
||||||
return do_callf_void(thr, child);
|
|
||||||
|
|
||||||
// XXXX NOT IMPLEMENTED
|
// This is the return value. Push a place-holder value. The function
|
||||||
|
// will replace this with the actual value using a %ret/real instruction.
|
||||||
|
thr->push_vec4(vvp_vector4_t());
|
||||||
|
child->args_vec4.push_back(0);
|
||||||
|
|
||||||
|
return do_callf_void(thr, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool of_CALLF_VOID(vthread_t thr, vvp_code_t cp)
|
bool of_CALLF_VOID(vthread_t thr, vvp_code_t cp)
|
||||||
|
|
@ -4964,6 +4975,47 @@ bool of_RET_REAL(vthread_t thr, vvp_code_t cp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* %ret/vec4 <index>
|
||||||
|
*/
|
||||||
|
bool of_RET_VEC4(vthread_t thr, vvp_code_t cp)
|
||||||
|
{
|
||||||
|
size_t index = cp->number;
|
||||||
|
vvp_vector4_t val = thr->pop_vec4();
|
||||||
|
|
||||||
|
assert(index >= 0 && index < thr->args_vec4.size());
|
||||||
|
unsigned depth = thr->args_vec4[index];
|
||||||
|
// Use the depth to put the value into the stack of
|
||||||
|
// the parent thread.
|
||||||
|
thr->parent->poke_vec4(depth, val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* %retload/real <index>
|
||||||
|
*/
|
||||||
|
bool of_RETLOAD_REAL(vthread_t thr, vvp_code_t cp)
|
||||||
|
{
|
||||||
|
// NOT IMPLEMENTED
|
||||||
|
assert(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* %retload/vec4 <index>
|
||||||
|
*/
|
||||||
|
bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t cp)
|
||||||
|
{
|
||||||
|
size_t index = cp->number;
|
||||||
|
|
||||||
|
assert(index >= 0 && index < thr->args_vec4.size());
|
||||||
|
unsigned depth = thr->args_vec4[index];
|
||||||
|
// Use the depth to put the value into the stack of
|
||||||
|
// the parent thread.
|
||||||
|
thr->push_vec4(thr->parent->peek_vec4(depth));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool of_SCOPY(vthread_t thr, vvp_code_t)
|
bool of_SCOPY(vthread_t thr, vvp_code_t)
|
||||||
{
|
{
|
||||||
vvp_object_t tmp;
|
vvp_object_t tmp;
|
||||||
|
|
@ -5971,7 +6023,20 @@ bool of_EXEC_UFUNC_REAL(vthread_t thr, vvp_code_t cp)
|
||||||
/* Create a temporary thread and run it immediately. */
|
/* Create a temporary thread and run it immediately. */
|
||||||
vthread_t child = vthread_new(cp->cptr, child_scope);
|
vthread_t child = vthread_new(cp->cptr, child_scope);
|
||||||
thr->push_real(0.0);
|
thr->push_real(0.0);
|
||||||
child->args_real.push_back(0.0);
|
child->args_real.push_back(0);
|
||||||
|
|
||||||
|
return do_exec_ufunc(thr, cp, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool of_EXEC_UFUNC_VEC4(vthread_t thr, vvp_code_t cp)
|
||||||
|
{
|
||||||
|
__vpiScope*child_scope = cp->ufunc_core_ptr->func_scope();
|
||||||
|
assert(child_scope);
|
||||||
|
|
||||||
|
/* Create a temporary thread and run it immediately. */
|
||||||
|
vthread_t child = vthread_new(cp->cptr, child_scope);
|
||||||
|
thr->push_vec4(vvp_vector4_t());
|
||||||
|
child->args_vec4.push_back(0);
|
||||||
|
|
||||||
return do_exec_ufunc(thr, cp, child);
|
return do_exec_ufunc(thr, cp, child);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue