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