| /* Hardware event manager. |
| Copyright (C) 1998-2021 Free Software Foundation, Inc. |
| Contributed by Cygnus Support. |
| |
| This file is part of GDB, the GNU debugger. |
| |
| This program 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. |
| |
| This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include "hw-main.h" |
| #include "hw-base.h" |
| |
| #include "sim-events.h" |
| |
| #include <string.h> |
| |
| /* The hw-events object is implemented using sim-events */ |
| |
| struct hw_event |
| { |
| void *data; |
| struct hw *me; |
| hw_event_callback *callback; |
| sim_event *real; |
| struct hw_event_data *entry; |
| }; |
| |
| struct hw_event_data |
| { |
| struct hw_event event; |
| struct hw_event_data *next; |
| }; |
| |
| void |
| create_hw_event_data (struct hw *me) |
| { |
| if (me->events_of_hw != NULL) |
| hw_abort (me, "stray events"); |
| /* NOP */ |
| } |
| |
| void |
| delete_hw_event_data (struct hw *me) |
| { |
| /* Remove the scheduled event. */ |
| while (me->events_of_hw) |
| hw_event_queue_deschedule (me, &me->events_of_hw->event); |
| } |
| |
| |
| /* Pass the H/W event onto the real callback */ |
| |
| static void |
| bounce_hw_event (SIM_DESC sd, |
| void *data) |
| { |
| /* save the data */ |
| struct hw_event_data *entry = (struct hw_event_data *) data; |
| struct hw *me = entry->event.me; |
| void *event_data = entry->event.data; |
| hw_event_callback *callback = entry->event.callback; |
| struct hw_event_data **prev = &me->events_of_hw; |
| while ((*prev) != entry) |
| prev = &(*prev)->next; |
| (*prev) = entry->next; |
| hw_free (me, entry); |
| callback (me, event_data); /* may not return */ |
| } |
| |
| |
| |
| /* Map onto the event functions */ |
| |
| struct hw_event * |
| hw_event_queue_schedule (struct hw *me, |
| signed64 delta_time, |
| hw_event_callback *callback, |
| void *data) |
| { |
| return hw_event_queue_schedule_tracef (me, delta_time, callback, data, NULL); |
| } |
| |
| struct hw_event * |
| hw_event_queue_schedule_tracef (struct hw *me, |
| signed64 delta_time, |
| hw_event_callback *callback, |
| void *data, |
| const char *fmt, |
| ...) |
| { |
| struct hw_event *event; |
| va_list ap; |
| va_start (ap, fmt); |
| event = hw_event_queue_schedule_vtracef (me, delta_time, callback, data, fmt, ap); |
| va_end (ap); |
| return event; |
| } |
| |
| struct hw_event * |
| hw_event_queue_schedule_vtracef (struct hw *me, |
| signed64 delta_time, |
| hw_event_callback *callback, |
| void *data, |
| const char *fmt, |
| va_list ap) |
| { |
| struct hw_event_data *entry = HW_ZALLOC (me, struct hw_event_data); |
| entry->next = me->events_of_hw; |
| me->events_of_hw = entry; |
| /* fill it in */ |
| entry->event.entry = entry; |
| entry->event.data = data; |
| entry->event.callback = callback; |
| entry->event.me = me; |
| entry->event.real = sim_events_schedule_vtracef (hw_system (me), |
| delta_time, |
| bounce_hw_event, |
| entry, |
| fmt, ap); |
| return &entry->event; |
| } |
| |
| |
| void |
| hw_event_queue_deschedule (struct hw *me, |
| struct hw_event *event_to_remove) |
| { |
| /* ZAP the event but only if it is still in the event queue. Note |
| that event_to_remove is only de-referenced after its validity has |
| been confirmed. */ |
| struct hw_event_data **prev; |
| for (prev = &me->events_of_hw; |
| (*prev) != NULL; |
| prev = &(*prev)->next) |
| { |
| struct hw_event_data *entry = (*prev); |
| if (&entry->event == event_to_remove) |
| { |
| sim_events_deschedule (hw_system (me), |
| entry->event.real); |
| (*prev) = entry->next; |
| hw_free (me, entry); |
| return; |
| } |
| } |
| } |
| |
| |
| signed64 |
| hw_event_queue_time (struct hw *me) |
| { |
| return sim_events_time (hw_system (me)); |
| } |
| |
| /* Returns the time that remains before the event is raised. */ |
| signed64 |
| hw_event_remain_time (struct hw *me, struct hw_event *event) |
| { |
| signed64 t; |
| |
| t = sim_events_remain_time (hw_system (me), event->real); |
| return t; |
| } |
| |
| /* Only worry about this compling on ANSI systems. |
| Build with `make test-hw-events' in sim/<cpu> directory*/ |
| |
| #if defined (MAIN) |
| #include "sim-main.h" |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| static void |
| test_handler (struct hw *me, |
| void *data) |
| { |
| int *n = data; |
| if (*n != hw_event_queue_time (me)) |
| abort (); |
| *n = -(*n); |
| } |
| |
| int |
| main (int argc, |
| char **argv) |
| { |
| host_callback *cb = ZALLOC (host_callback); |
| struct sim_state *sd = sim_state_alloc (0, cb); |
| struct hw *me = ZALLOC (struct hw); |
| sim_pre_argv_init (sd, "test-hw-events"); |
| sim_post_argv_init (sd); |
| me->system_of_hw = sd; |
| |
| printf ("Create hw-event-data\n"); |
| { |
| create_hw_alloc_data (me); |
| create_hw_event_data (me); |
| delete_hw_event_data (me); |
| delete_hw_alloc_data (me); |
| } |
| |
| printf ("Create hw-events\n"); |
| { |
| struct hw_event *a; |
| struct hw_event *b; |
| struct hw_event *c; |
| struct hw_event *d; |
| create_hw_alloc_data (me); |
| create_hw_event_data (me); |
| a = hw_event_queue_schedule (me, 0, NULL, NULL); |
| b = hw_event_queue_schedule (me, 1, NULL, NULL); |
| c = hw_event_queue_schedule (me, 2, NULL, NULL); |
| d = hw_event_queue_schedule (me, 1, NULL, NULL); |
| hw_event_queue_deschedule (me, c); |
| hw_event_queue_deschedule (me, b); |
| hw_event_queue_deschedule (me, a); |
| hw_event_queue_deschedule (me, d); |
| c = HW_ZALLOC (me, struct hw_event); |
| hw_event_queue_deschedule (me, b); /* OOPS! */ |
| hw_free (me, c); |
| delete_hw_event_data (me); |
| delete_hw_alloc_data (me); |
| } |
| |
| printf ("Schedule hw-events\n"); |
| { |
| struct hw_event **e; |
| int *n; |
| int i; |
| int nr = 4; |
| e = HW_NZALLOC (me, struct hw_event *, nr); |
| n = HW_NZALLOC (me, int, nr); |
| create_hw_alloc_data (me); |
| create_hw_event_data (me); |
| for (i = 0; i < nr; i++) |
| { |
| n[i] = i; |
| e[i] = hw_event_queue_schedule (me, i, test_handler, &n[i]); |
| } |
| sim_events_preprocess (sd, 1, 1); |
| for (i = 0; i < nr; i++) |
| { |
| if (sim_events_tick (sd)) |
| sim_events_process (sd); |
| } |
| for (i = 0; i < nr; i++) |
| { |
| if (n[i] != -i) |
| abort (); |
| hw_event_queue_deschedule (me, e[i]); |
| } |
| hw_free (me, n); |
| hw_free (me, e); |
| delete_hw_event_data (me); |
| delete_hw_alloc_data (me); |
| } |
| |
| return 0; |
| } |
| #endif |