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