CS318 - Pintos
Pintos source browser for JHU CS318 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. */
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 
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 {
39  intr_register_ext (0x20, timer_interrupt, "8254 Timer");
40 }
41 
42 /** Calibrates loops_per_tick, used to implement brief delays. */
43 void
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;
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. */
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
83 {
84  return timer_ticks () - then;
85 }
86 
87 /** Sleeps for approximately TICKS timer ticks. Interrupts must
88  be turned on. */
89 void
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. */
101 void
103 {
104  real_time_sleep (ms, 1000);
105 }
106 
107 /** Sleeps for approximately US microseconds. Interrupts must be
108  turned on. */
109 void
111 {
112  real_time_sleep (us, 1000 * 1000);
113 }
114 
115 /** Sleeps for approximately NS nanoseconds. Interrupts must be
116  turned on. */
117 void
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
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
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
158 {
159  real_time_delay (ns, 1000 * 1000 * 1000);
160 }
161 
162 /** Prints timer statistics. */
163 void
165 {
166  printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
167 }
168 
169 /** Timer interrupt handler. */
170 static 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. */
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
205 {
206  while (loops-- > 0)
207  barrier ();
208 }
209 
210 /** Sleep for approximately NUM/DENOM seconds. */
211 static 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 
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
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 }
uint64_t
unsigned long long int uint64_t
Definition: stdint.h:29
timer_interrupt
static intr_handler_func timer_interrupt
Definition: timer.c:27
timer_nsleep
void timer_nsleep(int64_t ns)
Sleeps for approximately NS nanoseconds.
Definition: timer.c:118
timer_udelay
void timer_udelay(int64_t us)
Sleeps for approximately US microseconds.
Definition: timer.c:144
TIMER_FREQ
#define TIMER_FREQ
Number of timer interrupts per second.
Definition: timer.h:8
start
char * start[]
Insult.c.
Definition: insult.c:13
timer_msleep
void timer_msleep(int64_t ms)
Sleeps for approximately MS milliseconds.
Definition: timer.c:102
ticks
static int64_t ticks
See [8254] for hardware details of the 8254 timer chip.
Definition: timer.c:21
intr_level
intr_level
Interrupts on or off?
Definition: interrupt.h:8
NO_INLINE
#define NO_INLINE
Definition: debug.h:9
intr_set_level
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
UNUSED
#define UNUSED
GCC lets us add "attributes" to functions, function parameters, etc.
Definition: debug.h:7
timer_elapsed
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
timer_print_stats
void timer_print_stats(void)
Prints timer statistics.
Definition: timer.c:164
PRId64
#define PRId64
Definition: inttypes.h:27
pit.h
timer_ndelay
void timer_ndelay(int64_t ns)
Sleeps execution for approximately NS nanoseconds.
Definition: timer.c:157
int64_t
signed long long int int64_t
Definition: stdint.h:16
interrupt.h
loops_per_tick
static unsigned loops_per_tick
Number of loops per timer tick.
Definition: timer.c:25
pit_configure_channel
void pit_configure_channel(int channel, int mode, int frequency)
Configure the given CHANNEL in the PIT.
Definition: pit.c:46
printf
int printf(const char *format,...)
Writes formatted output to the console.
Definition: stdio.c:79
intr_get_level
enum intr_level intr_get_level(void)
Returns the current interrupt status.
Definition: interrupt.c:65
timer.h
timer_mdelay
void timer_mdelay(int64_t ms)
Busy-waits for approximately MS milliseconds.
Definition: timer.c:131
too_many_loops
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
timer_sleep
void timer_sleep(int64_t ticks)
Sleeps for approximately TICKS timer ticks.
Definition: timer.c:90
real_time_delay
static void real_time_delay(int64_t num, int32_t denom)
Busy-wait for approximately NUM/DENOM seconds.
Definition: timer.c:240
intr_handler_func
void intr_handler_func(struct intr_frame *)
Definition: interrupt.h:58
round.h
timer_ticks
int64_t timer_ticks(void)
Returns the number of timer ticks since the OS booted.
Definition: timer.c:71
intr_disable
enum intr_level intr_disable(void)
Disables interrupts and returns the previous interrupt status.
Definition: interrupt.c:104
ASSERT
#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
barrier
#define barrier()
Optimization barrier.
Definition: synch.h:49
busy_wait
static void busy_wait(int64_t loops)
Iterates through a simple loop LOOPS times, for implementing brief delays.
Definition: timer.c:204
int32_t
signed int int32_t
Definition: stdint.h:12
timer_usleep
void timer_usleep(int64_t us)
Sleeps for approximately US microseconds.
Definition: timer.c:110
PRIu64
#define PRIu64
Definition: inttypes.h:30
timer_calibrate
void timer_calibrate(void)
Calibrates loops_per_tick, used to implement brief delays.
Definition: timer.c:44
real_time_sleep
static void real_time_sleep(int64_t num, int32_t denom)
Sleep for approximately NUM/DENOM seconds.
Definition: timer.c:212
intr_register_ext
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
intr_frame
Interrupt stack frame.
Definition: interrupt.h:20
timer_init
void timer_init(void)
Sets up the timer to interrupt TIMER_FREQ times per second, and registers the corresponding interrupt...
Definition: timer.c:36
thread.h
synch.h
inttypes.h
thread_tick
void thread_tick(void)
Called by the timer interrupt handler at each timer tick.
Definition: thread.c:123
thread_yield
void thread_yield(void)
Yields the CPU.
Definition: thread.c:302
debug.h
INTR_ON
Interrupts enabled.
Definition: interrupt.h:11