cpu: Get rid of the serviceInstCountEvents method.
[gem5.git] / src / cpu / minor / buffers.hh
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 /**
41 * @file
42 *
43 * Classes for buffer, queue and FIFO behaviour.
44 */
45
46 #ifndef __CPU_MINOR_BUFFERS_HH__
47 #define __CPU_MINOR_BUFFERS_HH__
48
49 #include <iostream>
50 #include <queue>
51 #include <sstream>
52
53 #include "base/logging.hh"
54 #include "cpu/activity.hh"
55 #include "cpu/minor/trace.hh"
56 #include "cpu/timebuf.hh"
57
58 namespace Minor
59 {
60
61 /** Interface class for data with reporting/tracing facilities. This
62 * interface doesn't actually have to be used as other classes which need
63 * this interface uses templating rather than inheritance but it's provided
64 * here to document the interface needed by those classes. */
65 class ReportIF
66 {
67 public:
68 /** Print the data in a format suitable to be the value in "name=value"
69 * trace lines */
70 virtual void reportData(std::ostream &os) const = 0;
71
72 virtual ~ReportIF() { }
73 };
74
75 /** Interface class for data with 'bubble' values. This interface doesn't
76 * actually have to be used as other classes which need this interface uses
77 * templating rather than inheritance but it's provided here to document
78 * the interface needed by those classes. */
79 class BubbleIF
80 {
81 public:
82 virtual bool isBubble() const = 0;
83 };
84
85 /** ...ReportTraits are trait classes with the same functionality as
86 * ReportIF, but with elements explicitly passed into the report...
87 * functions. */
88
89 /** Allow a template using ReportTraits to call report... functions of
90 * ReportIF-bearing elements themselves */
91 template <typename ElemType> /* ElemType should implement ReportIF */
92 class ReportTraitsAdaptor
93 {
94 public:
95 static void
96 reportData(std::ostream &os, const ElemType &elem)
97 { elem.reportData(os); }
98 };
99
100 /** A similar adaptor but for elements held by pointer
101 * ElemType should implement ReportIF */
102 template <typename PtrType>
103 class ReportTraitsPtrAdaptor
104 {
105 public:
106 static void
107 reportData(std::ostream &os, const PtrType &elem)
108 { elem->reportData(os); }
109 };
110
111 /** ... BubbleTraits are trait classes to add BubbleIF interface
112 * functionality to templates which process elements which don't necessarily
113 * implement BubbleIF themselves */
114
115 /** Default behaviour, no bubbles */
116 template <typename ElemType>
117 class NoBubbleTraits
118 {
119 public:
120 static bool isBubble(const ElemType &) { return false; }
121 static ElemType
122 bubble()
123 {
124 panic("bubble called but no bubble interface");
125 }
126 };
127
128 /** Pass on call to the element */
129 template <typename ElemType>
130 class BubbleTraitsAdaptor
131 {
132 public:
133 static bool isBubble(const ElemType &elem)
134 { return elem.isBubble(); }
135
136 static ElemType bubble() { return ElemType::bubble(); }
137 };
138
139 /** Pass on call to the element where the element is a pointer */
140 template <typename PtrType, typename ElemType>
141 class BubbleTraitsPtrAdaptor
142 {
143 public:
144 static bool isBubble(const PtrType &elem)
145 { return elem->isBubble(); }
146
147 static PtrType bubble() { return ElemType::bubble(); }
148 };
149
150 /** TimeBuffer with MinorTrace and Named interfaces */
151 template <typename ElemType,
152 typename ReportTraits = ReportTraitsAdaptor<ElemType>,
153 typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
154 class MinorBuffer : public Named, public TimeBuffer<ElemType>
155 {
156 protected:
157 /** The range of elements that should appear in trace lines */
158 int reportLeft, reportRight;
159
160 /** Name to use for the data in a MinorTrace line */
161 std::string dataName;
162
163 public:
164 MinorBuffer(const std::string &name,
165 const std::string &data_name,
166 int num_past, int num_future,
167 int report_left = -1, int report_right = -1) :
168 Named(name), TimeBuffer<ElemType>(num_past, num_future),
169 reportLeft(report_left), reportRight(report_right),
170 dataName(data_name)
171 { }
172
173 public:
174 /* Is this buffer full of only bubbles */
175 bool
176 empty() const
177 {
178 bool ret = true;
179
180 for (int i = -this->past; i <= this->future; i++) {
181 if (!BubbleTraits::isBubble((*this)[i]))
182 ret = false;
183 }
184
185 return ret;
186 }
187
188 /** Report buffer states from 'slot' 'from' to 'to'. For example 0,-1
189 * will produce two slices with current (just assigned) and last (one
190 * advance() old) slices with the current (0) one on the left.
191 * Reverse the numbers to change the order of slices */
192 void
193 minorTrace() const
194 {
195 std::ostringstream data;
196
197 int step = (reportLeft > reportRight ? -1 : 1);
198 int end = reportRight + step;
199 int i = reportLeft;
200
201 while (i != end) {
202 const ElemType &datum = (*this)[i];
203
204 ReportTraits::reportData(data, datum);
205 i += step;
206 if (i != end)
207 data << ',';
208 }
209
210 MINORTRACE("%s=%s\n", dataName, data.str());
211 }
212 };
213
214 /** Wraps a MinorBuffer with Input/Output interfaces to ensure that units
215 * within the model can only see the right end of buffers between them. */
216 template <typename Data>
217 class Latch
218 {
219 public:
220 typedef MinorBuffer<Data> Buffer;
221
222 protected:
223 /** Delays, in cycles, writing data into the latch and seeing it on the
224 * latched wires */
225 Cycles delay;
226
227 Buffer buffer;
228
229 public:
230 /** forward/backwardDelay specify the delay from input to output in each
231 * direction. These arguments *must* be >= 1 */
232 Latch(const std::string &name,
233 const std::string &data_name,
234 Cycles delay_ = Cycles(1),
235 bool report_backwards = false) :
236 delay(delay_),
237 buffer(name, data_name, delay_, 0, (report_backwards ? -delay_ : 0),
238 (report_backwards ? 0 : -delay_))
239 { }
240
241 public:
242 /** Encapsulate wires on either input or output of the latch.
243 * forward/backward correspond to data direction relative to the
244 * pipeline. Latched and Immediate specify delay for backward data.
245 * Immediate data is available to earlier stages *during* the cycle it
246 * is written */
247 class Input
248 {
249 public:
250 typename Buffer::wire inputWire;
251
252 public:
253 Input(typename Buffer::wire input_wire) :
254 inputWire(input_wire)
255 { }
256 };
257
258 class Output
259 {
260 public:
261 typename Buffer::wire outputWire;
262
263 public:
264 Output(typename Buffer::wire output_wire) :
265 outputWire(output_wire)
266 { }
267 };
268
269 bool empty() const { return buffer.empty(); }
270
271 /** An interface to just the input of the buffer */
272 Input input() { return Input(buffer.getWire(0)); }
273
274 /** An interface to just the output of the buffer */
275 Output output() { return Output(buffer.getWire(-delay)); }
276
277 void minorTrace() const { buffer.minorTrace(); }
278
279 void evaluate() { buffer.advance(); }
280 };
281
282 /** A pipeline simulating class that will stall (not advance when advance()
283 * is called) if a non-bubble value lies at the far end of the pipeline.
284 * The user can clear the stall before calling advance to unstall the
285 * pipeline. */
286 template <typename ElemType,
287 typename ReportTraits,
288 typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
289 class SelfStallingPipeline : public MinorBuffer<ElemType, ReportTraits>
290 {
291 protected:
292 /** Wire at the input end of the pipeline (for convenience) */
293 typename TimeBuffer<ElemType>::wire pushWire;
294 /** Wire at the output end of the pipeline (for convenience) */
295 typename TimeBuffer<ElemType>::wire popWire;
296
297 public:
298 /** If true, advance will not advance the pipeline */
299 bool stalled;
300
301 /** The number of slots with non-bubbles in them */
302 unsigned int occupancy;
303
304 public:
305 SelfStallingPipeline(const std::string &name,
306 const std::string &data_name,
307 unsigned depth) :
308 MinorBuffer<ElemType, ReportTraits>
309 (name, data_name, depth, 0, -1, -depth),
310 pushWire(this->getWire(0)),
311 popWire(this->getWire(-depth)),
312 stalled(false),
313 occupancy(0)
314 {
315 assert(depth > 0);
316
317 /* Write explicit bubbles to get around the case where the default
318 * constructor for the element type isn't good enough */
319 for (unsigned i = 0; i <= depth; i++)
320 (*this)[-i] = BubbleTraits::bubble();
321 }
322
323 public:
324 /** Write an element to the back of the pipeline. This doesn't cause
325 * the pipeline to advance until advance is called. Pushing twice
326 * without advance-ing will just cause an overwrite of the last push's
327 * data. */
328 void push(ElemType &elem)
329 {
330 assert(!alreadyPushed());
331 *pushWire = elem;
332 if (!BubbleTraits::isBubble(elem))
333 occupancy++;
334 }
335
336 /** Peek at the end element of the pipe */
337 ElemType &front() { return *popWire; }
338
339 const ElemType &front() const { return *popWire; }
340
341 /** Have we already pushed onto this pipe without advancing */
342 bool alreadyPushed() { return !BubbleTraits::isBubble(*pushWire); }
343
344 /** There's data (not a bubble) at the end of the pipe */
345 bool isPopable() { return !BubbleTraits::isBubble(front()); }
346
347 /** Try to advance the pipeline. If we're stalled, don't advance. If
348 * we're not stalled, advance then check to see if we become stalled
349 * (a non-bubble at the end of the pipe) */
350 void
351 advance()
352 {
353 bool data_at_end = isPopable();
354
355 if (!stalled) {
356 TimeBuffer<ElemType>::advance();
357 /* If there was data at the end of the pipe that has now been
358 * advanced out of the pipe, we've lost data */
359 if (data_at_end)
360 occupancy--;
361 /* Is there data at the end of the pipe now? */
362 stalled = isPopable();
363 /* Insert a bubble into the empty input slot to make sure that
364 * element is correct in the case where the default constructor
365 * for ElemType doesn't produce a bubble */
366 ElemType bubble = BubbleTraits::bubble();
367 *pushWire = bubble;
368 }
369 }
370 };
371
372 /** Base class for space reservation requestable objects */
373 class Reservable
374 {
375 public:
376 /** Can a slot be reserved? */
377 virtual bool canReserve() const = 0;
378
379 /** Reserve a slot in whatever structure this is attached to */
380 virtual void reserve() = 0;
381
382 /** Free a reserved slot */
383 virtual void freeReservation() = 0;
384
385 virtual ~Reservable() {};
386 };
387
388 /** Wrapper for a queue type to act as a pipeline stage input queue.
389 * Handles capacity management, bubble value suppression and provides
390 * reporting.
391 *
392 * In an ideal world, ElemType would be derived from ReportIF and BubbleIF,
393 * but here we use traits and allow the Adaptors ReportTraitsAdaptor and
394 * BubbleTraitsAdaptor to work on data which *does* directly implement
395 * those interfaces. */
396 template <typename ElemType,
397 typename ReportTraits = ReportTraitsAdaptor<ElemType>,
398 typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
399 class Queue : public Named, public Reservable
400 {
401 private:
402 std::deque<ElemType> queue;
403
404 /** Number of slots currently reserved for future (reservation
405 * respecting) pushes */
406 unsigned int numReservedSlots;
407
408 /** Need this here as queues usually don't have a limited capacity */
409 unsigned int capacity;
410
411 /** Name to use for the data in MinorTrace */
412 std::string dataName;
413
414 public:
415 Queue(const std::string &name, const std::string &data_name,
416 unsigned int capacity_) :
417 Named(name),
418 numReservedSlots(0),
419 capacity(capacity_),
420 dataName(data_name)
421 { }
422
423 public:
424 /** Push an element into the buffer if it isn't a bubble. Bubbles are
425 * just discarded. It is assummed that any push into a queue with
426 * reserved space intends to take that space */
427 void
428 push(ElemType &data)
429 {
430 if (!BubbleTraits::isBubble(data)) {
431 freeReservation();
432 queue.push_back(data);
433
434 if (queue.size() > capacity) {
435 warn("%s: No space to push data into queue of capacity"
436 " %u, pushing anyway\n", name(), capacity);
437 }
438
439 }
440 }
441
442 /** Clear all allocated space. Be careful how this is used */
443 void clearReservedSpace() { numReservedSlots = 0; }
444
445 /** Clear a single reserved slot */
446 void freeReservation()
447 {
448 if (numReservedSlots != 0)
449 numReservedSlots--;
450 }
451
452 /** Reserve space in the queue for future pushes. Enquiries about space
453 * in the queue using unreservedRemainingSpace will only tell about
454 * space which is not full and not reserved. */
455 void
456 reserve()
457 {
458 /* Check reservable space */
459 if (unreservedRemainingSpace() == 0)
460 warn("%s: No space is reservable in queue", name());
461
462 numReservedSlots++;
463 }
464
465 bool canReserve() const { return unreservedRemainingSpace() != 0; }
466
467 /** Number of slots available in an empty buffer */
468 unsigned int totalSpace() const { return capacity; }
469
470 /** Number of slots already occupied in this buffer */
471 unsigned int occupiedSpace() const { return queue.size(); }
472
473 /** Number of slots which are reserved. */
474 unsigned int reservedSpace() const { return numReservedSlots; }
475
476 /** Number of slots yet to fill in this buffer. This doesn't include
477 * reservation. */
478 unsigned int
479 remainingSpace() const
480 {
481 int ret = capacity - queue.size();
482
483 return (ret < 0 ? 0 : ret);
484 }
485
486 /** Like remainingSpace but does not count reserved spaces */
487 unsigned int
488 unreservedRemainingSpace() const
489 {
490 int ret = capacity - (queue.size() + numReservedSlots);
491
492 return (ret < 0 ? 0 : ret);
493 }
494
495 /** Head value. Like std::queue::front */
496 ElemType &front() { return queue.front(); }
497
498 const ElemType &front() const { return queue.front(); }
499
500 /** Pop the head item. Like std::queue::pop */
501 void pop() { queue.pop_front(); }
502
503 /** Is the queue empty? */
504 bool empty() const { return queue.empty(); }
505
506 void
507 minorTrace() const
508 {
509 std::ostringstream data;
510 /* If we become over-full, totalSpace() can actually be smaller than
511 * occupiedSpace(). Handle this */
512 unsigned int num_total = (occupiedSpace() > totalSpace() ?
513 occupiedSpace() : totalSpace());
514
515 unsigned int num_reserved = reservedSpace();
516 unsigned int num_occupied = occupiedSpace();
517
518 int num_printed = 1;
519 /* Bodge to rotate queue to report elements */
520 while (num_printed <= num_occupied) {
521 ReportTraits::reportData(data, queue[num_printed - 1]);
522 num_printed++;
523
524 if (num_printed <= num_total)
525 data << ',';
526 }
527
528 int num_printed_reserved = 1;
529 /* Show reserved slots */
530 while (num_printed_reserved <= num_reserved &&
531 num_printed <= num_total)
532 {
533 data << 'R';
534 num_printed_reserved++;
535 num_printed++;
536
537 if (num_printed <= num_total)
538 data << ',';
539 }
540
541 /* And finally pad with empty slots (if there are any) */
542 while (num_printed <= num_total) {
543 num_printed++;
544
545 if (num_printed <= num_total)
546 data << ',';
547 }
548
549 MINORTRACE("%s=%s\n", dataName, data.str());
550 }
551 };
552
553 /** Like a Queue but with a restricted interface and a setTail function
554 * which, when the queue is empty, just takes a reference to the pushed
555 * item as the single element. Calling pushTail will push that element
556 * onto the queue.
557 *
558 * The purpose of this class is to allow the faster operation of queues of
559 * items which usually don't get deeper than one item and for which the copy
560 * associated with a push is expensive enough to want to avoid
561 *
562 * The intended use case is the input buffer for pipeline stages, hence the
563 * class name */
564 template <typename ElemType,
565 typename ReportTraits = ReportTraitsAdaptor<ElemType>,
566 typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
567 class InputBuffer : public Reservable
568 {
569 protected:
570 /** Underlying queue */
571 mutable Queue<ElemType, ReportTraits, BubbleTraits> queue;
572
573 /** Pointer to the single element (if not NULL) */
574 mutable ElemType *elementPtr;
575
576 public:
577 InputBuffer(const std::string &name, const std::string &data_name,
578 unsigned int capacity_) :
579 queue(name, data_name, capacity_),
580 elementPtr(NULL)
581 { }
582
583 public:
584 /** Set the tail of the queue, this is like push but needs
585 * to be followed by pushTail for the new tail to make its
586 * way into the queue proper */
587 void
588 setTail(ElemType &new_element)
589 {
590 assert(!elementPtr);
591 if (!BubbleTraits::isBubble(new_element)) {
592 if (queue.empty())
593 elementPtr = &new_element;
594 else
595 queue.push(new_element);
596 }
597 }
598
599 /** No single element or queue entries */
600 bool empty() const { return !elementPtr && queue.empty(); }
601
602 /** Return the element, or the front of the queue */
603 const ElemType &front() const
604 { return (elementPtr ? *elementPtr : queue.front()); }
605
606 ElemType &front()
607 { return (elementPtr ? *elementPtr : queue.front()); }
608
609 /** Pop either the head, or if none, the head of the queue */
610 void
611 pop()
612 {
613 if (elementPtr) {
614 /* A popped element was expected to be pushed into queue
615 * and so take a reserved space */
616 elementPtr = NULL;
617 queue.freeReservation();
618 } else {
619 queue.pop();
620 }
621 }
622
623 /** Push the single element (if any) into the queue proper. If the
624 * element's reference points to a transient object, remember to
625 * always do this before the end of that object's life */
626 void
627 pushTail() const
628 {
629 if (elementPtr)
630 queue.push(*elementPtr);
631 elementPtr = NULL;
632 }
633
634 /** Report elements */
635 void
636 minorTrace() const
637 {
638 pushTail();
639 queue.minorTrace();
640 }
641
642 /** Reservable interface, passed on to queue */
643 bool canReserve() const { return queue.canReserve(); }
644 void reserve() { queue.reserve(); }
645 void freeReservation() { queue.freeReservation(); }
646
647 /** Like remainingSpace but does not count reserved spaces */
648 unsigned int
649 unreservedRemainingSpace()
650 {
651 pushTail();
652 return queue.unreservedRemainingSpace();
653 }
654 };
655
656 }
657
658 #endif /* __CPU_MINOR_BUFFERS_HH__ */