Merge zizzer.eecs.umich.edu:/bk/newmem
[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 "cpu/exetrace.hh"
34 #include "cpu/simple/timing.hh"
35 #include "mem/packet.hh"
36 #include "mem/packet_access.hh"
37 #include "sim/builder.hh"
38 #include "sim/system.hh"
39
40 using namespace std;
41 using namespace TheISA;
42
43 Port *
44 TimingSimpleCPU::getPort(const std::string &if_name, int idx)
45 {
46 if (if_name == "dcache_port")
47 return &dcachePort;
48 else if (if_name == "icache_port")
49 return &icachePort;
50 else
51 panic("No Such Port\n");
52 }
53
54 void
55 TimingSimpleCPU::init()
56 {
57 BaseCPU::init();
58 #if FULL_SYSTEM
59 for (int i = 0; i < threadContexts.size(); ++i) {
60 ThreadContext *tc = threadContexts[i];
61
62 // initialize CPU, including PC
63 TheISA::initCPU(tc, tc->readCpuId());
64 }
65 #endif
66 }
67
68 Tick
69 TimingSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt)
70 {
71 panic("TimingSimpleCPU doesn't expect recvAtomic callback!");
72 return curTick;
73 }
74
75 void
76 TimingSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt)
77 {
78 //No internal storage to update, jusst return
79 return;
80 }
81
82 void
83 TimingSimpleCPU::CpuPort::recvStatusChange(Status status)
84 {
85 if (status == RangeChange)
86 return;
87
88 panic("TimingSimpleCPU doesn't expect recvStatusChange callback!");
89 }
90
91
92 void
93 TimingSimpleCPU::CpuPort::TickEvent::schedule(PacketPtr _pkt, Tick t)
94 {
95 pkt = _pkt;
96 Event::schedule(t);
97 }
98
99 TimingSimpleCPU::TimingSimpleCPU(Params *p)
100 : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock),
101 cpu_id(p->cpu_id)
102 {
103 _status = Idle;
104 ifetch_pkt = dcache_pkt = NULL;
105 drainEvent = NULL;
106 fetchEvent = NULL;
107 previousTick = 0;
108 changeState(SimObject::Running);
109 }
110
111
112 TimingSimpleCPU::~TimingSimpleCPU()
113 {
114 }
115
116 void
117 TimingSimpleCPU::serialize(ostream &os)
118 {
119 SimObject::State so_state = SimObject::getState();
120 SERIALIZE_ENUM(so_state);
121 BaseSimpleCPU::serialize(os);
122 }
123
124 void
125 TimingSimpleCPU::unserialize(Checkpoint *cp, const string &section)
126 {
127 SimObject::State so_state;
128 UNSERIALIZE_ENUM(so_state);
129 BaseSimpleCPU::unserialize(cp, section);
130 }
131
132 unsigned int
133 TimingSimpleCPU::drain(Event *drain_event)
134 {
135 // TimingSimpleCPU is ready to drain if it's not waiting for
136 // an access to complete.
137 if (status() == Idle || status() == Running || status() == SwitchedOut) {
138 changeState(SimObject::Drained);
139 return 0;
140 } else {
141 changeState(SimObject::Draining);
142 drainEvent = drain_event;
143 return 1;
144 }
145 }
146
147 void
148 TimingSimpleCPU::resume()
149 {
150 if (_status != SwitchedOut && _status != Idle) {
151 assert(system->getMemoryMode() == System::Timing);
152
153 // Delete the old event if it existed.
154 if (fetchEvent) {
155 if (fetchEvent->scheduled())
156 fetchEvent->deschedule();
157
158 delete fetchEvent;
159 }
160
161 fetchEvent =
162 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
163 fetchEvent->schedule(curTick);
164 }
165
166 changeState(SimObject::Running);
167 previousTick = curTick;
168 }
169
170 void
171 TimingSimpleCPU::switchOut()
172 {
173 assert(status() == Running || status() == Idle);
174 _status = SwitchedOut;
175 numCycles += curTick - previousTick;
176
177 // If we've been scheduled to resume but are then told to switch out,
178 // we'll need to cancel it.
179 if (fetchEvent && fetchEvent->scheduled())
180 fetchEvent->deschedule();
181 }
182
183
184 void
185 TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU)
186 {
187 BaseCPU::takeOverFrom(oldCPU);
188
189 // if any of this CPU's ThreadContexts are active, mark the CPU as
190 // running and schedule its tick event.
191 for (int i = 0; i < threadContexts.size(); ++i) {
192 ThreadContext *tc = threadContexts[i];
193 if (tc->status() == ThreadContext::Active && _status != Running) {
194 _status = Running;
195 break;
196 }
197 }
198
199 if (_status != Running) {
200 _status = Idle;
201 }
202
203 Port *peer;
204 if (icachePort.getPeer() == NULL) {
205 peer = oldCPU->getPort("icache_port")->getPeer();
206 icachePort.setPeer(peer);
207 } else {
208 peer = icachePort.getPeer();
209 }
210 peer->setPeer(&icachePort);
211
212 if (dcachePort.getPeer() == NULL) {
213 peer = oldCPU->getPort("dcache_port")->getPeer();
214 dcachePort.setPeer(peer);
215 } else {
216 peer = dcachePort.getPeer();
217 }
218 peer->setPeer(&dcachePort);
219 }
220
221
222 void
223 TimingSimpleCPU::activateContext(int thread_num, int delay)
224 {
225 assert(thread_num == 0);
226 assert(thread);
227
228 assert(_status == Idle);
229
230 notIdleFraction++;
231 _status = Running;
232 // kick things off by initiating the fetch of the next instruction
233 fetchEvent =
234 new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false);
235 fetchEvent->schedule(curTick + cycles(delay));
236 }
237
238
239 void
240 TimingSimpleCPU::suspendContext(int thread_num)
241 {
242 assert(thread_num == 0);
243 assert(thread);
244
245 assert(_status == Running);
246
247 // just change status to Idle... if status != Running,
248 // completeInst() will not initiate fetch of next instruction.
249
250 notIdleFraction--;
251 _status = Idle;
252 }
253
254
255 template <class T>
256 Fault
257 TimingSimpleCPU::read(Addr addr, T &data, unsigned flags)
258 {
259 Request *req =
260 new Request(/* asid */ 0, addr, sizeof(T), flags, thread->readPC(),
261 cpu_id, /* thread ID */ 0);
262
263 if (traceData) {
264 traceData->setAddr(req->getVaddr());
265 }
266
267 // translate to physical address
268 Fault fault = thread->translateDataReadReq(req);
269
270 // Now do the access.
271 if (fault == NoFault) {
272 PacketPtr pkt =
273 new Packet(req, Packet::ReadReq, Packet::Broadcast);
274 pkt->dataDynamic<T>(new T);
275
276 if (!dcachePort.sendTiming(pkt)) {
277 _status = DcacheRetry;
278 dcache_pkt = pkt;
279 } else {
280 _status = DcacheWaitResponse;
281 // memory system takes ownership of packet
282 dcache_pkt = NULL;
283 }
284 }
285
286 // This will need a new way to tell if it has a dcache attached.
287 if (req->isUncacheable())
288 recordEvent("Uncached Read");
289
290 return fault;
291 }
292
293 #ifndef DOXYGEN_SHOULD_SKIP_THIS
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 // translate to physical address
345 Fault fault = thread->translateDataWriteReq(req);
346
347 // Now do the access.
348 if (fault == NoFault) {
349 assert(dcache_pkt == NULL);
350 dcache_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast);
351 dcache_pkt->allocate();
352 dcache_pkt->set(data);
353
354 bool do_access = true; // flag to suppress cache access
355
356 if (req->isLocked()) {
357 do_access = TheISA::handleLockedWrite(thread, req);
358 }
359
360 if (do_access) {
361 if (!dcachePort.sendTiming(dcache_pkt)) {
362 _status = DcacheRetry;
363 } else {
364 _status = DcacheWaitResponse;
365 // memory system takes ownership of packet
366 dcache_pkt = NULL;
367 }
368 }
369 }
370
371 // This will need a new way to tell if it's hooked up to a cache or not.
372 if (req->isUncacheable())
373 recordEvent("Uncached Write");
374
375 // If the write needs to have a fault on the access, consider calling
376 // changeStatus() and changing it to "bad addr write" or something.
377 return fault;
378 }
379
380
381 #ifndef DOXYGEN_SHOULD_SKIP_THIS
382 template
383 Fault
384 TimingSimpleCPU::write(uint64_t data, Addr addr,
385 unsigned flags, uint64_t *res);
386
387 template
388 Fault
389 TimingSimpleCPU::write(uint32_t data, Addr addr,
390 unsigned flags, uint64_t *res);
391
392 template
393 Fault
394 TimingSimpleCPU::write(uint16_t data, Addr addr,
395 unsigned flags, uint64_t *res);
396
397 template
398 Fault
399 TimingSimpleCPU::write(uint8_t data, Addr addr,
400 unsigned flags, uint64_t *res);
401
402 #endif //DOXYGEN_SHOULD_SKIP_THIS
403
404 template<>
405 Fault
406 TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res)
407 {
408 return write(*(uint64_t*)&data, addr, flags, res);
409 }
410
411 template<>
412 Fault
413 TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res)
414 {
415 return write(*(uint32_t*)&data, addr, flags, res);
416 }
417
418
419 template<>
420 Fault
421 TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res)
422 {
423 return write((uint32_t)data, addr, flags, res);
424 }
425
426
427 void
428 TimingSimpleCPU::fetch()
429 {
430 if (!curStaticInst || !curStaticInst->isDelayedCommit())
431 checkForInterrupts();
432
433 Request *ifetch_req = new Request();
434 ifetch_req->setThreadContext(cpu_id, /* thread ID */ 0);
435 Fault fault = setupFetchRequest(ifetch_req);
436
437 ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast);
438 ifetch_pkt->dataStatic(&inst);
439
440 if (fault == NoFault) {
441 if (!icachePort.sendTiming(ifetch_pkt)) {
442 // Need to wait for retry
443 _status = IcacheRetry;
444 } else {
445 // Need to wait for cache to respond
446 _status = IcacheWaitResponse;
447 // ownership of packet transferred to memory system
448 ifetch_pkt = NULL;
449 }
450 } else {
451 // fetch fault: advance directly to next instruction (fault handler)
452 advanceInst(fault);
453 }
454
455 numCycles += curTick - previousTick;
456 previousTick = curTick;
457 }
458
459
460 void
461 TimingSimpleCPU::advanceInst(Fault fault)
462 {
463 advancePC(fault);
464
465 if (_status == Running) {
466 // kick off fetch of next instruction... callback from icache
467 // response will cause that instruction to be executed,
468 // keeping the CPU running.
469 fetch();
470 }
471 }
472
473
474 void
475 TimingSimpleCPU::completeIfetch(PacketPtr pkt)
476 {
477 // received a response from the icache: execute the received
478 // instruction
479 assert(pkt->result == Packet::Success);
480 assert(_status == IcacheWaitResponse);
481
482 _status = Running;
483
484 delete pkt->req;
485 delete pkt;
486
487 numCycles += curTick - previousTick;
488 previousTick = curTick;
489
490 if (getState() == SimObject::Draining) {
491 completeDrain();
492 return;
493 }
494
495 preExecute();
496 if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) {
497 // load or store: just send to dcache
498 Fault fault = curStaticInst->initiateAcc(this, traceData);
499 if (_status != Running) {
500 // instruction will complete in dcache response callback
501 assert(_status == DcacheWaitResponse || _status == DcacheRetry);
502 assert(fault == NoFault);
503 } else {
504 if (fault == NoFault) {
505 // early fail on store conditional: complete now
506 assert(dcache_pkt != NULL);
507 fault = curStaticInst->completeAcc(dcache_pkt, this,
508 traceData);
509 delete dcache_pkt->req;
510 delete dcache_pkt;
511 dcache_pkt = NULL;
512 }
513 postExecute();
514 advanceInst(fault);
515 }
516 } else {
517 // non-memory instruction: execute completely now
518 Fault fault = curStaticInst->execute(this, traceData);
519 postExecute();
520 advanceInst(fault);
521 }
522 }
523
524 void
525 TimingSimpleCPU::IcachePort::ITickEvent::process()
526 {
527 cpu->completeIfetch(pkt);
528 }
529
530 bool
531 TimingSimpleCPU::IcachePort::recvTiming(PacketPtr pkt)
532 {
533 if (pkt->isResponse()) {
534 // delay processing of returned data until next CPU clock edge
535 Tick time = pkt->req->getTime();
536 while (time < curTick)
537 time += lat;
538
539 if (time == curTick)
540 cpu->completeIfetch(pkt);
541 else
542 tickEvent.schedule(pkt, time);
543
544 return true;
545 }
546 else {
547 //Snooping a Coherence Request, do nothing
548 return true;
549 }
550 }
551
552 void
553 TimingSimpleCPU::IcachePort::recvRetry()
554 {
555 // we shouldn't get a retry unless we have a packet that we're
556 // waiting to transmit
557 assert(cpu->ifetch_pkt != NULL);
558 assert(cpu->_status == IcacheRetry);
559 PacketPtr tmp = cpu->ifetch_pkt;
560 if (sendTiming(tmp)) {
561 cpu->_status = IcacheWaitResponse;
562 cpu->ifetch_pkt = NULL;
563 }
564 }
565
566 void
567 TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
568 {
569 // received a response from the dcache: complete the load or store
570 // instruction
571 assert(pkt->result == Packet::Success);
572 assert(_status == DcacheWaitResponse);
573 _status = Running;
574
575 numCycles += curTick - previousTick;
576 previousTick = curTick;
577
578 Fault fault = curStaticInst->completeAcc(pkt, this, traceData);
579
580 if (pkt->isRead() && pkt->req->isLocked()) {
581 TheISA::handleLockedRead(thread, pkt->req);
582 }
583
584 delete pkt->req;
585 delete pkt;
586
587 postExecute();
588
589 if (getState() == SimObject::Draining) {
590 advancePC(fault);
591 completeDrain();
592
593 return;
594 }
595
596 advanceInst(fault);
597 }
598
599
600 void
601 TimingSimpleCPU::completeDrain()
602 {
603 DPRINTF(Config, "Done draining\n");
604 changeState(SimObject::Drained);
605 drainEvent->process();
606 }
607
608 bool
609 TimingSimpleCPU::DcachePort::recvTiming(PacketPtr pkt)
610 {
611 if (pkt->isResponse()) {
612 // delay processing of returned data until next CPU clock edge
613 Tick time = pkt->req->getTime();
614 while (time < curTick)
615 time += lat;
616
617 if (time == curTick)
618 cpu->completeDataAccess(pkt);
619 else
620 tickEvent.schedule(pkt, time);
621
622 return true;
623 }
624 else {
625 //Snooping a coherence req, do nothing
626 return true;
627 }
628 }
629
630 void
631 TimingSimpleCPU::DcachePort::DTickEvent::process()
632 {
633 cpu->completeDataAccess(pkt);
634 }
635
636 void
637 TimingSimpleCPU::DcachePort::recvRetry()
638 {
639 // we shouldn't get a retry unless we have a packet that we're
640 // waiting to transmit
641 assert(cpu->dcache_pkt != NULL);
642 assert(cpu->_status == DcacheRetry);
643 PacketPtr tmp = cpu->dcache_pkt;
644 if (sendTiming(tmp)) {
645 cpu->_status = DcacheWaitResponse;
646 // memory system takes ownership of packet
647 cpu->dcache_pkt = NULL;
648 }
649 }
650
651
652 ////////////////////////////////////////////////////////////////////////
653 //
654 // TimingSimpleCPU Simulation Object
655 //
656 BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
657
658 Param<Counter> max_insts_any_thread;
659 Param<Counter> max_insts_all_threads;
660 Param<Counter> max_loads_any_thread;
661 Param<Counter> max_loads_all_threads;
662 Param<Tick> progress_interval;
663 SimObjectParam<MemObject *> mem;
664 SimObjectParam<System *> system;
665 Param<int> cpu_id;
666
667 #if FULL_SYSTEM
668 SimObjectParam<AlphaITB *> itb;
669 SimObjectParam<AlphaDTB *> dtb;
670 Param<Tick> profile;
671 #else
672 SimObjectParam<Process *> workload;
673 #endif // FULL_SYSTEM
674
675 Param<int> clock;
676
677 Param<bool> defer_registration;
678 Param<int> width;
679 Param<bool> function_trace;
680 Param<Tick> function_trace_start;
681 Param<bool> simulate_stalls;
682
683 END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU)
684
685 BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
686
687 INIT_PARAM(max_insts_any_thread,
688 "terminate when any thread reaches this inst count"),
689 INIT_PARAM(max_insts_all_threads,
690 "terminate when all threads have reached this inst count"),
691 INIT_PARAM(max_loads_any_thread,
692 "terminate when any thread reaches this load count"),
693 INIT_PARAM(max_loads_all_threads,
694 "terminate when all threads have reached this load count"),
695 INIT_PARAM(progress_interval, "Progress interval"),
696 INIT_PARAM(mem, "memory"),
697 INIT_PARAM(system, "system object"),
698 INIT_PARAM(cpu_id, "processor ID"),
699
700 #if FULL_SYSTEM
701 INIT_PARAM(itb, "Instruction TLB"),
702 INIT_PARAM(dtb, "Data TLB"),
703 INIT_PARAM(profile, ""),
704 #else
705 INIT_PARAM(workload, "processes to run"),
706 #endif // FULL_SYSTEM
707
708 INIT_PARAM(clock, "clock speed"),
709 INIT_PARAM(defer_registration, "defer system registration (for sampling)"),
710 INIT_PARAM(width, "cpu width"),
711 INIT_PARAM(function_trace, "Enable function trace"),
712 INIT_PARAM(function_trace_start, "Cycle to start function trace"),
713 INIT_PARAM(simulate_stalls, "Simulate cache stall cycles")
714
715 END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU)
716
717
718 CREATE_SIM_OBJECT(TimingSimpleCPU)
719 {
720 TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params();
721 params->name = getInstanceName();
722 params->numberOfThreads = 1;
723 params->max_insts_any_thread = max_insts_any_thread;
724 params->max_insts_all_threads = max_insts_all_threads;
725 params->max_loads_any_thread = max_loads_any_thread;
726 params->max_loads_all_threads = max_loads_all_threads;
727 params->progress_interval = progress_interval;
728 params->deferRegistration = defer_registration;
729 params->clock = clock;
730 params->functionTrace = function_trace;
731 params->functionTraceStart = function_trace_start;
732 params->mem = mem;
733 params->system = system;
734 params->cpu_id = cpu_id;
735
736 #if FULL_SYSTEM
737 params->itb = itb;
738 params->dtb = dtb;
739 params->profile = profile;
740 #else
741 params->process = workload;
742 #endif
743
744 TimingSimpleCPU *cpu = new TimingSimpleCPU(params);
745 return cpu;
746 }
747
748 REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU)
749