scc  2024.06
SystemC components library
tagged_target_mixin.h
1 /*******************************************************************************
2  * Copyright 2016, 2018 MINRES Technologies GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *******************************************************************************/
16 #ifndef __TAGGED_TARGET_MIXIN_H__
17 #define __TAGGED_TARGET_MIXIN_H__
18 
19 #ifndef SC_INCLUDE_DYNAMIC_PROCESSES // needed for sc_spawn
20 #define SC_INCLUDE_DYNAMIC_PROCESSES
21 #endif
22 
23 #include <scc/utilities.h>
24 #include <sstream>
25 #include <tlm>
26 #include <tlm_utils/peq_with_get.h>
27 
29 namespace tlm {
31 namespace scc {
32 
33 template <typename base_type, typename TYPES = tlm::tlm_base_protocol_types> class tagged_target_mixin : public base_type {
34  friend class fw_process;
35  friend class bw_process;
36 
37 public:
38  using transaction_type = typename TYPES::tlm_payload_type;
39  using phase_type = typename TYPES::tlm_phase_type;
40  using sync_enum_type = tlm::tlm_sync_enum;
41  using fw_interface_type = tlm::tlm_fw_transport_if<TYPES>;
42  using bw_interface_type = tlm::tlm_bw_transport_if<TYPES>;
43 
44 public:
49  : tagged_target_mixin(sc_core::sc_gen_unique_name("tagged_target_socket")) {}
54  explicit tagged_target_mixin(const char* n)
55  : base_type(n)
56  , m_fw_process(this)
57  , m_bw_process(this)
58  , m_current_transaction(nullptr) {
59  bind(m_fw_process);
60  }
61 
62  using base_type::bind;
63 
64  // bw transport must come thru us.
69  tlm::tlm_bw_transport_if<TYPES>* operator->() { return &m_bw_process; }
70  // REGISTER_XXX
76  void register_nb_transport_fw(std::function<sync_enum_type(unsigned int, transaction_type&, phase_type&, sc_core::sc_time&)> cb,
77  unsigned int tag) {
78  assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
79  m_fw_process.set_nb_transport_ptr(cb, tag);
80  }
86  void register_b_transport(std::function<void(unsigned int, transaction_type&, sc_core::sc_time&)> cb, unsigned int tag) {
87  assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
88  m_fw_process.set_b_transport_ptr(cb, tag);
89  }
95  void register_transport_dbg(std::function<unsigned int(unsigned int, transaction_type&)> cb, unsigned int tag) {
96  assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
97  m_fw_process.set_transport_dbg_ptr(cb, tag);
98  }
104  void register_get_direct_mem_ptr(std::function<bool(unsigned int, transaction_type&, tlm::tlm_dmi&)> cb, unsigned int tag) {
105  assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
106  m_fw_process.set_get_direct_mem_ptr(cb, tag);
107  }
108 
109 private:
110  // make call on bw path.
111  sync_enum_type bw_nb_transport(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) {
112  return base_type::operator->()->nb_transport_bw(trans, phase, t);
113  }
114 
115  void bw_invalidate_direct_mem_ptr(sc_dt::uint64 s, sc_dt::uint64 e) { base_type::operator->()->invalidate_direct_mem_ptr(s, e); }
116 
117  // Helper class to handle bw path calls
118  // Needed to detect transaction end when called from b_transport.
119  class bw_process : public tlm::tlm_bw_transport_if<TYPES> {
120  public:
121  bw_process(tagged_target_mixin* p_own)
122  : m_owner(p_own) {}
123 
124  sync_enum_type nb_transport_bw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) {
125  typename std::map<transaction_type*, sc_core::sc_event*>::iterator it;
126 
127  it = m_owner->m_pending_trans.find(&trans);
128  if(it == m_owner->m_pending_trans.end()) {
129  // Not a blocking call, forward.
130  return m_owner->bw_nb_transport(trans, phase, t);
131 
132  } else {
133  if(phase == tlm::END_REQ) {
134  m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME);
135  return tlm::TLM_ACCEPTED;
136 
137  } else if(phase == tlm::BEGIN_RESP) {
138  if(m_owner->m_current_transaction == &trans) {
139  m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME);
140  }
141  // TODO: add response-accept delay?
142  it->second->notify(t);
143  m_owner->m_pending_trans.erase(it);
144  return tlm::TLM_COMPLETED;
145 
146  } else {
147  assert(false);
148  exit(1);
149  }
150 
151  // return tlm::TLM_COMPLETED; //Should not reach here
152  }
153  }
154 
155  void invalidate_direct_mem_ptr(sc_dt::uint64 s, sc_dt::uint64 e) { m_owner->bw_invalidate_direct_mem_ptr(s, e); }
156 
157  private:
158  tagged_target_mixin* m_owner{nullptr};
159  };
160 
161  class fw_process : public tlm::tlm_fw_transport_if<TYPES>, public tlm::tlm_mm_interface {
162  public:
163  using NBTransportPtr = std::function<sync_enum_type(unsigned int, transaction_type&, phase_type&, sc_core::sc_time&)>;
164  using BTransportPtr = std::function<void(unsigned int, transaction_type&, sc_core::sc_time&)>;
165  using TransportDbgPtr = std::function<unsigned int(unsigned int, transaction_type&)>;
166  using GetDirectMemPtr = std::function<bool(unsigned int, transaction_type&, tlm::tlm_dmi&)>;
167 
168  fw_process(tagged_target_mixin* p_own)
169  : m_name(p_own->name())
170  , m_owner(p_own)
171  , m_nb_transport_ptr(nullptr)
172  , m_b_transport_ptr(nullptr)
173  , m_transport_dbg_ptr(nullptr)
174  , m_get_direct_mem_ptr(nullptr)
175  , m_peq(sc_core::sc_gen_unique_name("m_peq"))
176  , m_response_in_progress(false) {
177  sc_core::sc_spawn_options opts;
178  opts.set_sensitivity(&m_peq.get_event());
179  sc_core::sc_spawn(sc_bind(&fw_process::b2nb_thread, this), sc_core::sc_gen_unique_name("b2nb_thread"), &opts);
180  }
181 
182  void set_nb_transport_ptr(NBTransportPtr p, unsigned int tag) {
183  if(m_nb_transport_ptr) {
184  std::stringstream s;
185  s << m_name << ": non-blocking callback allready registered";
186  SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
187  } else {
188  m_nb_transport_ptr = p;
189  tags[1] = tag;
190  }
191  }
192 
193  void set_b_transport_ptr(BTransportPtr p, unsigned int tag) {
194  if(m_b_transport_ptr) {
195  std::stringstream s;
196  s << m_name << ": blocking callback allready registered";
197  SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
198  } else {
199  m_b_transport_ptr = p;
200  tags[0] = tag;
201  }
202  }
203 
204  void set_transport_dbg_ptr(TransportDbgPtr p, unsigned int tag) {
205  if(m_transport_dbg_ptr) {
206  std::stringstream s;
207  s << m_name << ": debug callback allready registered";
208  SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
209  } else {
210  m_transport_dbg_ptr = p;
211  tags[2] = tag;
212  }
213  }
214 
215  void set_get_direct_mem_ptr(GetDirectMemPtr p, unsigned int tag) {
216  if(m_get_direct_mem_ptr) {
217  std::stringstream s;
218  s << m_name << ": get DMI pointer callback allready registered";
219  SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
220  } else {
221  m_get_direct_mem_ptr = p;
222  tags[3] = tag;
223  }
224  }
225  // Interface implementation
226  sync_enum_type nb_transport_fw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) {
227  if(m_nb_transport_ptr) {
228  // forward call
229  return m_nb_transport_ptr(tags[1], trans, phase, t);
230  } else if(m_b_transport_ptr) {
231  if(phase == tlm::BEGIN_REQ) {
232  // prepare thread to do blocking call
233  process_handle_class* ph = m_process_handle.get_handle(&trans);
234 
235  if(!ph) { // create new dynamic process
236  ph = new process_handle_class(&trans);
237  m_process_handle.put_handle(ph);
238 
239  sc_core::sc_spawn_options opts;
240  opts.dont_initialize();
241  opts.set_sensitivity(&ph->m_e);
242  sc_core::sc_spawn(sc_bind(&fw_process::nb2b_thread, this, ph), sc_core::sc_gen_unique_name("nb2b_thread"), &opts);
243  }
244 
245  ph->m_e.notify(t);
246  return tlm::TLM_ACCEPTED;
247 
248  } else if(phase == tlm::END_RESP) {
249  m_response_in_progress = false;
250  m_end_response.notify(t);
251  return tlm::TLM_COMPLETED;
252 
253  } else {
254  assert(false);
255  exit(1);
256  // return tlm::TLM_COMPLETED; ///< unreachable code
257  }
258 
259  } else {
260  std::stringstream s;
261  s << m_name << ": no non-blocking transport callback registered";
262  SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket", s.str().c_str());
263  }
264  return tlm::TLM_ACCEPTED;
265  }
266 
267  void b_transport(transaction_type& trans, sc_core::sc_time& t) {
268  if(m_b_transport_ptr) {
269  // forward call
270  m_b_transport_ptr(tags[0], trans, t);
271  return;
272 
273  } else if(m_nb_transport_ptr) {
274  m_peq.notify(trans, t);
275  t = sc_core::SC_ZERO_TIME;
276 
277  mm_end_event_ext mm_ext;
278  const bool mm_added = !trans.has_mm();
279 
280  if(mm_added) {
281  trans.set_mm(this);
282  trans.set_auto_extension(&mm_ext);
283  trans.acquire();
284  }
285 
286  // wait until transaction is finished
287  sc_core::sc_event end_event;
288  m_owner->m_pending_trans[&trans] = &end_event;
289  sc_core::wait(end_event);
290 
291  if(mm_added) {
292  // release will not delete the transaction, it will notify mm_ext.done
293  trans.release();
294  if(trans.get_ref_count()) {
295  sc_core::wait(mm_ext.done);
296  }
297  trans.set_mm(0);
298  }
299 
300  } else {
301  std::stringstream s;
302  s << m_name << ": no blocking transport callback registered";
303  SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket", s.str().c_str());
304  }
305  }
306 
307  unsigned int transport_dbg(transaction_type& trans) {
308  if(m_transport_dbg_ptr) {
309  // forward call
310  return m_transport_dbg_ptr(tags[2], trans);
311  } else {
312  // No debug support
313  return 0;
314  }
315  }
316 
317  bool get_direct_mem_ptr(transaction_type& trans, tlm::tlm_dmi& dmi_data) {
318  if(m_get_direct_mem_ptr) {
319  // forward call
320  return m_get_direct_mem_ptr(tags[3], trans, dmi_data);
321 
322  } else {
323  // No DMI support
324  dmi_data.allow_read_write();
325  dmi_data.set_start_address(0x0);
326  dmi_data.set_end_address((sc_dt::uint64)-1);
327  return false;
328  }
329  }
330 
331  private:
332  // dynamic process handler for nb2b conversion
333 
334  class process_handle_class {
335  public:
336  explicit process_handle_class(transaction_type* trans)
337  : m_trans(trans)
338  , m_suspend(false) {}
339 
340  transaction_type* m_trans{nullptr};
341  sc_core::sc_event m_e{};
342  bool m_suspend{false};
343  };
344 
345  class process_handle_list {
346  public:
347  process_handle_list() = default;
348 
349  ~process_handle_list() {
350  for(typename std::vector<process_handle_class*>::iterator it = v.begin(), end = v.end(); it != end; ++it)
351  delete *it;
352  }
353 
354  process_handle_class* get_handle(transaction_type* trans) {
355  typename std::vector<process_handle_class*>::iterator it;
356 
357  for(it = v.begin(); it != v.end(); it++) {
358  if((*it)->m_suspend) { // found suspended dynamic process, re-use it
359  (*it)->m_trans = trans; // replace to new one
360  (*it)->m_suspend = false;
361  return *it;
362  }
363  }
364  return NULL; // no suspended process
365  }
366 
367  void put_handle(process_handle_class* ph) { v.push_back(ph); }
368 
369  private:
370  std::vector<process_handle_class*> v{};
371  };
372 
373  process_handle_list m_process_handle{};
374 
375  void nb2b_thread(process_handle_class* h) {
376 
377  while(true) {
378  transaction_type* trans = h->m_trans;
379  sc_core::sc_time t = sc_core::SC_ZERO_TIME;
380 
381  // forward call
382  m_b_transport_ptr(tags[0], *trans, t);
383 
384  sc_core::wait(t);
385 
386  // return path
387  while(m_response_in_progress) {
388  sc_core::wait(m_end_response);
389  }
390  t = sc_core::SC_ZERO_TIME;
391  phase_type phase = tlm::BEGIN_RESP;
392  sync_enum_type sync = m_owner->bw_nb_transport(*trans, phase, t);
393  if(!(sync == tlm::TLM_COMPLETED || (sync == tlm::TLM_UPDATED && phase == tlm::END_RESP))) {
394  m_response_in_progress = true;
395  }
396 
397  // suspend until next transaction
398  h->m_suspend = true;
399  sc_core::wait();
400  }
401  }
402 
403  void b2nb_thread() {
404  while(true) {
405  sc_core::wait(m_peq.get_event());
406 
407  transaction_type* trans;
408  while((trans = m_peq.get_next_transaction()) != 0) {
409  assert(m_nb_transport_ptr);
410  phase_type phase = tlm::BEGIN_REQ;
411  sc_core::sc_time t = sc_core::SC_ZERO_TIME;
412 
413  switch(m_nb_transport_ptr(tags[1], *trans, phase, t)) {
414  case tlm::TLM_COMPLETED: {
415  // notify transaction is finished
416  typename std::map<transaction_type*, sc_core::sc_event*>::iterator it = m_owner->m_pending_trans.find(trans);
417  assert(it != m_owner->m_pending_trans.end());
418  it->second->notify(t);
419  m_owner->m_pending_trans.erase(it);
420  break;
421  }
422 
423  case tlm::TLM_ACCEPTED:
424  case tlm::TLM_UPDATED:
425  switch(phase) {
426  case tlm::BEGIN_REQ:
427  m_owner->m_current_transaction = trans;
428  sc_core::wait(m_owner->m_end_request);
429  m_owner->m_current_transaction = 0;
430  break;
431 
432  case tlm::END_REQ:
433  sc_core::wait(t);
434  break;
435 
436  case tlm::BEGIN_RESP: {
437  phase = tlm::END_RESP;
438  sc_core::wait(t); // This line is a bug fix added in TLM-2.0.2
439  t = sc_core::SC_ZERO_TIME;
440  m_nb_transport_ptr(tags[1], *trans, phase, t);
441 
442  // notify transaction is finished
443  typename std::map<transaction_type*, sc_core::sc_event*>::iterator it = m_owner->m_pending_trans.find(trans);
444  assert(it != m_owner->m_pending_trans.end());
445  it->second->notify(t);
446  m_owner->m_pending_trans.erase(it);
447  break;
448  }
449 
450  default:
451  assert(false);
452  exit(1);
453  };
454  break;
455 
456  default:
457  assert(false);
458  exit(1);
459  };
460  }
461  }
462  }
463 
464  void free(tlm::tlm_generic_payload* trans) {
465  mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>();
466  assert(ext);
467  // notif event first before freeing extensions (reset)
468  ext->done.notify();
469  trans->reset();
470  }
471 
472  private:
473  class mm_end_event_ext : public tlm::tlm_extension<mm_end_event_ext> {
474  public:
475  tlm::tlm_extension_base* clone() const { return NULL; }
476  void free() {}
477  void copy_from(tlm::tlm_extension_base const&) {}
478  sc_core::sc_event done{};
479  };
480 
481  private:
482  const std::string m_name;
483  tagged_target_mixin* m_owner{nullptr};
484  unsigned int tags[4]; // bl, nb, dbg, dmi
485  NBTransportPtr m_nb_transport_ptr{};
486  BTransportPtr m_b_transport_ptr{};
487  TransportDbgPtr m_transport_dbg_ptr{};
488  GetDirectMemPtr m_get_direct_mem_ptr{};
489  tlm_utils::peq_with_get<transaction_type> m_peq{};
490  bool m_response_in_progress{false};
491  sc_core::sc_event m_end_response{};
492  };
493 
494 private:
495  fw_process m_fw_process;
496  bw_process m_bw_process;
497  std::map<transaction_type*, sc_core::sc_event*> m_pending_trans{};
498  sc_core::sc_event m_end_request{};
499  transaction_type* m_current_transaction{nullptr};
500 };
501 } // namespace scc
502 } // namespace tlm
503 
504 #endif //__TAGGED_TARGET_MIXIN_H__
void register_get_direct_mem_ptr(std::function< bool(unsigned int, transaction_type &, tlm::tlm_dmi &)> cb, unsigned int tag)
void register_transport_dbg(std::function< unsigned int(unsigned int, transaction_type &)> cb, unsigned int tag)
void register_b_transport(std::function< void(unsigned int, transaction_type &, sc_core::sc_time &)> cb, unsigned int tag)
void register_nb_transport_fw(std::function< sync_enum_type(unsigned int, transaction_type &, phase_type &, sc_core::sc_time &)> cb, unsigned int tag)
tlm::tlm_bw_transport_if< TYPES > * operator->()
SCC TLM utilities.
SystemC TLM.
Definition: cxs_tlm.h:69