Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

glue-vhdl.c

/* $Id: glue-vhdl.c,v 1.54 2009-01-27 17:06:41 potyra Exp $
 *
 *  Glue layer implementing the foreign interface of fauhdli for FAUmachine,
 *  so that fauhdli can access FAUmachine signals and components.
 *  Must not be called by FAUmachine itself.
 *
 * 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 "config.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <glue-vhdl.h>
#include "glue-log.h"
#include "glue-main.h"
#include "glue-shm.h"
#include "../node-pc/system.h"
#include "sig_boolean.h"
#include "sig_integer.h"
#include "sig_gen.h"
#include "sig_string.h"
#include "simsetup.h"

/* maximum possible signals. Must match system.c! */
#define GLUE_VHDL_MAX_SIGS 10000

/** mapping of one vhdl signal to FAUmachine signal */
00032 struct glue_vhdl_signal_t {
      /** type of signal */
00034       enum sig_gen_type type;
      /** link back to glue_vhdl instance */
00036       struct glue_vhdl *self;
      /** driver pointer (for callback) */
00038       void *_drv;
      /** callback to hand update through to VHDL interpreter */
      void (*drv_update)(void *_drv, union fauhdli_value value);
};

/** parameter passing type */
00044 struct glue_vhdl_param_t {
      /** name of the parameter */
00046       const char *name;
      /** passed argument */
00048       union fauhdli_value arg;
};

/** glue vhdl instance state */
00052 struct glue_vhdl {
      /** all signals */
00054       struct glue_vhdl_signal_t signals[GLUE_VHDL_MAX_SIGS];
      /** all parameters for a foreign procedure call */
00056       struct glue_vhdl_param_t params[100];
      /* FIXME handle nodes */
      int fixme_only_node;
      /** interpreter instance (as from glue_vhdl_enable_event_wakeups) */
00060       void *interpreter;
      /** callback to wakeup interpreter */
      void (*wakeup)(void *_interpreter);
      /** number of instantiated components */
00064       unsigned int inst_comps;
      /** number of instantiated signals */
00066       unsigned int inst_sigs;
      /** number of parameters present */
00068       unsigned int num_params;

      /** stack of component page_id's together with the stack_depth */
      struct {
            /** page_id */
00073             unsigned int page_id;
            /** stack depth where this page_id was created */
00075             unsigned int depth;
      } page_stack[10];
      /** number of top page_stack element (-1 means empty stack) */
00078       int page_top;
};

static void
glue_vhdl_wakeup_interpreter(struct glue_vhdl *s)
{
      /* in delta cycle, or call already scheduled. do nothing */
      if (s->interpreter == NULL) {
            return;
      }

      /* there should be one callback with the old t_next set...
       * remove it */
      assert(s->wakeup != NULL);
      time_call_delete(s->wakeup, s->interpreter);

      /* wake up interpreter now */
      time_call_at(time_virt(), s->wakeup, s->interpreter);
      s->wakeup = NULL;
      s->interpreter = NULL;
}

/** callback to proxy to boolean changes to fauhdli 
 *  @param _s glue_vhdl_signal_t entry.
 *  @param val boolean value.
 */
static void
glue_vhdl_data_get_boolean(void *_s, unsigned int val)
{
      struct glue_vhdl_signal_t *s = (struct glue_vhdl_signal_t *)_s;
      union fauhdli_value f_val;

      f_val.univ_int = val;

      assert(s->_drv != NULL);
      assert(s->drv_update != NULL);
      s->drv_update(s->_drv, f_val);
      
      glue_vhdl_wakeup_interpreter(s->self);
}

static void
glue_vhdl_data_get_integer(void *_s, int val)
{
      struct glue_vhdl_signal_t *s = (struct glue_vhdl_signal_t *)_s;
      union fauhdli_value f_val;

      f_val.univ_int = val;

      assert(s->_drv != NULL);
      assert(s->drv_update != NULL);
      s->drv_update(s->_drv, f_val);

      glue_vhdl_wakeup_interpreter(s->self);
}

/** determine signal kind by name
 *  TODO move to sig_gen.c?
 *  @param name of signal type.
 *  @return signal kind
 */
static enum sig_gen_type
glue_vhdl_determine_type(const char *name)
{
      if (strcmp(name, "boolean") == 0) {
            return SIG_GEN_BOOLEAN;
      } else if (strcmp(name, "integer") == 0) {
            return SIG_GEN_INTEGER;
      } else if (strcmp(name, "cstring") == 0) {
            return SIG_GEN_STRING;
      } else if (strcmp(name, "serial") == 0) {
            return SIG_GEN_SERIAL;
      } else if (strcmp(name, "match") == 0) {
            return SIG_GEN_MATCH;
      } else if (strcmp(name, "video") == 0) {
            return SIG_GEN_VIDEO;
      } else if (strcmp(name, "opt_rgb") == 0) {
            return SIG_GEN_OPT_RGB;
      } else if (strcmp(name, "usb_bus") == 0) {
            return SIG_GEN_USB_BUS;
      } else if (strcmp(name, "eth") == 0) {
            return SIG_GEN_ETH;
      } else if (strcmp(name, "mem_bus") == 0) {
            return SIG_GEN_MEM_BUS;
      } else if (strcmp(name, "pci_bus") == 0) {
            return SIG_GEN_PCI_BUS;
      } else if (strcmp(name, "parallel") == 0) {
            return SIG_GEN_PARALLEL;
      } else if (strcmp(name, "isa_bus") == 0) {
            return SIG_GEN_ISA_BUS;
      } else if (strcmp(name, "ide_bus") == 0) {
            return SIG_GEN_IDE_BUS;
      } else if (strcmp(name, "host_bus") == 0) {
            return SIG_GEN_HOST_BUS;
      } else if (strcmp(name, "agp_bus") == 0) {
            return SIG_GEN_AGP_BUS;
      } else if (strcmp(name, "ps2") == 0) {
            return SIG_GEN_PS2;
      } else if (strcmp(name, "scsi_bus") == 0) {
            return SIG_GEN_SCSI_BUS;
      } else if (strcmp(name, "shugart_bus") == 0) {
            return SIG_GEN_SHUGART_BUS;
      } else if (strcmp(name, "sound") == 0) {
            return SIG_GEN_SOUND;
      } else if (strcmp(name, "std_logic") == 0) {
            return SIG_GEN_STD_LOGIC;
      } else if (strcmp(name, "vga") == 0) {
            return SIG_GEN_VGA;
      } else if (strcmp(name, "power_board") == 0) {
            return SIG_GEN_POWER_BOARD;
      } else if (strcmp(name, "power_device") == 0) {
            return SIG_GEN_POWER_DEVICE;
      } else if (strcmp(name, "dio48") == 0) {
            return SIG_GEN_DIO48;
      } else if (strcmp(name, "dio24") == 0) {
            return SIG_GEN_DIO24;
      }

      faum_log(FAUM_LOG_WARNING, "glue-vhdl", __func__, 
            "Could not determine signal type '%s'.\n", name);
      return SIG_GEN_MANAGE;
}

static void
glue_vhdl_get_arg(
      const struct glue_vhdl *s,
      const char *arg,
      union fauhdli_value *param
)
{
      unsigned int i;
      for (i = 0; i < s->num_params; i++) {
            if (strcmp(arg, s->params[i].name) == 0) {
                  /* found */
                  *param = s->params[i].arg;
                  return;
            }
      }

      faum_log(FAUM_LOG_FATAL, "glue-vhdl", __func__,
            "Argument %s not found, aborting.\n", arg);
      assert(0);
}

/** flatten a VHDL constant unconstraint string array to a c-string
 *  @param s_ptr base pointer to temporary.
 *  @param s_len length of the string.
 *  @return flattened c-string.
 */
static char *
glue_vhdl_flatten_string(union fauhdli_value s_ptr, size_t s_len)
{
      static char buf[16384];
      size_t i;
      union fauhdli_value *walk = (union fauhdli_value *)s_ptr.pointer;

      assert(s_len < sizeof(buf));
      
      for (i = 0; i < s_len; i++, walk++) {
            buf[i] = (char)(walk->univ_int);
      }
      buf[i] = '\0';
      return buf;
}

static void
glue_vhdl_send_string(
      union fauhdli_value dest_driver,
      union fauhdli_value s_array,
      union fauhdli_value s_lb,
      union fauhdli_value s_ub
)
{
      size_t sz; 
      const char *msg;
      unsigned int sig_id;
      void *_sig;
      struct sig_string *sig;
      
      sz = (size_t)(s_ub.univ_int - s_lb.univ_int + 1);
      msg = glue_vhdl_flatten_string(s_array, sz);
      sig_id = fauhdli_get_sig_id_driver(dest_driver.pointer);
      _sig = system_sig_get(sig_id);

      assert(_sig != NULL);
      sig = (struct sig_string *)_sig;


#if 0 /* debug code */
      faum_log(FAUM_LOG_DEBUG, "glue-vhdl", __func__,
            "sending %s to signal %d\n", msg, sig_id);
#endif /* debug code */
      sig_string_set(sig, dest_driver.pointer, msg);
}

static void
glue_vhdl_terminate(void)
{
      frontend_quit();
}

static void
glue_vhdl_toggle_debug(void)
{
      loglevel ^= 1;
}

static void
glue_vhdl_shorcut_drv_out(
      union fauhdli_value s_driver,
      union fauhdli_value comp_array,
      union fauhdli_value comp_lb,
      union fauhdli_value comp_ub,
      union fauhdli_value comp_port_array,
      union fauhdli_value comp_port_lb,
      union fauhdli_value comp_port_ub
)
{
      size_t sz;
      int c_id;
      unsigned int sig_id;
      char *s;

      sz = (size_t)(comp_ub.univ_int - comp_lb.univ_int + 1);
      s = glue_vhdl_flatten_string(comp_array, sz);

      c_id = system_comp_lookup(s);
      assert(c_id >= 0);

      sz = (size_t)(comp_port_ub.univ_int - comp_port_lb.univ_int + 1);
      s = glue_vhdl_flatten_string(comp_port_array, sz);

      sig_id = fauhdli_get_sig_id_driver(s_driver.pointer);
      system_comp_connect(c_id, s, sig_id);
}

static unsigned int
glue_vhdl_handle_page(
      struct glue_vhdl *s, 
      const char *name, 
      unsigned int stack_depth
)
{
      static char buf[16348];
      size_t i;
      const char *page_name;

      /* FIXME convert ':' to '/' in name, cf. simsetup.l 
       */
      assert(strlen(name) < sizeof(buf) - 1);
      for (i = 0; i < strlen(name); i++) {
            if (name[i] == ':') {
                  buf[i] = '/';
            } else {
                  buf[i] = name[i];
            }
      }
      buf[i] = '\0';
      /* end FIXME */

      /* remove all pages of higher level from stack */
      while (    (s->page_top != -1) 
            && (stack_depth < s->page_stack[s->page_top].depth)) {

            s->page_top--;
      }

      page_name = simsetup_get(buf, "page");
      if (page_name == NULL) {
            /* no entry in simulation.setup */
            if (s->page_top == -1) {
                  /* but no page at all yet, create a default one */
                  s->page_top = 0;
                  s->page_stack[0].depth = 0;
                  s->page_stack[0].page_id = 
                        system_page_create("default");
                  return s->page_stack[0].page_id;
            }
            /* reuse existing page */
            return s->page_stack[s->page_top].page_id;
      }
      /* need new page with page_name */
      s->page_top++;
      assert(s->page_top 
             < (sizeof(s->page_stack)/sizeof(s->page_stack[0])));
      s->page_stack[s->page_top].depth = stack_depth;
      s->page_stack[s->page_top].page_id = system_page_create(page_name);

      return s->page_stack[s->page_top].page_id;
}

void
glue_vhdl_proc_set(
      struct glue_vhdl *s,
      const char *proc,
      const char *param, 
      union fauhdli_value value
)
{
      assert(s->num_params < (sizeof(s->params) / sizeof(s->params[0])));
      s->params[s->num_params].name = param;
      s->params[s->num_params].arg = value;
      s->num_params++;
}


void
glue_vhdl_proc_call(struct glue_vhdl *s, const char *proc)
{
      if (strcmp(proc, "send_string") == 0) {
            union fauhdli_value dest_driver;
            union fauhdli_value s_array;
            union fauhdli_value s_lb;
            union fauhdli_value s_ub;

            glue_vhdl_get_arg(s, "dest__driver__", &dest_driver);
            glue_vhdl_get_arg(s, "s", &s_array);
            glue_vhdl_get_arg(s, "s_lb_0", &s_lb);
            glue_vhdl_get_arg(s, "s_ub_0", &s_ub);

            glue_vhdl_send_string(dest_driver, s_array, s_lb, s_ub);

      } else if (strcmp(proc, "terminate") == 0) {
            glue_vhdl_terminate();

      } else if (strcmp(proc, "debug") == 0) {
            glue_vhdl_toggle_debug();

      } else if ((strcmp(proc, "shortcut_bool_out") == 0)
            || (strcmp(proc, "shortcut_int_out")  == 0)) {
            union fauhdli_value s_driver;
            union fauhdli_value comp_array;
            union fauhdli_value comp_lb;
            union fauhdli_value comp_ub;
            union fauhdli_value comp_port_array;
            union fauhdli_value comp_port_lb;
            union fauhdli_value comp_port_ub;

            glue_vhdl_get_arg(s, "s__driver__", &s_driver);
            glue_vhdl_get_arg(s, "comp", &comp_array);
            glue_vhdl_get_arg(s, "comp_lb_0", &comp_lb);
            glue_vhdl_get_arg(s, "comp_ub_0", &comp_ub);
            glue_vhdl_get_arg(s, "comp_port", &comp_port_array);
            glue_vhdl_get_arg(s, "comp_port_lb_0", &comp_port_lb);
            glue_vhdl_get_arg(s, "comp_port_ub_0", &comp_port_ub);

            glue_vhdl_shorcut_drv_out(
                        s_driver,
                        comp_array,
                        comp_lb,
                        comp_ub,
                        comp_port_array,
                        comp_port_lb,
                        comp_port_ub);
      } else {
            /* not implemented */
            faum_log(FAUM_LOG_FATAL, "glue-vhdl", __func__,
                  "Trying to call unimplemented foreign procedure "
                  "%s.\n", proc);
            assert(0);
      }

      s->num_params = 0;
}


void
glue_vhdl_set(
      struct glue_vhdl *s, 
      unsigned int sig_id, 
      union fauhdli_value data,
      void *drv
)
{
      void *_sig;

      assert(sig_id < GLUE_VHDL_MAX_SIGS);

      _sig = system_sig_get(sig_id);
      assert(_sig != NULL);
      
      switch (s->signals[sig_id].type) {
      case SIG_GEN_BOOLEAN:
            sig_boolean_set(
                  (struct sig_boolean *)_sig, 
                  drv, 
                  (unsigned int)data.univ_int);
            break;

      case SIG_GEN_INTEGER:
            sig_integer_set(
                  (struct sig_integer *)_sig,
                  drv,
                  (int)data.univ_int);
            break;

      case SIG_GEN_BOOLEAN_OR:
      case SIG_GEN_MANAGE:
      case SIG_GEN_MATCH:
      case SIG_GEN_STRING:
      case SIG_GEN_SERIAL:
      case SIG_GEN_VIDEO:
      case SIG_GEN_OPT_RGB:
      case SIG_GEN_USB_BUS:
      case SIG_GEN_ETH:
      case SIG_GEN_MEM_BUS:
      case SIG_GEN_PCI_BUS:
      case SIG_GEN_PARALLEL:
      case SIG_GEN_ISA_BUS:
      case SIG_GEN_IDE_BUS:
      case SIG_GEN_HOST_BUS:
      case SIG_GEN_AGP_BUS:
      case SIG_GEN_PS2:
      case SIG_GEN_SCSI_BUS:
      case SIG_GEN_SHUGART_BUS:
      case SIG_GEN_SOUND:
      case SIG_GEN_STD_LOGIC:
      case SIG_GEN_VGA:
      case SIG_GEN_POWER_BOARD:
      case SIG_GEN_POWER_DEVICE:
      case SIG_GEN_DIO48:
      case SIG_GEN_DIO24:
            /* TODO */
            assert(0);
            break;
      }
}

void
glue_vhdl_connect_out(
      struct glue_vhdl *s, 
      unsigned int sig_id,
      union fauhdli_value init,
      void *drv
)
{
      void *_sig;
      assert(sig_id < GLUE_VHDL_MAX_SIGS);
      _sig = system_sig_get(sig_id);

      switch (s->signals[sig_id].type) {
      case SIG_GEN_BOOLEAN:
            sig_boolean_connect_out((struct sig_boolean *)_sig,
                              drv,
                              (unsigned int)init.univ_int);
            break;

      case SIG_GEN_INTEGER:
            sig_integer_connect_out((struct sig_integer *)_sig,
                              drv,
                              (int)init.univ_int);
            break;

      case SIG_GEN_STRING:
            /* don't need to connect_out to string */
            break;

      case SIG_GEN_BOOLEAN_OR:
      case SIG_GEN_MANAGE:
      case SIG_GEN_MATCH:
      case SIG_GEN_SERIAL:
      case SIG_GEN_VIDEO:
      case SIG_GEN_OPT_RGB:
      case SIG_GEN_USB_BUS:
      case SIG_GEN_ETH:
      case SIG_GEN_MEM_BUS:
      case SIG_GEN_PCI_BUS:
      case SIG_GEN_PARALLEL:
      case SIG_GEN_ISA_BUS:
      case SIG_GEN_IDE_BUS:
      case SIG_GEN_HOST_BUS:
      case SIG_GEN_AGP_BUS:
      case SIG_GEN_PS2:
      case SIG_GEN_SCSI_BUS:
      case SIG_GEN_SHUGART_BUS:
      case SIG_GEN_SOUND:
      case SIG_GEN_STD_LOGIC:
      case SIG_GEN_VGA:
      case SIG_GEN_POWER_BOARD:
      case SIG_GEN_POWER_DEVICE:
      case SIG_GEN_DIO48:
      case SIG_GEN_DIO24:

            assert(0);
            break;
      }
}

void
glue_vhdl_connect_in(
      struct glue_vhdl *s,
      unsigned int sig_id,
      void (*drv_update)(void *_drv, union fauhdli_value value),
      void *drv
)
{
      void *_sig;

      assert(sig_id < GLUE_VHDL_MAX_SIGS);

      _sig = system_sig_get(sig_id);
      assert(_sig != NULL);

      s->signals[sig_id]._drv = drv;
      s->signals[sig_id].drv_update = drv_update;
      
      switch (s->signals[sig_id].type) {
      case SIG_GEN_BOOLEAN: {
            static const struct sig_boolean_funcs f = {
                  .set = glue_vhdl_data_get_boolean
            };

            sig_boolean_connect_in(
                  (struct sig_boolean *)_sig,
                  &s->signals[sig_id],
                  &f);
            break;
          }
      case SIG_GEN_INTEGER: {
            static const struct sig_integer_funcs f = {
                  .set = glue_vhdl_data_get_integer
            };

            sig_integer_connect_in(
                  (struct sig_integer *)_sig,
                  &s->signals[sig_id],
                  &f);
            break;
          }
      case SIG_GEN_BOOLEAN_OR:
      case SIG_GEN_MANAGE:
      case SIG_GEN_MATCH:
      case SIG_GEN_STRING:
      case SIG_GEN_SERIAL:
      case SIG_GEN_VIDEO:
      case SIG_GEN_OPT_RGB:
      case SIG_GEN_USB_BUS:
      case SIG_GEN_ETH:
      case SIG_GEN_MEM_BUS:
      case SIG_GEN_PCI_BUS:
      case SIG_GEN_PARALLEL:
      case SIG_GEN_ISA_BUS:
      case SIG_GEN_IDE_BUS:
      case SIG_GEN_HOST_BUS:
      case SIG_GEN_AGP_BUS:
      case SIG_GEN_PS2:
      case SIG_GEN_SCSI_BUS:
      case SIG_GEN_SHUGART_BUS:
      case SIG_GEN_SOUND:
      case SIG_GEN_STD_LOGIC:
      case SIG_GEN_VGA:
      case SIG_GEN_POWER_BOARD:
      case SIG_GEN_POWER_DEVICE:
      case SIG_GEN_DIO48:
      case SIG_GEN_DIO24:
            /* TODO */
            assert(0);
            break;
      }
}

unsigned int
glue_vhdl_create_signal(
      struct glue_vhdl *s, 
      const char *type,
      const char *name
)
{
      unsigned int sig_id;
      enum sig_gen_type t = glue_vhdl_determine_type(type);
      /* FIXME need to special case cstring, due to conflicting type
       *       string */
      switch (t) {
      case SIG_GEN_STRING:
            sig_id = system_sig_create("string", name);
            break;

      default:
            sig_id = system_sig_create(type, name);
      }
      assert(sig_id < GLUE_VHDL_MAX_SIGS);

      s->signals[sig_id].type = t;
      s->signals[sig_id].self = s;
      s->signals[sig_id]._drv = NULL;
      s->signals[sig_id].drv_update = NULL;

      /* signal id's must be sequential */
      assert(s->inst_sigs == sig_id);
      s->inst_sigs++;

      return sig_id;
}

void
glue_vhdl_enable_event_wakeups(
      struct glue_vhdl *s, 
      void *_instance, 
      void (*cb)(void *_inst)
)
{
      s->interpreter = _instance;
      s->wakeup = cb;
}

unsigned int
glue_vhdl_comp_create(
      struct glue_vhdl *s, 
      const char *type, 
      const char *name,
      unsigned int stack_depth
)
{
      /* need secrect "manage" signal */
      unsigned int sig_id = system_sig_create("manage", name);
      unsigned int comp_id;
      unsigned int page_id;

      /* FIXME */
      if (s->fixme_only_node == -1) {
            s->fixme_only_node = system_node_create("only_node");
      }

      page_id = glue_vhdl_handle_page(s, name, stack_depth);

#if 0
      faum_log(FAUM_LOG_DEBUG, "glue-vhdl", __func__,
            "creating component %s of type %s\n", name, type);
#endif
      comp_id = system_comp_create(
                        type, 
                        name, 
                        s->fixme_only_node, 
                        page_id);

      /* connect hidden manage signal */
      system_comp_port_connect(comp_id, "manage", sig_id);

      /* manage signal is a glue-vhdl created signal, too! */
      assert(s->inst_sigs == sig_id);
      s->inst_sigs++;

      /* component ids must be sequential */
      assert(s->inst_comps == comp_id);
      s->inst_comps++;

      return comp_id;
}

void
glue_vhdl_comp_init(struct glue_vhdl *s, unsigned int comp)
{
      system_comp_init(comp);
}

void
glue_vhdl_comp_port_connect(
      struct glue_vhdl *s,
      unsigned int comp,
      const char *port,
      unsigned int sig
)
{
      system_comp_port_connect(comp, port, sig);
}

void
glue_vhdl_comp_generic_set(
      struct glue_vhdl *s,
      unsigned int comp,
      const char *generic,
      union fauhdli_value val
)
{
      char buf[30];
      int ret;

      /* TODO float generics, composites */
      ret = snprintf(buf, sizeof(buf), "%" PRIi64, val.univ_int);
      assert(ret < sizeof(buf));

      system_comp_generic_set(comp, generic, buf);
}

struct glue_vhdl *
glue_vhdl_create(void)
{
      struct glue_vhdl *s;

      shm_create("glue_vhdl", 1, sizeof(struct glue_vhdl));
      s = shm_map("glue_vhdl", 1, sizeof(struct glue_vhdl), 0);

      s->fixme_only_node = -1;
      s->interpreter = NULL;
      s->wakeup = NULL;
      s->inst_comps = 0;
      s->inst_sigs = 0;
      s->num_params = 0;
      s->page_top = -1;
      return s;
}

void
glue_vhdl_destroy(struct glue_vhdl *s)
{
      unsigned int i;

      /* tear down components */
      for (i = 0; i < s->inst_comps; i++) {
            system_comp_exit(i);
      }
      for (i = 0; i < s->inst_comps; i++) {
            system_comp_destroy(i);
      }
      /* tear down signals */
      for (i = 0; i < s->inst_sigs; i++) {
            system_sig_destroy(i);
      }

      /* FIXME tear down pages */

      shm_unmap(s, sizeof(struct glue_vhdl));
      shm_destroy("glue_vhdl", 1);
}

Generated by  Doxygen 1.6.0   Back to index