17#include "tcp4tlm_client.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>
29#include <sysc/kernel/sc_module.h>
30#include <sysc/kernel/sc_simcontext.h>
31#include <sysc/kernel/sc_time.h>
34#define GETCLOCK(X) clock_gettime(CLOCK_REALTIME, X)
37using namespace std::chrono_literals;
39tcp4tlm_client::tcp4tlm_client(sc_core::sc_module_name name,
size_t no_of_ports)
40: sc_core::sc_module(name)
41#ifdef GENERATE_STATISTICS
47 tsckt.register_b_transport(
this, &tcp4tlm_client::btransport_cb);
48 tsckt.register_transport_dbg(
this, &tcp4tlm_client::transport_dbg_cb);
49 SC_THREAD(timing_thread);
50#ifdef GENERATE_STATISTICS
57#ifdef GENERATE_STATISTICS
58void tcp4tlm_client::statistics::updateStat(
unsigned long rt) {
68 int idx = indexer.getIndexFromAddr(rt);
71 SCCERR(SCMOD) <<
"Could not find index for " << rt;
73 if(histogram.size() <=
static_cast<unsigned>(idx)) {
74 histogram.resize(idx + 1);
84tcp4tlm_client::~tcp4tlm_client() { end_connection(); }
86void tcp4tlm_client::start_of_simulation() {
87 client::host = other_host_name.get_value();
88 client::port = other_host_port.get_value();
90 auto msg = tcp4tlm::make_notify_endpoint_msg(
"", std::numeric_limits<uint16_t>::max());
91 const auto* endpoint = msg.root()->payload_as_NotifyEndpointMsg();
92 SCCTRACE(SCMOD) <<
"sending coordinates downstream '" << (endpoint->hostname() ? endpoint->hostname()->str() : std::string{}) <<
":"
93 << endpoint->port() <<
"' to " << host <<
":" << port;
94 client_connection().write_data(msg);
95 std::shared_ptr<tcp4tlm::response_message> resp;
96 client_connection().read_data(resp);
97 SCCTRACE(SCMOD) <<
"got response, start simulating";
98 if(tcp4tlm::get_status(resp ? resp->root() :
nullptr) != tcp4tlm::ok) {
99 throw std::exception();
103void tcp4tlm_client::end_of_simulation() {
104#ifdef GENERATE_STATISTICS
105 const char* stream_type =
typeid(get_acceptor()) ==
typeid(boost::asio::ip::tcp::acceptor) ?
"tcp" :
"stream";
107 for(
size_t idx = 0; idx < 10; ++idx) {
108 indexer.setTargetRange(idx, idx * 10000, 10000);
110 for(
size_t idx = 10; idx < 20; ++idx) {
111 indexer.setTargetRange(idx, 100000 * (idx - 9), 100000);
113 for(
size_t idx = 20; idx < 30; ++idx) {
114 indexer.setTargetRange(idx, 1000000 * (idx - 19), 1000000);
116 for(
size_t idx = 30; idx < 40; ++idx) {
117 indexer.setTargetRange(idx, 10000000 * (idx - 29), 10000000);
119 statistics stat_tx(indexer, 40, txt[0]), stat_send(indexer, 40, rtto[0]), stat_rx(indexer, 30, rxt[0]);
120 for(
size_t i = 0; i < txt.size(); ++i) {
121 stat_tx.updateStat(txt[i]);
122 stat_send.updateStat(rtto[i]);
123 stat_rx.updateStat(rxt[i]);
125 cout <<
"Statistics for " << stream_type <<
" socket based communication" << endl;
126 cout <<
"Send times for " << txt.size() <<
" transactions in ns for writing (min,avg,max): " << stat_tx << endl;
127 cout <<
"Transmit times for " << txt.size() <<
" transactions in ns for writing (min,avg,max): " << stat_send << endl;
128 cout <<
"Receive times for " << txt.size() <<
" transactions in ns for reading (min,avg,max): " << stat_rx << endl;
129 stat_tx.print_histogram =
true;
130 cout <<
"Send times histogram:" << endl << stat_tx;
131 stat_rx.print_histogram =
true;
132 cout <<
"Receive times histogram:" << endl << stat_rx;
136unsigned tcp4tlm_client::transport_dbg_cb(tlm::tlm_generic_payload& gp) {
137 sc_core::sc_time delay(sc_core::SC_ZERO_TIME);
138 do_access(gp, delay,
true);
139 return gp.get_response_status() == tlm::TLM_OK_RESPONSE ? gp.get_data_length() : 0;
142void tcp4tlm_client::btransport_cb(tlm::tlm_generic_payload& gp, sc_core::sc_time& delay) { do_access(gp, delay); }
144void tcp4tlm_client::do_access(tlm::tlm_generic_payload& gp, sc_core::sc_time& delay,
bool debug) {
145#ifdef GENERATE_STATISTICS
146 static timespec tstart, twser, tmid, tend;
148 if(!is_remote_connected())
149 SCCFATAL(SCMOD) <<
"No remote connected";
150 gp.set_dmi_allowed(
false);
151#ifdef GENERATE_STATISTICS
152#define TIMEDIFF(X, Y) X.tv_nsec >= Y.tv_nsec ? X.tv_nsec - Y.tv_nsec : 1000000000 + X.tv_nsec - Y.tv_nsec
154 GETCLOCK(&connection_type::get_t_stamp());
156 gp.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
157 std::vector<uint8_t> byte_enable;
158 if(gp.get_byte_enable_ptr()) {
159 byte_enable.resize(gp.get_byte_enable_length());
160 std::copy(gp.get_byte_enable_ptr(), gp.get_byte_enable_ptr() + gp.get_byte_enable_length(), byte_enable.begin());
162 switch(gp.get_command()) {
163 case tlm::TLM_READ_COMMAND: {
164 SCCTRACE(SCMOD) <<
"Requesting a read @" << sc_core::sc_time_stamp();
166 tcp4tlm::make_bus_op_msg(sc_core::sc_time_stamp().value(), delay.value(), debug ? tcp4tlm::debug_acc : tcp4tlm::normal_acc, 0,
167 gp.get_address(), gp.get_data_length(),
false, {}, byte_enable);
168 client_connection().write_data(bmsg);
169#ifdef GENERATE_STATISTICS
170 twser = connection_type::get_t_stamp();
173 client_connection().read_data(resp_msg);
174 const auto* response = resp_msg ? resp_msg->root() :
nullptr;
175 if(tcp4tlm::get_status(response) != tcp4tlm::ok || !tcp4tlm::belongs_to(response, bmsg.root())) {
178 const auto* mresp = response->payload_as_BusDataMsg();
179 if(mresp ==
nullptr || mresp->data() ==
nullptr || mresp->data()->size() != gp.get_data_length()) {
182 unsigned char* end = std::copy(mresp->data()->begin(), mresp->data()->end(), gp.get_data_ptr());
183 assert(
static_cast<unsigned>(end - gp.get_data_ptr()) == gp.get_data_length());
184 gp.set_response_status(tlm::TLM_OK_RESPONSE);
186 case tlm::TLM_WRITE_COMMAND: {
187 SCCTRACE(SCMOD) <<
"Requesting a write @" << sc_core::sc_time_stamp();
188 std::vector<uint8_t> data(gp.get_data_length());
189 std::copy(gp.get_data_ptr(), gp.get_data_ptr() + gp.get_data_length(), data.begin());
191 tcp4tlm::make_bus_op_msg(sc_core::sc_time_stamp().value(), delay.value(), debug ? tcp4tlm::debug_acc : tcp4tlm::normal_acc, 0,
192 gp.get_address(), gp.get_data_length(), write_no_response.get_value(), data, byte_enable);
193 client_connection().write_data(bmsg);
194#ifdef GENERATE_STATISTICS
195 twser = connection_type::get_t_stamp();
199 if(write_no_response.get_value()) {
200 gp.set_response_status(tlm::TLM_OK_RESPONSE);
202 client_connection().read_data(resp_msg);
203 const auto* response = resp_msg ? resp_msg->root() :
nullptr;
204 gp.set_response_status((tcp4tlm::get_status(response) == tcp4tlm::ok && tcp4tlm::belongs_to(response, bmsg.root()))
205 ? tlm::TLM_OK_RESPONSE
206 : tlm::TLM_GENERIC_ERROR_RESPONSE);
212#ifdef GENERATE_STATISTICS
214 txt.push_back(TIMEDIFF(tmid, tstart));
215 rtto.push_back(TIMEDIFF(tmid, twser));
216 rxt.push_back(TIMEDIFF(tend, tmid));
220inline long long int get_time_of_day_us() {
222#if defined __x86_64__
223 gettimeofday(&checkpoint, 0);
224 return checkpoint.tv_sec * 100000 + checkpoint.tv_usec;
230void tcp4tlm_client::timing_thread() {
231 wait(sc_core::SC_ZERO_TIME);
234 auto smsg = tcp4tlm::make_sync_msg(sc_core::sc_time_stamp().value());
235 client_connection().write_data(smsg);