dev: Fixing EtherDevice stats initialization order
[gem5.git] / src / dev / intel_8254_timer.cc
1 /*
2 * Copyright (c) 2004, 2005
3 * The Regents of The University of Michigan
4 * All Rights Reserved
5 *
6 * This code is part of the M5 simulator.
7 *
8 * Permission is granted to use, copy, create derivative works and
9 * redistribute this software and such derivative works for any
10 * purpose, so long as the copyright notice above, this grant of
11 * permission, and the disclaimer below appear in all copies made; and
12 * so long as the name of The University of Michigan is not used in
13 * any advertising or publicity pertaining to the use or distribution
14 * of this software without specific, written prior authorization.
15 *
16 * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE
17 * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND
18 * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER
19 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
22 * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT,
23 * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM
24 * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
25 * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH
26 * DAMAGES.
27 */
28
29 #include "dev/intel_8254_timer.hh"
30
31 #include "base/logging.hh"
32 #include "debug/Intel8254Timer.hh"
33
34 using namespace std;
35
36 Intel8254Timer::Intel8254Timer(EventManager *em, const string &name,
37 Counter *counter0, Counter *counter1, Counter *counter2) :
38 EventManager(em), _name(name)
39 {
40 counter[0] = counter0;
41 counter[1] = counter1;
42 counter[2] = counter2;
43 }
44
45 Intel8254Timer::Intel8254Timer(EventManager *em, const string &name) :
46 EventManager(em), _name(name)
47 {
48 counter[0] = new Counter(this, name + ".counter0", 0);
49 counter[1] = new Counter(this, name + ".counter1", 1);
50 counter[2] = new Counter(this, name + ".counter2", 2);
51 }
52
53 void
54 Intel8254Timer::writeControl(const CtrlReg data)
55 {
56 int sel = data.sel;
57
58 if (sel == ReadBackCommand)
59 panic("PITimer Read-Back Command is not implemented.\n");
60
61 if (data.rw == LatchCommand)
62 counter[sel]->latchCount();
63 else {
64 counter[sel]->setRW(data.rw);
65 counter[sel]->setMode(data.mode);
66 counter[sel]->setBCD(data.bcd);
67 }
68 }
69
70 void
71 Intel8254Timer::serialize(const string &base, CheckpointOut &cp) const
72 {
73 // serialize the counters
74 counter[0]->serialize(base + ".counter0", cp);
75 counter[1]->serialize(base + ".counter1", cp);
76 counter[2]->serialize(base + ".counter2", cp);
77 }
78
79 void
80 Intel8254Timer::unserialize(const string &base, CheckpointIn &cp)
81 {
82 // unserialze the counters
83 counter[0]->unserialize(base + ".counter0", cp);
84 counter[1]->unserialize(base + ".counter1", cp);
85 counter[2]->unserialize(base + ".counter2", cp);
86 }
87
88 void
89 Intel8254Timer::startup()
90 {
91 counter[0]->startup();
92 counter[1]->startup();
93 counter[2]->startup();
94 }
95
96 Intel8254Timer::Counter::Counter(Intel8254Timer *p,
97 const string &name, unsigned int _num)
98 : _name(name), num(_num), event(this), running(false),
99 initial_count(0), latched_count(0), period(0), mode(0),
100 output_high(false), latch_on(false), read_byte(LSB),
101 write_byte(LSB), parent(p)
102 {
103 offset = period * event.getInterval();
104 }
105
106 void
107 Intel8254Timer::Counter::latchCount()
108 {
109 // behave like a real latch
110 if (!latch_on) {
111 latch_on = true;
112 read_byte = LSB;
113 latched_count = currentCount();
114 }
115 }
116
117 int
118 Intel8254Timer::Counter::currentCount()
119 {
120 int clocks = event.clocksLeft();
121 if (clocks == -1) {
122 warn_once("Reading current count from inactive timer.\n");
123 return 0;
124 }
125 if (mode == RateGen || mode == SquareWave)
126 return clocks + 1;
127 else
128 return clocks;
129 }
130
131 uint8_t
132 Intel8254Timer::Counter::read()
133 {
134 if (latch_on) {
135 switch (read_byte) {
136 case LSB:
137 read_byte = MSB;
138 return (uint8_t)latched_count;
139 break;
140 case MSB:
141 read_byte = LSB;
142 latch_on = false;
143 return latched_count >> 8;
144 break;
145 default:
146 panic("Shouldn't be here");
147 }
148 } else {
149 uint16_t count = currentCount();
150 switch (read_byte) {
151 case LSB:
152 read_byte = MSB;
153 return (uint8_t)count;
154 break;
155 case MSB:
156 read_byte = LSB;
157 return count >> 8;
158 break;
159 default:
160 panic("Shouldn't be here");
161 }
162 }
163 }
164
165 void
166 Intel8254Timer::Counter::write(const uint8_t data)
167 {
168 switch (write_byte) {
169 case LSB:
170 initial_count = (initial_count & 0xFF00) | data;
171
172 if (event.scheduled())
173 parent->deschedule(event);
174 output_high = false;
175 write_byte = MSB;
176 break;
177
178 case MSB:
179 initial_count = (initial_count & 0x00FF) | (data << 8);
180 // In the RateGen or SquareWave modes, the timer wraps around and
181 // triggers on a value of 1, not 0.
182 if (mode == RateGen || mode == SquareWave)
183 period = initial_count - 1;
184 else
185 period = initial_count;
186
187 offset = period * event.getInterval();
188
189 if (running && (period > 0))
190 event.setTo(period);
191
192 write_byte = LSB;
193 break;
194 }
195 }
196
197 void
198 Intel8254Timer::Counter::setRW(int rw_val)
199 {
200 if (rw_val != TwoPhase)
201 panic("Only LSB/MSB read/write is implemented.\n");
202 }
203
204 void
205 Intel8254Timer::Counter::setMode(int mode_val)
206 {
207 if (mode_val != InitTc && mode_val != RateGen &&
208 mode_val != SquareWave)
209 panic("PIT mode %#x is not implemented: \n", mode_val);
210
211 mode = mode_val;
212 }
213
214 void
215 Intel8254Timer::Counter::setBCD(int bcd_val)
216 {
217 if (bcd_val)
218 panic("PITimer does not implement BCD counts.\n");
219 }
220
221 bool
222 Intel8254Timer::Counter::outputHigh()
223 {
224 return output_high;
225 }
226
227 void
228 Intel8254Timer::Counter::serialize(const string &base, CheckpointOut &cp) const
229 {
230 paramOut(cp, base + ".initial_count", initial_count);
231 paramOut(cp, base + ".latched_count", latched_count);
232 paramOut(cp, base + ".period", period);
233 paramOut(cp, base + ".mode", mode);
234 paramOut(cp, base + ".output_high", output_high);
235 paramOut(cp, base + ".latch_on", latch_on);
236 paramOut(cp, base + ".read_byte", read_byte);
237 paramOut(cp, base + ".write_byte", write_byte);
238
239 Tick event_tick_offset = 0;
240 if (event.scheduled())
241 event_tick_offset = event.when() - curTick();
242 paramOut(cp, base + ".event_tick_offset", event_tick_offset);
243 }
244
245 void
246 Intel8254Timer::Counter::unserialize(const string &base, CheckpointIn &cp)
247 {
248 paramIn(cp, base + ".initial_count", initial_count);
249 paramIn(cp, base + ".latched_count", latched_count);
250 paramIn(cp, base + ".period", period);
251 paramIn(cp, base + ".mode", mode);
252 paramIn(cp, base + ".output_high", output_high);
253 paramIn(cp, base + ".latch_on", latch_on);
254 paramIn(cp, base + ".read_byte", read_byte);
255 paramIn(cp, base + ".write_byte", write_byte);
256
257 Tick event_tick_offset = 0;
258 assert(!event.scheduled());
259 paramIn(cp, base + ".event_tick_offset", event_tick_offset);
260 offset = event_tick_offset;
261 }
262
263 void
264 Intel8254Timer::Counter::startup()
265 {
266 running = true;
267 if ((period > 0) && (offset > 0))
268 {
269 parent->schedule(event, curTick() + offset);
270 }
271 }
272
273 Intel8254Timer::Counter::CounterEvent::CounterEvent(Counter* c_ptr)
274 {
275 interval = (Tick)(SimClock::Float::s / 1193180.0);
276 counter = c_ptr;
277 }
278
279 void
280 Intel8254Timer::Counter::CounterEvent::process()
281 {
282 switch (counter->mode) {
283 case InitTc:
284 counter->output_high = true;
285 break;
286 case RateGen:
287 case SquareWave:
288 setTo(counter->period);
289 break;
290 default:
291 panic("Unimplemented PITimer mode.\n");
292 }
293 counter->parent->counterInterrupt(counter->num);
294 }
295
296 void
297 Intel8254Timer::Counter::CounterEvent::setTo(int clocks)
298 {
299 if (clocks == 0)
300 panic("Timer can't be set to go off instantly.\n");
301 DPRINTF(Intel8254Timer, "Timer set to curTick() + %d\n",
302 clocks * interval);
303 counter->parent->schedule(this, curTick() + clocks * interval);
304 }
305
306 int
307 Intel8254Timer::Counter::CounterEvent::clocksLeft()
308 {
309 if (!scheduled())
310 return -1;
311 return (when() - curTick() + interval - 1) / interval;
312 }
313
314 const char *
315 Intel8254Timer::Counter::CounterEvent::description() const
316 {
317 return "Intel 8254 Interval timer";
318 }
319
320 Tick
321 Intel8254Timer::Counter::CounterEvent::getInterval()
322 {
323 return interval;
324 }
325