random: Fix behavior of kernel option "-rs".
[pintos-anon] / src / devices / timer.c
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 (loops_per_tick | 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 \f
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 }