diff options
Diffstat (limited to 'devices/timer.c')
| -rw-r--r-- | devices/timer.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/devices/timer.c b/devices/timer.c new file mode 100644 index 0000000..3533fe7 --- /dev/null +++ b/devices/timer.c | |||
| @@ -0,0 +1,297 @@ | |||
| 1 | #include "devices/timer.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <inttypes.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include "devices/pit.h" | ||
| 7 | #include "threads/interrupt.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | |||
| 11 | /* See [8254] for hardware details of the 8254 timer chip. */ | ||
| 12 | |||
| 13 | #if TIMER_FREQ < 19 | ||
| 14 | #error 8254 timer requires TIMER_FREQ >= 19 | ||
| 15 | #endif | ||
| 16 | #if TIMER_FREQ > 1000 | ||
| 17 | #error TIMER_FREQ <= 1000 recommended | ||
| 18 | #endif | ||
| 19 | |||
| 20 | /* Number of timer ticks since OS booted. */ | ||
| 21 | static int64_t ticks; | ||
| 22 | |||
| 23 | /* Number of loops per timer tick. | ||
| 24 | Initialized by timer_calibrate(). */ | ||
| 25 | static unsigned loops_per_tick; | ||
| 26 | |||
| 27 | static intr_handler_func timer_interrupt; | ||
| 28 | static bool too_many_loops (unsigned loops); | ||
| 29 | static void busy_wait (int64_t loops); | ||
| 30 | static void real_time_sleep (int64_t num, int32_t denom); | ||
| 31 | static void real_time_delay (int64_t num, int32_t denom); | ||
| 32 | |||
| 33 | /* list of processes waiting for an wakeup event | ||
| 34 | * and currently put to sleep */ | ||
| 35 | static struct list wakeup_list; | ||
| 36 | |||
| 37 | /* Sets up the timer to interrupt TIMER_FREQ times per second, | ||
| 38 | and registers the corresponding interrupt. */ | ||
| 39 | void | ||
| 40 | timer_init (void) | ||
| 41 | { | ||
| 42 | list_init (&wakeup_list); | ||
| 43 | pit_configure_channel (0, 2, TIMER_FREQ); | ||
| 44 | intr_register_ext (0x20, timer_interrupt, "8254 Timer"); | ||
| 45 | } | ||
| 46 | |||
| 47 | /* Calibrates loops_per_tick, used to implement brief delays. */ | ||
| 48 | void | ||
| 49 | timer_calibrate (void) | ||
| 50 | { | ||
| 51 | unsigned high_bit, test_bit; | ||
| 52 | |||
| 53 | ASSERT (intr_get_level () == INTR_ON); | ||
| 54 | printf ("Calibrating timer... "); | ||
| 55 | |||
| 56 | /* Approximate loops_per_tick as the largest power-of-two | ||
| 57 | still less than one timer tick. */ | ||
| 58 | loops_per_tick = 1u << 10; | ||
| 59 | while (!too_many_loops (loops_per_tick << 1)) | ||
| 60 | { | ||
| 61 | loops_per_tick <<= 1; | ||
| 62 | ASSERT (loops_per_tick != 0); | ||
| 63 | } | ||
| 64 | |||
| 65 | /* Refine the next 8 bits of loops_per_tick. */ | ||
| 66 | high_bit = loops_per_tick; | ||
| 67 | for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1) | ||
| 68 | if (!too_many_loops (high_bit | test_bit)) | ||
| 69 | loops_per_tick |= test_bit; | ||
| 70 | |||
| 71 | printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ); | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Returns the number of timer ticks since the OS booted. */ | ||
| 75 | int64_t | ||
| 76 | timer_ticks (void) | ||
| 77 | { | ||
| 78 | enum intr_level old_level = intr_disable (); | ||
| 79 | int64_t t = ticks; | ||
| 80 | intr_set_level (old_level); | ||
| 81 | return t; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* Returns the number of timer ticks elapsed since THEN, which | ||
| 85 | should be a value once returned by timer_ticks(). */ | ||
| 86 | int64_t | ||
| 87 | timer_elapsed (int64_t then) | ||
| 88 | { | ||
| 89 | return timer_ticks () - then; | ||
| 90 | } | ||
| 91 | |||
| 92 | /* Comparison function for our ascending ordered wakeup list. */ | ||
| 93 | static bool | ||
| 94 | wakeup_list_cmp_less(const struct list_elem *a, const struct list_elem *b, | ||
| 95 | void *AUX UNUSED) | ||
| 96 | { | ||
| 97 | struct thread *t1 = list_entry (a, struct thread, elem); | ||
| 98 | struct thread *t2 = list_entry (b, struct thread, elem); | ||
| 99 | return (t1->wakeup_tick < t2->wakeup_tick); | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Sleeps for approximately TICKS timer ticks. Interrupts must | ||
| 103 | be turned on. */ | ||
| 104 | void | ||
| 105 | timer_sleep (int64_t ticks) | ||
| 106 | { | ||
| 107 | struct thread *t = thread_current (); | ||
| 108 | enum intr_level old_level; | ||
| 109 | |||
| 110 | ASSERT (intr_get_level () == INTR_ON); | ||
| 111 | |||
| 112 | /* nothing to sleep here */ | ||
| 113 | if (ticks <= 0) | ||
| 114 | return; | ||
| 115 | |||
| 116 | t->wakeup_tick = timer_ticks () + ticks; | ||
| 117 | |||
| 118 | /* add thread to sorted wakeup list | ||
| 119 | * disable interrupts as this is critical */ | ||
| 120 | old_level = intr_disable (); | ||
| 121 | ASSERT (t->status == THREAD_RUNNING); | ||
| 122 | list_insert_ordered (&wakeup_list, &t->elem, &wakeup_list_cmp_less, NULL); | ||
| 123 | |||
| 124 | /* block the thread */ | ||
| 125 | thread_block (); | ||
| 126 | |||
| 127 | /* restore interrupt */ | ||
| 128 | intr_set_level (old_level); | ||
| 129 | } | ||
| 130 | |||
| 131 | /* Sleeps for approximately MS milliseconds. Interrupts must be | ||
| 132 | turned on. */ | ||
| 133 | void | ||
| 134 | timer_msleep (int64_t ms) | ||
| 135 | { | ||
| 136 | real_time_sleep (ms, 1000); | ||
| 137 | } | ||
| 138 | |||
| 139 | /* Sleeps for approximately US microseconds. Interrupts must be | ||
| 140 | turned on. */ | ||
| 141 | void | ||
| 142 | timer_usleep (int64_t us) | ||
| 143 | { | ||
| 144 | real_time_sleep (us, 1000 * 1000); | ||
| 145 | } | ||
| 146 | |||
| 147 | /* Sleeps for approximately NS nanoseconds. Interrupts must be | ||
| 148 | turned on. */ | ||
| 149 | void | ||
| 150 | timer_nsleep (int64_t ns) | ||
| 151 | { | ||
| 152 | real_time_sleep (ns, 1000 * 1000 * 1000); | ||
| 153 | } | ||
| 154 | |||
| 155 | /* Busy-waits for approximately MS milliseconds. Interrupts need | ||
| 156 | not be turned on. | ||
| 157 | |||
| 158 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 159 | interrupts off for the interval between timer ticks or longer | ||
| 160 | will cause timer ticks to be lost. Thus, use timer_msleep() | ||
| 161 | instead if interrupts are enabled. */ | ||
| 162 | void | ||
| 163 | timer_mdelay (int64_t ms) | ||
| 164 | { | ||
| 165 | real_time_delay (ms, 1000); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* Sleeps for approximately US microseconds. Interrupts need not | ||
| 169 | be turned on. | ||
| 170 | |||
| 171 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 172 | interrupts off for the interval between timer ticks or longer | ||
| 173 | will cause timer ticks to be lost. Thus, use timer_usleep() | ||
| 174 | instead if interrupts are enabled. */ | ||
| 175 | void | ||
| 176 | timer_udelay (int64_t us) | ||
| 177 | { | ||
| 178 | real_time_delay (us, 1000 * 1000); | ||
| 179 | } | ||
| 180 | |||
| 181 | /* Sleeps execution for approximately NS nanoseconds. Interrupts | ||
| 182 | need not be turned on. | ||
| 183 | |||
| 184 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 185 | interrupts off for the interval between timer ticks or longer | ||
| 186 | will cause timer ticks to be lost. Thus, use timer_nsleep() | ||
| 187 | instead if interrupts are enabled.*/ | ||
| 188 | void | ||
| 189 | timer_ndelay (int64_t ns) | ||
| 190 | { | ||
| 191 | real_time_delay (ns, 1000 * 1000 * 1000); | ||
| 192 | } | ||
| 193 | |||
| 194 | /* Prints timer statistics. */ | ||
| 195 | void | ||
| 196 | timer_print_stats (void) | ||
| 197 | { | ||
| 198 | printf ("Timer: %"PRId64" ticks\n", timer_ticks ()); | ||
| 199 | } | ||
| 200 | |||
| 201 | /* Timer interrupt handler. */ | ||
| 202 | static void | ||
| 203 | timer_interrupt (struct intr_frame *args UNUSED) | ||
| 204 | { | ||
| 205 | struct list_elem *el, *next; | ||
| 206 | |||
| 207 | ticks++; | ||
| 208 | thread_tick (); | ||
| 209 | |||
| 210 | /* Check threads waiting to get woken up/unblocked. | ||
| 211 | * Since this is an ordered list we can bail out as soon | ||
| 212 | * as the first element doesn't need to get unblocked. */ | ||
| 213 | for (el = list_begin (&wakeup_list); el != list_end (&wakeup_list); el = next) | ||
| 214 | { | ||
| 215 | struct thread *t = list_entry (el, struct thread, elem); | ||
| 216 | if (t->wakeup_tick == timer_ticks ()) | ||
| 217 | { | ||
| 218 | next = list_remove (el); | ||
| 219 | /* unblock must be called AFTER removing, | ||
| 220 | * as thread_unblock() will reuse t.elem */ | ||
| 221 | thread_unblock (t); | ||
| 222 | continue; | ||
| 223 | } | ||
| 224 | break; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | /* Returns true if LOOPS iterations waits for more than one timer | ||
| 229 | tick, otherwise false. */ | ||
| 230 | static bool | ||
| 231 | too_many_loops (unsigned loops) | ||
| 232 | { | ||
| 233 | /* Wait for a timer tick. */ | ||
| 234 | int64_t start = ticks; | ||
| 235 | while (ticks == start) | ||
| 236 | barrier (); | ||
| 237 | |||
| 238 | /* Run LOOPS loops. */ | ||
| 239 | start = ticks; | ||
| 240 | busy_wait (loops); | ||
| 241 | |||
| 242 | /* If the tick count changed, we iterated too long. */ | ||
| 243 | barrier (); | ||
| 244 | return start != ticks; | ||
| 245 | } | ||
| 246 | |||
| 247 | /* Iterates through a simple loop LOOPS times, for implementing | ||
| 248 | brief delays. | ||
| 249 | |||
| 250 | Marked NO_INLINE because code alignment can significantly | ||
| 251 | affect timings, so that if this function was inlined | ||
| 252 | differently in different places the results would be difficult | ||
| 253 | to predict. */ | ||
| 254 | static void NO_INLINE | ||
| 255 | busy_wait (int64_t loops) | ||
| 256 | { | ||
| 257 | while (loops-- > 0) | ||
| 258 | barrier (); | ||
| 259 | } | ||
| 260 | |||
| 261 | /* Sleep for approximately NUM/DENOM seconds. */ | ||
| 262 | static void | ||
| 263 | real_time_sleep (int64_t num, int32_t denom) | ||
| 264 | { | ||
| 265 | /* Convert NUM/DENOM seconds into timer ticks, rounding down. | ||
| 266 | |||
| 267 | (NUM / DENOM) s | ||
| 268 | ---------------------- = NUM * TIMER_FREQ / DENOM ticks. | ||
| 269 | 1 s / TIMER_FREQ ticks | ||
| 270 | */ | ||
| 271 | int64_t ticks = num * TIMER_FREQ / denom; | ||
| 272 | |||
| 273 | ASSERT (intr_get_level () == INTR_ON); | ||
| 274 | if (ticks > 0) | ||
| 275 | { | ||
| 276 | /* We're waiting for at least one full timer tick. Use | ||
| 277 | timer_sleep() because it will yield the CPU to other | ||
| 278 | processes. */ | ||
| 279 | timer_sleep (ticks); | ||
| 280 | } | ||
| 281 | else | ||
| 282 | { | ||
| 283 | /* Otherwise, use a busy-wait loop for more accurate | ||
| 284 | sub-tick timing. */ | ||
| 285 | real_time_delay (num, denom); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | /* Busy-wait for approximately NUM/DENOM seconds. */ | ||
| 290 | static void | ||
| 291 | real_time_delay (int64_t num, int32_t denom) | ||
| 292 | { | ||
| 293 | /* Scale the numerator and denominator down by 1000 to avoid | ||
| 294 | the possibility of overflow. */ | ||
| 295 | ASSERT (denom % 1000 == 0); | ||
| 296 | busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000)); | ||
| 297 | } | ||
