diff options
Diffstat (limited to 'pintos-progos/devices/timer.c')
| -rw-r--r-- | pintos-progos/devices/timer.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/pintos-progos/devices/timer.c b/pintos-progos/devices/timer.c new file mode 100644 index 0000000..befaaae --- /dev/null +++ b/pintos-progos/devices/timer.c | |||
| @@ -0,0 +1,246 @@ | |||
| 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 | /* Sets up the timer to interrupt TIMER_FREQ times per second, | ||
| 34 | and registers the corresponding interrupt. */ | ||
| 35 | void | ||
| 36 | timer_init (void) | ||
| 37 | { | ||
| 38 | pit_configure_channel (0, 2, TIMER_FREQ); | ||
| 39 | intr_register_ext (0x20, timer_interrupt, "8254 Timer"); | ||
| 40 | } | ||
| 41 | |||
| 42 | /* Calibrates loops_per_tick, used to implement brief delays. */ | ||
| 43 | void | ||
| 44 | timer_calibrate (void) | ||
| 45 | { | ||
| 46 | unsigned high_bit, test_bit; | ||
| 47 | |||
| 48 | ASSERT (intr_get_level () == INTR_ON); | ||
| 49 | printf ("Calibrating timer... "); | ||
| 50 | |||
| 51 | /* Approximate loops_per_tick as the largest power-of-two | ||
| 52 | still less than one timer tick. */ | ||
| 53 | loops_per_tick = 1u << 10; | ||
| 54 | while (!too_many_loops (loops_per_tick << 1)) | ||
| 55 | { | ||
| 56 | loops_per_tick <<= 1; | ||
| 57 | ASSERT (loops_per_tick != 0); | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Refine the next 8 bits of loops_per_tick. */ | ||
| 61 | high_bit = loops_per_tick; | ||
| 62 | for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1) | ||
| 63 | if (!too_many_loops (high_bit | test_bit)) | ||
| 64 | loops_per_tick |= test_bit; | ||
| 65 | |||
| 66 | printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ); | ||
| 67 | } | ||
| 68 | |||
| 69 | /* Returns the number of timer ticks since the OS booted. */ | ||
| 70 | int64_t | ||
| 71 | timer_ticks (void) | ||
| 72 | { | ||
| 73 | enum intr_level old_level = intr_disable (); | ||
| 74 | int64_t t = ticks; | ||
| 75 | intr_set_level (old_level); | ||
| 76 | return t; | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Returns the number of timer ticks elapsed since THEN, which | ||
| 80 | should be a value once returned by timer_ticks(). */ | ||
| 81 | int64_t | ||
| 82 | timer_elapsed (int64_t then) | ||
| 83 | { | ||
| 84 | return timer_ticks () - then; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Sleeps for approximately TICKS timer ticks. Interrupts must | ||
| 88 | be turned on. */ | ||
| 89 | void | ||
| 90 | timer_sleep (int64_t ticks) | ||
| 91 | { | ||
| 92 | int64_t start = timer_ticks (); | ||
| 93 | |||
| 94 | ASSERT (intr_get_level () == INTR_ON); | ||
| 95 | while (timer_elapsed (start) < ticks) | ||
| 96 | thread_yield (); | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Sleeps for approximately MS milliseconds. Interrupts must be | ||
| 100 | turned on. */ | ||
| 101 | void | ||
| 102 | timer_msleep (int64_t ms) | ||
| 103 | { | ||
| 104 | real_time_sleep (ms, 1000); | ||
| 105 | } | ||
| 106 | |||
| 107 | /* Sleeps for approximately US microseconds. Interrupts must be | ||
| 108 | turned on. */ | ||
| 109 | void | ||
| 110 | timer_usleep (int64_t us) | ||
| 111 | { | ||
| 112 | real_time_sleep (us, 1000 * 1000); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Sleeps for approximately NS nanoseconds. Interrupts must be | ||
| 116 | turned on. */ | ||
| 117 | void | ||
| 118 | timer_nsleep (int64_t ns) | ||
| 119 | { | ||
| 120 | real_time_sleep (ns, 1000 * 1000 * 1000); | ||
| 121 | } | ||
| 122 | |||
| 123 | /* Busy-waits for approximately MS milliseconds. Interrupts need | ||
| 124 | not be turned on. | ||
| 125 | |||
| 126 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 127 | interrupts off for the interval between timer ticks or longer | ||
| 128 | will cause timer ticks to be lost. Thus, use timer_msleep() | ||
| 129 | instead if interrupts are enabled. */ | ||
| 130 | void | ||
| 131 | timer_mdelay (int64_t ms) | ||
| 132 | { | ||
| 133 | real_time_delay (ms, 1000); | ||
| 134 | } | ||
| 135 | |||
| 136 | /* Sleeps for approximately US microseconds. Interrupts need not | ||
| 137 | be turned on. | ||
| 138 | |||
| 139 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 140 | interrupts off for the interval between timer ticks or longer | ||
| 141 | will cause timer ticks to be lost. Thus, use timer_usleep() | ||
| 142 | instead if interrupts are enabled. */ | ||
| 143 | void | ||
| 144 | timer_udelay (int64_t us) | ||
| 145 | { | ||
| 146 | real_time_delay (us, 1000 * 1000); | ||
| 147 | } | ||
| 148 | |||
| 149 | /* Sleeps execution for approximately NS nanoseconds. Interrupts | ||
| 150 | need not be turned on. | ||
| 151 | |||
| 152 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 153 | interrupts off for the interval between timer ticks or longer | ||
| 154 | will cause timer ticks to be lost. Thus, use timer_nsleep() | ||
| 155 | instead if interrupts are enabled.*/ | ||
| 156 | void | ||
| 157 | timer_ndelay (int64_t ns) | ||
| 158 | { | ||
| 159 | real_time_delay (ns, 1000 * 1000 * 1000); | ||
| 160 | } | ||
| 161 | |||
| 162 | /* Prints timer statistics. */ | ||
| 163 | void | ||
| 164 | timer_print_stats (void) | ||
| 165 | { | ||
| 166 | printf ("Timer: %"PRId64" ticks\n", timer_ticks ()); | ||
| 167 | } | ||
| 168 | |||
| 169 | /* Timer interrupt handler. */ | ||
| 170 | static void | ||
| 171 | timer_interrupt (struct intr_frame *args UNUSED) | ||
| 172 | { | ||
| 173 | ticks++; | ||
| 174 | thread_tick (); | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Returns true if LOOPS iterations waits for more than one timer | ||
| 178 | tick, otherwise false. */ | ||
| 179 | static bool | ||
| 180 | too_many_loops (unsigned loops) | ||
| 181 | { | ||
| 182 | /* Wait for a timer tick. */ | ||
| 183 | int64_t start = ticks; | ||
| 184 | while (ticks == start) | ||
| 185 | barrier (); | ||
| 186 | |||
| 187 | /* Run LOOPS loops. */ | ||
| 188 | start = ticks; | ||
| 189 | busy_wait (loops); | ||
| 190 | |||
| 191 | /* If the tick count changed, we iterated too long. */ | ||
| 192 | barrier (); | ||
| 193 | return start != ticks; | ||
| 194 | } | ||
| 195 | |||
| 196 | /* Iterates through a simple loop LOOPS times, for implementing | ||
| 197 | brief delays. | ||
| 198 | |||
| 199 | Marked NO_INLINE because code alignment can significantly | ||
| 200 | affect timings, so that if this function was inlined | ||
| 201 | differently in different places the results would be difficult | ||
| 202 | to predict. */ | ||
| 203 | static void NO_INLINE | ||
| 204 | busy_wait (int64_t loops) | ||
| 205 | { | ||
| 206 | while (loops-- > 0) | ||
| 207 | barrier (); | ||
| 208 | } | ||
| 209 | |||
| 210 | /* Sleep for approximately NUM/DENOM seconds. */ | ||
| 211 | static void | ||
| 212 | real_time_sleep (int64_t num, int32_t denom) | ||
| 213 | { | ||
| 214 | /* Convert NUM/DENOM seconds into timer ticks, rounding down. | ||
| 215 | |||
| 216 | (NUM / DENOM) s | ||
| 217 | ---------------------- = NUM * TIMER_FREQ / DENOM ticks. | ||
| 218 | 1 s / TIMER_FREQ ticks | ||
| 219 | */ | ||
| 220 | int64_t ticks = num * TIMER_FREQ / denom; | ||
| 221 | |||
| 222 | ASSERT (intr_get_level () == INTR_ON); | ||
| 223 | if (ticks > 0) | ||
| 224 | { | ||
| 225 | /* We're waiting for at least one full timer tick. Use | ||
| 226 | timer_sleep() because it will yield the CPU to other | ||
| 227 | processes. */ | ||
| 228 | timer_sleep (ticks); | ||
| 229 | } | ||
| 230 | else | ||
| 231 | { | ||
| 232 | /* Otherwise, use a busy-wait loop for more accurate | ||
| 233 | sub-tick timing. */ | ||
| 234 | real_time_delay (num, denom); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | /* Busy-wait for approximately NUM/DENOM seconds. */ | ||
| 239 | static void | ||
| 240 | real_time_delay (int64_t num, int32_t denom) | ||
| 241 | { | ||
| 242 | /* Scale the numerator and denominator down by 1000 to avoid | ||
| 243 | the possibility of overflow. */ | ||
| 244 | ASSERT (denom % 1000 == 0); | ||
| 245 | busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000)); | ||
| 246 | } | ||
