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