timing.cc (4200:f55b59fc848b) timing.cc (4224:7e828583f2cb)
1/*
2 * Copyright (c) 2002-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Steve Reinhardt
29 */
30
31#include "arch/locked_mem.hh"
32#include "arch/utility.hh"
33#include "base/bigint.hh"
34#include "cpu/exetrace.hh"
35#include "cpu/simple/timing.hh"
36#include "mem/packet.hh"
37#include "mem/packet_access.hh"
38#include "sim/builder.hh"
39#include "sim/system.hh"
40
41using namespace std;
42using namespace TheISA;
43
44Port *
45TimingSimpleCPU::getPort(const std::string &if_name, int idx)
46{
47 if (if_name == "dcache_port")
48 return &dcachePort;
49 else if (if_name == "icache_port")
50 return &icachePort;
51 else
52 panic("No Such Port\n");
53}
54
55void
56TimingSimpleCPU::init()
57{
58 BaseCPU::init();
59#if FULL_SYSTEM
60 for (int i = 0; i < threadContexts.size(); ++i) {
61 ThreadContext *tc = threadContexts[i];
62
63 // initialize CPU, including PC
64 TheISA::initCPU(tc, tc->readCpuId());
65 }
66#endif
67}
68
69Tick
70TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
71{
72 panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
73 return curTick;
74}
75
76void
77TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
78{
79 //No internal storage to update, jusst return
80 return;
81}
82
83void
84TimingSimpleCPU::CpuPort::recvStatusChange(Status status)
85{
86 if (status == RangeChange) {
87 if (!snoopRangeSent) {
88 snoopRangeSent = true;
89 sendStatusChange(Port::RangeChange);
90 }
91 return;
92 }
93
94 panic("TimingSimpleCPU doesn't expect recvStatusChange callback!");
95}
96
97
98void
99TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
100{
101 pkt = _pkt;
102 Event::schedule(t);
103}
104
105TimingSimpleCPU::TimingSimpleCPU(Params *p)
106 : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock),
107 cpu_id(p->cpu_id)
108{
109 _status = Idle;
110
111 icachePort.snoopRangeSent = false;
112 dcachePort.snoopRangeSent = false;
113
114 ifetch_pkt = dcache_pkt = NULL;
115 drainEvent = NULL;
116 fetchEvent = NULL;
117 previousTick = 0;
118 changeState(SimObject::Running);
119}
120
121
122TimingSimpleCPU::~TimingSimpleCPU()
123{
124}
125
126void
127TimingSimpleCPU::serialize(ostream &os)
128{
129 SimObject::State so_state = SimObject::getState();
130 SERIALIZE_ENUM(so_state);
131 BaseSimpleCPU::serialize(os);
132}
133
134void
135TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
136{
137 SimObject::State so_state;
138 UNSERIALIZE_ENUM(so_state);
139 BaseSimpleCPU::unserialize(cp, section);
140}
141
142unsigned int
143TimingSimpleCPU::drain(Event *drain_event)
144{
145 // TimingSimpleCPU is ready to drain if it's not waiting for
146 // an access to complete.
147 if (status() == Idle || status() == Running || status() == SwitchedOut) {
148 changeState(SimObject::Drained);
149 return 0;
150 } else {
151 changeState(SimObject::Draining);
152 drainEvent = drain_event;
153 return 1;
154 }
155}
156
157void
158TimingSimpleCPU::resume()
159{
160 if (_status != SwitchedOut && _status != Idle) {
161 assert(system->getMemoryMode() == System::Timing);
162
163 // Delete the old event if it existed.
164 if (fetchEvent) {
165 if (fetchEvent->scheduled())
166 fetchEvent->deschedule();
167
168 delete fetchEvent;
169 }
170
171 fetchEvent =
172 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
173 fetchEvent->schedule(nextCycle());
174 }
175
176 changeState(SimObject::Running);
177 previousTick = curTick;
178}
179
180void
181TimingSimpleCPU::switchOut()
182{
183 assert(status() == Running || status() == Idle);
184 _status = SwitchedOut;
185 numCycles += curTick - previousTick;
186
187 // If we've been scheduled to resume but are then told to switch out,
188 // we'll need to cancel it.
189 if (fetchEvent && fetchEvent->scheduled())
190 fetchEvent->deschedule();
191}
192
193
194void
195TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
196{
197 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
198
199 // if any of this CPU's ThreadContexts are active, mark the CPU as
200 // running and schedule its tick event.
201 for (int i = 0; i < threadContexts.size(); ++i) {
202 ThreadContext *tc = threadContexts[i];
203 if (tc->status() == ThreadContext::Active && _status != Running) {
204 _status = Running;
205 break;
206 }
207 }
208
209 if (_status != Running) {
210 _status = Idle;
211 }
212}
213
214
215void
216TimingSimpleCPU::activateContext(int thread_num, int delay)
217{
218 assert(thread_num == 0);
219 assert(thread);
220
221 assert(_status == Idle);
222
223 notIdleFraction++;
224 _status = Running;
225
226 // kick things off by initiating the fetch of the next instruction
227 fetchEvent =
228 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
229 fetchEvent->schedule(nextCycle(curTick + cycles(delay)));
230}
231
232
233void
234TimingSimpleCPU::suspendContext(int thread_num)
235{
236 assert(thread_num == 0);
237 assert(thread);
238
239 assert(_status == Running);
240
241 // just change status to Idle... if status != Running,
242 // completeInst() will not initiate fetch of next instruction.
243
244 notIdleFraction--;
245 _status = Idle;
246}
247
248
249template <class T>
250Fault
251TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
252{
253 Request *req =
254 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
255 cpu_id, /* thread ID */ 0);
256
257 if (traceData) {
258 traceData->setAddr(req->getVaddr());
259 }
260
261 // translate to physical address
262 Fault fault = thread->translateDataReadReq(req);
263
264 // Now do the access.
265 if (fault == NoFault) {
266 PacketPtr pkt =
267 new Packet(req, MemCmd::ReadReq, Packet::Broadcast);
268 pkt->dataDynamic<T>(new T);
269
270 if (!dcachePort.sendTiming(pkt)) {
271 _status = DcacheRetry;
272 dcache_pkt = pkt;
273 } else {
274 _status = DcacheWaitResponse;
275 // memory system takes ownership of packet
276 dcache_pkt = NULL;
277 }
278
279 // This will need a new way to tell if it has a dcache attached.
280 if (req->isUncacheable())
281 recordEvent("Uncached Read");
282 } else {
283 delete req;
284 }
285
286 return fault;
287}
288
289#ifndef DOXYGEN_SHOULD_SKIP_THIS
290
291template
292Fault
293TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
294
295template
296Fault
297TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
298
299template
300Fault
301TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
302
303template
304Fault
305TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
306
307template
308Fault
309TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
310
311template
312Fault
313TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
314
315#endif //DOXYGEN_SHOULD_SKIP_THIS
316
317template<>
318Fault
319TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
320{
321 return read(addr, *(uint64_t*)&data, flags);
322}
323
324template<>
325Fault
326TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
327{
328 return read(addr, *(uint32_t*)&data, flags);
329}
330
331
332template<>
333Fault
334TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
335{
336 return read(addr, (uint32_t&)data, flags);
337}
338
339
340template <class T>
341Fault
342TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
343{
344 Request *req =
345 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
346 cpu_id, /* thread ID */ 0);
347
348 if (traceData) {
349 traceData->setAddr(req->getVaddr());
350 }
351
352 // translate to physical address
353 Fault fault = thread->translateDataWriteReq(req);
354
355 // Now do the access.
356 if (fault == NoFault) {
357 assert(dcache_pkt == NULL);
358 if (req->isSwap())
359 dcache_pkt = new Packet(req, MemCmd::SwapReq, Packet::Broadcast);
360 else
361 dcache_pkt = new Packet(req, MemCmd::WriteReq, Packet::Broadcast);
362 dcache_pkt->allocate();
363 dcache_pkt->set(data);
364
365 bool do_access = true; // flag to suppress cache access
366
367 if (req->isLocked()) {
368 do_access = TheISA::handleLockedWrite(thread, req);
369 }
370 if (req->isCondSwap()) {
371 assert(res);
372 req->setExtraData(*res);
373 }
374
375 if (do_access) {
376 if (!dcachePort.sendTiming(dcache_pkt)) {
377 _status = DcacheRetry;
378 } else {
379 _status = DcacheWaitResponse;
380 // memory system takes ownership of packet
381 dcache_pkt = NULL;
382 }
383 }
384 // This will need a new way to tell if it's hooked up to a cache or not.
385 if (req->isUncacheable())
386 recordEvent("Uncached Write");
387 } else {
388 delete req;
389 }
390
391
392 // If the write needs to have a fault on the access, consider calling
393 // changeStatus() and changing it to "bad addr write" or something.
394 return fault;
395}
396
397
398#ifndef DOXYGEN_SHOULD_SKIP_THIS
399template
400Fault
1/*
2 * Copyright (c) 2002-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Steve Reinhardt
29 */
30
31#include "arch/locked_mem.hh"
32#include "arch/utility.hh"
33#include "base/bigint.hh"
34#include "cpu/exetrace.hh"
35#include "cpu/simple/timing.hh"
36#include "mem/packet.hh"
37#include "mem/packet_access.hh"
38#include "sim/builder.hh"
39#include "sim/system.hh"
40
41using namespace std;
42using namespace TheISA;
43
44Port *
45TimingSimpleCPU::getPort(const std::string &if_name, int idx)
46{
47 if (if_name == "dcache_port")
48 return &dcachePort;
49 else if (if_name == "icache_port")
50 return &icachePort;
51 else
52 panic("No Such Port\n");
53}
54
55void
56TimingSimpleCPU::init()
57{
58 BaseCPU::init();
59#if FULL_SYSTEM
60 for (int i = 0; i < threadContexts.size(); ++i) {
61 ThreadContext *tc = threadContexts[i];
62
63 // initialize CPU, including PC
64 TheISA::initCPU(tc, tc->readCpuId());
65 }
66#endif
67}
68
69Tick
70TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
71{
72 panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
73 return curTick;
74}
75
76void
77TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
78{
79 //No internal storage to update, jusst return
80 return;
81}
82
83void
84TimingSimpleCPU::CpuPort::recvStatusChange(Status status)
85{
86 if (status == RangeChange) {
87 if (!snoopRangeSent) {
88 snoopRangeSent = true;
89 sendStatusChange(Port::RangeChange);
90 }
91 return;
92 }
93
94 panic("TimingSimpleCPU doesn't expect recvStatusChange callback!");
95}
96
97
98void
99TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
100{
101 pkt = _pkt;
102 Event::schedule(t);
103}
104
105TimingSimpleCPU::TimingSimpleCPU(Params *p)
106 : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock),
107 cpu_id(p->cpu_id)
108{
109 _status = Idle;
110
111 icachePort.snoopRangeSent = false;
112 dcachePort.snoopRangeSent = false;
113
114 ifetch_pkt = dcache_pkt = NULL;
115 drainEvent = NULL;
116 fetchEvent = NULL;
117 previousTick = 0;
118 changeState(SimObject::Running);
119}
120
121
122TimingSimpleCPU::~TimingSimpleCPU()
123{
124}
125
126void
127TimingSimpleCPU::serialize(ostream &os)
128{
129 SimObject::State so_state = SimObject::getState();
130 SERIALIZE_ENUM(so_state);
131 BaseSimpleCPU::serialize(os);
132}
133
134void
135TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
136{
137 SimObject::State so_state;
138 UNSERIALIZE_ENUM(so_state);
139 BaseSimpleCPU::unserialize(cp, section);
140}
141
142unsigned int
143TimingSimpleCPU::drain(Event *drain_event)
144{
145 // TimingSimpleCPU is ready to drain if it's not waiting for
146 // an access to complete.
147 if (status() == Idle || status() == Running || status() == SwitchedOut) {
148 changeState(SimObject::Drained);
149 return 0;
150 } else {
151 changeState(SimObject::Draining);
152 drainEvent = drain_event;
153 return 1;
154 }
155}
156
157void
158TimingSimpleCPU::resume()
159{
160 if (_status != SwitchedOut && _status != Idle) {
161 assert(system->getMemoryMode() == System::Timing);
162
163 // Delete the old event if it existed.
164 if (fetchEvent) {
165 if (fetchEvent->scheduled())
166 fetchEvent->deschedule();
167
168 delete fetchEvent;
169 }
170
171 fetchEvent =
172 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
173 fetchEvent->schedule(nextCycle());
174 }
175
176 changeState(SimObject::Running);
177 previousTick = curTick;
178}
179
180void
181TimingSimpleCPU::switchOut()
182{
183 assert(status() == Running || status() == Idle);
184 _status = SwitchedOut;
185 numCycles += curTick - previousTick;
186
187 // If we've been scheduled to resume but are then told to switch out,
188 // we'll need to cancel it.
189 if (fetchEvent && fetchEvent->scheduled())
190 fetchEvent->deschedule();
191}
192
193
194void
195TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
196{
197 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
198
199 // if any of this CPU's ThreadContexts are active, mark the CPU as
200 // running and schedule its tick event.
201 for (int i = 0; i < threadContexts.size(); ++i) {
202 ThreadContext *tc = threadContexts[i];
203 if (tc->status() == ThreadContext::Active && _status != Running) {
204 _status = Running;
205 break;
206 }
207 }
208
209 if (_status != Running) {
210 _status = Idle;
211 }
212}
213
214
215void
216TimingSimpleCPU::activateContext(int thread_num, int delay)
217{
218 assert(thread_num == 0);
219 assert(thread);
220
221 assert(_status == Idle);
222
223 notIdleFraction++;
224 _status = Running;
225
226 // kick things off by initiating the fetch of the next instruction
227 fetchEvent =
228 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
229 fetchEvent->schedule(nextCycle(curTick + cycles(delay)));
230}
231
232
233void
234TimingSimpleCPU::suspendContext(int thread_num)
235{
236 assert(thread_num == 0);
237 assert(thread);
238
239 assert(_status == Running);
240
241 // just change status to Idle... if status != Running,
242 // completeInst() will not initiate fetch of next instruction.
243
244 notIdleFraction--;
245 _status = Idle;
246}
247
248
249template <class T>
250Fault
251TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
252{
253 Request *req =
254 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
255 cpu_id, /* thread ID */ 0);
256
257 if (traceData) {
258 traceData->setAddr(req->getVaddr());
259 }
260
261 // translate to physical address
262 Fault fault = thread->translateDataReadReq(req);
263
264 // Now do the access.
265 if (fault == NoFault) {
266 PacketPtr pkt =
267 new Packet(req, MemCmd::ReadReq, Packet::Broadcast);
268 pkt->dataDynamic<T>(new T);
269
270 if (!dcachePort.sendTiming(pkt)) {
271 _status = DcacheRetry;
272 dcache_pkt = pkt;
273 } else {
274 _status = DcacheWaitResponse;
275 // memory system takes ownership of packet
276 dcache_pkt = NULL;
277 }
278
279 // This will need a new way to tell if it has a dcache attached.
280 if (req->isUncacheable())
281 recordEvent("Uncached Read");
282 } else {
283 delete req;
284 }
285
286 return fault;
287}
288
289#ifndef DOXYGEN_SHOULD_SKIP_THIS
290
291template
292Fault
293TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
294
295template
296Fault
297TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
298
299template
300Fault
301TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
302
303template
304Fault
305TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
306
307template
308Fault
309TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
310
311template
312Fault
313TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
314
315#endif //DOXYGEN_SHOULD_SKIP_THIS
316
317template<>
318Fault
319TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
320{
321 return read(addr, *(uint64_t*)&data, flags);
322}
323
324template<>
325Fault
326TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
327{
328 return read(addr, *(uint32_t*)&data, flags);
329}
330
331
332template<>
333Fault
334TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
335{
336 return read(addr, (uint32_t&)data, flags);
337}
338
339
340template <class T>
341Fault
342TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
343{
344 Request *req =
345 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
346 cpu_id, /* thread ID */ 0);
347
348 if (traceData) {
349 traceData->setAddr(req->getVaddr());
350 }
351
352 // translate to physical address
353 Fault fault = thread->translateDataWriteReq(req);
354
355 // Now do the access.
356 if (fault == NoFault) {
357 assert(dcache_pkt == NULL);
358 if (req->isSwap())
359 dcache_pkt = new Packet(req, MemCmd::SwapReq, Packet::Broadcast);
360 else
361 dcache_pkt = new Packet(req, MemCmd::WriteReq, Packet::Broadcast);
362 dcache_pkt->allocate();
363 dcache_pkt->set(data);
364
365 bool do_access = true; // flag to suppress cache access
366
367 if (req->isLocked()) {
368 do_access = TheISA::handleLockedWrite(thread, req);
369 }
370 if (req->isCondSwap()) {
371 assert(res);
372 req->setExtraData(*res);
373 }
374
375 if (do_access) {
376 if (!dcachePort.sendTiming(dcache_pkt)) {
377 _status = DcacheRetry;
378 } else {
379 _status = DcacheWaitResponse;
380 // memory system takes ownership of packet
381 dcache_pkt = NULL;
382 }
383 }
384 // This will need a new way to tell if it's hooked up to a cache or not.
385 if (req->isUncacheable())
386 recordEvent("Uncached Write");
387 } else {
388 delete req;
389 }
390
391
392 // If the write needs to have a fault on the access, consider calling
393 // changeStatus() and changing it to "bad addr write" or something.
394 return fault;
395}
396
397
398#ifndef DOXYGEN_SHOULD_SKIP_THIS
399template
400Fault
401TimingSimpleCPU::write(Twin32_t data, Addr addr,
402 unsigned flags, uint64_t *res);
403
404template
405Fault
406TimingSimpleCPU::write(Twin64_t data, Addr addr,
407 unsigned flags, uint64_t *res);
408
409template
410Fault
401TimingSimpleCPU::write(uint64_t data, Addr addr,
402 unsigned flags, uint64_t *res);
403
404template
405Fault
406TimingSimpleCPU::write(uint32_t data, Addr addr,
407 unsigned flags, uint64_t *res);
408
409template
410Fault
411TimingSimpleCPU::write(uint16_t data, Addr addr,
412 unsigned flags, uint64_t *res);
413
414template
415Fault
416TimingSimpleCPU::write(uint8_t data, Addr addr,
417 unsigned flags, uint64_t *res);
418
419#endif //DOXYGEN_SHOULD_SKIP_THIS
420
421template<>
422Fault
423TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
424{
425 return write(*(uint64_t*)&data, addr, flags, res);
426}
427
428template<>
429Fault
430TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
431{
432 return write(*(uint32_t*)&data, addr, flags, res);
433}
434
435
436template<>
437Fault
438TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
439{
440 return write((uint32_t)data, addr, flags, res);
441}
442
443
444void
445TimingSimpleCPU::fetch()
446{
447 if (!curStaticInst || !curStaticInst->isDelayedCommit())
448 checkForInterrupts();
449
450 Request *ifetch_req = new Request();
451 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0);
452 Fault fault = setupFetchRequest(ifetch_req);
453
454 ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast);
455 ifetch_pkt->dataStatic(&inst);
456
457 if (fault == NoFault) {
458 if (!icachePort.sendTiming(ifetch_pkt)) {
459 // Need to wait for retry
460 _status = IcacheRetry;
461 } else {
462 // Need to wait for cache to respond
463 _status = IcacheWaitResponse;
464 // ownership of packet transferred to memory system
465 ifetch_pkt = NULL;
466 }
467 } else {
468 delete ifetch_req;
469 delete ifetch_pkt;
470 // fetch fault: advance directly to next instruction (fault handler)
471 advanceInst(fault);
472 }
473
474 numCycles += curTick - previousTick;
475 previousTick = curTick;
476}
477
478
479void
480TimingSimpleCPU::advanceInst(Fault fault)
481{
482 advancePC(fault);
483
484 if (_status == Running) {
485 // kick off fetch of next instruction... callback from icache
486 // response will cause that instruction to be executed,
487 // keeping the CPU running.
488 fetch();
489 }
490}
491
492
493void
494TimingSimpleCPU::completeIfetch(PacketPtr pkt)
495{
496 // received a response from the icache: execute the received
497 // instruction
498 assert(pkt->result == Packet::Success);
499 assert(_status == IcacheWaitResponse);
500
501 _status = Running;
502
503 numCycles += curTick - previousTick;
504 previousTick = curTick;
505
506 if (getState() == SimObject::Draining) {
507 delete pkt->req;
508 delete pkt;
509
510 completeDrain();
511 return;
512 }
513
514 preExecute();
515 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
516 // load or store: just send to dcache
517 Fault fault = curStaticInst->initiateAcc(this, traceData);
518 if (_status != Running) {
519 // instruction will complete in dcache response callback
520 assert(_status == DcacheWaitResponse || _status == DcacheRetry);
521 assert(fault == NoFault);
522 } else {
523 if (fault == NoFault) {
524 // early fail on store conditional: complete now
525 assert(dcache_pkt != NULL);
526 fault = curStaticInst->completeAcc(dcache_pkt, this,
527 traceData);
528 delete dcache_pkt->req;
529 delete dcache_pkt;
530 dcache_pkt = NULL;
531 }
532 postExecute();
533 advanceInst(fault);
534 }
535 } else {
536 // non-memory instruction: execute completely now
537 Fault fault = curStaticInst->execute(this, traceData);
538 postExecute();
539 advanceInst(fault);
540 }
541
542 delete pkt->req;
543 delete pkt;
544}
545
546void
547TimingSimpleCPU::IcachePort::ITickEvent::process()
548{
549 cpu->completeIfetch(pkt);
550}
551
552bool
553TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
554{
555 if (pkt->isResponse()) {
556 // delay processing of returned data until next CPU clock edge
557 Tick mem_time = pkt->req->getTime();
558 Tick next_tick = cpu->nextCycle(mem_time);
559
560 if (next_tick == curTick)
561 cpu->completeIfetch(pkt);
562 else
563 tickEvent.schedule(pkt, next_tick);
564
565 return true;
566 }
567 else {
568 //Snooping a Coherence Request, do nothing
569 return true;
570 }
571}
572
573void
574TimingSimpleCPU::IcachePort::recvRetry()
575{
576 // we shouldn't get a retry unless we have a packet that we're
577 // waiting to transmit
578 assert(cpu->ifetch_pkt != NULL);
579 assert(cpu->_status == IcacheRetry);
580 PacketPtr tmp = cpu->ifetch_pkt;
581 if (sendTiming(tmp)) {
582 cpu->_status = IcacheWaitResponse;
583 cpu->ifetch_pkt = NULL;
584 }
585}
586
587void
588TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
589{
590 // received a response from the dcache: complete the load or store
591 // instruction
592 assert(pkt->result == Packet::Success);
593 assert(_status == DcacheWaitResponse);
594 _status = Running;
595
596 numCycles += curTick - previousTick;
597 previousTick = curTick;
598
599 Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
600
601 if (pkt->isRead() && pkt->req->isLocked()) {
602 TheISA::handleLockedRead(thread, pkt->req);
603 }
604
605 delete pkt->req;
606 delete pkt;
607
608 postExecute();
609
610 if (getState() == SimObject::Draining) {
611 advancePC(fault);
612 completeDrain();
613
614 return;
615 }
616
617 advanceInst(fault);
618}
619
620
621void
622TimingSimpleCPU::completeDrain()
623{
624 DPRINTF(Config, "Done draining\n");
625 changeState(SimObject::Drained);
626 drainEvent->process();
627}
628
629void
630TimingSimpleCPU::DcachePort::setPeer(Port *port)
631{
632 Port::setPeer(port);
633
634#if FULL_SYSTEM
635 // Update the ThreadContext's memory ports (Functional/Virtual
636 // Ports)
637 cpu->tcBase()->connectMemPorts();
638#endif
639}
640
641bool
642TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
643{
644 if (pkt->isResponse()) {
645 // delay processing of returned data until next CPU clock edge
646 Tick mem_time = pkt->req->getTime();
647 Tick next_tick = cpu->nextCycle(mem_time);
648
649 if (next_tick == curTick)
650 cpu->completeDataAccess(pkt);
651 else
652 tickEvent.schedule(pkt, next_tick);
653
654 return true;
655 }
656 else {
657 //Snooping a coherence req, do nothing
658 return true;
659 }
660}
661
662void
663TimingSimpleCPU::DcachePort::DTickEvent::process()
664{
665 cpu->completeDataAccess(pkt);
666}
667
668void
669TimingSimpleCPU::DcachePort::recvRetry()
670{
671 // we shouldn't get a retry unless we have a packet that we're
672 // waiting to transmit
673 assert(cpu->dcache_pkt != NULL);
674 assert(cpu->_status == DcacheRetry);
675 PacketPtr tmp = cpu->dcache_pkt;
676 if (sendTiming(tmp)) {
677 cpu->_status = DcacheWaitResponse;
678 // memory system takes ownership of packet
679 cpu->dcache_pkt = NULL;
680 }
681}
682
683
684////////////////////////////////////////////////////////////////////////
685//
686// TimingSimpleCPU Simulation Object
687//
688BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
689
690 Param<Counter> max_insts_any_thread;
691 Param<Counter> max_insts_all_threads;
692 Param<Counter> max_loads_any_thread;
693 Param<Counter> max_loads_all_threads;
694 Param<Tick> progress_interval;
695 SimObjectParam<System *> system;
696 Param<int> cpu_id;
697
698#if FULL_SYSTEM
699 SimObjectParam<TheISA::ITB *> itb;
700 SimObjectParam<TheISA::DTB *> dtb;
701 Param<Tick> profile;
702
703 Param<bool> do_quiesce;
704 Param<bool> do_checkpoint_insts;
705 Param<bool> do_statistics_insts;
706#else
707 SimObjectParam<Process *> workload;
708#endif // FULL_SYSTEM
709
710 Param<int> clock;
711 Param<int> phase;
712
713 Param<bool> defer_registration;
714 Param<int> width;
715 Param<bool> function_trace;
716 Param<Tick> function_trace_start;
717 Param<bool> simulate_stalls;
718
719END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
720
721BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
722
723 INIT_PARAM(max_insts_any_thread,
724 "terminate when any thread reaches this inst count"),
725 INIT_PARAM(max_insts_all_threads,
726 "terminate when all threads have reached this inst count"),
727 INIT_PARAM(max_loads_any_thread,
728 "terminate when any thread reaches this load count"),
729 INIT_PARAM(max_loads_all_threads,
730 "terminate when all threads have reached this load count"),
731 INIT_PARAM(progress_interval, "Progress interval"),
732 INIT_PARAM(system, "system object"),
733 INIT_PARAM(cpu_id, "processor ID"),
734
735#if FULL_SYSTEM
736 INIT_PARAM(itb, "Instruction TLB"),
737 INIT_PARAM(dtb, "Data TLB"),
738 INIT_PARAM(profile, ""),
739 INIT_PARAM(do_quiesce, ""),
740 INIT_PARAM(do_checkpoint_insts, ""),
741 INIT_PARAM(do_statistics_insts, ""),
742#else
743 INIT_PARAM(workload, "processes to run"),
744#endif // FULL_SYSTEM
745
746 INIT_PARAM(clock, "clock speed"),
747 INIT_PARAM_DFLT(phase, "clock phase", 0),
748 INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
749 INIT_PARAM(width, "cpu width"),
750 INIT_PARAM(function_trace, "Enable function trace"),
751 INIT_PARAM(function_trace_start, "Cycle to start function trace"),
752 INIT_PARAM(simulate_stalls, "Simulate cache stall cycles")
753
754END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
755
756
757CREATE_SIM_OBJECT(TimingSimpleCPU)
758{
759 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
760 params->name = getInstanceName();
761 params->numberOfThreads = 1;
762 params->max_insts_any_thread = max_insts_any_thread;
763 params->max_insts_all_threads = max_insts_all_threads;
764 params->max_loads_any_thread = max_loads_any_thread;
765 params->max_loads_all_threads = max_loads_all_threads;
766 params->progress_interval = progress_interval;
767 params->deferRegistration = defer_registration;
768 params->clock = clock;
769 params->phase = phase;
770 params->functionTrace = function_trace;
771 params->functionTraceStart = function_trace_start;
772 params->system = system;
773 params->cpu_id = cpu_id;
774
775#if FULL_SYSTEM
776 params->itb = itb;
777 params->dtb = dtb;
778 params->profile = profile;
779 params->do_quiesce = do_quiesce;
780 params->do_checkpoint_insts = do_checkpoint_insts;
781 params->do_statistics_insts = do_statistics_insts;
782#else
783 params->process = workload;
784#endif
785
786 TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
787 return cpu;
788}
789
790REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU)
791
411TimingSimpleCPU::write(uint64_t data, Addr addr,
412 unsigned flags, uint64_t *res);
413
414template
415Fault
416TimingSimpleCPU::write(uint32_t data, Addr addr,
417 unsigned flags, uint64_t *res);
418
419template
420Fault
421TimingSimpleCPU::write(uint16_t data, Addr addr,
422 unsigned flags, uint64_t *res);
423
424template
425Fault
426TimingSimpleCPU::write(uint8_t data, Addr addr,
427 unsigned flags, uint64_t *res);
428
429#endif //DOXYGEN_SHOULD_SKIP_THIS
430
431template<>
432Fault
433TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
434{
435 return write(*(uint64_t*)&data, addr, flags, res);
436}
437
438template<>
439Fault
440TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
441{
442 return write(*(uint32_t*)&data, addr, flags, res);
443}
444
445
446template<>
447Fault
448TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
449{
450 return write((uint32_t)data, addr, flags, res);
451}
452
453
454void
455TimingSimpleCPU::fetch()
456{
457 if (!curStaticInst || !curStaticInst->isDelayedCommit())
458 checkForInterrupts();
459
460 Request *ifetch_req = new Request();
461 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0);
462 Fault fault = setupFetchRequest(ifetch_req);
463
464 ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast);
465 ifetch_pkt->dataStatic(&inst);
466
467 if (fault == NoFault) {
468 if (!icachePort.sendTiming(ifetch_pkt)) {
469 // Need to wait for retry
470 _status = IcacheRetry;
471 } else {
472 // Need to wait for cache to respond
473 _status = IcacheWaitResponse;
474 // ownership of packet transferred to memory system
475 ifetch_pkt = NULL;
476 }
477 } else {
478 delete ifetch_req;
479 delete ifetch_pkt;
480 // fetch fault: advance directly to next instruction (fault handler)
481 advanceInst(fault);
482 }
483
484 numCycles += curTick - previousTick;
485 previousTick = curTick;
486}
487
488
489void
490TimingSimpleCPU::advanceInst(Fault fault)
491{
492 advancePC(fault);
493
494 if (_status == Running) {
495 // kick off fetch of next instruction... callback from icache
496 // response will cause that instruction to be executed,
497 // keeping the CPU running.
498 fetch();
499 }
500}
501
502
503void
504TimingSimpleCPU::completeIfetch(PacketPtr pkt)
505{
506 // received a response from the icache: execute the received
507 // instruction
508 assert(pkt->result == Packet::Success);
509 assert(_status == IcacheWaitResponse);
510
511 _status = Running;
512
513 numCycles += curTick - previousTick;
514 previousTick = curTick;
515
516 if (getState() == SimObject::Draining) {
517 delete pkt->req;
518 delete pkt;
519
520 completeDrain();
521 return;
522 }
523
524 preExecute();
525 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
526 // load or store: just send to dcache
527 Fault fault = curStaticInst->initiateAcc(this, traceData);
528 if (_status != Running) {
529 // instruction will complete in dcache response callback
530 assert(_status == DcacheWaitResponse || _status == DcacheRetry);
531 assert(fault == NoFault);
532 } else {
533 if (fault == NoFault) {
534 // early fail on store conditional: complete now
535 assert(dcache_pkt != NULL);
536 fault = curStaticInst->completeAcc(dcache_pkt, this,
537 traceData);
538 delete dcache_pkt->req;
539 delete dcache_pkt;
540 dcache_pkt = NULL;
541 }
542 postExecute();
543 advanceInst(fault);
544 }
545 } else {
546 // non-memory instruction: execute completely now
547 Fault fault = curStaticInst->execute(this, traceData);
548 postExecute();
549 advanceInst(fault);
550 }
551
552 delete pkt->req;
553 delete pkt;
554}
555
556void
557TimingSimpleCPU::IcachePort::ITickEvent::process()
558{
559 cpu->completeIfetch(pkt);
560}
561
562bool
563TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
564{
565 if (pkt->isResponse()) {
566 // delay processing of returned data until next CPU clock edge
567 Tick mem_time = pkt->req->getTime();
568 Tick next_tick = cpu->nextCycle(mem_time);
569
570 if (next_tick == curTick)
571 cpu->completeIfetch(pkt);
572 else
573 tickEvent.schedule(pkt, next_tick);
574
575 return true;
576 }
577 else {
578 //Snooping a Coherence Request, do nothing
579 return true;
580 }
581}
582
583void
584TimingSimpleCPU::IcachePort::recvRetry()
585{
586 // we shouldn't get a retry unless we have a packet that we're
587 // waiting to transmit
588 assert(cpu->ifetch_pkt != NULL);
589 assert(cpu->_status == IcacheRetry);
590 PacketPtr tmp = cpu->ifetch_pkt;
591 if (sendTiming(tmp)) {
592 cpu->_status = IcacheWaitResponse;
593 cpu->ifetch_pkt = NULL;
594 }
595}
596
597void
598TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
599{
600 // received a response from the dcache: complete the load or store
601 // instruction
602 assert(pkt->result == Packet::Success);
603 assert(_status == DcacheWaitResponse);
604 _status = Running;
605
606 numCycles += curTick - previousTick;
607 previousTick = curTick;
608
609 Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
610
611 if (pkt->isRead() && pkt->req->isLocked()) {
612 TheISA::handleLockedRead(thread, pkt->req);
613 }
614
615 delete pkt->req;
616 delete pkt;
617
618 postExecute();
619
620 if (getState() == SimObject::Draining) {
621 advancePC(fault);
622 completeDrain();
623
624 return;
625 }
626
627 advanceInst(fault);
628}
629
630
631void
632TimingSimpleCPU::completeDrain()
633{
634 DPRINTF(Config, "Done draining\n");
635 changeState(SimObject::Drained);
636 drainEvent->process();
637}
638
639void
640TimingSimpleCPU::DcachePort::setPeer(Port *port)
641{
642 Port::setPeer(port);
643
644#if FULL_SYSTEM
645 // Update the ThreadContext's memory ports (Functional/Virtual
646 // Ports)
647 cpu->tcBase()->connectMemPorts();
648#endif
649}
650
651bool
652TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
653{
654 if (pkt->isResponse()) {
655 // delay processing of returned data until next CPU clock edge
656 Tick mem_time = pkt->req->getTime();
657 Tick next_tick = cpu->nextCycle(mem_time);
658
659 if (next_tick == curTick)
660 cpu->completeDataAccess(pkt);
661 else
662 tickEvent.schedule(pkt, next_tick);
663
664 return true;
665 }
666 else {
667 //Snooping a coherence req, do nothing
668 return true;
669 }
670}
671
672void
673TimingSimpleCPU::DcachePort::DTickEvent::process()
674{
675 cpu->completeDataAccess(pkt);
676}
677
678void
679TimingSimpleCPU::DcachePort::recvRetry()
680{
681 // we shouldn't get a retry unless we have a packet that we're
682 // waiting to transmit
683 assert(cpu->dcache_pkt != NULL);
684 assert(cpu->_status == DcacheRetry);
685 PacketPtr tmp = cpu->dcache_pkt;
686 if (sendTiming(tmp)) {
687 cpu->_status = DcacheWaitResponse;
688 // memory system takes ownership of packet
689 cpu->dcache_pkt = NULL;
690 }
691}
692
693
694////////////////////////////////////////////////////////////////////////
695//
696// TimingSimpleCPU Simulation Object
697//
698BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
699
700 Param<Counter> max_insts_any_thread;
701 Param<Counter> max_insts_all_threads;
702 Param<Counter> max_loads_any_thread;
703 Param<Counter> max_loads_all_threads;
704 Param<Tick> progress_interval;
705 SimObjectParam<System *> system;
706 Param<int> cpu_id;
707
708#if FULL_SYSTEM
709 SimObjectParam<TheISA::ITB *> itb;
710 SimObjectParam<TheISA::DTB *> dtb;
711 Param<Tick> profile;
712
713 Param<bool> do_quiesce;
714 Param<bool> do_checkpoint_insts;
715 Param<bool> do_statistics_insts;
716#else
717 SimObjectParam<Process *> workload;
718#endif // FULL_SYSTEM
719
720 Param<int> clock;
721 Param<int> phase;
722
723 Param<bool> defer_registration;
724 Param<int> width;
725 Param<bool> function_trace;
726 Param<Tick> function_trace_start;
727 Param<bool> simulate_stalls;
728
729END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
730
731BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
732
733 INIT_PARAM(max_insts_any_thread,
734 "terminate when any thread reaches this inst count"),
735 INIT_PARAM(max_insts_all_threads,
736 "terminate when all threads have reached this inst count"),
737 INIT_PARAM(max_loads_any_thread,
738 "terminate when any thread reaches this load count"),
739 INIT_PARAM(max_loads_all_threads,
740 "terminate when all threads have reached this load count"),
741 INIT_PARAM(progress_interval, "Progress interval"),
742 INIT_PARAM(system, "system object"),
743 INIT_PARAM(cpu_id, "processor ID"),
744
745#if FULL_SYSTEM
746 INIT_PARAM(itb, "Instruction TLB"),
747 INIT_PARAM(dtb, "Data TLB"),
748 INIT_PARAM(profile, ""),
749 INIT_PARAM(do_quiesce, ""),
750 INIT_PARAM(do_checkpoint_insts, ""),
751 INIT_PARAM(do_statistics_insts, ""),
752#else
753 INIT_PARAM(workload, "processes to run"),
754#endif // FULL_SYSTEM
755
756 INIT_PARAM(clock, "clock speed"),
757 INIT_PARAM_DFLT(phase, "clock phase", 0),
758 INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
759 INIT_PARAM(width, "cpu width"),
760 INIT_PARAM(function_trace, "Enable function trace"),
761 INIT_PARAM(function_trace_start, "Cycle to start function trace"),
762 INIT_PARAM(simulate_stalls, "Simulate cache stall cycles")
763
764END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
765
766
767CREATE_SIM_OBJECT(TimingSimpleCPU)
768{
769 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
770 params->name = getInstanceName();
771 params->numberOfThreads = 1;
772 params->max_insts_any_thread = max_insts_any_thread;
773 params->max_insts_all_threads = max_insts_all_threads;
774 params->max_loads_any_thread = max_loads_any_thread;
775 params->max_loads_all_threads = max_loads_all_threads;
776 params->progress_interval = progress_interval;
777 params->deferRegistration = defer_registration;
778 params->clock = clock;
779 params->phase = phase;
780 params->functionTrace = function_trace;
781 params->functionTraceStart = function_trace_start;
782 params->system = system;
783 params->cpu_id = cpu_id;
784
785#if FULL_SYSTEM
786 params->itb = itb;
787 params->dtb = dtb;
788 params->profile = profile;
789 params->do_quiesce = do_quiesce;
790 params->do_checkpoint_insts = do_checkpoint_insts;
791 params->do_statistics_insts = do_statistics_insts;
792#else
793 params->process = workload;
794#endif
795
796 TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
797 return cpu;
798}
799
800REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU)
801