Commit 4b8b599fa8544cb5a9d0adfc235e256e6b8548fb
authorMichel Pollet <buserror@gmail.com>
Thu, 23 Feb 2017 10:53:55 +0000 (10:53 +0000)
committerMichel Pollet <buserror@gmail.com>
Thu, 2 Mar 2017 17:31:15 +0000 (17:31 +0000)
Massive update here. Now allow reading from a VCD file, as long as the
traces are named in a way that allows us to connect an IRQ. Typically,
you can send pin values to for example PORTB5 by naming the trace
'iogB_5' -- you can use sigrock for example to capture traces from
real hardware, and 'replay' it in simavr.

Also since I was there, reworked the whole module to use a 'proper' FIFO
and simplified the output generation too.

There are a few 'TODO' left, but it's functional.

Signed-off-by: Michel Pollet <buserror@gmail.com>
2 files changed:
simavr/sim/sim_vcd_file.c
simavr/sim/sim_vcd_file.h

index d710967617de4854abcf2db1ec0028d9735ad77a..18ae4b1f7a88d7d43836f18ed44af3b0f8717873 100644 (file)
        You should have received a copy of the GNU General Public License
        along with simavr.  If not, see <http://www.gnu.org/licenses/>.
  */
-
+#define _GNU_SOURCE /* for strdupa */
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include<inttypes.h>
+#include <inttypes.h>
+#include <ctype.h>
 #include "sim_vcd_file.h"
 #include "sim_avr.h"
 #include "sim_time.h"
-
-void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param);
-
-int avr_vcd_init(struct avr_t * avr, const char * filename, avr_vcd_t * vcd, uint32_t period)
+#include "sim_utils.h"
+
+DEFINE_FIFO(avr_vcd_log_t, avr_vcd_fifo);
+
+static void
+_avr_vcd_notify(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param);
+
+int
+avr_vcd_init(
+               struct avr_t * avr,
+               const char * filename,
+               avr_vcd_t * vcd,
+               uint32_t period)
 {
        memset(vcd, 0, sizeof(avr_vcd_t));
        vcd->avr = avr;
-       strncpy(vcd->filename, filename, sizeof(vcd->filename));
+       vcd->filename = strdup(filename);
        vcd->period = avr_usec_to_cycles(vcd->avr, period);
 
-       // No longer initializes the IRQs here, they can be initialized on the
-       // fly when traces are added
-
        return 0;
 }
 
-void avr_vcd_close(avr_vcd_t * vcd)
+/*
+ * Parse a VCD 'timing' line. The lines are assumed to be:
+ * #<absolute timestamp>[\n][<value x/0/1><signal alias character>|
+ *             b[x/0/1]?<space><signal alias character]+
+ * For example:
+ * #1234 1' 0$
+ * Or:
+ * #1234
+ * b1101x1 '
+ * 0$
+ *
+ * This function tries to handle this transparently, and pushes the
+ * signal/values into the FIFO for processing by the timer when
+ * convenient.
+ * NOTE: Add 'floating' support here. Also, FIX THE TIMING.
+ */
+static avr_cycle_count_t
+avr_vcd_input_parse_line(
+               avr_vcd_t * vcd,
+               argv_p v )
 {
-       avr_vcd_stop(vcd);
+       avr_cycle_count_t res = 0;
+       int vi = 0;
+
+       if (v->argc == 0)
+               return res;
+
+       if (v->argv[0][0] == '#') {
+               res = atoll(v->argv[0] + 1) * vcd->vcd_to_us;
+               vcd->start = vcd->period;
+               vcd->period = res;
+               vi++;
+       }
+       for (int i = vi; i < v->argc; i++) {
+               char * a = v->argv[i];
+               uint32_t val = 0;
+               int floating = 0;
+               char name = 0;
+               int sigindex = -1;
+
+               if (*a == 'b')
+                       a++;
+               while (*a) {
+                       if (*a == 'x') {
+                               val <<= 1;
+                               floating |= (floating << 1) | 1;
+                       } else if (*a == '0' || *a == '1') {
+                               val = (val << 1) | (*a - '0');
+                               floating <<= 1;
+                       } else {
+                               name = *a;
+                               break;
+                       }
+                       a++;
+               }
+               if (!name && (i < v->argc - 1)) {
+                       const char *n = v->argv[i+1];
+                       if (strlen(n) == 1) {
+                               // we've got a name, it was not attached
+                               name = *n;
+                               i++;    // skip that one
+                       }
+               }
+               if (name) {
+                       for (int si = 0;
+                                               si < vcd->signal_count &&
+                                               sigindex == -1; si++) {
+                               if (vcd->signal[si].alias == name)
+                                       sigindex = si;
+                       }
+               }
+               if (sigindex == -1) {
+                       printf("Signal name '%c' value %x not found\n",
+                                       name? name : '?', val);
+                       continue;
+               }
+               avr_vcd_log_t e = {
+                               .when = vcd->period,
+                               .sigindex = sigindex,
+                               .value = val,
+               };
+               avr_vcd_fifo_write(&vcd->log, e);
+       }
+       return res;
 }
 
-void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param)
+/*
+ * Read some signals from the file and fill the FIFO with it, we read
+ * a completely arbitrary amount of stuff to fill the FIFO reasonably well
+ */
+static int
+avr_vcd_input_read(
+               avr_vcd_t * vcd )
 {
-       avr_vcd_t * vcd = (avr_vcd_t *)param;
-       if (!vcd->output)
-               return;
+       char line[1024];
+
+       while (fgets(line, sizeof(line), vcd->input) &&
+                       avr_vcd_fifo_get_read_size(&vcd->log) < 128) {
+               if (!line[0])   // technically can't happen, but make sure next line works
+                       continue;
+               vcd->input_line = argv_parse(vcd->input_line, line);
+               /* stop once we found a new timestamp */
+               avr_vcd_input_parse_line(vcd, vcd->input_line);
+       }
+       return avr_vcd_fifo_isempty(&vcd->log);
+}
+
+/*
+ * This is called when we need to change the state of one or more IRQ,
+ * so look in the FIFO to know 'our' stamp time, read as much as we can
+ * that is still on that same timestamp.
+ * When when the FIFO content has too far in the future, re-schedule the
+ * timer for that time and shoot of.
+ * Also try to top up the FIFO with new read stuff when it's drained
+ */
+static avr_cycle_count_t
+_avr_vcd_input_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
+{
+       avr_vcd_t * vcd = param;
+
+       // get some more if needed
+       if (avr_vcd_fifo_get_read_size(&vcd->log) < (vcd->signal_count * 16))
+               avr_vcd_input_read(vcd);
+
+       if (avr_vcd_fifo_isempty(&vcd->log)) {
+               printf("%s DONE but why are we here?\n", __func__);
+               return 0;
+       }
+
+       avr_vcd_log_t log = avr_vcd_fifo_read_at(&vcd->log, 0);
+       uint64_t stamp = log.when;
+       while (!avr_vcd_fifo_isempty(&vcd->log)) {
+               log = avr_vcd_fifo_read_at(&vcd->log, 0);
+               if (log.when != stamp)  // leave those in the FIFO
+                       break;
+               // we already have it
+               avr_vcd_fifo_read_offset(&vcd->log, 1);
+               avr_vcd_signal_p signal = &vcd->signal[log.sigindex];
+               avr_raise_irq(&signal->irq, log.value);
+       }
+
+       if (avr_vcd_fifo_isempty(&vcd->log)) {
+               AVR_LOG(vcd->avr, LOG_TRACE,
+                               "%s Finished reading, ending simavr\n",
+                               vcd->filename);
+               avr->state = cpu_Done;
+               return 0;
+       }
+       log = avr_vcd_fifo_read_at(&vcd->log, 0);
+
+       when += avr_usec_to_cycles(avr, log.when - stamp);
+
+       return when;
+}
 
-       /*
-        * buffer starts empty, the first trace will resize it to AVR_VCD_LOG_CHUNK_SIZE,
-        * further growth will resize it accordingly.
-        */
-       if (vcd->logindex >= vcd->logsize) {
-               vcd->logsize += AVR_VCD_LOG_CHUNK_SIZE;
-               vcd->log = (avr_vcd_log_p)realloc(vcd->log, vcd->logsize * sizeof(vcd->log[0]));
-               AVR_LOG(vcd->avr, LOG_TRACE, "%s trace buffer resized to %d\n",
-                               __func__, (int)vcd->logsize);
-               if ((vcd->logsize / AVR_VCD_LOG_CHUNK_SIZE) == 8) {
-                       AVR_LOG(vcd->avr, LOG_WARNING, "%s log size runnaway (%d) flush problem?\n",
-                                       __func__, (int)vcd->logsize);
+int
+avr_vcd_init_input(
+               struct avr_t * avr,
+               const char * filename,  // filename to read
+               avr_vcd_t * vcd )               // vcd struct to initialize
+{
+       memset(vcd, 0, sizeof(avr_vcd_t));
+       vcd->avr = avr;
+       vcd->filename = strdup(filename);
+
+       vcd->input = fopen(vcd->filename, "r");
+       if (!vcd->input) {
+               perror(filename);
+               return -1;
+       }
+       char line[1024];
+       argv_p v = NULL;
+
+       while (fgets(line, sizeof(line), vcd->input)) {
+               if (!line[0])   // technically can't happen, but make sure next line works
+                       continue;
+               v = argv_parse(v, line);
+
+               // we are done reading headers, got our first timestamp
+               if (v->line[0] == '#') {
+                       vcd->start = 0;
+                       avr_vcd_input_parse_line(vcd, v);
+                       avr_cycle_timer_register_usec(vcd->avr,
+                                               vcd->period, _avr_vcd_input_timer, vcd);
+                       break;
                }
-               if (!vcd->log) {
-                       AVR_LOG(vcd->avr, LOG_ERROR, "%s log resizing, out of memory (%d)!\n",
-                                       __func__, (int)vcd->logsize);
-                       vcd->logsize = 0;
-                       return;
+               // ignore multiline stuff
+               if (v->line[0] != '$')
+                       continue;
+
+               const char * end = !strcmp(v->argv[v->argc - 1], "$end") ?
+                                                               v->argv[v->argc - 1] : NULL;
+               const char *keyword = v->argv[0];
+
+               if (keyword == end)
+                       keyword = NULL;
+               if (!keyword)
+                       continue;
+
+               if (!strcmp(keyword, "$timescale")) {
+                       double cnt = 0;
+                       vcd->vcd_to_us = 1;
+                       char *si = v->argv[1];
+                       while (si && *si && isdigit(*si))
+                               cnt = (cnt * 10) + (*si++ - '0');
+                       while (*si == ' ')
+                               si++;
+                       if (!*si)
+                               si = v->argv[2];
+               //      if (!strcmp(si, "ns")) // TODO: Check that,
+               //              vcd->vcd_to_us = cnt;
+               //      printf("cnt %dus; unit %s\n", (int)cnt, si);
+               } else if (!strcmp(keyword, "$var")) {
+                       const char *name = v->argv[4];
+
+                       vcd->signal[vcd->signal_count].alias = v->argv[3][0];
+                       vcd->signal[vcd->signal_count].size = atoi(v->argv[2]);
+                       strncpy(vcd->signal[vcd->signal_count].name, name,
+                                               sizeof(vcd->signal[0].name));
+
+                       vcd->signal_count++;
                }
        }
-       avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
-       avr_vcd_log_t *l = &vcd->log[vcd->logindex++];
-       l->signal = s;
-       l->when = vcd->avr->cycle;
-       l->value = value;
+       // reuse this one
+       vcd->input_line = v;
+
+       for (int i = 0; i < vcd->signal_count; i++) {
+               AVR_LOG(vcd->avr, LOG_TRACE, "%s %2d '%c' %s : size %d\n",
+                               __func__, i,
+                               vcd->signal[i].alias, vcd->signal[i].name,
+                               vcd->signal[i].size);
+               /* format is <four-character ioctl>[_<IRQ index>] */
+               if (strlen(vcd->signal[i].name) >= 4) {
+                       char *dup = strdupa(vcd->signal[i].name);
+                       char *ioctl = strsep(&dup, "_");
+                       int index = 0;
+                       if (dup)
+                               index = atoi(dup);
+                       if (strlen(ioctl) == 4) {
+                               uint32_t ioc = AVR_IOCTL_DEF(
+                                                                       ioctl[0], ioctl[1], ioctl[2], ioctl[3]);
+                               avr_irq_t * irq = avr_io_getirq(vcd->avr, ioc, index);
+                               printf("  irq %p\n", irq);
+                               if (irq) {
+                                       vcd->signal[i].irq.flags = IRQ_FLAG_INIT;
+                                       avr_connect_irq(&vcd->signal[i].irq, irq);
+                               } else
+                                       AVR_LOG(vcd->avr, LOG_WARNING,
+                                                       "%s IRQ was not found\n",
+                                                       vcd->signal[i].name);
+                               continue;
+                       }
+                       AVR_LOG(vcd->avr, LOG_WARNING,
+                                       "%s is an invalid IRQ format\n",
+                                       vcd->signal[i].name);
+               }
+       }
+       return 0;
 }
 
+void
+avr_vcd_close(
+               avr_vcd_t * vcd)
+{
+       avr_vcd_stop(vcd);
+
+       /* dispose of any link and hooks */
+       for (int i = 0; i < vcd->signal_count; i++) {
+               avr_vcd_signal_t * s = &vcd->signal[i];
+
+               avr_free_irq(&s->irq, 1);
+       }
 
-static char * _avr_vcd_get_float_signal_text(avr_vcd_signal_t * s, char * out)
+       if (vcd->filename) {
+               free(vcd->filename);
+               vcd->filename = NULL;
+       }
+}
+
+static char *
+_avr_vcd_get_float_signal_text(
+               avr_vcd_signal_t * s,
+               char * out)
 {
        char * dst = out;
 
@@ -100,7 +356,11 @@ static char * _avr_vcd_get_float_signal_text(avr_vcd_signal_t * s, char * out)
        return out;
 }
 
-static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out, uint32_t value)
+static char *
+_avr_vcd_get_signal_text(
+               avr_vcd_signal_t * s,
+               char * out,
+               uint32_t value)
 {
        char * dst = out;
 
@@ -116,7 +376,9 @@ static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out, uint32_
        return out;
 }
 
-static void avr_vcd_flush_log(avr_vcd_t * vcd)
+static void
+avr_vcd_flush_log(
+               avr_vcd_t * vcd)
 {
 #if AVR_VCD_MAX_SIGNALS > 32
        uint64_t seen = 0;
@@ -126,45 +388,84 @@ static void avr_vcd_flush_log(avr_vcd_t * vcd)
        uint64_t oldbase = 0;   // make sure it's different
        char out[48];
 
-       if (!vcd->logindex || !vcd->output)
+       if (avr_vcd_fifo_isempty(&vcd->log) || !vcd->output)
                return;
-//     printf("avr_vcd_flush_log %d\n", vcd->logindex);
-
-
-       for (uint32_t li = 0; li < vcd->logindex; li++) {
-               avr_vcd_log_t *l = &vcd->log[li];
-               uint64_t base = avr_cycles_to_nsec(vcd->avr, l->when - vcd->start);     // 1ns base
 
-               // if that trace was seen in this nsec already, we fudge the base time
-               // to make sure the new value is offset by one nsec, to make sure we get
-               // at least a small pulse on the waveform
-               // This is a bit of a fudge, but it is the only way to represent very
-               // short"pulses" that are still visible on the waveform.
-               if (base == oldbase && seen & (1 << l->signal->irq.irq))
+       while (!avr_vcd_fifo_isempty(&vcd->log)) {
+               avr_vcd_log_t l = avr_vcd_fifo_read(&vcd->log);
+               // 1ns base
+               uint64_t base = avr_cycles_to_nsec(vcd->avr, l.when - vcd->start);
+
+               /*
+                * if that trace was seen in this nsec already, we fudge the
+                * base time to make sure the new value is offset by one nsec,
+                * to make sure we get at least a small pulse on the waveform.
+                *
+                * This is a bit of a fudge, but it is the only way to represent
+                * very short "pulses" that are still visible on the waveform.
+                */
+               if (base == oldbase &&
+                               (seen & (1 << l.sigindex)))
                        base++; // this forces a new timestamp
 
-               if (base > oldbase || li == 0) {
+               if (base > oldbase || !seen) {
                        seen = 0;
                        fprintf(vcd->output, "#%" PRIu64  "\n", base);
                        oldbase = base;
                }
-               seen |= (1 << l->signal->irq.irq);      // mark this trace as seen for this timestamp
-               fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(l->signal, out, l->value));
+               // mark this trace as seen for this timestamp
+               seen |= (1 << l.sigindex);
+               fprintf(vcd->output, "%s\n",
+                               _avr_vcd_get_signal_text(&vcd->signal[l.sigindex],
+                                               out, l.value));
        }
-       vcd->logindex = 0;
 }
 
-static avr_cycle_count_t _avr_vcd_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
+static avr_cycle_count_t
+_avr_vcd_timer(
+               struct avr_t * avr,
+               avr_cycle_count_t when,
+               void * param)
 {
        avr_vcd_t * vcd = param;
        avr_vcd_flush_log(vcd);
        return when + vcd->period;
 }
 
-int avr_vcd_add_signal(avr_vcd_t * vcd,
-       avr_irq_t * signal_irq,
-       int signal_bit_size,
-       const char * name )
+static void
+_avr_vcd_notify(
+               struct avr_irq_t * irq,
+               uint32_t value,
+               void * param)
+{
+       avr_vcd_t * vcd = (avr_vcd_t *)param;
+
+       if (!vcd->output)
+               return;
+
+       avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
+       avr_vcd_log_t l = {
+               .sigindex = s->irq.irq,
+               .when = vcd->avr->cycle,
+               .value = value,
+       };
+       if (avr_vcd_fifo_isfull(&vcd->log)) {
+               AVR_LOG(vcd->avr, LOG_WARNING,
+                               "%s FIFO Overload, flushing!\n",
+                               __func__);
+               /* Decrease period by a quarter, for next time */
+               vcd->period -= vcd->period >> 2;
+               avr_vcd_flush_log(vcd);
+       }
+       avr_vcd_fifo_write(&vcd->log, l);
+}
+
+int
+avr_vcd_add_signal(
+               avr_vcd_t * vcd,
+               avr_irq_t * signal_irq,
+               int signal_bit_size,
+               const char * name )
 {
        if (vcd->signal_count == AVR_VCD_MAX_SIGNALS)
                return -1;
@@ -191,8 +492,20 @@ int avr_vcd_add_signal(avr_vcd_t * vcd,
 }
 
 
-int avr_vcd_start(avr_vcd_t * vcd)
+int
+avr_vcd_start(
+               avr_vcd_t * vcd)
 {
+       vcd->start = vcd->avr->cycle;
+       avr_vcd_fifo_reset(&vcd->log);
+
+       if (vcd->input) {
+               /*
+                * nothing to do here, the first cycle timer will take care
+                * if it.
+                */
+               return 0;
+       }
        if (vcd->output)
                avr_vcd_stop(vcd);
        vcd->output = fopen(vcd->filename, "w");
@@ -216,21 +529,29 @@ int avr_vcd_start(avr_vcd_t * vcd)
        for (int i = 0; i < vcd->signal_count; i++) {
                avr_vcd_signal_t * s = &vcd->signal[i];
                char out[48];
-               fprintf(vcd->output, "%s\n", _avr_vcd_get_float_signal_text(s, out));
+               fprintf(vcd->output, "%s\n",
+                               _avr_vcd_get_float_signal_text(s, out));
        }
        fprintf(vcd->output, "$end\n");
-       vcd->logindex = 0;
-       vcd->start = vcd->avr->cycle;
        avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);
        return 0;
 }
 
-int avr_vcd_stop(avr_vcd_t * vcd)
+int
+avr_vcd_stop(
+               avr_vcd_t * vcd)
 {
        avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
+       avr_cycle_timer_cancel(vcd->avr, _avr_vcd_input_timer, vcd);
 
        avr_vcd_flush_log(vcd);
 
+       if (vcd->input_line)
+               free(vcd->input_line);
+       vcd->input_line = NULL;
+       if (vcd->input)
+               fclose(vcd->input);
+       vcd->input = NULL;
        if (vcd->output)
                fclose(vcd->output);
        vcd->output = NULL;
index 0c6b77e4e57b0c10c7d8b8385b700c30d53fedfc..221081d6fb15f73420595a6fec9fe4591b4490ba 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <stdio.h>
 #include "sim_irq.h"
+#include "fifo_declare.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -34,62 +35,92 @@ extern "C" {
 
 /*
  * Value Change dump module for simavr.
- * 
+ *
  * This structure registers IRQ change hooks to various "source" IRQs
- * and dumps their values (if changed) at certain intervals into the VCD file
+ * and dumps their values (if changed) at certain intervals into the VCD
+ * file.
+ *
+ * It can also do the reverse, load a VCD file generated by for example
+ * sigrock signal analyzer, and 'replay' digital input with the proper
+ * timing.
+ *
+ * TODO: Add support for 'looping' a VCD input.
  */
 
 #define AVR_VCD_MAX_SIGNALS 64
 
 typedef struct avr_vcd_signal_t {
-       avr_irq_t       irq;            // receiving IRQ
-       char    alias;                  // vcd one character alias
-       int             size;                   // in bits
-       char    name[32];               // full human name      
-} avr_vcd_signal_t;
+       /*
+        * For VCD output this is the IRQ we receive new values from.
+        * For VCD input, this is the IRQ we broadcast the values to
+        */
+       avr_irq_t               irq;
+       char                    alias;                  // vcd one character alias
+       uint8_t                 size;                   // in bits
+       char                    name[32];               // full human name
+} avr_vcd_signal_t, *avr_vcd_signal_p;
 
 typedef struct avr_vcd_log_t {
-       uint64_t        when;
-       avr_vcd_signal_t * signal;
-       uint32_t value;
+       uint64_t                when;
+       uint64_t                        sigindex : 8,                   // index in signal table
+                                       value : 32;
 } avr_vcd_log_t, *avr_vcd_log_p;
 
-#define AVR_VCD_LOG_CHUNK_SIZE (4096 / sizeof(avr_vcd_signal_t))
+DECLARE_FIFO(avr_vcd_log_t, avr_vcd_fifo, 256);
+
+struct argv_t;
 
 typedef struct avr_vcd_t {
        struct avr_t *  avr;    // AVR we are attaching timers to..
-       
-       char filename[74];              // output filename
-       FILE * output;
 
-       int signal_count;
+       char *                  filename;               // .vcd filename
+       /* can be input OR output, not both */
+       FILE *                  output;
+       FILE *                  input;
+       struct argv_t   * input_line;
+
+       int                             signal_count;
        avr_vcd_signal_t        signal[AVR_VCD_MAX_SIGNALS];
 
-       uint64_t period;
-       uint64_t start;
+       uint64_t                start;
+       uint64_t                period;         // for output cycles
+       uint64_t                vcd_to_us;      // for input unit mapping
 
-       size_t                  logsize;
-       uint32_t                logindex;
-       avr_vcd_log_p   log;
+       avr_vcd_fifo_t  log;
 } avr_vcd_t;
 
 // initializes a new VCD trace file, and returns zero if all is well
-int avr_vcd_init(struct avr_t * avr, 
-       const char * filename,  // filename to write
-       avr_vcd_t * vcd,                // vcd struct to initialize
-       uint32_t        period );       // file flushing period is in usec
-void avr_vcd_close(avr_vcd_t * vcd);
+int
+avr_vcd_init(
+               struct avr_t * avr,
+               const char * filename,  // filename to write
+               avr_vcd_t * vcd,                // vcd struct to initialize
+               uint32_t        period );       // file flushing period is in usec
+int
+avr_vcd_init_input(
+               struct avr_t * avr,
+               const char * filename,  // filename to read
+               avr_vcd_t * vcd );              // vcd struct to initialize
+void
+avr_vcd_close(
+               avr_vcd_t * vcd );
 
 // Add a trace signal to the vcd file. Must be called before avr_vcd_start()
-int avr_vcd_add_signal(avr_vcd_t * vcd, 
-       avr_irq_t * signal_irq,
-       int signal_bit_size,
-       const char * name );
+int
+avr_vcd_add_signal(
+               avr_vcd_t * vcd,
+               avr_irq_t * signal_irq,
+               int signal_bit_size,
+               const char * name );
 
 // Starts recording the signal value into the file
-int avr_vcd_start(avr_vcd_t * vcd);
+int
+avr_vcd_start(
+               avr_vcd_t * vcd);
 // stops recording signal values into the file
-int avr_vcd_stop(avr_vcd_t * vcd);
+int
+avr_vcd_stop(
+               avr_vcd_t * vcd);
 
 #ifdef __cplusplus
 };