peq_with_cb_and_phase.h revision 13513:bbf275465d3d
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 <systemc>
24#include <tlm>
25#include <vector>
26
27namespace tlm_utils
28{
29
30template <typename PAYLOAD>
31class time_ordered_list
32{
33  public:
34    struct element
35    {
36        struct element *next;
37        PAYLOAD p;
38        sc_core::sc_time t;
39        sc_dt::uint64 d;
40        element(PAYLOAD &p, sc_core::sc_time t, sc_dt::uint64 d) :
41            p(p), t(t), d(d)
42        {}
43        element() {}
44    };
45
46    element *nill;
47    element *empties;
48    element *list;
49    unsigned int size;
50
51    time_ordered_list() : nill(new element()), empties(NULL),
52                          list(nill), size(0)
53    {}
54
55    ~time_ordered_list()
56    {
57        reset();
58        while (empties) {
59            struct element *e = empties->next;
60            delete empties;
61            empties = e;
62        }
63        delete nill;
64    }
65
66    void
67    reset()
68    {
69        while (size) {
70            delete_top();
71        }
72    }
73
74    void
75    insert(const PAYLOAD &p, sc_core::sc_time t)
76    {
77        if (!empties) {
78            empties = new struct element();
79            empties->next=NULL;
80        }
81
82        struct element *e = empties;
83        empties = empties->next;
84        e->p = p;
85        e->t = t;
86        e->d = sc_core::sc_delta_count();
87
88        struct element *ancestor = nill;
89        struct element *iterator = list;
90        while (iterator != nill && iterator->t <= t) {
91            ancestor = iterator;
92            iterator = iterator->next;
93        }
94        if (ancestor == nill) {
95            e->next = list;
96            list = e;
97        } else {
98            e->next = iterator;
99            ancestor->next = e;
100        }
101        size++;
102    }
103
104    void
105    delete_top()
106    {
107        if (list != nill) {
108            struct element *e = list;
109            list = list->next;
110            e->next = empties;
111            empties = e;
112            size--;
113        }
114    }
115
116    unsigned int get_size() { return size; }
117    PAYLOAD &top() { return list->p; }
118    sc_core::sc_time top_time() { return list->t; }
119    sc_dt::uint64 &top_delta() { return list->d; }
120    sc_core::sc_time next_time() { return list->next->t; }
121};
122
123//---------------------------------------------------------------------------
124/**
125 * An event queue that can contain any number of pending
126 * notifications. Each notification have an associate payload.
127 */
128//---------------------------------------------------------------------------
129template<typename OWNER, typename TYPES=tlm::tlm_base_protocol_types>
130class peq_with_cb_and_phase : public sc_core::sc_object
131{
132    typedef typename TYPES::tlm_payload_type tlm_payload_type;
133    typedef typename TYPES::tlm_phase_type tlm_phase_type;
134    typedef std::pair<tlm_payload_type *, tlm_phase_type> PAYLOAD;
135    typedef void (OWNER::*cb)(tlm_payload_type &, const tlm_phase_type &);
136
137    class delta_list
138    {
139      public:
140        delta_list()
141        {
142            reset();
143            entries.resize(100);
144        }
145
146        inline void
147        insert(const PAYLOAD &p)
148        {
149            if (size==entries.size()) {
150                entries.resize(entries.size() * 2);
151            }
152            entries[size++] = p;
153        }
154
155        inline PAYLOAD &get() { return entries[out++]; }
156        inline bool next() { return out < size; }
157        inline void
158        reset()
159        {
160            size=0;
161            out=0;
162        }
163
164      public:
165        unsigned int size;
166
167      private:
168        std::vector<PAYLOAD> entries;
169        unsigned int out;
170    };
171
172  public:
173    peq_with_cb_and_phase(OWNER *_owner, cb _cb) :
174        sc_core::sc_object(sc_core::sc_gen_unique_name(
175                    "peq_with_cb_and_phase")),
176        m_owner(_owner), m_cb(_cb)
177    {
178        sc_core::sc_spawn_options opts;
179        opts.spawn_method();
180        opts.set_sensitivity(&m_e);
181        opts.dont_initialize();
182        sc_core::sc_spawn(sc_bind(&peq_with_cb_and_phase::fec, this),
183                          sc_core::sc_gen_unique_name("fec"), &opts);
184    }
185
186    peq_with_cb_and_phase(const char *_name, OWNER *_owner, cb _cb) :
187        sc_core::sc_object(_name), m_owner(_owner), m_cb(_cb)
188    {
189        sc_core::sc_spawn_options opts;
190        opts.spawn_method();
191        opts.set_sensitivity(&m_e);
192        opts.dont_initialize();
193        sc_core::sc_spawn(sc_bind(&peq_with_cb_and_phase::fec, this),
194                          sc_core::sc_gen_unique_name("fec"), &opts);
195    }
196
197    ~peq_with_cb_and_phase() {}
198
199    void
200    notify(tlm_payload_type &t, const tlm_phase_type &p,
201            const sc_core::sc_time &when)
202    {
203        if (when == sc_core::SC_ZERO_TIME) {
204            if (sc_core::sc_delta_count() & (sc_dt::uint64)0x1) {
205                // Uneven delta cycle so delta delay is for even cycle.
206                m_even_delta.insert(PAYLOAD(&t,p));
207            } else {
208                // Even delta cycle so delta delay is for uneven delta.
209                m_uneven_delta.insert(PAYLOAD(&t, p));
210            }
211            m_e.notify(sc_core::SC_ZERO_TIME);
212        } else {
213            m_ppq.insert(PAYLOAD(&t, p), when + sc_core::sc_time_stamp());
214            // Note, this will only overwrite the "newest" event.
215            m_e.notify(when);
216        }
217    }
218
219    void
220    notify(tlm_payload_type &t, const tlm_phase_type &p)
221    {
222        m_immediate_yield.insert(PAYLOAD(&t, p));
223        m_e.notify(); // Immediate notification.
224    }
225
226    // Cancel all events from the event queue.
227    void
228    cancel_all()
229    {
230        m_ppq.reset();
231        m_uneven_delta.reset();
232        m_even_delta.reset();
233        m_immediate_yield.reset();
234        m_e.cancel();
235    }
236
237  private:
238    void
239    fec()
240    {
241        // Immediate yield notifications.
242        while (m_immediate_yield.next()) {
243            PAYLOAD &tmp = m_immediate_yield.get();
244            (m_owner->*m_cb)(*tmp.first, tmp.second);
245        }
246        m_immediate_yield.reset();
247
248        // Delta notifications.
249        if (sc_core::sc_delta_count() & (sc_dt::uint64)0x1) {
250            // Uneven delta so put out all payloads for uneven delta.
251            while (m_uneven_delta.next()) {
252                PAYLOAD &tmp = m_uneven_delta.get();
253                (m_owner->*m_cb)(*tmp.first, tmp.second);
254            }
255            m_uneven_delta.reset();
256            if (m_even_delta.size)
257                m_e.notify(sc_core::SC_ZERO_TIME);
258        } else {
259            while (m_even_delta.next()) {
260                PAYLOAD &tmp = m_even_delta.get();
261                (m_owner->*m_cb)(*tmp.first, tmp.second);
262            }
263            m_even_delta.reset();
264            if (m_uneven_delta.size)
265                m_e.notify(sc_core::SC_ZERO_TIME);
266        }
267        if (!m_ppq.get_size())
268            return; // There were only delta notification.
269
270        // Timed notifications.
271        const sc_core::sc_time now = sc_core::sc_time_stamp();
272        sc_core::sc_time top = m_ppq.top_time();
273
274        while (m_ppq.get_size() && top == now) {
275            // Push all active ones into target.
276            PAYLOAD &tmp = m_ppq.top();
277            (m_owner->*m_cb)(*tmp.first, tmp.second);
278            m_ppq.delete_top();
279            top = m_ppq.top_time();
280        }
281        if (m_ppq.get_size()) {
282            m_e.notify(top - now);
283        }
284    }
285
286    OWNER *m_owner;
287    cb m_cb;
288
289    time_ordered_list<PAYLOAD> m_ppq;
290    delta_list m_uneven_delta;
291    delta_list m_even_delta;
292    delta_list m_immediate_yield;
293
294    sc_core::sc_event m_e; // Default event.
295};
296
297} // namespace tlm_utils
298
299#endif /* __SYSTEMC_EXT_TLM_UTILS_PEQ_WITH_CB_AND_PHASE_H__ */
300