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));
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
connect to the inputs of the function. */
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)),
ivl_lpm_width(net), ivl_lpm_trigger(net));
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)),
ivl_lpm_width(net));
@ -1982,7 +1995,7 @@ static void draw_lpm_ufunc(ivl_lpm_t net)
}
fprintf(vvp_out, ")");
#if 0
/* Now print the reference to the signal from which the
result is collected. */
{ 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);
}
#endif
/* Finally, print the scope identifier. */
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_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_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.
*/
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 portc, struct symb_s*portv,
struct symb_s retv, char*scope_label,
char*trigger_label);
char*scope_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

View File

@ -208,7 +208,7 @@ static char* strdupnew(char const *str)
".tranif0" { return K_TRANIF0; }
".tranif1" { return K_TRANIF1; }
".tranvp" { return K_TRANVP; }
".ufunc" { return K_UFUNC; }
".ufunc/real" { return K_UFUNC_REAL; }
".ufunc/e" { return K_UFUNC_E; }
".var" { return K_VAR; }
".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_SUBSTITUTE
%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_QUEUE
%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 ';'
{ 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
bits in the symbols list change. */
| T_LABEL K_UFUNC T_SYMBOL ',' T_NUMBER ','
symbols '(' symbols ')' symbol T_SYMBOL ';'
{ compile_ufunc($1, $3, $5,
$7.cnt, $7.vect,
$9.cnt, $9.vect,
$11, $12, 0); }
| 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); }
| T_LABEL K_UFUNC_E T_SYMBOL ',' T_NUMBER ',' T_SYMBOL ','
symbols '(' symbols ')' symbol T_SYMBOL ';'
{ compile_ufunc($1, $3, $5,
$9.cnt, $9.vect,
$11.cnt, $11.vect,
$13, $14, $7); }
| 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); }
/* Resolver statements are very much like functors. They are
compiled to functors of a different mode. */

View File

@ -41,7 +41,7 @@
ufunc_core::ufunc_core(unsigned owid, vvp_net_t*ptr,
unsigned nports, vvp_net_t**ports,
vvp_code_t sa, __vpiScope*call_scope__,
char*result_label, char*scope_label)
char*scope_label)
: vvp_wide_fun_core(ptr, nports)
{
owid_ = owid;
@ -50,8 +50,6 @@ ufunc_core::ufunc_core(unsigned owid, vvp_net_t*ptr,
thread_ = 0;
call_scope_ = call_scope__;
functor_ref_lookup(&result_, result_label);
/* A __vpiScope starts with a __vpiHandle structure so this is
a safe cast. We need the (void*) to avoid a dereferenced
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
* of the functor, back into the netlist.
*/
void ufunc_core::finish_thread()
void ufunc_core::finish_thread_real_()
{
thread_ = 0;
if (vvp_fun_signal_real*sig = dynamic_cast<vvp_fun_signal_real*>(result_->fun))
propagate_real(sig->real_unfiltered_value());
assert(thread_);
if (vvp_fun_signal_vec*sig = dynamic_cast<vvp_fun_signal_vec*>(result_->fun))
propagate_vec4(sig->vec4_unfiltered_value());
double val = vthread_get_real_stack(thread_, 0);
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
* 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
* 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 portc, struct symb_s*portv,
struct symb_s retv, char*scope_label,
char*trigger_label)
char*scope_label, char*trigger_label)
{
/* The input argument list and port list must have the same
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. */
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);
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
that will contain the execution. */
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,
retv.text, scope_label);
scope_label);
ptr->fun = fcore;
define_functor_symbol(label, ptr);
free(label);
@ -229,6 +246,14 @@ void compile_ufunc(char*label, char*code, unsigned wid,
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
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,
vvp_code_t start_address,
__vpiScope*call_scope,
char*result_label,
char*scope_label);
~ufunc_core();
virtual ~ufunc_core() =0;
__vpiScope*call_scope() { return call_scope_; }
__vpiScope*func_scope() { return func_scope_; }
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,
vvp_context_t context);
protected:
void finish_thread_real_();
private:
void recv_vec4_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*func_scope_;
vvp_code_t code_;
};
// Where the result will be.
vvp_net_t*result_;
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 */

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
* 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();
assert(child_scope);
assert(child_scope->get_type_code() == vpiFunction);
assert(thr->children.empty());
/* We can take a number of shortcuts because we know that a
continuous assignment can only occur in a static scope. */
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->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
variables of the function. This copies all the values
atomically. */
cp->ufunc_core_ptr->assign_bits_to_ports(child_context);
/* 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->delay_delete = 1;
child->parent = thr;
thr->children.insert(child);
thr->i_am_joining = 1;
return false;
// This should be the only child
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);
}
/*