From 64f4695fc0818cf29a3b762ef276c606281f0204 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Thu, 17 Dec 2009 20:24:11 +0000 Subject: [PATCH] VCD: Reworked 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 --- simavr/sim/sim_vcd_file.c | 85 ++++++++++++++++++++++++++------------- simavr/sim/sim_vcd_file.h | 20 ++++++--- 2 files changed, 72 insertions(+), 33 deletions(-) diff --git a/simavr/sim/sim_vcd_file.c b/simavr/sim/sim_vcd_file.c index 0dcd2e7..411b6b8 100644 --- a/simavr/sim/sim_vcd_file.c +++ b/simavr/sim/sim_vcd_file.c @@ -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; diff --git a/simavr/sim/sim_vcd_file.h b/simavr/sim/sim_vcd_file.h index b0845f4..a94c256 100644 --- a/simavr/sim/sim_vcd_file.h +++ b/simavr/sim/sim_vcd_file.h @@ -34,17 +34,23 @@ * 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, -- 2.39.5