Logo Search packages:      
Sourcecode: fauhdlc version File versions

kernel.c

/* $Id: kernel.c 4321 2009-01-27 13:02:52Z potyra $
 *
 * 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 "libfauhdli.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 "glue-vhdl.h"
#include <inttypes.h>
#include "log.h"

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

#if 1 /* kernel debugging enabled */
#define kernel_debug(fmt, ...) (\
      faum_log(FAUM_LOG_DEBUG, "fauhdli", __func__, fmt, __VA_ARGS__)\
      )
#else /* kernel debugging disabled */
#define kernel_debug(fmt, ...) ()
#endif /* kernel debugging switch */

/** layout of one stack entry */
00037 struct stack_frame {
      /** raw data of the stack frame */
00039       void *data;
      /** transfer data of the stack frame */
00041       void *transfer_data;
      /** link to parent stack entry */
00043       struct stack_frame *parent;
      /** link to static parent stack entry */
00045       struct stack_frame *static_parent;
      /** stored return address */
00047       const struct slist_entry *return_address;
      /** stack frame for an upcoming function call 
       *  (initializied by begintrans) */
00050       struct stack_frame *transfer;
      /** nesting level of currently executed function */
00052       unsigned int nesting_level;
      /** for foreign stack related transactions, recalled id (if any) */
00054       unsigned int foreign_id;
      /** name of the stack frame (can be used to construct instance name) 
       */
00057       const char *name;
};

/** state of one process */
00061 struct fauhdli_process {
      /** currently evaluated opcode (aka program counter) */
00063       const struct slist_entry *pc;
      /** link back to instance (to obtain registers) */
00065       struct fauhdli *instance;
      /** current stack entry */
00067       struct stack_frame *stack;
      /** sensitivity set of the process (shadow copy of selected signals) 
       */
00070       struct slset *sensitivities;
      /** time of next event */
00072       universal_integer next_event;
      /** debugging only: name of process */
00074       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 enum type_kind
fauhdli_get_basic_type(const struct type_element *te)
{
      /* must not be composite */
      assert(te->elem_count == 1);
      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;
}

static bool
fauhdli_is_composite(const struct type_element *te)
{
      assert(te->elem_count != -1);
      if (te->elem_count > 1) {
            return true;
      }

      return false;
}

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;
      int display_size;

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

      default:
            /* not interested */
            return;
      }

      /* it's a signal! */
      assert(data->type != NULL);
      assert(data->type->type != NULL);
      
      if (fauhdli_is_composite(data->type)) {
            faum_log(FAUM_LOG_WARNING, "fauhdli", "kernel",
                  "Tracing composite signal %s not (yet) supported\n",
                  data->name);

      }
      sig_loc = (const char *)set_ptr + data->offset;
      sig_loc = *(const char * const *)sig_loc;
      basic_type = fauhdli_get_basic_type(data->type);
      display_size = fauhdli_get_display_size(data->annotations);

      trace_add_signal(instance->tracer, 
                  (const struct signal *)sig_loc,
                  data->name, 
                  basic_type, 
                  display_size);
}

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 declaration_element *e = 
                  (const struct declaration_element *)i->data;

            switch (e->kind) {
            case DECLARATION_TYPE:
                  /* not interested */
                  break;

            case DECLARATION_DATA:
                  fauhdli_trace_data(instance, 
                              &e->bytype.data_def, 
                              segment);
                  break;
            }
      }
}

static void
fauhdli_trace_sigs(struct trace_t *tracer, const struct slset *all_sigs)
{
      const struct slset_entry *i;

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

            if (sig->event) {
                  trace_sig_event(tracer, sig);
            }
      }
}

/* ---------- 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(void)
{
      struct fauhdli_process *process;

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

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

      return process;
}

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

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

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)
{
      if (sf == NULL) {
            return;
      }

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

      if (sf->transfer_data != NULL) {
            free(sf->transfer_data);
      }
      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).
 */
static size_t
create_basic_data(
      struct fauhdli_process *s,
      const struct type_element *elem, 
      char *dest, 
      enum storage_kind storage,
      const char *name,
      const char *foreign_val,
      bool foreign_to_vhdl
)
{
      union fauhdli_value init;

      if (elem->initial != NULL) {
            assert(elem->initial->kind == OPERAND_IMMEDIATE);
            init = elem->initial->bytype.immediate;
      } else {
            faum_log(FAUM_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*)dest = init;
            break;

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

            *sig = signal_create(init, 
                              foreign_val, 
                              s->instance->glue_vhdl,
                              name);
            slset_add(s->instance->signals, *sig);
            if (foreign_to_vhdl) {
                  signal_connect_foreign_in_driver(
                                    *sig,
                                    s->instance->glue_vhdl,
                                    s->instance->drivers);
            }
            break;
          }
      }

      return sizeof(union fauhdli_value);
}

/** create a data by type definition 
 *  (TODO)
 *  @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, 
      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);

      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;
                  
                  n = snprintf(buf, sizeof(buf), "%s__%" PRIi64, name, 
                              cnt);
                  assert(n > 0);
                  assert((size_t)n < sizeof(buf));
                  sz = create_data_by_type(s, te, 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, dest, storage, buf, 
                              foreign_val, foreign_to_vhdl);
            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;
      int i;

      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 (strlen(instance_prefix) > 0) {
            i = snprintf(buf, sizeof(buf), "%s%s", instance_prefix,
                        data->name);
            assert(i > 0);
            assert(i < sizeof(buf));
            n = buf;
      }

      ret = create_data_by_type(s, 
                        data->type, 
                        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 = malloc(storage_size);
      assert(ret != NULL);
      d = (char *)ret;

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

            switch (elem->kind) {
            case DECLARATION_TYPE:
                  /* skip types */
                  break;

            case DECLARATION_DATA:
                  sz = create_data(s, 
                              &elem->bytype.data_def, 
                              d,
                              instance_prefix);
                  d += sz;
                  break;
            }
      }

      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 = 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;

      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
)
{
      /* 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 = 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;
      }
}

/* FIXME if registers don't use type checking and checking if these
 *       are initialized already, read_register and 
 *       write_register would be much simpler
 */

/** 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
)
{
      const struct reg_value *val;

      assert(reg < ARRAY_SIZE(s->instance->registers));

      val = &s->instance->registers[reg];

      assert(val->initialized);
      assert(val->kind == type);

      *ret = val->value;
}

static void
write_register(
      struct fauhdli_process *s,
      unsigned int reg,
      union fauhdli_value val,
      enum type_kind type
)
{
      struct reg_value *r;

      assert(reg < ARRAY_SIZE(s->instance->registers));

      r = &s->instance->registers[reg];

      if (r->initialized) {
            assert(r->kind == type);
      } else {
            r->initialized = true;
            r->kind = type;
      }

      r->value = 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;

      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;

      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;

      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;

      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 glue_vhdl *glue_vhdl, 
      unsigned int comp_id,
      const char *comp_name
)
{
      glue_vhdl_comp_init(glue_vhdl, comp_id);
}

static void
eval_foreign_call(
      struct glue_vhdl *glue_vhdl,
      const struct stack_frame *foreign_stack,
      const char *foreign_val,
      const char *target
)
{
      if (strcmp(foreign_val, "entity") == 0) {
            eval_foreign_comp_init(glue_vhdl,
                              foreign_stack->foreign_id,
                              target);
      } else if (strcmp(foreign_val, "procedure") == 0) {
            glue_vhdl_proc_call(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->glue_vhdl,
                        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();
      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);
      slset_add(s->instance->processes, process);

      inc_pc(s);
}

static unsigned int
eval_create_foreign_component(
      struct glue_vhdl *glue_vhdl,
      const char *comp_name,
      const char *type,
      const struct stack_frame *stack
)
{
      unsigned int comp_id;
      static char demangled[2048];
      int ret;
      unsigned int stack_depth = 0;

      while (stack != NULL) {
            stack = stack->parent;
            stack_depth++;
      }

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

      comp_id = glue_vhdl_comp_create(glue_vhdl, type, demangled, 
                              stack_depth);
      return comp_id;
}

/** evaluate a foreign begintrans opcode.
 *  @param glue_vhdl glue_vhdl 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 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 glue_vhdl *glue_vhdl,
      const char *container_name,
      const char *kind,
      const struct stack_frame *stack,
      const char *name
)
{
      unsigned int ret;

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

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

            ret = eval_create_foreign_component(
                              glue_vhdl, 
                              n,
                              container_name,
                              stack);
            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);

      assert(begintrans->op1 != NULL);
      assert(begintrans->op2 != NULL);
      assert(begintrans->op3 == 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->glue_vhdl,
                              begintrans->op1->bytype.data.name,
                              spec->string_value,
                              s->stack,
                              begintrans->op2->bytype.data.name);
            s->stack->transfer = create_foreign_stack(s->stack, id);
            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);

      inc_pc(s);
}

static void
eval_foreign_endtrans(struct stack_frame *sf)
{
      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);

      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->stack->transfer = NULL;
            inc_pc(s);
            return;
      }

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

/** setparam/foreign: set a port via glue-vhdl.
 *  @param glue_vhdl glue_vhdl 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.
 */
static void
eval_set_foreign_port(
      struct glue_vhdl *glue_vhdl,
      unsigned int comp_id, 
      struct signal *sig, 
      const char *comp, 
      const char *port
)
{
      assert(sig->is_foreign);
      glue_vhdl_comp_port_connect(glue_vhdl, comp_id, port, sig->foreign_id);
}

static void
eval_set_foreign_proc_param(
      struct glue_vhdl *glue_vhdl,
      const char *proc,
      const char *param,
      union fauhdli_value value
)
{
      glue_vhdl_proc_set(glue_vhdl, proc, param, value);
}

static void
eval_set_foreign_generic(
      struct glue_vhdl *glue_vhdl,
      unsigned int comp_id,
      union fauhdli_value val,
      const char *comp,
      const char *generic
)
{
      glue_vhdl_comp_generic_set(glue_vhdl, comp_id, generic, val);
}

static void
eval_foreign_setparam(
      struct glue_vhdl *glue_vhdl,
      const struct stack_frame *foreign_stack,
      const char *foreign_val,
      union fauhdli_value src,
      const char *component,
      const char *target
)
{
      if (strcmp(foreign_val, "procedure") == 0) {
            eval_set_foreign_proc_param(glue_vhdl,
                              component,
                              target,
                              src);
      } else if (strcmp(foreign_val, "port") == 0) {
            eval_set_foreign_port(glue_vhdl,
                              foreign_stack->foreign_id,
                              (struct signal *)src.pointer,
                              component,
                              target);
      } else if (strcmp(foreign_val, "generic") == 0) {
            eval_set_foreign_generic(glue_vhdl,
                              foreign_stack->foreign_id,
                              src,
                              component,
                              target);
      } 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->glue_vhdl,
                              s->stack->transfer,
                              spec->string_value,
                              src,
                              setparam->op2->bytype.data.name,
                              setparam->op3->bytype.data.name);
            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;

      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);
      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->glue_vhdl);

      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);
            slset_add(s->instance->foreign_drivers, drv.pointer);
      }

      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((unsigned int)severity.univ_int, (char)c.univ_int);
      
      inc_pc(s);
}

static void
eval_abort(struct fauhdli_process *s, const struct opcode *opc)
{
      failure = 1;
      frontend_quit();
      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("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;
      }

      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) {
                  faum_log(FAUM_LOG_ERROR, "fauhdli", __func__,
                        "Endless loop detected.\n");
                  /* can happen only, if an opcode doesn't change PC */
                  faum_log(FAUM_LOG_DEBUG, "fauhdli", __func__,
                        "pc type=%d\n", opcode->kind);
                  assert(0);
            }

            if (ret) {
                  break;
            }
      }
}

/** find the subcontainer with name "name" in container.
 *  looks only through container itself (as top entities should reside there).
 *  @param container container to look through for given text segment.
 *  @param name use subcontainer with this name instead of top-container
 *  @return subcontainer, or NULL if not found.
 */
static struct code_container *
fauhdli_kernel_find_con(struct code_container *container, const char *name)
{
      struct slist_entry *i;
      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)) {
            faum_log(FAUM_LOG_ERROR, "fauhdli", "kernel",
                  "Name \"%s\" too long!\n", name);
            return NULL;
      }

      if (container->sub_containers == NULL) {
            faum_log(FAUM_LOG_ERROR, "fauhdli", "kernel",
                  "cannot locate container %s: no subcontainers to "
                  "top container.\n", name);
            return NULL;
      }

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

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

      /* not found */
      faum_log(FAUM_LOG_ERROR, "fauhdli", "kernel",
            "cannot locate container %s as child of top container.\n",
            name);
      return NULL;
}

/** 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 slset *s,
      universal_integer sim_time,
      struct glue_vhdl *glue_vhdl
)
{
      struct slset_entry *i;

      /* FIXME resolution functions */
      for (i = s->first; i != NULL; i = i->next) {
            driver_propagate(
                  (struct driver *)i->data, 
                  sim_time,
                  glue_vhdl);
      }
}


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->next_event = INT64_MAX;
      vhdl_sched_wakeup(sched, p);
}

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_propagate(drv, sim_time, instance->glue_vhdl);
      }
}

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);

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

      return t_next;
}

/* FIXME interface: remove t_next */
static void
fauhdli_kernel_simulation_cycle(void *_instance)
{
      struct fauhdli *instance = (struct fauhdli *)_instance;
      struct slset_entry *i;
      universal_integer t_next;

      while (true) {
            /* a: advance time, exit if sim_time == INT64_MAX */
            t_next = time_virt();
            instance->sim_time = t_next;
            instance->registers[3].value.univ_int = t_next;

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

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

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

            if (instance->tracer != NULL) {
                  fauhdli_trace_sigs(instance->tracer, 
                              instance->signals);
            }

            /* 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);

            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 */
            time_call_at(t_next, fauhdli_kernel_simulation_cycle, 
                        instance);
            glue_vhdl_enable_event_wakeups(
                              instance->glue_vhdl, 
                              instance, 
                              fauhdli_kernel_simulation_cycle);
            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)) {
            faum_log(FAUM_LOG_WARNING, "fauhdli", "kernel",
                  "No process to run.\n");
            return;
      }

      t_next = fauhdli_kernel_simulation_start(instance);
      time_call_at(t_next, fauhdli_kernel_simulation_cycle, instance);
}

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);
      }

      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);
      }

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

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();
      
      if (instance->container->transfer_segment != NULL) {
            faum_log(FAUM_LOG_ERROR, "fauhdli", "kernel",
                  "Top container needs parameters.\n");
            return;
      }

      container = fauhdli_kernel_find_con(instance->container, top_entity);
      if (container == NULL) {
            return;
      }

      if (container->text_segment == NULL) {
            faum_log(FAUM_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;
            }

            faum_log(FAUM_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;
      }

      if (instance->tracer != NULL) {
            fauhdli_trace_segment(instance, 
                              container->stack_segment,
                              process->stack->data);
      }

      fauhdli_kernel_execute(process);

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

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

      time_call_at(0, fauhdli_kernel_cycle_start, instance);
}

Generated by  Doxygen 1.6.0   Back to index