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