Add .ufunc/real functor to handle functions with return value on stack.

This commit is contained in:
Stephen Williams 2016-01-17 16:23:28 -08:00
parent bfc9cd8715
commit 0c91a6b041
8 changed files with 125 additions and 58 deletions

View File

@ -1942,6 +1942,19 @@ static void draw_lpm_ufunc(ivl_lpm_t net)
ivl_variable_type_t dt = data_type_of_nexus(ivl_lpm_q(net)); ivl_variable_type_t dt = data_type_of_nexus(ivl_lpm_q(net));
const char*dly = draw_lpm_output_delay(net, dt); const char*dly = draw_lpm_output_delay(net, dt);
const char*type_string = "";
switch (dt) {
case IVL_VT_REAL:
type_string = "/real";
break;
case IVL_VT_BOOL:
case IVL_VT_LOGIC:
type_string = "/vec4";
break;
default:
break;
}
/* Get all the input labels that I will use for net signals that /* Get all the input labels that I will use for net signals that
connect to the inputs of the function. */ connect to the inputs of the function. */
ninp = ivl_lpm_size(net); ninp = ivl_lpm_size(net);
@ -1954,7 +1967,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net)
vvp_mangle_id(ivl_scope_name(def)), vvp_mangle_id(ivl_scope_name(def)),
ivl_lpm_width(net), ivl_lpm_trigger(net)); ivl_lpm_width(net), ivl_lpm_trigger(net));
else else
fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly, fprintf(vvp_out, "L_%p%s .ufunc%s TD_%s, %u", net, dly, type_string,
vvp_mangle_id(ivl_scope_name(def)), vvp_mangle_id(ivl_scope_name(def)),
ivl_lpm_width(net)); ivl_lpm_width(net));
@ -1982,7 +1995,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net)
} }
fprintf(vvp_out, ")"); fprintf(vvp_out, ")");
#if 0
/* Now print the reference to the signal from which the /* Now print the reference to the signal from which the
result is collected. */ result is collected. */
{ ivl_signal_t psig = ivl_scope_port(def, 0); { ivl_signal_t psig = ivl_scope_port(def, 0);
@ -1991,7 +2004,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net)
fprintf(vvp_out, " v%p_0", psig); fprintf(vvp_out, " v%p_0", psig);
} }
#endif
/* Finally, print the scope identifier. */ /* Finally, print the scope identifier. */
fprintf(vvp_out, " S_%p;\n", def); fprintf(vvp_out, " S_%p;\n", def);
} }

View File

@ -248,7 +248,8 @@ extern bool of_XORR(vthread_t thr, vvp_code_t code);
extern bool of_ZOMBIE(vthread_t thr, vvp_code_t code); extern bool of_ZOMBIE(vthread_t thr, vvp_code_t code);
extern bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t code); extern bool of_EXEC_UFUNC_REAL(vthread_t thr, vvp_code_t code);
extern bool of_EXEC_UFUNC_VEC4(vthread_t thr, vvp_code_t code);
extern bool of_REAP_UFUNC(vthread_t thr, vvp_code_t code); extern bool of_REAP_UFUNC(vthread_t thr, vvp_code_t code);
extern bool of_CHUNK_LINK(vthread_t thr, vvp_code_t code); extern bool of_CHUNK_LINK(vthread_t thr, vvp_code_t code);

View File

@ -393,11 +393,14 @@ extern void compile_array_cleanup(void);
/* /*
* Compile the .ufunc statement. * Compile the .ufunc statement.
*/ */
extern void compile_ufunc(char*label, char*code, unsigned wid, extern void compile_ufunc_real(char*label, char*code, unsigned wid,
unsigned argc, struct symb_s*argv, unsigned argc, struct symb_s*argv,
unsigned portc, struct symb_s*portv, unsigned portc, struct symb_s*portv,
struct symb_s retv, char*scope_label, char*scope_label, char*trigger_label);
char*trigger_label); extern void compile_ufunc_vec4(char*label, char*code, unsigned wid,
unsigned argc, struct symb_s*argv,
unsigned portc, struct symb_s*portv,
char*scope_label, char*trigger_label);
/* /*
* The compile_event function takes the parts of the event statement * The compile_event function takes the parts of the event statement

View File

@ -208,7 +208,7 @@ static char* strdupnew(char const *str)
".tranif0" { return K_TRANIF0; } ".tranif0" { return K_TRANIF0; }
".tranif1" { return K_TRANIF1; } ".tranif1" { return K_TRANIF1; }
".tranvp" { return K_TRANVP; } ".tranvp" { return K_TRANVP; }
".ufunc" { return K_UFUNC; } ".ufunc/real" { return K_UFUNC_REAL; }
".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; }

View File

@ -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 K_UFUNC_E K_UDP K_UDP_C K_UDP_S %token K_UFUNC_REAL 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
@ -250,23 +250,15 @@ statement
| T_LABEL K_ARRAY T_STRING ',' T_SYMBOL ';' | T_LABEL K_ARRAY T_STRING ',' T_SYMBOL ';'
{ compile_array_alias($1, $3, $5); } { compile_array_alias($1, $3, $5); }
/* The .ufunc functor is for implementing user defined functions, or /* The .ufunc functors are for implementing user defined functions, or
other thread code that is automatically invoked if any of the other thread code that is automatically invoked if any of the
bits in the symbols list change. */ bits in the symbols list change. */
| T_LABEL K_UFUNC T_SYMBOL ',' T_NUMBER ',' | T_LABEL K_UFUNC_REAL T_SYMBOL ',' T_NUMBER ',' symbols '(' symbols ')' T_SYMBOL ';'
symbols '(' symbols ')' symbol T_SYMBOL ';' { compile_ufunc_real($1, $3, $5, $7.cnt, $7.vect, $9.cnt, $9.vect, $11, 0); }
{ compile_ufunc($1, $3, $5,
$7.cnt, $7.vect,
$9.cnt, $9.vect,
$11, $12, 0); }
| T_LABEL K_UFUNC_E T_SYMBOL ',' T_NUMBER ',' T_SYMBOL ',' | T_LABEL K_UFUNC_E T_SYMBOL ',' T_NUMBER ',' T_SYMBOL ',' symbols '(' symbols ')' T_SYMBOL ';'
symbols '(' symbols ')' symbol T_SYMBOL ';' { compile_ufunc_vec4($1, $3, $5, $9.cnt, $9.vect, $11.cnt, $11.vect, $13, $7); }
{ compile_ufunc($1, $3, $5,
$9.cnt, $9.vect,
$11.cnt, $11.vect,
$13, $14, $7); }
/* Resolver statements are very much like functors. They are /* Resolver statements are very much like functors. They are
compiled to functors of a different mode. */ compiled to functors of a different mode. */

View File

@ -41,7 +41,7 @@
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__,
char*result_label, char*scope_label) char*scope_label)
: vvp_wide_fun_core(ptr, nports) : vvp_wide_fun_core(ptr, nports)
{ {
owid_ = owid; owid_ = owid;
@ -50,8 +50,6 @@ ufunc_core::ufunc_core(unsigned owid, vvp_net_t*ptr,
thread_ = 0; thread_ = 0;
call_scope_ = call_scope__; call_scope_ = call_scope__;
functor_ref_lookup(&result_, result_label);
/* A __vpiScope starts with a __vpiHandle structure so this is /* A __vpiScope starts with a __vpiHandle structure so this is
a safe cast. We need the (void*) to avoid a dereferenced a safe cast. We need the (void*) to avoid a dereferenced
type punned pointer warning from some gcc compilers. */ type punned pointer warning from some gcc compilers. */
@ -104,14 +102,16 @@ void ufunc_core::assign_bits_to_ports(vvp_context_t context)
* result from the return code variable and deliver it to the output * result from the return code variable and deliver it to the output
* of the functor, back into the netlist. * of the functor, back into the netlist.
*/ */
void ufunc_core::finish_thread() void ufunc_core::finish_thread_real_()
{ {
thread_ = 0; assert(thread_);
if (vvp_fun_signal_real*sig = dynamic_cast<vvp_fun_signal_real*>(result_->fun))
propagate_real(sig->real_unfiltered_value());
if (vvp_fun_signal_vec*sig = dynamic_cast<vvp_fun_signal_vec*>(result_->fun)) double val = vthread_get_real_stack(thread_, 0);
propagate_vec4(sig->vec4_unfiltered_value()); vthread_pop_real(thread_, 1);
propagate_real(val);
thread_ = 0;
} }
/* /*
@ -147,6 +147,24 @@ void ufunc_core::invoke_thread_()
} }
} }
ufunc_real::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_core(ow, ptr, nports, ports, start_address, call_scope, scope_label)
{
}
ufunc_real::~ufunc_real()
{
}
void ufunc_real::finish_thread()
{
finish_thread_real_();
}
/* /*
* This function compiles the .ufunc statement that is discovered in * This function compiles the .ufunc statement that is discovered in
* the source file. Create all the functors and the thread, and * the source file. Create all the functors and the thread, and
@ -157,11 +175,10 @@ void ufunc_core::invoke_thread_()
* The portv list is a list of variables that the function reads as * The portv list is a list of variables that the function reads as
* inputs. The core assigns values to these nets as part of the startup. * inputs. The core assigns values to these nets as part of the startup.
*/ */
void compile_ufunc(char*label, char*code, unsigned wid, void compile_ufunc_real(char*label, char*code, unsigned wid,
unsigned argc, struct symb_s*argv, unsigned argc, struct symb_s*argv,
unsigned portc, struct symb_s*portv, unsigned portc, struct symb_s*portv,
struct symb_s retv, char*scope_label, char*scope_label, char*trigger_label)
char*trigger_label)
{ {
/* The input argument list and port list must have the same /* The input argument list and port list must have the same
sizes, since internally we will be mapping the inputs list sizes, since internally we will be mapping the inputs list
@ -187,7 +204,7 @@ void compile_ufunc(char*label, char*code, unsigned wid,
the output value to the destination net functor. */ the output value to the destination net functor. */
vvp_code_t exec_code = codespace_allocate(); vvp_code_t exec_code = codespace_allocate();
exec_code->opcode = of_EXEC_UFUNC; exec_code->opcode = of_EXEC_UFUNC_REAL;
code_label_lookup(exec_code, code, false); code_label_lookup(exec_code, code, false);
vvp_code_t reap_code = codespace_allocate(); vvp_code_t reap_code = codespace_allocate();
@ -208,9 +225,9 @@ void compile_ufunc(char*label, char*code, unsigned wid,
it about the start address of the code stub, and the scope it about the start address of the code stub, and the scope
that will contain the execution. */ that will contain the execution. */
vvp_net_t*ptr = new vvp_net_t; vvp_net_t*ptr = new vvp_net_t;
ufunc_core*fcore = new ufunc_core(wid, ptr, portc, ports, ufunc_core*fcore = new ufunc_real(wid, ptr, portc, ports,
exec_code, call_scope, exec_code, call_scope,
retv.text, scope_label); scope_label);
ptr->fun = fcore; ptr->fun = fcore;
define_functor_symbol(label, ptr); define_functor_symbol(label, ptr);
free(label); free(label);
@ -229,6 +246,14 @@ void compile_ufunc(char*label, char*code, unsigned wid,
free(portv); free(portv);
} }
void compile_ufunc_vec4(char*label, char*code, unsigned wid,
unsigned argc, struct symb_s*argv,
unsigned portc, struct symb_s*portv,
char*scope_label, char*trigger_label)
{
// XXXX NOT IMPLEMENTED
assert(0);
}
#ifdef CHECK_WITH_VALGRIND #ifdef CHECK_WITH_VALGRIND
static map<ufunc_core*, bool> ufunc_map; static map<ufunc_core*, bool> ufunc_map;

View File

@ -56,19 +56,21 @@ class ufunc_core : public vvp_wide_fun_core {
unsigned nports, vvp_net_t**ports, unsigned nports, vvp_net_t**ports,
vvp_code_t start_address, vvp_code_t start_address,
__vpiScope*call_scope, __vpiScope*call_scope,
char*result_label,
char*scope_label); char*scope_label);
~ufunc_core(); virtual ~ufunc_core() =0;
__vpiScope*call_scope() { return call_scope_; } __vpiScope*call_scope() { return call_scope_; }
__vpiScope*func_scope() { return func_scope_; } __vpiScope*func_scope() { return func_scope_; }
void assign_bits_to_ports(vvp_context_t context); void assign_bits_to_ports(vvp_context_t context);
void finish_thread(); virtual void finish_thread() =0;
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t context); vvp_context_t context);
protected:
void finish_thread_real_();
private: private:
void recv_vec4_from_inputs(unsigned port); void recv_vec4_from_inputs(unsigned port);
void recv_real_from_inputs(unsigned port); void recv_real_from_inputs(unsigned port);
@ -90,9 +92,18 @@ class ufunc_core : public vvp_wide_fun_core {
__vpiScope*call_scope_; __vpiScope*call_scope_;
__vpiScope*func_scope_; __vpiScope*func_scope_;
vvp_code_t code_; vvp_code_t code_;
};
// Where the result will be. class ufunc_real : public ufunc_core {
vvp_net_t*result_; 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 */

View File

@ -5911,13 +5911,15 @@ bool of_ZOMBIE(vthread_t thr, vvp_code_t)
* a ufunc_core object that has all the port information about the * a ufunc_core object that has all the port information about the
* function. * function.
*/ */
bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t cp) static bool do_exec_ufunc(vthread_t thr, vvp_code_t cp, vthread_t child)
{ {
__vpiScope*child_scope = cp->ufunc_core_ptr->func_scope(); __vpiScope*child_scope = cp->ufunc_core_ptr->func_scope();
assert(child_scope); assert(child_scope);
assert(child_scope->get_type_code() == vpiFunction);
assert(thr->children.empty()); assert(thr->children.empty());
/* We can take a number of shortcuts because we know that a /* We can take a number of shortcuts because we know that a
continuous assignment can only occur in a static scope. */ continuous assignment can only occur in a static scope. */
assert(thr->wt_context == 0); assert(thr->wt_context == 0);
@ -5930,28 +5932,48 @@ bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t cp)
thr->wt_context = child_context; thr->wt_context = child_context;
thr->rd_context = child_context; thr->rd_context = child_context;
} }
child->wt_context = child_context;
child->rd_context = child_context;
/* Copy all the inputs to the ufunc object to the port /* Copy all the inputs to the ufunc object to the port
variables of the function. This copies all the values variables of the function. This copies all the values
atomically. */ atomically. */
cp->ufunc_core_ptr->assign_bits_to_ports(child_context); cp->ufunc_core_ptr->assign_bits_to_ports(child_context);
child->delay_delete = 1;
/* Create a temporary thread and run it immediately. */
vthread_t child = vthread_new(cp->cptr, child_scope);
child->wt_context = child_context;
child->rd_context = child_context;
child->is_scheduled = 1;
child->delay_delete = 1;
vthread_run(child);
running_thread = thr;
if (child->i_have_ended)
return true;
child->parent = thr; child->parent = thr;
thr->children.insert(child); thr->children.insert(child);
thr->i_am_joining = 1; // This should be the only child
return false; assert(thr->children.size()==1);
child->is_scheduled = 1;
child->i_am_in_function = 1;
vthread_run(child);
running_thread = thr;
assert(test_joinable(thr, child));
if (child->i_have_ended) {
do_join(thr, child);
return true;
} else {
thr->i_am_joining = 1;
return false;
}
}
bool of_EXEC_UFUNC_REAL(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_real(0.0);
child->args_real.push_back(0.0);
return do_exec_ufunc(thr, cp, child);
} }
/* /*