peq_with_cb_and_phase.h revision 13743
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