summaryrefslogtreecommitdiffstats
path: root/devices/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'devices/timer.c')
-rw-r--r--devices/timer.c297
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. */
21static int64_t ticks;
22
23/* Number of loops per timer tick.
24 Initialized by timer_calibrate(). */
25static unsigned loops_per_tick;
26
27static intr_handler_func timer_interrupt;
28static bool too_many_loops (unsigned loops);
29static void busy_wait (int64_t loops);
30static void real_time_sleep (int64_t num, int32_t denom);
31static 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 */
35static struct list wakeup_list;
36
37/* Sets up the timer to interrupt TIMER_FREQ times per second,
38 and registers the corresponding interrupt. */
39void
40timer_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. */
48void
49timer_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. */
75int64_t
76timer_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(). */
86int64_t
87timer_elapsed (int64_t then)
88{
89 return timer_ticks () - then;
90}
91
92/* Comparison function for our ascending ordered wakeup list. */
93static bool
94wakeup_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. */
104void
105timer_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. */
133void
134timer_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. */
141void
142timer_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. */
149void
150timer_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. */
162void
163timer_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. */
175void
176timer_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.*/
188void
189timer_ndelay (int64_t ns)
190{
191 real_time_delay (ns, 1000 * 1000 * 1000);
192}
193
194/* Prints timer statistics. */
195void
196timer_print_stats (void)
197{
198 printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
199}
200
201/* Timer interrupt handler. */
202static void
203timer_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. */
230static bool
231too_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. */
254static void NO_INLINE
255busy_wait (int64_t loops)
256{
257 while (loops-- > 0)
258 barrier ();
259}
260
261/* Sleep for approximately NUM/DENOM seconds. */
262static void
263real_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. */
290static void
291real_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}