to_vcd.c 9.88 KB
Newer Older
1 2 3 4 5 6
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <signal.h>
Cedric Roux's avatar
Cedric Roux committed
7
#include <unistd.h>
8 9 10 11 12 13 14
#include "database.h"
#include "utils.h"
#include "handler.h"
#include "config.h"
#include "logger/logger.h"
#include "view/view.h"

Cedric Roux's avatar
Cedric Roux committed
15 16 17 18 19 20
enum var_type {
  DEFAULT,
  VCD_FUNCTION,
  VCD_VARIABLE
};

21
typedef struct {
Cedric Roux's avatar
Cedric Roux committed
22
  enum var_type type;
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
  char *event;
  char *arg;
  char *vcd_name;
  int boolean;
} vcd_vars;

/****************************************************************************/
/* VCD file handling begin                                                  */
/****************************************************************************/

FILE *out;

uint64_t start_time;
int start_time_inited;

void vcd_init(char *file)
{
  out = fopen(file, "w"); if (out == NULL) { perror(file); exit(1); }
}

void vcd_write_header(vcd_vars *v, int n)
{
  int i;

  if (fprintf(out,
"$date\n"
"  January, 1, 1970.\n"
"$end\n"
"$version\n"
"  to_vcd\n"
"$end\n"
Cedric Roux's avatar
Cedric Roux committed
54 55 56
"$timescale 1ns $end\n") <= 0) abort();

  if (fprintf(out,
57
"$scope module logic $end\n") <= 0) abort();
Cedric Roux's avatar
Cedric Roux committed
58 59 60 61 62 63 64
  for (i = 0; i < n; i++)
    if (v[i].type == DEFAULT)
      if (fprintf(out, "$var wire %d %s %s $end\n",
             v[i].boolean ? 1 : 64,
             v[i].vcd_name, v[i].vcd_name) <= 0) abort();
  if (fprintf(out,
"$upscope $end\n") <= 0) abort();
65

Cedric Roux's avatar
Cedric Roux committed
66 67 68 69 70 71 72 73 74 75 76 77
  if (fprintf(out,
"$scope module functions $end\n") <= 0) abort();
  for (i = 0; i < n; i++)
    if (v[i].type == VCD_FUNCTION)
      if (fprintf(out, "$var wire %d %s %s $end\n",
             v[i].boolean ? 1 : 64,
             v[i].vcd_name, v[i].vcd_name) <= 0) abort();
  if (fprintf(out,
"$upscope $end\n") <= 0) abort();

  if (fprintf(out,
"$scope module variables $end\n") <= 0) abort();
78
  for (i = 0; i < n; i++)
Cedric Roux's avatar
Cedric Roux committed
79 80 81 82 83 84
    if (v[i].type == VCD_VARIABLE)
      if (fprintf(out, "$var wire %d %s %s $end\n",
             v[i].boolean ? 1 : 64,
             v[i].vcd_name, v[i].vcd_name) <= 0) abort();
  if (fprintf(out,
"$upscope $end\n") <= 0) abort();
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206

  if (fprintf(out,
"$enddefinitions $end\n"
"$dumpvars\n") <= 0) abort();

  for (i = 0; i < n; i++)
    if (v[i].boolean) {
      if (fprintf(out, "0%s\n", v[i].vcd_name) <= 0) abort();
    } else {
      if (fprintf(out, "b0 %s\n", v[i].vcd_name) <= 0) abort();
    }

  if (fprintf(out,
"$end\n") <= 0) abort();

}

void vcd_end(void)
{
  if (fclose(out)) { perror("error closing VCD file"); exit(1); }
}

char *b64(uint64_t val)
{
  static char v[65];
  char *s = &v[64];
  *s = 0;
  if (val == 0) { s--; *s = '0'; return s; }
  while (val) {
    s--;
    *s = val&1 ? '1' : '0';
    val >>= 1;
  }
  return s;
}

void vcd_dump(char *v)
{
  uint64_t h, m, s, ns;
  char t;
  uint64_t val;
  uint64_t time;
  char var[256];
  if (sscanf(v, "%"SCNu64":%"SCNu64":%"SCNu64".%"SCNu64": %c %"SCNu64" %s",
      &h, &m, &s, &ns, &t, &val, var) != 7)
    goto err;
  time = h*60*60*1000000000 +
            m*60*1000000000 +
               s*1000000000 +
                         ns;
  if (!start_time_inited) {
    start_time = time;
    start_time_inited = 1;
  }
  if (fprintf(out, "#%"PRIu64"\n", time - start_time) <= 0) abort();
  switch (t) {
  case 'b': if (fprintf(out, "%d%s\n", val!=0, var) <= 0) abort(); break;
  case 'l': if (fprintf(out, "b%s %s\n", b64(val), var) <= 0) abort(); break;
  default: goto err;
  }

  return;

err:
  printf("bad vcd_dump line '%s'\n", v);
  exit(1);
}

/****************************************************************************/
/* VCD file handling end                                                    */
/****************************************************************************/


/****************************************************************************/
/* vcd view start                                                           */
/****************************************************************************/

struct vcd_view {
  view common;
};

static void vcd_view_clear(view *this)
{
  /* nothing */
}

static void vcd_view_append(view *_this, char *s)
{
  vcd_dump(s);
}

static view *new_view_vcd(void)
{
  struct vcd_view *ret = calloc(1, sizeof(struct vcd_view));
  if (ret == NULL) abort();
  ret->common.clear = vcd_view_clear;
  ret->common.append = (void (*)(view *, ...))vcd_view_append;
  return (view *)ret;
}

/****************************************************************************/
/* vcd view end                                                             */
/****************************************************************************/

void activate_traces(int socket, int number_of_events, int *is_on)
{
  char t = 1;
  if (socket_send(socket, &t, 1) == -1 ||
      socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
      socket_send(socket, is_on, number_of_events * sizeof(int)) == -1)
    abort();
}

void usage(void)
{
  printf(
"options:\n"
"    -d <database file>           this option is mandatory\n"
"    -o <output file>             this option is mandatory\n"
"    -ip <host>                   connect to given IP address (default %s)\n"
"    -p <port>                    connect to given port (default %d)\n"
"    -b <event> <arg> <vcd name>  trace as binary (0 off, anything else on)\n"
207 208
"    -l <event> <arg> <vcd name>  trace as uint64_t\n"
"    -vcd                         trace all VCD variables and functions\n",
209 210 211 212 213 214 215
  DEFAULT_REMOTE_IP,
  DEFAULT_REMOTE_PORT
  );
  exit(1);
}

int run = 1;
Cedric Roux's avatar
Cedric Roux committed
216
static int socket = -1;
217 218 219 220

void force_stop(int x)
{
  printf("\ngently quit...\n");
Cedric Roux's avatar
Cedric Roux committed
221 222
  close(socket);
  socket = -1;
223 224 225
  run = 0;
}

226
vcd_vars *add_var(vcd_vars *vars, int nvars,
Cedric Roux's avatar
Cedric Roux committed
227
    char *event, char *arg, char *vcd_name, int is_boolean, enum var_type t)
228 229 230 231 232
{
  if (nvars % 64 == 0) {
    vars = realloc(vars, (nvars+64) * sizeof(vcd_vars));
    if (vars == NULL) abort();
  }
Cedric Roux's avatar
Cedric Roux committed
233
  vars[nvars].type = t;
234 235 236 237 238 239 240
  vars[nvars].event = event;
  vars[nvars].arg = arg;
  vars[nvars].vcd_name = vcd_name;
  vars[nvars].boolean = is_boolean;
  return vars;
}

241 242 243 244 245 246 247 248 249 250
int main(int n, char **v)
{
  char *output_filename = NULL;
  char *database_filename = NULL;
  void *database;
  char *ip = DEFAULT_REMOTE_IP;
  int port = DEFAULT_REMOTE_PORT;
  int *is_on;
  int number_of_events;
  int i;
251
  vcd_vars *vars = NULL;
252 253 254 255
  int nvars = 0;
  view *vcd_view;
  event_handler *h;
  logger *textlog;
256
  int all_vcd = 0;
257 258 259 260 261 262 263 264 265 266 267 268 269 270

  /* write on a socket fails if the other end is closed and we get SIGPIPE */
  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) abort();

  for (i = 1; i < n; i++) {
    if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
    if (!strcmp(v[i], "-d"))
      { if (i > n-2) usage(); database_filename = v[++i]; continue; }
    if (!strcmp(v[i], "-o"))
      { if (i > n-2) usage(); output_filename = v[++i]; continue; }
    if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
    if (!strcmp(v[i], "-p"))
      { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
    if (!strcmp(v[i], "-b")) { if(i>n-4)usage();
271 272 273
      char *event    = v[++i];
      char *arg      = v[++i];
      char *vcd_name = v[++i];
Cedric Roux's avatar
Cedric Roux committed
274
      vars = add_var(vars, nvars, event, arg, vcd_name, 1, DEFAULT);
275
      nvars++;
276 277 278
      continue;
    }
    if (!strcmp(v[i], "-l")) { if(i>n-4)usage();
279 280 281
      char *event    = v[++i];
      char *arg      = v[++i];
      char *vcd_name = v[++i];
Cedric Roux's avatar
Cedric Roux committed
282
      vars = add_var(vars, nvars, event, arg, vcd_name, 0, DEFAULT);
283
      nvars++;
284 285
      continue;
    }
286
    if (!strcmp(v[i], "-vcd")) { all_vcd = 1; continue; }
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
    usage();
  }

  if (output_filename == NULL) {
    printf("ERROR; provide an output file (-o)\n");
    exit(1);
  }

  if (database_filename == NULL) {
    printf("ERROR: provide a database file (-d)\n");
    exit(1);
  }

  database = parse_database(database_filename);

  load_config_file(database_filename);

  number_of_events = number_of_ids(database);
  is_on = calloc(number_of_events, sizeof(int));
  if (is_on == NULL) abort();

  h = new_handler(database);

  /* create the view */
  vcd_view = new_view_vcd();

313 314 315 316
  if (all_vcd) {
    /* activate all VCD traces */
    for (i = 0; i < number_of_events; i++) {
      int is_boolean;
Cedric Roux's avatar
Cedric Roux committed
317
      enum var_type type;
318 319
      int prefix_length;
      char *name = event_name_from_id(database, i);
Cedric Roux's avatar
Cedric Roux committed
320
      char *vcd_name;
321 322 323 324 325
      char *var_prefix = "VCD_VARIABLE_";
      char *fun_prefix = "VCD_FUNCTION_";
      if (!strncmp(name, var_prefix, strlen(var_prefix))) {
        prefix_length = strlen(var_prefix);
        is_boolean = 0;
Cedric Roux's avatar
Cedric Roux committed
326
        type = VCD_VARIABLE;
327 328 329
      } else if (!strncmp(name, fun_prefix, strlen(fun_prefix))) {
        prefix_length = strlen(fun_prefix);
        is_boolean = 1;
Cedric Roux's avatar
Cedric Roux committed
330
        type = VCD_FUNCTION;
331 332
      } else
        continue;
Cedric Roux's avatar
Cedric Roux committed
333 334 335 336 337 338
      vcd_name = event_vcd_name_from_id(database, i);
      if (vcd_name == NULL) {
        vcd_name = name+prefix_length;
        printf("WARNING: ID %s does not define VCD_NAME in the file %s, using %s\n",
              name, database_filename, vcd_name);
      }
339
      vars = add_var(vars, nvars,
Cedric Roux's avatar
Cedric Roux committed
340
          name, "value", vcd_name, is_boolean, type);
341 342 343 344
      nvars++;
    }
  }

345 346 347
  /* setup traces */
  for (i = 0; i < nvars; i++) {
    char format[256];
348
    if (strlen(vars[i].arg) + strlen(vars[i].vcd_name) > 256-16) abort();
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
    sprintf(format, "%c [%s] %s",
        vars[i].boolean ? 'b' : 'l',
        vars[i].arg,
        vars[i].vcd_name);
    textlog = new_textlog(h, database, vars[i].event, format);
    logger_add_view(textlog, vcd_view);
    on_off(database, vars[i].event, is_on, 1);
  }

  socket = connect_to(ip, port);

  /* activate selected traces */
  activate_traces(socket, number_of_events, is_on);

  vcd_init(output_filename);
  vcd_write_header(vars, nvars);

  /* exit on ctrl+c and ctrl+z */
  if (signal(SIGQUIT, force_stop) == SIG_ERR) abort();
  if (signal(SIGINT, force_stop) == SIG_ERR) abort();
  if (signal(SIGTSTP, force_stop) == SIG_ERR) abort();

371 372
  OBUF ebuf = { osize: 0, omaxsize: 0, obuf: NULL };

373 374 375
  /* read messages */
  while (run) {
    event e;
376
    e = get_event(socket, &ebuf, database);
377 378 379 380 381 382 383 384
    if (e.type == -1) { printf("disconnected? let's quit gently\n"); break; }
    handle_event(h, e);
  }

  vcd_end();

  return 0;
}