quic/qbox
Loading...
Searching...
No Matches
remote.h
1/*
2 * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#ifndef _GREENSOCS_BASE_COMPONENTS_REMOTE_H
8#define _GREENSOCS_BASE_COMPONENTS_REMOTE_H
9
10#include <cci_configuration>
11#include <systemc>
12#include <tlm>
13#include <scp/report.h>
14
15#include <tlm_utils/simple_initiator_socket.h>
16#include <tlm_utils/simple_target_socket.h>
17#include <tlm_utils/multi_passthrough_initiator_socket.h>
18#include <tlm_utils/multi_passthrough_target_socket.h>
19
20#include <libgsutils.h>
21#include <uutils.h>
22#include <libgssync.h>
23#include <cciutils.h>
24#include <ports/initiator-signal-socket.h>
25#include <ports/target-signal-socket.h>
26#include <tlm-extensions/shmem_extension.h>
27#include <module_factory_registery.h>
28#include <module_factory_container.h>
29#include <iomanip>
30#include <unistd.h>
31#include <condition_variable>
32#include <mutex>
33#include <thread>
34#include <algorithm>
35#include <list>
36#include <vector>
37#include <atomic>
38#include <future>
39#include <queue>
40#include <utility>
41#include <type_traits>
42#include <chrono>
43#include <memory_services.h>
44
45#include <rpc/client.h>
46#include <rpc/rpc_error.h>
47#include <rpc/server.h>
48#include <rpc/this_handler.h>
49#include <rpc/this_session.h>
50#include <async_event.h>
51#include <transaction_forwarder_if.h>
52#include <tlm_sockets_buswidth.h>
53
54#define GS_Process_Server_Port "GS_Process_Server_Port"
55#define GS_Process_Server_Port_Len 22
56#define DECIMAL_PORT_NUM_STR_LEN 20
57#define DECIMAL_PID_T_STR_LEN 20
58#define RPC_TIMEOUT 500
59
60namespace gs {
61
62// #define DMICACHE switchthis on - then you need a mutex
63/* rpc pass through should pass through ONE forward connection ? */
64
65template <unsigned int BUSWIDTH = DEFAULT_TLM_BUSWIDTH>
66class PassRPC : public sc_core::sc_module, public transaction_forwarder_if<PASS>
67{
68 SCP_LOGGER();
69 using MOD = PassRPC<BUSWIDTH>;
70
71 static std::string txn_str(tlm::tlm_generic_payload& trans)
72 {
73 std::stringstream info;
74
75 const char* cmd = "unknown";
76 switch (trans.get_command()) {
77 case tlm::TLM_IGNORE_COMMAND:
78 cmd = "ignore";
79 break;
80 case tlm::TLM_WRITE_COMMAND:
81 cmd = "write";
82 break;
83 case tlm::TLM_READ_COMMAND:
84 cmd = "read";
85 break;
86 }
87
88 info << " " << cmd << " to address "
89 << "0x" << std::hex << trans.get_address();
90
91 info << " len:" << trans.get_data_length();
92 unsigned char* ptr = trans.get_data_ptr();
93 info << " returned with data 0x";
94 for (int i = trans.get_data_length(); i; i--) {
95 info << std::setw(2) << std::setfill('0') << std::hex << (unsigned int)(ptr[i - 1]);
96 }
97 info << " status:" << trans.get_response_status() << " ";
98 if (trans.is_dmi_allowed()) info << " DMI OK ";
99 for (int i = 0; i < tlm::max_num_extensions(); i++) {
100 if (trans.get_extension(i)) {
101 info << " extn " << i;
102 }
103 }
104 return info.str();
105 }
106
107 using str_pairs = std::vector<std::pair<std::string, std::string>>;
108#ifdef DMICACHE
109 /* Handle local DMI cache */
110
111 std::map<uint64_t, tlm::tlm_dmi> m_dmi_cache;
112 tlm::tlm_dmi* in_cache(uint64_t address)
113 {
114 if (m_dmi_cache.size() > 0) {
115 auto it = m_dmi_cache.upper_bound(address);
116 if (it != m_dmi_cache.begin()) {
117 it = std::prev(it);
118 if ((address >= it->second.get_start_address()) && (address <= it->second.get_end_address())) {
119 return &(it->second);
120 }
121 }
122 }
123 return nullptr;
124 }
125 void cache_clean(uint64_t start, uint64_t end)
126 {
127 auto it = m_dmi_cache.upper_bound(start);
128
129 if (it != m_dmi_cache.begin()) {
130 /*
131 * Start with the preceding region, as it may already cross the
132 * range we must invalidate.
133 */
134 it--;
135 }
136
137 while (it != m_dmi_cache.end()) {
138 tlm::tlm_dmi& r = it->second;
139
140 if (r.get_start_address() > end) {
141 /* We've got out of the invalidation range */
142 break;
143 }
144
145 if (r.get_end_address() < start) {
146 /* We are not in yet */
147 it++;
148 continue;
149 }
150 it = m_dmi_cache.erase(it);
151 }
152 }
153#endif
154 /* RPC structure for TLM_DMI */
155 struct tlm_dmi_rpc {
156 std::string m_shmem_fn;
157 size_t m_shmem_size;
158 uint64_t m_shmem_offset;
159
160 uint64_t m_dmi_start_address;
161 uint64_t m_dmi_end_address;
162 int m_dmi_access; /*tlm::tlm_dmi::dmi_access_e */
163 double m_dmi_read_latency;
164 double m_dmi_write_latency;
165
166 MSGPACK_DEFINE_ARRAY(m_shmem_fn, m_shmem_size, m_shmem_offset, m_dmi_start_address, m_dmi_end_address,
167 m_dmi_access, m_dmi_read_latency, m_dmi_write_latency);
168
169 void from_tlm(tlm::tlm_dmi& other, ShmemIDExtension* shm)
170 {
171 m_shmem_fn = shm->m_memid;
172 m_shmem_size = shm->m_size;
173 m_shmem_offset = (uint64_t)(other.get_dmi_ptr()) - shm->m_mapped_addr;
174 m_dmi_start_address = other.get_start_address();
175 m_dmi_end_address = other.get_end_address();
176 m_dmi_access = other.get_granted_access();
177 m_dmi_read_latency = other.get_read_latency().to_seconds();
178 m_dmi_write_latency = other.get_write_latency().to_seconds();
179 }
180
181 void to_tlm(tlm::tlm_dmi& other)
182 {
183 if (m_shmem_size == 0) return;
184 other.set_dmi_ptr(m_shmem_offset + MemoryServices::get().map_mem_join(m_shmem_fn.c_str(), m_shmem_size));
185 other.set_start_address(m_dmi_start_address);
186 other.set_end_address(m_dmi_end_address);
187 other.set_granted_access((tlm::tlm_dmi::dmi_access_e)m_dmi_access);
188 other.set_read_latency(sc_core::sc_time(m_dmi_read_latency, sc_core::SC_SEC));
189 other.set_write_latency(sc_core::sc_time(m_dmi_write_latency, sc_core::SC_SEC));
190 }
191 };
192
193 struct tlm_generic_payload_rpc {
194 sc_dt::uint64 m_address;
195 int m_command;
196 unsigned int m_length;
197 int m_response_status;
198 bool m_dmi;
199 unsigned int m_byte_enable_length;
200 unsigned int m_streaming_width;
201 int m_gp_option;
202
203 double m_sc_time;
204 double m_quantum_time;
205
206 std::vector<unsigned char> m_data;
207 std::vector<unsigned char> m_byte_enable;
208 // extensions will not be carried
209 MSGPACK_DEFINE_ARRAY(m_address, m_command, m_length, m_response_status, m_dmi, m_byte_enable_length,
210 m_streaming_width, m_gp_option, m_sc_time, m_quantum_time, m_data, m_byte_enable);
211
212 void from_tlm(tlm::tlm_generic_payload& other)
213 {
214 m_command = other.get_command();
215 m_address = other.get_address();
216 m_length = other.get_data_length();
217 m_response_status = other.get_response_status();
218 m_byte_enable_length = other.get_byte_enable_length();
219 m_streaming_width = other.get_streaming_width();
220 m_gp_option = other.get_gp_option();
221 m_dmi = other.is_dmi_allowed();
222 unsigned char* data_ptr = other.get_data_ptr();
223 if (m_length && data_ptr) {
224 m_data.resize(m_length);
225 std::copy(data_ptr, data_ptr + m_length, m_data.begin());
226 } else {
227 m_length = 0;
228 m_data.clear();
229 }
230 unsigned char* byte_enable_ptr = other.get_byte_enable_ptr();
231 if (m_byte_enable_length && byte_enable_ptr) {
232 m_byte_enable.resize(m_byte_enable_length);
233 std::copy(byte_enable_ptr, byte_enable_ptr + m_byte_enable_length, m_byte_enable.begin());
234 } else {
235 m_byte_enable_length = 0;
236 m_byte_enable.clear();
237 }
238 }
239 void deep_copy_to_tlm(tlm::tlm_generic_payload& other)
240 {
241 other.set_command((tlm::tlm_command)(m_command));
242 other.set_address(m_address);
243 other.set_data_length(m_length);
244 other.set_response_status((tlm::tlm_response_status)(m_response_status));
245 other.set_byte_enable_length(m_byte_enable_length);
246 other.set_streaming_width(m_streaming_width);
247 other.set_gp_option((tlm::tlm_gp_option)(m_gp_option));
248 other.set_dmi_allowed(m_dmi);
249 if (!m_length) {
250 other.set_data_ptr(nullptr);
251 } else {
252 other.set_data_ptr(reinterpret_cast<unsigned char*>(m_data.data()));
253 }
254 if (!m_byte_enable_length) {
255 other.set_byte_enable_ptr(nullptr);
256 } else {
257 other.set_byte_enable_ptr(reinterpret_cast<unsigned char*>(m_byte_enable.data()));
258 }
259 }
260
261 void update_to_tlm(tlm::tlm_generic_payload& other)
262 {
263 tlm::tlm_generic_payload tmp; // make use of TLM's built in update
264 if (!m_length) {
265 tmp.set_data_ptr(nullptr);
266 } else {
267 tmp.set_data_ptr(reinterpret_cast<unsigned char*>(m_data.data()));
268 }
269
270 if (!m_byte_enable_length) {
271 other.set_byte_enable_ptr(nullptr);
272 } else {
273 tmp.set_byte_enable_ptr(reinterpret_cast<unsigned char*>(m_byte_enable.data()));
274 }
275 tmp.set_data_length(m_length);
276 tmp.set_byte_enable_length(m_byte_enable_length);
277 tmp.set_response_status((tlm::tlm_response_status)m_response_status);
278 tmp.set_dmi_allowed(m_dmi);
279 other.update_original_from(tmp, m_byte_enable_length > 0);
280 }
281 };
282
283 cci::cci_broker_handle m_broker;
284 str_pairs m_cci_db;
285 std::mutex m_cci_db_mut;
286
287 class initiator_socket_spying
288 : public tlm_utils::simple_initiator_socket_b<MOD, BUSWIDTH, tlm::tlm_base_protocol_types,
289 sc_core::SC_ZERO_OR_MORE_BOUND>
290 {
291 using socket_type = tlm_utils::simple_initiator_socket_b<MOD, BUSWIDTH, tlm::tlm_base_protocol_types,
292 sc_core::SC_ZERO_OR_MORE_BOUND>;
293 using typename socket_type::base_target_socket_type; // tlm_utils::simple_initiator_socket<MOD,
294 // BUSWIDTH>::base_target_socket_type;
295
296 const std::function<void(std::string)> register_cb;
297
298 public:
299 initiator_socket_spying(const char* name, const std::function<void(std::string)>& f)
300 : socket_type::simple_initiator_socket_b(name), register_cb(f)
301 {
302 }
303
304 void bind(base_target_socket_type& socket)
305 {
306 socket_type::bind(socket);
307 register_cb(socket.get_base_export().name());
308 }
309
310 // hierarchial binding
311 void bind(tlm::tlm_initiator_socket<BUSWIDTH>& socket)
312 {
313 socket_type::bind(socket);
314 register_cb(socket.get_base_port().name());
315 }
316 };
317
318 /* NB use the EXPORT name, so as not to be hassled by the _port_0*/
319 std::string nameFromSocket(std::string s) { return s; }
320
321 void remote_register_boundto(std::string s) { SCP_DEBUG(()) << "Binding: " << s; }
322
323public:
324 // NB there is a 'feature' in multi passthrough sockets, the 'name' is always returned as the
325 // name of the socket itself, in our case "target_socket_0". This means that address lookup will
326 // only work for the FIRST such socket, all others will require a 'pass' or should be driven
327 // from models that dont use the CCI address map info.
328 // We can fix this using a template and vector of sockets....
329 using tlm_target_socket = tlm_utils::simple_target_socket_tagged_b<MOD, BUSWIDTH, tlm::tlm_base_protocol_types,
330 sc_core::SC_ZERO_OR_MORE_BOUND>;
331
332 sc_core::sc_vector<tlm_target_socket> target_sockets;
333 sc_core::sc_vector<initiator_socket_spying> initiator_sockets;
334
335 sc_core::sc_vector<InitiatorSignalSocket<bool>> initiator_signal_sockets;
336 sc_core::sc_vector<TargetSignalSocket<bool>> target_signal_sockets;
337
338 cci::cci_param<int> p_cport;
339 cci::cci_param<int> p_sport;
340 cci::cci_param<std::string> p_exec_path;
341 bool m_is_local;
342 cci::cci_param<std::string> p_sync_policy;
343 cci::cci_param<uint32_t> p_tlm_initiator_ports_num;
344 cci::cci_param<uint32_t> p_tlm_target_ports_num;
345 cci::cci_param<uint32_t> p_initiator_signals_num;
346 cci::cci_param<uint32_t> p_target_signals_num;
347
348private:
349 rpc::client* client = nullptr;
350 rpc::server* server = nullptr;
351 int m_child_pid = 0;
352 ProcAliveHandler pahandler;
353 std::condition_variable is_client_connected;
354 std::condition_variable is_sc_status_set;
355 std::mutex client_conncted_mut;
356 std::mutex sc_status_mut;
357 std::mutex stop_mutex;
358 std::atomic_bool cancel_waiting;
359 std::thread::id sc_tid;
360 std::queue<std::pair<int, bool>> sig_queue;
361 std::mutex sig_queue_mut;
362 std::vector<const char*> m_remote_args;
363 sc_core::sc_status m_remote_status = static_cast<sc_core::sc_status>(0);
364
365 int targets_bound = 0;
366
367 // std::shared_ptr<gs::tlm_quantumkeeper_extended> m_qk;
368 gs::runonsysc m_sc;
370
371 class trans_waiter
372 {
373 private:
374 std::string m_name;
375 std::queue<std::function<void()>> notifiers;
376 std::thread notifier_thread;
377 std::atomic_bool is_stopped;
378 std::atomic_bool is_started;
379
380 public:
381 std::vector<gs::async_event> data_ready_events;
382 std::vector<sc_core::sc_event> port_available_events;
383 std::vector<bool> is_port_busy;
384 std::condition_variable is_rpc_execed;
385 std::mutex start_stop_mutex;
386 std::mutex rpc_execed_mut;
393 private:
394 void notifier_task()
395 {
396 while (!is_stopped) {
397 std::unique_lock<std::mutex> ul(rpc_execed_mut);
398 is_rpc_execed.wait_for(ul, std::chrono::milliseconds(RPC_TIMEOUT),
399 [this]() { return (!notifiers.empty() || is_stopped); });
400 while (!notifiers.empty()) {
401 notifiers.front()();
402 notifiers.pop();
403 }
404 ul.unlock();
405 }
406 }
407
408 public:
409 trans_waiter(std::string p_name, uint32_t ev_num)
410 : m_name(p_name)
411 , data_ready_events(ev_num)
412 , port_available_events(ev_num)
413 , is_port_busy(ev_num, false)
414 , is_stopped(false)
415 , is_started(false)
416 {
417 }
418 void start()
419 {
420 {
421 std::lock_guard<std::mutex> start_lg(start_stop_mutex);
422 if (is_started) return;
423 is_started = true;
424 }
425
426 notifier_thread = std::thread(&trans_waiter::notifier_task, this);
427 }
428
429 void stop()
430 {
431 {
432 std::lock_guard<std::mutex> stop_lg(start_stop_mutex);
433 if (is_stopped || !is_started) return;
434 is_stopped = true;
435 }
436 {
437 std::lock_guard<std::mutex> lg(rpc_execed_mut);
438 is_rpc_execed.notify_one();
439 }
440
441 if (notifier_thread.joinable()) notifier_thread.join();
442 }
443
444 ~trans_waiter() { stop(); }
445
446 void enqueue_notifier(std::function<void()> notifier) { notifiers.push(notifier); }
447 };
448
449 std::unique_ptr<trans_waiter> btspt_waiter;
450
451 template <typename... Args>
452 std::future<RPCLIB_MSGPACK::object_handle> do_rpc_async_call(std::string const& func_name, Args... args)
453 {
454 if (!client) {
455 stop_and_exit();
456 }
457 std::future<RPCLIB_MSGPACK::object_handle> ret;
458 try {
459 ret = client->async_call(func_name, std::forward<Args>(args)...);
460 } catch (const std::future_error& e) {
461 SCP_DEBUG(()) << name() << " PassRPC::do_rpc_async_call() Connection with remote is closed: " << e.what();
462 stop_and_exit();
463 } catch (...) {
464 SCP_DEBUG(()) << name() << " PassRPC::do_rpc_async_call() Unknown error!";
465 stop_and_exit();
466 }
467 return ret;
468 }
469
470 template <typename T>
471 T do_rpc_async_get(std::future<RPCLIB_MSGPACK::object_handle> fut)
472 {
473 T ret;
474 try {
475 ret = do_rpc_as<T>(fut.get());
476 } catch (const std::future_error& e) {
477 SCP_DEBUG(()) << name() << " PassRPC::do_rpc_async_get() RPC future is corrupted: " << e.what();
478 stop_and_exit();
479 } catch (...) {
480 SCP_DEBUG(()) << name() << " PassRPC::do_rpc_async_get() Unknown error!";
481 stop_and_exit();
482 }
483 return ret;
484 }
485
486 template <typename T>
487 T do_rpc_as(RPCLIB_MSGPACK::object_handle handle)
488 {
489 T ret;
490 try {
491 ret = handle.template as<T>();
492 } catch (...) {
493 SCP_DEBUG(()) << name() << " PassRPC::do_rpc_as() RPC remote value is corrupted!";
494 stop_and_exit();
495 }
496 return ret;
497 }
498
499 template <typename... Args>
500 RPCLIB_MSGPACK::object_handle do_rpc_call(std::string const& func_name, Args... args)
501 {
502 if (!client) {
503 stop_and_exit();
504 }
505 RPCLIB_MSGPACK::object_handle ret;
506 try {
507 ret = client->call(func_name, std::forward<Args>(args)...);
508 }
509
510 catch (const std::future_error& e) {
511 SCP_DEBUG(()) << name() << " PassRPC::do_rpc_call() Connection with remote is closed: " << e.what();
512 stop_and_exit();
513 } catch (...) {
514 SCP_DEBUG(()) << name() << " PassRPC::do_rpc_call() Unknown error!";
515 stop_and_exit();
516 }
517
518 return ret;
519 }
520
521 bool is_local_mode() { return (m_container && m_is_local); }
522
523 void fw_b_transport(int id, tlm::tlm_generic_payload& trans, sc_core::sc_time& delay) override
524 {
525 SCP_DEBUG(()) << "calling b_transport on initiator_socket_" << id << " " << scp::scp_txn_tostring(trans);
526 initiator_sockets[id]->b_transport(trans, delay);
527 SCP_DEBUG(()) << "return from b_transport on initiator_socket_" << id << " " << scp::scp_txn_tostring(trans);
528 }
529
530 unsigned int fw_transport_dbg(int id, tlm::tlm_generic_payload& trans) override
531 {
532 SCP_DEBUG(()) << "calling transport_dbg on initiator_socket_" << id << " " << scp::scp_txn_tostring(trans);
533 unsigned int ret = initiator_sockets[id]->transport_dbg(trans);
534 SCP_DEBUG(()) << "return from transport_dbg on initiator_socket_" << id << " " << scp::scp_txn_tostring(trans);
535 return ret;
536 }
537
538 bool fw_get_direct_mem_ptr(int id, tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data) override
539 {
540 SCP_DEBUG(()) << "calling get_direct_mem_ptr on initiator_socket_" << id << " " << scp::scp_txn_tostring(trans);
541 bool ret = initiator_sockets[id]->get_direct_mem_ptr(trans, dmi_data);
542 SCP_DEBUG(()) << "return from get_direct_mem_ptr on initiator_socket_" << id << " "
543 << " RET: " << std::boolalpha << ret << " " << scp::scp_txn_tostring(trans)
544 << " IS_READ_ALLOWED: " << std::boolalpha << dmi_data.is_read_allowed() << " "
545 << " IS_WRITE_ALLOWED: " << std::boolalpha << dmi_data.is_write_allowed();
546 return ret;
547 }
548
549 void fw_invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end) override
550 {
551 SCP_DEBUG(()) << " " << name() << " invalidate_direct_mem_ptr "
552 << " start address 0x" << std::hex << start << " end address 0x" << std::hex << end;
553 for (int i = 0; i < target_sockets.size(); i++) {
554 target_sockets[i]->invalidate_direct_mem_ptr(start, end);
555 }
556 }
557
558 void fw_handle_signal(int id, bool value) override
559 {
560 SCP_DEBUG(()) << "calling handle_signal on initiator_signal_socket_" << id << " value: " << std::boolalpha
561 << value;
562 initiator_signal_sockets[id]->write(value);
563 }
564
565 /* b_transport interface */
566 void b_transport(int id, tlm::tlm_generic_payload& trans, sc_core::sc_time& delay)
567 {
568 if (is_local_mode()) {
569 m_container->fw_b_transport(id, trans, delay);
570 return;
571 }
572
573 while (btspt_waiter->is_port_busy[id]) {
574 sc_core::wait(btspt_waiter->port_available_events[id]);
575 }
576 btspt_waiter->is_port_busy[id] = true;
577
578 tlm_generic_payload_rpc t;
579 tlm_generic_payload_rpc r;
580 double time = sc_core::sc_time_stamp().to_seconds();
581
582 uint64_t addr = trans.get_address();
583 // If we have a locally cached DMI, use it!
584#ifdef DMICACHE
585 auto c = in_cache(addr);
586 if (c) {
587 uint64_t len = trans.get_data_length();
588 if (addr >= c->get_start_address() && addr + len <= c->get_end_address()) {
589 switch (trans.get_command()) {
590 case tlm::TLM_IGNORE_COMMAND:
591 break;
592 case tlm::TLM_WRITE_COMMAND:
593 memcpy(c->get_dmi_ptr() + (addr - c->get_start_address()), trans.get_data_ptr(), len);
594 break;
595 case tlm::TLM_READ_COMMAND:
596 memcpy(trans.get_data_ptr(), c->get_dmi_ptr() + (addr - c->get_start_address()), len);
597 break;
598 }
599 trans.set_dmi_allowed(true);
600 trans.set_response_status(tlm::TLM_OK_RESPONSE);
601 return;
602 }
603 }
604#endif
605 t.from_tlm(trans);
606 t.m_quantum_time = delay.to_seconds();
607 t.m_sc_time = sc_core::sc_time_stamp().to_seconds();
614 if (std::this_thread::get_id() == sc_tid && sc_core::sc_get_status() >= sc_core::sc_status::SC_RUNNING &&
615 sc_core::sc_get_curr_process_kind() != sc_core::sc_curr_proc_kind::SC_NO_PROC_) {
616 SCP_DEBUG(()) << name() << " B_TSPT handle reentrancy, sc_get_curr_simcontext " << sc_get_curr_simcontext()
617 << " SC current process kind = " << sc_core::sc_get_curr_process_kind();
618 btspt_waiter->start();
619
620 std::unique_lock<std::mutex> ul(btspt_waiter->rpc_execed_mut);
621 btspt_waiter->enqueue_notifier([&]() {
622 r = do_rpc_as<tlm_generic_payload_rpc>(do_rpc_call("b_tspt", id, t));
623 btspt_waiter->data_ready_events[id].async_notify();
624 });
625 btspt_waiter->is_rpc_execed.notify_one();
626 ul.unlock();
627 SCP_DEBUG(()) << name() << " B_TSPT wait for event, sc_get_curr_simcontext " << sc_get_curr_simcontext()
628 << " SC current process kind = " << sc_core::sc_get_curr_process_kind();
629 if (sc_core::sc_get_curr_process_kind() != sc_core::sc_curr_proc_kind::SC_METHOD_PROC_) {
630 sc_core::wait(btspt_waiter->data_ready_events[id]); // systemc wait
631 } else {
632 SCP_FATAL(()) << name() << " b_transport was called from the context of SC_METHOD!";
633 }
634 } else {
635 r = do_rpc_as<tlm_generic_payload_rpc>(do_rpc_call("b_tspt", id, t));
636 }
637
638 r.update_to_tlm(trans);
639 delay = sc_core::sc_time(r.m_quantum_time, sc_core::SC_SEC);
640 sc_core::sc_time other_time = sc_core::sc_time(r.m_sc_time, sc_core::SC_SEC);
641 btspt_waiter->is_port_busy[id] = false;
642 btspt_waiter->port_available_events[id].notify(sc_core::SC_ZERO_TIME);
643 }
644 tlm_generic_payload_rpc b_transport_rpc(int id, tlm_generic_payload_rpc t)
645 {
646 tlm::tlm_generic_payload trans;
647 t.deep_copy_to_tlm(trans);
648 sc_core::sc_time delay = sc_core::sc_time(t.m_quantum_time, sc_core::SC_SEC);
649 sc_core::sc_time other_time = sc_core::sc_time(t.m_sc_time, sc_core::SC_SEC);
650
651 m_sc.run_on_sysc([&] { initiator_sockets[id]->b_transport(trans, delay); });
652 t.from_tlm(trans);
653 t.m_quantum_time = delay.to_seconds();
654 return t;
655 }
656
657 /* Debug transport interface */
658 unsigned int transport_dbg(int id, tlm::tlm_generic_payload& trans)
659 {
660 if (is_local_mode()) {
661 return m_container->fw_transport_dbg(id, trans);
662 }
663 SCP_DEBUG(()) << name() << " ->remote debug tlm " << txn_str(trans);
664 tlm_generic_payload_rpc t;
665 tlm_generic_payload_rpc r;
666
667 t.from_tlm(trans);
668 r = do_rpc_as<tlm_generic_payload_rpc>(do_rpc_call("dbg_tspt", id, t));
669 r.update_to_tlm(trans);
670 SCP_DEBUG(()) << name() << " <-remote debug tlm done " << txn_str(trans);
671 // this is not entirely accurate, but see below
672 return trans.get_response_status() == tlm::TLM_OK_RESPONSE ? trans.get_data_length() : 0;
673 }
674 tlm_generic_payload_rpc transport_dbg_rpc(int id, tlm_generic_payload_rpc t)
675 {
676 tlm::tlm_generic_payload trans;
677 t.deep_copy_to_tlm(trans);
678 int ret_len;
679 SCP_DEBUG(()) << name() << " remote-> debug tlm " << txn_str(trans);
680 ret_len = initiator_sockets[id]->transport_dbg(trans);
681 t.from_tlm(trans);
682 SCP_DEBUG(()) << name() << " remote<- debug tlm done " << txn_str(trans);
683
684 if (!(trans.get_data_length() == ret_len || trans.get_response_status() != tlm::TLM_OK_RESPONSE)) {
685 assert(false);
686 SCP_WARN(()) << "debug transaction not able to access required length of data.";
687 }
688 return t;
689 }
690
691 bool get_direct_mem_ptr(int id, tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data)
692 {
693 if (is_local_mode()) {
694 return m_container->fw_get_direct_mem_ptr(id, trans, dmi_data);
695 }
696 tlm::tlm_dmi* c;
697 SCP_DEBUG(()) << " " << name() << " get_direct_mem_ptr to address "
698 << "0x" << std::hex << trans.get_address();
699
700#ifdef DMICACHE
701 c = in_cache(trans.get_address());
702 if (c) {
703 dmi_data = *c;
704 return !(dmi_data.is_none_allowed());
705 }
706#endif
707 tlm_generic_payload_rpc t;
708 tlm_dmi_rpc r;
709 t.from_tlm(trans);
710 r = do_rpc_as<tlm_dmi_rpc>(do_rpc_call("dmi_req", id, t));
711
712 if (r.m_shmem_size == 0) {
713 SCP_DEBUG(()) << name() << "DMI OK, but no shared memory available?" << trans.get_address();
714 return false;
715 }
716 r.to_tlm(dmi_data);
717#ifdef DMICACHE
718 assert(m_dmi_cache.count(dmi_data.get_start_address()) == 0);
719 m_dmi_cache[dmi_data.get_start_address()] = dmi_data;
720#endif
721 return !(dmi_data.is_none_allowed());
722 }
723
724 tlm_dmi_rpc get_direct_mem_ptr_rpc(int id, tlm_generic_payload_rpc t)
725 {
726 tlm::tlm_generic_payload trans;
727 t.deep_copy_to_tlm(trans);
728
729 SCP_DEBUG(()) << " " << name() << " get_direct_mem_ptr " << txn_str(trans);
730
731 tlm::tlm_dmi dmi_data;
732 tlm_dmi_rpc ret;
733 ret.m_shmem_size = 0;
734 if (initiator_sockets[id]->get_direct_mem_ptr(trans, dmi_data)) {
735 ShmemIDExtension* ext = trans.get_extension<ShmemIDExtension>();
736 if (!ext) return ret;
737 ret.from_tlm(dmi_data, ext);
738 }
739 return ret;
740 }
741
742 /* Invalidate DMI Interface */
743 void invalidate_direct_mem_ptr(sc_dt::uint64 start, sc_dt::uint64 end)
744 {
745 if (is_local_mode()) {
746 m_container->fw_invalidate_direct_mem_ptr(start, end);
747 }
748 SCP_DEBUG(()) << " " << name() << " invalidate_direct_mem_ptr "
749 << " start address 0x" << std::hex << start << " end address 0x" << std::hex << end;
750 do_rpc_async_call("dmi_inv", start, end);
751 }
752 void invalidate_direct_mem_ptr_rpc(sc_dt::uint64 start, sc_dt::uint64 end)
753 {
754 SCP_DEBUG(()) << " " << name() << " invalidate_direct_mem_ptr "
755 << " start address 0x" << std::hex << start << " end address 0x" << std::hex << end;
756#ifdef DMICACHE
757 cache_clean(start, end);
758#endif
759 for (int i = 0; i < target_sockets.size(); i++) {
760 target_sockets[i]->invalidate_direct_mem_ptr(start, end);
761 }
762 }
763
767 bool is_self_param(const std::string& parent, const std::string& parname, const std::string& value)
768 {
769 std::vector<std::string> match_words = {
770 "args", "moduletype", "initiator_socket", "target_socket", "initiator_signal_socket", "target_signal_socket"
771 };
772 return (std::find_if(match_words.begin(), match_words.end(), [parent, parname, value](std::string entry) {
773 std::string search_str = parent + "." + entry;
774 if (entry == "moduletype" && value == "\"RemotePass\"") search_str = entry;
775 return parname.find(search_str) != std::string::npos;
776 }) != match_words.end());
777 }
778
779 str_pairs get_cci_db()
780 {
781 std::string parent = std::string(name());
782 str_pairs ret;
783 for (auto p : m_broker.get_unconsumed_preset_values()) {
784 std::string name = p.first;
785 std::string k = p.first;
786 if (k.find(parent) == 0) {
787 if (is_self_param(parent, k, p.second.to_json())) continue;
788 k = k.substr(parent.length() + 1);
789 ret.push_back(std::make_pair("$" + k, p.second.to_json()));
790 // mark parameters in the remote as 'used'
791 m_broker.ignore_unconsumed_preset_values(
792 [name](const std::pair<std::string, cci::cci_value>& iv) -> bool { return iv.first == name; });
793 } else if (k.find('.') == std::string::npos) {
794 ret.push_back(std::make_pair(k, p.second.to_json()));
795 }
796 }
797 return ret;
798 }
799
800 void set_cci_db(str_pairs db)
801 {
802 std::string modname = std::string(name());
803 std::string parent = std::string(modname).substr(0, modname.find_last_of("."));
804
805 for (auto p : db) {
806 std::string parname;
807 if (p.first[0] == '$') {
808 parname = parent + "." + p.first.substr(1);
809 } else if (p.first[0] == '@') {
810 parname = modname + "." + p.first.substr(1);
811 } else {
812 parname = p.first;
813 }
814 auto handle = m_broker.get_param_handle(parname);
815 if (handle.is_valid())
816 handle.set_cci_value(cci::cci_value::from_json(p.second));
817 else
818 m_broker.set_preset_cci_value(parname, cci::cci_value::from_json(p.second));
819
820 SCP_DEBUG(()) << "Setting " << parname << " to " << p.second;
821 }
822 }
823
824 std::vector<std::string> get_extra_argv()
825 {
826 std::vector<std::string> argv;
827 std::list<std::string> argv_cci_children = gs::sc_cci_children((std::string(name()) + ".remote_argv").c_str());
828 if (!argv_cci_children.empty())
829 std::transform(argv_cci_children.begin(), argv_cci_children.end(), std::back_inserter(argv),
830 [this](const std::string& arg) {
831 std::string arg_full_name = (std::string(name()) + ".remote_argv." + arg);
832 return gs::cci_get<std::string>(cci::cci_get_broker(), arg_full_name);
833 });
834 return argv;
835 }
836
837public:
838 PassRPC(const sc_core::sc_module_name& nm, bool is_local = false)
839 : sc_core::sc_module(nm)
840 , m_broker(cci::cci_get_broker())
841 , initiator_sockets("initiator_socket")
842 , target_sockets("target_socket")
843 , initiator_signal_sockets("initiator_signal_socket")
844 , target_signal_sockets("target_signal_socket")
845 , p_cport("client_port", 0,
846 "The port that should be used to connect this client to the "
847 "remote server")
848 , p_sport("server_port", 0, "The port that should be used to server on")
849 , p_exec_path("exec_path", "",
850 "The path to the executable that should be started by "
851 "the bridge")
852 , m_is_local(is_local)
853 , p_sync_policy("sync_policy", "multithread-unconstrained", "Sync policy for the remote")
854 , p_tlm_initiator_ports_num("tlm_initiator_ports_num", 0, "number of tlm initiator ports")
855 , p_tlm_target_ports_num("tlm_target_ports_num", 0, "number of tlm target ports")
856 , p_initiator_signals_num("initiator_signals_num", 0, "number of initiator signals")
857 , p_target_signals_num("target_signals_num", 0, "number of target signals")
858 , cancel_waiting(false)
859 {
860 SigHandler::get().add_sig_handler(SIGINT, SigHandler::Handler_CB::PASS);
861 SigHandler::get().register_on_exit_cb(std::string(name()) + ".gs::PassRPC::stop", [this]() { stop(); });
862 sc_tid = std::this_thread::get_id();
863 SCP_DEBUG(()) << "PassRPC constructor";
864 m_container = dynamic_cast<gs::ModuleFactory::ContainerBase*>(get_parent_object());
865 if (is_local_mode()) {
866 SCP_DEBUG(()) << "Working in LOCAL mode!";
867 } else {
868 SCP_DEBUG(()) << getpid() << " IS THE RPC PID " << std::this_thread::get_id() << " is the thread ID";
869 // always serve on a new port.
870 server = new rpc::server(p_sport);
871 server->suppress_exceptions(true);
872 p_sport = server->port();
873 assert(p_sport > 0);
874
875 if (p_cport.get_value() == 0 &&
876 getenv((std::string(GS_Process_Server_Port) + std::to_string(getpid())).c_str())) {
877 p_cport = std::stoi(
878 std::string(getenv((std::string(GS_Process_Server_Port) + std::to_string(getpid())).c_str())));
879 }
880
881 // other end contacted us, connect to their port
882 // and return back the cci database
883 server->bind("reg", [&](int port) {
884 SCP_DEBUG(()) << "reg " << name() << " pid: " << getpid();
885 assert(p_cport == 0 && client == nullptr);
886 p_cport = port;
887 if (!client) client = new rpc::client("localhost", p_cport);
888 std::unique_lock<std::mutex> ul(client_conncted_mut);
889 is_client_connected.notify_one();
890 ul.unlock();
891 // we are not interested in the return future from async_call
892 do_rpc_async_call("sock_pair", pahandler.get_sockpair_fd0(), pahandler.get_sockpair_fd1());
893 return get_cci_db();
894 });
895
896 // would it be better to have a 'remote cci broker' that connected back,
897
898 server->bind("cci_db", [&](str_pairs db) {
899 if (sc_core::sc_get_status() > sc_core::sc_status::SC_BEFORE_END_OF_ELABORATION) {
900 std::cerr << "Attempt to do cci_db() RPC after "
901 "sc_core::sc_status::SC_BEFORE_END_OF_ELABORATION"
902 << std::endl;
903 exit(1);
904 }
905 std::lock_guard<std::mutex> lg(m_cci_db_mut);
906 m_cci_db.insert(m_cci_db.end(), db.begin(), db.end());
907 return;
908 });
909
910 server->bind("status", [&](int s) {
911 SCP_DEBUG(()) << "SIMULATION STATE " << name() << " to status " << s;
912 assert(s > m_remote_status);
913 m_remote_status = static_cast<sc_core::sc_status>(s);
914 std::lock_guard<std::mutex> lg(sc_status_mut);
915 is_sc_status_set.notify_one();
916 return;
917 });
918
919 server->bind("b_tspt", [&](int id, tlm_generic_payload_rpc txn) {
920 try {
921 return PassRPC::b_transport_rpc(id, txn);
922 } catch (std::runtime_error const& e) {
923 std::cerr << "Main Error: '" << e.what() << "'\n";
924 exit(1);
925 } catch (const std::exception& exc) {
926 std::cerr << "main Error: '" << exc.what() << "'\n";
927 exit(1);
928 } catch (...) {
929 std::cerr << "Unknown error (main.cc)!\n";
930 exit(1);
931 }
932 });
933
934 server->bind("dbg_tspt", [&](int id, tlm_generic_payload_rpc txn) {
935 SCP_DEBUG(()) << "Got DBG Tspt";
936 return PassRPC::transport_dbg_rpc(id, txn);
937 });
938
939 server->bind("dmi_inv", [&](uint64_t start, uint64_t end) {
940 return PassRPC::invalidate_direct_mem_ptr_rpc(start, end);
941 });
942
943 server->bind("dmi_req",
944 [&](int id, tlm_generic_payload_rpc txn) { return PassRPC::get_direct_mem_ptr_rpc(id, txn); });
945
946 server->bind("exit", [&](int i) {
947 SCP_DEBUG(()) << "exit " << name();
948 m_sc.run_on_sysc([&] {
949 rpc::this_session().post_exit();
950 delete client;
951 client = nullptr;
952 sc_core::sc_stop();
953 });
954 // m_qk->stop();
955 return;
956 });
957
958 server->bind("signal", [&](int i, bool v) {
959 if (sc_core::sc_get_status() < sc_core::sc_status::SC_START_OF_SIMULATION) {
960 std::lock_guard<std::mutex> lg(sig_queue_mut);
961 sig_queue.push(std::make_pair(i, v));
962 return;
963 }
964 m_sc.run_on_sysc([&] { initiator_signal_sockets[i]->write(v); },
965 (sc_core::sc_get_status() < sc_core::sc_status::SC_RUNNING ? false : true));
966 return;
967 });
968
969 server->bind("sock_pair", [&](int sock_fd0, int sock_fd1) {
970 pahandler.recv_sockpair_fds_from_remote(sock_fd0, sock_fd1);
971 pahandler.check_parent_conn_nth([&]() {
972 std::cerr << "remote process (" << getpid() << ") detected parent (" << pahandler.get_ppid()
973 << ") exit!" << std::endl;
974 });
975 return;
976 });
977
978 server->async_run(1);
979
980 if (p_cport) {
981 SCP_INFO(()) << "Connecting client on port " << p_cport;
982 if (!client) client = new rpc::client("localhost", p_cport);
983 set_cci_db(do_rpc_as<str_pairs>(do_rpc_call("reg", (int)p_sport)));
984 }
985 }
986
987 btspt_waiter = std::make_unique<trans_waiter>("btspt_waiter", p_tlm_target_ports_num.get_value());
988
989 initiator_sockets.init(p_tlm_initiator_ports_num.get_value(), [this](const char* n, int i) {
990 return new initiator_socket_spying(n, [&](std::string s) -> void { remote_register_boundto(s); });
991 });
992 target_sockets.init(p_tlm_target_ports_num.get_value(),
993 [this](const char* n, int i) { return new tlm_target_socket(n); });
994 initiator_signal_sockets.init(p_initiator_signals_num.get_value(),
995 [this](const char* n, int i) { return new InitiatorSignalSocket<bool>(n); });
996 target_signal_sockets.init(p_target_signals_num.get_value(),
997 [this](const char* n, int i) { return new TargetSignalSocket<bool>(n); });
998
999 for (int i = 0; i < p_tlm_target_ports_num.get_value(); i++) {
1000 target_sockets[i].register_b_transport(this, &PassRPC::b_transport, i);
1001 target_sockets[i].register_transport_dbg(this, &PassRPC::transport_dbg, i);
1002 target_sockets[i].register_get_direct_mem_ptr(this, &PassRPC::get_direct_mem_ptr, i);
1003 }
1004
1005 for (int i = 0; i < p_tlm_initiator_ports_num.get_value(); i++) {
1006 initiator_sockets[i].register_invalidate_direct_mem_ptr(this, &PassRPC::invalidate_direct_mem_ptr);
1007 }
1008
1009 for (int i = 0; i < p_target_signals_num.get_value(); i++) {
1010 target_signal_sockets[i].register_value_changed_cb([&, i](bool value) {
1011 if (is_local_mode()) {
1012 m_container->fw_handle_signal(i, value);
1013 return;
1014 }
1015 do_rpc_async_call("signal", i, value);
1016 });
1017 }
1018
1019 if (!is_local_mode()) {
1020 if (!p_exec_path.get_value().empty()) {
1021 SCP_INFO(()) << "Forking remote " << p_exec_path.get_value();
1022 pahandler.init_peer_conn_checker();
1023
1024 std::vector<std::string> extra_args;
1025 extra_args = get_extra_argv();
1026 m_remote_args.reserve(extra_args.size() + 2);
1027 m_remote_args.push_back(p_exec_path.get_value().c_str());
1028 std::transform(extra_args.begin(), extra_args.end(), std::back_inserter(m_remote_args),
1029 [](const std::string& s) { return s.c_str(); });
1030 m_remote_args.push_back(0);
1031 char val[DECIMAL_PORT_NUM_STR_LEN + 1]; // can't be bigger than this.
1032 snprintf(val, DECIMAL_PORT_NUM_STR_LEN + 1, "%d", p_sport.get_value());
1033 m_child_pid = fork();
1034 if (m_child_pid > 0) {
1035 pahandler.setup_parent_conn_checker();
1036 SigHandler::get().set_nosig_chld_stop();
1037 SigHandler::get().add_sig_handler(SIGCHLD, SigHandler::Handler_CB::EXIT);
1038 } else if (m_child_pid == 0) {
1039 char key[GS_Process_Server_Port_Len + DECIMAL_PID_T_STR_LEN + 1]; // can't be bigger than this.
1040 snprintf(key, GS_Process_Server_Port_Len + DECIMAL_PID_T_STR_LEN + 1, "%s%d",
1041 GS_Process_Server_Port, getpid());
1042 setenv(key, val, 1);
1043
1044 execv(p_exec_path.get_value().c_str(), const_cast<char**>(&m_remote_args[0]));
1045
1046 SCP_FATAL(()) << "Unable to exec the remote child process, '" << p_exec_path.get_value()
1047 << "', error: " << std::strerror(errno);
1048 } else {
1049 SCP_FATAL(()) << "failed to fork remote process, error: " << std::strerror(errno);
1050 }
1051 }
1052
1053 // Make sure by now the client is connected so we can send/recieve.
1054 std::unique_lock<std::mutex> ul(client_conncted_mut);
1055 is_client_connected.wait(ul, [&]() { return (p_cport > 0 || cancel_waiting); });
1056 ul.unlock();
1057 send_status();
1058 }
1059 } // namespace gs
1060 PassRPC(const sc_core::sc_module_name& nm, int port): PassRPC(nm, "", port){}; // convenience constructor
1061
1062 void send_status()
1063 {
1064 SCP_DEBUG(()) << "SIMULATION STATE send " << name() << " to status " << sc_core::sc_get_status();
1065 do_rpc_call("status", static_cast<int>(sc_core::sc_get_status()));
1066 std::unique_lock<std::mutex> ul(sc_status_mut);
1067 is_sc_status_set.wait(ul, [&]() { return (m_remote_status >= sc_core::sc_get_status() || cancel_waiting); });
1068 ul.unlock();
1069 SCP_DEBUG(()) << "SIMULATION STATE synced " << name() << " to status " << sc_core::sc_get_status();
1070 }
1071
1072 void handle_before_sim_start_signals()
1073 {
1074 std::lock_guard<std::mutex> lg(sig_queue_mut);
1075 while (!sig_queue.empty()) {
1076 std::pair<int, bool> sig = sig_queue.front();
1077 sig_queue.pop();
1078 m_sc.run_on_sysc([&] { initiator_signal_sockets[sig.first]->write(sig.second); }, false);
1079 }
1080 }
1081
1082 void stop()
1083 {
1084 {
1085 std::lock_guard<std::mutex> lg(stop_mutex);
1086 if (cancel_waiting || is_local_mode()) return;
1087 cancel_waiting = true;
1088 }
1089 {
1090 std::lock_guard<std::mutex> cc_lg(client_conncted_mut);
1091 is_client_connected.notify_one();
1092 }
1093 {
1094 std::lock_guard<std::mutex> scs_lg(sc_status_mut);
1095 is_sc_status_set.notify_one();
1096 }
1097 btspt_waiter->stop();
1098 if (server) {
1099 server->close_sessions();
1100 server->stop();
1101 delete server;
1102 server = nullptr;
1103 }
1104 if (client) {
1105 do_rpc_async_call("exit", 0);
1106 delete client;
1107 client = nullptr;
1108 }
1109 }
1110
1111 void stop_and_exit()
1112 {
1113 stop();
1115 }
1116
1117 void before_end_of_elaboration() override
1118 {
1119 if (is_local_mode()) return;
1120 send_status();
1121 std::lock_guard<std::mutex> lg(m_cci_db_mut);
1122 set_cci_db(m_cci_db);
1123 }
1124
1125 void end_of_elaboration() override
1126 {
1127 if (is_local_mode()) return;
1128 send_status();
1129 }
1130
1131 void start_of_simulation() override
1132 {
1133 if (is_local_mode()) return;
1134 send_status();
1135 handle_before_sim_start_signals();
1136 }
1137
1138 PassRPC() = delete;
1139 PassRPC(const PassRPC&) = delete;
1140 ~PassRPC()
1141 {
1142 SigHandler::get().deregister_on_exit_cb(std::string(name()) + ".gs::PassRPC::stop");
1143 if (is_local_mode()) return;
1144 SCP_DEBUG(()) << "EXIT " << name();
1145 stop();
1146#ifdef DMICACHE
1147 m_dmi_cache.clear();
1148#endif
1149 }
1150
1151 void end_of_simulation() override
1152 {
1153 if (is_local_mode()) return;
1154 stop();
1155 }
1156}; // namespace gs
1157template <unsigned int BUSWIDTH = DEFAULT_TLM_BUSWIDTH>
1158class LocalPass : public PassRPC<>
1159{
1160public:
1161 SCP_LOGGER(());
1162 LocalPass(const sc_core::sc_module_name& n): PassRPC(n, true) { SCP_DEBUG(()) << "LocalPass constructor"; }
1163
1164 virtual ~LocalPass() = default;
1165};
1166
1167template <unsigned int BUSWIDTH = DEFAULT_TLM_BUSWIDTH>
1168class RemotePass : public PassRPC<>
1169{
1170public:
1171 SCP_LOGGER(());
1172 RemotePass(const sc_core::sc_module_name& n): PassRPC(n, false) { SCP_DEBUG(()) << "RemotePass constructor"; }
1173
1174 virtual ~RemotePass() = default;
1175};
1176
1177} // namespace gs
1178// need to be register without the dynamic library feature because of the order of execution of the code.
1181GSC_MODULE_REGISTER(LocalPass);
1182GSC_MODULE_REGISTER(RemotePass);
1183
1184#endif
Definition target.h:160
Definition remote.h:1159
Definition module_factory_container.h:50
Definition remote.h:67
Definition uutils.h:142
void check_parent_conn_nth(std::function< void()> on_parent_exit)
Definition uutils.cc:376
Definition remote.h:1169
Definition shmem_extension.h:24
Definition runonsysc.h:22
bool run_on_sysc(std::function< void()> job_entry, bool wait=true)
Run a job on the SystemC kernel thread.
Definition runonsysc.h:181
Definition transaction_forwarder_if.h:23
Tool which reads a Lua configuration file and sets parameters.
Definition biflow.cc:10
std::list< std::string > sc_cci_children(sc_core::sc_module_name name)
return a list of 'unconsumed' children from the given module name, can be used inside or outside the ...
Definition cciutils.cc:63