diff options
author | Vic Yang <victoryang@chromium.org> | 2013-05-20 00:00:27 +0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-01-06 12:40:45 +0000 |
commit | cdcaf6ed8a1d18bdedb72fb665263c0dbff0ac8e (patch) | |
tree | ab01862ee3a4114e43ec974fbbbdd615e1e61d63 /core/host | |
parent | 7c673390aecdd35da01ddf446a23667d95dccef1 (diff) | |
download | chrome-ec-cdcaf6ed8a1d18bdedb72fb665263c0dbff0ac8e.tar.gz |
Add interrupt support for emulator
This provides us a way to inject interrupts during a test. If a test has
interrupt_generator() defined, it will run in a separate thread. The
generator can then trigger interrupts when it decides to. The current
running task is suspended while emulator is executing ISR.
Also fixes a bug that tasks run without scheduler notifying them during
emulator start-up.
BUG=chrome-os-partner:19235
TEST=Repeatedly run all tests.
BRANCH=None
Change-Id: I0f921c47c0f848a9626da6272d9040e2b7c5ac86
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/55671
Diffstat (limited to 'core/host')
-rw-r--r-- | core/host/task.c | 179 | ||||
-rw-r--r-- | core/host/timer.c | 14 |
2 files changed, 171 insertions, 22 deletions
diff --git a/core/host/task.c b/core/host/task.c index e945b603a8..23d16bd802 100644 --- a/core/host/task.c +++ b/core/host/task.c @@ -6,6 +6,8 @@ /* Task scheduling / events module for Chrome EC operating system */ #include <pthread.h> +#include <semaphore.h> +#include <signal.h> #include <stdint.h> #include <stdio.h> @@ -21,6 +23,7 @@ struct emu_task_t { pthread_cond_t resume; uint32_t event; timestamp_t wake_time; + uint8_t started; }; struct task_args { @@ -31,6 +34,17 @@ struct task_args { static struct emu_task_t tasks[TASK_ID_COUNT]; static pthread_cond_t scheduler_cond; static pthread_mutex_t run_lock; +static task_id_t running_task_id; + +static sem_t interrupt_sem; +static pthread_mutex_t interrupt_lock; +static pthread_t interrupt_thread; +static int in_interrupt; +static int interrupt_disabled; +static void (*pending_isr)(void); +static int generator_sleeping; +static timestamp_t generator_sleep_deadline; +static int has_interrupt_generator = 1; static __thread task_id_t my_task_id; /* thread local task id */ @@ -76,17 +90,59 @@ void task_pre_init(void) int in_interrupt_context(void) { - return 0; /* No interrupt support yet */ + return !!in_interrupt; } void interrupt_disable(void) { - /* Not supported yet */ + interrupt_disabled = 1; } void interrupt_enable(void) { - /* Not supported yet */ + interrupt_disabled = 0; +} + +void _task_execute_isr(int sig) +{ + in_interrupt = 1; + pending_isr(); + sem_post(&interrupt_sem); + in_interrupt = 0; +} + +void task_register_interrupt(void) +{ + sem_init(&interrupt_sem, 0, 0); + signal(SIGUSR1, _task_execute_isr); +} + +void task_trigger_test_interrupt(void (*isr)(void)) +{ + if (interrupt_disabled) + return; + pthread_mutex_lock(&interrupt_lock); + + /* Suspend current task and excute ISR */ + pending_isr = isr; + pthread_kill(tasks[running_task_id].thread, SIGUSR1); + + /* Wait for ISR to complete */ + sem_wait(&interrupt_sem); + while (in_interrupt) + ; + pending_isr = NULL; + + pthread_mutex_unlock(&interrupt_lock); +} + +void interrupt_generator_udelay(unsigned us) +{ + generator_sleep_deadline.val = get_time().val + us; + generator_sleeping = 1; + while (get_time().val < generator_sleep_deadline.val) + ; + generator_sleeping = 0; } uint32_t task_set_event(task_id_t tskid, uint32_t event, int wait) @@ -101,12 +157,18 @@ uint32_t task_wait_event(int timeout_us) { int tid = task_get_current(); int ret; + pthread_mutex_lock(&interrupt_lock); if (timeout_us > 0) tasks[tid].wake_time.val = get_time().val + timeout_us; + + /* Transfer control to scheduler */ pthread_cond_signal(&scheduler_cond); pthread_cond_wait(&tasks[tid].resume, &run_lock); + + /* Resume */ ret = tasks[tid].event; tasks[tid].event = 0; + pthread_mutex_unlock(&interrupt_lock); return ret; } @@ -148,6 +210,23 @@ task_id_t task_get_current(void) return my_task_id; } +void wait_for_task_started(void) +{ + int i, ok; + + while (1) { + ok = 1; + for (i = 0; i < TASK_ID_COUNT - 1; ++i) + if (!tasks[i].started) { + msleep(10); + ok = 0; + break; + } + if (ok) + return; + } +} + static task_id_t task_get_next_wake(void) { int i; @@ -165,6 +244,42 @@ static task_id_t task_get_next_wake(void) return which_task; } +static int fast_forward(void) +{ + /* + * No task has event pending, and thus the next time we have an + * event to process must be either of: + * 1. Interrupt generator triggers an interrupt + * 2. The next wake alarm is reached + * So we should check whether an interrupt may happen, and fast + * forward to the nearest among: + * 1. When interrupt generator wakes up + * 2. When the next task wakes up + */ + int task_id = task_get_next_wake(); + + if (!has_interrupt_generator) { + if (task_id == TASK_ID_INVALID) { + return TASK_ID_IDLE; + } else { + force_time(tasks[task_id].wake_time); + return task_id; + } + } + + if (!generator_sleeping) + return TASK_ID_IDLE; + + if (task_id != TASK_ID_INVALID && + tasks[task_id].wake_time.val < generator_sleep_deadline.val) { + force_time(tasks[task_id].wake_time); + return task_id; + } else { + force_time(generator_sleep_deadline); + return TASK_ID_IDLE; + } +} + void task_scheduler(void) { int i; @@ -178,25 +293,12 @@ void task_scheduler(void) break; --i; } - if (i < 0) { - /* - * No task has event pending, and thus we are only - * waiting for the next wake-up timer to fire. Let's - * just find out which timer is the next and fast - * forward the system time to its deadline. - * - * Note that once we have interrupt support, we need - * to take into account the fact that an interrupt - * might set an event before the next timer fires. - */ - i = task_get_next_wake(); - if (i == TASK_ID_INVALID) - i = TASK_ID_IDLE; - else - force_time(tasks[i].wake_time); - } + if (i < 0) + i = fast_forward(); tasks[i].wake_time.val = ~0ull; + running_task_id = i; + tasks[i].started = 1; pthread_cond_signal(&tasks[i].resume); pthread_cond_wait(&scheduler_cond, &run_lock); } @@ -208,17 +310,39 @@ void *_task_start_impl(void *a) struct task_args *arg = task_info + tid; my_task_id = tid; pthread_mutex_lock(&run_lock); + + /* Wait for scheduler */ + task_wait_event(1); tasks[tid].event = 0; + + /* Start the task routine */ (arg->routine)(arg->d); + + /* Catch exited routine */ while (1) task_wait_event(-1); } +test_mockable void interrupt_generator(void) +{ + has_interrupt_generator = 0; +} + +void *_task_int_generator_start(void *d) +{ + my_task_id = TASK_ID_INT_GEN; + interrupt_generator(); + return NULL; +} + int task_start(void) { int i; + task_register_interrupt(); + pthread_mutex_init(&run_lock, NULL); + pthread_mutex_init(&interrupt_lock, NULL); pthread_cond_init(&scheduler_cond, NULL); pthread_mutex_lock(&run_lock); @@ -226,12 +350,27 @@ int task_start(void) for (i = 0; i < TASK_ID_COUNT; ++i) { tasks[i].event = TASK_EVENT_WAKE; tasks[i].wake_time.val = ~0ull; + tasks[i].started = 0; pthread_cond_init(&tasks[i].resume, NULL); pthread_create(&tasks[i].thread, NULL, _task_start_impl, (void *)(uintptr_t)i); pthread_cond_wait(&scheduler_cond, &run_lock); + /* + * Interrupt lock is grabbed by the task which just started. + * Let's unlock it so the next task can be started. + */ + pthread_mutex_unlock(&interrupt_lock); } + /* + * All tasks are now waiting in task_wait_event(). Lock interrupt_lock + * here so the first task chosen sees it locked. + */ + pthread_mutex_lock(&interrupt_lock); + + pthread_create(&interrupt_thread, NULL, + _task_int_generator_start, NULL); + task_scheduler(); return 0; diff --git a/core/host/timer.c b/core/host/timer.c index d945a2c4f2..dd8b88dbb4 100644 --- a/core/host/timer.c +++ b/core/host/timer.c @@ -10,7 +10,9 @@ #include <time.h> #include "task.h" +#include "test_util.h" #include "timer.h" +#include "util.h" /* * For test that need to test for longer than 10 seconds, adjust @@ -26,6 +28,8 @@ static int time_set; void usleep(unsigned us) { + ASSERT(!in_interrupt_context() && + task_get_current() != TASK_ID_INT_GEN); task_wait_event(us); } @@ -55,8 +59,14 @@ void force_time(timestamp_t ts) void udelay(unsigned us) { - timestamp_t deadline = get_time(); - deadline.val += us; + timestamp_t deadline; + + if (!in_interrupt_context() && task_get_current() == TASK_ID_INT_GEN) { + interrupt_generator_udelay(us); + return; + } + + deadline.val = get_time().val + us; while (get_time().val < deadline.val) ; } |