/* $Id$
 *
 * Interpreter kernel.
 *
 * 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.
 */

#include "kernel.h"
#include "signals.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "glue-log.h"
#include "slset.h"
#include "mangle_names.h"
#include "trace.h"
#include "glue-main.h"
#include <inttypes.h>
#include "log.h"

#define ARRAY_SIZE(t)	(sizeof t / sizeof(t[0]))

#if 1 /* kernel debugging enabled */
#define kernel_debug(instance, fmt, ...) (\
	instance->callbacks.log(\
		FAUHDLI_LOG_DEBUG, \
		"fauhdli", \
		__func__, \
		fmt, \
		__VA_ARGS__)\
	)
#else /* kernel debugging disabled */
#define kernel_debug(fmt, ...) ()
#endif /* kernel debugging switch */

/** layout of one stack entry */
struct stack_frame {
	/** raw data of the stack frame */
	void *data;
	/** transfer data of the stack frame */
	void *transfer_data;
	/** link to parent stack entry */
	struct stack_frame *parent;
	/** link to static parent stack entry */
	struct stack_frame *static_parent;
	/** stored return address */
	const struct slist_entry *return_address;
	/** stack frame for an upcoming function call 
	 *  (initializied by begintrans) */
	struct stack_frame *transfer;
	/** evil hack: store last transfer stack there */
	struct stack_frame *last_transfer;
	/** nesting level of currently executed function */
	unsigned int nesting_level;
	/** for foreign stack related transactions, recalled id (if any) */
	unsigned int foreign_id;
	/** name of the stack frame (can be used to construct instance name) 
	 */
	const char *name;
	/** pointer to allocated virtual registers */
	union fauhdli_value *vregs;
};

/** state of one process */
struct fauhdli_process {
	/** currently evaluated opcode (aka program counter) */
	const struct slist_entry *pc;
	/** link back to instance (to obtain registers) */
	struct fauhdli *instance;
	/** current stack entry */
	struct stack_frame *stack;
	/** sensitivity set of the process (shadow copy of selected signals) 
	 */
	struct slset *sensitivities;
	/** time of next event */
	universal_integer next_event;
	/** debugging only: name of process */
	const char *name;
};


static void
fauhdli_kernel_execute(struct fauhdli_process *s);

/* ---------------- tracing related ----------------------- */

/** determine the display size out of annotations.
 *  @param annotations list of annotations, maybe NULL.
 *  @return annotated display size, or -1 if not found.
 */
static int
fauhdli_get_display_size(const struct slist *annotations)
{
	const struct annotation_spec *spec;

	spec = fauhdli_find_annotation("display_size", annotations);
	if (spec == NULL) {
		return -1;
	}

	return spec->int_value;
}

static const char *
fauhdli_get_display_type(const struct slist *annotations)
{
	const struct annotation_spec *spec;

	spec = fauhdli_find_annotation("display", annotations);
	if (spec == NULL) {
		return NULL;
	}

	return spec->string_value;
}


static enum type_kind
fauhdli_get_basic_type(const struct type_element *te)
{
	assert(te->elem_count == te->elements);
	assert(te->type != NULL);
	
	if (strcmp("universal_integer", te->type->name) == 0) {
		return TYPE_INT;
	}

	if (strcmp("universal_real", te->type->name) == 0) {
		return TYPE_FLOAT;
	}

	if (strcmp("__pointer__", te->type->name) == 0) {
		return TYPE_POINTER;
	}

	/* only reached, if not a composite type, but then the structures
	 * are bogus! */
	assert(false);
	return TYPE_INT;
}

/* return false if it is a simple array or non-composite type.
 * true, if it is an array of a record or something similar complex.
 */
static bool
fauhdli_is_complex_composite(const struct type_element *te)
{
	assert(te->elem_count != -1);
	return te->elem_count != te->elements;
}

static void
fauhdli_trace_data(
	struct fauhdli *instance,
	const struct data_definition *data,
	const void *set_ptr
)
{
	enum type_kind basic_type;
	const char *sig_loc;
	const char *display_type;
	int display_size;

	switch (data->storage) {
	case STORAGE_SIGNAL:
		break;

	default:
		/* not supported right now */
		return;
	}

	/* it's a signal! */
	assert(data->type != NULL);
	assert(data->type->type != NULL);
	
	if (fauhdli_is_complex_composite(data->type)) {
		instance->callbacks.log(
			FAUHDLI_LOG_WARNING, "fauhdli", "kernel",
			"Tracing composite signal %s not (yet) supported\n",
			data->name);
		return;

	}
	sig_loc = (const char *)set_ptr + data->offset;

	basic_type = fauhdli_get_basic_type(data->type);
	display_size = fauhdli_get_display_size(data->annotations);
	display_type = fauhdli_get_display_type(data->annotations);

	trace_add_signal(instance->tracer,
			(const union fauhdli_value*)(const void *)sig_loc,
			data->name, 
			basic_type, 
			display_size,
			display_type,
			&instance->callbacks,
			data->type->elements);
}

static void
fauhdli_trace_segment(
	struct fauhdli *instance,
	const struct slist *seg_defs,
	const void *segment
)
{
	const struct slist_entry *i;

	for (i = seg_defs->first; i != NULL; i = i->next) {
		const struct data_definition *d = 
			(const struct data_definition *)i->data;

		fauhdli_trace_data(instance, d, segment);
	}
}

/* ---------- misc. helper functions ----------------------- */

#if 0 /* enable for debugging stack frames, currently unused */
static void
fauhdli_kernel_debug_stack_r(
	struct stack_frame *sf, 
	unsigned int depth, 
	bool static_parent
)
{
	char buf[2048];
	memset(buf, ' ', sizeof(buf) - 1);
	assert(depth * 4 < sizeof(buf));
	buf[depth * 4] = '\0';

	if (static_parent) {
		kernel_debug("%sstatic parent=%p\n", buf, sf);
	} else {
		kernel_debug("%sstack=%p\n", buf, sf);
	}

	if (sf == NULL) {
		return;
	}

	fauhdli_kernel_debug_stack_r(sf->parent, depth + 1, false);
	fauhdli_kernel_debug_stack_r(sf->static_parent, depth + 1, true);
}

static void
__attribute__((__used__))
fauhdli_kernel_debug_stack(struct stack_frame *sf)
{
	kernel_debug("*************** STACK %p *************\n", sf);
	fauhdli_kernel_debug_stack_r(sf, 0, false);
}
#endif

static struct fauhdli_process *
process_create(const struct glue_vhdl_cb *callbacks)
{
	struct fauhdli_process *process;

	process = callbacks->malloc(sizeof(struct fauhdli_process));
	assert(process != NULL);

	process->pc = NULL;
	process->instance = NULL;
	process->stack = NULL;
	process->sensitivities = slset_create(NULL, callbacks->malloc);
	process->next_event = INT64_MAX;
	process->name = NULL;

	return process;
}

static void
process_destroy(struct fauhdli_process *p)
{
	slset_destroy(p->sensitivities, p->instance->callbacks.free);
	p->instance->callbacks.free(p);
}

static void
kernel_add_sensitivity(struct fauhdli_process *s, struct signal *sig)
{
	slset_add(s->sensitivities, sig, s->instance->callbacks.malloc);
}

static void
kernel_add_timeout(struct fauhdli_process *s, universal_integer timeout)
{
	assert(s->instance->sim_time < timeout);
	s->next_event = timeout;
}

static void
fauhdli_kernel_destroy_stack_frame(
	struct stack_frame *sf, 
	const struct glue_vhdl_cb *callbacks
)
{
	if (sf == NULL) {
		return;
	}

	if (sf->data != NULL) {
		callbacks->free(sf->data);
	}

	if (sf->transfer_data != NULL) {
		callbacks->free(sf->transfer_data);
	}
	if (sf->vregs != NULL) {
		callbacks->free(sf->vregs);
	}

	callbacks->free(sf);
}

static void
fauhdli_kernel_proc_run(void *_proc)
{
	struct fauhdli_process *proc = 
		(struct fauhdli_process *)_proc;

	fauhdli_kernel_execute(proc);
}

/* ---------- functions modifiy the internal state --------- */

static void
inc_pc(struct fauhdli_process *s)
{
	s->pc = s->pc->next;
	assert(s->pc != NULL);
}

/* ---------- functions to create data types/segments ------ */

/** create a basic data element (i.e. non-composite).
 *  @param initializer initializer from surrounding data. For each data 
 *         element, this will be advanced.
 */
static size_t
create_basic_data(
	struct fauhdli_process *s,
	const struct type_element *elem,
	const struct code_container *resolver,
	char *dest, 
	enum storage_kind storage,
	const char *name,
	const char *foreign_val,
	bool foreign_to_vhdl,
	struct slist_entry **initializer
)
{
	union fauhdli_value init;

	assert(initializer != NULL);
	if (*initializer != NULL) {
		const struct operand *imm_init = 
			(const struct operand*)(*initializer)->data;
		assert(imm_init->kind == OPERAND_IMMEDIATE);
		init = imm_init->bytype.immediate;
		*initializer = (*initializer)->next;
	} else {
		if (s->instance->debug) {
			s->instance->callbacks.log(
				FAUHDLI_LOG_WARNING, "fauhdli", "kernel",
				"Basic type %s defines no initial value!\n",
				elem->name);
		}
		init.univ_int = 0;
	}


	/* "foreign" is only interesting when creating signals
	 * Variables that are foreign are created regularly,
	 * however signals *additionally* create a corresponding hidden
	 * driver.
	 */
	switch (storage) {
	case STORAGE_VARIABLE:
		*(union fauhdli_value*)(void *)dest = init;
		break;

	case STORAGE_DRIVER: {
		struct driver **drv = (struct driver **)(void *)dest;
		*drv = driver_create(init, &s->instance->callbacks);
		slset_add(
			s->instance->drivers, 
			*drv,
			s->instance->callbacks.malloc);
		break;
	    }
	case STORAGE_SIGNAL: {
		struct signal **sig = (struct signal **)(void *)dest;

		*sig = signal_create(init, 
					foreign_val, 
					s->instance,
					name,
					resolver
					);
		slset_add(
			s->instance->signals, 
			*sig,
			s->instance->callbacks.malloc);
		if (foreign_to_vhdl) {
			signal_connect_foreign_in_driver(
						*sig,
						s->instance,
						s->instance->drivers);

		}
		break;
	    }
	}

	return sizeof(union fauhdli_value);
}

/** create a data by type definition 
 *  (TODO)
 *  @param initializer initializer of data element.
 *  @param name name of the data type (for debugging)
 *  @param foreign_val mapped to foreign type name (or NULL if not 
 *         foreign)
 *  @param foreign_to_vhdl true, if a foreign signal is read from
 *         VHDL context, false otherwise.
 */
static size_t
create_data_by_type(
	struct fauhdli_process *s,
	const struct type_element *elem,
	const struct code_container *resolver,
	struct slist_entry **initializer,
	char *dest, 
	enum storage_kind storage,
	const char *name,
	const char *foreign_val,
	bool foreign_to_vhdl
)
{
	const struct type_declaration *t;
	struct slist_entry *i;
	size_t ret = 0;
	size_t sz;
	universal_integer cnt;
	char buf[8192];
	int n;

	t = elem->type;
	assert(t != NULL);
	assert(initializer != NULL);

	if (t->elements != NULL) {
		cnt = 0;
		/* not a basic type, traverse to elements */
		for (i = t->elements->first; i != NULL; i = i->next) {
			const struct type_element *te = 
				(const struct type_element *)i->data;

			if (*initializer == NULL) {
				/* data doesn't have initial value, so use
				   default value from type element */
				initializer = &te->initial_list->first;
			}
			
			n = snprintf(buf, sizeof(buf), "%s__%" PRIi64, name, 
					cnt);
			assert(n > 0);
			assert((size_t)n < sizeof(buf));
			/* FIXME not handled yet */
			assert(resolver == NULL);
			sz = create_data_by_type(s, 
						te,
						NULL,
						initializer,
						dest, 
						storage, 
						buf, 
						foreign_val, 
						foreign_to_vhdl);
			dest += sz;
			ret += sz;
			cnt++;
		}

		return ret;
	}
	
	/* basic type */
	assert(elem->elements != -1);
	for (cnt = 0; cnt < elem->elements; cnt++) {
		n = snprintf(buf, sizeof(buf), "%s__%" PRIi64, name, cnt);
		assert(n > 0);
		assert((size_t)n < sizeof(buf));
		sz = create_basic_data(s, 
					elem,
					resolver,
					dest, 
					storage, 
					buf, 
					foreign_val, 
					foreign_to_vhdl,
					initializer);
		dest += sz;
		ret += sz;
	}
	return ret;
}

/** @param current process instance.
 *  @param data data definition to create
 *  @parem dest destination pointer in stack space.
 *  @param instance_prefix instance name prefix to use for data.
 *  @return size of generate data entry.
 */
static size_t
create_data(
	struct fauhdli_process *s,
	const struct data_definition *data, 
	char *dest,
	const char *instance_prefix
)
{
	size_t ret;
	const struct annotation_spec *spec;
	const char *foreign_val = NULL;
	bool foreign_to_vhdl = false;
	char buf[8192];
	char *n;
	struct slist_entry *initializer;

	assert(data != NULL);
	assert(dest != NULL);
	assert(data->type != NULL);


	spec = fauhdli_find_annotation("foreign", data->annotations);
	if (spec != NULL) {
		foreign_val = spec->string_value;
	}

	if (foreign_val != NULL) {
		spec = fauhdli_find_annotation("read", data->annotations);
		if (spec != NULL) {
			assert(spec->int_value == 1);
			foreign_to_vhdl = true;
		}
	}

	n = data->name;
	if (*instance_prefix != '\0') {
		int i;
		i = snprintf(buf, sizeof(buf), "%s%s", instance_prefix,
				data->name);
		assert(i > 0);
		assert(i < sizeof(buf));
		n = buf;
	}

	initializer = data->type->initial_list->first;
	ret = create_data_by_type(s,
				data->type,
				data->resolver.container,
				&initializer,
				dest, 
				data->storage,
				n,
				foreign_val,
				foreign_to_vhdl);

	assert(ret == data->storage_size);
	return data->storage_size;
}

static void *
create_data_segment(
	struct fauhdli_process *s,
	const struct slist *data_segment,
	size_t storage_size,
	const char *instance_prefix
)
{
	void *ret;
	char *d;
	struct slist_entry *i;

	ret = s->instance->callbacks.malloc(storage_size);
	assert(ret != NULL);
	d = (char *)ret;

	for (i = data_segment->first; i != NULL; i = i->next) {
		size_t sz;
		struct data_definition *elem = 
			(struct data_definition *)i->data;

		sz = create_data(s, elem, d, instance_prefix);
		d += sz;
	}

	assert(d - (char *)ret >= 0);
	assert((size_t)(d - (char *)ret) == storage_size);

	return ret;
}

static size_t
get_stack_name_r(const struct stack_frame *s, char *buf, size_t sz)
{
	size_t ret;

	if (s == NULL) {
		ret = snprintf(buf, sz, "__c_");
		assert(ret < sz);
		return ret;
	}

	ret = get_stack_name_r(s->parent, buf, sz);
	sz -= ret;
	buf += ret;

	assert(sz > 0);
	if (s->name == NULL) {
		return ret;
	}

	if (strlen(s->name) == 0) {
		return ret;
	}

	ret += snprintf(buf, sz, "%s__c_", s->name);
	return ret;
}

static const char *
get_stack_name(const struct stack_frame *s)
{
	static char buf[8192];

	buf[0] = '\0';
	get_stack_name_r(s, buf, sizeof(buf));
	return buf;
}

static struct stack_frame *
create_stack_frame(
	struct fauhdli_process *s,
	const struct code_container *container, 
	struct stack_frame *parent,
	struct stack_frame *static_parent,
	const char *name
)
{
	struct stack_frame *sf;
	const char *instance_name;

	assert(container != NULL);

	sf = s->instance->callbacks.malloc(sizeof(struct stack_frame));
	assert(sf != NULL);

	sf->parent = parent;
	sf->static_parent = static_parent;
	sf->return_address = NULL;
	sf->transfer = NULL;
	sf->foreign_id = 0;
	sf->name = name;
	sf->vregs = 
		s->instance->callbacks.malloc(
			sizeof(union fauhdli_value) * container->num_vregs);
	assert(sf->vregs != NULL);

	if (static_parent != NULL) {
		sf->nesting_level = static_parent->nesting_level + 1;
	} else {
		sf->nesting_level = 1; /* 1 -> top stack frame */
	}

	instance_name = get_stack_name(sf);

	if (container->stack_segment != NULL) {
		sf->data = create_data_segment(
				s,
				container->stack_segment, 
				container->stack_size,
				instance_name);
	} else {
		sf->data = NULL;
	}

	if (container->transfer_segment != NULL) {
		sf->transfer_data = create_data_segment(
					s,
					container->transfer_segment,
					container->transfer_size,
					"transfer__c_" /* FIXME? */);
	} else {
		sf->transfer_data = NULL;
	}

	return sf;
}

static struct stack_frame *
create_foreign_stack(
	struct stack_frame *parent,
	unsigned int foreign_id,
	const struct glue_vhdl_cb *callbacks
)
{
	/* TODO (idea) actually, it might even be possible to use the
	 *      transfer segment as mechanism between fauhdli
	 *      and foreign components.
	 */
	struct stack_frame *sf;
	sf = callbacks->malloc(sizeof(struct stack_frame));
	assert(sf != NULL);

	sf->parent = parent;
	sf->static_parent = NULL;
	sf->return_address = NULL;
	sf->transfer = NULL;
	sf->foreign_id = foreign_id;
	sf->nesting_level = 0;
	sf->data = NULL;

	return sf;
}

/* -------------- functions related to operands ------------ */

static const struct slist_entry *
fetch_target(struct operand *target)
{
	assert(target != NULL);
	assert(target->kind == OPERAND_TARGET);
	assert(target->bytype.target.ref != NULL);

	return target->bytype.target.ref;
}

static const struct code_container *
fetch_target_container(struct operand *target)
{
	assert(target != NULL);
	assert(target->kind == OPERAND_REFERENCE);
	assert(target->bytype.data.container != NULL);

	return target->bytype.data.container;
}

static void
read_mem(
	const struct fauhdli_process *s,
	union fauhdli_value *ret,
	void *mem,
	enum type_kind type
)
{
	switch (type) {
	case TYPE_INT:
		ret->univ_int = *(universal_integer*)mem;
		break;

	case TYPE_FLOAT:
		ret->univ_real = *(universal_real*)mem;
		break;

	case TYPE_POINTER:
		ret->pointer = *(void **)mem;
		break;
	}
}

static void
write_mem(
	struct fauhdli_process *s, 
	void *mem, 
	union fauhdli_value val, 
	enum type_kind type
)
{
	switch (type) {
	case TYPE_INT:
		*(universal_integer*)mem = val.univ_int;
		break;

	case TYPE_FLOAT:
		*(universal_real*)mem = val.univ_real;
		break;

	case TYPE_POINTER:
		*(void **)mem = val.pointer;
		break;
	}
}

/** read the contents of a register.
 *  @param s current process state.
 *  @param ret value will be stored here.
 *  @param reg register number 
 *  @param type type of register, only useful for debugging.
 */
static void
read_register(
	const struct fauhdli_process *s,
	union fauhdli_value *ret,
	unsigned int reg,
	enum type_kind type
)
{
	*ret = s->stack->vregs[reg];
}

static void
write_register(
	struct fauhdli_process *s,
	unsigned int reg,
	union fauhdli_value val,
	enum type_kind type
)
{
	s->stack->vregs[reg] = val;
}

static void
read_reference(
	const struct fauhdli_process *s,
	union fauhdli_value *ret,
	const struct data_definition *src
)
{
	char *loc = NULL;
	struct stack_frame *stack = s->stack;

	assert(src != NULL);
	assert(stack != NULL);

	/* find correct stack segment by static_parent */
	while (src->nesting_level < stack->nesting_level) {
		assert(stack->static_parent != NULL);
		stack = stack->static_parent;
	}

	switch (src->loc) {
	case SEGMENT_TRANSFER:
		assert(stack->transfer_data != NULL);
		loc = (char *)stack->transfer_data + src->offset;
		break;

	case SEGMENT_STACK:
		assert(stack->data != NULL);
		loc = (char *)stack->data + src->offset;
		break;
	}

	ret->pointer = loc;
}

static void
read_operand(
	const struct fauhdli_process *s,
	union fauhdli_value *ret,
	const struct operand *src
)
{
	union fauhdli_value ind;

	assert(ret != NULL);
	assert(src != NULL);

	switch (src->kind) {
	case OPERAND_REGISTER:
		read_register(s, ret, src->bytype.reg, src->type);
		break;

	case OPERAND_IMMEDIATE:
		*ret = src->bytype.immediate;
		break;
		
	case OPERAND_INDIRECT:
		read_register(s, &ind, src->bytype.reg, TYPE_POINTER);
		read_mem(s, ret, ind.pointer, src->type);
		break;

	case OPERAND_REFERENCE:
		assert(src->type == TYPE_POINTER);
		read_reference(s, ret, src->bytype.data.ref);
		break;

	case OPERAND_TARGET:
		/* FIXME: valid? */
		assert(0);
		break;
	}
}

static void
write_operand(
	struct fauhdli_process *s, 
	struct operand *dst, 
	union fauhdli_value value
)
{
	union fauhdli_value ind;
	assert(dst != NULL);

	switch (dst->kind) {
	case OPERAND_REGISTER:
		write_register(s, dst->bytype.reg, value, dst->type);
		break;

	case OPERAND_INDIRECT:
		read_register(s, &ind, dst->bytype.reg, TYPE_POINTER);
		write_mem(s, ind.pointer, value, dst->type);
		break;

	case OPERAND_REFERENCE:
		/* doesn't make sense (reference is a pointer by name, so
		 * it is some kind of immediate) */
		assert(0);
		break;

	case OPERAND_TARGET:
		/* mustn't write into text segment */
		assert(0);
		break;
		
	case OPERAND_IMMEDIATE:
		/* mustn't write to immedate */
		assert(0);
		break;
	}
}

/** compare two operand values.
 *  @param v1 first operand value to compare
 *  @param v2 second operand value to compare.
 *  @return 0 if equal, -1 if v1 < v2 and 1 if v1 > v2
 */
static int
compare_values(
	union fauhdli_value v1, 
	union fauhdli_value v2,
	enum type_kind kind
)
{
	switch (kind) {
	case TYPE_INT:
		if (v1.univ_int == v2.univ_int) {
			return 0;
		}
		if (v1.univ_int < v2.univ_int) {
			return -1;
		}
		return 1;
	
	case TYPE_FLOAT:
		if (v1.univ_real == v2.univ_real) {
			return 0;
		}
		if (v1.univ_real < v2.univ_real) {
			return -1;
		}
		return 1;

	case TYPE_POINTER:
		if (v1.pointer == v2.pointer) {
			return 0;
		}
		if (v1.pointer < v2.pointer) {
			return -1;
		}
		return 1;
	}

	/* not reached */
	assert(0);
	return 0;
}

/* -------------- functions for evaluating opcodes --------- */

static void
eval_jmp(struct fauhdli_process *s, const struct opcode *jmp)
{
	assert(jmp->kind == OPCODE_JMP);
	assert(jmp->op2 == NULL);
	assert(jmp->op3 == NULL);
	assert(jmp->label == NULL);
	
	s->pc = fetch_target(jmp->op1);
}

static void
eval_mov(struct fauhdli_process *s, const struct opcode *mov)
{
	union fauhdli_value val;

	assert(mov != NULL);
	assert(mov->op3 == NULL);
	assert(mov->label == NULL);

	read_operand(s, &val, mov->op1);
	write_operand(s, mov->op2, val);
	inc_pc(s);
}

static void
eval_add(struct fauhdli_process *s, const struct opcode *add)
{
	union fauhdli_value op1;
	union fauhdli_value op2;
	union fauhdli_value res;

	res.univ_int = 0;

	assert(add->op1 != NULL);
	assert(add->op2 != NULL);
	assert(add->op3 != NULL);
	assert(add->op1->type == add->op2->type);
	assert(add->op1->type == add->op3->type);

	read_operand(s, &op1, add->op1);
	read_operand(s, &op2, add->op2);

	switch (add->op1->type) {
	case TYPE_INT:
		res.univ_int = op1.univ_int + op2.univ_int;
		break;

	case TYPE_FLOAT:
		res.univ_real = op1.univ_real + op2.univ_real;
		break;

	case TYPE_POINTER:
		/* currently no use to add two pointers */
		assert(0);
		break;
	}

	write_operand(s, add->op3, res);
	inc_pc(s);
}

static void
eval_sub(struct fauhdli_process *s, const struct opcode *sub)
{
	union fauhdli_value op1;
	union fauhdli_value op2;
	union fauhdli_value res;

	res.univ_int = 0;

	assert(sub->op1 != NULL);
	assert(sub->op2 != NULL);
	assert(sub->op3 != NULL);
	assert(sub->op1->type == sub->op2->type);
	assert(sub->op1->type == sub->op3->type);
	
	read_operand(s, &op1, sub->op1);
	read_operand(s, &op2, sub->op2);

	switch (sub->op1->type) {
	case TYPE_INT:
		res.univ_int = op1.univ_int - op2.univ_int;
		break;

	case TYPE_FLOAT:
		res.univ_real = op1.univ_real - op2.univ_real;
		break;

	case TYPE_POINTER:
		/* currently no use to subtract two pointers */
		assert(0);
		break;
	}

	write_operand(s, sub->op3, res);
	inc_pc(s);
}

static void
eval_imul(struct fauhdli_process *s, const struct opcode *imul)
{
	union fauhdli_value op1;
	union fauhdli_value op2;
	union fauhdli_value res;

	res.univ_int = 0;

	assert(imul->op1 != NULL);
	assert(imul->op2 != NULL);
	assert(imul->op3 != NULL);
	assert(imul->op1->type == imul->op2->type);
	assert(imul->op1->type == imul->op3->type);
	
	read_operand(s, &op1, imul->op1);
	read_operand(s, &op2, imul->op2);

	switch (imul->op1->type) {
	case TYPE_INT:
		res.univ_int = op1.univ_int * op2.univ_int;
		break;

	case TYPE_FLOAT:
		res.univ_real = op1.univ_real * op2.univ_real;
		break;

	case TYPE_POINTER:
		/* currently no use to multiply two pointers */
		assert(0);
		break;
	}

	write_operand(s, imul->op3, res);
	inc_pc(s);
}

static void
eval_div(struct fauhdli_process *s, const struct opcode *op_div)
{
	union fauhdli_value op1;
	union fauhdli_value op2;
	union fauhdli_value res;

	res.univ_int = 0;

	assert(op_div->op1 != NULL);
	assert(op_div->op2 != NULL);
	assert(op_div->op3 != NULL);
	assert(op_div->op1->type == op_div->op2->type);
	assert(op_div->op1->type == op_div->op3->type);
	
	read_operand(s, &op1, op_div->op1);
	read_operand(s, &op2, op_div->op2);

	switch (op_div->op1->type) {
	case TYPE_INT:
		res.univ_int = op1.univ_int / op2.univ_int;
		break;

	case TYPE_FLOAT:
		res.univ_real = op1.univ_real / op2.univ_real;
		break;

	case TYPE_POINTER:
		/* currently no use to divide two pointers */
		assert(0);
		break;
	}

	write_operand(s, op_div->op3, res);
	inc_pc(s);
}


static void
eval_je(struct fauhdli_process *s, const struct opcode *je)
{
	union fauhdli_value cmp1;
	union fauhdli_value cmp2;
	int cmp;

	assert(je->op1 != NULL);
	assert(je->op2 != NULL);
	assert(je->op3 != NULL);
	assert(je->op1->type == je->op2->type);

	read_operand(s, &cmp1, je->op1);
	read_operand(s, &cmp2, je->op2);
	cmp = compare_values(cmp1, cmp2, je->op1->type);

	if (cmp == 0) {
		/* branch taken */
		s->pc = fetch_target(je->op3);
	} else {
		/* branch not taken */
		inc_pc(s);
	}
}

static void
eval_jb(struct fauhdli_process *s, const struct opcode *jb)
{
	union fauhdli_value cmp1;
	union fauhdli_value cmp2;
	int cmp;

	assert(jb->op1 != NULL);
	assert(jb->op2 != NULL);
	assert(jb->op3 != NULL);
	assert(jb->op1->type == jb->op2->type);

	read_operand(s, &cmp1, jb->op1);
	read_operand(s, &cmp2, jb->op2);
	cmp = compare_values(cmp1, cmp2, jb->op1->type);

	if (cmp == -1) {
		/* branch taken */
		s->pc = fetch_target(jb->op3);
	} else {
		/* branch not taken */
		inc_pc(s);
	}
}

static void
eval_jne(struct fauhdli_process *s, const struct opcode *jne)
{
	union fauhdli_value cmp1;
	union fauhdli_value cmp2;
	int cmp;

	assert(jne->op1 != NULL);
	assert(jne->op2 != NULL);
	assert(jne->op3 != NULL);
	assert(jne->op1->type == jne->op2->type);

	read_operand(s, &cmp1, jne->op1);
	read_operand(s, &cmp2, jne->op2);
	cmp = compare_values(cmp1, cmp2, jne->op1->type);

	if (cmp != 0) {
		/* branch taken */
		s->pc = fetch_target(jne->op3);
	} else {
		/* branch not taken */
		inc_pc(s);
	}
}

static void
eval_jbe(struct fauhdli_process *s, const struct opcode *jbe)
{
	union fauhdli_value cmp1;
	union fauhdli_value cmp2;
	int cmp;

	assert(jbe->op1 != NULL);
	assert(jbe->op2 != NULL);
	assert(jbe->op3 != NULL);
	assert(jbe->op1->type == jbe->op2->type);

	read_operand(s, &cmp1, jbe->op1);
	read_operand(s, &cmp2, jbe->op2);
	cmp = compare_values(cmp1, cmp2, jbe->op1->type);

	if (cmp != 1) {
		/* branch taken */
		s->pc = fetch_target(jbe->op3);
	} else {
		/* branch not taken */
		inc_pc(s);
	}
}

static void
eval_foreign_comp_init(
	struct fauhdli *instance,
	unsigned int comp_id,
	const char *comp_name
)
{
	assert(instance->callbacks.comp_init != NULL);
	instance->callbacks.comp_init(instance->glue_vhdl, comp_id);
}

static void
eval_foreign_arch_init(
	struct fauhdli *instance,
	unsigned int comp_id,
	const char *comp_name
)
{
	assert(instance->callbacks.arch_init != NULL);
	instance->callbacks.arch_init(instance->glue_vhdl, comp_id);
}

static void
eval_foreign_call(
	struct fauhdli *instance,
	const struct stack_frame *foreign_stack,
	const char *foreign_val,
	const char *target
)
{
	if (strcmp(foreign_val, "entity") == 0) {
		eval_foreign_comp_init(instance,
					foreign_stack->foreign_id,
					target);
	} else if (strcmp(foreign_val, "architecture") == 0) {
		eval_foreign_arch_init(instance,
					foreign_stack->foreign_id,
					target);
	} else if (strcmp(foreign_val, "procedure") == 0) {
		assert(instance->callbacks.proc_call != NULL);
		instance->callbacks.proc_call(instance->glue_vhdl, target);
	} else {
		assert(0);
	}
}

static void
eval_call(struct fauhdli_process *s, const struct opcode *call)
{
	const struct code_container *target;
	const struct annotation_spec *spec = 
		fauhdli_find_annotation("foreign", call->annotations);


	assert(call->op1 != NULL);
	assert(call->op2 == NULL);
	assert(call->op3 == NULL);

	assert(s->stack != NULL);
	assert(s->stack->transfer != NULL);

	if (spec != NULL) {
		eval_foreign_call(s->instance,
				s->stack->transfer,
				spec->string_value,
				call->op1->bytype.data.name);
		inc_pc(s);
		return;				
	}

	/* should have been set by begintrans */
	assert(s->stack->transfer->static_parent != NULL);
	assert(s->stack->transfer->parent != NULL);
	assert(s->stack->transfer->transfer == NULL);

	inc_pc(s);
	s->stack->transfer->return_address = s->pc;
	s->stack = s->stack->transfer;

	target = fetch_target_container(call->op1);
	assert(target->text_segment != NULL);
	s->pc = target->text_segment->first;
}

static void
eval_proc(struct fauhdli_process *s, const struct opcode *proc)
{
	const struct code_container *target;
	struct fauhdli_process *process;

	assert(proc->op1 != NULL);
	assert(proc->op2 == NULL);
	assert(proc->op3 == NULL);

	assert(s->stack != NULL);
	assert(s->stack->transfer != NULL);

	/* should have been set by begintrans */
	assert(s->stack->transfer->static_parent != NULL);
	assert(s->stack->transfer->parent != NULL);
	assert(s->stack->transfer->transfer == NULL);

	s->stack->transfer->return_address = NULL;

	target = fetch_target_container(proc->op1);
	assert(target->text_segment != NULL);

	process = process_create(&s->instance->callbacks);
	process->name = target->name;
	process->instance = s->instance;
	process->stack = s->stack->transfer;
	process->pc = target->text_segment->first;

	vhdl_sched_add_process(s->instance->scheduler, process, 
				fauhdli_kernel_proc_run, target->name,
				&s->instance->callbacks);
	slset_add(
		s->instance->processes, 
		process,
		s->instance->callbacks.malloc);

	inc_pc(s);
}

static unsigned int
eval_create_foreign_component(
	struct fauhdli *instance,
	const char *comp_name,
	const char *type,
	enum foreign_mode_e foreign_mode
)
{
	unsigned int comp_id = 0;
	static char demangled[2048];
	int ret;

	ret = demangle_name(comp_name, demangled, sizeof(demangled));
	assert(ret >= 0);
	assert((size_t)ret < sizeof(demangled));

	switch (foreign_mode) {
	case FOREIGN_MODE_COMPONENT:
		assert(instance->callbacks.comp_create != NULL);
		comp_id = instance->callbacks.comp_create(
						instance->glue_vhdl,
						type, 
						demangled);
		break;

	case FOREIGN_MODE_ARCHITECTURE:
		assert(instance->callbacks.arch_create != NULL);
		comp_id = instance->callbacks.arch_create(
						instance->glue_vhdl,
						type, 
						demangled);
		break;

	}

	return comp_id;
}

/** evaluate a foreign begintrans opcode.
 *  @param instance interpreter instance.
 *  @param container_name name of the container (reference name of op1)
 *  @param kind kind of foreign begintrans (foreign annotation value)
 *  @param stack current stack frame for instance prefix
 *  @param foreign_mode will get set according to annotation
 *  @param name instance name of the componenent
 *  @return optional foreign_id, that will be stored in foreign_id of
 *          stack_frame.
 */
static unsigned int
eval_foreign_begintrans(
	struct fauhdli *instance,
	const char *container_name,
	const char *kind,
	const struct stack_frame *stack,
	enum foreign_mode_e *foreign_mode,
	const char *name
)
{
	unsigned int ret;

	if (strcmp(kind, "entity") == 0) {
		char buf[8192];
		const char *n;

		n = get_stack_name(stack);
		if (*n != '\0') {
			int r;
			r = snprintf(buf, sizeof(buf), "%s%s", n, name);
			assert(r >= 0);
			assert(r < sizeof(buf));
			n = buf;
		} else {
			n = name;
		}

		*foreign_mode = FOREIGN_MODE_COMPONENT;
		ret = eval_create_foreign_component(
					instance, 
					n,
					container_name,
					FOREIGN_MODE_COMPONENT);
		return ret;
	} else if (strcmp(kind, "architecture") == 0) {
		const char *n;
		size_t sz;
		char buf[8192];

		n = get_stack_name(stack);
		strncpy(buf, n, sizeof(buf));
		buf[sizeof(buf) - 1] = '\0';
		/* foreign architectures are created from within the 
		 * architecture. The stack name will hence be the actually
		 * instance name ending with a ':', and the name is the
		 * name of the architecture (still mangled here though).
		 * Turn this back into the instance name by removing the last
		 * ':'.
		 */
		sz = strlen(buf);
		assert(sz > 4); /* should end with (mangled) "__c_" */
		assert(buf[sz - 1] == '_');
		assert(buf[sz - 2] == 'c');
		assert(buf[sz - 3] == '_');
		assert(buf[sz - 4] == '_');
		buf[sz - 4] = '\0';

		*foreign_mode = FOREIGN_MODE_ARCHITECTURE;
		ret = eval_create_foreign_component(
					instance, 
					buf,
					container_name,
					FOREIGN_MODE_ARCHITECTURE);
		return ret;
	} else if (strcmp(kind, "procedure") == 0) {
		/* nothing to do for a procedure... or should this be 
		 * proxied to glue-vhdl as well? */
		return 0;
	} else {
		/* TODO */
		assert(0);
	}

	/* not reached */
	return 0;
}

static void
eval_begintrans(
	struct fauhdli_process *s, 
	const struct opcode *begintrans
)
{
	unsigned int i;
	const struct code_container *target;
	struct stack_frame *static_parent;
	const struct annotation_spec *spec = 
		fauhdli_find_annotation("foreign", begintrans->annotations);
	struct stack_frame *last_transfer;


	assert(begintrans->op1 != NULL);
	assert(begintrans->op2 != NULL);
	assert(begintrans->op3 == NULL);

	/* FIXME evil hack to work around broken code generator */
	last_transfer = s->stack->transfer;
	s->stack->transfer = NULL;

	assert(s->stack->transfer == NULL);

	assert(begintrans->op2->kind == OPERAND_REFERENCE);
	if (spec != NULL) {
		unsigned int id;
		/* don't create any stack frame for foreign entities. */
		assert(spec->string_value != NULL);
		id = eval_foreign_begintrans(s->instance,
					begintrans->op1->bytype.data.name,
					spec->string_value,
					s->stack,
					&s->instance->foreign_mode,
					begintrans->op2->bytype.data.name);
		s->stack->transfer = create_foreign_stack(
						s->stack, 
						id,
						&s->instance->callbacks);
		s->stack->transfer->last_transfer = last_transfer;
		inc_pc(s);
		return;
	}
	
	target = fetch_target_container(begintrans->op1);

	static_parent = s->stack;
	/* it's only possible to jump one level deeper, stay on the same level
	 * or to jump upwards.
	 */
	assert(target->nesting_level - 1 <= s->stack->nesting_level);
	for (i = target->nesting_level; i <= s->stack->nesting_level; i++) {
		assert(static_parent != NULL);
		static_parent = static_parent->static_parent;
	}

	s->stack->transfer = 
		create_stack_frame(s, 
				target, 
				s->stack, 
				static_parent,
				begintrans->op2->bytype.data.name);
	assert(s->stack->transfer->nesting_level == target->nesting_level);
	s->stack->transfer->last_transfer = last_transfer;

	inc_pc(s);
}

static void
eval_foreign_endtrans(
	struct stack_frame *sf,
	const struct glue_vhdl_cb *callbacks
)
{
	callbacks->free(sf);
}

static void
eval_endtrans(struct fauhdli_process *s, const struct opcode *endtrans)
{
	union fauhdli_value fs;
	const struct annotation_spec *spec = 
		fauhdli_find_annotation("foreign", endtrans->annotations);
	struct stack_frame *tmp;

	assert(endtrans->op1 != NULL);
	assert(endtrans->op2 != NULL);
	assert(endtrans->op3 == NULL);

	assert(s->stack->transfer != NULL);

	assert(endtrans->op2->type == TYPE_INT);
	read_operand(s, &fs, endtrans->op2);

	if (spec != NULL) {
		/* for foreign stack operations, always free the stack
		 * frame again. */
		eval_foreign_endtrans(
				s->stack->transfer,
				&s->instance->callbacks);
		s->stack->transfer = s->stack->transfer->last_transfer;
		inc_pc(s);
		return;
	}

	tmp = s->stack->transfer->last_transfer;
	if (fs.univ_int != 0) {
		fauhdli_kernel_destroy_stack_frame(
					s->stack->transfer,
					&s->instance->callbacks);
	} else {
		slset_add(
			s->instance->pending_stack_frames,
			s->stack->transfer,
			s->instance->callbacks.malloc);
	}
	
	s->stack->transfer = tmp;
	inc_pc(s);
}

/** setparam/foreign: set a port via glue-vhdl.
 *  @param instance fauhdli instance.
 *  @param comp_id stored id of the instantiated component via 
 *         begintrans.
 *  @param sig signal (should be foreign!)
 *  @param comp component name (entity name, not instantiation name)
 *  @param port name of port of component.
 *  @param foreign_mode_e do we instantiate a component or create 
 *         an architecture?
 */
static void
eval_set_foreign_port(
	struct fauhdli *instance,
	unsigned int comp_id, 
	struct signal *sig, 
	const char *comp, 
	const char *port,
	enum foreign_mode_e foreign_mode
)
{
	assert(sig->is_foreign);

	switch (foreign_mode) {
	case FOREIGN_MODE_COMPONENT:
		assert(instance->callbacks.comp_port_connect != NULL);
		instance->callbacks.comp_port_connect(
				instance->glue_vhdl, 
				comp_id, 
				port, 
				sig->foreign_id);
		break;

	case FOREIGN_MODE_ARCHITECTURE:
		assert(instance->callbacks.arch_port_connect != NULL);
		instance->callbacks.arch_port_connect(
				instance->glue_vhdl, 
				comp_id, 
				port, 
				sig->foreign_id);
		break;
	}
}

static void
eval_set_foreign_proc_param(
	struct fauhdli *instance,
	const char *proc,
	const char *param,
	union fauhdli_value value
)
{
	assert(instance->callbacks.proc_set != NULL);
	instance->callbacks.proc_set(instance->glue_vhdl, proc, param, value);
}

/** FIXME that's a mess here.
 *        there already is foreign_stack, and *that* should get
 * 	  reused for proper calling into glue-vhdl, not a second
 * 	  hidden stack like structure introduced in the fauhdli
 * 	  instance.
 */
static void
eval_set_foreign_generic(
	struct fauhdli *s,
	unsigned int comp_id,
	union fauhdli_value val,
	const char *comp,
	const char *generic,
	struct slist *annotations
)
{
	const struct annotation_spec *is_bound;
	const struct annotation_spec *is_unconstraint;
	const struct annotation_spec *is_array = 
		fauhdli_find_annotation("array", annotations);
	const struct annotation_spec *type = 
			fauhdli_find_annotation("type", annotations);

	if ((is_array == NULL) || (is_array->int_value == 0)) {
		/* not an array */
		assert(type != NULL);

		switch (s->foreign_mode) {
		case FOREIGN_MODE_COMPONENT:
			assert(s->callbacks.comp_generic_nonarray_set != NULL);
			s->callbacks.comp_generic_nonarray_set(s->glue_vhdl,
							comp_id, 
							generic, 
							val,
							type->string_value);
			break;

		case FOREIGN_MODE_ARCHITECTURE:
			assert(s->callbacks.arch_generic_nonarray_set != NULL);
			s->callbacks.arch_generic_nonarray_set(s->glue_vhdl,
							comp_id, 
							generic, 
							val,
							type->string_value);
			break;
		}
		return;
	}

	/* array */
	is_unconstraint = 
		fauhdli_find_annotation("unconstraint", annotations);
	if ((is_unconstraint == NULL) || (is_unconstraint->int_value == 0)) {
		/* constraint array */
		/* FIXME */
		assert(0);
	}

	if (comp_id != s->foreign_g_array_params.comp_id) {
		/* sanity check */
		assert(s->foreign_g_array_params.base.pointer == NULL);
		assert(! s->foreign_g_array_params.left_set);
		assert(! s->foreign_g_array_params.right_set);
		
		s->foreign_g_array_params.comp_id = comp_id;
	}

	is_bound = fauhdli_find_annotation("bounds", annotations);
	if (! is_bound) {
		/* base pointer */
		assert(type != NULL);
		assert(s->foreign_g_array_params.base.pointer == NULL);
		s->foreign_g_array_params.base = val;
		s->foreign_g_array_params.element_type = type->string_value;
		s->foreign_g_array_params.formal_name = generic;


	} else if (strcmp(is_bound->string_value, "l") == 0) {
		/* low bound */
		assert(! s->foreign_g_array_params.left_set);
		s->foreign_g_array_params.left_set = true;
		s->foreign_g_array_params.left = val.univ_int;

		/* FIXME use only right bound! */
	} else if ((strcmp(is_bound->string_value, "u") == 0) 
		|| (strcmp(is_bound->string_value, "r") == 0)) {
		/* right bound */
		assert(! s->foreign_g_array_params.right_set);
		s->foreign_g_array_params.right_set = true;
		s->foreign_g_array_params.right = val.univ_int;

	} else if (strcmp(is_bound->string_value, "dir") == 0) {
		/* direction */
		assert(! s->foreign_g_array_params.direction_set);
		s->foreign_g_array_params.direction_set = true;
		s->foreign_g_array_params.direction = val.univ_int;
	} else {
		/* must not happen */
		assert(0);
	}

	if ( s->foreign_g_array_params.base.pointer != NULL
	  && s->foreign_g_array_params.left_set
	  && s->foreign_g_array_params.right_set
	  && s->foreign_g_array_params.direction_set) {

	  	/* array parameter completely collected, flush to glue_vhdl */
	  	universal_integer sz = 
			((s->foreign_g_array_params.right
				- s->foreign_g_array_params.left)
			* s->foreign_g_array_params.direction)
			+ 1;

		if (sz < 0) {
			sz = 0;
		}

		switch (s->foreign_mode) {
		case FOREIGN_MODE_COMPONENT:
			assert(s->callbacks.comp_generic_array_set != NULL);
			s->callbacks.comp_generic_array_set(
				s->glue_vhdl,
				comp_id,
				s->foreign_g_array_params.formal_name,
				s->foreign_g_array_params.element_type,
				s->foreign_g_array_params.base,
				sz);
			break;

		case FOREIGN_MODE_ARCHITECTURE:
			assert(s->callbacks.arch_generic_array_set != NULL);
			s->callbacks.arch_generic_array_set(
				s->glue_vhdl,
				comp_id,
				s->foreign_g_array_params.formal_name,
				s->foreign_g_array_params.element_type,
				s->foreign_g_array_params.base,
				sz);
			break;

		}
		
		s->foreign_g_array_params.base.pointer = NULL;
		s->foreign_g_array_params.element_type = NULL;
		s->foreign_g_array_params.formal_name = NULL;
		s->foreign_g_array_params.left_set = false;
		s->foreign_g_array_params.right_set = false;
		s->foreign_g_array_params.direction_set = false;
	}
}

static void
eval_foreign_setparam(
	struct fauhdli *s,
	const struct stack_frame *foreign_stack,
	const char *foreign_val,
	union fauhdli_value src,
	const char *component,
	const char *target,
	struct slist *annotations
)
{
	/* FIXME why special case generics? */
	/* FIXME that's all a complete and utter mess... 
	 *       foreign generics should be known by FAUmachine,
	 *       in the same way as foreign procedures and 
	 *       functions should be known!
	 */

	if (strcmp(foreign_val, "procedure") == 0) {
		eval_set_foreign_proc_param(s,
					component,
					target,
					src);
	} else if (strcmp(foreign_val, "port") == 0) {
		eval_set_foreign_port(s,
					foreign_stack->foreign_id,
					(struct signal *)src.pointer,
					component,
					target,
					s->foreign_mode);
	} else if (strcmp(foreign_val, "generic") == 0) {
		eval_set_foreign_generic(s,
					foreign_stack->foreign_id,
					src,
					component,
					target,
					annotations);
	} else {
		assert(0);
	}
}

static void
eval_setparam(struct fauhdli_process *s, const struct opcode *setparam)
{
	const struct code_container *container;
	union fauhdli_value src;
	char *loc;
	const struct annotation_spec *spec = 
		fauhdli_find_annotation("foreign", setparam->annotations);

	assert(setparam->op1 != NULL); /* source */
	assert(setparam->op2 != NULL); /* container_name */
	assert(setparam->op3 != NULL); /* target */

	assert(s->stack->transfer != NULL);
	read_operand(s, &src, setparam->op1);

	if (spec != NULL) {
		assert(spec->string_value != NULL);
		eval_foreign_setparam(s->instance,
					s->stack->transfer,
					spec->string_value,
					src,
					setparam->op2->bytype.data.name,
					setparam->op3->bytype.data.name,
					setparam->annotations);
		inc_pc(s);
		return;
	}

	assert(s->stack->transfer->transfer_data != NULL);

	container = fetch_target_container(setparam->op2);
	assert(container != NULL);
	
	assert(setparam->op3->kind == OPERAND_REFERENCE);
	assert(setparam->op3->bytype.data.ref->loc == SEGMENT_TRANSFER);

	loc = (char *)s->stack->transfer->transfer_data;	
	loc += setparam->op3->bytype.data.ref->offset;

	switch (setparam->op3->bytype.data.ref->storage) {
	case STORAGE_VARIABLE:
		write_mem(s, (void *)loc, src, setparam->op1->type);
		break;

	case STORAGE_DRIVER:
	case STORAGE_SIGNAL:
		assert(setparam->op1->type == TYPE_POINTER);
		write_mem(s, (void *)loc, src, TYPE_POINTER);
		break;
	}

	inc_pc(s);
}

static void
eval_getparam(struct fauhdli_process *s, const struct opcode *getparam)
{
	const struct code_container *container;
	union fauhdli_value dst;
	char *loc;

	dst.pointer = NULL;

	assert(getparam->op1 != NULL); /* transfer element */
	assert(getparam->op2 != NULL); /* container_name */
	assert(getparam->op3 != NULL); /* target */

	assert(s->stack->transfer != NULL);
	assert(s->stack->transfer->transfer_data != NULL);

	container = fetch_target_container(getparam->op2);
	assert(container != NULL);
	
	assert(getparam->op1->kind == OPERAND_REFERENCE);
	assert(getparam->op1->bytype.data.ref->loc == SEGMENT_TRANSFER);

	loc = (char *)s->stack->transfer->transfer_data;
	loc += getparam->op1->bytype.data.ref->offset;

	switch (getparam->op1->bytype.data.ref->storage) {
	case STORAGE_VARIABLE:
		read_mem(s, &dst, (void *)loc, getparam->op3->type);
		write_operand(s, getparam->op3, dst);
		break;

	case STORAGE_DRIVER:
	case STORAGE_SIGNAL:
		/* must not be a driver/signal! */
		assert(0);
		break;
	}

	inc_pc(s);
}


static void
eval_return(struct fauhdli_process *s, const struct opcode *ret)
{
	struct stack_frame *current = s->stack;
	assert(current != NULL);

	s->stack = s->stack->parent;
	s->pc = current->return_address;
}

static void
eval_update(struct fauhdli_process *s, const struct opcode *update)
{
	union fauhdli_value val;
	union fauhdli_value delay;
	union fauhdli_value dst;

	assert(update->op1 != NULL);
	assert(update->op2 != NULL);
	assert(update->op3 != NULL);
	assert(update->op2->type == TYPE_INT);
	assert(update->op3->type == TYPE_POINTER);

	read_operand(s, &val, update->op1);
	read_operand(s, &delay, update->op2);
	read_operand(s, &dst, update->op3);

	driver_update((struct driver *)dst.pointer, val, 
			delay.univ_int + s->instance->sim_time,
			&s->instance->callbacks);
	inc_pc(s);
}

static void
eval_getsig(struct fauhdli_process *s, const struct opcode *getsig)
{
	union fauhdli_value sig_ptr;
	union fauhdli_value val;

	assert(getsig->op1 != NULL);
	assert(getsig->op1->type == TYPE_POINTER);
	assert(getsig->op2 != NULL);
	assert(getsig->op3 == NULL);

	read_operand(s, &sig_ptr, getsig->op1);
	signal_read((const struct signal *)sig_ptr.pointer, &val);
	write_operand(s, getsig->op2, val);

	inc_pc(s);
}

static void
eval_connect(struct fauhdli_process *s, const struct opcode *connect)
{
	union fauhdli_value drv;
	union fauhdli_value sig;
	bool is_foreign;

	assert(connect->op1 != NULL);
	assert(connect->op2 != NULL);
	assert(connect->op3 == NULL);

	assert(connect->op1->type == TYPE_POINTER);
	assert(connect->op2->type == TYPE_POINTER);

	read_operand(s, &drv, connect->op1);
	read_operand(s, &sig, connect->op2);

	is_foreign = driver_connect((struct driver *)drv.pointer, 
				(struct signal *)sig.pointer,
				s->instance);

	if (is_foreign) {
		/* remove driver from process drivers set, and sort
		 * it into foreign_drivers set of instance,
		 * since a foreign driver needs to propagate the
		 * value at a different time during the delta cycle
		 */
		assert(slset_contains(s->instance->drivers, drv.pointer));
		slset_remove(
				s->instance->drivers, 
				drv.pointer,
				s->instance->callbacks.free);
		slset_add(
			s->instance->foreign_drivers, 
			drv.pointer,
			s->instance->callbacks.malloc);
	}

	inc_pc(s);
}

static void
eval_wakeat(struct fauhdli_process *s, const struct opcode *wakeat)
{
	union fauhdli_value timeout;
	assert(wakeat->op1 != NULL);
	assert(wakeat->op2 == NULL);
	assert(wakeat->op3 == NULL);

	read_operand(s, &timeout, wakeat->op1);
	kernel_add_timeout(s, timeout.univ_int);
	inc_pc(s);
}

static void
eval_wakeon(struct fauhdli_process *s, const struct opcode *wakeon)
{
	union fauhdli_value sig_ptr;
	assert(wakeon->op1 != NULL);
	assert(wakeon->op2 == NULL);
	assert(wakeon->op3 == NULL);

	read_operand(s, &sig_ptr, wakeon->op1);
	kernel_add_sensitivity(s, (struct signal *)sig_ptr.pointer);
	inc_pc(s);
}

static void
eval_offset(struct fauhdli_process *s, const struct opcode *offset)
{
	union fauhdli_value ptr;
	union fauhdli_value idx;
	char *result;

	assert(offset->op1 != NULL);
	assert(offset->op2 != NULL);
	assert(offset->op3 != NULL);
	assert(offset->indexed_type != NULL);
	assert(offset->indexed_type->elem_count != -1);

	assert(offset->op1->type == TYPE_POINTER);
	assert(offset->op2->type == TYPE_INT);
	assert(offset->op3->type == TYPE_POINTER);


	read_operand(s, &ptr, offset->op1);
	read_operand(s, &idx, offset->op2);

	result = (char *)ptr.pointer;
	result += offset->indexed_type->elem_count 
		* sizeof(union fauhdli_value) 
		* idx.univ_int;
	ptr.pointer = (void *)result;

	write_operand(s, offset->op3, ptr);
	inc_pc(s);
}

static void
eval_log(struct fauhdli_process *s, const struct opcode *log)
{
	union fauhdli_value severity;
	union fauhdli_value c;

	assert(log->op1 != NULL);
	assert(log->op2 != NULL);
	assert(log->op3 == NULL);

	assert(log->op1->type == TYPE_INT);
	assert(log->op2->type == TYPE_INT);

	read_operand(s, &severity, log->op1);
	read_operand(s, &c, log->op2);

	log_vhdl(
		&s->instance->callbacks,
		(unsigned int)severity.univ_int, 
		s->name,
		(char)c.univ_int);
	
	inc_pc(s);
}

static void
eval_abort(struct fauhdli_process *s, const struct opcode *opc)
{
	assert(s->instance->callbacks.quit != NULL);
	s->instance->callbacks.quit(1);
	inc_pc(s);
}

static void
eval_get_sim_time(struct fauhdli_process *s, const struct opcode *opc)
{
	union fauhdli_value sim_time;

	assert(opc->op1 != NULL);
	assert(opc->op2 == NULL);
	assert(opc->op3 == NULL);

	sim_time.univ_int = s->instance->sim_time;
	write_operand(s, opc->op1, sim_time);

	inc_pc(s);
}

/** evaluate one opcode.
 *  @param s current process
 *  @param opcode opcode to evaluate.
 *  @return true, if the process of the opcode should get suspended, false
 *          otherwise.
 */
static bool
evaluate_opcode(struct fauhdli_process *s, const struct opcode *opcode)
{
	if (s->instance->debug) {
		kernel_debug(s->instance, "Line %d\n", opcode->lineno);
	}
	
	switch (opcode->kind) {
	case OPCODE_MOV:
		eval_mov(s, opcode);
		break;

	case OPCODE_ADD:
		eval_add(s, opcode);
		break;

	case OPCODE_SUB:
		eval_sub(s, opcode);
		break;

	case OPCODE_IMUL:
		eval_imul(s, opcode);
		break;

	case OPCODE_DIV:
		eval_div(s, opcode);
		break;

	case OPCODE_CALL:
		eval_call(s, opcode);
		break;

	case OPCODE_PROC:
		eval_proc(s, opcode);
		break;

	case OPCODE_RETURN:
		eval_return(s, opcode);
		break;

	case OPCODE_BEGINTRANS:
		eval_begintrans(s, opcode);
		break;
	
	case OPCODE_ENDTRANS:
		eval_endtrans(s, opcode);
		break;
	
	case OPCODE_SETPARAM:
		eval_setparam(s, opcode);
		break;

	case OPCODE_GETPARAM:
		eval_getparam(s, opcode);
		break;

	case OPCODE_UPDATE:
		eval_update(s, opcode);
		break;

	case OPCODE_GETSIG:
		eval_getsig(s, opcode);
		break;

	case OPCODE_JMP:
		eval_jmp(s, opcode);
		break;

	case OPCODE_JE:
		eval_je(s, opcode);
		break;

	case OPCODE_JB:
		eval_jb(s, opcode);
		break;

	case OPCODE_JNE:
		eval_jne(s, opcode);
		break;

	case OPCODE_JBE:
		eval_jbe(s, opcode);
		break;

	case OPCODE_AOFFSET:
	case OPCODE_ROFFSET:
		/* both can be evaluated the same */
		eval_offset(s, opcode);
		break;

	case OPCODE_SUSPEND:
		inc_pc(s);
		return true;

	case OPCODE_WAKEAT:
		eval_wakeat(s, opcode);
		break;

	case OPCODE_WAKEON:
		eval_wakeon(s, opcode);
		break;

	case OPCODE_CONNECT:
		eval_connect(s, opcode);
		break;

	case OPCODE_LOG:
		eval_log(s, opcode);
		break;

	case OPCODE_LABEL:
		/* nothing to do. just increase the pc */
		inc_pc(s);
		break;

	case OPCODE_ABORT:
		eval_abort(s, opcode);
		return true;

	case OPCODE_GETSIMTIME:
		eval_get_sim_time(s, opcode);
		break;
	}

	return false;
}

static void
fauhdli_kernel_execute(struct fauhdli_process *s)
{
	const struct opcode *opcode;
	const struct slist_entry *last_pc = NULL;

	while (s->pc != NULL) {
		bool ret;

		opcode = (const struct opcode*)s->pc->data;
		assert(opcode != NULL);

		last_pc = s->pc;
		ret = evaluate_opcode(s, opcode);

		if (last_pc == s->pc) {
			s->instance->callbacks.log(
				FAUHDLI_LOG_ERROR, "fauhdli", __func__,
				"Endless loop detected.\n");
			/* can happen only, if an opcode doesn't change PC */
			s->instance->callbacks.log(
				FAUHDLI_LOG_DEBUG, "fauhdli", __func__,
				"pc type=%d\n", opcode->kind);
			assert(0);
		}

		if (ret) {
			break;
		}
	}
}

static struct code_container *
fauhdli_kernel_locate_con_r(
	struct code_container *container,
	const char *mangled_name,
	bool traverse
)
{
	struct slist_entry *i;

	if (container == NULL) {
		return NULL;
	}

	if (container->sub_containers == NULL) {
		return NULL;
	}

	for (i = container->sub_containers->first; i != NULL; i = i->next) {
		struct code_container * const c = 
				(struct code_container *)i->data;

		if (strcmp(c->name, mangled_name) == 0) {
			return c;
		}

		if (traverse) {
			struct code_container *r;

			r = fauhdli_kernel_locate_con_r(c, mangled_name, true);
			if (r != NULL) {
				return r;
			}
		}
	}
	return NULL;
}


/** find the subcontainer with name "name" in container.
 *  @param container container to look through for given text segment.
 *  @param name use subcontainer with this name instead of top-container
 *  @param traverse if true, traverses recursively, otherwise just 
 *         looks at direct subcontainers.
 *  @return subcontainer, or NULL if not found.
 */
static struct code_container *
fauhdli_kernel_find_con(
	const struct glue_vhdl_cb *callbacks,
	struct code_container *container, 
	const char *name,
	bool traverse
)
{
	struct code_container *c;
	char buf[2048];
	int r;

	if (name == NULL) {
		return container;
	}

	r = mangle_name(name, buf, sizeof(buf));
	if (r == sizeof(buf)) {
		callbacks->log(FAUHDLI_LOG_ERROR, "fauhdli", "kernel",
			"Name \"%s\" too long!\n", name);
		return NULL;
	}

	c = fauhdli_kernel_locate_con_r(container, buf, traverse);
	return c;
}


/** clear event flag of all signals 
 *  @param all_sigs set containing all signals */
static void
fauhdli_kernel_clear_event(struct slset *all_sigs)
{
	struct slset_entry *i;

	for (i = all_sigs->first; i != NULL; i = i->next) {
		signal_clear_event((struct signal *)i->data);
	}
}

static void
fauhdli_kernel_drv_propagate(
	struct fauhdli *instance,
	struct slset *s, /* FIXME both parts of instance */
	universal_integer sim_time
)
{
	struct slset_entry *i;

	for (i = s->first; i != NULL; i = i->next) {
		signal_drivers_propagate(instance, 
					(struct signal *)i->data, 
					sim_time);
	}
}


static void
fauhdli_kernel_process_wake(
	struct vhdl_sched *sched,
	struct fauhdli_process *p,
	universal_integer sim_time
)
{
	struct slset_entry *i;
	bool wakeup = false;

	/* if any sensitivity has an event, wakeup the process */
	for (i = p->sensitivities->first; i != NULL; i = i->next) {
		struct signal *s = (struct signal *)i->data;

		if (s->event) {
			wakeup = true;
			break;
		}
	}

	if (p->next_event <= sim_time) {
		wakeup = true;
	}

	if (! wakeup) {
		return;
	}

	/* wakeup/reset process */
	slset_clear(p->sensitivities, p->instance->callbacks.free);
	p->next_event = INT64_MAX;
	vhdl_sched_wakeup(sched, p, &p->instance->callbacks);
}

static universal_integer
fauhdli_kernel_get_next_time(
	const struct slset *all_procs,
	const struct slset *all_drvs,
	const struct slset *foreign_drivers
)
{
	const struct slset_entry *i;
	universal_integer ret = INT64_MAX;
	
	for (i = all_procs->first; i != NULL; i = i->next) {
		const struct fauhdli_process *p = 
			(const struct fauhdli_process *)i->data;

		if (p->next_event < ret) {
			ret = p->next_event;
		}
	}

	for (i = all_drvs->first; i != NULL; i = i->next) {
		universal_integer d_event;
		const struct driver *d = (const struct driver*)i->data;
		d_event = driver_get_next_event(d);

		if (d_event < ret) {
			ret = d_event;
		}
	}

	for (i = foreign_drivers->first; i != NULL; i = i->next) {
		universal_integer d_event;
		const struct driver *d = (const struct driver *)i->data;

		d_event = driver_get_next_event(d);
		if (d_event < ret) {
			ret = d_event;
		}
	}

	return ret;
}


/** propagate all current driving values of foreign (out) drivers */
static void
fauhdli_kernel_forward_foreign_drvs(
	struct fauhdli *instance, 
	universal_integer sim_time
)
{
	struct slset_entry *i;

	for (i = instance->foreign_drivers->first; i != NULL; i = i->next) {
		struct driver *drv = (struct driver *)i->data;

		driver_forward_foreign(instance, drv, sim_time);
	}
}

static universal_integer
fauhdli_kernel_simulation_start(struct fauhdli *instance)
{
	universal_integer t_next;
	/* calculate initial values/driving values (done implicitly here) */

	/* run all processes */
	vhdl_sched_run(instance->scheduler, &instance->callbacks);

	/* determine t_next */
	t_next = fauhdli_kernel_get_next_time(instance->processes,
						instance->drivers,
						instance->foreign_drivers);

	return t_next;
}

void
fauhdli_kernel_simulation_cycle(void *_instance)
{
	struct fauhdli *instance = (struct fauhdli *)_instance;
	struct slset_entry *i;
	universal_integer t_next;

	/* no need to schedule wakeups *during* the delta cycle */
	instance->wakeup_scheduled = true;

	while (true) {
		/* a: advance time, exit if sim_time == INT64_MAX */
		t_next = (int64_t)instance->callbacks.time_virt();
		assert(0 <= t_next);
		instance->sim_time = t_next;

		if (instance->sim_time == INT64_MAX) {
			instance->callbacks.log(
				FAUHDLI_LOG_INFO, "fauhdli", "kernel", 
				"simulation end.\n");
			return;
		}

		/* b: update explicit signals */
		fauhdli_kernel_drv_propagate(instance, 
						instance->signals,
						instance->sim_time);

		/* c: update implicit signals (no implicit signals 
		 *    right now, TODO) */

		/* d: wake up all processes with events */
		for (i = instance->processes->first; i != NULL; i = i->next) {
			fauhdli_kernel_process_wake(
				instance->scheduler,
				(struct fauhdli_process *)i->data,
				instance->sim_time);
		}

		/* e0: "run foreign process" (aka call foreign signal set
		 *     methods on active drivers)
		 */
		fauhdli_kernel_forward_foreign_drvs(instance, t_next);

		/* e1: resume all processes with events */
		vhdl_sched_run(instance->scheduler, &instance->callbacks);

		fauhdli_kernel_clear_event(instance->signals);

		/* f: next_time = min(driver, processes, time'high), 
		 *    delta cycle present if next_time == sim_time */
		t_next = fauhdli_kernel_get_next_time(instance->processes,
						instance->drivers,
						instance->foreign_drivers);

		if (t_next == instance->sim_time) {
			continue;
		}

		if ((instance->tracer != NULL) && (t_next != INT64_MAX)) {
			trace_time_advance(instance->tracer, t_next);
		}

#if 0 /* no postponed processes currently */
		/* g: if not delta_cycle: execute all postponed processes,
		 *    recalculate next_time according to f
		 */
		t_next = fauhdli_kernel_get_next_time(instance->processes);
#endif
		/* no delta cycle... return */
		instance->callbacks.time_call_at(
				t_next, 
				fauhdli_kernel_simulation_cycle, 
				instance);

		instance->wakeup_scheduled = false;
		return;
	}
}

/** start the simulation cycle and schedule the following 
 *  The following runs can be executed via fauhdli_kernel_simulation_cycle.
 *  @param _instance fauhdli instance.
 */
static void
fauhdli_kernel_cycle_start(void *_instance)
{
	struct fauhdli *instance = (struct fauhdli *)_instance;
	universal_integer t_next;

	assert(instance != NULL);
	assert(instance->scheduler != NULL);

	if (! vhdl_sched_has_runnable(instance->scheduler)) {
		instance->callbacks.log(
			FAUHDLI_LOG_WARNING, "fauhdli", "kernel",
			"No process to run.\n");
		return;
	}

	t_next = fauhdli_kernel_simulation_start(instance);
	instance->callbacks.time_call_at(
				t_next, 
				fauhdli_kernel_simulation_cycle, 
				instance);
}

static const struct fauhdli_process *
fauhdli_kernel_find_process(const struct fauhdli *instance, const char *name)
{
	struct slset_entry *i;
	char buf[2048];
	int ret;

	for (i = instance->processes->first; i != NULL; i = i->next) {
		const struct fauhdli_process *p = 
			(const struct fauhdli_process *)i->data;
		char *c;

		ret = demangle_name(p->name, buf, sizeof(buf));
		assert(ret >= 0);
		assert((size_t)ret < sizeof(buf));
		c = strrchr(buf, ':');
		assert(c != NULL);
		*c = '\0';

		if (strcmp(name, buf) == 0) {
			return p;
		}
	}

	return NULL;
}

static void
fauhdli_kernel_trace(struct fauhdli *instance)
{
	struct slist_entry *i;

	if (instance->tracer == NULL) {
		return;
	}

	for (i = instance->trace_list->first; i != NULL; i = i->next) {
		const char *s = (const char *)i->data;
		struct code_container *container;

		container = fauhdli_kernel_find_con(
					&instance->callbacks,
					instance->container,
					s,
					true);
		if (container == NULL) {
			instance->callbacks.log(FAUHDLI_LOG_WARNING,
					"fauhdli", "kernel",
					"Cannot find entity <%s>, skipping "
					"trace.\n", s);
		} else {
			/* FIXME hideous: locate a process in the entity
			 * and go one hierarchy above, that's the stack 
			 * segment.
			 */
			const struct fauhdli_process *p = 
				fauhdli_kernel_find_process(instance, s);
			assert(p != NULL);
			assert(p->stack != NULL);
			assert(p->stack->static_parent != NULL);
		
			fauhdli_trace_segment(
					instance,
					container->stack_segment,
					p->stack->static_parent->data);
		}
	}
}

void
fauhdli_kernel_resolve(
	struct fauhdli *instance,
	const struct code_container *resolver, 
	union fauhdli_value *ret,
	union fauhdli_value *drv_values,
	size_t sz
)
{
	struct fauhdli_process *process;
	union fauhdli_value *data;

	process = process_create(&instance->callbacks);
	process->pc = resolver->text_segment->first;
	process->instance = instance;
	/* since the resolution function must be pure, it mustn't 
	 * access any values outside of the function itself, hence
	 * there is no need to create a static_parent.
	 */
	process->stack = 
		create_stack_frame(process, resolver, NULL, NULL, "");
	/* loop the stack frame, "return" opcode will otherwise overwrite it*/
	process->stack->parent = process->stack;

	/* getting ugly: we neither know what the transfer variables are 
	 * called, nor do we have these resolved by symbol table.
	 * But we do know how fauhdlc orders them, and that it's exactly
	 * one unconstraint array as parameter to the resolution function.
	 * Hence we can manually fumble things into place here.
	 */

	/* the transfer variable order is 
	 *   array base pointer
	 *   array lower bound
	 *   array upper bound
	 *   array direction
	 *   return value.
	 */
	data = (union fauhdli_value *)process->stack->transfer_data;
	/* array base pointer */
	data->pointer = drv_values;
	data++;
	/* lower bound */
	data->univ_int = 0;
	data++;
	/* upper bound */
	data->univ_int = sz - 1;
	data++;
	/* direction (to = 1) */
	data->univ_int = 1;
	data++;
	/* now pointing to return value */

	fauhdli_kernel_execute(process);

	*ret = *data;

	fauhdli_kernel_destroy_stack_frame(
					process->stack, 
					&instance->callbacks);
	process_destroy(process);
}


void
fauhdli_kernel_destroy(struct fauhdli *instance)
{
	struct slset_entry *i;

	for (i = instance->pending_stack_frames->first; i != NULL; 
		i = i->next) {

		fauhdli_kernel_destroy_stack_frame(
				(struct stack_frame *)i->data,
				&instance->callbacks);
	}

	for (i = instance->processes->first; i != NULL; i = i->next) {
		process_destroy((struct fauhdli_process *)i->data);
	}

	for (i = instance->drivers->first; i != NULL; i = i->next) {
		struct driver *drv = (struct driver *)i->data;
		driver_destroy(drv, &instance->callbacks);
	}

	for (i = instance->foreign_drivers->first; i != NULL; i = i->next) {
		struct driver *drv = (struct driver *)i->data;
		driver_destroy(drv, &instance->callbacks);
	}
}

void
fauhdli_kernel_init(struct fauhdli *instance, const char *top_entity)
{
	struct fauhdli_process *process;
	struct code_container *container;
	const char *n;

	assert(instance->container != NULL);
	process = process_create(&instance->callbacks);
	
	if (instance->container->transfer_segment != NULL) {
		instance->callbacks.log(
			FAUHDLI_LOG_ERROR, "fauhdli", "kernel",
			"Top container needs parameters.\n");
		return;
	}

	container = fauhdli_kernel_find_con(
				&instance->callbacks, 
				instance->container, 
				top_entity,
				false);
	if (container == NULL) {
		instance->callbacks.log(
				FAUHDLI_LOG_ERROR, 
				"fauhdli", 
				"kernel",
				"cannot locate container %s as child of top "
				"container.\n", top_entity);

		return;
	}

	if (container->text_segment == NULL) {
		instance->callbacks.log(
			FAUHDLI_LOG_ERROR, "fauhdli", "kernel", 
			"No code to run in container. Exiting.\n");
		return;
	}

	if (container->transfer_segment != NULL) {
		if (top_entity == NULL) {
			n = "top";
		} else {
			n = top_entity;
		}

		instance->callbacks.log(
			FAUHDLI_LOG_ERROR, "fauhdli", "kernel",
			"Container %s needs parameters\n", n);
		return;
	}

	process->pc = container->text_segment->first;
	process->instance = instance;

	process->stack = 
		create_stack_frame(process, 
					instance->container, 
					NULL, 
					NULL, 
					"");
	if (container != instance->container) {
		/* must be a direct child of container */
		process->stack->transfer = create_stack_frame(
						process,
						container,
						process->stack, 
						process->stack,
						"");
		process->stack = process->stack->transfer;
	}

	fauhdli_kernel_execute(process);

	if (process->stack != NULL) {
		slset_add(
			instance->pending_stack_frames, 
			process->stack,
			instance->callbacks.malloc);

		if (process->stack->transfer != NULL) {
			slset_add(
				instance->pending_stack_frames, 
				process->stack->transfer,
				instance->callbacks.malloc);
		}
	}
	process_destroy(process);

	/* make sure, tracer is initialised *after* setup process was run */
	fauhdli_kernel_trace(instance);

	instance->callbacks.time_call_at(
					0, 
					fauhdli_kernel_cycle_start, 
					instance);
}
