diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 57459498f..503682806 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -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); } diff --git a/vvp/codes.h b/vvp/codes.h index 8af97689b..fed8a6737 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -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); diff --git a/vvp/compile.h b/vvp/compile.h index 8d4d1ee82..627eff35d 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -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 diff --git a/vvp/lexor.lex b/vvp/lexor.lex index 2b2a81f0d..6ce4af9fb 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -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; } diff --git a/vvp/parse.y b/vvp/parse.y index d54dac6c3..d91b7b3b8 100644 --- a/vvp/parse.y +++ b/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. */ diff --git a/vvp/ufunc.cc b/vvp/ufunc.cc index f15f038bf..d4e5f6779 100644 --- a/vvp/ufunc.cc +++ b/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(result_->fun)) - propagate_real(sig->real_unfiltered_value()); + assert(thread_); - if (vvp_fun_signal_vec*sig = dynamic_cast(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_map; diff --git a/vvp/ufunc.h b/vvp/ufunc.h index f267c629a..437bf1d8b 100644 --- a/vvp/ufunc.h +++ b/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 */ diff --git a/vvp/vthread.cc b/vvp/vthread.cc index df0714295..473e48ae8 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -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); } /*