cpu: `Minor' in-order CPU model
[gem5.git] / src / cpu / minor / fetch1.cc
1 /*
2 * Copyright (c) 2013-2014 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Andrew Bardsley
38 */
39
40 #include <cstring>
41 #include <iomanip>
42 #include <sstream>
43
44 #include "base/cast.hh"
45 #include "cpu/minor/fetch1.hh"
46 #include "cpu/minor/pipeline.hh"
47 #include "debug/Drain.hh"
48 #include "debug/Fetch.hh"
49 #include "debug/MinorTrace.hh"
50
51 namespace Minor
52 {
53
54 Fetch1::Fetch1(const std::string &name_,
55 MinorCPU &cpu_,
56 MinorCPUParams &params,
57 Latch<BranchData>::Output inp_,
58 Latch<ForwardLineData>::Input out_,
59 Latch<BranchData>::Output prediction_,
60 Reservable &next_stage_input_buffer) :
61 Named(name_),
62 cpu(cpu_),
63 inp(inp_),
64 out(out_),
65 prediction(prediction_),
66 nextStageReserve(next_stage_input_buffer),
67 icachePort(name_ + ".icache_port", *this, cpu_),
68 lineSnap(params.fetch1LineSnapWidth),
69 maxLineWidth(params.fetch1LineWidth),
70 fetchLimit(params.fetch1FetchLimit),
71 state(FetchWaitingForPC),
72 pc(0),
73 streamSeqNum(InstId::firstStreamSeqNum),
74 predictionSeqNum(InstId::firstPredictionSeqNum),
75 blocked(false),
76 requests(name_ + ".requests", "lines", params.fetch1FetchLimit),
77 transfers(name_ + ".transfers", "lines", params.fetch1FetchLimit),
78 icacheState(IcacheRunning),
79 lineSeqNum(InstId::firstLineSeqNum),
80 numFetchesInMemorySystem(0),
81 numFetchesInITLB(0)
82 {
83 if (lineSnap == 0) {
84 lineSnap = cpu.cacheLineSize();
85 DPRINTF(Fetch, "lineSnap set to cache line size of: %d\n",
86 lineSnap);
87 }
88
89 if (maxLineWidth == 0) {
90 maxLineWidth = cpu.cacheLineSize();
91 DPRINTF(Fetch, "maxLineWidth set to cache line size of: %d\n",
92 maxLineWidth);
93 }
94
95 /* These assertions should be copied to the Python config. as well */
96 if ((lineSnap % sizeof(TheISA::MachInst)) != 0) {
97 fatal("%s: fetch1LineSnapWidth must be a multiple "
98 "of sizeof(TheISA::MachInst) (%d)\n", name_,
99 sizeof(TheISA::MachInst));
100 }
101
102 if (!(maxLineWidth >= lineSnap &&
103 (maxLineWidth % sizeof(TheISA::MachInst)) == 0))
104 {
105 fatal("%s: fetch1LineWidth must be a multiple of"
106 " sizeof(TheISA::MachInst)"
107 " (%d), and >= fetch1LineSnapWidth (%d)\n",
108 name_, sizeof(TheISA::MachInst), lineSnap);
109 }
110
111 if (fetchLimit < 1) {
112 fatal("%s: fetch1FetchLimit must be >= 1 (%d)\n", name_,
113 fetchLimit);
114 }
115 }
116
117 void
118 Fetch1::fetchLine()
119 {
120 /* If line_offset != 0, a request is pushed for the remainder of the
121 * line. */
122 /* Use a lower, sizeof(MachInst) aligned address for the fetch */
123 Addr aligned_pc = pc.instAddr() & ~((Addr) lineSnap - 1);
124 unsigned int line_offset = aligned_pc % lineSnap;
125 unsigned int request_size = maxLineWidth - line_offset;
126
127 /* Fill in the line's id */
128 InstId request_id(0 /* thread */,
129 streamSeqNum, predictionSeqNum,
130 lineSeqNum);
131
132 FetchRequestPtr request = new FetchRequest(*this, request_id, pc);
133
134 DPRINTF(Fetch, "Inserting fetch into the fetch queue "
135 "%s addr: 0x%x pc: %s line_offset: %d request_size: %d\n",
136 request_id, aligned_pc, pc, line_offset, request_size);
137
138 request->request.setThreadContext(cpu.cpuId(), /* thread id */ 0);
139 request->request.setVirt(0 /* asid */,
140 aligned_pc, request_size, Request::INST_FETCH, cpu.instMasterId(),
141 /* I've no idea why we need the PC, but give it */
142 pc.instAddr());
143
144 DPRINTF(Fetch, "Submitting ITLB request\n");
145 numFetchesInITLB++;
146
147 request->state = FetchRequest::InTranslation;
148
149 /* Reserve space in the queues upstream of requests for results */
150 transfers.reserve();
151 requests.push(request);
152
153 /* Submit the translation request. The response will come
154 * through finish/markDelayed on this request as it bears
155 * the Translation interface */
156 cpu.threads[request->id.threadId]->itb->translateTiming(
157 &request->request,
158 cpu.getContext(request->id.threadId),
159 request, BaseTLB::Execute);
160
161 lineSeqNum++;
162
163 /* Step the PC for the next line onto the line aligned next address.
164 * Note that as instructions can span lines, this PC is only a
165 * reliable 'new' PC if the next line has a new stream sequence number. */
166 #if THE_ISA == ALPHA_ISA
167 /* Restore the low bits of the PC used as address space flags */
168 Addr pc_low_bits = pc.instAddr() &
169 ((Addr) (1 << sizeof(TheISA::MachInst)) - 1);
170
171 pc.set(aligned_pc + request_size + pc_low_bits);
172 #else
173 pc.set(aligned_pc + request_size);
174 #endif
175 }
176
177 std::ostream &
178 operator <<(std::ostream &os, Fetch1::IcacheState state)
179 {
180 switch (state) {
181 case Fetch1::IcacheRunning:
182 os << "IcacheRunning";
183 break;
184 case Fetch1::IcacheNeedsRetry:
185 os << "IcacheNeedsRetry";
186 break;
187 default:
188 os << "IcacheState-" << static_cast<int>(state);
189 break;
190 }
191 return os;
192 }
193
194 void
195 Fetch1::FetchRequest::makePacket()
196 {
197 /* Make the necessary packet for a memory transaction */
198 packet = new Packet(&request, MemCmd::ReadReq);
199 packet->allocate();
200
201 /* This FetchRequest becomes SenderState to allow the response to be
202 * identified */
203 packet->pushSenderState(this);
204 }
205
206 void
207 Fetch1::FetchRequest::finish(
208 Fault fault_, RequestPtr request_, ThreadContext *tc, BaseTLB::Mode mode)
209 {
210 fault = fault_;
211
212 state = Translated;
213 fetch.handleTLBResponse(this);
214
215 /* Let's try and wake up the processor for the next cycle */
216 fetch.cpu.wakeupOnEvent(Pipeline::Fetch1StageId);
217 }
218
219 void
220 Fetch1::handleTLBResponse(FetchRequestPtr response)
221 {
222 numFetchesInITLB--;
223
224 if (response->fault != NoFault) {
225 DPRINTF(Fetch, "Fault in address ITLB translation: %s, "
226 "paddr: 0x%x, vaddr: 0x%x\n",
227 response->fault->name(),
228 (response->request.hasPaddr() ? response->request.getPaddr() : 0),
229 response->request.getVaddr());
230
231 if (DTRACE(MinorTrace))
232 minorTraceResponseLine(name(), response);
233 } else {
234 DPRINTF(Fetch, "Got ITLB response\n");
235 }
236
237 response->state = FetchRequest::Translated;
238
239 tryToSendToTransfers(response);
240 }
241
242 Fetch1::FetchRequest::~FetchRequest()
243 {
244 if (packet)
245 delete packet;
246 }
247
248 void
249 Fetch1::tryToSendToTransfers(FetchRequestPtr request)
250 {
251 if (!requests.empty() && requests.front() != request) {
252 DPRINTF(Fetch, "Fetch not at front of requests queue, can't"
253 " issue to memory\n");
254 return;
255 }
256
257 if (request->state == FetchRequest::InTranslation) {
258 DPRINTF(Fetch, "Fetch still in translation, not issuing to"
259 " memory\n");
260 return;
261 }
262
263 if (request->isDiscardable() || request->fault != NoFault) {
264 /* Discarded and faulting requests carry on through transfers
265 * as Complete/packet == NULL */
266
267 request->state = FetchRequest::Complete;
268 moveFromRequestsToTransfers(request);
269
270 /* Wake up the pipeline next cycle as there will be no event
271 * for this queue->queue transfer */
272 cpu.wakeupOnEvent(Pipeline::Fetch1StageId);
273 } else if (request->state == FetchRequest::Translated) {
274 if (!request->packet)
275 request->makePacket();
276
277 /* Ensure that the packet won't delete the request */
278 assert(request->packet->needsResponse());
279
280 if (tryToSend(request))
281 moveFromRequestsToTransfers(request);
282 } else {
283 DPRINTF(Fetch, "Not advancing line fetch\n");
284 }
285 }
286
287 void
288 Fetch1::moveFromRequestsToTransfers(FetchRequestPtr request)
289 {
290 assert(!requests.empty() && requests.front() == request);
291
292 requests.pop();
293 transfers.push(request);
294 }
295
296 bool
297 Fetch1::tryToSend(FetchRequestPtr request)
298 {
299 bool ret = false;
300
301 if (icachePort.sendTimingReq(request->packet)) {
302 /* Invalidate the fetch_requests packet so we don't
303 * accidentally fail to deallocate it (or use it!)
304 * later by overwriting it */
305 request->packet = NULL;
306 request->state = FetchRequest::RequestIssuing;
307 numFetchesInMemorySystem++;
308
309 ret = true;
310
311 DPRINTF(Fetch, "Issued fetch request to memory: %s\n",
312 request->id);
313 } else {
314 /* Needs to be resent, wait for that */
315 icacheState = IcacheNeedsRetry;
316
317 DPRINTF(Fetch, "Line fetch needs to retry: %s\n",
318 request->id);
319 }
320
321 return ret;
322 }
323
324 void
325 Fetch1::stepQueues()
326 {
327 IcacheState old_icache_state = icacheState;
328
329 switch (icacheState) {
330 case IcacheRunning:
331 /* Move ITLB results on to the memory system */
332 if (!requests.empty()) {
333 tryToSendToTransfers(requests.front());
334 }
335 break;
336 case IcacheNeedsRetry:
337 break;
338 }
339
340 if (icacheState != old_icache_state) {
341 DPRINTF(Fetch, "Step in state %s moving to state %s\n",
342 old_icache_state, icacheState);
343 }
344 }
345
346 void
347 Fetch1::popAndDiscard(FetchQueue &queue)
348 {
349 if (!queue.empty()) {
350 delete queue.front();
351 queue.pop();
352 }
353 }
354
355 unsigned int
356 Fetch1::numInFlightFetches()
357 {
358 return requests.occupiedSpace() +
359 transfers.occupiedSpace();
360 }
361
362 /** Print the appropriate MinorLine line for a fetch response */
363 void
364 Fetch1::minorTraceResponseLine(const std::string &name,
365 Fetch1::FetchRequestPtr response) const
366 {
367 Request &request M5_VAR_USED = response->request;
368
369 if (response->packet && response->packet->isError()) {
370 MINORLINE(this, "id=F;%s vaddr=0x%x fault=\"error packet\"\n",
371 response->id, request.getVaddr());
372 } else if (response->fault != NoFault) {
373 MINORLINE(this, "id=F;%s vaddr=0x%x fault=\"%s\"\n",
374 response->id, request.getVaddr(), response->fault->name());
375 } else {
376 MINORLINE(this, "id=%s size=%d vaddr=0x%x paddr=0x%x\n",
377 response->id, request.getSize(),
378 request.getVaddr(), request.getPaddr());
379 }
380 }
381
382 bool
383 Fetch1::recvTimingResp(PacketPtr response)
384 {
385 DPRINTF(Fetch, "recvTimingResp %d\n", numFetchesInMemorySystem);
386
387 /* Only push the response if we didn't change stream? No, all responses
388 * should hit the responses queue. It's the job of 'step' to throw them
389 * away. */
390 FetchRequestPtr fetch_request = safe_cast<FetchRequestPtr>
391 (response->popSenderState());
392
393 /* Fixup packet in fetch_request as this may have changed */
394 assert(!fetch_request->packet);
395 fetch_request->packet = response;
396
397 numFetchesInMemorySystem--;
398 fetch_request->state = FetchRequest::Complete;
399
400 if (DTRACE(MinorTrace))
401 minorTraceResponseLine(name(), fetch_request);
402
403 if (response->isError()) {
404 DPRINTF(Fetch, "Received error response packet: %s\n",
405 fetch_request->id);
406 }
407
408 /* We go to idle even if there are more things to do on the queues as
409 * it's the job of step to actually step us on to the next transaction */
410
411 /* Let's try and wake up the processor for the next cycle to move on
412 * queues */
413 cpu.wakeupOnEvent(Pipeline::Fetch1StageId);
414
415 /* Never busy */
416 return true;
417 }
418
419 void
420 Fetch1::recvRetry()
421 {
422 DPRINTF(Fetch, "recvRetry\n");
423 assert(icacheState == IcacheNeedsRetry);
424 assert(!requests.empty());
425
426 FetchRequestPtr retryRequest = requests.front();
427
428 icacheState = IcacheRunning;
429
430 if (tryToSend(retryRequest))
431 moveFromRequestsToTransfers(retryRequest);
432 }
433
434 std::ostream &
435 operator <<(std::ostream &os, Fetch1::FetchState state)
436 {
437 switch (state) {
438 case Fetch1::FetchHalted:
439 os << "FetchHalted";
440 break;
441 case Fetch1::FetchWaitingForPC:
442 os << "FetchWaitingForPC";
443 break;
444 case Fetch1::FetchRunning:
445 os << "FetchRunning";
446 break;
447 default:
448 os << "FetchState-" << static_cast<int>(state);
449 break;
450 }
451 return os;
452 }
453
454 void
455 Fetch1::changeStream(const BranchData &branch)
456 {
457 updateExpectedSeqNums(branch);
458
459 /* Start fetching again if we were stopped */
460 switch (branch.reason) {
461 case BranchData::SuspendThread:
462 DPRINTF(Fetch, "Suspending fetch: %s\n", branch);
463 state = FetchWaitingForPC;
464 break;
465 case BranchData::HaltFetch:
466 DPRINTF(Fetch, "Halting fetch\n");
467 state = FetchHalted;
468 break;
469 default:
470 DPRINTF(Fetch, "Changing stream on branch: %s\n", branch);
471 state = FetchRunning;
472 break;
473 }
474 pc = branch.target;
475 }
476
477 void
478 Fetch1::updateExpectedSeqNums(const BranchData &branch)
479 {
480 DPRINTF(Fetch, "Updating streamSeqNum from: %d to %d,"
481 " predictionSeqNum from: %d to %d\n",
482 streamSeqNum, branch.newStreamSeqNum,
483 predictionSeqNum, branch.newPredictionSeqNum);
484
485 /* Change the stream */
486 streamSeqNum = branch.newStreamSeqNum;
487 /* Update the prediction. Note that it's possible for this to
488 * actually set the prediction to an *older* value if new
489 * predictions have been discarded by execute */
490 predictionSeqNum = branch.newPredictionSeqNum;
491 }
492
493 void
494 Fetch1::processResponse(Fetch1::FetchRequestPtr response,
495 ForwardLineData &line)
496 {
497 PacketPtr packet = response->packet;
498
499 /* Pass the prefetch abort (if any) on to Fetch2 in a ForwardLineData
500 * structure */
501 line.setFault(response->fault);
502 /* Make sequence numbers valid in return */
503 line.id = response->id;
504 /* Set PC to virtual address */
505 line.pc = response->pc;
506 /* Set the lineBase, which is a sizeof(MachInst) aligned address <=
507 * pc.instAddr() */
508 line.lineBaseAddr = response->request.getVaddr();
509
510 if (response->fault != NoFault) {
511 /* Stop fetching if there was a fault */
512 /* Should probably try to flush the queues as well, but we
513 * can't be sure that this fault will actually reach Execute, and we
514 * can't (currently) selectively remove this stream from the queues */
515 DPRINTF(Fetch, "Stopping line fetch because of fault: %s\n",
516 response->fault->name());
517 state = Fetch1::FetchWaitingForPC;
518 } else {
519 line.adoptPacketData(packet);
520 /* Null the response's packet to prevent the response from trying to
521 * deallocate the packet */
522 response->packet = NULL;
523 }
524 }
525
526 void
527 Fetch1::evaluate()
528 {
529 const BranchData &execute_branch = *inp.outputWire;
530 const BranchData &fetch2_branch = *prediction.outputWire;
531 ForwardLineData &line_out = *out.inputWire;
532
533 assert(line_out.isBubble());
534
535 blocked = !nextStageReserve.canReserve();
536
537 /* Are we changing stream? Look to the Execute branches first, then
538 * to predicted changes of stream from Fetch2 */
539 /* @todo, find better way to express ignoring branch predictions */
540 if (execute_branch.isStreamChange() &&
541 execute_branch.reason != BranchData::BranchPrediction)
542 {
543 if (state == FetchHalted) {
544 if (execute_branch.reason == BranchData::WakeupFetch) {
545 DPRINTF(Fetch, "Waking up fetch: %s\n", execute_branch);
546 changeStream(execute_branch);
547 } else {
548 DPRINTF(Fetch, "Halted, ignoring branch: %s\n",
549 execute_branch);
550 }
551 } else {
552 changeStream(execute_branch);
553 }
554
555 if (!fetch2_branch.isBubble()) {
556 DPRINTF(Fetch, "Ignoring simultaneous prediction: %s\n",
557 fetch2_branch);
558 }
559
560 /* The streamSeqNum tagging in request/response ->req should handle
561 * discarding those requests when we get to them. */
562 } else if (state != FetchHalted && fetch2_branch.isStreamChange()) {
563 /* Handle branch predictions by changing the instruction source
564 * if we're still processing the same stream (as set by streamSeqNum)
565 * as the one of the prediction.
566 */
567 if (fetch2_branch.newStreamSeqNum != streamSeqNum) {
568 DPRINTF(Fetch, "Not changing stream on prediction: %s,"
569 " streamSeqNum mismatch\n",
570 fetch2_branch);
571 } else {
572 changeStream(fetch2_branch);
573 }
574 }
575
576 /* Can we fetch? */
577 /* The bare minimum requirements for initiating a fetch */
578 /* THREAD need to handle multiple threads */
579 if (state == FetchRunning && /* We are actually fetching */
580 !blocked && /* Space in the Fetch2 inputBuffer */
581 /* The thread we're going to fetch for (thread 0), is active */
582 cpu.getContext(0)->status() == ThreadContext::Active &&
583 numInFlightFetches() < fetchLimit)
584 {
585 fetchLine();
586 /* Take up a slot in the fetch queue */
587 nextStageReserve.reserve();
588 }
589
590 /* Halting shouldn't prevent fetches in flight from being processed */
591 /* Step fetches through the icachePort queues and memory system */
592 stepQueues();
593
594 /* As we've thrown away early lines, if there is a line, it must
595 * be from the right stream */
596 if (!transfers.empty() &&
597 transfers.front()->isComplete())
598 {
599 Fetch1::FetchRequestPtr response = transfers.front();
600
601 if (response->isDiscardable()) {
602 nextStageReserve.freeReservation();
603
604 DPRINTF(Fetch, "Discarding translated fetch at it's for"
605 " an old stream\n");
606
607 /* Wake up next cycle just in case there was some other
608 * action to do */
609 cpu.wakeupOnEvent(Pipeline::Fetch1StageId);
610 } else {
611 DPRINTF(Fetch, "Processing fetched line: %s\n",
612 response->id);
613
614 processResponse(response, line_out);
615 }
616
617 popAndDiscard(transfers);
618 }
619
620 /* If we generated output, and mark the stage as being active
621 * to encourage that output on to the next stage */
622 if (!line_out.isBubble())
623 cpu.activityRecorder->activity();
624
625 /* Fetch1 has no inputBuffer so the only activity we can have is to
626 * generate a line output (tested just above) or to initiate a memory
627 * fetch which will signal activity when it returns/needs stepping
628 * between queues */
629 }
630
631 bool
632 Fetch1::isDrained()
633 {
634 DPRINTF(Drain, "isDrained %s %s%s%s\n",
635 state == FetchHalted,
636 (numInFlightFetches() == 0 ? "" : "inFlightFetches "),
637 ((*out.inputWire).isBubble() ? "" : "outputtingLine"));
638
639 return state == FetchHalted &&
640 numInFlightFetches() == 0 &&
641 (*out.inputWire).isBubble();
642 }
643
644 void
645 Fetch1::FetchRequest::reportData(std::ostream &os) const
646 {
647 os << id;
648 }
649
650 bool Fetch1::FetchRequest::isDiscardable() const
651 {
652 /* Can't discard lines in TLB/memory */
653 return state != InTranslation && state != RequestIssuing &&
654 (id.streamSeqNum != fetch.streamSeqNum ||
655 id.predictionSeqNum != fetch.predictionSeqNum);
656 }
657
658 void
659 Fetch1::minorTrace() const
660 {
661 std::ostringstream data;
662
663 if (blocked)
664 data << 'B';
665 else
666 (*out.inputWire).reportData(data);
667
668 MINORTRACE("state=%s icacheState=%s in_tlb_mem=%s/%s"
669 " streamSeqNum=%d lines=%s\n", state, icacheState,
670 numFetchesInITLB, numFetchesInMemorySystem,
671 streamSeqNum, data.str());
672 requests.minorTrace();
673 transfers.minorTrace();
674 }
675
676 }