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