/* * Copyright (c) 2002-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU * General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ # include "compile.h" # include "symbols.h" # include "codes.h" # include "ufunc.h" # include "vthread.h" # include "schedule.h" #ifdef HAVE_MALLOC_H # include #endif # include # include # include # include #ifdef __MINGW32__ #include #endif ufunc_core::ufunc_core(unsigned owid, vvp_net_t*ptr, unsigned nports, vvp_net_t**ports, vvp_code_t sa, struct __vpiScope*run_scope, char*result_label) : vvp_wide_fun_core(ptr, nports) { owid_ = owid; ports_ = ports; code_ = sa; thread_ = 0; scope_ = run_scope; functor_ref_lookup(&result_, result_label); } ufunc_core::~ufunc_core() { } /* * This method is called by the %fork_ufunc function to prepare the * input variables of the function for execution. The method copies * the input values collected by the core to the variables. */ void ufunc_core::assign_bits_to_ports(void) { for (unsigned idx = 0 ; idx < port_count() ; idx += 1) { vvp_net_t*net = ports_[idx]; vvp_net_ptr_t pp (net, 0); if (vvp_fun_signal_real*tmp = dynamic_cast(net->fun)) tmp->recv_real(pp, value_r(idx)); if (vvp_fun_signal_vec*tmp = dynamic_cast(net->fun)) tmp->recv_vec4(pp, value(idx)); } } /* * This method is called by the %join_ufunc instruction to copy the * result from the return code variable and deliver it to the output * of the functor, back into the netlist. */ void ufunc_core::finish_thread(vthread_t thr) { thread_ = 0; if (vvp_fun_signal_real*sig = dynamic_cast(result_->fun)) propagate_real(sig->real_value()); if (vvp_fun_signal_vec*sig = dynamic_cast(result_->fun)) propagate_vec4(sig->vec4_value()); } /* * The recv_vec4 methods of the input functors call this to assign the * input value to the port of the functor. I save the input value and * arrange for the function to be called. */ void ufunc_core::recv_vec4_from_inputs(unsigned port) { invoke_thread_(); } void ufunc_core::recv_real_from_inputs(unsigned port) { invoke_thread_(); } void ufunc_core::invoke_thread_() { if (thread_ == 0) { thread_ = vthread_new(code_, scope_); schedule_vthread(thread_, 0); } } /* * This function compiles the .ufunc statement that is discovered in * the source file. Create all the functors and the thread, and * connect them all up. * * The argv list is a list of the inputs to the function. * * 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, unsigned argc, struct symb_s*argv, unsigned portc, struct symb_s*portv, struct symb_s retv) { /* The input argument list and port list must have the same sizes, since internally we will be mapping the inputs list to the ports list. */ assert(argc == portc); struct __vpiScope*run_scope = vpip_peek_current_scope(); assert(run_scope); /* 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 like this: %fork_ufunc ; %join; %join_ufunc; %end; The %fork_ufunc starts the user defined function by copying the input values into local regs, forking a thread and pushing that thread. The %join waits on that thread. The $join_ufunc then copies the output values to the destination net functors. */ vvp_code_t start_code = codespace_allocate(); start_code->opcode = of_FORK_UFUNC; code_label_lookup(start_code, code); { vvp_code_t codep = codespace_allocate(); codep->opcode = &of_JOIN; } vvp_code_t ujoin_code; ujoin_code = codespace_allocate(); ujoin_code->opcode = &of_JOIN_UFUNC; { vvp_code_t codep = codespace_allocate(); codep->opcode = &of_END; } /* Run through the function ports (which are related to but not the same as the input ports) and arrange for their binding. */ vvp_net_t**ports = new vvp_net_t*[portc]; for (unsigned idx = 0 ; idx < portc ; idx += 1) { functor_ref_lookup(&ports[idx], portv[idx].text); } /* Create the output functor and attach it to the label. Tell 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, start_code, run_scope, retv.text); ptr->fun = fcore; define_functor_symbol(label, ptr); free(label); start_code->ufunc_core_ptr = fcore; ujoin_code->ufunc_core_ptr = fcore; wide_inputs_connect(fcore, argc, argv); free(argv); free(portv); }