From 3e41a93f3e3b37c6edb38536eeb4535673b5d9d6 Mon Sep 17 00:00:00 2001 From: Martin Whitaker Date: Sat, 1 Mar 2014 23:25:35 +0000 Subject: [PATCH] Fix for br948 : user-func call may return incorrect value in a CA context. %exec_ufunc assumed that because a function can never block, a call to vthread_run() on the function code would only return when the final %end instruction had been executed. This is not true if the function contains a named block, which will be executed via a %fork instruction, allowing the main function thread to suspend after a %join instruction. The fix is to break %exec_ufunc into two instructions, the first setting the function inputs and executing the function code, the second collecting the function result. This provides the opportunity for the parent thread to suspend after the %exec_ufunc instruction until all its children have completed. --- vvp/codes.h | 1 + vvp/ufunc.cc | 26 ++++++++++++++++---------- vvp/vthread.cc | 27 ++++++++++++++++++++++----- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/vvp/codes.h b/vvp/codes.h index 3cab77057..dd6adf362 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -218,6 +218,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 94ded9a43..ca1d4da6b 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -5755,23 +5755,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; }