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