| /* |
| * Copyright (c) 2013-2014 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * |
| * Classes for buffer, queue and FIFO behaviour. |
| */ |
| |
| #ifndef __CPU_MINOR_BUFFERS_HH__ |
| #define __CPU_MINOR_BUFFERS_HH__ |
| |
| #include <iostream> |
| #include <queue> |
| #include <sstream> |
| #include <string> |
| |
| #include "base/logging.hh" |
| #include "base/named.hh" |
| #include "base/types.hh" |
| #include "cpu/activity.hh" |
| #include "cpu/minor/trace.hh" |
| #include "cpu/timebuf.hh" |
| |
| namespace gem5 |
| { |
| |
| namespace minor |
| { |
| |
| /** Interface class for data with reporting/tracing facilities. This |
| * interface doesn't actually have to be used as other classes which need |
| * this interface uses templating rather than inheritance but it's provided |
| * here to document the interface needed by those classes. */ |
| class ReportIF |
| { |
| public: |
| /** Print the data in a format suitable to be the value in "name=value" |
| * trace lines */ |
| virtual void reportData(std::ostream &os) const = 0; |
| |
| virtual ~ReportIF() { } |
| }; |
| |
| /** Interface class for data with 'bubble' values. This interface doesn't |
| * actually have to be used as other classes which need this interface uses |
| * templating rather than inheritance but it's provided here to document |
| * the interface needed by those classes. */ |
| class BubbleIF |
| { |
| public: |
| virtual bool isBubble() const = 0; |
| }; |
| |
| /** ...ReportTraits are trait classes with the same functionality as |
| * ReportIF, but with elements explicitly passed into the report... |
| * functions. */ |
| |
| /** Allow a template using ReportTraits to call report... functions of |
| * ReportIF-bearing elements themselves */ |
| template <typename ElemType> /* ElemType should implement ReportIF */ |
| class ReportTraitsAdaptor |
| { |
| public: |
| static void |
| reportData(std::ostream &os, const ElemType &elem) |
| { elem.reportData(os); } |
| }; |
| |
| /** A similar adaptor but for elements held by pointer |
| * ElemType should implement ReportIF */ |
| template <typename PtrType> |
| class ReportTraitsPtrAdaptor |
| { |
| public: |
| static void |
| reportData(std::ostream &os, const PtrType &elem) |
| { elem->reportData(os); } |
| }; |
| |
| /** ... BubbleTraits are trait classes to add BubbleIF interface |
| * functionality to templates which process elements which don't necessarily |
| * implement BubbleIF themselves */ |
| |
| /** Default behaviour, no bubbles */ |
| template <typename ElemType> |
| class NoBubbleTraits |
| { |
| public: |
| static bool isBubble(const ElemType &) { return false; } |
| static ElemType |
| bubble() |
| { |
| panic("bubble called but no bubble interface"); |
| } |
| }; |
| |
| /** Pass on call to the element */ |
| template <typename ElemType> |
| class BubbleTraitsAdaptor |
| { |
| public: |
| static bool isBubble(const ElemType &elem) |
| { return elem.isBubble(); } |
| |
| static ElemType bubble() { return ElemType::bubble(); } |
| }; |
| |
| /** Pass on call to the element where the element is a pointer */ |
| template <typename PtrType, typename ElemType> |
| class BubbleTraitsPtrAdaptor |
| { |
| public: |
| static bool isBubble(const PtrType &elem) |
| { return elem->isBubble(); } |
| |
| static PtrType bubble() { return ElemType::bubble(); } |
| }; |
| |
| /** TimeBuffer with MinorTrace and Named interfaces */ |
| template <typename ElemType, |
| typename ReportTraits = ReportTraitsAdaptor<ElemType>, |
| typename BubbleTraits = BubbleTraitsAdaptor<ElemType> > |
| class MinorBuffer : public Named, public TimeBuffer<ElemType> |
| { |
| protected: |
| /** The range of elements that should appear in trace lines */ |
| int reportLeft, reportRight; |
| |
| /** Name to use for the data in a MinorTrace line */ |
| std::string dataName; |
| |
| public: |
| MinorBuffer(const std::string &name, |
| const std::string &data_name, |
| int num_past, int num_future, |
| int report_left = -1, int report_right = -1) : |
| Named(name), TimeBuffer<ElemType>(num_past, num_future), |
| reportLeft(report_left), reportRight(report_right), |
| dataName(data_name) |
| { } |
| |
| public: |
| /* Is this buffer full of only bubbles */ |
| bool |
| empty() const |
| { |
| bool ret = true; |
| |
| for (int i = -this->past; i <= this->future; i++) { |
| if (!BubbleTraits::isBubble((*this)[i])) |
| ret = false; |
| } |
| |
| return ret; |
| } |
| |
| /** Report buffer states from 'slot' 'from' to 'to'. For example 0,-1 |
| * will produce two slices with current (just assigned) and last (one |
| * advance() old) slices with the current (0) one on the left. |
| * Reverse the numbers to change the order of slices */ |
| void |
| minorTrace() const |
| { |
| std::ostringstream data; |
| |
| int step = (reportLeft > reportRight ? -1 : 1); |
| int end = reportRight + step; |
| int i = reportLeft; |
| |
| while (i != end) { |
| const ElemType &datum = (*this)[i]; |
| |
| ReportTraits::reportData(data, datum); |
| i += step; |
| if (i != end) |
| data << ','; |
| } |
| |
| minor::minorTrace("%s=%s\n", dataName, data.str()); |
| } |
| }; |
| |
| /** Wraps a MinorBuffer with Input/Output interfaces to ensure that units |
| * within the model can only see the right end of buffers between them. */ |
| template <typename Data> |
| class Latch |
| { |
| public: |
| typedef MinorBuffer<Data> Buffer; |
| |
| protected: |
| /** Delays, in cycles, writing data into the latch and seeing it on the |
| * latched wires */ |
| Cycles delay; |
| |
| Buffer buffer; |
| |
| public: |
| /** forward/backwardDelay specify the delay from input to output in each |
| * direction. These arguments *must* be >= 1 */ |
| Latch(const std::string &name, |
| const std::string &data_name, |
| Cycles delay_ = Cycles(1), |
| bool report_backwards = false) : |
| delay(delay_), |
| buffer(name, data_name, delay_, 0, (report_backwards ? -delay_ : 0), |
| (report_backwards ? 0 : -delay_)) |
| { } |
| |
| public: |
| /** Encapsulate wires on either input or output of the latch. |
| * forward/backward correspond to data direction relative to the |
| * pipeline. Latched and Immediate specify delay for backward data. |
| * Immediate data is available to earlier stages *during* the cycle it |
| * is written */ |
| class Input |
| { |
| public: |
| typename Buffer::wire inputWire; |
| |
| public: |
| Input(typename Buffer::wire input_wire) : |
| inputWire(input_wire) |
| { } |
| }; |
| |
| class Output |
| { |
| public: |
| typename Buffer::wire outputWire; |
| |
| public: |
| Output(typename Buffer::wire output_wire) : |
| outputWire(output_wire) |
| { } |
| }; |
| |
| bool empty() const { return buffer.empty(); } |
| |
| /** An interface to just the input of the buffer */ |
| Input input() { return Input(buffer.getWire(0)); } |
| |
| /** An interface to just the output of the buffer */ |
| Output output() { return Output(buffer.getWire(-delay)); } |
| |
| void minorTrace() const { buffer.minorTrace(); } |
| |
| void evaluate() { buffer.advance(); } |
| }; |
| |
| /** A pipeline simulating class that will stall (not advance when advance() |
| * is called) if a non-bubble value lies at the far end of the pipeline. |
| * The user can clear the stall before calling advance to unstall the |
| * pipeline. */ |
| template <typename ElemType, |
| typename ReportTraits, |
| typename BubbleTraits = BubbleTraitsAdaptor<ElemType> > |
| class SelfStallingPipeline : public MinorBuffer<ElemType, ReportTraits> |
| { |
| protected: |
| /** Wire at the input end of the pipeline (for convenience) */ |
| typename TimeBuffer<ElemType>::wire pushWire; |
| /** Wire at the output end of the pipeline (for convenience) */ |
| typename TimeBuffer<ElemType>::wire popWire; |
| |
| public: |
| /** If true, advance will not advance the pipeline */ |
| bool stalled; |
| |
| /** The number of slots with non-bubbles in them */ |
| unsigned int occupancy; |
| |
| public: |
| SelfStallingPipeline(const std::string &name, |
| const std::string &data_name, |
| unsigned depth) : |
| MinorBuffer<ElemType, ReportTraits> |
| (name, data_name, depth, 0, -1, -depth), |
| pushWire(this->getWire(0)), |
| popWire(this->getWire(-depth)), |
| stalled(false), |
| occupancy(0) |
| { |
| assert(depth > 0); |
| |
| /* Write explicit bubbles to get around the case where the default |
| * constructor for the element type isn't good enough */ |
| for (unsigned i = 0; i <= depth; i++) |
| (*this)[-i] = BubbleTraits::bubble(); |
| } |
| |
| public: |
| /** Write an element to the back of the pipeline. This doesn't cause |
| * the pipeline to advance until advance is called. Pushing twice |
| * without advance-ing will just cause an overwrite of the last push's |
| * data. */ |
| void push(ElemType &elem) |
| { |
| assert(!alreadyPushed()); |
| *pushWire = elem; |
| if (!BubbleTraits::isBubble(elem)) |
| occupancy++; |
| } |
| |
| /** Peek at the end element of the pipe */ |
| ElemType &front() { return *popWire; } |
| |
| const ElemType &front() const { return *popWire; } |
| |
| /** Have we already pushed onto this pipe without advancing */ |
| bool alreadyPushed() { return !BubbleTraits::isBubble(*pushWire); } |
| |
| /** There's data (not a bubble) at the end of the pipe */ |
| bool isPopable() { return !BubbleTraits::isBubble(front()); } |
| |
| /** Try to advance the pipeline. If we're stalled, don't advance. If |
| * we're not stalled, advance then check to see if we become stalled |
| * (a non-bubble at the end of the pipe) */ |
| void |
| advance() |
| { |
| bool data_at_end = isPopable(); |
| |
| if (!stalled) { |
| TimeBuffer<ElemType>::advance(); |
| /* If there was data at the end of the pipe that has now been |
| * advanced out of the pipe, we've lost data */ |
| if (data_at_end) |
| occupancy--; |
| /* Is there data at the end of the pipe now? */ |
| stalled = isPopable(); |
| /* Insert a bubble into the empty input slot to make sure that |
| * element is correct in the case where the default constructor |
| * for ElemType doesn't produce a bubble */ |
| ElemType bubble = BubbleTraits::bubble(); |
| *pushWire = bubble; |
| } |
| } |
| }; |
| |
| /** Base class for space reservation requestable objects */ |
| class Reservable |
| { |
| public: |
| /** Can a slot be reserved? */ |
| virtual bool canReserve() const = 0; |
| |
| /** Reserve a slot in whatever structure this is attached to */ |
| virtual void reserve() = 0; |
| |
| /** Free a reserved slot */ |
| virtual void freeReservation() = 0; |
| |
| virtual ~Reservable() {}; |
| }; |
| |
| /** Wrapper for a queue type to act as a pipeline stage input queue. |
| * Handles capacity management, bubble value suppression and provides |
| * reporting. |
| * |
| * In an ideal world, ElemType would be derived from ReportIF and BubbleIF, |
| * but here we use traits and allow the Adaptors ReportTraitsAdaptor and |
| * BubbleTraitsAdaptor to work on data which *does* directly implement |
| * those interfaces. */ |
| template <typename ElemType, |
| typename ReportTraits = ReportTraitsAdaptor<ElemType>, |
| typename BubbleTraits = BubbleTraitsAdaptor<ElemType> > |
| class Queue : public Named, public Reservable |
| { |
| private: |
| std::deque<ElemType> queue; |
| |
| /** Number of slots currently reserved for future (reservation |
| * respecting) pushes */ |
| unsigned int numReservedSlots; |
| |
| /** Need this here as queues usually don't have a limited capacity */ |
| unsigned int capacity; |
| |
| /** Name to use for the data in MinorTrace */ |
| std::string dataName; |
| |
| public: |
| Queue(const std::string &name, const std::string &data_name, |
| unsigned int capacity_) : |
| Named(name), |
| numReservedSlots(0), |
| capacity(capacity_), |
| dataName(data_name) |
| { } |
| |
| public: |
| /** Push an element into the buffer if it isn't a bubble. Bubbles are |
| * just discarded. It is assummed that any push into a queue with |
| * reserved space intends to take that space */ |
| void |
| push(ElemType &data) |
| { |
| if (!BubbleTraits::isBubble(data)) { |
| freeReservation(); |
| queue.push_back(data); |
| |
| if (queue.size() > capacity) { |
| warn("%s: No space to push data into queue of capacity" |
| " %u, pushing anyway\n", name(), capacity); |
| } |
| |
| } |
| } |
| |
| /** Clear all allocated space. Be careful how this is used */ |
| void clearReservedSpace() { numReservedSlots = 0; } |
| |
| /** Clear a single reserved slot */ |
| void freeReservation() |
| { |
| if (numReservedSlots != 0) |
| numReservedSlots--; |
| } |
| |
| /** Reserve space in the queue for future pushes. Enquiries about space |
| * in the queue using unreservedRemainingSpace will only tell about |
| * space which is not full and not reserved. */ |
| void |
| reserve() |
| { |
| /* Check reservable space */ |
| if (unreservedRemainingSpace() == 0) |
| warn("%s: No space is reservable in queue", name()); |
| |
| numReservedSlots++; |
| } |
| |
| bool canReserve() const { return unreservedRemainingSpace() != 0; } |
| |
| /** Number of slots available in an empty buffer */ |
| unsigned int totalSpace() const { return capacity; } |
| |
| /** Number of slots already occupied in this buffer */ |
| unsigned int occupiedSpace() const { return queue.size(); } |
| |
| /** Number of slots which are reserved. */ |
| unsigned int reservedSpace() const { return numReservedSlots; } |
| |
| /** Number of slots yet to fill in this buffer. This doesn't include |
| * reservation. */ |
| unsigned int |
| remainingSpace() const |
| { |
| int ret = capacity - queue.size(); |
| |
| return (ret < 0 ? 0 : ret); |
| } |
| |
| /** Like remainingSpace but does not count reserved spaces */ |
| unsigned int |
| unreservedRemainingSpace() const |
| { |
| int ret = capacity - (queue.size() + numReservedSlots); |
| |
| return (ret < 0 ? 0 : ret); |
| } |
| |
| /** Head value. Like std::queue::front */ |
| ElemType &front() { return queue.front(); } |
| |
| const ElemType &front() const { return queue.front(); } |
| |
| /** Pop the head item. Like std::queue::pop */ |
| void pop() { queue.pop_front(); } |
| |
| /** Is the queue empty? */ |
| bool empty() const { return queue.empty(); } |
| |
| void |
| minorTrace() const |
| { |
| std::ostringstream data; |
| /* If we become over-full, totalSpace() can actually be smaller than |
| * occupiedSpace(). Handle this */ |
| unsigned int num_total = (occupiedSpace() > totalSpace() ? |
| occupiedSpace() : totalSpace()); |
| |
| unsigned int num_reserved = reservedSpace(); |
| unsigned int num_occupied = occupiedSpace(); |
| |
| int num_printed = 1; |
| /* Bodge to rotate queue to report elements */ |
| while (num_printed <= num_occupied) { |
| ReportTraits::reportData(data, queue[num_printed - 1]); |
| num_printed++; |
| |
| if (num_printed <= num_total) |
| data << ','; |
| } |
| |
| int num_printed_reserved = 1; |
| /* Show reserved slots */ |
| while (num_printed_reserved <= num_reserved && |
| num_printed <= num_total) |
| { |
| data << 'R'; |
| num_printed_reserved++; |
| num_printed++; |
| |
| if (num_printed <= num_total) |
| data << ','; |
| } |
| |
| /* And finally pad with empty slots (if there are any) */ |
| while (num_printed <= num_total) { |
| num_printed++; |
| |
| if (num_printed <= num_total) |
| data << ','; |
| } |
| |
| minor::minorTrace("%s=%s\n", dataName, data.str()); |
| } |
| }; |
| |
| /** Like a Queue but with a restricted interface and a setTail function |
| * which, when the queue is empty, just takes a reference to the pushed |
| * item as the single element. Calling pushTail will push that element |
| * onto the queue. |
| * |
| * The purpose of this class is to allow the faster operation of queues of |
| * items which usually don't get deeper than one item and for which the copy |
| * associated with a push is expensive enough to want to avoid |
| * |
| * The intended use case is the input buffer for pipeline stages, hence the |
| * class name */ |
| template <typename ElemType, |
| typename ReportTraits = ReportTraitsAdaptor<ElemType>, |
| typename BubbleTraits = BubbleTraitsAdaptor<ElemType> > |
| class InputBuffer : public Reservable |
| { |
| protected: |
| /** Underlying queue */ |
| mutable Queue<ElemType, ReportTraits, BubbleTraits> queue; |
| |
| /** Pointer to the single element (if not NULL) */ |
| mutable ElemType *elementPtr; |
| |
| public: |
| InputBuffer(const std::string &name, const std::string &data_name, |
| unsigned int capacity_) : |
| queue(name, data_name, capacity_), |
| elementPtr(NULL) |
| { } |
| |
| public: |
| /** Set the tail of the queue, this is like push but needs |
| * to be followed by pushTail for the new tail to make its |
| * way into the queue proper */ |
| void |
| setTail(ElemType &new_element) |
| { |
| assert(!elementPtr); |
| if (!BubbleTraits::isBubble(new_element)) { |
| if (queue.empty()) |
| elementPtr = &new_element; |
| else |
| queue.push(new_element); |
| } |
| } |
| |
| /** No single element or queue entries */ |
| bool empty() const { return !elementPtr && queue.empty(); } |
| |
| /** Return the element, or the front of the queue */ |
| const ElemType &front() const |
| { return (elementPtr ? *elementPtr : queue.front()); } |
| |
| ElemType &front() |
| { return (elementPtr ? *elementPtr : queue.front()); } |
| |
| /** Pop either the head, or if none, the head of the queue */ |
| void |
| pop() |
| { |
| if (elementPtr) { |
| /* A popped element was expected to be pushed into queue |
| * and so take a reserved space */ |
| elementPtr = NULL; |
| queue.freeReservation(); |
| } else { |
| queue.pop(); |
| } |
| } |
| |
| /** Push the single element (if any) into the queue proper. If the |
| * element's reference points to a transient object, remember to |
| * always do this before the end of that object's life */ |
| void |
| pushTail() const |
| { |
| if (elementPtr) |
| queue.push(*elementPtr); |
| elementPtr = NULL; |
| } |
| |
| /** Report elements */ |
| void |
| minorTrace() const |
| { |
| pushTail(); |
| queue.minorTrace(); |
| } |
| |
| /** Reservable interface, passed on to queue */ |
| bool canReserve() const { return queue.canReserve(); } |
| void reserve() { queue.reserve(); } |
| void freeReservation() { queue.freeReservation(); } |
| |
| /** Like remainingSpace but does not count reserved spaces */ |
| unsigned int |
| unreservedRemainingSpace() |
| { |
| pushTail(); |
| return queue.unreservedRemainingSpace(); |
| } |
| }; |
| |
| } // namespace minor |
| } // namespace gem5 |
| |
| #endif /* __CPU_MINOR_BUFFERS_HH__ */ |