Add .ufunc/real functor to handle functions with return value on stack.
This commit is contained in:
parent
bfc9cd8715
commit
0c91a6b041
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
20
vvp/parse.y
20
vvp/parse.y
|
|
@ -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. */
|
||||
|
|
|
|||
55
vvp/ufunc.cc
55
vvp/ufunc.cc
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
21
vvp/ufunc.h
21
vvp/ufunc.h
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in New Issue