timing.cc (4762:c94e103c83ad) timing.cc (4776:8c8407243a2c)
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 "params/TimingSimpleCPU.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() == Enums::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 = new FetchEvent(this, nextCycle());
172 }
173
174 changeState(SimObject::Running);
175 previousTick = curTick;
176}
177
178void
179TimingSimpleCPU::switchOut()
180{
181 assert(status() == Running || status() == Idle);
182 _status = SwitchedOut;
183 numCycles += curTick - previousTick;
184
185 // If we've been scheduled to resume but are then told to switch out,
186 // we'll need to cancel it.
187 if (fetchEvent && fetchEvent->scheduled())
188 fetchEvent->deschedule();
189}
190
191
192void
193TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
194{
195 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
196
197 // if any of this CPU's ThreadContexts are active, mark the CPU as
198 // running and schedule its tick event.
199 for (int i = 0; i < threadContexts.size(); ++i) {
200 ThreadContext *tc = threadContexts[i];
201 if (tc->status() == ThreadContext::Active && _status != Running) {
202 _status = Running;
203 break;
204 }
205 }
206
207 if (_status != Running) {
208 _status = Idle;
209 }
210}
211
212
213void
214TimingSimpleCPU::activateContext(int thread_num, int delay)
215{
216 assert(thread_num == 0);
217 assert(thread);
218
219 assert(_status == Idle);
220
221 notIdleFraction++;
222 _status = Running;
223
224 // kick things off by initiating the fetch of the next instruction
225 fetchEvent = new FetchEvent(this, nextCycle(curTick + cycles(delay)));
226}
227
228
229void
230TimingSimpleCPU::suspendContext(int thread_num)
231{
232 assert(thread_num == 0);
233 assert(thread);
234
235 assert(_status == Running);
236
237 // just change status to Idle... if status != Running,
238 // completeInst() will not initiate fetch of next instruction.
239
240 notIdleFraction--;
241 _status = Idle;
242}
243
244
245template <class T>
246Fault
247TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
248{
249 Request *req =
250 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
251 cpu_id, /* thread ID */ 0);
252
253 if (traceData) {
254 traceData->setAddr(req->getVaddr());
255 }
256
257 // translate to physical address
258 Fault fault = thread->translateDataReadReq(req);
259
260 // Now do the access.
261 if (fault == NoFault) {
262 PacketPtr pkt =
263 new Packet(req, MemCmd::ReadReq, Packet::Broadcast);
264 pkt->dataDynamic<T>(new T);
265
266 if (!dcachePort.sendTiming(pkt)) {
267 _status = DcacheRetry;
268 dcache_pkt = pkt;
269 } else {
270 _status = DcacheWaitResponse;
271 // memory system takes ownership of packet
272 dcache_pkt = NULL;
273 }
274
275 // This will need a new way to tell if it has a dcache attached.
276 if (req->isUncacheable())
277 recordEvent("Uncached Read");
278 } else {
279 delete req;
280 }
281
282 return fault;
283}
284
285#ifndef DOXYGEN_SHOULD_SKIP_THIS
286
287template
288Fault
289TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
290
291template
292Fault
293TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
294
295template
296Fault
297TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
298
299template
300Fault
301TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
302
303template
304Fault
305TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
306
307template
308Fault
309TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
310
311#endif //DOXYGEN_SHOULD_SKIP_THIS
312
313template<>
314Fault
315TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
316{
317 return read(addr, *(uint64_t*)&data, flags);
318}
319
320template<>
321Fault
322TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
323{
324 return read(addr, *(uint32_t*)&data, flags);
325}
326
327
328template<>
329Fault
330TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
331{
332 return read(addr, (uint32_t&)data, flags);
333}
334
335
336template <class T>
337Fault
338TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
339{
340 Request *req =
341 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
342 cpu_id, /* thread ID */ 0);
343
344 if (traceData) {
345 traceData->setAddr(req->getVaddr());
346 }
347
348 // translate to physical address
349 Fault fault = thread->translateDataWriteReq(req);
350
351 // Now do the access.
352 if (fault == NoFault) {
353 assert(dcache_pkt == NULL);
354 if (req->isSwap())
355 dcache_pkt = new Packet(req, MemCmd::SwapReq, Packet::Broadcast);
356 else
357 dcache_pkt = new Packet(req, MemCmd::WriteReq, Packet::Broadcast);
358 dcache_pkt->allocate();
359 dcache_pkt->set(data);
360
361 bool do_access = true; // flag to suppress cache access
362
363 if (req->isLocked()) {
364 do_access = TheISA::handleLockedWrite(thread, req);
365 }
366 if (req->isCondSwap()) {
367 assert(res);
368 req->setExtraData(*res);
369 }
370
371 if (do_access) {
372 if (!dcachePort.sendTiming(dcache_pkt)) {
373 _status = DcacheRetry;
374 } else {
375 _status = DcacheWaitResponse;
376 // memory system takes ownership of packet
377 dcache_pkt = NULL;
378 }
379 }
380 // This will need a new way to tell if it's hooked up to a cache or not.
381 if (req->isUncacheable())
382 recordEvent("Uncached Write");
383 } else {
384 delete req;
385 }
386
387
388 // If the write needs to have a fault on the access, consider calling
389 // changeStatus() and changing it to "bad addr write" or something.
390 return fault;
391}
392
393
394#ifndef DOXYGEN_SHOULD_SKIP_THIS
395template
396Fault
397TimingSimpleCPU::write(Twin32_t data, Addr addr,
398 unsigned flags, uint64_t *res);
399
400template
401Fault
402TimingSimpleCPU::write(Twin64_t data, Addr addr,
403 unsigned flags, uint64_t *res);
404
405template
406Fault
407TimingSimpleCPU::write(uint64_t data, Addr addr,
408 unsigned flags, uint64_t *res);
409
410template
411Fault
412TimingSimpleCPU::write(uint32_t data, Addr addr,
413 unsigned flags, uint64_t *res);
414
415template
416Fault
417TimingSimpleCPU::write(uint16_t data, Addr addr,
418 unsigned flags, uint64_t *res);
419
420template
421Fault
422TimingSimpleCPU::write(uint8_t data, Addr addr,
423 unsigned flags, uint64_t *res);
424
425#endif //DOXYGEN_SHOULD_SKIP_THIS
426
427template<>
428Fault
429TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
430{
431 return write(*(uint64_t*)&data, addr, flags, res);
432}
433
434template<>
435Fault
436TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
437{
438 return write(*(uint32_t*)&data, addr, flags, res);
439}
440
441
442template<>
443Fault
444TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
445{
446 return write((uint32_t)data, addr, flags, res);
447}
448
449
450void
451TimingSimpleCPU::fetch()
452{
453 if (!curStaticInst || !curStaticInst->isDelayedCommit())
454 checkForInterrupts();
455
456 Request *ifetch_req = new Request();
457 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0);
458 Fault fault = setupFetchRequest(ifetch_req);
459
460 ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast);
461 ifetch_pkt->dataStatic(&inst);
462
463 if (fault == NoFault) {
464 if (!icachePort.sendTiming(ifetch_pkt)) {
465 // Need to wait for retry
466 _status = IcacheRetry;
467 } else {
468 // Need to wait for cache to respond
469 _status = IcacheWaitResponse;
470 // ownership of packet transferred to memory system
471 ifetch_pkt = NULL;
472 }
473 } else {
474 delete ifetch_req;
475 delete ifetch_pkt;
476 // fetch fault: advance directly to next instruction (fault handler)
477 advanceInst(fault);
478 }
479
480 numCycles += curTick - previousTick;
481 previousTick = curTick;
482}
483
484
485void
486TimingSimpleCPU::advanceInst(Fault fault)
487{
488 advancePC(fault);
489
490 if (_status == Running) {
491 // kick off fetch of next instruction... callback from icache
492 // response will cause that instruction to be executed,
493 // keeping the CPU running.
494 fetch();
495 }
496}
497
498
499void
500TimingSimpleCPU::completeIfetch(PacketPtr pkt)
501{
502 // received a response from the icache: execute the received
503 // instruction
504 assert(pkt->result == Packet::Success);
505 assert(_status == IcacheWaitResponse);
506
507 _status = Running;
508
509 numCycles += curTick - previousTick;
510 previousTick = curTick;
511
512 if (getState() == SimObject::Draining) {
513 delete pkt->req;
514 delete pkt;
515
516 completeDrain();
517 return;
518 }
519
520 preExecute();
521 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
522 // load or store: just send to dcache
523 Fault fault = curStaticInst->initiateAcc(this, traceData);
524 if (_status != Running) {
525 // instruction will complete in dcache response callback
526 assert(_status == DcacheWaitResponse || _status == DcacheRetry);
527 assert(fault == NoFault);
528 } else {
529 if (fault == NoFault) {
530 // early fail on store conditional: complete now
531 assert(dcache_pkt != NULL);
532 fault = curStaticInst->completeAcc(dcache_pkt, this,
533 traceData);
534 delete dcache_pkt->req;
535 delete dcache_pkt;
536 dcache_pkt = NULL;
537 }
538 postExecute();
539 advanceInst(fault);
540 }
541 } else {
542 // non-memory instruction: execute completely now
543 Fault fault = curStaticInst->execute(this, traceData);
544 postExecute();
545 advanceInst(fault);
546 }
547
548 delete pkt->req;
549 delete pkt;
550}
551
552void
553TimingSimpleCPU::IcachePort::ITickEvent::process()
554{
555 cpu->completeIfetch(pkt);
556}
557
558bool
559TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
560{
561 if (pkt->isResponse()) {
562 // delay processing of returned data until next CPU clock edge
563 Tick next_tick = cpu->nextCycle(curTick);
564
565 if (next_tick == curTick)
566 cpu->completeIfetch(pkt);
567 else
568 tickEvent.schedule(pkt, next_tick);
569
570 return true;
571 }
572 else if (pkt->result == Packet::Nacked) {
573 assert(cpu->_status == IcacheWaitResponse);
574 pkt->reinitNacked();
575 if (!sendTiming(pkt)) {
576 cpu->_status = IcacheRetry;
577 cpu->ifetch_pkt = pkt;
578 }
579 }
580 //Snooping a Coherence Request, do nothing
581 return true;
582}
583
584void
585TimingSimpleCPU::IcachePort::recvRetry()
586{
587 // we shouldn't get a retry unless we have a packet that we're
588 // waiting to transmit
589 assert(cpu->ifetch_pkt != NULL);
590 assert(cpu->_status == IcacheRetry);
591 PacketPtr tmp = cpu->ifetch_pkt;
592 if (sendTiming(tmp)) {
593 cpu->_status = IcacheWaitResponse;
594 cpu->ifetch_pkt = NULL;
595 }
596}
597
598void
599TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
600{
601 // received a response from the dcache: complete the load or store
602 // instruction
603 assert(pkt->result == Packet::Success);
604 assert(_status == DcacheWaitResponse);
605 _status = Running;
606
607 numCycles += curTick - previousTick;
608 previousTick = curTick;
609
610 Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
611
612 if (pkt->isRead() && pkt->req->isLocked()) {
613 TheISA::handleLockedRead(thread, pkt->req);
614 }
615
616 delete pkt->req;
617 delete pkt;
618
619 postExecute();
620
621 if (getState() == SimObject::Draining) {
622 advancePC(fault);
623 completeDrain();
624
625 return;
626 }
627
628 advanceInst(fault);
629}
630
631
632void
633TimingSimpleCPU::completeDrain()
634{
635 DPRINTF(Config, "Done draining\n");
636 changeState(SimObject::Drained);
637 drainEvent->process();
638}
639
640void
641TimingSimpleCPU::DcachePort::setPeer(Port *port)
642{
643 Port::setPeer(port);
644
645#if FULL_SYSTEM
646 // Update the ThreadContext's memory ports (Functional/Virtual
647 // Ports)
648 cpu->tcBase()->connectMemPorts();
649#endif
650}
651
652bool
653TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
654{
655 if (pkt->isResponse()) {
656 // delay processing of returned data until next CPU clock edge
657 Tick next_tick = cpu->nextCycle(curTick);
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 if (pkt->result == Packet::Nacked) {
667 assert(cpu->_status == DcacheWaitResponse);
668 pkt->reinitNacked();
669 if (!sendTiming(pkt)) {
670 cpu->_status = DcacheRetry;
671 cpu->dcache_pkt = pkt;
672 }
673 }
674 //Snooping a Coherence Request, do nothing
675 return true;
676}
677
678void
679TimingSimpleCPU::DcachePort::DTickEvent::process()
680{
681 cpu->completeDataAccess(pkt);
682}
683
684void
685TimingSimpleCPU::DcachePort::recvRetry()
686{
687 // we shouldn't get a retry unless we have a packet that we're
688 // waiting to transmit
689 assert(cpu->dcache_pkt != NULL);
690 assert(cpu->_status == DcacheRetry);
691 PacketPtr tmp = cpu->dcache_pkt;
692 if (sendTiming(tmp)) {
693 cpu->_status = DcacheWaitResponse;
694 // memory system takes ownership of packet
695 cpu->dcache_pkt = NULL;
696 }
697}
698
699
700////////////////////////////////////////////////////////////////////////
701//
702// TimingSimpleCPU Simulation Object
703//
704TimingSimpleCPU *
705TimingSimpleCPUParams::create()
706{
707 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
708 params->name = name;
709 params->numberOfThreads = 1;
710 params->max_insts_any_thread = max_insts_any_thread;
711 params->max_insts_all_threads = max_insts_all_threads;
712 params->max_loads_any_thread = max_loads_any_thread;
713 params->max_loads_all_threads = max_loads_all_threads;
714 params->progress_interval = progress_interval;
715 params->deferRegistration = defer_registration;
716 params->clock = clock;
717 params->phase = phase;
718 params->functionTrace = function_trace;
719 params->functionTraceStart = function_trace_start;
720 params->system = system;
721 params->cpu_id = cpu_id;
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 "params/TimingSimpleCPU.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() == Enums::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 = new FetchEvent(this, nextCycle());
172 }
173
174 changeState(SimObject::Running);
175 previousTick = curTick;
176}
177
178void
179TimingSimpleCPU::switchOut()
180{
181 assert(status() == Running || status() == Idle);
182 _status = SwitchedOut;
183 numCycles += curTick - previousTick;
184
185 // If we've been scheduled to resume but are then told to switch out,
186 // we'll need to cancel it.
187 if (fetchEvent && fetchEvent->scheduled())
188 fetchEvent->deschedule();
189}
190
191
192void
193TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
194{
195 BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);
196
197 // if any of this CPU's ThreadContexts are active, mark the CPU as
198 // running and schedule its tick event.
199 for (int i = 0; i < threadContexts.size(); ++i) {
200 ThreadContext *tc = threadContexts[i];
201 if (tc->status() == ThreadContext::Active && _status != Running) {
202 _status = Running;
203 break;
204 }
205 }
206
207 if (_status != Running) {
208 _status = Idle;
209 }
210}
211
212
213void
214TimingSimpleCPU::activateContext(int thread_num, int delay)
215{
216 assert(thread_num == 0);
217 assert(thread);
218
219 assert(_status == Idle);
220
221 notIdleFraction++;
222 _status = Running;
223
224 // kick things off by initiating the fetch of the next instruction
225 fetchEvent = new FetchEvent(this, nextCycle(curTick + cycles(delay)));
226}
227
228
229void
230TimingSimpleCPU::suspendContext(int thread_num)
231{
232 assert(thread_num == 0);
233 assert(thread);
234
235 assert(_status == Running);
236
237 // just change status to Idle... if status != Running,
238 // completeInst() will not initiate fetch of next instruction.
239
240 notIdleFraction--;
241 _status = Idle;
242}
243
244
245template <class T>
246Fault
247TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
248{
249 Request *req =
250 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
251 cpu_id, /* thread ID */ 0);
252
253 if (traceData) {
254 traceData->setAddr(req->getVaddr());
255 }
256
257 // translate to physical address
258 Fault fault = thread->translateDataReadReq(req);
259
260 // Now do the access.
261 if (fault == NoFault) {
262 PacketPtr pkt =
263 new Packet(req, MemCmd::ReadReq, Packet::Broadcast);
264 pkt->dataDynamic<T>(new T);
265
266 if (!dcachePort.sendTiming(pkt)) {
267 _status = DcacheRetry;
268 dcache_pkt = pkt;
269 } else {
270 _status = DcacheWaitResponse;
271 // memory system takes ownership of packet
272 dcache_pkt = NULL;
273 }
274
275 // This will need a new way to tell if it has a dcache attached.
276 if (req->isUncacheable())
277 recordEvent("Uncached Read");
278 } else {
279 delete req;
280 }
281
282 return fault;
283}
284
285#ifndef DOXYGEN_SHOULD_SKIP_THIS
286
287template
288Fault
289TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
290
291template
292Fault
293TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
294
295template
296Fault
297TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
298
299template
300Fault
301TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
302
303template
304Fault
305TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
306
307template
308Fault
309TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
310
311#endif //DOXYGEN_SHOULD_SKIP_THIS
312
313template<>
314Fault
315TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
316{
317 return read(addr, *(uint64_t*)&data, flags);
318}
319
320template<>
321Fault
322TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
323{
324 return read(addr, *(uint32_t*)&data, flags);
325}
326
327
328template<>
329Fault
330TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
331{
332 return read(addr, (uint32_t&)data, flags);
333}
334
335
336template <class T>
337Fault
338TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res)
339{
340 Request *req =
341 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
342 cpu_id, /* thread ID */ 0);
343
344 if (traceData) {
345 traceData->setAddr(req->getVaddr());
346 }
347
348 // translate to physical address
349 Fault fault = thread->translateDataWriteReq(req);
350
351 // Now do the access.
352 if (fault == NoFault) {
353 assert(dcache_pkt == NULL);
354 if (req->isSwap())
355 dcache_pkt = new Packet(req, MemCmd::SwapReq, Packet::Broadcast);
356 else
357 dcache_pkt = new Packet(req, MemCmd::WriteReq, Packet::Broadcast);
358 dcache_pkt->allocate();
359 dcache_pkt->set(data);
360
361 bool do_access = true; // flag to suppress cache access
362
363 if (req->isLocked()) {
364 do_access = TheISA::handleLockedWrite(thread, req);
365 }
366 if (req->isCondSwap()) {
367 assert(res);
368 req->setExtraData(*res);
369 }
370
371 if (do_access) {
372 if (!dcachePort.sendTiming(dcache_pkt)) {
373 _status = DcacheRetry;
374 } else {
375 _status = DcacheWaitResponse;
376 // memory system takes ownership of packet
377 dcache_pkt = NULL;
378 }
379 }
380 // This will need a new way to tell if it's hooked up to a cache or not.
381 if (req->isUncacheable())
382 recordEvent("Uncached Write");
383 } else {
384 delete req;
385 }
386
387
388 // If the write needs to have a fault on the access, consider calling
389 // changeStatus() and changing it to "bad addr write" or something.
390 return fault;
391}
392
393
394#ifndef DOXYGEN_SHOULD_SKIP_THIS
395template
396Fault
397TimingSimpleCPU::write(Twin32_t data, Addr addr,
398 unsigned flags, uint64_t *res);
399
400template
401Fault
402TimingSimpleCPU::write(Twin64_t data, Addr addr,
403 unsigned flags, uint64_t *res);
404
405template
406Fault
407TimingSimpleCPU::write(uint64_t data, Addr addr,
408 unsigned flags, uint64_t *res);
409
410template
411Fault
412TimingSimpleCPU::write(uint32_t data, Addr addr,
413 unsigned flags, uint64_t *res);
414
415template
416Fault
417TimingSimpleCPU::write(uint16_t data, Addr addr,
418 unsigned flags, uint64_t *res);
419
420template
421Fault
422TimingSimpleCPU::write(uint8_t data, Addr addr,
423 unsigned flags, uint64_t *res);
424
425#endif //DOXYGEN_SHOULD_SKIP_THIS
426
427template<>
428Fault
429TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
430{
431 return write(*(uint64_t*)&data, addr, flags, res);
432}
433
434template<>
435Fault
436TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
437{
438 return write(*(uint32_t*)&data, addr, flags, res);
439}
440
441
442template<>
443Fault
444TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
445{
446 return write((uint32_t)data, addr, flags, res);
447}
448
449
450void
451TimingSimpleCPU::fetch()
452{
453 if (!curStaticInst || !curStaticInst->isDelayedCommit())
454 checkForInterrupts();
455
456 Request *ifetch_req = new Request();
457 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0);
458 Fault fault = setupFetchRequest(ifetch_req);
459
460 ifetch_pkt = new Packet(ifetch_req, MemCmd::ReadReq, Packet::Broadcast);
461 ifetch_pkt->dataStatic(&inst);
462
463 if (fault == NoFault) {
464 if (!icachePort.sendTiming(ifetch_pkt)) {
465 // Need to wait for retry
466 _status = IcacheRetry;
467 } else {
468 // Need to wait for cache to respond
469 _status = IcacheWaitResponse;
470 // ownership of packet transferred to memory system
471 ifetch_pkt = NULL;
472 }
473 } else {
474 delete ifetch_req;
475 delete ifetch_pkt;
476 // fetch fault: advance directly to next instruction (fault handler)
477 advanceInst(fault);
478 }
479
480 numCycles += curTick - previousTick;
481 previousTick = curTick;
482}
483
484
485void
486TimingSimpleCPU::advanceInst(Fault fault)
487{
488 advancePC(fault);
489
490 if (_status == Running) {
491 // kick off fetch of next instruction... callback from icache
492 // response will cause that instruction to be executed,
493 // keeping the CPU running.
494 fetch();
495 }
496}
497
498
499void
500TimingSimpleCPU::completeIfetch(PacketPtr pkt)
501{
502 // received a response from the icache: execute the received
503 // instruction
504 assert(pkt->result == Packet::Success);
505 assert(_status == IcacheWaitResponse);
506
507 _status = Running;
508
509 numCycles += curTick - previousTick;
510 previousTick = curTick;
511
512 if (getState() == SimObject::Draining) {
513 delete pkt->req;
514 delete pkt;
515
516 completeDrain();
517 return;
518 }
519
520 preExecute();
521 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
522 // load or store: just send to dcache
523 Fault fault = curStaticInst->initiateAcc(this, traceData);
524 if (_status != Running) {
525 // instruction will complete in dcache response callback
526 assert(_status == DcacheWaitResponse || _status == DcacheRetry);
527 assert(fault == NoFault);
528 } else {
529 if (fault == NoFault) {
530 // early fail on store conditional: complete now
531 assert(dcache_pkt != NULL);
532 fault = curStaticInst->completeAcc(dcache_pkt, this,
533 traceData);
534 delete dcache_pkt->req;
535 delete dcache_pkt;
536 dcache_pkt = NULL;
537 }
538 postExecute();
539 advanceInst(fault);
540 }
541 } else {
542 // non-memory instruction: execute completely now
543 Fault fault = curStaticInst->execute(this, traceData);
544 postExecute();
545 advanceInst(fault);
546 }
547
548 delete pkt->req;
549 delete pkt;
550}
551
552void
553TimingSimpleCPU::IcachePort::ITickEvent::process()
554{
555 cpu->completeIfetch(pkt);
556}
557
558bool
559TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
560{
561 if (pkt->isResponse()) {
562 // delay processing of returned data until next CPU clock edge
563 Tick next_tick = cpu->nextCycle(curTick);
564
565 if (next_tick == curTick)
566 cpu->completeIfetch(pkt);
567 else
568 tickEvent.schedule(pkt, next_tick);
569
570 return true;
571 }
572 else if (pkt->result == Packet::Nacked) {
573 assert(cpu->_status == IcacheWaitResponse);
574 pkt->reinitNacked();
575 if (!sendTiming(pkt)) {
576 cpu->_status = IcacheRetry;
577 cpu->ifetch_pkt = pkt;
578 }
579 }
580 //Snooping a Coherence Request, do nothing
581 return true;
582}
583
584void
585TimingSimpleCPU::IcachePort::recvRetry()
586{
587 // we shouldn't get a retry unless we have a packet that we're
588 // waiting to transmit
589 assert(cpu->ifetch_pkt != NULL);
590 assert(cpu->_status == IcacheRetry);
591 PacketPtr tmp = cpu->ifetch_pkt;
592 if (sendTiming(tmp)) {
593 cpu->_status = IcacheWaitResponse;
594 cpu->ifetch_pkt = NULL;
595 }
596}
597
598void
599TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
600{
601 // received a response from the dcache: complete the load or store
602 // instruction
603 assert(pkt->result == Packet::Success);
604 assert(_status == DcacheWaitResponse);
605 _status = Running;
606
607 numCycles += curTick - previousTick;
608 previousTick = curTick;
609
610 Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
611
612 if (pkt->isRead() && pkt->req->isLocked()) {
613 TheISA::handleLockedRead(thread, pkt->req);
614 }
615
616 delete pkt->req;
617 delete pkt;
618
619 postExecute();
620
621 if (getState() == SimObject::Draining) {
622 advancePC(fault);
623 completeDrain();
624
625 return;
626 }
627
628 advanceInst(fault);
629}
630
631
632void
633TimingSimpleCPU::completeDrain()
634{
635 DPRINTF(Config, "Done draining\n");
636 changeState(SimObject::Drained);
637 drainEvent->process();
638}
639
640void
641TimingSimpleCPU::DcachePort::setPeer(Port *port)
642{
643 Port::setPeer(port);
644
645#if FULL_SYSTEM
646 // Update the ThreadContext's memory ports (Functional/Virtual
647 // Ports)
648 cpu->tcBase()->connectMemPorts();
649#endif
650}
651
652bool
653TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
654{
655 if (pkt->isResponse()) {
656 // delay processing of returned data until next CPU clock edge
657 Tick next_tick = cpu->nextCycle(curTick);
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 if (pkt->result == Packet::Nacked) {
667 assert(cpu->_status == DcacheWaitResponse);
668 pkt->reinitNacked();
669 if (!sendTiming(pkt)) {
670 cpu->_status = DcacheRetry;
671 cpu->dcache_pkt = pkt;
672 }
673 }
674 //Snooping a Coherence Request, do nothing
675 return true;
676}
677
678void
679TimingSimpleCPU::DcachePort::DTickEvent::process()
680{
681 cpu->completeDataAccess(pkt);
682}
683
684void
685TimingSimpleCPU::DcachePort::recvRetry()
686{
687 // we shouldn't get a retry unless we have a packet that we're
688 // waiting to transmit
689 assert(cpu->dcache_pkt != NULL);
690 assert(cpu->_status == DcacheRetry);
691 PacketPtr tmp = cpu->dcache_pkt;
692 if (sendTiming(tmp)) {
693 cpu->_status = DcacheWaitResponse;
694 // memory system takes ownership of packet
695 cpu->dcache_pkt = NULL;
696 }
697}
698
699
700////////////////////////////////////////////////////////////////////////
701//
702// TimingSimpleCPU Simulation Object
703//
704TimingSimpleCPU *
705TimingSimpleCPUParams::create()
706{
707 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
708 params->name = name;
709 params->numberOfThreads = 1;
710 params->max_insts_any_thread = max_insts_any_thread;
711 params->max_insts_all_threads = max_insts_all_threads;
712 params->max_loads_any_thread = max_loads_any_thread;
713 params->max_loads_all_threads = max_loads_all_threads;
714 params->progress_interval = progress_interval;
715 params->deferRegistration = defer_registration;
716 params->clock = clock;
717 params->phase = phase;
718 params->functionTrace = function_trace;
719 params->functionTraceStart = function_trace_start;
720 params->system = system;
721 params->cpu_id = cpu_id;
722 params->tracer = tracer;
722
723#if FULL_SYSTEM
724 params->itb = itb;
725 params->dtb = dtb;
726 params->profile = profile;
727 params->do_quiesce = do_quiesce;
728 params->do_checkpoint_insts = do_checkpoint_insts;
729 params->do_statistics_insts = do_statistics_insts;
730#else
731 if (workload.size() != 1)
732 panic("only one workload allowed");
733 params->process = workload[0];
734#endif
735
736 TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
737 return cpu;
738}
723
724#if FULL_SYSTEM
725 params->itb = itb;
726 params->dtb = dtb;
727 params->profile = profile;
728 params->do_quiesce = do_quiesce;
729 params->do_checkpoint_insts = do_checkpoint_insts;
730 params->do_statistics_insts = do_statistics_insts;
731#else
732 if (workload.size() != 1)
733 panic("only one workload allowed");
734 params->process = workload[0];
735#endif
736
737 TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
738 return cpu;
739}