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_NW_TARGET_MIXIN_H_
17#define _TLM_NW_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 "tlm_network_sockets.h"
25#include <functional>
26#include <sstream>
27#include <tlm>
28#include <tlm_utils/peq_with_get.h>
29
31namespace tlm {
33namespace nw {
37template <typename BASE_TYPE, bool REQUESTOR = true, typename TYPES = typename BASE_TYPE::protocol_types>
38class target_mixin : public BASE_TYPE {
39 // friend class fw_process;
40 // friend class bw_process;
41
42public:
43 using transaction_type = typename TYPES::tlm_payload_type;
44 using phase_type = typename TYPES::tlm_phase_type;
45 using sync_enum_type = tlm::tlm_sync_enum;
46 using fw_interface_type = tlm::nw::tlm_network_fw_transport_if<TYPES>;
47 using bw_interface_type = tlm::nw::tlm_network_bw_transport_if<TYPES>;
48
49public:
54 : target_mixin(sc_core::sc_gen_unique_name("target_mixin_socket")) {}
55
60 explicit target_mixin(const sc_core::sc_module_name& n)
61 : BASE_TYPE(n)
62 , m_fw_process(this)
63 , m_bw_process(this)
64 , m_current_transaction(nullptr) {
65 bind(m_fw_process);
66 }
67
68 using BASE_TYPE::bind;
80 void register_nb_transport_fw(std::function<sync_enum_type(transaction_type&, phase_type&, sc_core::sc_time&)> cb) {
81 assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
82 m_fw_process.set_nb_transport_ptr(cb);
83 }
84
89 void register_b_transport(std::function<void(transaction_type&, sc_core::sc_time&)> cb) {
90 assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
91 m_fw_process.set_b_transport_ptr(cb);
92 }
93
97 void register_transport_dbg(std::function<unsigned int(transaction_type&)> cb) {
98 assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
99 m_fw_process.set_transport_dbg_ptr(cb);
100 }
101
106 void register_get_direct_mem_ptr(std::function<bool(transaction_type&, tlm::tlm_dmi&)> cb) {
107 assert(!sc_core::sc_get_curr_simcontext()->elaboration_done());
108 m_fw_process.set_get_direct_mem_ptr(cb);
109 }
110
111private:
112 // make call on bw path.
113 sync_enum_type bw_nb_transport(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) {
114 return BASE_TYPE::operator->()->nb_transport_bw(trans, phase, t);
115 }
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::nw::tlm_network_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::nw::CONFIRM || phase == tlm::nw::RESPONSE) {
134 m_owner->m_end_request.notify(sc_core::SC_ZERO_TIME);
135 return tlm::TLM_ACCEPTED;
136 } else {
137 assert(false);
138 exit(1);
139 }
140 }
141 }
142
143 private:
144 target_mixin* m_owner;
145 };
146
147 class fw_process : public tlm::nw::tlm_network_fw_transport_if<TYPES>, public tlm::nw::tlm_base_mm_interface {
148 public:
149 using NBTransportPtr = std::function<sync_enum_type(transaction_type&, phase_type&, sc_core::sc_time&)>;
150 using BTransportPtr = std::function<void(transaction_type&, sc_core::sc_time&)>;
151 using TransportDbgPtr = std::function<unsigned int(transaction_type&)>;
152 using GetDirectMemPtr = std::function<bool(transaction_type&, tlm::tlm_dmi&)>;
153
154 fw_process(target_mixin* p_own)
155 : m_name(p_own->name())
156 , m_owner(p_own)
157 , m_nb_transport_ptr(0)
158 , m_b_transport_ptr(0)
159 , m_transport_dbg_ptr(0)
160 , m_get_direct_mem_ptr(0)
161 , m_peq(sc_core::sc_gen_unique_name("m_peq")) {
162 sc_core::sc_spawn_options opts;
163 opts.set_sensitivity(&m_peq.get_event());
164 sc_core::sc_spawn(sc_bind(&fw_process::b2nb_thread, this), sc_core::sc_gen_unique_name("b2nb_thread"), &opts);
165 }
166
167 void set_nb_transport_ptr(NBTransportPtr p) {
168 if(m_nb_transport_ptr) {
169 std::stringstream s;
170 s << m_name << ": non-blocking callback allready registered";
171 SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
172 } else {
173 m_nb_transport_ptr = p;
174 }
175 }
176
177 void set_b_transport_ptr(BTransportPtr p) {
178 if(m_b_transport_ptr) {
179 std::stringstream s;
180 s << m_name << ": blocking callback allready registered";
181 SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
182 } else {
183 m_b_transport_ptr = p;
184 }
185 }
186
187 void set_transport_dbg_ptr(TransportDbgPtr p) {
188 if(m_transport_dbg_ptr) {
189 std::stringstream s;
190 s << m_name << ": debug callback allready registered";
191 SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
192 } else {
193 m_transport_dbg_ptr = p;
194 }
195 }
196
197 void set_get_direct_mem_ptr(GetDirectMemPtr p) {
198 if(m_get_direct_mem_ptr) {
199 std::stringstream s;
200 s << m_name << ": get DMI pointer callback allready registered";
201 SC_REPORT_WARNING("/OSCI_TLM-2/simple_socket", s.str().c_str());
202 } else {
203 m_get_direct_mem_ptr = p;
204 }
205 }
206 // Interface implementation
207 sync_enum_type nb_transport_fw(transaction_type& trans, phase_type& phase, sc_core::sc_time& t) {
208 if(m_nb_transport_ptr) {
209 // forward call
210 return m_nb_transport_ptr(trans, phase, t);
211 } else if(m_b_transport_ptr) {
212 if(phase == tlm::nw::REQUEST || phase == tlm::nw::INDICATION) {
213 // prepare thread to do blocking call
214 process_handle_class* ph = m_process_handle.get_handle(&trans, phase);
215
216 if(!ph) { // create new dynamic process
217 ph = new process_handle_class(&trans, phase);
218 m_process_handle.put_handle(ph);
219
220 sc_core::sc_spawn_options opts;
221 opts.dont_initialize();
222 opts.set_sensitivity(&ph->m_e);
223 sc_core::sc_spawn(sc_bind(&fw_process::nb2b_thread, this, ph), sc_core::sc_gen_unique_name("nb2b_thread"), &opts);
224 }
225 ph->m_e.notify(t);
226 return tlm::TLM_ACCEPTED;
227 } else {
228 assert(false);
229 exit(1);
230 }
231 } else {
232 std::stringstream s;
233 s << m_name << ": no non-blocking transport callback registered";
234 SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket", s.str().c_str());
235 }
236 return tlm::TLM_ACCEPTED;
237 }
238
239 void b_transport(transaction_type& trans, sc_core::sc_time& t) {
240 if(m_b_transport_ptr) {
241 // forward call
242 m_b_transport_ptr(trans, t);
243 return;
244
245 } else if(m_nb_transport_ptr) {
246 m_peq.notify(trans, t);
247 t = sc_core::SC_ZERO_TIME;
248
249 mm_end_event_ext mm_ext;
250 const bool mm_added = !trans.has_mm();
251
252 if(mm_added) {
253 trans.set_mm(this);
254 trans.set_auto_extension(&mm_ext);
255 trans.acquire();
256 }
257
258 // wait until transaction is finished
259 sc_core::sc_event end_event;
260 m_owner->m_pending_trans[&trans] = &end_event;
261 sc_core::wait(end_event);
262
263 if(mm_added) {
264 // release will not delete the transaction, it will notify mm_ext.done
265 trans.release();
266 if(trans.get_ref_count()) {
267 sc_core::wait(mm_ext.done);
268 }
269 trans.set_mm(0);
270 }
271
272 } else {
273 std::stringstream s;
274 s << m_name << ": no blocking transport callback registered";
275 SC_REPORT_ERROR("/OSCI_TLM-2/simple_socket", s.str().c_str());
276 }
277 }
278
279 unsigned int transport_dbg(transaction_type& trans) {
280 if(m_transport_dbg_ptr) {
281 // forward call
282 return m_transport_dbg_ptr(trans);
283 } else {
284 // No debug support
285 return 0;
286 }
287 }
288
289 bool get_direct_mem_ptr(transaction_type& trans, tlm::tlm_dmi& dmi_data) {
290 if(m_get_direct_mem_ptr) {
291 // forward call
292 return m_get_direct_mem_ptr(trans, dmi_data);
293
294 } else {
295 // No DMI support
296 dmi_data.allow_read_write();
297 dmi_data.set_start_address(0x0);
298 dmi_data.set_end_address((sc_dt::uint64)-1);
299 return false;
300 }
301 }
302
303 private:
304 // dynamic process handler for nb2b conversion
305
306 class process_handle_class {
307 public:
308 explicit process_handle_class(transaction_type* trans, phase_type phase)
309 : m_trans(trans)
310 , m_phase(phase)
311 , m_suspend(false) {}
312
313 transaction_type* m_trans;
314 phase_type m_phase;
315 sc_core::sc_event m_e;
316 bool m_suspend;
317 };
318
319 class process_handle_list {
320 public:
321 process_handle_list() = default;
322
323 ~process_handle_list() {
324 for(typename std::vector<process_handle_class*>::iterator it = v.begin(), end = v.end(); it != end; ++it)
325 delete *it;
326 }
327
328 process_handle_class* get_handle(transaction_type* trans, phase_type phase) {
329 typename std::vector<process_handle_class*>::iterator it;
330
331 for(it = v.begin(); it != v.end(); it++) {
332 if((*it)->m_suspend) { // found suspended dynamic process, re-use it
333 (*it)->m_trans = trans; // replace to new one
334 (*it)->m_phase = phase;
335 (*it)->m_suspend = false;
336 return *it;
337 }
338 }
339 return NULL; // no suspended process
340 }
341
342 void put_handle(process_handle_class* ph) { v.push_back(ph); }
343
344 private:
345 std::vector<process_handle_class*> v;
346 };
347
348 process_handle_list m_process_handle;
349
350 void nb2b_thread(process_handle_class* h) {
351 while(true) {
352 transaction_type* trans = h->m_trans;
353 sc_core::sc_time t = sc_core::SC_ZERO_TIME;
354 // forward call
355 m_b_transport_ptr(*trans, t);
356 sc_core::wait(t);
357 t = sc_core::SC_ZERO_TIME;
358 phase_type phase = tlm::nw::RESPONSE;
359 if(h->m_phase == tlm::nw::REQUEST)
360 phase = tlm::nw::CONFIRM;
361 sync_enum_type sync = m_owner->bw_nb_transport(*trans, phase, t);
362 // suspend until next transaction
363 h->m_suspend = true;
364 sc_core::wait();
365 }
366 }
367
368 void b2nb_thread() {
369 while(true) {
370 sc_core::wait(m_peq.get_event());
371 transaction_type* trans;
372 while((trans = m_peq.get_next_transaction()) != 0) {
373 assert(m_nb_transport_ptr);
374 phase_type phase = tlm::nw::REQUEST;
375 if(REQUESTOR)
376 phase = tlm::nw::INDICATION;
377 sc_core::sc_time t = sc_core::SC_ZERO_TIME;
378
379 switch(m_nb_transport_ptr(*trans, phase, t)) {
380 case tlm::TLM_COMPLETED: {
381 assert(phase == tlm::nw::CONFIRM || phase == tlm::nw::RESPONSE);
382 // notify transaction is finished
383 typename std::map<transaction_type*, sc_core::sc_event*>::iterator it = m_owner->m_pending_trans.find(trans);
384 assert(it != m_owner->m_pending_trans.end());
385 it->second->notify(t);
386 m_owner->m_pending_trans.erase(it);
387 break;
388 }
389 case tlm::TLM_ACCEPTED:
390 case tlm::TLM_UPDATED:
391 if(phase == tlm::nw::REQUEST || phase == tlm::nw::INDICATION) {
392 m_owner->m_current_transaction = trans;
393 sc_core::wait(m_owner->m_end_request);
394 m_owner->m_current_transaction = 0;
395 } else if(phase == tlm::nw::CONFIRM || phase == tlm::nw::RESPONSE) {
396 sc_core::wait(t);
397 } else
398 assert(false && "Illegal phase received");
399 break;
400 default:
401 assert(false && "Illegate sync enum received");
402 exit(1);
403 };
404 }
405 }
406 }
407
408 void free(tlm::nw::tlm_network_payload_base* trans) {
409 mm_end_event_ext* ext = trans->template get_extension<mm_end_event_ext>();
410 assert(ext);
411 // notif event first before freeing extensions (reset)
412 ext->done.notify();
413 trans->reset();
414 }
415
416 private:
417 class mm_end_event_ext : public tlm::tlm_extension<mm_end_event_ext> {
418 public:
419 tlm::tlm_extension_base* clone() const { return NULL; }
420 void free() {}
421 void copy_from(tlm::tlm_extension_base const&) {}
422 sc_core::sc_event done;
423 };
424
425 const std::string m_name;
426 target_mixin* m_owner;
427 NBTransportPtr m_nb_transport_ptr;
428 BTransportPtr m_b_transport_ptr;
429 TransportDbgPtr m_transport_dbg_ptr;
430 GetDirectMemPtr m_get_direct_mem_ptr;
431 tlm_utils::peq_with_get<transaction_type> m_peq;
432 };
433
434private:
435 fw_process m_fw_process;
436 bw_process m_bw_process;
437 std::map<transaction_type*, sc_core::sc_event*> m_pending_trans;
438 sc_core::sc_event m_end_request;
439 transaction_type* m_current_transaction;
440};
441} // namespace nw
442} // namespace tlm
443
444#endif //_TLM_NW_TARGET_MIXIN_H_
void register_b_transport(std::function< void(transaction_type &, sc_core::sc_time &)> cb)
void register_get_direct_mem_ptr(std::function< bool(transaction_type &, tlm::tlm_dmi &)> cb)
tlm::nw::tlm_network_bw_transport_if< TYPES > * operator->()
void register_nb_transport_fw(std::function< sync_enum_type(transaction_type &, phase_type &, sc_core::sc_time &)> cb)
void register_transport_dbg(std::function< unsigned int(transaction_type &)> cb)
target_mixin(const sc_core::sc_module_name &n)
SCC TLM utilities.
Definition cxs_tlm.h:544
SystemC TLM.
Definition dmi_mgr.h:19
void reset()
Constructor with memory management interface.
The SystemC Transaction Level Model (TLM) Network TLM utilities.