/* $Id$
 *
 * Signal/Driver handling related functions.
 *
 * 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 __SIGNALS_H_INCLUDED
#define __SIGNALS_H_INCLUDED

#include "fauhdli_private.h"
#include <stdbool.h>

/** a VHDL signal */
struct signal {
	/** current signal value */
	union fauhdli_value value;
	/** set of drivers, that are connected to the signal */
	struct slset *connected_drivers;
	/** signal value was changed in the delta cycle. */
	bool event;
	/** is this a "foreign" signal? */
	bool is_foreign;
	/** id of the foreign signal */
	unsigned int foreign_id;
	/** associated resolution function (if any) */
	const struct code_container *resolver;
};

/** a VHDL driver */
struct driver {
	/** current driving value of the driver */
	union fauhdli_value driving_value;
	/** signal to which the driver is connected to (or NULL) */
	struct signal *connected_signal;
	/** is the driver active during this delta cycle? (i.e. it 
	 *  recieved any update) 
	 *  FIXME not set correctly!
	 */
	bool active;
	/** transactions on the driver */
	struct slset *transactions;
	/** is this a foreign out driver? */
	bool foreign_out;
	/** id of the foreign signal (if any) */
	unsigned int foreign_id;
};


/** connect a driver to a signal
 *  @param driver driver that should get connected to signal
 *  @param signal signal that the driver should get connected to.
 *  @param instance fauhdli instance.
 *  @return true if it is a foreign driver, false otherwise.
 */
extern bool
driver_connect(
	struct driver *driver, 
	struct signal *_signal, 
	struct fauhdli *instance
);

/** create a driver at *driver_p.
 *  @param init initial value of the driver.
 *  @param callbacks callbacks (for malloc)
 *  @return created driver instance.
 */
extern
struct driver *
driver_create(union fauhdli_value init, const struct glue_vhdl_cb *callbacks);

/** destroy a driver (freeing the memory). 
 *  @param driver driver to destroy 
 *  @param callbacks for free.
 */
extern void
driver_destroy(struct driver *driver, const struct glue_vhdl_cb *callbacks);

/** update a driver with a new value after a specific delay.
 *  @param driver driver instance.
 *  @param val new driving value.
 *  @param sim_time simulation time at which the update should be 
 *         performed.
 *  @param callbacks callbacks for malloc.
 */
extern void
driver_update(
	struct driver *driver,
	union fauhdli_value val,
	universal_integer sim_time,
	const struct glue_vhdl_cb *callbacks
);

/** propagate the driving value to the (foreign) signal via 
 *  glue-vhdl.
 *  @param instance fauhdli instance.
 *  @param driver driver to propagate the value to the signal
 *  @param sim_time current simulation time.
 */
extern void
driver_forward_foreign(
	struct fauhdli *instance,
	struct driver *driver,
	universal_integer sim_time
);

/** propagate all driving values of the drivers to the signal.
 *  foreign_out drivers will not get propagated.
 *
 *  @param instance fauhdli instance
 *  @param sig signal which should get updated.
 *  @param sim_time current simulation time.
 */
extern void
signal_drivers_propagate(
	struct fauhdli *instance,
	struct signal *sig, 
	universal_integer sim_time
);


/** determine the time of the next event on a driver.
 *  @param drv driver instance.
 *  @return scheduled simulation time of next event, or INT64_MAX in case
 *          no event is scheduled.
 */
extern 
universal_integer
driver_get_next_event(const struct driver *drv);

/** create a VHDL signal. If foreign is specified, a foreign signal will
 *  additionally get created.
 *  @param init initial value of the signal.
 *  @param foreign name of foreign signal to create, or NULL if not foreign.
 *  @param instance fauhdli instance.
 *  @param name name for debugging only
 *  @param resolver resolution function container (or NULL if unresolved)
 *  @return pointer to newly created signal.
 */
extern struct signal *
signal_create(
	union fauhdli_value init,
	const char *foreign,
	struct fauhdli *instance,
	const char *name,
	const struct code_container *resolver
);

/** destroy a signal. This won't destroy the associated drivers.
 *  @param signal signal to destroy
 *  @param callbacks for free
 */
extern void
signal_destroy(struct signal *_signal, const struct glue_vhdl_cb *callbacks);

/** read the current value of a signal.
 *  @param signal read the signal value from this signal instance.
 *  @param val read value will be stored there.
 */
static inline void 
__attribute__((__always_inline__))
signal_read(
	const struct signal *sig, 
	union fauhdli_value *val
)
{
	*val = sig->value;
}

/** clear the event flag of the signal 
 *  @param sig signal to clear the event flag */
static inline void
__attribute__((__always_inline__))
signal_clear_event(struct signal *sig) {
	sig->event = false;
}

/* If a foreign signal is read, create a driver for it, add the callback
 * to glue_vhdl, and set the initial value.
 * Will do nothing, if not a foreign signal, or the foreign driver is
 * already created.
 *
 * @param sig signal instance.
 * @param glue_vhdl glue_vhdl instance
 * @param driver_set add the driver to driver_set.
 */
extern void
signal_connect_foreign_in_driver(
	struct signal *sig,
	struct fauhdli *instance,
	struct slset *driver_set
);


#endif /* __SIGNALS_H_INCLUDED */
