1 #include "devices/timer.h"
6 #include "devices/pit.h"
7 #include "threads/interrupt.h"
8 #include "threads/synch.h"
9 #include "threads/thread.h"
11 /* See [8254] for hardware details of the 8254 timer chip. */
14 #error 8254 timer requires TIMER_FREQ >= 19
17 #error TIMER_FREQ <= 1000 recommended
20 /* Number of timer ticks since OS booted. */
23 /* Number of loops per timer tick.
24 Initialized by timer_calibrate(). */
25 static unsigned loops_per_tick;
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);
33 /* Sets up the timer to interrupt TIMER_FREQ times per second,
34 and registers the corresponding interrupt. */
38 pit_configure_channel (0, 2, TIMER_FREQ);
39 intr_register_ext (0x20, timer_interrupt, "8254 Timer");
42 /* Calibrates loops_per_tick, used to implement brief delays. */
44 timer_calibrate (void)
46 unsigned high_bit, test_bit;
48 ASSERT (intr_get_level () == INTR_ON);
49 printf ("Calibrating timer... ");
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))
57 ASSERT (loops_per_tick != 0);
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;
66 printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ);
69 /* Returns the number of timer ticks since the OS booted. */
73 enum intr_level old_level = intr_disable ();
75 intr_set_level (old_level);
80 /* Returns the number of timer ticks elapsed since THEN, which
81 should be a value once returned by timer_ticks(). */
83 timer_elapsed (int64_t then)
85 return timer_ticks () - then;
88 /* Sleeps for approximately TICKS timer ticks. Interrupts must
91 timer_sleep (int64_t ticks)
93 int64_t start = timer_ticks ();
95 ASSERT (intr_get_level () == INTR_ON);
96 while (timer_elapsed (start) < ticks)
100 /* Sleeps for approximately MS milliseconds. Interrupts must be
103 timer_msleep (int64_t ms)
105 real_time_sleep (ms, 1000);
108 /* Sleeps for approximately US microseconds. Interrupts must be
111 timer_usleep (int64_t us)
113 real_time_sleep (us, 1000 * 1000);
116 /* Sleeps for approximately NS nanoseconds. Interrupts must be
119 timer_nsleep (int64_t ns)
121 real_time_sleep (ns, 1000 * 1000 * 1000);
124 /* Busy-waits for approximately MS milliseconds. Interrupts need
127 Busy waiting wastes CPU cycles, and busy waiting with
128 interrupts off for the interval between timer ticks or longer
129 will cause timer ticks to be lost. Thus, use timer_msleep()
130 instead if interrupts are enabled. */
132 timer_mdelay (int64_t ms)
134 real_time_delay (ms, 1000);
137 /* Sleeps for approximately US microseconds. Interrupts need not
140 Busy waiting wastes CPU cycles, and busy waiting with
141 interrupts off for the interval between timer ticks or longer
142 will cause timer ticks to be lost. Thus, use timer_usleep()
143 instead if interrupts are enabled. */
145 timer_udelay (int64_t us)
147 real_time_delay (us, 1000 * 1000);
150 /* Sleeps execution for approximately NS nanoseconds. Interrupts
151 need not be turned on.
153 Busy waiting wastes CPU cycles, and busy waiting with
154 interrupts off for the interval between timer ticks or longer
155 will cause timer ticks to be lost. Thus, use timer_nsleep()
156 instead if interrupts are enabled.*/
158 timer_ndelay (int64_t ns)
160 real_time_delay (ns, 1000 * 1000 * 1000);
163 /* Prints timer statistics. */
165 timer_print_stats (void)
167 printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
170 /* Timer interrupt handler. */
172 timer_interrupt (struct intr_frame *args UNUSED)
178 /* Returns true if LOOPS iterations waits for more than one timer
179 tick, otherwise false. */
181 too_many_loops (unsigned loops)
183 /* Wait for a timer tick. */
184 int64_t start = ticks;
185 while (ticks == start)
188 /* Run LOOPS loops. */
192 /* If the tick count changed, we iterated too long. */
194 return start != ticks;
197 /* Iterates through a simple loop LOOPS times, for implementing
200 Marked NO_INLINE because code alignment can significantly
201 affect timings, so that if this function was inlined
202 differently in different places the results would be difficult
204 static void NO_INLINE
205 busy_wait (int64_t loops)
211 /* Sleep for approximately NUM/DENOM seconds. */
213 real_time_sleep (int64_t num, int32_t denom)
215 /* Convert NUM/DENOM seconds into timer ticks, rounding down.
218 ---------------------- = NUM * TIMER_FREQ / DENOM ticks.
219 1 s / TIMER_FREQ ticks
221 int64_t ticks = num * TIMER_FREQ / denom;
223 ASSERT (intr_get_level () == INTR_ON);
226 /* We're waiting for at least one full timer tick. Use
227 timer_sleep() because it will yield the CPU to other
233 /* Otherwise, use a busy-wait loop for more accurate
235 real_time_delay (num, denom);
239 /* Busy-wait for approximately NUM/DENOM seconds. */
241 real_time_delay (int64_t num, int32_t denom)
243 /* Scale the numerator and denominator down by 1000 to avoid
244 the possibility of overflow. */
245 ASSERT (denom % 1000 == 0);
246 busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000));