Merge with head.
[gem5.git] / src / cpu / simple / timing.cc
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
41 using namespace std;
42 using namespace TheISA;
43
44 Port *
45 TimingSimpleCPU::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
55 void
56 TimingSimpleCPU::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
69 Tick
70 TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
71 {
72 panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
73 return curTick;
74 }
75
76 void
77 TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
78 {
79 //No internal storage to update, jusst return
80 return;
81 }
82
83 void
84 TimingSimpleCPU::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
98 void
99 TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
100 {
101 pkt = _pkt;
102 Event::schedule(t);
103 }
104
105 TimingSimpleCPU::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
122 TimingSimpleCPU::~TimingSimpleCPU()
123 {
124 }
125
126 void
127 TimingSimpleCPU::serialize(ostream &os)
128 {
129 SimObject::State so_state = SimObject::getState();
130 SERIALIZE_ENUM(so_state);
131 BaseSimpleCPU::serialize(os);
132 }
133
134 void
135 TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
136 {
137 SimObject::State so_state;
138 UNSERIALIZE_ENUM(so_state);
139 BaseSimpleCPU::unserialize(cp, section);
140 }
141
142 unsigned int
143 TimingSimpleCPU::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
157 void
158 TimingSimpleCPU::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
178 void
179 TimingSimpleCPU::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
192 void
193 TimingSimpleCPU::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
213 void
214 TimingSimpleCPU::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
229 void
230 TimingSimpleCPU::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
245 template <class T>
246 Fault
247 TimingSimpleCPU::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
287 template
288 Fault
289 TimingSimpleCPU::read(Addr addr, Twin64_t &data, unsigned flags);
290
291 template
292 Fault
293 TimingSimpleCPU::read(Addr addr, Twin32_t &data, unsigned flags);
294
295 template
296 Fault
297 TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags);
298
299 template
300 Fault
301 TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags);
302
303 template
304 Fault
305 TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags);
306
307 template
308 Fault
309 TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags);
310
311 #endif //DOXYGEN_SHOULD_SKIP_THIS
312
313 template<>
314 Fault
315 TimingSimpleCPU::read(Addr addr, double &data, unsigned flags)
316 {
317 return read(addr, *(uint64_t*)&data, flags);
318 }
319
320 template<>
321 Fault
322 TimingSimpleCPU::read(Addr addr, float &data, unsigned flags)
323 {
324 return read(addr, *(uint32_t*)&data, flags);
325 }
326
327
328 template<>
329 Fault
330 TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags)
331 {
332 return read(addr, (uint32_t&)data, flags);
333 }
334
335
336 template <class T>
337 Fault
338 TimingSimpleCPU::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
395 template
396 Fault
397 TimingSimpleCPU::write(Twin32_t data, Addr addr,
398 unsigned flags, uint64_t *res);
399
400 template
401 Fault
402 TimingSimpleCPU::write(Twin64_t data, Addr addr,
403 unsigned flags, uint64_t *res);
404
405 template
406 Fault
407 TimingSimpleCPU::write(uint64_t data, Addr addr,
408 unsigned flags, uint64_t *res);
409
410 template
411 Fault
412 TimingSimpleCPU::write(uint32_t data, Addr addr,
413 unsigned flags, uint64_t *res);
414
415 template
416 Fault
417 TimingSimpleCPU::write(uint16_t data, Addr addr,
418 unsigned flags, uint64_t *res);
419
420 template
421 Fault
422 TimingSimpleCPU::write(uint8_t data, Addr addr,
423 unsigned flags, uint64_t *res);
424
425 #endif //DOXYGEN_SHOULD_SKIP_THIS
426
427 template<>
428 Fault
429 TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
430 {
431 return write(*(uint64_t*)&data, addr, flags, res);
432 }
433
434 template<>
435 Fault
436 TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
437 {
438 return write(*(uint32_t*)&data, addr, flags, res);
439 }
440
441
442 template<>
443 Fault
444 TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
445 {
446 return write((uint32_t)data, addr, flags, res);
447 }
448
449
450 void
451 TimingSimpleCPU::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
485 void
486 TimingSimpleCPU::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
499 void
500 TimingSimpleCPU::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
552 void
553 TimingSimpleCPU::IcachePort::ITickEvent::process()
554 {
555 cpu->completeIfetch(pkt);
556 }
557
558 bool
559 TimingSimpleCPU::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
584 void
585 TimingSimpleCPU::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
598 void
599 TimingSimpleCPU::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
632 void
633 TimingSimpleCPU::completeDrain()
634 {
635 DPRINTF(Config, "Done draining\n");
636 changeState(SimObject::Drained);
637 drainEvent->process();
638 }
639
640 void
641 TimingSimpleCPU::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
652 bool
653 TimingSimpleCPU::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
678 void
679 TimingSimpleCPU::DcachePort::DTickEvent::process()
680 {
681 cpu->completeDataAccess(pkt);
682 }
683
684 void
685 TimingSimpleCPU::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 //
704 TimingSimpleCPU *
705 TimingSimpleCPUParams::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;
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 }