Commit 64f4695fc0818cf29a3b762ef276c606281f0204
authorMichel Pollet <buserror@gmail.com>
Thu, 17 Dec 2009 20:24:11 +0000 (20:24 +0000)
committerMichel Pollet <buserror@gmail.com>
Thu, 17 Dec 2009 20:24:11 +0000 (20:24 +0000)
More or less re-did the VCD handling, now ues a "log" of signal
changes instead of a timeslice. The log is flushed at regular
interval using a timer.
The log also handles signal changes that are smaller than one usec
(the minimum time slice in our VCD).

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

index 0dcd2e7c7d08b4bad586dab31b7131649723f4e3..411b6b85853f8f912a8eaa47625bbfcca6b9a2f2 100644 (file)
@@ -34,12 +34,11 @@ int avr_vcd_init(struct avr_t * avr, const char * filename, avr_vcd_t * vcd, uin
        memset(vcd, 0, sizeof(avr_vcd_t));
        vcd->avr = avr;
        strncpy(vcd->filename, filename, sizeof(vcd->filename));
-       vcd->period_usec = period;
        vcd->period = avr_usec_to_cycles(vcd->avr, period);
        
        for (int i = 0; i < AVR_VCD_MAX_SIGNALS; i++) {
                avr_init_irq(&vcd->signal[i].irq, i, 1);
-               avr_irq_register_notify(&vcd->signal[i].irq, _avr_vcd_notify, &vcd->signal[i]);
+               avr_irq_register_notify(&vcd->signal[i].irq, _avr_vcd_notify, vcd);
        }
        
        return 0;
@@ -52,11 +51,21 @@ void avr_vcd_close(avr_vcd_t * vcd)
 
 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param)
 {
-       avr_vcd_signal_t * s = param;
-       s->touched++;
+       avr_vcd_t * vcd = (avr_vcd_t *)param;
+       if (!vcd->output)
+               return;
+       avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
+       if (vcd->logindex == AVR_VCD_LOG_SIZE) {
+               printf("_avr_vcd_notify %s overrun value buffer %d\n", s->name, AVR_VCD_LOG_SIZE);
+               return;
+       }
+       avr_vcd_log_t *l = &vcd->log[vcd->logindex++];
+       l->signal = s;
+       l->when = vcd->avr->cycle;
+       l->value = value;
 }
 
-static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out)
+static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out, uint32_t value)
 {
        char * dst = out;
                
@@ -64,7 +73,7 @@ static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out)
                *dst++ = 'b';
        
        for (int i = s->size; i > 0; i--)
-               *dst++ = s->irq.value & (1 << (i-1)) ? '1' : '0';
+               *dst++ = value & (1 << (i-1)) ? '1' : '0';
        if (s->size > 1)
                *dst++ = ' ';
        *dst++ = s->alias;
@@ -72,28 +81,46 @@ static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out)
        return out;
 }
 
-static avr_cycle_count_t _avr_vcd_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
+static void avr_vcd_flush_log(avr_vcd_t * vcd)
 {
-       avr_vcd_t * vcd = param;
-       int done = 0;
-
-       if (vcd->start == 0)
-               vcd->start = when;
-
-       for (int i = 0; i < vcd->signal_count; i++) {
-               avr_vcd_signal_t * s = &vcd->signal[i];
-               if (s->touched) {
-                       if (done == 0) {
-                               fprintf(vcd->output, "#%ld\n", 
-                                       avr_cycles_to_usec(vcd->avr, when - vcd->start) / vcd->period_usec);
-                       }
-                       char out[32];
-                       fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out));
+       if (!vcd->logindex)
+               return;
+//     printf("avr_vcd_flush_log %d\n", vcd->logindex);
+       uint32_t oldbase = 0;   // make sure it's different
+       char out[32];
+
+#if AVR_VCD_MAX_SIGNALS > 32
+       uint64_t seen = 0;
+#else
+       uint32_t seen = 0;
+#endif
+       for (int li = 0; li < vcd->logindex; li++) {
+               avr_vcd_log_t *l = &vcd->log[li];
+               uint32_t base = avr_cycles_to_usec(vcd->avr, l->when - vcd->start);
+
+               // if that trace was seen in this usec already, we fudge the base time
+               // to make sure the new value is offset by one usec, 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))
+                       base++; // this forces a new timestamp
                        
-                       s->touched = 0;
-                       done++;
+               if (base > oldbase || li == 0) {
+                       seen = 0;
+                       fprintf(vcd->output, "#%ld\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));
        }
+       vcd->logindex = 0;
+}
+
+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;
 }
 
@@ -108,7 +135,6 @@ int avr_vcd_add_signal(avr_vcd_t * vcd,
        strncpy(s->name, name, sizeof(s->name));
        s->size = signal_bit_size;
        s->alias = ' ' + vcd->signal_count ;
-       s->touched = 0; // want an initial dump...
        avr_connect_irq(signal_irq, &s->irq);
 }
 
@@ -122,7 +148,8 @@ int avr_vcd_start(avr_vcd_t * vcd)
                perror(vcd->filename);
                return -1;
        }
-       fprintf(vcd->output, "$timescale %dus $end\n", vcd->period_usec);
+               
+       fprintf(vcd->output, "$timescale 1us $end\n");
        fprintf(vcd->output, "$scope module logic $end\n");
 
        for (int i = 0; i < vcd->signal_count; i++) {
@@ -136,11 +163,11 @@ int avr_vcd_start(avr_vcd_t * vcd)
        fprintf(vcd->output, "$dumpvars\n");
        for (int i = 0; i < vcd->signal_count; i++) {
                avr_vcd_signal_t * s = &vcd->signal[i];
-               s->touched = 0;
                char out[32];
-               fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out));
+               fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out, s->irq.value));
        }
        fprintf(vcd->output, "$end\n");
+       vcd->start = vcd->avr->cycle;
        avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);   
 }
 
@@ -148,6 +175,8 @@ int avr_vcd_stop(avr_vcd_t * vcd)
 {
        avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
 
+       avr_vcd_flush_log(vcd);
+       
        if (vcd->output)
                fclose(vcd->output);
        vcd->output = NULL;
index b0845f4ce6491efcb21c9dafc61da9c162a8994d..a94c256ff5d9c123faf7d4ca513f3282fd6d3b2a 100644 (file)
  * This structure registers IRQ change hooks to various "source" IRQs
  * and dumps their values (if changed) at certains intervals into the VCD file
  */
+
 #define AVR_VCD_MAX_SIGNALS 32
+#define AVR_VCD_LOG_SIZE       128
 
 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      
-       int             touched;                // mark it ready to be flushed in VCD
 } avr_vcd_signal_t;
 
+typedef struct avr_vcd_log_t {
+       uint64_t        when;
+       avr_vcd_signal_t * signal;
+       uint32_t value;
+} avr_vcd_log_t;
+
 typedef struct avr_vcd_t {
        struct avr_t *  avr;    // AVR we are attaching timers to..
        
@@ -53,16 +59,20 @@ typedef struct avr_vcd_t {
 
        int signal_count;
        avr_vcd_signal_t        signal [AVR_VCD_MAX_SIGNALS];   
+
        uint64_t period;
-       uint32_t period_usec;
        uint64_t start;
+
+       uint32_t                logindex;
+       avr_vcd_log_t   log[AVR_VCD_LOG_SIZE];
 } avr_vcd_t;
 
-// initializes a new VCD trace file, opens it and returns zero if all is well
+// 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 );       // period is in cycles
+       uint32_t        period );       // file flushing period is in usec
+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,