peq_with_cb_and_phase.h revision 13586:008fe87c1ad4
1/*****************************************************************************
2
3  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4  more contributor license agreements.  See the NOTICE file distributed
5  with this work for additional information regarding copyright ownership.
6  Accellera licenses this file to you under the Apache License, Version 2.0
7  (the "License"); you may not use this file except in compliance with the
8  License.  You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15  implied.  See the License for the specific language governing
16  permissions and limitations under the License.
17
18 *****************************************************************************/
19
20#ifndef __SYSTEMC_EXT_TLM_UTILS_PEQ_WITH_CB_AND_PHASE_H__
21#define __SYSTEMC_EXT_TLM_UTILS_PEQ_WITH_CB_AND_PHASE_H__
22
23#include <vector>
24
25#include "../core/sc_main.hh"
26#include "../core/sc_object.hh"
27#include "../core/sc_spawn.hh"
28#include "../core/sc_time.hh"
29#include "../dt/int/sc_nbdefs.hh"
30
31namespace tlm_utils
32{
33
34template <typename PAYLOAD>
35class time_ordered_list
36{
37  public:
38    struct element
39    {
40        struct element *next;
41        PAYLOAD p;
42        sc_core::sc_time t;
43        sc_dt::uint64 d;
44        element(PAYLOAD &p, sc_core::sc_time t, sc_dt::uint64 d) :
45            p(p), t(t), d(d)
46        {}
47        element() {}
48    };
49
50    element *nill;
51    element *empties;
52    element *list;
53    unsigned int size;
54
55    time_ordered_list() : nill(new element()), empties(NULL),
56                          list(nill), size(0)
57    {}
58
59    ~time_ordered_list()
60    {
61        reset();
62        while (empties) {
63            struct element *e = empties->next;
64            delete empties;
65            empties = e;
66        }
67        delete nill;
68    }
69
70    void
71    reset()
72    {
73        while (size) {
74            delete_top();
75        }
76    }
77
78    void
79    insert(const PAYLOAD &p, sc_core::sc_time t)
80    {
81        if (!empties) {
82            empties = new struct element();
83            empties->next=NULL;
84        }
85
86        struct element *e = empties;
87        empties = empties->next;
88        e->p = p;
89        e->t = t;
90        e->d = sc_core::sc_delta_count();
91
92        struct element *ancestor = nill;
93        struct element *iterator = list;
94        while (iterator != nill && iterator->t <= t) {
95            ancestor = iterator;
96            iterator = iterator->next;
97        }
98        if (ancestor == nill) {
99            e->next = list;
100            list = e;
101        } else {
102            e->next = iterator;
103            ancestor->next = e;
104        }
105        size++;
106    }
107
108    void
109    delete_top()
110    {
111        if (list != nill) {
112            struct element *e = list;
113            list = list->next;
114            e->next = empties;
115            empties = e;
116            size--;
117        }
118    }
119
120    unsigned int get_size() { return size; }
121    PAYLOAD &top() { return list->p; }
122    sc_core::sc_time top_time() { return list->t; }
123    sc_dt::uint64 &top_delta() { return list->d; }
124    sc_core::sc_time next_time() { return list->next->t; }
125};
126
127//---------------------------------------------------------------------------
128/**
129 * An event queue that can contain any number of pending
130 * notifications. Each notification have an associate payload.
131 */
132//---------------------------------------------------------------------------
133template<typename OWNER, typename TYPES=tlm::tlm_base_protocol_types>
134class peq_with_cb_and_phase : public sc_core::sc_object
135{
136    typedef typename TYPES::tlm_payload_type tlm_payload_type;
137    typedef typename TYPES::tlm_phase_type tlm_phase_type;
138    typedef std::pair<tlm_payload_type *, tlm_phase_type> PAYLOAD;
139    typedef void (OWNER::*cb)(tlm_payload_type &, const tlm_phase_type &);
140
141    class delta_list
142    {
143      public:
144        delta_list()
145        {
146            reset();
147            entries.resize(100);
148        }
149
150        inline void
151        insert(const PAYLOAD &p)
152        {
153            if (size==entries.size()) {
154                entries.resize(entries.size() * 2);
155            }
156            entries[size++] = p;
157        }
158
159        inline PAYLOAD &get() { return entries[out++]; }
160        inline bool next() { return out < size; }
161        inline void
162        reset()
163        {
164            size=0;
165            out=0;
166        }
167
168      public:
169        unsigned int size;
170
171      private:
172        std::vector<PAYLOAD> entries;
173        unsigned int out;
174    };
175
176  public:
177    peq_with_cb_and_phase(OWNER *_owner, cb _cb) :
178        sc_core::sc_object(sc_core::sc_gen_unique_name(
179                    "peq_with_cb_and_phase")),
180        m_owner(_owner), m_cb(_cb)
181    {
182        sc_core::sc_spawn_options opts;
183        opts.spawn_method();
184        opts.set_sensitivity(&m_e);
185        opts.dont_initialize();
186        sc_core::sc_spawn(sc_bind(&peq_with_cb_and_phase::fec, this),
187                          sc_core::sc_gen_unique_name("fec"), &opts);
188    }
189
190    peq_with_cb_and_phase(const char *_name, OWNER *_owner, cb _cb) :
191        sc_core::sc_object(_name), m_owner(_owner), m_cb(_cb)
192    {
193        sc_core::sc_spawn_options opts;
194        opts.spawn_method();
195        opts.set_sensitivity(&m_e);
196        opts.dont_initialize();
197        sc_core::sc_spawn(sc_bind(&peq_with_cb_and_phase::fec, this),
198                          sc_core::sc_gen_unique_name("fec"), &opts);
199    }
200
201    ~peq_with_cb_and_phase() {}
202
203    void
204    notify(tlm_payload_type &t, const tlm_phase_type &p,
205            const sc_core::sc_time &when)
206    {
207        if (when == sc_core::SC_ZERO_TIME) {
208            if (sc_core::sc_delta_count() & (sc_dt::uint64)0x1) {
209                // Uneven delta cycle so delta delay is for even cycle.
210                m_even_delta.insert(PAYLOAD(&t,p));
211            } else {
212                // Even delta cycle so delta delay is for uneven delta.
213                m_uneven_delta.insert(PAYLOAD(&t, p));
214            }
215            m_e.notify(sc_core::SC_ZERO_TIME);
216        } else {
217            m_ppq.insert(PAYLOAD(&t, p), when + sc_core::sc_time_stamp());
218            // Note, this will only overwrite the "newest" event.
219            m_e.notify(when);
220        }
221    }
222
223    void
224    notify(tlm_payload_type &t, const tlm_phase_type &p)
225    {
226        m_immediate_yield.insert(PAYLOAD(&t, p));
227        m_e.notify(); // Immediate notification.
228    }
229
230    // Cancel all events from the event queue.
231    void
232    cancel_all()
233    {
234        m_ppq.reset();
235        m_uneven_delta.reset();
236        m_even_delta.reset();
237        m_immediate_yield.reset();
238        m_e.cancel();
239    }
240
241  private:
242    void
243    fec()
244    {
245        // Immediate yield notifications.
246        while (m_immediate_yield.next()) {
247            PAYLOAD &tmp = m_immediate_yield.get();
248            (m_owner->*m_cb)(*tmp.first, tmp.second);
249        }
250        m_immediate_yield.reset();
251
252        // Delta notifications.
253        if (sc_core::sc_delta_count() & (sc_dt::uint64)0x1) {
254            // Uneven delta so put out all payloads for uneven delta.
255            while (m_uneven_delta.next()) {
256                PAYLOAD &tmp = m_uneven_delta.get();
257                (m_owner->*m_cb)(*tmp.first, tmp.second);
258            }
259            m_uneven_delta.reset();
260            if (m_even_delta.size)
261                m_e.notify(sc_core::SC_ZERO_TIME);
262        } else {
263            while (m_even_delta.next()) {
264                PAYLOAD &tmp = m_even_delta.get();
265                (m_owner->*m_cb)(*tmp.first, tmp.second);
266            }
267            m_even_delta.reset();
268            if (m_uneven_delta.size)
269                m_e.notify(sc_core::SC_ZERO_TIME);
270        }
271        if (!m_ppq.get_size())
272            return; // There were only delta notification.
273
274        // Timed notifications.
275        const sc_core::sc_time now = sc_core::sc_time_stamp();
276        sc_core::sc_time top = m_ppq.top_time();
277
278        while (m_ppq.get_size() && top == now) {
279            // Push all active ones into target.
280            PAYLOAD &tmp = m_ppq.top();
281            (m_owner->*m_cb)(*tmp.first, tmp.second);
282            m_ppq.delete_top();
283            top = m_ppq.top_time();
284        }
285        if (m_ppq.get_size()) {
286            m_e.notify(top - now);
287        }
288    }
289
290    OWNER *m_owner;
291    cb m_cb;
292
293    time_ordered_list<PAYLOAD> m_ppq;
294    delta_list m_uneven_delta;
295    delta_list m_even_delta;
296    delta_list m_immediate_yield;
297
298    sc_core::sc_event m_e; // Default event.
299};
300
301} // namespace tlm_utils
302
303#endif /* __SYSTEMC_EXT_TLM_UTILS_PEQ_WITH_CB_AND_PHASE_H__ */
304