9334b038ed1264bfa4b5570041a5541603ff21fe
[simavr] / simavr / sim / sim_interrupts.c
1 /*
2         sim_interrupts.c
3
4         Copyright 2008-2012 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <strings.h>
27 #include "sim_interrupts.h"
28 #include "sim_avr.h"
29 #include "sim_core.h"
30
31 // modulo a cursor value on the pending interrupt fifo
32 #define INT_FIFO_SIZE (sizeof(table->pending) / sizeof(avr_int_vector_t *))
33 #define INT_FIFO_MOD(_v) ((_v) &  (INT_FIFO_SIZE - 1))
34
35 void
36 avr_interrupt_reset(
37                 avr_t * avr )
38 {
39         avr_int_table_p table = &avr->interrupts;
40         memset(table, 0, sizeof(*table));
41 }
42
43 void
44 avr_register_vector(
45                 avr_t *avr,
46                 avr_int_vector_t * vector)
47 {
48         if (!vector->vector)
49                 return;
50
51         avr_int_table_p table = &avr->interrupts;
52
53         vector->irq.irq = vector->vector;
54         table->vector[table->vector_count++] = vector;
55         if (vector->trace)
56                 printf("%s register vector %d (enabled %04x:%d)\n", __FUNCTION__, vector->vector, vector->enable.reg, vector->enable.bit);
57
58         if (!vector->enable.reg)
59                 printf("avr_register_vector: No 'enable' bit on vector %d !\n", vector->vector);
60 }
61
62 int
63 avr_has_pending_interrupts(
64                 avr_t * avr)
65 {
66         avr_int_table_p table = &avr->interrupts;
67         return table->pending_r != table->pending_w;
68 }
69
70 int
71 avr_is_interrupt_pending(
72                 avr_t * avr,
73                 avr_int_vector_t * vector)
74 {
75         return vector->pending;
76 }
77
78 int
79 avr_is_interrupt_enabled(
80                 avr_t * avr,
81                 avr_int_vector_t * vector)
82 {
83         return avr_regbit_get(avr, vector->enable);
84 }
85
86 int
87 avr_raise_interrupt(
88                 avr_t * avr,
89                 avr_int_vector_t * vector)
90 {
91         if (!vector || !vector->vector)
92                 return 0;
93         if (vector->trace)
94                 printf("%s raising %d (enabled %d)\n", __FUNCTION__, vector->vector, avr_regbit_get(avr, vector->enable));
95         if (vector->pending) {
96                 if (vector->trace)
97                         printf("%s trying to double raise %d (enabled %d)\n", __FUNCTION__, vector->vector, avr_regbit_get(avr, vector->enable));
98                 return 0;
99         }
100         // always mark the 'raised' flag to one, even if the interrupt is disabled
101         // this allow "pooling" for the "raised" flag, like for non-interrupt
102         // driven UART and so so. These flags are often "write one to clear"
103         if (vector->raised.reg)
104                 avr_regbit_set(avr, vector->raised);
105
106         avr_raise_irq(&vector->irq, 1);
107
108         // If the interrupt is enabled, attempt to wake the core
109         if (avr_regbit_get(avr, vector->enable)) {
110                 // Mark the interrupt as pending
111                 vector->pending = 1;
112
113                 avr_int_table_p table = &avr->interrupts;
114
115                 table->pending[table->pending_w++] = vector;
116                 table->pending_w = INT_FIFO_MOD(table->pending_w);
117
118                 if (!table->pending_wait)
119                         table->pending_wait = 1;                // latency on interrupts ??
120                 if (avr->state != cpu_Running) {
121                         if (vector->trace)
122                                 printf("Waking CPU due to interrupt\n");
123                         avr->state = cpu_Running;       // in case we were sleeping
124                 }
125         }
126         // return 'raised' even if it was already pending
127         return 1;
128 }
129
130 void
131 avr_clear_interrupt(
132                 avr_t * avr,
133                 avr_int_vector_t * vector)
134 {
135         if (!vector)
136                 return;
137         if (vector->trace)
138                 printf("%s cleared %d\n", __FUNCTION__, vector->vector);
139         vector->pending = 0;
140         avr_raise_irq(&vector->irq, 0);
141         if (vector->raised.reg)
142                 avr_regbit_clear(avr, vector->raised);
143 }
144
145 int
146 avr_clear_interrupt_if(
147                 avr_t * avr,
148                 avr_int_vector_t * vector,
149                 uint8_t old)
150 {
151         if (avr_regbit_get(avr, vector->raised)) {
152                 avr_clear_interrupt(avr, vector);
153                 avr_regbit_clear(avr, vector->raised);
154                 return 1;
155         }
156         avr_regbit_setto(avr, vector->raised, old);
157         return 0;
158 }
159
160 avr_irq_t *
161 avr_get_interrupt_irq(
162                 avr_t * avr,
163                 uint8_t v)
164 {
165         avr_int_table_p table = &avr->interrupts;
166         for (int i = 0; i < table->vector_count; i++)
167                 if (table->vector[i]->vector == v)
168                         return &table->vector[i]->irq;
169         return NULL;
170 }
171
172 /*
173  * check whether interrupts are pending. If so, check if the interrupt "latency" is reached,
174  * and if so triggers the handlers and jump to the vector.
175  */
176 void
177 avr_service_interrupts(
178                 avr_t * avr)
179 {
180         if (!avr->sreg[S_I])
181                 return;
182
183         if (!avr_has_pending_interrupts(avr))
184                 return;
185
186         avr_int_table_p table = &avr->interrupts;
187
188         if (!table->pending_wait) {
189                 table->pending_wait = 2;        // for next one...
190                 return;
191         }
192         table->pending_wait--;
193         if (table->pending_wait)
194                 return;
195
196         // how many are pending...
197         int cnt = table->pending_w > table->pending_r ?
198                         table->pending_w - table->pending_r :
199                         (table->pending_w + INT_FIFO_SIZE) - table->pending_r;
200         // locate the highest priority one
201         int min = 0xff;
202         int mini = 0;
203         for (int ii = 0; ii < cnt; ii++) {
204                 int vi = INT_FIFO_MOD(table->pending_r + ii);
205                 avr_int_vector_t * v = table->pending[vi];
206                 if (v->vector < min) {
207                         min = v->vector;
208                         mini = vi;
209                 }
210         }
211         avr_int_vector_t * vector = table->pending[mini];
212
213         // now move the one at the front of the fifo in the slot of
214         // the one we service
215         table->pending[mini] = table->pending[table->pending_r++];
216         table->pending_r = INT_FIFO_MOD(table->pending_r);
217
218         // if that single interrupt is masked, ignore it and continue
219         // could also have been disabled, or cleared
220         if (!avr_regbit_get(avr, vector->enable) || !vector->pending) {
221                 vector->pending = 0;
222         } else {
223                 if (vector && vector->trace)
224                         printf("%s calling %d\n", __FUNCTION__, (int)vector->vector);
225                 _avr_push16(avr, avr->pc >> 1);
226                 avr->sreg[S_I] = 0;
227                 avr->pc = vector->vector * avr->vector_size;
228
229                 avr_clear_interrupt(avr, vector);
230         }
231 }
232