summaryrefslogtreecommitdiffstats
path: root/pintos-progos/devices/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'pintos-progos/devices/timer.c')
-rw-r--r--pintos-progos/devices/timer.c246
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. */
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/* Sets up the timer to interrupt TIMER_FREQ times per second,
34 and registers the corresponding interrupt. */
35void
36timer_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. */
43void
44timer_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. */
70int64_t
71timer_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(). */
81int64_t
82timer_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. */
89void
90timer_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. */
101void
102timer_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. */
109void
110timer_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. */
117void
118timer_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. */
130void
131timer_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. */
143void
144timer_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.*/
156void
157timer_ndelay (int64_t ns)
158{
159 real_time_delay (ns, 1000 * 1000 * 1000);
160}
161
162/* Prints timer statistics. */
163void
164timer_print_stats (void)
165{
166 printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
167}
168
169/* Timer interrupt handler. */
170static void
171timer_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. */
179static bool
180too_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. */
203static void NO_INLINE
204busy_wait (int64_t loops)
205{
206 while (loops-- > 0)
207 barrier ();
208}
209
210/* Sleep for approximately NUM/DENOM seconds. */
211static void
212real_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. */
239static void
240real_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}