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 (loops_per_tick | 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);
79 /* Returns the number of timer ticks elapsed since THEN, which
80 should be a value once returned by timer_ticks(). */
82 timer_elapsed (int64_t then)
84 return timer_ticks () - then;
87 /* Sleeps for approximately TICKS timer ticks. Interrupts must
90 timer_sleep (int64_t ticks)
92 int64_t start = timer_ticks ();
94 ASSERT (intr_get_level () == INTR_ON);
95 while (timer_elapsed (start) < ticks)
99 /* Sleeps for approximately MS milliseconds. Interrupts must be
102 timer_msleep (int64_t ms)
104 real_time_sleep (ms, 1000);
107 /* Sleeps for approximately US microseconds. Interrupts must be
110 timer_usleep (int64_t us)
112 real_time_sleep (us, 1000 * 1000);
115 /* Sleeps for approximately NS nanoseconds. Interrupts must be
118 timer_nsleep (int64_t ns)
120 real_time_sleep (ns, 1000 * 1000 * 1000);
123 /* Busy-waits for approximately MS milliseconds. Interrupts need
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. */
131 timer_mdelay (int64_t ms)
133 real_time_delay (ms, 1000);
136 /* Sleeps for approximately US microseconds. Interrupts need not
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. */
144 timer_udelay (int64_t us)
146 real_time_delay (us, 1000 * 1000);
149 /* Sleeps execution for approximately NS nanoseconds. Interrupts
150 need not be turned on.
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.*/
157 timer_ndelay (int64_t ns)
159 real_time_delay (ns, 1000 * 1000 * 1000);
162 /* Prints timer statistics. */
164 timer_print_stats (void)
166 printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
169 /* Timer interrupt handler. */
171 timer_interrupt (struct intr_frame *args UNUSED)
177 /* Returns true if LOOPS iterations waits for more than one timer
178 tick, otherwise false. */
180 too_many_loops (unsigned loops)
182 /* Wait for a timer tick. */
183 int64_t start = ticks;
184 while (ticks == start)
187 /* Run LOOPS loops. */
191 /* If the tick count changed, we iterated too long. */
193 return start != ticks;
196 /* Iterates through a simple loop LOOPS times, for implementing
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
203 static void NO_INLINE
204 busy_wait (int64_t loops)
210 /* Sleep for approximately NUM/DENOM seconds. */
212 real_time_sleep (int64_t num, int32_t denom)
214 /* Convert NUM/DENOM seconds into timer ticks, rounding down.
217 ---------------------- = NUM * TIMER_FREQ / DENOM ticks.
218 1 s / TIMER_FREQ ticks
220 int64_t ticks = num * TIMER_FREQ / denom;
222 ASSERT (intr_get_level () == INTR_ON);
225 /* We're waiting for at least one full timer tick. Use
226 timer_sleep() because it will yield the CPU to other
232 /* Otherwise, use a busy-wait loop for more accurate
234 real_time_delay (num, denom);
238 /* Busy-wait for approximately NUM/DENOM seconds. */
240 real_time_delay (int64_t num, int32_t denom)
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));