scc 2025.09
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 _TLM_SCC_TARGET_MIXIN_H_
17#define _TLM_SCC_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
30namespace tlm {
32namespace scc {
36template <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
40public:
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
47public:
52 : target_mixin(sc_core::sc_gen_unique_name("target_mixin_socket")) {}
53
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 }
65
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 }
82
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 }
91
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 }
99
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
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(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
485private:
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 //_TLM_SCC_TARGET_MIXIN_H_
void register_transport_dbg(std::function< unsigned int(transaction_type &)> cb)
target_mixin(const sc_core::sc_module_name &n)
void register_nb_transport_fw(std::function< sync_enum_type(transaction_type &, phase_type &, sc_core::sc_time &)> cb)
void register_get_direct_mem_ptr(std::function< bool(transaction_type &, tlm::tlm_dmi &)> cb)
void register_b_transport(std::function< void(transaction_type &, sc_core::sc_time &)> cb)
tlm::tlm_bw_transport_if< TYPES > * operator->()
SCC TLM utilities.
Definition axis_tlm.h:56
SystemC TLM.
Definition dmi_mgr.h:19