transmit_segment_example.c Source File

Reference Documentation

Platform
Intel® PAC
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.10
Napatech Software Suite: examples/net/transmit_segment/transmit_segment_example.c Source File
transmit_segment_example.c
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2023 Napatech A/S. All Rights Reserved.
4  *
5  * 1. Copying, modification, and distribution of this file, or executable
6  * versions of this file, is governed by the terms of the Napatech Software
7  * license agreement under which this file was made available. If you do not
8  * agree to the terms of the license do not install, copy, access or
9  * otherwise use this file.
10  *
11  * 2. Under the Napatech Software license agreement you are granted a
12  * limited, non-exclusive, non-assignable, copyright license to copy, modify
13  * and distribute this file in conjunction with Napatech SmartNIC's and
14  * similar hardware manufactured or supplied by Napatech A/S.
15  *
16  * 3. The full Napatech Software license agreement is included in this
17  * distribution, please see "NP-0405 Napatech Software license
18  * agreement.pdf"
19  *
20  * 4. Redistributions of source code must retain this copyright notice,
21  * list of conditions and the following disclaimer.
22  *
23  * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES, EXPRESS OR
24  * IMPLIED, AND NAPATECH DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING ANY
25  * IMPLIED WARRANTY OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, OR OF
26  * FITNESS FOR A PARTICULAR PURPOSE. TO THE EXTENT NOT PROHIBITED BY
27  * APPLICABLE LAW, IN NO EVENT SHALL NAPATECH BE LIABLE FOR PERSONAL INJURY,
28  * OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER,
29  * INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, CORRUPTION OR
30  * LOSS OF DATA, FAILURE TO TRANSMIT OR RECEIVE ANY DATA OR INFORMATION,
31  * BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING
32  * OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE NAPATECH SOFTWARE OR
33  * SERVICES OR ANY THIRD PARTY SOFTWARE OR APPLICATIONS IN CONJUNCTION WITH
34  * THE NAPATECH SOFTWARE OR SERVICES, HOWEVER CAUSED, REGARDLESS OF THE THEORY
35  * OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF NAPATECH HAS BEEN
36  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW
37  * THE EXCLUSION OR LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF
38  * INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU.
39  *
40  *
41 
42  */
43 
44 /**
45  * @example net/transmit_segment/transmit_segment_example.c
46  * @section transmit_segment_example_description Description
47  *
48  * This source file is an example of how to transmit packets using the segment
49  * interface in NTAPI. The example will transmit 2500000 packets with a size of
50  * 1024 bytes from port 0. The packet contains an incrementing 32bit pattern.
51  *
52  * The following NTAPI functions are used:
53  * - @ref NT_Init()
54  * - @ref NT_NetTxOpen()
55  * - @ref NT_NetTxGet()
56  * - @ref NT_NET_GET_PKT_L2_PTR()
57  * - @ref NT_NET_SET_PKT_CAP_LENGTH()
58  * - @ref NT_NET_SET_PKT_WIRE_LENGTH()
59  * - @ref NT_NET_SET_PKT_CLEAR_DESCR_NT()
60  * - @ref NT_NET_SET_PKT_DESCR_TYPE_NT()
61  * - @ref NT_NET_SET_PKT_TXPORT()
62  * - @ref NT_NET_SET_PKT_TXIGNORE()
63  * - @ref NT_NET_UPDATE_PKT_L2_PTR()
64  * - @ref NT_NetTxRelease()
65  * - @ref NT_NetTxClose()
66  * - @ref NT_Done()
67  * - @ref NT_ExplainError()
68  *
69  * @section transmit_segment_example_prerequisites Prerequisites
70  * - The ntservice.ini must have at least one HostBuffersTx defined. Below is
71  * an example of a minimum ini-file. It will create a 4MB TX hostbuffer from
72  * NUMA node 0.
73  *
74  * @code
75  * [System]
76  * TimestampFormat = NATIVE
77  *
78  * [Adapter0]
79  * AdapterType = NT20E2
80  * BusId = 00:0a:00.00
81  * HostBuffersTx = [1,4,0]
82  * @endcode
83  *
84  * @section transmit_segment_example_flow Program flow
85  * @{
86  * The following is required to transmit packages:
87  * - \#include/nt.h - Applications/Tools only need to include @ref
88  * nt.h to obtain prototypes, macros etc. from NTAPI.
89  * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
90  * library. @ref NTAPI_VERSION is a define that describes the version
91  * of the API described in the header files included by @ref
92  * nt.h. NT_Init() will ask the NTAPI library to convert return data
93  * to the @ref NTAPI_VERSION if possible. This will ensure that
94  * applications can run on NTAPI libraries of newer versions.
95  * - @ref NT_NetTxOpen() - Open a hostbuffer than can transmit packets to port 0.
96  * - @ref NT_NetTxGet() - Get an empty tx buffer. This will get a 1024 byte
97  * wire length packet buffer that will be sent onto port 0 when
98  * released.
99  * - @ref _nt_net_build_pkt_netbuf() - Initialize a view into the fiest packet
100  * of the segment.
101  * - While there is still room in the segment:
102  * - @ref NT_NET_SET_PKT_CLEAR_DESCR_NT() - Zero the packet descriptor.
103  * - @ref NT_NET_SET_PKT_DESCR_TYPE_NT() - Set the packet descriptor type.
104  * - @ref NT_NET_UPDATE_PKT_L2_PTR() - Recalculate the pointer to the packet
105  * payload.
106  * - @ref NT_NET_SET_PKT_TXPORT() - Set the transmit port in the packet
107  * descriptor.
108  * - @ref NT_NET_SET_PKT_CAP_LENGTH() - Set the aligned packet buffer size.
109  * This macro automatically aligns and adds the descriptor length.
110  * - @ref NT_NET_SET_PKT_WIRE_LENGTH() - Set the payload length.
111  * - @ref NT_NET_SET_PKT_TXIGNORE() - If this is a padding packet, ignore
112  * the packet.
113  * - _nt_net_get_next_packet() - Move the view to the next packet in the
114  * buffer. Loop.
115  * - @ref NT_NetTxRelease() - Release the tx packet buffer. Once a tx
116  * buffer is released it will be transmitted
117  * - @ref NT_NetTxClose() - Close the TX stream.
118  * - @ref NT_Done() - Close down the NTAPI library.
119  *
120  *<hr>
121  * @section transmit_segment_example_code Code
122  * @}
123  */
124 
125 // Include this in order to access the Napatech API
126 #include <nt.h>
127 
128 #ifdef WIN32
129  #include <windows.h>
130 #else
131  #include <unistd.h>
132 #endif
133 
134 #if defined(WIN32) || defined(WIN64)
135  #define snprintf(dst, ...) _snprintf_s((dst), _countof(dst), __VA_ARGS__)
136 #endif
137 
138  // default TX packet test setup
139 #define PACKETS 2500000
140 #define PACKET_SIZE 1024 // Packet size to transmit (incl crc.)
141 #define SEGMENT_SIZE (1024 * 1024)
142 #define PORT 0
143 
144 // A segment has a fixed size, and if we do not use it all, we have to pad
145 // it with TX_IGNORE packets. However, a TX_IGNORE packet uses at least
146 // 64 + NT_DESCR_NT_LENGTH bytes, so we must stop adding packets if it
147 // would leave less space left than that.
148 #define MIN_PADDING_SIZE_WITH_DESCR (64 + NT_DESCR_NT_LENGTH)
149 #define PADDING_THRESHOLD (PACKET_SIZE + NT_DESCR_NT_LENGTH + MIN_PADDING_SIZE_WITH_DESCR)
150 
151 static void msleep(int ms)
152 {
153 #ifdef _WIN32
154  Sleep(ms);
155 #else
156  usleep(ms * 1000);
157 #endif
158 }
159 
160 // printError is a simple convenience function for printing an NTAPI error
161 // message to stderr.
162 static void printError(const char *prefix, int errorCode) {
164  NT_ExplainError(errorCode, errorBuffer, sizeof errorBuffer);
165  fprintf(stderr, "%s: %s\n", prefix, errorBuffer);
166 }
167 
168 int main(void)
169 {
170  //
171  // Initialize the API. This also checks if we are compatible with the
172  // installed library version.
173  //
174  int status;
175  bool isFpga4Garch;
176 
177  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
178  printError("NT_Init() failed", status);
179  return -1;
180  }
181 
182  //
183  // Open info stream to retrieve port info for our port. We are specifically
184  // interested in the "maxTxPktSize" parameter, which tells us maximum packet
185  // size the adapter can process. This is used later for padding purposes.
186  //
187  NtInfoStream_t hInfo;
188  NtInfo_t info;
189 
190  if ((status = NT_InfoOpen(&hInfo,
191  "transmit_segment_example_info")) != NT_SUCCESS) {
192  printError("NT_InfoOpen() failed", status);
193  return -1;
194  }
195 
197  info.u.port_v9.portNo = PORT;
198  if ((status = NT_InfoRead(hInfo, &info)) != NT_SUCCESS) {
199  NT_InfoClose(hInfo);
200  printError("NT_InfoRead() failed", status);
201  return -1;
202  }
203 
204  // Save adapter number of the TX port
205  const int adapterNo = info.u.port_v9.data.adapterNo;
206 
208  snprintf(info.u.property.path, sizeof(info.u.property.path), "Adapter%d.FpgaGeneration", adapterNo);
209 
210  if ((status = NT_InfoRead(hInfo, &info)) != NT_SUCCESS) {
211  NT_InfoClose(hInfo);
212  printError("xNT_InfoRead() failed", status);
213  return -1;
214  }
215 
216  isFpga4Garch = info.u.property.data.u.i >= 4;
217 
218  if ((status = NT_InfoClose(hInfo)) != NT_SUCCESS) {
219  printError("NT_InfoClose() failed", status);
220  return -1;
221  }
222 
223  // Max packet size (descriptor length added for convenience in later
224  // calculations).
225  uint64_t maxTxSizeWithDescr = info.u.port_v9.data.capabilities.maxTxPktSize;
226  maxTxSizeWithDescr += NT_DESCR_NT_LENGTH;
227 
228  //
229  // Open a TX stream
230  //
231  NtNetStreamTx_t hNetTx;
232 
233  status = NT_NetTxOpen(&hNetTx, "transmit_segment_example_txstream",
234  1ULL << PORT, NT_NETTX_NUMA_ANY_HB, 0);
235  if (status != NT_SUCCESS) {
236  printError("NT_NetTxOpen() failed", status);
237  return -1;
238  }
239 
240  //
241  // Retrieve a segment, fill it with packets and transmit it, and repeat
242  // until we have transmitted the requested amount of packets.
243  //
244  printf("Commencing transmission\n");
245 
246  NtNetBuf_t hNetBufTx;
247  int numPackets = 0;
248 
249  while (numPackets < PACKETS) {
250 
251  // Get a segment TX buffer for this tx stream and port, without timeout.
252  if ((status = NT_NetTxGet(hNetTx, &hNetBufTx, PORT, SEGMENT_SIZE,
253  NT_NETTX_SEGMENT_OPTION_RAW, -1)) != NT_SUCCESS) {
254  printError("NT_NetTxGet() failed", status);
255  return -1;
256  }
257 
258  // Prepare the NetBuf.
259  struct NtNetBuf_s pktNetBuf;
260  _nt_net_build_pkt_netbuf(hNetBufTx, &pktNetBuf);
261  uint64_t spaceLeftInSegment = NT_NET_GET_SEGMENT_LENGTH(hNetBufTx);
262 
263  // Fill the segment.
264  while (true) {
265  if (spaceLeftInSegment == 0)
266  break;
267 
268  // Our buffers are recycled, so start out by clearing the descriptor.
269  NT_NET_SET_PKT_CLEAR_DESCR_NT(&pktNetBuf);
270  NT_NET_SET_PKT_DESCR_TYPE_NT(&pktNetBuf);
271  NT_NET_UPDATE_PKT_L2_PTR(&pktNetBuf);
272  NT_NET_SET_PKT_TXPORT(&pktNetBuf, PORT);
273 
274  if (numPackets == PACKETS || (spaceLeftInSegment < PADDING_THRESHOLD)) {
275 
276  // We're out of space, or we're done transmitting packets. Pad the rest
277  // of the segment. In order to do so, we must calculate the largest
278  // possible value for paddingWithDescr that satisfies the following
279  // constraint, whilst ensuring that any necessary later padding is also
280  // capable of satisfying the constraint:
281  //
282  // spaceLeftInSegment >= paddingWithDescr <= maxTxSizeWithDescr
283  //
284  uint64_t paddingWithDescr;
285  if (spaceLeftInSegment <= maxTxSizeWithDescr) {
286 
287  // We can fill the rest of the segment in one go.
288  paddingWithDescr = spaceLeftInSegment;
289  } else if ((spaceLeftInSegment - MIN_PADDING_SIZE_WITH_DESCR) >=
290  maxTxSizeWithDescr) {
291 
292  // There is room for many more packets, so pad as much as we can.
293  paddingWithDescr = maxTxSizeWithDescr;
294  } else {
295 
296  // We cannot fill the entire segment, but there's not much more room
297  // left, so make sure we leave room for another padding packet.
298  paddingWithDescr = spaceLeftInSegment - MIN_PADDING_SIZE_WITH_DESCR;
299  }
300 
301  uint64_t paddingNoDescr = paddingWithDescr - NT_DESCR_NT_LENGTH;
302 
303  // NT_NET_SET_PKT_CAP_LENGTH handles alignment, and adds the length
304  // of the descriptor before assignment.
305  NT_NET_SET_PKT_CAP_LENGTH(&pktNetBuf, (uint16_t)paddingNoDescr);
306  NT_NET_SET_PKT_WIRE_LENGTH(&pktNetBuf, (uint16_t)paddingNoDescr);
307 
308  // Do not transmit this padding packet.
309  NT_NET_SET_PKT_TXIGNORE(&pktNetBuf, 1);
310  } else {
311 
312  // NT_NET_SET_PKT_CAP_LENGTH handles alignment, and adds the length
313  // of the descriptor before assignment.
314  NT_NET_SET_PKT_CAP_LENGTH(&pktNetBuf, (uint16_t)PACKET_SIZE);
315  NT_NET_SET_PKT_WIRE_LENGTH(&pktNetBuf, (uint16_t)PACKET_SIZE);
316 
317  // Fill the packet with an incrementing payload. Note that this will
318  // result in a garbage ethernet frame.
319  uint32_t *ptr = (uint32_t*)NT_NET_GET_PKT_L2_PTR(&pktNetBuf);
320  for (uint32_t i = 0; i < PACKET_SIZE/4; i++) {
321  *(ptr+i) = i;
322  }
323 
324  //On 4Garch the ethernet FCS is calculated by the FPGA itself, and it is
325  //not mandatory to specify the recalculation. Calculating it anyway will
326  //make no harm however. At 3Garch the recalculation is mandatory.
327  if (!isFpga4Garch)
328  NT_NET_SET_PKT_RECALC_L2_CRC(&pktNetBuf, 1);
329 
330  numPackets++;
331  }
332 
333 
334  // Get the next NetBuf and get the remaining segment size.
335  spaceLeftInSegment = _nt_net_get_next_packet(hNetBufTx,
336  NT_NET_GET_SEGMENT_LENGTH(hNetBufTx), &pktNetBuf);
337  }
338 
339  // Release the TX buffer to transmit the segment.
340  if ((status = NT_NetTxRelease(hNetTx, hNetBufTx)) != NT_SUCCESS) {
341  printError("NT_NetTxRelease() failed", status);
342  return -1;
343  }
344  }
345 
346  //Wait until all packet have been delivered to the FPGA and this when all
347  //packets have left host memory
348  int timeOut = 0;
349 
350  while (true) {
351  if (timeOut >= 1000) { //Wait max 1 second
352  printError("Timeout waiting for data to be sent", NT_ERROR_OPERATION_TIMEOUT);
353  break;
354  }
355 
356  // Evaluate if hostbuffer contents have been delivered to FPGA
357  // This is used to detect when Tx data has left host memory
358  NtNetTx_t ntNetTx;
360  status = NT_NetTxRead(hNetTx, &ntNetTx);
361 
362  if (status != NT_SUCCESS) {
363  printError("NT_NetTxRead failed", status);
364  break;
365  }
366 
367  size_t nHbSizeTotal = ntNetTx.u.hbInfo.aHostBuffer[0].size;
368  size_t nHbSizeAvail = ntNetTx.u.hbInfo.aHostBuffer[0].available;
369  size_t nHbSizeRel = ntNetTx.u.hbInfo.aHostBuffer[0].released;
370  size_t nHbSizeDeq = ntNetTx.u.hbInfo.aHostBuffer[0].dequeued;
371 
372  if ((nHbSizeAvail == nHbSizeTotal) && (nHbSizeRel == 0) && (nHbSizeDeq == 0))
373  break;
374 
375  msleep(1); // Dont busy wait but sleep 1 ms
376  timeOut++;
377  };
378 
379  //printf("timeOut = %dms\n", timeOut); //Find timeout value
380  printf("Done: %d packets sent\n", numPackets);
381 
382  // Close the TX stream
383  NT_NetTxClose(hNetTx);
384 
385  // Close the API
386  NT_Done();
387 
388  return 0;
389 }