]>
Commit | Line | Data |
---|---|---|
8517f62b AC |
1 | /* This file is part of the program psim. |
2 | ||
3 | Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au> | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | ||
19 | */ | |
20 | ||
21 | ||
22 | #ifndef _SIM_EVENTS_C_ | |
23 | #define _SIM_EVENTS_C_ | |
24 | ||
25 | #include "sim-main.h" | |
26 | #include "sim-assert.h" | |
27 | ||
28 | #include <signal.h> | |
29 | ||
30 | ||
31 | /* The event queue maintains a single absolute time using two | |
32 | variables. | |
33 | ||
34 | TIME_OF_EVENT: this holds the time at which the next event is ment | |
35 | to occure. If no next event it will hold the time of the last | |
36 | event. | |
37 | ||
38 | TIME_FROM_EVENT: The current distance from TIME_OF_EVENT. If an | |
39 | event is pending, this will be positive. If no future event is | |
40 | pending this will be negative. This variable is decremented once | |
41 | for each iteration of a clock cycle. | |
42 | ||
43 | Initially, the clock is started at time one (0) with TIME_OF_EVENT | |
44 | == 0 and TIME_FROM_EVENT == 0. | |
45 | ||
46 | Clearly there is a bug in that this code assumes that the absolute | |
47 | time counter will never become greater than 2^62. | |
48 | ||
49 | To avoid the need to use 64bit arithmetic, the event queue always | |
50 | contains at least one event scheduled every 16 000 ticks. This | |
51 | limits the time from event counter to values less than | |
52 | 16 000. */ | |
53 | ||
54 | ||
55 | #if !defined (SIM_EVENTS_POLL_RATE) | |
56 | #define SIM_EVENTS_POLL_RATE 0x4000 | |
57 | #endif | |
58 | ||
59 | ||
60 | #define _ETRACE sd | |
61 | ||
62 | #undef ETRACE | |
63 | #define ETRACE(ARGS) \ | |
64 | do \ | |
65 | { \ | |
66 | if (WITH_TRACE) \ | |
67 | { \ | |
68 | if (sd->events.trace) \ | |
69 | { \ | |
70 | const char *file; \ | |
71 | SIM_FILTER_PATH(file, __FILE__); \ | |
72 | sim_io_printf (sd, "%s:%d: ", file, __LINE__); \ | |
73 | sim_io_printf ARGS; \ | |
74 | } \ | |
75 | } \ | |
76 | } \ | |
77 | while (0) | |
78 | ||
79 | ||
80 | STATIC_INLINE_SIM_EVENTS\ | |
81 | (void) | |
82 | sim_events_poll (void *data) | |
83 | { | |
84 | /* just re-schedule in 1000 million ticks time */ | |
85 | SIM_DESC sd = data; | |
86 | sim_events_schedule(sd, SIM_EVENTS_POLL_RATE, sim_events_poll, sd); | |
87 | sim_io_poll_quit (sd); | |
88 | } | |
89 | ||
90 | ||
91 | INLINE_SIM_EVENTS\ | |
92 | (void) | |
93 | sim_events_init(SIM_DESC sd) | |
94 | { | |
95 | sim_events *events = &sd->events; | |
96 | sim_event *event; | |
97 | ||
98 | /* drain the interrupt queue */ | |
99 | { | |
100 | #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) | |
101 | sigset_t old_mask; | |
102 | sigset_t new_mask; | |
103 | sigfillset(&new_mask); | |
104 | /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask); | |
105 | #endif | |
106 | event = events->held; | |
107 | while (event != NULL) { | |
108 | sim_event *dead = event; | |
109 | event = event->next; | |
110 | zfree(dead); | |
111 | } | |
112 | events->held = NULL; | |
113 | events->held_end = &events->held; | |
114 | #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) | |
115 | /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL); | |
116 | #endif | |
117 | } | |
118 | ||
119 | /* drain the normal queue */ | |
120 | event = events->queue; | |
121 | while (event != NULL) { | |
122 | sim_event *dead = event; | |
123 | event = event->next; | |
124 | zfree(dead); | |
125 | } | |
126 | events->queue = NULL; | |
127 | ||
128 | /* wind time back to zero */ | |
129 | events->processing = 0; | |
130 | events->time_of_event = 0; | |
131 | events->time_from_event = 0; | |
132 | ||
133 | /* schedule our initial counter event */ | |
134 | sim_events_schedule(sd, 0, sim_events_poll, sd); | |
135 | ||
136 | /* from now on, except when the large-int event is being processed | |
137 | the event queue is non empty */ | |
138 | SIM_ASSERT(events->queue != NULL); | |
139 | } | |
140 | ||
141 | INLINE_SIM_EVENTS\ | |
142 | (signed64) | |
143 | sim_events_time(SIM_DESC sd) | |
144 | { | |
145 | sim_events *events = &sd->events; | |
146 | return events->time_of_event - events->time_from_event; | |
147 | } | |
148 | ||
149 | STATIC_INLINE_SIM_EVENTS\ | |
150 | (void) | |
151 | update_time_from_event(SIM_DESC sd) | |
152 | { | |
153 | sim_events *events = &sd->events; | |
154 | signed64 current_time = sim_events_time(sd); | |
155 | if (events->queue != NULL) { | |
156 | events->time_from_event = (events->queue->time_of_event - current_time); | |
157 | events->time_of_event = events->queue->time_of_event; | |
158 | } | |
159 | else { | |
160 | events->time_of_event = current_time - 1; | |
161 | events->time_from_event = -1; | |
162 | } | |
163 | SIM_ASSERT(current_time == sim_events_time (sd)); | |
164 | SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL)); | |
165 | } | |
166 | ||
167 | STATIC_INLINE_SIM_EVENTS\ | |
168 | (void) | |
169 | insert_sim_event(SIM_DESC sd, | |
170 | sim_event *new_event, | |
171 | signed64 delta) | |
172 | { | |
173 | sim_events *events = &sd->events; | |
174 | sim_event *curr; | |
175 | sim_event **prev; | |
176 | signed64 time_of_event; | |
177 | ||
178 | if (delta < 0) | |
179 | engine_error (sd, "what is past is past!\n"); | |
180 | ||
181 | /* compute when the event should occure */ | |
182 | time_of_event = sim_events_time(sd) + delta; | |
183 | ||
184 | /* find the queue insertion point - things are time ordered */ | |
185 | prev = &events->queue; | |
186 | curr = events->queue; | |
187 | while (curr != NULL && time_of_event >= curr->time_of_event) { | |
188 | SIM_ASSERT(curr->next == NULL | |
189 | || curr->time_of_event <= curr->next->time_of_event); | |
190 | prev = &curr->next; | |
191 | curr = curr->next; | |
192 | } | |
193 | SIM_ASSERT(curr == NULL || time_of_event < curr->time_of_event); | |
194 | ||
195 | /* insert it */ | |
196 | new_event->next = curr; | |
197 | *prev = new_event; | |
198 | new_event->time_of_event = time_of_event; | |
199 | ||
200 | /* adjust the time until the first event */ | |
201 | update_time_from_event(sd); | |
202 | } | |
203 | ||
204 | INLINE_SIM_EVENTS\ | |
205 | (sim_event *) | |
206 | sim_events_schedule(SIM_DESC sd, | |
207 | signed64 delta_time, | |
208 | sim_event_handler *handler, | |
209 | void *data) | |
210 | { | |
211 | sim_event *new_event = ZALLOC(sim_event); | |
212 | new_event->data = data; | |
213 | new_event->handler = handler; | |
214 | insert_sim_event(sd, new_event, delta_time); | |
215 | ETRACE((_ETRACE, | |
216 | "event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n", | |
217 | (long)sim_events_time(sd), | |
218 | (long)new_event, | |
219 | (long)new_event->time_of_event, | |
220 | (long)new_event->handler, | |
221 | (long)new_event->data)); | |
222 | return new_event; | |
223 | } | |
224 | ||
225 | ||
226 | INLINE_SIM_EVENTS\ | |
227 | (sim_event *) | |
228 | sim_events_schedule_after_signal(SIM_DESC sd, | |
229 | signed64 delta_time, | |
230 | sim_event_handler *handler, | |
231 | void *data) | |
232 | { | |
233 | sim_events *events = &sd->events; | |
234 | sim_event *new_event = ZALLOC(sim_event); | |
235 | ||
236 | new_event->data = data; | |
237 | new_event->handler = handler; | |
238 | new_event->time_of_event = delta_time; /* work it out later */ | |
239 | new_event->next = NULL; | |
240 | ||
241 | { | |
242 | #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) | |
243 | sigset_t old_mask; | |
244 | sigset_t new_mask; | |
245 | sigfillset(&new_mask); | |
246 | /*-LOCK-*/ sigprocmask(SIG_SETMASK, &new_mask, &old_mask); | |
247 | #endif | |
248 | if (events->held == NULL) { | |
249 | events->held = new_event; | |
250 | } | |
251 | else { | |
252 | *events->held_end = new_event; | |
253 | } | |
254 | events->held_end = &new_event->next; | |
255 | #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) | |
256 | /*-UNLOCK-*/ sigprocmask(SIG_SETMASK, &old_mask, NULL); | |
257 | #endif | |
258 | } | |
259 | ||
260 | ETRACE((_ETRACE, | |
261 | "event scheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n", | |
262 | (long)sim_events_time(sd), | |
263 | (long)new_event, | |
264 | (long)new_event->time_of_event, | |
265 | (long)new_event->handler, | |
266 | (long)new_event->data)); | |
267 | ||
268 | return new_event; | |
269 | } | |
270 | ||
271 | ||
272 | INLINE_SIM_EVENTS\ | |
273 | (void) | |
274 | sim_events_deschedule(SIM_DESC sd, | |
275 | sim_event *event_to_remove) | |
276 | { | |
277 | sim_events *events = &sd->events; | |
278 | sim_event *to_remove = (sim_event*)event_to_remove; | |
279 | SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL)); | |
280 | if (event_to_remove != NULL) { | |
281 | sim_event *current; | |
282 | sim_event **ptr_to_current; | |
283 | for (ptr_to_current = &events->queue, current = *ptr_to_current; | |
284 | current != NULL && current != to_remove; | |
285 | ptr_to_current = ¤t->next, current = *ptr_to_current); | |
286 | if (current == to_remove) { | |
287 | *ptr_to_current = current->next; | |
288 | ETRACE((_ETRACE, | |
289 | "event descheduled at %ld - tag 0x%lx - time %ld, handler 0x%lx, data 0x%lx\n", | |
290 | (long)sim_events_time(sd), | |
291 | (long)event_to_remove, | |
292 | (long)current->time_of_event, | |
293 | (long)current->handler, | |
294 | (long)current->data)); | |
295 | zfree(current); | |
296 | update_time_from_event(sd); | |
297 | } | |
298 | else { | |
299 | ETRACE((_ETRACE, | |
300 | "event descheduled at %ld - tag 0x%lx - not found\n", | |
301 | (long)sim_events_time(sd), | |
302 | (long)event_to_remove)); | |
303 | } | |
304 | } | |
305 | SIM_ASSERT((events->time_from_event >= 0) == (events->queue != NULL)); | |
306 | } | |
307 | ||
308 | ||
309 | ||
310 | ||
311 | INLINE_SIM_EVENTS\ | |
312 | (int) | |
313 | sim_events_tick(SIM_DESC sd) | |
314 | { | |
315 | sim_events *events = &sd->events; | |
316 | ||
317 | /* we should only be here when the previous tick has been fully | |
318 | processed */ | |
319 | SIM_ASSERT(!events->processing && events->queue != NULL); | |
320 | ||
321 | /* Advance the time but *only* if there is nothing to process */ | |
322 | if (events->time_from_event == 0) | |
323 | return 1; | |
324 | else if (events->held != NULL) | |
325 | return 1; | |
326 | else { | |
327 | events->time_from_event -= 1; | |
328 | return 0; | |
329 | } | |
330 | } | |
331 | ||
332 | ||
333 | ||
334 | INLINE_SIM_EVENTS\ | |
335 | (void) | |
336 | sim_events_process(SIM_DESC sd) | |
337 | { | |
338 | sim_events *events = &sd->events; | |
339 | signed64 event_time = sim_events_time(sd); | |
340 | ||
341 | /* something to do */ | |
342 | SIM_ASSERT(events->time_from_event == 0 || events->held != NULL); | |
343 | SIM_ASSERT(events->queue != NULL); | |
344 | ||
345 | /* move any events that were queued by any signal handlers onto the | |
346 | real event queue. */ | |
347 | if (events->held != NULL) { | |
348 | sim_event *held_events; | |
349 | sim_event *curr_event; | |
350 | ||
351 | #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) | |
352 | /*-LOCK-*/ | |
353 | sigset_t old_mask; | |
354 | sigset_t new_mask; | |
355 | sigfillset(&new_mask); | |
356 | sigprocmask(SIG_SETMASK, &new_mask, &old_mask); | |
357 | #endif | |
358 | ||
359 | held_events = events->held; | |
360 | events->held = NULL; | |
361 | events->held_end = &events->held; | |
362 | ||
363 | #if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK) | |
364 | /*-UNLOCK-*/ | |
365 | sigprocmask(SIG_SETMASK, &old_mask, NULL); | |
366 | #endif | |
367 | ||
368 | do { | |
369 | curr_event = held_events; | |
370 | held_events = curr_event->next; | |
371 | insert_sim_event(sd, curr_event, curr_event->time_of_event); | |
372 | } while (held_events != NULL); | |
373 | } | |
374 | ||
375 | /* consume all events for this or earlier times. Be careful to | |
376 | allow a new event to appear under our feet */ | |
377 | events->processing = 1; | |
378 | while (events->queue->time_of_event <= event_time) { | |
379 | sim_event *to_do = events->queue; | |
380 | sim_event_handler *handler = to_do->handler; | |
381 | void *data = to_do->data; | |
382 | events->queue = to_do->next; | |
383 | ETRACE((_ETRACE, | |
384 | "event issued at %ld - tag 0x%lx - handler 0x%lx, data 0x%lx\n", | |
385 | (long)event_time, | |
386 | (long)to_do, | |
387 | (long)handler, | |
388 | (long)data)); | |
389 | zfree (to_do); | |
390 | handler (data); | |
391 | } | |
392 | events->processing = 0; | |
393 | ||
394 | /* re-caculate time for new events - advance the time */ | |
395 | update_time_from_event(sd); | |
396 | SIM_ASSERT(events->time_from_event > 0 && events->queue != NULL); | |
397 | events->time_from_event -= 1; | |
398 | } | |
399 | ||
400 | #endif |