cloudFPGA (cF) API  1.0
The documentation of the source code of cloudFPGA (cF)
tc_UdpSend.py
Go to the documentation of this file.
1 #/*
2 # * Copyright 2016 -- 2021 IBM Corporation
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 # *****************************************************************************
18 # * @file : tc_UdpSend.py
19 # * @brief : A single-threaded script to send traffic on the UDP
20 # * connection of an FPGA module.
21 # *
22 # * System: : cloudFPGA
23 # * Component : cFp_BringUp/ROLE
24 # * Language : Python 3
25 # *
26 # *****************************************************************************
27 
28 # ### REQUIRED PYTHON PACKAGES ################################################
29 import argparse
30 import datetime
31 import socket
32 import time
33 
34 # ### REQUIRED TESTCASE MODULES ###############################################
35 from tc_utils import *
36 
37 def udp_tx_loop(sock, message, count, verbose=False):
38  """UDP Tx Single-Thread Loop.
39  :param sock The socket to send/receive to/from.
40  :param message The message string to sent.
41  :param count The number of datagrams to send.
42  :param verbose Enables verbosity.
43  :return None"""
44  if verbose:
45  print("[INFO] The following message of %d bytes will be sent out %d times:\n Message=%s\n" %
46  (len(message), count, message.decode()))
47  nrErr = 0
48  loop = 0
49  txByteCnt = 0
50  startTime = datetime.datetime.now()
51  while loop < count:
52  # Send datagram
53  # -------------------
54  try:
55  sock.sendall(message)
56  except socket.error as exc:
57  # Any exception
58  print("[EXCEPTION] Socket error while transmitting :: %s" % exc)
59  exit(1)
60  finally:
61  pass
62  txByteCnt += len(message)
63  if verbose:
64  print("Loop=%d | TxBytes=%d" % (loop, txByteCnt))
65  loop += 1
66  endTime = datetime.datetime.now()
67  elapseTime = endTime - startTime
68 
69  if txByteCnt < 1000000:
70  print("[INFO] Transferred a total of %d bytes." % txByteCnt)
71  elif txByteCnt < 1000000000:
72  megaBytes = (txByteCnt * 1.0) / (1024 * 1024 * 1.0)
73  print("[INFO] Transferred a total of %.1f MB." % megaBytes)
74  else:
75  gigaBytes = (txByteCnt * 1.0) / (1024 * 1024 * 1024 * 1.0)
76  print("[INFO] Transferred a total of %.1f GB." % gigaBytes)
77 
78  bandwidth = (txByteCnt * 8 * 1.0) / (elapseTime.total_seconds() * 1024 * 1024)
79  print("#####################################################")
80  if bandwidth < 1000:
81  print("#### UDP Tx DONE with bandwidth = %6.1f Mb/s ####" % bandwidth)
82  else:
83  bandwidth = bandwidth / 1000
84  print("#### UDP Tx DONE with bandwidth = %2.1f Gb/s ####" % bandwidth)
85  print("#####################################################")
86  print()
87 
88 
89 def udp_tx_ramp(sock, message, count, verbose=False):
90  """UDP Tx Single-Thread Ramp.
91  :param sock The socket to send/receive to/from.
92  :param message The message string to sent.
93  :param count The number of datagrams to send.
94  :param verbose Enables verbosity.
95  :return None"""
96  if verbose:
97  print("[INFO] The following message of %d bytes will be sent out incrementally %d times:\n Message=%s\n" %
98  (len(message), count, message.decode()))
99  nrErr = 0
100  loop = 0
101  txByteCnt = 0
102  startTime = datetime.datetime.now()
103  while loop < count:
104  i = 1
105  while i <= len(message):
106  subMsg = message[0:i]
107  # Send datagram
108  # -------------------
109  try:
110  udpSock.sendall(subMsg)
111  except socket.error as exc:
112  # Any exception
113  print("[EXCEPTION] Socket error while transmitting :: %s" % exc)
114  exit(1)
115  finally:
116  pass
117  txByteCnt += len(subMsg)
118  if verbose:
119  print("Loop=%d | TxBytes=%d | Msg=%s" % (loop, len(subMsg), subMsg))
120  i += 1
121  loop += 1
122  endTime = datetime.datetime.now()
123  elapseTime = endTime - startTime
124 
125  if txByteCnt < 1000000:
126  print("[INFO] Transferred a total of %d bytes." % txByteCnt)
127  elif txByteCnt < 1000000000:
128  megaBytes = (txByteCnt * 1.0) / (1024 * 1024 * 1.0)
129  print("[INFO] Transferred a total of %.1f MB." % megaBytes)
130  else:
131  gigaBytes = (txByteCnt * 1.0) / (1024 * 1024 * 1024 * 1.0)
132  print("[INFO] Transferred a total of %.1f GB." % gigaBytes)
133 
134  bandwidth = (txByteCnt * 8 * 1.0) / (elapseTime.total_seconds() * 1024 * 1024)
135  print("#####################################################")
136  if bandwidth < 1000:
137  print("#### UDP Tx DONE with bandwidth = %6.1f Mb/s ####" % bandwidth)
138  else:
139  bandwidth = bandwidth / 1000
140  print("#### UDP Tx DONE with bandwidth = %2.1f Gb/s ####" % bandwidth)
141  print("#####################################################")
142  print()
143 
144 
145 
150 
151 # STEP-1: Parse the command line strings into Python objects
152 # -----------------------------------------------------------------------------
153 parser = argparse.ArgumentParser(description='A script to send UDP data to an FPGA module.')
154 parser.add_argument('-fi', '--fpga_ipv4', type=str, default='',
155  help='The IPv4 address of the FPGA (a.k.a image_ip / e.g. 10.12.200.163)')
156 parser.add_argument('-ii', '--inst_id', type=int, default=0,
157  help='The instance ID assigned by the cloudFPGA Resource Manager (e.g. 42)')
158 parser.add_argument('-lc', '--loop_count', type=int, default=10,
159  help='The number of test runs (default is 10)')
160 parser.add_argument('-mi', '--mngr_ipv4', type=str, default='10.12.0.132',
161  help='The IPv4 address of the cloudFPGA Resource Manager (default is 10.12.0.132)')
162 parser.add_argument('-mp', '--mngr_port', type=int, default=8080,
163  help='The TCP port of the cloudFPGA Resource Manager (default is 8080)')
164 parser.add_argument('-sd', '--seed', type=int, default=-1,
165  help='The initial number to seed the pseudorandom number generator.')
166 parser.add_argument('-sz', '--size', type=int, default=-1,
167  help='The size of the datagram to generate.')
168 parser.add_argument('-un', '--user_name', type=str, default='',
169  help='A user-name as used to log in ZYC2 (.e.g \'fab\')')
170 parser.add_argument('-up', '--user_passwd', type=str, default='',
171  help='The ZYC2 password attached to the user-name')
172 parser.add_argument('-v', '--verbose', action="store_true",
173  help='Enable verbosity')
174 
175 args = parser.parse_args()
176 
177 if args.user_name == '' or args.user_passwd == '':
178  print("\nWARNING: You must provide a ZYC2 user name and the corresponding password for this script to execute.\n")
179  exit(1)
180 
181 # STEP-2a: Retrieve the IP address of the FPGA module (this will be the SERVER)
182 # -----------------------------------------------------------------------------
183 ipFpga = getFpgaIpv4(args)
184 
185 # STEP-2b: Retrieve the instance Id assigned by the cloudFPGA Resource Manager
186 # -----------------------------------------------------------------------------
187 instId = getInstanceId(args)
188 
189 # STEP-2c: Retrieve the IP address of the cF Resource Manager
190 # -----------------------------------------------------------------------------
191 ipResMngr = getResourceManagerIpv4(args)
192 
193 # STEP-3a: Set the UDP listen port of the FPGA server (this one is static)
194 # -----------------------------------------------------------------------------
195 portFpga = RECV_MODE_LSN_PORT # 8800
196 
197 # STEP-3b: Retrieve the TCP port of the cloudFPGA Resource Manager
198 # -----------------------------------------------------------------------------
199 portResMngr = getResourceManagerPort(args)
200 
201 # STEP-4: Trigger the FPGA role to restart (i.e. perform SW reset of the role)
202 # -----------------------------------------------------------------------------
203 restartApp(instId, ipResMngr, portResMngr, args.user_name, args.user_passwd)
204 
205 # STEP-5: Ping the FPGA
206 # -----------------------------------------------------------------------------
207 pingFpga(ipFpga)
208 
209 # STEP-6a: Set the FPGA socket association
210 # -----------------------------------------------------------------------------
211 fpgaAssociation = (str(ipFpga), portFpga)
212 
213 # STEP-6b: Set the HOST socket association (optional)
214 # Info: Linux selects a source port from an ephemeral port range, which by
215 # default is a set to range from 32768 to 61000. You can check it
216 # with the command:
217 # > cat /proc/sys/net/ipv4/ip_local_port_range
218 # If we want to force the source port ourselves, we must use the
219 # "bind before connect" trick.
220 # -----------------------------------------------------------------------------
221 if 0:
222  udpSP = portFpga + 49152 # 8803 + 0xC000
223  hostAssociation = (ipSaStr, udpSP)
224 
225 # STEP-8a: Create a UDP/IP socket
226 # -----------------------------------------------------------------------------
227 try:
228  udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
229 except Exception as exc:
230  print("[EXCEPTION] %s" % exc)
231  exit(1)
232 
233 # [FIXME - Disable the IP fragmentation via setsockopt()]
234 # See for also:
235 # https://stackoverflow.com/questions/973439/how-to-set-the-dont-fragment-df-flag-on-a-socket
236 # https://stackoverflow.com/questions/26440761/why-isnt-dont-fragment-flag-getting-disabled
237 
238 # STEP-8b: Bind before connect (optional).
239 # This trick enables us to ask the kernel to select a specific source IP and
240 # source PORT by calling bind() before calling connect().
241 # -----------------------------------------------------------------------------
242 if 0:
243  try:
244  udpSock.bind(hostAssociation)
245  print('Binding the socket address of the HOST to {%s, %d}' % hostAssociation)
246  except Exception as exc:
247  print("[EXCEPTION] %s" % exc)
248  exit(1)
249 
250 # STEP-9a: Connect to the remote FPGA
251 # Info: Although UDP is connectionless, 'connect()' might still be called. This enables
252 # the OS kernel to set the default destination address for the send, whick makes it
253 # faster to send a message.
254 # -----------------------------------------------------------------------------
255 try:
256  udpSock.connect(fpgaAssociation)
257 except Exception as exc:
258  print("[EXCEPTION] %s" % exc)
259  exit(1)
260 else:
261  print('\nSuccessful connection with socket address of FPGA at {%s, %d} \n' % fpgaAssociation)
262 
263 # STEP-9b: Set the socket non-blocking
264 # --------------------------------------
265 udpSock.setblocking(False)
266 udpSock.settimeout(5)
267 
268 # STEP-10: Setup the test
269 # -------------------------------
270 print("[INFO] Testcase `%s` is run with:" % (os.path.basename(__file__)))
271 seed = args.seed
272 if seed == -1:
273  seed = random.randint(0, 100000)
274 random.seed(seed)
275 
276 print("\t\t seed = %d" % seed)
277 
278 size = args.size
279 if size == -1:
280  size = random.randint(1, UDP_MDS)
281 elif size > UDP_MDS:
282  print('\nERROR: ')
283  print("[ERROR] The UDP stack does not support the reception of datagrams larger than %d bytes.\n" % UDP_MDS)
284  exit(1)
285 print("\t\t size = %d" % size)
286 
287 count = args.loop_count
288 print("\t\t loop = %d" % count)
289 
290 if seed % 1:
291  message = str_static_gen(size)
292 else:
293  message = str_rand_gen(size)
294 
295 verbose = args.verbose
296 
297 # STEP-11: Run the test
298 # -------------------------------
299 print("[INFO] This testcase is sending traffic from HOST-to-FPGA.")
300 print("[INFO] This run is executed in single-threading mode.\n")
301 if seed == 0:
302  udp_tx_ramp(udpSock, message, count, args.verbose)
303 else:
304  udp_tx_loop(udpSock, message, count, args.verbose)
305 
306 # STEP-14: Close socket
307 # -----------------------
308 time.sleep(2)
309 udpSock.close()
310 
311 
def udp_tx_ramp(sock, message, count, verbose=False)
Definition: tc_UdpSend.py:89
def udp_tx_loop(sock, message, count, verbose=False)
Definition: tc_UdpSend.py:37
def restartApp(instId, ipResMngr, portResMngr, user_name, user_passwd)
Definition: tc_utils.py:174
def getFpgaIpv4(args)
Definition: tc_utils.py:95
def getInstanceId(args)
Definition: tc_utils.py:153
def getResourceManagerIpv4(args)
Definition: tc_utils.py:124
def str_rand_gen(size)
Definition: tc_utils.py:89
def str_static_gen(size)
Definition: tc_utils.py:79
def pingFpga(ipFpga)
Definition: tc_utils.py:199
def getResourceManagerPort(args)
Definition: tc_utils.py:142