intel_8254_timer.cc revision 5444
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 * Authors: Ali G. Saidi
29 *          Andrew L. Schultz
30 *          Miguel J. Serrano
31 */
32
33#include "base/misc.hh"
34#include "dev/intel_8254_timer.hh"
35
36using namespace std;
37
38Intel8254Timer::Intel8254Timer(const string &name)
39    : _name(name), counter0(name + ".counter0"), counter1(name + ".counter1"),
40      counter2(name + ".counter2")
41{
42    counter[0] = &counter0;
43    counter[1] = &counter0;
44    counter[2] = &counter0;
45}
46
47void
48Intel8254Timer::writeControl(const CtrlReg data)
49{
50    int sel = data.sel;
51
52    if (sel == ReadBackCommand)
53       panic("PITimer Read-Back Command is not implemented.\n");
54
55    if (data.rw == LatchCommand)
56        counter[sel]->latchCount();
57    else {
58        counter[sel]->setRW(data.rw);
59        counter[sel]->setMode(data.mode);
60        counter[sel]->setBCD(data.bcd);
61    }
62}
63
64void
65Intel8254Timer::serialize(const string &base, ostream &os)
66{
67    // serialize the counters
68    counter0.serialize(base + ".counter0", os);
69    counter1.serialize(base + ".counter1", os);
70    counter2.serialize(base + ".counter2", os);
71}
72
73void
74Intel8254Timer::unserialize(const string &base, Checkpoint *cp,
75        const string &section)
76{
77    // unserialze the counters
78    counter0.unserialize(base + ".counter0", cp, section);
79    counter1.unserialize(base + ".counter1", cp, section);
80    counter2.unserialize(base + ".counter2", cp, section);
81}
82
83Intel8254Timer::Counter::Counter(const string &name)
84    : _name(name), event(this), count(0), latched_count(0), period(0),
85      mode(0), output_high(false), latch_on(false), read_byte(LSB),
86      write_byte(LSB)
87{
88
89}
90
91void
92Intel8254Timer::Counter::latchCount()
93{
94    // behave like a real latch
95    if(!latch_on) {
96        latch_on = true;
97        read_byte = LSB;
98        latched_count = count;
99    }
100}
101
102uint8_t
103Intel8254Timer::Counter::read()
104{
105    if (latch_on) {
106        switch (read_byte) {
107          case LSB:
108            read_byte = MSB;
109            return (uint8_t)latched_count;
110            break;
111          case MSB:
112            read_byte = LSB;
113            latch_on = false;
114            return latched_count >> 8;
115            break;
116          default:
117            panic("Shouldn't be here");
118        }
119    } else {
120        switch (read_byte) {
121          case LSB:
122            read_byte = MSB;
123            return (uint8_t)count;
124            break;
125          case MSB:
126            read_byte = LSB;
127            return count >> 8;
128            break;
129          default:
130            panic("Shouldn't be here");
131        }
132    }
133}
134
135void
136Intel8254Timer::Counter::write(const uint8_t data)
137{
138    switch (write_byte) {
139      case LSB:
140        count = (count & 0xFF00) | data;
141
142        if (event.scheduled())
143          event.deschedule();
144        output_high = false;
145        write_byte = MSB;
146        break;
147
148      case MSB:
149        count = (count & 0x00FF) | (data << 8);
150        // In the RateGen or SquareWave modes, the timer wraps around and
151        // triggers on a value of 1, not 0.
152        if (mode == RateGen || mode == SquareWave)
153            period = count - 1;
154        else
155            period = count;
156
157        if (period > 0)
158            event.setTo(period);
159
160        write_byte = LSB;
161        break;
162    }
163}
164
165void
166Intel8254Timer::Counter::setRW(int rw_val)
167{
168    if (rw_val != TwoPhase)
169        panic("Only LSB/MSB read/write is implemented.\n");
170}
171
172void
173Intel8254Timer::Counter::setMode(int mode_val)
174{
175    if(mode_val != InitTc && mode_val != RateGen &&
176       mode_val != SquareWave)
177        panic("PIT mode %#x is not implemented: \n", mode_val);
178
179    mode = mode_val;
180}
181
182void
183Intel8254Timer::Counter::setBCD(int bcd_val)
184{
185    if (bcd_val)
186        panic("PITimer does not implement BCD counts.\n");
187}
188
189bool
190Intel8254Timer::Counter::outputHigh()
191{
192    return output_high;
193}
194
195void
196Intel8254Timer::Counter::serialize(const string &base, ostream &os)
197{
198    paramOut(os, base + ".count", count);
199    paramOut(os, base + ".latched_count", latched_count);
200    paramOut(os, base + ".period", period);
201    paramOut(os, base + ".mode", mode);
202    paramOut(os, base + ".output_high", output_high);
203    paramOut(os, base + ".latch_on", latch_on);
204    paramOut(os, base + ".read_byte", read_byte);
205    paramOut(os, base + ".write_byte", write_byte);
206
207    Tick event_tick = 0;
208    if (event.scheduled())
209        event_tick = event.when();
210    paramOut(os, base + ".event_tick", event_tick);
211}
212
213void
214Intel8254Timer::Counter::unserialize(const string &base, Checkpoint *cp,
215                                         const string &section)
216{
217    paramIn(cp, section, base + ".count", count);
218    paramIn(cp, section, base + ".latched_count", latched_count);
219    paramIn(cp, section, base + ".period", period);
220    paramIn(cp, section, base + ".mode", mode);
221    paramIn(cp, section, base + ".output_high", output_high);
222    paramIn(cp, section, base + ".latch_on", latch_on);
223    paramIn(cp, section, base + ".read_byte", read_byte);
224    paramIn(cp, section, base + ".write_byte", write_byte);
225
226    Tick event_tick;
227    paramIn(cp, section, base + ".event_tick", event_tick);
228    if (event_tick)
229        event.schedule(event_tick);
230}
231
232Intel8254Timer::Counter::CounterEvent::CounterEvent(Counter* c_ptr)
233    : Event(&mainEventQueue)
234{
235    interval = (Tick)(Clock::Float::s / 1193180.0);
236    counter = c_ptr;
237}
238
239void
240Intel8254Timer::Counter::CounterEvent::process()
241{
242    DPRINTF(Intel8254Timer, "Timer Interrupt\n");
243    switch (counter->mode) {
244      case InitTc:
245        counter->output_high = true;
246        break;
247      case RateGen:
248      case SquareWave:
249        setTo(counter->period);
250        break;
251      default:
252        panic("Unimplemented PITimer mode.\n");
253    }
254}
255
256void
257Intel8254Timer::Counter::CounterEvent::setTo(int clocks)
258{
259    if (clocks == 0)
260        panic("Timer can't be set to go off instantly.\n");
261    DPRINTF(Intel8254Timer, "Timer set to curTick + %d\n",
262            clocks * interval);
263    schedule(curTick + clocks * interval);
264}
265
266const char *
267Intel8254Timer::Counter::CounterEvent::description() const
268{
269    return "tsunami 8254 Interval timer";
270}
271