scc 2026.07
SystemC components library
tcp4tlm_client.cpp
1/*******************************************************************************
2 * Copyright 2026 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
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"
22#include <algorithm>
23#include <atomic>
24#include <boost/asio.hpp>
25#include <cstdint>
26#include <cstring>
27#include <ctime>
28#include <limits>
29#include <sysc/kernel/sc_module.h>
30#include <sysc/kernel/sc_simcontext.h>
31#include <sysc/kernel/sc_time.h>
32#include <thread>
33
34#define GETCLOCK(X) clock_gettime(CLOCK_REALTIME, X)
35namespace scc {
36
37using namespace std::chrono_literals;
38
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
42, rtto()
43, txt()
44, rxt()
45#endif
46{
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
51 rtto.reserve(100000);
52 txt.reserve(100000);
53 rxt.reserve(100000);
54#endif
55}
56
57#ifdef GENERATE_STATISTICS
58void tcp4tlm_client::statistics::updateStat(unsigned long rt) {
59 if(rt > max) {
60 max = rt;
61 }
62
63 if(rt < min) {
64 min = rt;
65 }
66
67 sum += rt;
68 int idx = indexer.getIndexFromAddr(rt);
69
70 if(idx < 0) {
71 SCCERR(SCMOD) << "Could not find index for " << rt;
72 } else {
73 if(histogram.size() <= static_cast<unsigned>(idx)) {
74 histogram.resize(idx + 1);
75 }
76
77 histogram[idx]++;
78 }
79
80 count++;
81}
82#endif
83
84tcp4tlm_client::~tcp4tlm_client() { end_connection(); }
85
86void tcp4tlm_client::start_of_simulation() {
87 client::host = other_host_name.get_value();
88 client::port = other_host_port.get_value();
89 connect();
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();
100 }
101}
102
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";
106 util::range_lut indexer;
107 for(size_t idx = 0; idx < 10; ++idx) {
108 indexer.setTargetRange(idx, idx * 10000, 10000);
109 }
110 for(size_t idx = 10; idx < 20; ++idx) {
111 indexer.setTargetRange(idx, 100000 * (idx - 9), 100000);
112 }
113 for(size_t idx = 20; idx < 30; ++idx) {
114 indexer.setTargetRange(idx, 1000000 * (idx - 19), 1000000);
115 }
116 for(size_t idx = 30; idx < 40; ++idx) {
117 indexer.setTargetRange(idx, 10000000 * (idx - 29), 10000000);
118 }
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]);
124 }
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;
133#endif
134}
135
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;
140}
141
142void tcp4tlm_client::btransport_cb(tlm::tlm_generic_payload& gp, sc_core::sc_time& delay) { do_access(gp, delay); }
143
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;
147#endif
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
153 GETCLOCK(&tstart);
154 GETCLOCK(&connection_type::get_t_stamp());
155#endif
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());
161 }
162 switch(gp.get_command()) {
163 case tlm::TLM_READ_COMMAND: {
164 SCCTRACE(SCMOD) << "Requesting a read @" << sc_core::sc_time_stamp();
165 auto bmsg =
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();
171 GETCLOCK(&tmid);
172#endif
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())) {
176 break;
177 }
178 const auto* mresp = response->payload_as_BusDataMsg();
179 if(mresp == nullptr || mresp->data() == nullptr || mresp->data()->size() != gp.get_data_length()) {
180 break;
181 }
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);
185 } break;
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());
190 auto bmsg =
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();
196 GETCLOCK(&tmid);
197#endif
198
199 if(write_no_response.get_value()) {
200 gp.set_response_status(tlm::TLM_OK_RESPONSE);
201 } else {
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);
207 }
208 } break;
209 default:
210 break;
211 }
212#ifdef GENERATE_STATISTICS
213 GETCLOCK(&tend);
214 txt.push_back(TIMEDIFF(tmid, tstart));
215 rtto.push_back(TIMEDIFF(tmid, twser));
216 rxt.push_back(TIMEDIFF(tend, tmid));
217#endif
218}
219
220inline long long int get_time_of_day_us() {
221 timeval checkpoint;
222#if defined __x86_64__
223 gettimeofday(&checkpoint, 0);
224 return checkpoint.tv_sec * 100000 + checkpoint.tv_usec;
225#else
226 return 0;
227#endif
228}
229
230void tcp4tlm_client::timing_thread() {
231 wait(sc_core::SC_ZERO_TIME);
232 while(true) {
233 wait(1_ms);
234 auto smsg = tcp4tlm::make_sync_msg(sc_core::sc_time_stamp().value());
235 client_connection().write_data(smsg);
236 }
237}
238
239} // namespace scc
range based lookup table
Definition range_lut.h:37
SCC TLM utilities.