From e198c93ab297f763a18a77c64dbdfc0adc7ee4bb Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Mon, 14 Dec 2009 21:00:04 +0000 Subject: [PATCH] Adds VCD (Value Change Dump) file output support (gtkwave) This subsystem is not called by the core itself, it is an utility available to other "boards". Allows the simulator to dump graphical traces readable in gtkwave. The subsystem allows any number of trace files in parallel, any sampling periods, and a maximum of 32 traces per file. See updated "ledramp.c" example for a very simple way to use the code. Signed-off-by: Michel Pollet --- simavr/sim/sim_vcd_file.c | 155 ++++++++++++++++++++++++++++++++++++++ simavr/sim/sim_vcd_file.h | 77 +++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 simavr/sim/sim_vcd_file.c create mode 100644 simavr/sim/sim_vcd_file.h diff --git a/simavr/sim/sim_vcd_file.c b/simavr/sim/sim_vcd_file.c new file mode 100644 index 0000000..bc1a6d4 --- /dev/null +++ b/simavr/sim/sim_vcd_file.c @@ -0,0 +1,155 @@ +/* + sim_vcd_file.c + + Implements a Value Change Dump file outout to generate + traces & curves and display them in gtkwave. + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include "sim_vcd_file.h" +#include "sim_avr.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) +{ + memset(vcd, 0, sizeof(avr_vcd_t)); + vcd->avr = avr; + strncpy(vcd->filename, filename, sizeof(vcd->filename)); + 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]); + } + + return 0; +} + +void avr_vcd_close(avr_vcd_t * vcd) +{ + avr_vcd_stop(vcd); +} + +void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param) +{ + avr_vcd_signal_t * s = param; + s->touched++; +} + +static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out) +{ + char * dst = out; + + if (s->size > 1) + *dst++ = 'b'; + + for (int i = s->size; i > 0; i--) + *dst++ = s->irq.value & (1 << (i-1)) ? '1' : '0'; + if (s->size > 1) + *dst++ = ' '; + *dst++ = s->alias; + *dst = 0; + return out; +} + +static avr_cycle_count_t _avr_vcd_timer(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + 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)); + } + char out[32]; + fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out)); + + s->touched = 0; + done++; + } + } + 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 ) +{ + if (vcd->signal_count == AVR_VCD_MAX_SIGNALS) + return -1; + avr_vcd_signal_t * s = &vcd->signal[vcd->signal_count++]; + 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); +} + + +int avr_vcd_start(avr_vcd_t * vcd) +{ + if (vcd->output) + avr_vcd_stop(vcd); + vcd->output = fopen(vcd->filename, "w"); + if (vcd->output == NULL) { + perror(vcd->filename); + return -1; + } + 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, "$var wire %d %c %s $end\n", + vcd->signal[i].size, vcd->signal[i].alias, vcd->signal[i].name); + } + + fprintf(vcd->output, "$upscope $end\n"); + fprintf(vcd->output, "$enddefinitions $end\n"); + + 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, "$end\n"); + avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd); +} + +int avr_vcd_stop(avr_vcd_t * vcd) +{ + avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, 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 new file mode 100644 index 0000000..46f14be --- /dev/null +++ b/simavr/sim/sim_vcd_file.h @@ -0,0 +1,77 @@ +/* + sim_vcd_file.c + + Implements a Value Change Dump file outout to generate + traces & curves and display them in gtkwave. + + Copyright 2008, 2009 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#ifndef __SIM_VCD_FILE_H__ +#define __SIM_VCD_FILE_H__ + +#include +#include "sim_irq.h" + +/* + * Value Change dump module for simavr. + * + * 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 + +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_t { + struct avr_t * avr; // AVR we are attaching timers to.. + + char filename[74]; // output filename + FILE * output; + + int signal_count; + avr_vcd_signal_t signal [AVR_VCD_MAX_SIGNALS]; + uint64_t period; + uint64_t start; +} avr_vcd_t; + +// initializes a new VCD trace file, opens it 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 + +// 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 ); + +// Starts recording the signal value into the file +int avr_vcd_start(avr_vcd_t * vcd); +// stops recording signal values into the file +int avr_vcd_stop(avr_vcd_t * vcd); + +#endif /* __SIM_VCD_FILE_H__ */ -- 2.39.5