diff --git a/vvp/codes.h b/vvp/codes.h index d02a2711a..e646f9f07 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -242,6 +242,7 @@ 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_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/ufunc.cc b/vvp/ufunc.cc index b89a10780..7ac1f00ba 100644 --- a/vvp/ufunc.cc +++ b/vvp/ufunc.cc @@ -173,20 +173,25 @@ void compile_ufunc(char*label, char*code, unsigned wid, /* Construct some phantom code that is the thread of the function call. The first instruction, at the start_address - of the function, loads the points and calls the function. - The last instruction is the usual %end. So the thread looks + 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 ; + %reap_ufunc ; %end; - The %exec_ufunc copies the input values into local regs, - runs the function code, then copies the output values to - the destination net functors. */ + 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 start_code = codespace_allocate(); - start_code->opcode = of_EXEC_UFUNC; - code_label_lookup(start_code, code); + vvp_code_t exec_code = codespace_allocate(); + exec_code->opcode = of_EXEC_UFUNC; + code_label_lookup(exec_code, code); + + 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; @@ -204,13 +209,14 @@ void compile_ufunc(char*label, char*code, unsigned wid, that will contain the execution. */ vvp_net_t*ptr = new vvp_net_t; ufunc_core*fcore = new ufunc_core(wid, ptr, portc, ports, - start_code, call_scope, + exec_code, call_scope, retv.text, scope_label); ptr->fun = fcore; define_functor_symbol(label, ptr); free(label); - start_code->ufunc_core_ptr = fcore; + exec_code->ufunc_core_ptr = fcore; + reap_code->ufunc_core_ptr = fcore; wide_inputs_connect(fcore, argc, argv); diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 6c304944f..f2b4c5397 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -6468,23 +6468,40 @@ bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t cp) atomically. */ cp->ufunc_core_ptr->assign_bits_to_ports(child_context); - /* Create a temporary thread and run it immediately. A function - may not contain any blocking statements, so vthread_run() can - only return when the %end opcode is reached. */ + /* 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->parent = thr; child->is_scheduled = 1; vthread_run(child); running_thread = thr; - /* Now copy the output from the result variable to the output + if (child->i_have_ended) + return true; + + thr->children.insert(child); + thr->i_am_joining = 1; + return false; +} + +/* + * This is a phantom opcode used to harvest the result of calling a user + * defined function. It is used in code generated by the .ufunc statement. + */ +bool of_REAP_UFUNC(vthread_t thr, vvp_code_t cp) +{ + struct __vpiScope*child_scope = cp->ufunc_core_ptr->func_scope(); + assert(child_scope); + + /* Copy the output from the result variable to the output ports of the .ufunc device. */ cp->ufunc_core_ptr->finish_thread(); /* If an automatic function, free the context for this call. */ if (child_scope->is_automatic) { - vthread_free_context(child_context, child_scope); + vthread_free_context(thr->rd_context, child_scope); thr->wt_context = 0; thr->rd_context = 0; }