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