scc  2022.4.0
SystemC components library
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 __TARGET_MIXIN_H__
17 #define __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 <functional>
25 #include <sstream>
26 #include <tlm>
27 #include <tlm_utils/peq_with_get.h>
28 
30 namespace tlm {
32 namespace scc {
36 template <typename BASE_TYPE, typename TYPES = tlm::tlm_base_protocol_types> class target_mixin : public BASE_TYPE {
37  // friend class fw_process;
38  // friend class bw_process;
39 
40 public:
41  using transaction_type = typename TYPES::tlm_payload_type;
42  using phase_type = typename TYPES::tlm_phase_type;
43  using sync_enum_type = tlm::tlm_sync_enum;
44  using fw_interface_type = tlm::tlm_fw_transport_if<TYPES>;
45  using bw_interface_type = tlm::tlm_bw_transport_if<TYPES>;
46 
47 public:
52  : target_mixin(sc_core::sc_gen_unique_name("target_mixin_socket")) {}
58  explicit target_mixin(const sc_core::sc_module_name& n)
59  : BASE_TYPE(n)
60  , m_fw_process(this)
61  , m_bw_process(this)
62  , m_current_transaction(nullptr) {
63  bind(m_fw_process);
64  }
66  using BASE_TYPE::bind;
72  tlm::tlm_bw_transport_if<TYPES>* operator->() { return &m_bw_process; }
78  void register_nb_transport_fw(std::function<sync_enum_type(transaction_type&, phase_type&, sc_core::sc_time&)> cb) {
79  assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
80  m_fw_process.set_nb_transport_ptr(cb);
81  }
87  void register_b_transport(std::function<void(transaction_type&, sc_core::sc_time&)> cb) {
88  assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
89  m_fw_process.set_b_transport_ptr(cb);
90  }
95  void register_transport_dbg(std::function<unsigned int(transaction_type&)> cb) {
96  assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
97  m_fw_process.set_transport_dbg_ptr(cb);
98  }
104  void register_get_direct_mem_ptr(std::function<bool(transaction_type&, tlm::tlm_dmi&)> cb) {
105  assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
106  m_fw_process.set_get_direct_mem_ptr(cb);
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(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  target_mixin* m_owner;
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(transaction_type&, phase_type&, sc_core::sc_time&)>;
164  using BTransportPtr = std::function<void(transaction_type&, sc_core::sc_time&)>;
165  using TransportDbgPtr = std::function<unsigned int(transaction_type&)>;
166  using GetDirectMemPtr = std::function<bool(transaction_type&, tlm::tlm_dmi&)>;
167 
168  fw_process(target_mixin* p_own)
169  : m_name(p_own->name())
170  , m_owner(p_own)
171  , m_nb_transport_ptr(0)
172  , m_b_transport_ptr(0)
173  , m_transport_dbg_ptr(0)
174  , m_get_direct_mem_ptr(0)
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) {
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  }
190  }
191 
192  void set_b_transport_ptr(BTransportPtr p) {
193  if(m_b_transport_ptr) {
194  std::stringstream s;
195  s << m_name << ": blocking callback allready registered";
196  SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
197  } else {
198  m_b_transport_ptr = p;
199  }
200  }
201 
202  void set_transport_dbg_ptr(TransportDbgPtr p) {
203  if(m_transport_dbg_ptr) {
204  std::stringstream s;
205  s << m_name << ": debug callback allready registered";
206  SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
207  } else {
208  m_transport_dbg_ptr = p;
209  }
210  }
211 
212  void set_get_direct_mem_ptr(GetDirectMemPtr p) {
213  if(m_get_direct_mem_ptr) {
214  std::stringstream s;
215  s << m_name << ": get DMI pointer callback allready registered";
216  SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
217  } else {
218  m_get_direct_mem_ptr = p;
219  }
220  }
221  // Interface implementation
222  sync_enum_type nb_transport_fw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) {
223  if(m_nb_transport_ptr) {
224  // forward call
225  return m_nb_transport_ptr(trans, phase, t);
226  } else if(m_b_transport_ptr) {
227  if(phase == tlm::BEGIN_REQ) {
228  // prepare thread to do blocking call
229  process_handle_class* ph = m_process_handle.get_handle(&trans);
230 
231  if(!ph) { // create new dynamic process
232  ph = new process_handle_class(&trans);
233  m_process_handle.put_handle(ph);
234 
235  sc_core::sc_spawn_options opts;
236  opts.dont_initialize();
237  opts.set_sensitivity(&ph->m_e);
238  sc_core::sc_spawn(sc_bind(&fw_process::nb2b_thread, this, ph), sc_core::sc_gen_unique_name("nb2b_thread"), &opts);
239  }
240  ph->m_e.notify(t);
241  phase = tlm::END_REQ;
242  return tlm::TLM_UPDATED;
243  } else if(phase == tlm::END_RESP) {
244  m_response_in_progress = false;
245  m_end_response.notify(t);
246  return tlm::TLM_COMPLETED;
247  } else {
248  assert(false);
249  exit(1);
250  }
251  } else {
252  std::stringstream s;
253  s << m_name << ": no non-blocking transport callback registered";
254  SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket", s.str().c_str());
255  }
256  return tlm::TLM_ACCEPTED;
257  }
258 
259  void b_transport(transaction_type& trans, sc_core::sc_time& t) {
260  if(m_b_transport_ptr) {
261  // forward call
262  m_b_transport_ptr(trans, t);
263  return;
264 
265  } else if(m_nb_transport_ptr) {
266  m_peq.notify(trans, t);
267  t = sc_core::SC_ZERO_TIME;
268 
269  mm_end_event_ext mm_ext;
270  const bool mm_added = !trans.has_mm();
271 
272  if(mm_added) {
273  trans.set_mm(this);
274  trans.set_auto_extension(&mm_ext);
275  trans.acquire();
276  }
277 
278  // wait until transaction is finished
279  sc_core::sc_event end_event;
280  m_owner->m_pending_trans[&trans] = &end_event;
281  sc_core::wait(end_event);
282 
283  if(mm_added) {
284  // release will not delete the transaction, it will notify mm_ext.done
285  trans.release();
286  if(trans.get_ref_count()) {
287  sc_core::wait(mm_ext.done);
288  }
289  trans.set_mm(0);
290  }
291 
292  } else {
293  std::stringstream s;
294  s << m_name << ": no blocking transport callback registered";
295  SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket", s.str().c_str());
296  }
297  }
298 
299  unsigned int transport_dbg(transaction_type& trans) {
300  if(m_transport_dbg_ptr) {
301  // forward call
302  return m_transport_dbg_ptr(trans);
303  } else {
304  // No debug support
305  return 0;
306  }
307  }
308 
309  bool get_direct_mem_ptr(transaction_type& trans, tlm::tlm_dmi& dmi_data) {
310  if(m_get_direct_mem_ptr) {
311  // forward call
312  return m_get_direct_mem_ptr(trans, dmi_data);
313 
314  } else {
315  // No DMI support
316  dmi_data.allow_read_write();
317  dmi_data.set_start_address(0x0);
318  dmi_data.set_end_address((sc_dt::uint64)-1);
319  return false;
320  }
321  }
322 
323  private:
324  // dynamic process handler for nb2b conversion
325 
326  class process_handle_class {
327  public:
328  explicit process_handle_class(transaction_type* trans)
329  : m_trans(trans)
330  , m_suspend(false) {}
331 
332  transaction_type* m_trans;
333  sc_core::sc_event m_e;
334  bool m_suspend;
335  };
336 
337  class process_handle_list {
338  public:
339  process_handle_list() = default;
340 
341  ~process_handle_list() {
342  for(typename std::vector<process_handle_class*>::iterator it = v.begin(), end = v.end(); it != end; ++it)
343  delete *it;
344  }
345 
346  process_handle_class* get_handle(transaction_type* trans) {
347  typename std::vector<process_handle_class*>::iterator it;
348 
349  for(it = v.begin(); it != v.end(); it++) {
350  if((*it)->m_suspend) { // found suspended dynamic process, re-use it
351  (*it)->m_trans = trans; // replace to new one
352  (*it)->m_suspend = false;
353  return *it;
354  }
355  }
356  return NULL; // no suspended process
357  }
358 
359  void put_handle(process_handle_class* ph) { v.push_back(ph); }
360 
361  private:
362  std::vector<process_handle_class*> v;
363  };
364 
365  process_handle_list m_process_handle;
366 
367  void nb2b_thread(process_handle_class* h) {
368 
369  while(true) {
370  transaction_type* trans = h->m_trans;
371  sc_core::sc_time t = sc_core::SC_ZERO_TIME;
372 
373  // forward call
374  m_b_transport_ptr(*trans, t);
375 
376  sc_core::wait(t);
377 
378  // return path
379  while(m_response_in_progress) {
380  sc_core::wait(m_end_response);
381  }
382  t = sc_core::SC_ZERO_TIME;
383  phase_type phase = tlm::BEGIN_RESP;
384  sync_enum_type sync = m_owner->bw_nb_transport(*trans, phase, t);
385  if(!(sync == tlm::TLM_COMPLETED || (sync == tlm::TLM_UPDATED && phase == tlm::END_RESP))) {
386  m_response_in_progress = true;
387  }
388 
389  // suspend until next transaction
390  h->m_suspend = true;
391  sc_core::wait();
392  }
393  }
394 
395  void b2nb_thread() {
396  while(true) {
397  sc_core::wait(m_peq.get_event());
398 
399  transaction_type* trans;
400  while((trans = m_peq.get_next_transaction()) != 0) {
401  assert(m_nb_transport_ptr);
402  phase_type phase = tlm::BEGIN_REQ;
403  sc_core::sc_time t = sc_core::SC_ZERO_TIME;
404 
405  switch(m_nb_transport_ptr(*trans, phase, t)) {
406  case tlm::TLM_COMPLETED: {
407  // notify transaction is finished
408  typename std::map<transaction_type*, sc_core::sc_event*>::iterator it = m_owner->m_pending_trans.find(trans);
409  assert(it != m_owner->m_pending_trans.end());
410  it->second->notify(t);
411  m_owner->m_pending_trans.erase(it);
412  break;
413  }
414 
415  case tlm::TLM_ACCEPTED:
416  case tlm::TLM_UPDATED:
417  switch(phase) {
418  case tlm::BEGIN_REQ:
419  m_owner->m_current_transaction = trans;
420  sc_core::wait(m_owner->m_end_request);
421  m_owner->m_current_transaction = 0;
422  break;
423 
424  case tlm::END_REQ:
425  sc_core::wait(t);
426  break;
427 
428  case tlm::BEGIN_RESP: {
429  phase = tlm::END_RESP;
430  sc_core::wait(t); // This line is a bug fix added in TLM-2.0.2
431  t = sc_core::SC_ZERO_TIME;
432  m_nb_transport_ptr(*trans, phase, t);
433 
434  // notify transaction is finished
435  typename std::map<transaction_type*, sc_core::sc_event*>::iterator it = m_owner->m_pending_trans.find(trans);
436  assert(it != m_owner->m_pending_trans.end());
437  it->second->notify(t);
438  m_owner->m_pending_trans.erase(it);
439  break;
440  }
441 
442  default:
443  assert(false);
444  exit(1);
445  };
446  break;
447 
448  default:
449  assert(false);
450  exit(1);
451  };
452  }
453  }
454  }
455 
456  void free(tlm::tlm_generic_payload* trans) {
457  mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>();
458  assert(ext);
459  // notif event first before freeing extensions (reset)
460  ext->done.notify();
461  trans->reset();
462  }
463 
464  private:
465  class mm_end_event_ext : public tlm::tlm_extension<mm_end_event_ext> {
466  public:
467  tlm::tlm_extension_base* clone() const { return NULL; }
468  void free() {}
469  void copy_from(tlm::tlm_extension_base const&) {}
470  sc_core::sc_event done;
471  };
472 
473  private:
474  const std::string m_name;
475  target_mixin* m_owner;
476  NBTransportPtr m_nb_transport_ptr;
477  BTransportPtr m_b_transport_ptr;
478  TransportDbgPtr m_transport_dbg_ptr;
479  GetDirectMemPtr m_get_direct_mem_ptr;
480  tlm_utils::peq_with_get<transaction_type> m_peq;
481  bool m_response_in_progress;
482  sc_core::sc_event m_end_response;
483  };
484 
485 private:
486  fw_process m_fw_process;
487  bw_process m_bw_process;
488  std::map<transaction_type*, sc_core::sc_event*> m_pending_trans;
489  sc_core::sc_event m_end_request;
490  transaction_type* m_current_transaction;
491 };
492 } // namespace scc
493 } // namespace tlm
494 
495 #endif //__TARGET_MIXIN_H__
void register_transport_dbg(std::function< unsigned int(transaction_type &)> cb)
Definition: target_mixin.h:95
target_mixin(const sc_core::sc_module_name &n)
Definition: target_mixin.h:58
void register_nb_transport_fw(std::function< sync_enum_type(transaction_type &, phase_type &, sc_core::sc_time &)> cb)
Definition: target_mixin.h:78
void register_get_direct_mem_ptr(std::function< bool(transaction_type &, tlm::tlm_dmi &)> cb)
Definition: target_mixin.h:104
void register_b_transport(std::function< void(transaction_type &, sc_core::sc_time &)> cb)
Definition: target_mixin.h:87
tlm::tlm_bw_transport_if< TYPES > * operator->()
Definition: target_mixin.h:72
SCC SystemC utilities.
SystemC TLM.