orca-sim
NetBridge.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  * This file is part of project ORCA. More information on the project
3  * can be found at the following repositories at GitHub's website.
4  *
5  * http://https://github.com/andersondomingues/orca-sim
6  * http://https://github.com/andersondomingues/orca-software
7  * http://https://github.com/andersondomingues/orca-mpsoc
8  * http://https://github.com/andersondomingues/orca-tools
9  *
10  * Copyright (C) 2018-2020 Anderson Domingues, <ti.andersondomingues@gmail.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 ******************************************************************************/
26 
27 #include <stdio.h>
28 #include <netinet/in.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netdb.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 
36 // std API
37 #include <iostream>
38 #include <sstream>
39 #include <chrono>
40 #include <string>
41 
42 // simulator API
43 #include "TimedModel.hpp"
44 #include "Buffer.hpp"
45 
46 #include "NetBridge.hpp"
47 
52 
56 
61 NetBridge::NetBridge(std::string name) : TimedModel(name) {
62  // open debug file
63  output_debug.open("logs/000.net_debug.log",
64  std::ofstream::out | std::ofstream::trunc);
65  output_uart.open("logs/000.net_uart.log",
66  std::ofstream::out | std::ofstream::trunc);
67 
68  // create a new signal so that the module can be interrupted by
69  // the external network when a new packet arrives
71  this->GetName() + ".signalUdpIntr");
72  _signal_recv->Write(0);
73 
74  #ifdef NETBRIDGE_ENABLE_LOG_INPUT
75  _trafficIn = 0;
76  #endif
77 
78  #ifdef NETBRIDGE_ENABLE_LOG_OUTPUT
79  _trafficOut = 0;
80  #endif
81 
82  // initialize states
85 
86  // initialize a new client (client sends messages)
87  const std::string& client_addr = NETSOCKET_CLIENT_ADDRESS;
88  _udp_client = new udp_client(client_addr, NETSOCKET_CLIENT_PORT);
89 
90  const std::string& server_addr = NETSOCKET_SERVER_ADDRESS;
91  _udp_server = new udp_server(server_addr, NETSOCKET_SERVER_PORT);
92 
93  // this code depends on linux's libraries. I warned you.
95  << "Virtual Ethernet Adapter is up." << std::endl
96  << "Server ip:" << NETSOCKET_SERVER_ADDRESS << ":"
97  << NETSOCKET_SERVER_PORT << std::endl
98  << "Client ip:" << NETSOCKET_CLIENT_ADDRESS << ":"
99  << NETSOCKET_CLIENT_PORT << std::endl;
100 
101  // instantiate a new input buffer (only input is buferred)
102  _ib = new Buffer<FlitType>(this->GetName() + ".buffer.in",
103  HWBUFFER_LEN);
104 
105  this->Reset();
106 
108  if (pthread_create(&_t, NULL, NetBridge::udpRecvThread, this)) {
109  std::cout << "unable to create new thread using lpthread."
110  << std:: endl;
111  }
112 }
113 
115  return _signal_recv;
116 }
117 
119  return _recv_buffer;
120 }
121 
126  delete(_ib);
127  delete(_signal_recv);
128 
129  delete(_udp_client);
130  delete(_udp_server);
131 
133 
134  output_debug.close();
135  output_uart.close();
136 }
137 
142  // TODO(ad): missing reset method
143 }
144 
146  return _ib;
147 }
148 
150  _ob = b;
151 }
152 
154  return _udp_server;
155 }
156 
162  // std::cout << this->GetName() << std::endl;
163  this->udpToNocProcess(); // process for receiving from the UDP socket
164  this->nocToUdpProcess(); // process for sending through the UDP socket
165  return 1; // takes exactly 1 cycle to run both processes
166 }
167 
168 void* NetBridge::udpRecvThread(void* gs) {
169  NetBridge* ns = reinterpret_cast<NetBridge*>(gs);
170 
171  // recv while the program lives
172  while (!(ns->udpRecvThread_terminate)) {
173  // recv only when interface is not busy
174  if (ns->GetSignalRecv()->Read() == 0x0) {
175  // TODO(ad): switch to udp polling
176  // recvs from external network
177  ns->GetUdpServer()->recv(
178  reinterpret_cast<char*>(ns->GetBuffer()), RECV_BUFFER_LEN);
179 
180  // start interface
181  ns->GetSignalRecv()->Write(0x1);
182  }
183  }
184  return 0;
185 }
186 
187 void NetBridge::LogWrite(std::string ss) {
188  this->output_debug << ss << std::flush;
189 }
190 
192  // Receive a packet from the network and send data flit-by-flit
193  // to the noc. NoC buffers have unlimited size, although we can
194  // check on buffers' size if necessary.
195  // TODO(ad): to model network congestion.
196  switch (_recv_state) {
197  // READY: In this state, the module is waiting for an UDP packet. There
198  // is a separated process that receives the packet and writes it to the
199  // internal buffer, so we just wait for the control signal to raise.
201  // packet has arrived and the network is not sending packets
202  if (_signal_recv->Read() == 0x1 && !_ob->full()) {
203  // push the first flit to the NoC, whatever the content.
204  FlitType* buf = reinterpret_cast<FlitType*>(_recv_buffer);
205  _out_reg = buf[0];
206  _ob->push(_out_reg);
207 
208  // proceed to the next state
210 
211  #ifdef NETBRIDGE_ENABLE_LOG_INPUT
212  int32_t x = (_out_reg & 0xf0) >> 4;
213  int32_t y = (_out_reg & 0x0f);
214  int32_t z = x + y * 4;
215 
216  std::chrono::time_point<std::chrono::system_clock> now;
217  now = std::chrono::system_clock::now();
218 
219  auto duration = now.time_since_epoch();
220  auto millis =
221  std::chrono::duration_cast<std::chrono::milliseconds>(
222  duration).count();
223 
224  output_debug << "[" << millis << "] IN " << _trafficIn
225  << " FROM " << _udp_server->get_addr() << ":"
226  << _udp_server->get_port()
227  << " TO #" << z << std::endl << std::flush;
228 
229  _trafficIn++;
230  #endif
231  }
232  } break;
233 
234  // READ_LEN: In this state we read the second flit, which carries the
235  // size of the burst. We determine how many cycle we spend bursting data
236  // out of the module in this state.
238  if (!_ob->full()) {
239  FlitType* buf = reinterpret_cast<FlitType*>(_recv_buffer);
240  _out_reg = buf[1];
241  _ob->push(_out_reg);
242 
243  // there is no condition to trigger this state as the data
244  // is already stored to the memory. we increment the number
245  // of flits in two given that neither address and size flits
246  // are accounted.
247  _flits_to_recv = _out_reg + 2;
249 
250  // proceed to next state
252  }
253  } break;
254 
255  // RECV_PAYLOAD: We stay in this state until we send all the remaining
256  // flits to the noc.
258  // no more flits to recv, go back to the first state
260  _signal_recv->Write(0x0);
262 
263  // still have flits to send
264  } else if (!_ob->full()) {
265  _out_reg = (reinterpret_cast<FlitType*>(_recv_buffer))
267  _ob->push(_out_reg);
268  }
269  } break;
270  }
271 }
272 
274  switch (_send_state) {
275  // READY: In this state we wait for the first flit to come from the noc
277  // fall whether we have any flit coming from the noc
278  if (_ib->size() > 0) {
279  // put address flit into the send buffer
280  FlitType* buf = reinterpret_cast<FlitType*>(_send_buffer);
281  buf[0] = _ib->top();
282  _ib->pop();
283 
284  #ifdef NETBRIDGE_ENABLE_LOG_OUTPUT
285  uint32_t x = buf[4];
286 
287  std::chrono::time_point<std::chrono::system_clock> now =
288  std::chrono::system_clock::now();
289  auto duration = now.time_since_epoch();
290  auto millis =
291  std::chrono::duration_cast<std::chrono::milliseconds>(
292  duration).count();
293 
294  output_debug << "[" << millis << "] OUT " << _trafficOut
295  << " TO " << _udp_client->get_addr() << ":"
296  << _udp_client->get_port()
297  << " FROM #" << x << std::endl << std::flush;
298  #endif
299 
300  // change states
302  }
303  } break;
304 
305  // SEND_LEN: In this state we read how many flits we must receive from
306  // the noc until we send the next network packet.
308  if (_ib->size() > 0) {
309  // put address flit into the send buffer
310  FlitType* buf = reinterpret_cast<FlitType*>(_send_buffer);
311  buf[1] = _ib->top();
312 
313  _flits_to_send = _ib->top() + 2;
314 
315  // starts in two due to we have added address and size flits
317  _ib->pop();
318 
319  // change states
321  }
322  } break;
323 
324  // SEND_PAYLOAD: Send store data and go back to the first state
326  // still have flits to receive from the noc
328  if (_ib->size() > 0) {
329  FlitType* buf = reinterpret_cast<FlitType*>(_send_buffer);
330  buf[_flits_to_send_count] = _ib->top();
331  _ib->pop();
332 
334  }
335  } else {
336  // send message through the udp socket
338 
339  // go back to the first state
341  }
342  }
343  }
344 }
345 
346 // ---------------------- UDP stuff
347 // reference: https://linux.m2osw.com/c-implementation-udp-clientserver
348 
349 
350 // ========================= CLIENT =========================
351 
380 udp_client::udp_client(const std::string& addr, int port)
381  : f_port(port)
382  , f_addr(addr) {
383  char decimal_port[16];
384  snprintf(decimal_port, sizeof(decimal_port), "%d", f_port);
385  decimal_port[sizeof(decimal_port) / sizeof(decimal_port[0]) - 1] = '\0';
386  struct addrinfo hints;
387  memset(&hints, 0, sizeof(hints));
388  hints.ai_family = AF_UNSPEC;
389  hints.ai_socktype = SOCK_DGRAM;
390  hints.ai_protocol = IPPROTO_UDP;
391  int r(getaddrinfo(addr.c_str(), decimal_port, &hints, &f_addrinfo));
392  if (r != 0 || f_addrinfo == NULL) {
393  throw udp_client_server_runtime_error(("invalid address or port: \""
394  + addr + ":" + decimal_port + "\"").c_str());
395  }
396 
397  f_socket = socket(f_addrinfo->ai_family, SOCK_DGRAM |
398  SOCK_CLOEXEC, IPPROTO_UDP);
399 
400  if (f_socket == -1) {
401  freeaddrinfo(f_addrinfo);
402  throw udp_client_server_runtime_error(("could not create socket for: \""
403  + addr + ":" + decimal_port + "\"").c_str());
404  }
405 }
406 
413  freeaddrinfo(f_addrinfo);
414  close(f_socket);
415 }
416 
425  return f_socket;
426 }
427 
435 int udp_client::get_port() const {
436  return f_port;
437 }
438 
449 std::string udp_client::get_addr() const {
450  return f_addr;
451 }
452 
470 int udp_client::send(const char *msg, size_t size) {
471  return sendto(f_socket, msg, size, 0, f_addrinfo->ai_addr,
472  f_addrinfo->ai_addrlen);
473 }
474 
475 // ========================= SEVER =========================
508 udp_server::udp_server(const std::string& addr, int port)
509  : f_port(port)
510  , f_addr(addr) {
511  char decimal_port[16];
512  snprintf(decimal_port, sizeof(decimal_port), "%d", f_port);
513  decimal_port[sizeof(decimal_port) / sizeof(decimal_port[0]) - 1] = '\0';
514  struct addrinfo hints;
515  memset(&hints, 0, sizeof(hints));
516  hints.ai_family = AF_UNSPEC;
517  hints.ai_socktype = SOCK_DGRAM;
518  hints.ai_protocol = IPPROTO_UDP;
519  int r(getaddrinfo(addr.c_str(), decimal_port, &hints, &f_addrinfo));
520 
521  if (r != 0 || f_addrinfo == NULL) {
523  ("invalid address or port for UDP socket: \"" + addr + ":"
524  + decimal_port + "\"").c_str());
525  }
526 
527  f_socket = socket(f_addrinfo->ai_family, SOCK_DGRAM |
528  SOCK_CLOEXEC, IPPROTO_UDP);
529 
530  if (f_socket == -1) {
531  freeaddrinfo(f_addrinfo);
533  ("could not create UDP socket for: \"" + addr + ":" + decimal_port
534  + "\"").c_str());
535  }
536  r = bind(f_socket, f_addrinfo->ai_addr, f_addrinfo->ai_addrlen);
537  if (r != 0) {
538  freeaddrinfo(f_addrinfo);
539  close(f_socket);
541  ("could not bind UDP socket with: \"" + addr + ":" + decimal_port
542  + "\"").c_str());
543  }
544 }
545 
551  freeaddrinfo(f_addrinfo);
552  close(f_socket);
553 }
554 
563  return f_socket;
564 }
565 
573 int udp_server::get_port() const {
574  return f_port;
575 }
576 
585 std::string udp_server::get_addr() const {
586  return f_addr;
587 }
588 
607 int udp_server::recv(char *msg, size_t max_size) {
608  return ::recv(f_socket, msg, max_size, 0);
609 }
610 
630 int udp_server::timed_recv(char *msg, size_t max_size, int max_wait_ms) {
631  fd_set s;
632  FD_ZERO(&s);
633  FD_SET(f_socket, &s);
634  struct timeval timeout;
635  timeout.tv_sec = max_wait_ms / 1000;
636  timeout.tv_usec = (max_wait_ms % 1000) * 1000;
637 
645  int retval = select(f_socket + 1, &s, 0, 0, &timeout);
646  if (retval == -1) {
647  // select() set errno accordingly
648  return -1;
649  }
650 
651  if (retval > 0) {
652  // our socket has data
653  return ::recv(f_socket, msg, max_size, 0);
654  }
655 
656  // our socket has no data
657  errno = EAGAIN;
658  return -1;
659 }
The Signal class models a generic bus of width equals to the sizeof(T)
Definition: Signal.hpp:45
uint8_t _recv_buffer[RECV_BUFFER_LEN]
Definition: NetBridge.hpp:181
udp_server(const std::string &addr, int port)
Initialize a UDP server object.
Definition: NetBridge.cpp:508
#define NETSOCKET_SERVER_PORT
Definition: NetBridge.hpp:56
uint8_t _send_buffer[SEND_BUFFER_LEN]
Definition: NetBridge.hpp:182
void push(T)
Pushes an object to the back of the buffer.
Definition: Buffer.cpp:74
int get_port() const
The port used by this UDP server.
Definition: NetBridge.cpp:573
udp_client(const std::string &addr, int port)
Initialize a UDP client object.
Definition: NetBridge.cpp:380
This class models a TimedModel.
Definition: TimedModel.hpp:42
std::string get_addr() const
Return the address of this UDP server.
Definition: NetBridge.cpp:585
void SetOutputBuffer(Buffer< FlitType > *)
Definition: NetBridge.cpp:149
#define RECV_BUFFER_LEN
Definition: NetBridge.hpp:119
#define NETSOCKET_SERVER_ADDRESS
Definition: NetBridge.hpp:51
T top()
Peeks at the top of the buffer.
Definition: Buffer.cpp:100
SimulationTime Run()
Runs a state.
Definition: NetBridge.cpp:161
void Reset()
Return the module to its initial state (if stateful).
Definition: NetBridge.cpp:141
uint32_t full()
Returns TRUE when the buffer is full.
Definition: Buffer.cpp:118
NetBridgeSendState _send_state
Definition: NetBridge.hpp:138
Signal< int8_t > * _signal_recv
Definition: NetBridge.hpp:146
T Read()
Get the last value writen to the bus.
Definition: Signal.cpp:118
#define SEND_BUFFER_LEN
Definition: NetBridge.hpp:120
int get_socket() const
The socket used by this UDP server.
Definition: NetBridge.cpp:562
Buffer< FlitType > * GetInputBuffer()
Definition: NetBridge.cpp:145
~udp_client()
Clean up the UDP client object.
Definition: NetBridge.cpp:412
uint32_t SimulationTime
void Write(T val)
Writes some value to the bus.
Definition: Signal.cpp:127
#define HWBUFFER_LEN
Definition: NetBridge.hpp:122
int get_port() const
Retrieve the port used by this UDP client.
Definition: NetBridge.cpp:435
std::string GetName()
Getter method for the <_name> field.
Definition: Model.cpp:34
int timed_recv(char *msg, size_t max_size, int max_wait_ms)
Wait for data to come in.
Definition: NetBridge.cpp:630
uint16_t FlitType
flit
Definition: DmaNetif.hpp:58
std::string get_addr() const
Retrieve a copy of the address.
Definition: NetBridge.cpp:449
Signal< int8_t > * GetSignalRecv()
Definition: NetBridge.cpp:114
NetBridgeRecvState _recv_state
Definition: NetBridge.hpp:139
~udp_server()
Clean up the UDP server.
Definition: NetBridge.cpp:550
int send(const char *msg, size_t size)
Send a message through this UDP client.
Definition: NetBridge.cpp:470
static void * udpRecvThread(void *)
Definition: NetBridge.cpp:168
Buffer< FlitType > * _ib
Definition: NetBridge.hpp:185
int get_socket() const
Retrieve a copy of the socket identifier.
Definition: NetBridge.cpp:424
#define NETSOCKET_CLIENT_ADDRESS
Definition: NetBridge.hpp:41
uint32_t size()
Counts elements from the buffer.
Definition: Buffer.cpp:109
void pop()
Removes the object at the front of the buffer.
Definition: Buffer.cpp:56
int recv(char *msg, size_t max_size)
Wait on a message.
Definition: NetBridge.cpp:607
#define NETSOCKET_CLIENT_PORT
Definition: NetBridge.hpp:46
Buffer< FlitType > * _ob
Definition: NetBridge.hpp:186