transmit_segment_example.c Source File

Reference Documentation

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