scc 2026.07
SystemC components library
tcp4tlm_server.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_server.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 <sysc/kernel/sc_module.h>
29#include <sysc/kernel/sc_simcontext.h>
30#include <sysc/kernel/sc_time.h>
31#include <thread>
32
33#define GETCLOCK(X) clock_gettime(CLOCK_REALTIME, X)
34namespace scc {
35
36using namespace std::chrono_literals;
37
38tcp4tlm_server::tcp4tlm_server(sc_core::sc_module_name name, size_t no_of_ports)
39: sc_core::sc_module(name)
40, tcp4tlm::server<tcp4tlm::request_message, tcp4tlm::response_message>(2)
41, signals{"signals", no_of_ports}
42, next_time_stamp(16)
43#ifdef GENERATE_STATISTICS
44, rtto()
45, txt()
46, rxt()
47#endif
48{
49 SC_THREAD(timing_thread);
50 SC_THREAD(process_task_que);
51 SC_THREAD(process_timed_task_que);
52#ifdef GENERATE_STATISTICS
53 rtto.reserve(100000);
54 txt.reserve(100000);
55 rxt.reserve(100000);
56#endif
57}
58
59#ifdef GENERATE_STATISTICS
60void tcp4tlm_server::statistics::updateStat(unsigned long rt) {
61 if(rt > max) {
62 max = rt;
63 }
64
65 if(rt < min) {
66 min = rt;
67 }
68
69 sum += rt;
70 int idx = indexer.getIndexFromAddr(rt);
71
72 if(idx < 0) {
73 SCCERR(SCMOD) << "Could not find index for " << rt;
74 } else {
75 if(histogram.size() <= static_cast<unsigned>(idx)) {
76 histogram.resize(idx + 1);
77 }
78
79 histogram[idx]++;
80 }
81
82 count++;
83}
84#endif
85
86tcp4tlm_server::~tcp4tlm_server() { shutdown_server(); }
87
88void tcp4tlm_server::start_of_simulation() {
89 SCCINFO(SCMOD) << "starting server on port " << this_host_port.get_value();
90 server::start_server(this_host_port.get_value());
91}
92
93void tcp4tlm_server::end_of_simulation() {
94 if(is_server_running()) {
95 request_shutdown();
96 }
97#ifdef GENERATE_STATISTICS
98 const char* stream_type = typeid(get_acceptor()) == typeid(boost::asio::ip::tcp::acceptor) ? "tcp" : "stream";
99 util::range_lut indexer;
100 for(size_t idx = 0; idx < 10; ++idx) {
101 indexer.setTargetRange(idx, idx * 10000, 10000);
102 }
103 for(size_t idx = 10; idx < 20; ++idx) {
104 indexer.setTargetRange(idx, 100000 * (idx - 9), 100000);
105 }
106 for(size_t idx = 20; idx < 30; ++idx) {
107 indexer.setTargetRange(idx, 1000000 * (idx - 19), 1000000);
108 }
109 for(size_t idx = 30; idx < 40; ++idx) {
110 indexer.setTargetRange(idx, 10000000 * (idx - 29), 10000000);
111 }
112 statistics stat_tx(indexer, 40, txt[0]), stat_send(indexer, 40, rtto[0]), stat_rx(indexer, 30, rxt[0]);
113 for(size_t i = 0; i < txt.size(); ++i) {
114 stat_tx.updateStat(txt[i]);
115 stat_send.updateStat(rtto[i]);
116 stat_rx.updateStat(rxt[i]);
117 }
118 cout << "Statistics for " << stream_type << " socket based communication" << endl;
119 cout << "Send times for " << txt.size() << " transactions in ns for writing (min,avg,max): " << stat_tx << endl;
120 cout << "Transmit times for " << txt.size() << " transactions in ns for writing (min,avg,max): " << stat_send << endl;
121 cout << "Receive times for " << txt.size() << " transactions in ns for reading (min,avg,max): " << stat_rx << endl;
122 stat_tx.print_histogram = true;
123 cout << "Send times histogram:" << endl << stat_tx;
124 stat_rx.print_histogram = true;
125 cout << "Receive times histogram:" << endl << stat_rx;
126#endif
127}
128
129inline long long int get_time_of_day_us() {
130 timeval checkpoint;
131#if defined __x86_64__
132 gettimeofday(&checkpoint, 0);
133 return checkpoint.tv_sec * 100000 + checkpoint.tv_usec;
134#else
135 return 0;
136#endif
137}
138
139void tcp4tlm_server::timing_thread() {
140 wait(sc_core::SC_ZERO_TIME);
141 // wait until the client connects. We cannot do this in start_of_simulation as we
142 // would (potentially) block the server start of other bridges
143 wait4connection();
144 // now deal with the timing
145 const auto usecs_to_sleep = 1000LL;
146 if(wall_time_simulation_speed.get_value()) {
147 SCCDEBUG(SCMOD) << "Running in wall time mode";
148#if defined __x86_64__
149 auto duration = usecs_to_sleep;
150 auto checkpoint_us = get_time_of_day_us();
151 while(true) {
152 wait(usecs_to_sleep, sc_core::SC_US);
153 auto act_us = get_time_of_day_us();
154 auto consumed = act_us - checkpoint_us;
155 if(consumed > 0 && duration > consumed) {
156 struct timespec tv;
157 tv.tv_sec = static_cast<time_t>(duration - consumed) / 1000000;
158 tv.tv_nsec = static_cast<decltype(tv.tv_nsec)>((duration - consumed) * 1000);
159 nanosleep(&tv, &tv);
160 }
161 checkpoint_us = get_time_of_day_us();
162 }
163#else
164 std::posix_time::time_duration duration = std::posix_time::microsec(usecsToSleep);
165 std::posix_time::ptime checkpoint = std::posix_time::microsec_clock::local_time();
166 while(true) {
167 wait(usecsToSleep, sc_core::SC_US);
168 std::posix_time::time_duration consumed = std::posix_time::microsec_clock::local_time() - checkpoint;
169 if(duration > consumed) {
170 std::this_thread::sleep(duration - consumed);
171 }
172 checkpoint = std::posix_time::microsec_clock::local_time();
173 }
174#endif
175 } else {
176 SCCDEBUG(SCMOD) << "Running in simulated time mode";
177 while(true) {
178 while(next_time_stamp.empty()) {
179 wait(sc_core::SC_ZERO_TIME);
180 std::this_thread::yield();
181 }
182 auto next = *next_time_stamp.front();
183 SCCTRACEALL(SCMOD) << "Got time stamp, advancing to " << next;
184 next_time_stamp.pop();
185 if(next > sc_core::sc_time_stamp()) {
186 wait(next - sc_core::sc_time_stamp());
187 }
188 }
189 }
190}
191
192tlm::scc::tlm_gp_shared_ptr tcp4tlm_server::init_gp(const tcp4tlm::BusOpMsg* const msg) {
193 tlm::scc::tlm_gp_shared_ptr gp = mm.get().allocate<tlm::scc::data_buffer>();
194 auto ext = gp->get_extension<tlm::scc::data_buffer>();
195 gp->set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
196 gp->set_address(msg->address());
197 gp->set_streaming_width(msg->size());
198 gp->set_data_length(msg->size());
199 ext->set_size(msg->size());
200 gp->set_data_ptr(ext->get_buf_ptr());
201 if(msg->data() == nullptr || msg->data()->size() == 0) {
202 gp->set_command(tlm::TLM_READ_COMMAND);
203 } else {
204 gp->set_command(tlm::TLM_WRITE_COMMAND);
205 std::memcpy(gp->get_data_ptr(), msg->data()->Data(), gp->get_data_length());
206 }
207 return gp;
208}
209
210void tcp4tlm_server::server_receive_completed(con_ptr& con, const tcp4tlm::request_message* const result) {
211 const auto* request = result ? result->root() : nullptr;
212 if(request == nullptr) {
213 auto msg = tcp4tlm::make_response(uint32_t{0}, tcp4tlm::declined);
214 con->async_write(msg);
215 con->async_read();
216 return;
217 }
218 auto okmsg = tcp4tlm::make_response(request);
219 switch(request->payload_type()) {
220 case tcp4tlm::RequestPayload_NotifyEndpointMsg: {
221 SCCTRACE(SCMOD) << "Got NotifyEndpointMsg";
222 const auto* msg = request->payload_as_NotifyEndpointMsg();
223 callback_task task([this, msg, con]() {
224 auto okmsg = tcp4tlm::make_response(msg->id());
225 con->async_write(okmsg);
226 return true;
227 });
228 timed_task tup{std::move(task), sc_core::SC_ZERO_TIME};
229 task_que.emplace(std::move(tup));
230 con_est.store(true, std::memory_order_acq_rel);
231 con_est_sig.notify_all();
232 } break;
233 case tcp4tlm::RequestPayload_BusOpMsg: {
234 SCCTRACE(SCMOD) << "Got BusOpMsg";
235 const auto* msg = request->payload_as_BusOpMsg();
236 auto time_point = sc_core::sc_time::from_value(msg->time_stamp());
237 callback_task task([this, msg, con]() {
238 auto gp = init_gp(msg);
239 auto delay = sc_core::sc_time::from_value(msg->time_offset());
240 isckt->b_transport(*gp, delay);
241 if(gp->is_read()) {
242 auto* ext = gp->get_extension<tlm::scc::data_buffer>();
243 auto dmsg = tcp4tlm::make_bus_data_msg(msg->id(), ext->data(),
244 gp->get_response_status() == tlm::TLM_OK_RESPONSE ? tcp4tlm::ok : tcp4tlm::failure);
245 con->async_write(dmsg);
246 } else if(gp->get_response_status() != tlm::TLM_OK_RESPONSE) {
247 auto failmsg = tcp4tlm::make_response(msg->id(), tcp4tlm::failure);
248 con->async_write(failmsg);
249 } else if(!msg->no_response()) {
250 auto okmsg = tcp4tlm::make_response(msg->id());
251 con->async_write(okmsg);
252 }
253 return true;
254 });
255 std::future<bool> fut = task.get_future();
256 timed_task tup{std::move(task), time_point};
257 task_que.emplace(std::move(tup));
258 next_time_stamp.push(time_point);
259 fut.wait();
260 fut.get();
261 } break;
262 case tcp4tlm::RequestPayload_SyncMsg: {
263 SCCTRACE(SCMOD) << "Got SyncMsg";
264 const auto* msg = request->payload_as_SyncMsg();
265 auto time_point = sc_core::sc_time::from_value(msg->time_stamp());
266 next_time_stamp.push(time_point);
267 // no response
268 } break;
269 case tcp4tlm::RequestPayload_SigOpMsg: {
270 SCCTRACE(SCMOD) << "Got SigOpMsg";
271 const auto* msg = request->payload_as_SigOpMsg();
272 if(signals.size() > msg->index()) {
273 callback_task task([this, &msg]() {
274 signals[msg->index()] = msg->value();
275 return true;
276 });
277 std::future<bool> fut = task.get_future();
278 timed_task tup{std::move(task), sc_core::SC_ZERO_TIME};
279 task_que.emplace(std::move(tup));
280 fut.wait();
281 fut.get();
282 con->async_write(okmsg);
283 } else {
284 auto declined_msg = tcp4tlm::make_response(request, tcp4tlm::declined);
285 con->async_write(declined_msg);
286 }
287 } break;
288 case tcp4tlm::RequestPayload_NotifyShutdownMsg: {
289 SCCTRACE(SCMOD) << "Got NotifyShutdownMsg";
290 if(is_server_running()) {
291 request_shutdown();
292 }
293 callback_task task([this]() {
294 this->shutdown_evt.notify(sc_core::SC_ZERO_TIME);
295 return true;
296 });
297 std::future<bool> fut = task.get_future();
298 timed_task tup{std::move(task), sc_core::SC_ZERO_TIME};
299 task_que.emplace(std::move(tup));
300 fut.wait();
301 fut.get();
302 return;
303 }
304 default: {
305 SCCWARN(SCMOD) << "Got an unhandled message";
306 auto msg = tcp4tlm::make_response(request, tcp4tlm::declined);
307 con->async_write(msg);
308 } break;
309 }
310 con->async_read();
311}
312
313void tcp4tlm_server::process_task_que() {
314 timed_task res;
315 while(true) {
316 while(task_que.try_get(res)) {
317 SCCTRACEALL(SCMOD) << "Got a task @" << res.timepoint;
318 if(no_systemc_sync.get_value() || sc_core::sc_time_stamp() > res.timepoint) {
319 res.t();
320 } else {
321 auto time_point = res.timepoint - sc_core::sc_time_stamp();
322 timed_task_que.notify(std::move(res.t), time_point);
323 }
324 }
325 wait(task_que.data_event());
326 }
327}
328
329void tcp4tlm_server::process_timed_task_que() {
330 while(true) {
331 wait(timed_task_que.event());
332 auto task = timed_task_que.get();
333 SCCTRACEALL(SCMOD) << "Executing a task";
334 task();
335 }
336}
337} // namespace scc
range based lookup table
Definition range_lut.h:37
SCC TLM utilities.
Extension for data buffering.