/*
 * 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  <malloc.h>
#endif
# include  <stdlib.h>
# include  <string.h>
# include  <iostream>
# include  <assert.h>

#ifdef __MINGW32__
#include <windows.h>
#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<vvp_fun_signal_real*>(net->fun))
		  tmp->recv_real(pp, value_r(idx));
	    if (vvp_fun_signal_vec*tmp = dynamic_cast<vvp_fun_signal_vec*>(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<vvp_fun_signal_real*>(result_->fun))

	    propagate_real(sig->real_value());

      if (vvp_fun_signal_vec*sig = dynamic_cast<vvp_fun_signal_vec*>(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 <core>;
	      %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);
}
