Merge ktlim@zamp:./local/clean/o3-merge/m5
[gem5.git] / src / cpu / ozone / inorder_back_end_impl.hh
1 /*
2 * Copyright (c) 2006 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: Kevin Lim
29 */
30
31 #include "arch/faults.hh"
32 #include "arch/types.hh"
33 #include "cpu/ozone/inorder_back_end.hh"
34 #include "cpu/ozone/thread_state.hh"
35
36 template <class Impl>
37 InorderBackEnd<Impl>::InorderBackEnd(Params *params)
38 : squashPending(false),
39 squashSeqNum(0),
40 squashNextPC(0),
41 faultFromFetch(NoFault),
42 interruptBlocked(false),
43 cacheCompletionEvent(this),
44 dcacheInterface(params->dcacheInterface),
45 width(params->backEndWidth),
46 latency(params->backEndLatency),
47 squashLatency(params->backEndSquashLatency),
48 numInstsToWB(0, latency + 1)
49 {
50 instsAdded = numInstsToWB.getWire(latency);
51 instsToExecute = numInstsToWB.getWire(0);
52
53 memReq = new MemReq;
54 memReq->data = new uint8_t[64];
55 status = Running;
56 }
57
58 template <class Impl>
59 std::string
60 InorderBackEnd<Impl>::name() const
61 {
62 return cpu->name() + ".inorderbackend";
63 }
64
65 template <class Impl>
66 void
67 InorderBackEnd<Impl>::setXC(ExecContext *xc_ptr)
68 {
69 xc = xc_ptr;
70 memReq->xc = xc;
71 }
72
73 template <class Impl>
74 void
75 InorderBackEnd<Impl>::setThreadState(OzoneThreadState<Impl> *thread_ptr)
76 {
77 thread = thread_ptr;
78 thread->setFuncExeInst(0);
79 }
80
81 #if FULL_SYSTEM
82 template <class Impl>
83 void
84 InorderBackEnd<Impl>::checkInterrupts()
85 {
86 //Check if there are any outstanding interrupts
87 //Handle the interrupts
88 int ipl = 0;
89 int summary = 0;
90
91 cpu->checkInterrupts = false;
92
93 if (thread->readMiscReg(IPR_ASTRR))
94 panic("asynchronous traps not implemented\n");
95
96 if (thread->readMiscReg(IPR_SIRR)) {
97 for (int i = INTLEVEL_SOFTWARE_MIN;
98 i < INTLEVEL_SOFTWARE_MAX; i++) {
99 if (thread->readMiscReg(IPR_SIRR) & (ULL(1) << i)) {
100 // See table 4-19 of the 21164 hardware reference
101 ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1;
102 summary |= (ULL(1) << i);
103 }
104 }
105 }
106
107 uint64_t interrupts = cpu->intr_status();
108
109 if (interrupts) {
110 for (int i = INTLEVEL_EXTERNAL_MIN;
111 i < INTLEVEL_EXTERNAL_MAX; i++) {
112 if (interrupts & (ULL(1) << i)) {
113 // See table 4-19 of the 21164 hardware reference
114 ipl = i;
115 summary |= (ULL(1) << i);
116 }
117 }
118 }
119
120 if (ipl && ipl > thread->readMiscReg(IPR_IPLR)) {
121 thread->inSyscall = true;
122
123 thread->setMiscReg(IPR_ISR, summary);
124 thread->setMiscReg(IPR_INTID, ipl);
125 Fault(new InterruptFault)->invoke(xc);
126 DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n",
127 thread->readMiscReg(IPR_IPLR), ipl, summary);
128
129 // May need to go 1 inst prior
130 squashPending = true;
131
132 thread->inSyscall = false;
133
134 setSquashInfoFromXC();
135 }
136 }
137 #endif
138
139 template <class Impl>
140 void
141 InorderBackEnd<Impl>::tick()
142 {
143 // Squash due to an external source
144 // Not sure if this or an interrupt has higher priority
145 if (squashPending) {
146 squash(squashSeqNum, squashNextPC);
147 return;
148 }
149
150 // if (interrupt) then set thread PC, stall front end, record that
151 // I'm waiting for it to drain. (for now just squash)
152 #if FULL_SYSTEM
153 if (interruptBlocked ||
154 (cpu->checkInterrupts &&
155 cpu->check_interrupts() &&
156 !cpu->inPalMode())) {
157 if (!robEmpty()) {
158 interruptBlocked = true;
159 } else if (robEmpty() && cpu->inPalMode()) {
160 // Will need to let the front end continue a bit until
161 // we're out of pal mode. Hopefully we never get into an
162 // infinite loop...
163 interruptBlocked = false;
164 } else {
165 interruptBlocked = false;
166 checkInterrupts();
167 return;
168 }
169 }
170 #endif
171
172 if (status != DcacheMissLoadStall &&
173 status != DcacheMissStoreStall) {
174 for (int i = 0; i < width && (*instsAdded) < width; ++i) {
175 DynInstPtr inst = frontEnd->getInst();
176
177 if (!inst)
178 break;
179
180 instList.push_back(inst);
181
182 (*instsAdded)++;
183 }
184
185 #if FULL_SYSTEM
186 if (faultFromFetch && robEmpty() && frontEnd->isEmpty()) {
187 handleFault();
188 } else {
189 executeInsts();
190 }
191 #else
192 executeInsts();
193 #endif
194 }
195 }
196
197 template <class Impl>
198 void
199 InorderBackEnd<Impl>::executeInsts()
200 {
201 bool completed_last_inst = true;
202 int insts_to_execute = *instsToExecute;
203 int freed_regs = 0;
204
205 while (insts_to_execute > 0) {
206 assert(!instList.empty());
207 DynInstPtr inst = instList.front();
208
209 commitPC = inst->readPC();
210
211 thread->setPC(commitPC);
212 thread->setNextPC(inst->readNextPC());
213
214 #if FULL_SYSTEM
215 int count = 0;
216 Addr oldpc;
217 do {
218 if (count == 0)
219 assert(!thread->inSyscall && !thread->trapPending);
220 oldpc = thread->readPC();
221 cpu->system->pcEventQueue.service(
222 thread->getXCProxy());
223 count++;
224 } while (oldpc != thread->readPC());
225 if (count > 1) {
226 DPRINTF(IBE, "PC skip function event, stopping commit\n");
227 completed_last_inst = false;
228 squashPending = true;
229 break;
230 }
231 #endif
232
233 Fault inst_fault = NoFault;
234
235 if (status == DcacheMissComplete) {
236 DPRINTF(IBE, "Completing inst [sn:%lli]\n", inst->seqNum);
237 status = Running;
238 } else if (inst->isMemRef() && status != DcacheMissComplete &&
239 (!inst->isDataPrefetch() && !inst->isInstPrefetch())) {
240 DPRINTF(IBE, "Initiating mem op inst [sn:%lli] PC: %#x\n",
241 inst->seqNum, inst->readPC());
242
243 cacheCompletionEvent.inst = inst;
244 inst_fault = inst->initiateAcc();
245 if (inst_fault == NoFault &&
246 status != DcacheMissLoadStall &&
247 status != DcacheMissStoreStall) {
248 inst_fault = inst->completeAcc();
249 }
250 ++thread->funcExeInst;
251 } else {
252 DPRINTF(IBE, "Executing inst [sn:%lli] PC: %#x\n",
253 inst->seqNum, inst->readPC());
254 inst_fault = inst->execute();
255 ++thread->funcExeInst;
256 }
257
258 // Will need to be able to break this loop in case the load
259 // misses. Split access/complete ops would be useful here
260 // with writeback events.
261 if (status == DcacheMissLoadStall) {
262 *instsToExecute = insts_to_execute;
263
264 completed_last_inst = false;
265 break;
266 } else if (status == DcacheMissStoreStall) {
267 // Figure out how to fix this hack. Probably have DcacheMissLoad
268 // vs DcacheMissStore.
269 *instsToExecute = insts_to_execute;
270 completed_last_inst = false;
271 /*
272 instList.pop_front();
273 --insts_to_execute;
274 if (inst->traceData) {
275 inst->traceData->finalize();
276 }
277 */
278
279 // Don't really need to stop for a store stall as long as
280 // the memory system is able to handle store forwarding
281 // and such. Breaking out might help avoid the cache
282 // interface becoming blocked.
283 break;
284 }
285
286 inst->setExecuted();
287 inst->setResultReady();
288 inst->setCanCommit();
289
290 instList.pop_front();
291
292 --insts_to_execute;
293 --(*instsToExecute);
294
295 if (inst->traceData) {
296 inst->traceData->finalize();
297 inst->traceData = NULL;
298 }
299
300 if (inst_fault != NoFault) {
301 #if FULL_SYSTEM
302 DPRINTF(IBE, "Inst [sn:%lli] PC %#x has a fault\n",
303 inst->seqNum, inst->readPC());
304
305 assert(!thread->inSyscall);
306
307 thread->inSyscall = true;
308
309 // Hack for now; DTB will sometimes need the machine instruction
310 // for when faults happen. So we will set it here, prior to the
311 // DTB possibly needing it for this translation.
312 thread->setInst(
313 static_cast<TheISA::MachInst>(inst->staticInst->machInst));
314
315 // Consider holding onto the trap and waiting until the trap event
316 // happens for this to be executed.
317 inst_fault->invoke(xc);
318
319 // Exit state update mode to avoid accidental updating.
320 thread->inSyscall = false;
321
322 squashPending = true;
323
324 // Generate trap squash event.
325 // generateTrapEvent(tid);
326 completed_last_inst = false;
327 break;
328 #else // !FULL_SYSTEM
329 panic("fault (%d) detected @ PC %08p", inst_fault,
330 inst->PC);
331 #endif // FULL_SYSTEM
332 }
333
334 for (int i = 0; i < inst->numDestRegs(); ++i) {
335 renameTable[inst->destRegIdx(i)] = inst;
336 thread->renameTable[inst->destRegIdx(i)] = inst;
337 ++freed_regs;
338 }
339
340 inst->clearDependents();
341
342 comm->access(0)->doneSeqNum = inst->seqNum;
343
344 if (inst->mispredicted()) {
345 squash(inst->seqNum, inst->readNextPC());
346
347 thread->setNextPC(inst->readNextPC());
348
349 break;
350 } else if (squashPending) {
351 // Something external happened that caused the CPU to squash.
352 // Break out of commit and handle the squash next cycle.
353 break;
354 }
355 // If it didn't mispredict, then it executed fine. Send back its
356 // registers and BP info? What about insts that may still have
357 // latency, like loads? Probably can send back the information after
358 // it is completed.
359
360 // keep an instruction count
361 cpu->numInst++;
362 thread->numInsts++;
363 }
364
365 frontEnd->addFreeRegs(freed_regs);
366
367 assert(insts_to_execute >= 0);
368
369 // Should only advance this if I have executed all instructions.
370 if (insts_to_execute == 0) {
371 numInstsToWB.advance();
372 }
373
374 // Should I set the PC to the next PC here? What do I set next PC to?
375 if (completed_last_inst) {
376 thread->setPC(thread->readNextPC());
377 thread->setNextPC(thread->readPC() + sizeof(MachInst));
378 }
379
380 if (squashPending) {
381 setSquashInfoFromXC();
382 }
383 }
384
385 template <class Impl>
386 void
387 InorderBackEnd<Impl>::handleFault()
388 {
389 DPRINTF(Commit, "Handling fault from fetch\n");
390
391 assert(!thread->inSyscall);
392
393 thread->inSyscall = true;
394
395 // Consider holding onto the trap and waiting until the trap event
396 // happens for this to be executed.
397 faultFromFetch->invoke(xc);
398
399 // Exit state update mode to avoid accidental updating.
400 thread->inSyscall = false;
401
402 squashPending = true;
403
404 setSquashInfoFromXC();
405 }
406
407 template <class Impl>
408 void
409 InorderBackEnd<Impl>::squash(const InstSeqNum &squash_num, const Addr &next_PC)
410 {
411 DPRINTF(IBE, "Squashing from [sn:%lli], setting PC to %#x\n",
412 squash_num, next_PC);
413
414 InstListIt squash_it = --(instList.end());
415
416 int freed_regs = 0;
417
418 while (!instList.empty() && (*squash_it)->seqNum > squash_num) {
419 DynInstPtr inst = *squash_it;
420
421 DPRINTF(IBE, "Squashing instruction PC %#x, [sn:%lli].\n",
422 inst->readPC(),
423 inst->seqNum);
424
425 // May cause problems with misc regs
426 freed_regs+= inst->numDestRegs();
427 inst->clearDependents();
428 squash_it--;
429 instList.pop_back();
430 }
431
432 frontEnd->addFreeRegs(freed_regs);
433
434 for (int i = 0; i < latency+1; ++i) {
435 numInstsToWB.advance();
436 }
437
438 squashPending = false;
439
440 // Probably want to make sure that this squash is the one that set the
441 // thread into inSyscall mode.
442 thread->inSyscall = false;
443
444 // Tell front end to squash, reset PC to new one.
445 frontEnd->squash(squash_num, next_PC);
446
447 faultFromFetch = NULL;
448 }
449
450 template <class Impl>
451 void
452 InorderBackEnd<Impl>::squashFromXC()
453 {
454 // Record that I need to squash
455 squashPending = true;
456
457 thread->inSyscall = true;
458 }
459
460 template <class Impl>
461 void
462 InorderBackEnd<Impl>::setSquashInfoFromXC()
463 {
464 // Need to handle the case of the instList being empty. In that case
465 // probably any number works, except maybe with stores in the store buffer.
466 squashSeqNum = instList.empty() ? 0 : instList.front()->seqNum - 1;
467
468 squashNextPC = thread->PC;
469 }
470
471 template <class Impl>
472 void
473 InorderBackEnd<Impl>::fetchFault(Fault &fault)
474 {
475 faultFromFetch = fault;
476 }
477
478 template <class Impl>
479 void
480 InorderBackEnd<Impl>::dumpInsts()
481 {
482 int num = 0;
483 int valid_num = 0;
484
485 InstListIt inst_list_it = instList.begin();
486
487 cprintf("Inst list size: %i\n", instList.size());
488
489 while (inst_list_it != instList.end())
490 {
491 cprintf("Instruction:%i\n",
492 num);
493 if (!(*inst_list_it)->isSquashed()) {
494 if (!(*inst_list_it)->isIssued()) {
495 ++valid_num;
496 cprintf("Count:%i\n", valid_num);
497 } else if ((*inst_list_it)->isMemRef() &&
498 !(*inst_list_it)->memOpDone) {
499 // Loads that have not been marked as executed still count
500 // towards the total instructions.
501 ++valid_num;
502 cprintf("Count:%i\n", valid_num);
503 }
504 }
505
506 cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n"
507 "Issued:%i\nSquashed:%i\n",
508 (*inst_list_it)->readPC(),
509 (*inst_list_it)->seqNum,
510 (*inst_list_it)->threadNumber,
511 (*inst_list_it)->isIssued(),
512 (*inst_list_it)->isSquashed());
513
514 if ((*inst_list_it)->isMemRef()) {
515 cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone);
516 }
517
518 cprintf("\n");
519
520 inst_list_it++;
521 ++num;
522 }
523 }
524
525 template <class Impl>
526 InorderBackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent(
527 InorderBackEnd *_be)
528 : Event(&mainEventQueue, CPU_Tick_Pri), be(_be)
529 {
530 // this->setFlags(Event::AutoDelete);
531 }
532
533 template <class Impl>
534 void
535 InorderBackEnd<Impl>::DCacheCompletionEvent::process()
536 {
537 inst->completeAcc();
538 be->status = DcacheMissComplete;
539 }
540
541 template <class Impl>
542 const char *
543 InorderBackEnd<Impl>::DCacheCompletionEvent::description()
544 {
545 return "DCache completion event";
546 }