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;
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;
*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;
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;
}
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);
}
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++) {
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);
}
{
avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
+ avr_vcd_flush_log(vcd);
+
if (vcd->output)
fclose(vcd->output);
vcd->output = NULL;
* 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..
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,