/* $Id$
 * Interpreter library. External API
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */


#ifndef __FAUHDLI_H_INCLUDED
#define __FAUHDLI_H_INCLUDED

#include <stdbool.h>
#include <basetypes.h>
#include <stdlib.h>

/** forward declaration of struct fauhdli, which is opaque to external 
 *  users. */
struct fauhdli;

/** any directly used VHDL value. */
union fauhdli_value {
	/** integer value */
	universal_integer univ_int;
	/** real value */
	universal_real univ_real;
	/** pointer value */
	void *pointer;
};

/** Callbacks for foreign interface. */
struct glue_vhdl_cb {
	/* --------------- memory management ------------ */

	/** allocate memory (optional, fallback: malloc).
	 *  @param size number of bytes to allocate. 
	 *  @return pointer to allocated memory (or NULL on error).
	 */
	void *(*malloc)(size_t size);

	/** free allocated memory again (optional, fallback: free).
	 *  @param ptr pointer to allocated chunk.
	 */
	void (*free)(void *ptr);

	/* ------------------ scheduling --------------- */

	/* for the scheduling callbacks, either all must be set, or 
	 * None may be set, resulting in the builtin implementation.
	 */

	/** Get virtual time.
	 *  @return virtual time. */
	unsigned long long (*time_virt)(void);

	/** Register a timer to call func with parameter data as soon as the
	 *  simulation time tsc is reached.
	 *  @param tsc virtual timestamp counter, 1 second == 1 * TIME_HZ.
	 *  @param func callback that will be called.
	 *  @param data parameter to the callback.
	 */
	void (*time_call_at)(
				unsigned long long tsc,
				void (*func)(void *data), 
				void *data);

	/** Delete a registered timer from time_call_at.
	 *  @param func callback that would have been called.
	 *  @param data data that would have been the argument (must match
	 *         data from time_call_at/time_call_after).
	 *  @return 0 for success, 1 if the timer was not found.
	 */
	int (*time_call_delete)(void (*func)(void *), void *data);

	/** Tell the scheduler to quit the simulation.
	 *  @param status: 0=success, others=error.
	 */
	void (*quit)(int status);

	/* ------------------- logging ----------------- */
	/** Log an interpreter error message. Optional, fallback: builtin
	 *  implementation.
	 *  @param level severity of error (0=fatal, 1=critical, 2=error,
	 *         3=warning, 4=info, 5=debug)
	 *  @param type submodule from which the error comes.
	 *  @param name name of the module/function that throws the error.
	 *  @param fmt format string.
	 */
	void (*log)(
				int level,
				const char *type, 
				const char *name, 
				const char *fmt, 
				...);

	/** Create a foreign component (entity).
	 *  @param _cpssp opaque pointer.
	 *  @param type type of the component (entity name).
	 *  @param name name of the instantiated unit
	 *  @return unique component id.
	 */
	unsigned int (*comp_create)(
				void *_cpssp, 
				const char *type, 
				const char *name);

	/** Initialize a foreign component.
	 *  The foreign component must first have been created with
	 *  comp_create and the ports must have been connected
	 *  with comp_port_connect.
	 *
	 *  @param _cpssp opaque pointer.
	 *  @param comp component id (cf. glue_vhdl_comp_create).
	 */
	void (*comp_init)(void *_cpssp, unsigned int comp_id);

	/*  Create a foreign signal and return its ID.
	 *  @param _cpssp opaque pointer.
	 *  @param type type of the signal.
	 *  @param name signal name (for debugging only)
	 *  @return unique signal id.
	 */
	unsigned int (*signal_create)(
				void *_cpssp, 
				const char *type, 
				const char *name);


	/** connect a foreign signal to the port of a foreign component.
	 *  @param _cpssp opaque pointer.
	 *  @param comp unique component id (cf. comp_create, return value)
	 *  @param port name of the desired port.
	 *  @param sig unique signal id (cf. signal_create, return value)
	 */ 
	void (*comp_port_connect)(
				void *_cpssp, 
				unsigned int comp, 
				const char *port,
				unsigned int sig);


	/** Create a foreign architecture. A foreign architecture means, that
	 *  it is really declared in VHDL, but could e.g. instantiate only 
	 *  foreign entities. This is mainly a callback before the VHDL
	 *  architecture gets created (but after the signals for the 
	 *  architecture have been created), in case of need.
	 *
	 *  @param _cpssp opaque pointer.
	 *  @param type type of the component (entity name).
	 *  @param name name of the instantiated architecture
	 *  @return unique architecture id.
	 */
	unsigned int (*arch_create)(
				void *_cpssp,
				const char *type,
				const char *name);

	/** Initialize a foreign architecture.
	 *  The foreign architecture must first have been created with
	 *  glue_vhdl_arch_create and the ports must have been connected
	 *  with glue_vhdl_arch_port_connect (same with generics).
	 *
	 *  @param _cpssp opaque pointer.
	 *  @param arch architecture id (cf. arch_create).
	 */
	void (*arch_init)(void *_cpssp, unsigned int arch);

	/** connect a foreign signal to the port of a foreign component.
	 *  @param _cpssp opaque pointer
	 *  @param arch unique architecture id (cf. glue_vhdl_arch_create, 
	 *  	   return value)
	 *  @param port name of the desired port.
	 *  @param sig unique signal id (cf. glue_vhdl_create_signal, return 
	 *         value)
	 */ 
	void (*arch_port_connect)(
				void *_cpssp,
				unsigned int arch, 
				const char *port, 
				unsigned int sig);

	/** Set the value of a foreign driver.
	 *  @param _cpssp opaque pointer.
	 *  @param sig_id signal id.
	 *  @param data data fitting to the signal. (FIXME composites).
	 *  @param drv pointer to VHDL driver (cf. connect_out, this 
	 *         pointer can be used to identify a driver. It mustn't
	 *         be modified nor may any assumptions be made about the
	 *         data stored at the driver.)
	 */
	void (*drv_set)(
				void *_cpssp, 
				unsigned int sig_id, 
				union fauhdli_value data,
				void *drv);

	/** Connect a VHDL signal to a foreign signal so that writes are 
	 *  getting forwarded to the foreign interface.
	 *  This function gets called if a VHDL driver is connected to a 
	 *  foreign VHDL signal.
	 *  @param _cpssp opaque pointer.
	 *  @param sig_id foreign signal id.
	 *  @param init initial driving value.
	 *  @param drv corresponding driver pointer.
	 */
	void (*connect_out)(
				void *_cpssp,
				unsigned int sig_id,
				union fauhdli_value init,
				void *drv);

	/** Connect a foreign signal that is read from VHDL.
	 *  Register a foreign signal, that is read from VHDL. Whenever the
	 *  foreign signal recieves a new value, it should call
	 *  fauhdli_foreign_drv_update.
	 *  @param _cpssp opaque pointer.
	 *  @param sig_id unique foreign signal id.
	 *  @param drv driver instance that will be passed as _drv parameter 
	 *  	   for fauhdli_foreign_drv_update.
	 */
	void (*connect_in)(
				void *_cpssp,
				unsigned int sig_id,
				void *drv);

	/** set a generic of a foreign component (which is not an array).
	 *  @param _cpssp opaque pointer.
	 *  @param comp unique component id (cf. comp_create, return value)
	 *  @param generic name of the desired generic.
	 *  @param val VHDL value (direct value for non-composites, 
	 *  	   pointer for record types).
	 *  @param type name of the corresponding VHDL type.
	 */
	void (*comp_generic_nonarray_set)(
				void *_cpssp,
				unsigned int comp,
				const char *generic,
				union fauhdli_value val,
				const char *type);

	/** set a generic of a foreign component (which is an array).
	 *  @param _cpssp opaque pointer.
	 *  @param comp unique component id (cf. glue_vhdl_comp_create,
	 *  	   return value)
	 *  @param generic name of the desired generic.
	 *  @param element_type type name of the element type
	 *  @param base pointer to base of array.
	 *  @param array_size size of array.
	 */
	void (*comp_generic_array_set)(
				void *_cpssp,
				unsigned int comp,
				const char *generic,
				const char *element_type,
				union fauhdli_value base,
				universal_integer array_size);


	/** set a generic of a foreign architecture (which is not an array).
	 *  @param _cpssp opaque pointer.
	 *  @param comp unique component id (cf. glue_vhdl_comp_create, 
	 *  	   return value)
	 *  @param generic name of the desired generic.
	 *  @param val VHDL value (direct value for non-composites, pointer 
	 *  	   for record types).
	 *  @param type name of the corresponding VHDL type.
	 */
	void (*arch_generic_nonarray_set)(
				void *_cpssp,
				unsigned int arch,
				const char *generic,
				union fauhdli_value val,
				const char *type);

	/** set a generic of a foreign architecture (which is an array).
	 *  @param _cpssp opaque pointer.
	 *  @param arch unique architecture id (cf. arch_create, 
	 *  	   return value)
	 *  @param generic name of the desired generic.
	 *  @param element_type type name of the element type
	 *  @param base pointer to base of array.
	 *  @param array_size size of array.
	 */
	void (*arch_generic_array_set)(
				void *_cpssp,
				unsigned int arch,
				const char *generic,
				const char *element_type,
				union fauhdli_value base,
				universal_integer array_size);

	/** Set a procedure call argument of a foreign procedure.
	 *  @param _cpssp opaque pointer.
	 *  @param proc name of the foreign procedure.
	 *  @param param name of the parameter
	 *  @param value parameter value. Content depending on the signature
	 *         of the foreign procedure.
	 *         For parameter passing mechanisms, see GenCode.cpp.
	 */
	void (*proc_set)(
				void *_cpssp,
				const char *proc,
				const char *param, 
				union fauhdli_value value);

	/** Call a foreign procedure.
	 *  @param _cpssp opaque pointer.
	 *  @param proc name of the procedure to call.
	 */
	void (*proc_call)(void *_cpssp, const char *proc);
};


/** create the VHDL interpreter.
 *  @param parse_file intermediate code file to parse.
 *  @param trace_file name of vcd trace file (to trace signals of top_entity)
 *         or NULL, if no output is desired.
 *  @param debug debugging output enabled/disabled?
 *  @param callbacks callbacks for foreign interface.
 *  @param _cpssp opaque pointer that will get passed back to the callbacks.
 *  @return fauhdli instance.
 */
extern
__attribute__((visibility("default")))
struct fauhdli *
fauhdli_create(
	const char *parse_file, 
	const char *trace_file, 
	bool debug,
	const struct glue_vhdl_cb *callbacks,
	void *_cpssp
);

/** destroy the fauhdli instance.
 *  @param instance fauhdli instance.
 */
extern void
__attribute__((visibility("default")))
fauhdli_destroy(struct fauhdli *instance);

/** initialize/run simulator
 *  @param instance simulator instance.
 *  @param top_entity start simulation with entity called like this.
 */
extern void
__attribute__((visibility("default")))
fauhdli_init(struct fauhdli *instance, const char *top_entity);


/** find out the foreign signal id for a signal
 *  @param _sig pointer to signal instance.
 *  @return foreign signal id
 */
extern
__attribute__((visibility("default")))
unsigned int
fauhdli_get_sig_id(const void *_sig);

/** find out the foreign signal id for a driver
 *  @param _drv pointer to driver instance.
 *  @return foreign signal id
 */
extern
__attribute__((visibility("default")))
unsigned int
fauhdli_get_sig_id_driver(const void *_drv);

/** Update the value of a driver.
 *  @param instance fauhdli instance.
 *  @param _drv pointer to the VHDL driver instance.
 *  @param value updated value.
 */
extern void
__attribute__((visibility("default")))
fauhdli_set_driver(
	struct fauhdli *instance, 
	void *_drv, 
	union fauhdli_value value
);

#endif /* __FAUHDLI_H_INCLUDED */
