buffers.hh revision 13449:2f7efa89c58b
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
58namespace 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. */
65class 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. */
79class 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 */
91template <typename ElemType> /* ElemType should implement ReportIF */
92class 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 */
102template <typename PtrType>
103class 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 */
116template <typename ElemType>
117class 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 */
129template <typename ElemType>
130class 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 */
140template <typename PtrType, typename ElemType>
141class 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 */
151template <typename ElemType,
152    typename ReportTraits = ReportTraitsAdaptor<ElemType>,
153    typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
154class 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. */
216template <typename Data>
217class 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. */
286template <typename ElemType,
287    typename ReportTraits,
288    typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
289class 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 */
373class 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
386/** Wrapper for a queue type to act as a pipeline stage input queue.
387 *  Handles capacity management, bubble value suppression and provides
388 *  reporting.
389 *
390 *  In an ideal world, ElemType would be derived from ReportIF and BubbleIF,
391 *  but here we use traits and allow the Adaptors ReportTraitsAdaptor and
392 *  BubbleTraitsAdaptor to work on data which *does* directly implement
393 *  those interfaces. */
394template <typename ElemType,
395    typename ReportTraits = ReportTraitsAdaptor<ElemType>,
396    typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
397class Queue : public Named, public Reservable
398{
399  private:
400      std::deque<ElemType> queue;
401
402    /** Number of slots currently reserved for future (reservation
403     *  respecting) pushes */
404    unsigned int numReservedSlots;
405
406    /** Need this here as queues usually don't have a limited capacity */
407    unsigned int capacity;
408
409    /** Name to use for the data in MinorTrace */
410    std::string dataName;
411
412  public:
413    Queue(const std::string &name, const std::string &data_name,
414        unsigned int capacity_) :
415        Named(name),
416        numReservedSlots(0),
417        capacity(capacity_),
418        dataName(data_name)
419    { }
420
421    virtual ~Queue() { }
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 */
564template <typename ElemType,
565    typename ReportTraits = ReportTraitsAdaptor<ElemType>,
566    typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
567class 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__ */
659