scc 2025.09
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
29namespace tlm {
31namespace scc {
32
33template <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
37public:
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
44public:
48 tagged_target_mixin()
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 }
81
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 }
90
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 }
99
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
109private:
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
494private:
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.
Definition axis_tlm.h:56
SystemC TLM.
Definition dmi_mgr.h:19