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