PKUOS - Pintos
Pintos source browser for PKU Operating System course
timer.c
Go to the documentation of this file.
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. */
22
23/** Number of loops per timer tick.
24 Initialized by timer_calibrate(). */
25static unsigned loops_per_tick;
26
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
37{
39 intr_register_ext (0x20, timer_interrupt, "8254 Timer");
40}
41
42/** Calibrates loops_per_tick, used to implement brief delays. */
43void
45{
46 unsigned high_bit, test_bit;
47
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;
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. */
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(). */
83{
84 return timer_ticks () - then;
85}
86
87/** Sleeps for approximately TICKS timer ticks. Interrupts must
88 be turned on. */
89void
91{
93
95 while (timer_elapsed (start) < ticks)
96 thread_yield ();
97}
98
99/** Sleeps for approximately MS milliseconds. Interrupts must be
100 turned on. */
101void
103{
104 real_time_sleep (ms, 1000);
105}
106
107/** Sleeps for approximately US microseconds. Interrupts must be
108 turned on. */
109void
111{
112 real_time_sleep (us, 1000 * 1000);
113}
114
115/** Sleeps for approximately NS nanoseconds. Interrupts must be
116 turned on. */
117void
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
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
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
158{
159 real_time_delay (ns, 1000 * 1000 * 1000);
160}
161
162/** Prints timer statistics. */
163void
165{
166 printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
167}
168
169/** Timer interrupt handler. */
170static void
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. */
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
205{
206 while (loops-- > 0)
207 barrier ();
208}
209
210/** Sleep for approximately NUM/DENOM seconds. */
211static void
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
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. */
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
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}
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
#define NO_INLINE
Definition: debug.h:9
#define UNUSED
GCC lets us add "attributes" to functions, function parameters, etc.
Definition: debug.h:7
char * start[]
Insult.c.
Definition: insult.c:13
void intr_register_ext(uint8_t vec_no, intr_handler_func *handler, const char *name)
Registers external interrupt VEC_NO to invoke HANDLER, which is named NAME for debugging purposes.
Definition: interrupt.c:181
enum intr_level intr_disable(void)
Disables interrupts and returns the previous interrupt status.
Definition: interrupt.c:104
enum intr_level intr_get_level(void)
Returns the current interrupt status.
Definition: interrupt.c:65
enum intr_level intr_set_level(enum intr_level level)
Enables or disables interrupts as specified by LEVEL and returns the previous interrupt status.
Definition: interrupt.c:81
void intr_handler_func(struct intr_frame *)
Definition: interrupt.h:58
intr_level
Interrupts on or off?
Definition: interrupt.h:9
@ INTR_ON
Interrupts enabled.
Definition: interrupt.h:11
#define PRIu64
Definition: inttypes.h:30
#define PRId64
Definition: inttypes.h:27
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
void pit_configure_channel(int channel, int mode, int frequency)
Configure the given CHANNEL in the PIT.
Definition: pit.c:46
signed long long int int64_t
Definition: stdint.h:16
signed int int32_t
Definition: stdint.h:12
unsigned long long int uint64_t
Definition: stdint.h:29
Interrupt stack frame.
Definition: interrupt.h:21
#define barrier()
Optimization barrier.
Definition: synch.h:49
void thread_tick(void)
Called by the timer interrupt handler at each timer tick.
Definition: thread.c:123
void thread_yield(void)
Yields the CPU.
Definition: thread.c:302
void timer_msleep(int64_t ms)
Sleeps for approximately MS milliseconds.
Definition: timer.c:102
static void real_time_delay(int64_t num, int32_t denom)
Busy-wait for approximately NUM/DENOM seconds.
Definition: timer.c:240
int64_t timer_ticks(void)
Returns the number of timer ticks since the OS booted.
Definition: timer.c:71
static unsigned loops_per_tick
Number of loops per timer tick.
Definition: timer.c:25
void timer_print_stats(void)
Prints timer statistics.
Definition: timer.c:164
void timer_udelay(int64_t us)
Sleeps for approximately US microseconds.
Definition: timer.c:144
int64_t timer_elapsed(int64_t then)
Returns the number of timer ticks elapsed since THEN, which should be a value once returned by timer_...
Definition: timer.c:82
static int64_t ticks
See [8254] for hardware details of the 8254 timer chip.
Definition: timer.c:21
void timer_nsleep(int64_t ns)
Sleeps for approximately NS nanoseconds.
Definition: timer.c:118
void timer_init(void)
Sets up the timer to interrupt TIMER_FREQ times per second, and registers the corresponding interrupt...
Definition: timer.c:36
static void busy_wait(int64_t loops)
Iterates through a simple loop LOOPS times, for implementing brief delays.
Definition: timer.c:204
void timer_calibrate(void)
Calibrates loops_per_tick, used to implement brief delays.
Definition: timer.c:44
static bool too_many_loops(unsigned loops)
Returns true if LOOPS iterations waits for more than one timer tick, otherwise false.
Definition: timer.c:180
static void real_time_sleep(int64_t num, int32_t denom)
Sleep for approximately NUM/DENOM seconds.
Definition: timer.c:212
void timer_sleep(int64_t ticks)
Sleeps for approximately TICKS timer ticks.
Definition: timer.c:90
static intr_handler_func timer_interrupt
Definition: timer.c:27
void timer_usleep(int64_t us)
Sleeps for approximately US microseconds.
Definition: timer.c:110
void timer_mdelay(int64_t ms)
Busy-waits for approximately MS milliseconds.
Definition: timer.c:131
void timer_ndelay(int64_t ns)
Sleeps execution for approximately NS nanoseconds.
Definition: timer.c:157
#define TIMER_FREQ
Number of timer interrupts per second.
Definition: timer.h:8