17#include "tcp4tlm_bridge.h"
18#include "scc/report.h"
19#include "scc/tcp4tlm/messages.h"
20#include "tlm/scc/tlm_extensions.h"
21#include "tlm/scc/tlm_gp_shared.h"
24#include <boost/asio.hpp>
30#include <sysc/kernel/sc_module.h>
31#include <sysc/kernel/sc_simcontext.h>
32#include <sysc/kernel/sc_time.h>
35#define GETCLOCK(X) clock_gettime(CLOCK_REALTIME, X)
38using namespace std::chrono_literals;
40tcp4tlm_bridge::tcp4tlm_bridge(sc_core::sc_module_name name,
size_t no_of_ports)
41: sc_core::sc_module(name)
42, tcp4tlm::server<tcp4tlm::request_message, tcp4tlm::response_message>(2)
43, signals{
"signals", no_of_ports}
45#ifdef GENERATE_STATISTICS
51 tsckt.register_b_transport(
this, &tcp4tlm_bridge::btransport_cb);
52 tsckt.register_transport_dbg(
this, &tcp4tlm_bridge::transport_dbg_cb);
53 SC_THREAD(timing_thread);
54 SC_THREAD(process_task_que);
55 SC_THREAD(process_timed_task_que);
56#ifdef GENERATE_STATISTICS
63#ifdef GENERATE_STATISTICS
64void tcp4tlm_bridge::statistics::updateStat(
unsigned long rt) {
74 int idx = indexer.getIndexFromAddr(rt);
77 SCCERR(SCMOD) <<
"Could not find index for " << rt;
79 if(histogram.size() <=
static_cast<unsigned>(idx)) {
80 histogram.resize(idx + 1);
90tcp4tlm_bridge::~tcp4tlm_bridge() {
91 if(is_server_running()) {
92 if(!is_connection_server.get_value() && !is_shutdown_requested()) {
93 SCCTRACE(SCMOD) <<
"[" << __FUNCTION__ <<
"] shutting down connection";
96 SCCTRACE(SCMOD) <<
"[" << __FUNCTION__ <<
"] shutting down server";
101void tcp4tlm_bridge::end_of_elaboration() {
102 client::host = other_host_name.get_value();
103 client::port = other_host_port.get_value();
106void tcp4tlm_bridge::start_of_simulation() {
107 if(is_connection_server.get_value()) {
108 SCCINFO(SCMOD) <<
"starting server on port " << this_host_port.get_value();
109 server::start_server(this_host_port.get_value());
112 server::start_server(this_host_port.get_value());
113 tcp4tlm::connection<tcp4tlm::request_message, tcp4tlm::response_message>::endpoint_t lep = get_acceptor().local_endpoint();
114 auto msg = get_notify_endpoint_msg(lep);
115 const auto* endpoint = msg.root()->payload_as_NotifyEndpointMsg();
116 SCCTRACE(SCMOD) <<
"sending coordinates downstream '" << (endpoint->hostname() ? endpoint->hostname()->str() : std::string{}) <<
":"
117 << endpoint->port() <<
"' to " << host <<
":" << port;
118 client_connection().write_data(msg);
119 std::shared_ptr<tcp4tlm::response_message> resp;
120 client_connection().read_data(resp);
121 SCCTRACE(SCMOD) <<
"got response, start simulating";
122 if(tcp4tlm::get_status(resp ? resp->root() :
nullptr) != tcp4tlm::ok) {
123 throw std::exception();
128void tcp4tlm_bridge::end_of_simulation() {
129 if(is_server_running()) {
130 if(!is_connection_server.get_value() && is_connected) {
131 SCCTRACE(SCMOD) <<
"[" << __FUNCTION__ <<
"] shutting down connection";
134 SCCTRACE(SCMOD) <<
"[" << __FUNCTION__ <<
"] shutting down server";
137#ifdef GENERATE_STATISTICS
138 const char* stream_type =
typeid(get_acceptor()) ==
typeid(boost::asio::ip::tcp::acceptor) ?
"tcp" :
"stream";
140 for(
size_t idx = 0; idx < 10; ++idx) {
141 indexer.setTargetRange(idx, idx * 10000, 10000);
143 for(
size_t idx = 10; idx < 20; ++idx) {
144 indexer.setTargetRange(idx, 100000 * (idx - 9), 100000);
146 for(
size_t idx = 20; idx < 30; ++idx) {
147 indexer.setTargetRange(idx, 1000000 * (idx - 19), 1000000);
149 for(
size_t idx = 30; idx < 40; ++idx) {
150 indexer.setTargetRange(idx, 10000000 * (idx - 29), 10000000);
152 statistics stat_tx(indexer, 40, txt[0]), stat_send(indexer, 40, rtto[0]), stat_rx(indexer, 30, rxt[0]);
153 for(
size_t i = 0; i < txt.size(); ++i) {
154 stat_tx.updateStat(txt[i]);
155 stat_send.updateStat(rtto[i]);
156 stat_rx.updateStat(rxt[i]);
158 cout <<
"Statistics for " << stream_type <<
" socket based communication" << endl;
159 cout <<
"Send times for " << txt.size() <<
" transactions in ns for writing (min,avg,max): " << stat_tx << endl;
160 cout <<
"Transmit times for " << txt.size() <<
" transactions in ns for writing (min,avg,max): " << stat_send << endl;
161 cout <<
"Receive times for " << txt.size() <<
" transactions in ns for reading (min,avg,max): " << stat_rx << endl;
162 stat_tx.print_histogram =
true;
163 cout <<
"Send times histogram:" << endl << stat_tx;
164 stat_rx.print_histogram =
true;
165 cout <<
"Receive times histogram:" << endl << stat_rx;
169unsigned tcp4tlm_bridge::transport_dbg_cb(tlm::tlm_generic_payload& gp) {
170 sc_core::sc_time delay(sc_core::SC_ZERO_TIME);
171 do_access(gp, delay,
true);
172 return gp.get_response_status() == tlm::TLM_OK_RESPONSE ? gp.get_data_length() : 0;
175void tcp4tlm_bridge::btransport_cb(tlm::tlm_generic_payload& gp, sc_core::sc_time& delay) { do_access(gp, delay); }
177void tcp4tlm_bridge::do_access(tlm::tlm_generic_payload& gp, sc_core::sc_time& delay,
bool debug) {
178#ifdef GENERATE_STATISTICS
179 static timespec tstart, twser, tmid, tend;
181 if(!is_remote_connected())
182 SCCFATAL(SCMOD) <<
"No remote connected";
183 gp.set_dmi_allowed(
false);
184#ifdef GENERATE_STATISTICS
185#define TIMEDIFF(X, Y) X.tv_nsec >= Y.tv_nsec ? X.tv_nsec - Y.tv_nsec : 1000000000 + X.tv_nsec - Y.tv_nsec
187 GETCLOCK(&connection_type::get_t_stamp());
189 gp.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
190 std::vector<uint8_t> byte_enable;
191 if(gp.get_byte_enable_ptr()) {
192 byte_enable.resize(gp.get_byte_enable_length());
193 std::copy(gp.get_byte_enable_ptr(), gp.get_byte_enable_ptr() + gp.get_byte_enable_length(), byte_enable.begin());
195 switch(gp.get_command()) {
196 case tlm::TLM_READ_COMMAND: {
197 SCCTRACE(SCMOD) <<
"Requesting a read @" << sc_core::sc_time_stamp();
199 tcp4tlm::make_bus_op_msg(sc_core::sc_time_stamp().value(), delay.value(), debug ? tcp4tlm::debug_acc : tcp4tlm::normal_acc, 0,
200 gp.get_address(), gp.get_data_length(),
false, {}, byte_enable);
201 client_connection().write_data(bmsg);
202#ifdef GENERATE_STATISTICS
203 twser = connection_type::get_t_stamp();
206 client_connection().read_data(resp_msg);
207 const auto* response = resp_msg ? resp_msg->root() :
nullptr;
208 if(tcp4tlm::get_status(response) != tcp4tlm::ok || !tcp4tlm::belongs_to(response, bmsg.root())) {
211 const auto* mresp = response->payload_as_BusDataMsg();
212 if(mresp ==
nullptr || mresp->data() ==
nullptr || mresp->data()->size() != gp.get_data_length()) {
215 unsigned char* end = std::copy(mresp->data()->begin(), mresp->data()->end(), gp.get_data_ptr());
216 assert(
static_cast<unsigned>(end - gp.get_data_ptr()) == gp.get_data_length());
217 gp.set_response_status(tlm::TLM_OK_RESPONSE);
219 case tlm::TLM_WRITE_COMMAND: {
220 SCCTRACE(SCMOD) <<
"Requesting a write @" << sc_core::sc_time_stamp();
221 std::vector<uint8_t> data(gp.get_data_length());
222 std::copy(gp.get_data_ptr(), gp.get_data_ptr() + gp.get_data_length(), data.begin());
224 tcp4tlm::make_bus_op_msg(sc_core::sc_time_stamp().value(), delay.value(), debug ? tcp4tlm::debug_acc : tcp4tlm::normal_acc, 0,
225 gp.get_address(), gp.get_data_length(), write_no_response.get_value(), data, byte_enable);
226 client_connection().write_data(bmsg);
227#ifdef GENERATE_STATISTICS
228 twser = connection_type::get_t_stamp();
232 if(write_no_response.get_value()) {
233 gp.set_response_status(tlm::TLM_OK_RESPONSE);
235 client_connection().read_data(resp_msg);
236 const auto* response = resp_msg ? resp_msg->root() :
nullptr;
237 gp.set_response_status((tcp4tlm::get_status(response) == tcp4tlm::ok && tcp4tlm::belongs_to(response, bmsg.root()))
238 ? tlm::TLM_OK_RESPONSE
239 : tlm::TLM_GENERIC_ERROR_RESPONSE);
245#ifdef GENERATE_STATISTICS
247 txt.push_back(TIMEDIFF(tmid, tstart));
248 rtto.push_back(TIMEDIFF(tmid, twser));
249 rxt.push_back(TIMEDIFF(tend, tmid));
253inline long long int get_time_of_day_us() {
255#if defined __x86_64__
256 gettimeofday(&checkpoint, 0);
257 return checkpoint.tv_sec * 100000 + checkpoint.tv_usec;
263void tcp4tlm_bridge::timing_thread() {
264 wait(sc_core::SC_ZERO_TIME);
267 if(is_connection_server.get_value())
270 const auto usecs_to_sleep = 1000LL;
271#if defined __x86_64__
272 if(!is_connection_server.get_value()) {
275 auto smsg = tcp4tlm::make_sync_msg(sc_core::sc_time_stamp().value());
276 client_connection().write_data(smsg);
278 }
else if(wall_time_simulation_speed.get_value()) {
279 SCCDEBUG(SCMOD) <<
"Running in wall time mode";
280 auto duration = usecs_to_sleep;
281 auto checkpoint_us = get_time_of_day_us();
283 wait(usecs_to_sleep, sc_core::SC_US);
284 auto act_us = get_time_of_day_us();
285 auto consumed = act_us - checkpoint_us;
286 if(consumed > 0 && duration > consumed) {
288 tv.tv_sec =
static_cast<time_t
>(duration - consumed) / 1000000;
289 tv.tv_nsec =
static_cast<decltype(tv.tv_nsec)
>((duration - consumed) * 1000);
292 checkpoint_us = get_time_of_day_us();
295 SCCDEBUG(SCMOD) <<
"Running in simulated time mode";
297 while(next_time_stamp.empty()) {
298 wait(sc_core::SC_ZERO_TIME);
299 std::this_thread::yield();
301 auto next = *next_time_stamp.front();
302 SCCTRACEALL(SCMOD) <<
"Got time stamp, advancing to " << next;
303 next_time_stamp.pop();
304 if(next > sc_core::sc_time_stamp()) {
305 wait(next - sc_core::sc_time_stamp());
310 std::posix_time::ptime checkpoint = std::posix_time::microsec_clock::local_time();
311 std::posix_time::time_duration duration = std::posix_time::microsec(usecsToSleep);
312 if(!is_connection_server.get_value() || !limit_simulation_speed.get_value())
315 wait(usecsToSleep, sc_core::SC_US);
316 std::posix_time::time_duration consumed = std::posix_time::microsec_clock::local_time() - checkpoint;
317 if(duration > consumed) {
318 std::this_thread::sleep(duration - consumed);
320 checkpoint = std::posix_time::microsec_clock::local_time();
325tlm::scc::tlm_gp_shared_ptr tcp4tlm_bridge::init_gp(
const tcp4tlm::BusOpMsg*
const msg) {
328 gp->set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
329 gp->set_address(msg->address());
330 gp->set_streaming_width(msg->size());
331 gp->set_data_length(msg->size());
332 ext->set_size(msg->size());
333 gp->set_data_ptr(ext->get_buf_ptr());
334 if(msg->data() ==
nullptr || msg->data()->size() == 0) {
335 gp->set_command(tlm::TLM_READ_COMMAND);
337 gp->set_command(tlm::TLM_WRITE_COMMAND);
338 std::memcpy(gp->get_data_ptr(), msg->data()->Data(), gp->get_data_length());
343void tcp4tlm_bridge::server_receive_completed(con_ptr& con,
const tcp4tlm::request_message*
const result) {
344 const auto* request = result ? result->root() :
nullptr;
345 if(request ==
nullptr) {
346 auto msg = tcp4tlm::make_response(uint32_t{0}, tcp4tlm::declined);
347 con->async_write(msg);
351 auto okmsg = tcp4tlm::make_response(request);
352 switch(request->payload_type()) {
353 case tcp4tlm::RequestPayload_NotifyEndpointMsg: {
354 SCCTRACE(SCMOD) <<
"Got NotifyEndpointMsg";
355 const auto* msg = request->payload_as_NotifyEndpointMsg();
356 if(msg->hostname() !=
nullptr && msg->hostname()->str() ==
"0.0.0.0") {
359 host = msg->hostname() ? msg->hostname()->str() : std::string{};
363 callback_task task([
this, msg, con]() {
364 auto okmsg = tcp4tlm::make_response(msg->id());
365 con->async_write(okmsg);
368 timed_task tup{std::move(task), sc_core::SC_ZERO_TIME};
369 task_que.emplace(std::move(tup));
370 con_est.store(
true, std::memory_order_acq_rel);
371 con_est_sig.notify_all();
373 case tcp4tlm::RequestPayload_BusOpMsg: {
374 SCCTRACE(SCMOD) <<
"Got BusOpMsg";
375 const auto* msg = request->payload_as_BusOpMsg();
376 auto time_point = sc_core::sc_time::from_value(msg->time_stamp());
377 callback_task task([
this, msg, con]() {
378 auto gp = init_gp(msg);
379 auto delay = sc_core::sc_time::from_value(msg->time_offset());
380 isckt->b_transport(*gp, delay);
383 auto dmsg = tcp4tlm::make_bus_data_msg(msg->id(), ext->data(),
384 gp->get_response_status() == tlm::TLM_OK_RESPONSE ? tcp4tlm::ok : tcp4tlm::failure);
385 con->async_write(dmsg);
386 }
else if(gp->get_response_status() != tlm::TLM_OK_RESPONSE) {
387 auto failmsg = tcp4tlm::make_response(msg->id(), tcp4tlm::failure);
388 con->async_write(failmsg);
389 }
else if(!msg->no_response()) {
390 auto okmsg = tcp4tlm::make_response(msg->id());
391 con->async_write(okmsg);
395 std::future<bool> fut = task.get_future();
396 timed_task tup{std::move(task), time_point};
397 task_que.emplace(std::move(tup));
398 next_time_stamp.push(time_point);
402 case tcp4tlm::RequestPayload_SyncMsg: {
403 SCCTRACE(SCMOD) <<
"Got SyncMsg";
404 if(is_connection_server.get_value()) {
405 const auto* msg = request->payload_as_SyncMsg();
406 auto time_point = sc_core::sc_time::from_value(msg->time_stamp());
407 next_time_stamp.push(time_point);
411 case tcp4tlm::RequestPayload_SigOpMsg: {
412 SCCTRACE(SCMOD) <<
"Got SigOpMsg";
413 const auto* msg = request->payload_as_SigOpMsg();
414 if(signals.size() > msg->index()) {
415 callback_task task([
this, &msg]() {
416 signals[msg->index()] = msg->value();
419 std::future<bool> fut = task.get_future();
420 timed_task tup{std::move(task), sc_core::SC_ZERO_TIME};
421 task_que.emplace(std::move(tup));
424 con->async_write(okmsg);
426 auto declined_msg = tcp4tlm::make_response(request, tcp4tlm::declined);
427 con->async_write(declined_msg);
430 case tcp4tlm::RequestPayload_NotifyShutdownMsg: {
431 SCCTRACE(SCMOD) <<
"Got NotifyShutdownMsg";
432 client_connection().socket().close();
433 is_connected =
false;
434 if(is_server_running()) {
437 callback_task task([
this]() {
438 this->shutdown_evt.notify(sc_core::SC_ZERO_TIME);
441 std::future<bool> fut = task.get_future();
442 timed_task tup{std::move(task), sc_core::SC_ZERO_TIME};
443 task_que.emplace(std::move(tup));
449 SCCWARN(SCMOD) <<
"Got an unhandled message";
450 auto msg = tcp4tlm::make_response(request, tcp4tlm::declined);
451 con->async_write(msg);
457void tcp4tlm_bridge::process_task_que() {
460 while(task_que.try_get(res)) {
461 SCCTRACEALL(SCMOD) <<
"Got a task @" << res.timepoint;
462 if(no_systemc_sync.get_value() || sc_core::sc_time_stamp() > res.timepoint) {
465 auto time_point = res.timepoint - sc_core::sc_time_stamp();
466 timed_task_que.notify(std::move(res.t), time_point);
469 wait(task_que.data_event());
473void tcp4tlm_bridge::process_timed_task_que() {
475 wait(timed_task_que.event());
476 auto task = timed_task_que.get();
477 SCCTRACEALL(SCMOD) <<
"Executing a task";
Extension for data buffering.