twi: Move avr_twi_irq_msg to avr_twi.c
[simavr] / simavr / sim / sim_vcd_file.c
1 /*
2         sim_vcd_file.c
3
4         Implements a Value Change Dump file outout to generate
5         traces & curves and display them in gtkwave.
6
7         Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
8
9         This file is part of simavr.
10
11         simavr is free software: you can redistribute it and/or modify
12         it under the terms of the GNU General Public License as published by
13         the Free Software Foundation, either version 3 of the License, or
14         (at your option) any later version.
15
16         simavr is distributed in the hope that it will be useful,
17         but WITHOUT ANY WARRANTY; without even the implied warranty of
18         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19         GNU General Public License for more details.
20
21         You should have received a copy of the GNU General Public License
22         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25 #include <stdio.h>
26 #include <string.h>
27 #include "sim_vcd_file.h"
28 #include "sim_avr.h"
29 #include "sim_time.h"
30
31 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param);
32
33 int avr_vcd_init(struct avr_t * avr, const char * filename, avr_vcd_t * vcd, uint32_t period)
34 {
35         memset(vcd, 0, sizeof(avr_vcd_t));
36         vcd->avr = avr;
37         strncpy(vcd->filename, filename, sizeof(vcd->filename));
38         vcd->period = avr_usec_to_cycles(vcd->avr, period);
39         
40         for (int i = 0; i < AVR_VCD_MAX_SIGNALS; i++) {
41                 avr_init_irq(&avr->irq_pool, &vcd->signal[i].irq, i, 1, NULL /* TODO IRQ name */);
42                 avr_irq_register_notify(&vcd->signal[i].irq, _avr_vcd_notify, vcd);
43         }
44         
45         return 0;
46 }
47
48 void avr_vcd_close(avr_vcd_t * vcd)
49 {
50         avr_vcd_stop(vcd);
51 }
52
53 void _avr_vcd_notify(struct avr_irq_t * irq, uint32_t value, void * param)
54 {
55         avr_vcd_t * vcd = (avr_vcd_t *)param;
56         if (!vcd->output)
57                 return;
58         avr_vcd_signal_t * s = (avr_vcd_signal_t*)irq;
59         if (vcd->logindex == AVR_VCD_LOG_SIZE) {
60                 printf("_avr_vcd_notify %s overrun value buffer %d\n", s->name, AVR_VCD_LOG_SIZE);
61                 return;
62         }
63         avr_vcd_log_t *l = &vcd->log[vcd->logindex++];
64         l->signal = s;
65         l->when = vcd->avr->cycle;
66         l->value = value;
67 }
68
69 static char * _avr_vcd_get_signal_text(avr_vcd_signal_t * s, char * out, uint32_t value)
70 {
71         char * dst = out;
72                 
73         if (s->size > 1)
74                 *dst++ = 'b';
75         
76         for (int i = s->size; i > 0; i--)
77                 *dst++ = value & (1 << (i-1)) ? '1' : '0';
78         if (s->size > 1)
79                 *dst++ = ' ';
80         *dst++ = s->alias;
81         *dst = 0;
82         return out;
83 }
84
85 static void avr_vcd_flush_log(avr_vcd_t * vcd)
86 {
87 #if AVR_VCD_MAX_SIGNALS > 32
88         uint64_t seen = 0;
89 #else
90         uint32_t seen = 0;
91 #endif
92         uint64_t oldbase = 0;   // make sure it's different
93         char out[48];
94
95         if (!vcd->logindex)
96                 return;
97 //      printf("avr_vcd_flush_log %d\n", vcd->logindex);
98
99
100         for (uint32_t li = 0; li < vcd->logindex; li++) {
101                 avr_vcd_log_t *l = &vcd->log[li];
102                 uint64_t base = avr_cycles_to_nsec(vcd->avr, l->when - vcd->start);     // 1ns base
103
104                 // if that trace was seen in this usec already, we fudge the base time
105                 // to make sure the new value is offset by one usec, to make sure we get
106                 // at least a small pulse on the waveform
107                 // This is a bit of a fudge, but it is the only way to represent very 
108                 // short"pulses" that are still visible on the waveform.
109                 if (base == oldbase && seen & (1 << l->signal->irq.irq))
110                         base++; // this forces a new timestamp
111                         
112                 if (base > oldbase || li == 0) {
113                         seen = 0;
114                         fprintf(vcd->output, "#%llu\n", (long long unsigned int)base);
115                         oldbase = base;
116                 }
117                 seen |= (1 << l->signal->irq.irq);      // mark this trace as seen for this timestamp
118                 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(l->signal, out, l->value));
119         }
120         vcd->logindex = 0;
121 }
122
123 static avr_cycle_count_t _avr_vcd_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
124 {
125         avr_vcd_t * vcd = param;
126         avr_vcd_flush_log(vcd);
127         return when + vcd->period;
128 }
129
130 int avr_vcd_add_signal(avr_vcd_t * vcd, 
131         avr_irq_t * signal_irq,
132         int signal_bit_size,
133         const char * name )
134 {
135         if (vcd->signal_count == AVR_VCD_MAX_SIGNALS)
136                 return -1;
137         avr_vcd_signal_t * s = &vcd->signal[vcd->signal_count++];
138         strncpy(s->name, name, sizeof(s->name));
139         s->size = signal_bit_size;
140         s->alias = ' ' + vcd->signal_count ;
141         avr_connect_irq(signal_irq, &s->irq);
142         return 0;
143 }
144
145
146 int avr_vcd_start(avr_vcd_t * vcd)
147 {
148         if (vcd->output)
149                 avr_vcd_stop(vcd);
150         vcd->output = fopen(vcd->filename, "w");
151         if (vcd->output == NULL) {
152                 perror(vcd->filename);
153                 return -1;
154         }
155                 
156         fprintf(vcd->output, "$timescale 1ns $end\n");  // 1ns base
157         fprintf(vcd->output, "$scope module logic $end\n");
158
159         for (int i = 0; i < vcd->signal_count; i++) {
160                 fprintf(vcd->output, "$var wire %d %c %s $end\n",
161                         vcd->signal[i].size, vcd->signal[i].alias, vcd->signal[i].name);
162         }
163
164         fprintf(vcd->output, "$upscope $end\n");
165         fprintf(vcd->output, "$enddefinitions $end\n");
166         
167         fprintf(vcd->output, "$dumpvars\n");
168         for (int i = 0; i < vcd->signal_count; i++) {
169                 avr_vcd_signal_t * s = &vcd->signal[i];
170                 char out[48];
171                 fprintf(vcd->output, "%s\n", _avr_vcd_get_signal_text(s, out, s->irq.value));
172         }
173         fprintf(vcd->output, "$end\n");
174         vcd->start = vcd->avr->cycle;
175         avr_cycle_timer_register(vcd->avr, vcd->period, _avr_vcd_timer, vcd);   
176         return 0;
177 }
178
179 int avr_vcd_stop(avr_vcd_t * vcd)
180 {
181         avr_cycle_timer_cancel(vcd->avr, _avr_vcd_timer, vcd);
182
183         avr_vcd_flush_log(vcd);
184         
185         if (vcd->output)
186                 fclose(vcd->output);
187         vcd->output = NULL;
188         return 0;
189 }
190
191