cloudFPGA (cF) API  1.0
The documentation of the source code of cloudFPGA (cF)
test_icmp.cpp
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 
30 #include "test_icmp.hpp"
31 
32 using namespace hls;
33 using namespace std;
34 
35 //---------------------------------------------------------
36 // HELPERS FOR THE DEBUGGING TRACES
37 // .e.g: DEBUG_LEVEL = (CGF_TRACE | IPS_TRACE)
38 //---------------------------------------------------------
39 #define THIS_NAME "TB"
40 
41 #define TRACE_OFF 0x0000
42 #define TRACE_CGF 1 << 1
43 #define TRACE_ALL 0xFFFF
44 
45 #define DEBUG_LEVEL (TRACE_OFF)
46 
47 
50 void stepSim() {
51  gSimCycCnt++;
52  if (gTraceEvent || ((gSimCycCnt % 1000) == 0)) {
53  printInfo(THIS_NAME, "-- [@%4.4d] -----------------------------\n", gSimCycCnt);
54  gTraceEvent = false;
55  }
56  else if (0) {
57  printInfo(THIS_NAME, "------------------- [@%d] ------------\n", gSimCycCnt);
58  }
59 }
60 
61 
71  string inpDAT_FileName,
72  string outDAT_GoldName,
73  Ip4Addr myIp4Address)
74 {
75  const char *myName = concat3(THIS_NAME, "/", "CGF");
76 
77  ifstream ifsDAT;
78  ofstream ofsDAT;
79 
80  char currPath[FILENAME_MAX];
81  int ret=NTS_OK;
82  int inpChunks=0, outChunks=0;
83  int inpPackets=0, outPackets=0;
84  int inpBytes=0, outBytes=0;
85 
86  //-- STEP-1 : OPEN INPUT FILE AND ASSESS ITS EXTENSION
87  ifsDAT.open(inpDAT_FileName.c_str());
88  if (!ifsDAT) {
89  getcwd(currPath, sizeof(currPath));
90  printError(myName, "Cannot open the file: %s \n\t (FYI - The current working directory is: %s) \n",
91  inpDAT_FileName.c_str(), currPath);
92  return(NTS_KO);
93  }
94  if (not isDatFile(inpDAT_FileName)) {
95  printError(myName, "Cannot create golden files from input file \'%s\' because file is not of type \'.dat\'.\n",
96  inpDAT_FileName.c_str());
97  ifsDAT.close();
98  return(NTS_KO);
99  }
100 
101  //-- STEP-2 : OPEN THE OUTPUT GOLD FILE
102  remove(outDAT_GoldName.c_str());
103  if (!ofsDAT.is_open()) {
104  ofsDAT.open (outDAT_GoldName.c_str(), ofstream::out);
105  if (!ofsDAT) {
106  printFatal(myName, "Could not open the output gold file \'%s\'. \n",
107  outDAT_GoldName.c_str());
108  }
109  }
110 
111  //-- STEP-3 : READ AND PARSE THE INPUT ICMP FILE
112  while ((ifsDAT.peek() != EOF) && (ret != NTS_KO)) {
113  SimIp4Packet ipPacket;
114  AxisIp4 axisIp4;
115  bool endOfPkt=false;
116  bool rc;
117  // Read one packet at a time from input file
118  while ((ifsDAT.peek() != EOF) && (!endOfPkt)) {
119  rc = readAxisRawFromFile(axisIp4, ifsDAT);
120  if (rc) {
121  if (axisIp4.isValid()) {
122  ipPacket.pushChunk(axisIp4);
123  if (axisIp4.getLE_TLast()) {
124  inpPackets++;
125  endOfPkt = true;
126  }
127  }
128  else {
129  // We always abort the stream as this point by asserting
130  // 'tlast' and de-asserting 'tkeep'.
131  axisIp4.setLE_TKeep(0x00);
132  axisIp4.setLE_TLast(TLAST);
133  ipPacket.pushChunk(axisIp4);
134  inpPackets++;
135  endOfPkt = true;
136  }
137  inpChunks++;
138  inpBytes += axisIp4.getLen();
139  }
140  }
141  // Check consistency of the read packet
142  if (endOfPkt and rc) {
143  if (not ipPacket.isWellFormed(myName)) {
144  printError(myName, "IP packet #%d is dropped because it is malformed.\n", inpPackets);
145  endOfPkt=false;
146  }
147  }
148  // Build an ICMP reply packet based on the ICMP packet read from file
149  if (endOfPkt) {
150  // Assess EtherType is ICMP
151  Ip4Prot ip4Prot = ipPacket.getIpProtocol();
152  if (ip4Prot != ICMP_PROTOCOL) {
153  printWarn(myName, "IP packet #%d is dropped because it is not an ICMP packet.\n");
154  printInfo(myName, " Received Ip4Prot = 0x%2.2X\n", ip4Prot.to_uchar());
155  printInfo(myName, " Expected Ip4Prot = 0x%2.2X\n", ICMP_PROTOCOL.to_uint());
156  continue;
157  }
158 
159  // Retrieve the ICMP packet from the IPv4 Packet
160  SimIcmpPacket icmpDataPacket;
161  icmpDataPacket = ipPacket.getIcmpPacket();
162 
163  IcmpCsum icmpHCsum = icmpDataPacket.getIcmpChecksum();
164  if (icmpDataPacket.calculateIcmpChecksum() != 0) {
165  // ICMP packet comes with an invalid checksum
166  printInfo(myName, "IP4 packet #%d contains an ICMP message with an invalid checksum. It will be dropped by the ICMP core.\n", inpPackets);
167  printInfo(myName, "\tFound checksum field=0x%4.4X, Was expecting 0x%4.4X)\n",
168  icmpHCsum.to_uint(),
169  icmpDataPacket.reCalculateIcmpChecksum().to_uint());
170  }
171  else if ((icmpDataPacket.getIcmpType() == ICMP_ECHO_REQUEST) &&
172  (icmpDataPacket.getCode() == 0)) {
173  // Assess the 'Type' and 'Code' of the ICMP message
174  printInfo(myName, "IP4 packet #%d contains an ICMP Echo Request message.\n", inpPackets);
175  printInfo(myName, "\t\t(FYI: the ICMP checksum of this message = 0x%4.4X)\n", icmpDataPacket.getIcmpChecksum().to_uint());
176 
177  //-- Build ICMP gold ECHO REPLY message as a clone of the incoming packet
178  printInfo(myName, "\tBuilding a gold ICMP Echo Reply message.\n");
179  SimIp4Packet ipGoldPacket;
180  ipGoldPacket.cloneHeader(ipPacket);
181  // Swap IP_SA and IP_DA
182  ipGoldPacket.setIpDestinationAddress(ipPacket.getIpSourceAddress());
183  ipGoldPacket.setIpSourceAddress(ipPacket.getIpDestinationAddress());
184  // Retrieve the ICMP packet
185  SimIcmpPacket icmpGoldPacket = ipPacket.getIcmpPacket();
186  // Replace ICMP/ECHO_REQUEST field with ICMP/ECHO_REPLY
187  icmpGoldPacket.setIcmpType(ICMP_ECHO_REPLY);
188  icmpGoldPacket.setIcmpCode(0);
189  IcmpCsum newCsum = icmpGoldPacket.reCalculateIcmpChecksum();
190  printInfo(myName, "\t\t(the new ICMP checksum = 0x%4.4X)\n", newCsum.to_uint());
191  // Set the ICMP gold packet as data payload of the IP4 packet.
192  if (ipGoldPacket.addIpPayload(icmpGoldPacket) == false) {
193  printError(myName, "Failed to add ICMP packet as payload to an IP4 packet.\n");
194  ret = NTS_KO;
195  }
196  else if (ipGoldPacket.writeToDatFile(ofsDAT) == false) {
197  printError(myName, "Failed to write IP4 packet to DAT file.\n");
198  ret = NTS_KO;
199  }
200  else {
201  outPackets += 1;
202  outChunks += ipGoldPacket.size();
203  outBytes += ipGoldPacket.length();
204  }
205  }
206  else {
207  printInfo(myName, "IP4 packet #%d contains an unsupported ICMP message. It will be dropped by the ICMP core.\n", inpPackets);
208  printInfo(myName, "\t(FYI: the ICMP checksum of this message is 0x%4.4X)\n", icmpDataPacket.getIcmpChecksum().to_uint());
209  printInfo(myName, "\t(FYI: the message-type is %d and the message-code is %d)\n",
210  icmpDataPacket.getIcmpType().to_uint(), icmpDataPacket.getCode().to_uint());
211  }
212  } // End-of if (endOfPacket)
213  } // End-of While ()
214 
215  //-- STEP-4: CLOSE FILES
216  ifsDAT.close();
217  ofsDAT.close();
218 
219  //-- STEP-5: PRINT RESULTS
220  printInfo(myName, "Done with the creation of the golden file.\n");
221  printInfo(myName, "\tProcessed %5d chunks in %4d packets, for a total of %6d bytes.\n",
222  inpChunks, inpPackets, inpBytes);
223  printInfo(myName, "\tGenerated %5d chunks in %4d packets, for a total of %6d bytes.\n",
224  outChunks, outPackets, outBytes);
225  return(ret);
226 }
227 
228 #if HLS_VERSION != 2017
229 
244  //-- MMIO Interfaces
245  Ip4Addr piMMIO_Ip4Address,
246  //-- IPRX Interfaces
247  stream<AxisIp4> &siIPRX_Data,
248  stream<AxisIp4> &siIPRX_Derr,
249  //-- UOE Interface
250  stream<AxisIcmp> &siUOE_Data,
251  //-- IPTX Interface
252  stream<AxisIp4> &soIPTX_Data)
253 {
254  //-- LOCAL INPUT and OUTPUT STREAMS ----------------------------------------
255  static stream<AxisRaw> ssiIPRX_Data ("ssiIPRX_Data");
256  static stream<AxisRaw> ssiIPRX_Derr ("ssiIPRX_Derr");
257  static stream<AxisRaw> ssiUOE_Data ("ssiUOE_Data");
258  static stream<AxisRaw> ssoIPTX_Data ("ssoIPTX_Data");
259 
260  //-- INPUT STREAM CASTING --------------------------------------------------
261  pAxisRawCast(siIPRX_Data, ssiIPRX_Data);
262  pAxisRawCast(siIPRX_Derr, ssiIPRX_Derr);
263  pAxisRawCast(siUOE_Data, ssiUOE_Data);
264 
265  //-- MAIN ICMP PROCESS -----------------------------------------------------
266  icmp_top(
267  //-- MMIO Interfaces
268  piMMIO_Ip4Address,
269  //-- IPRX Interfaces
270  ssiIPRX_Data,
271  ssiIPRX_Derr,
272  //-- UOE Interface
273  ssiUOE_Data,
274  //-- IPTX Interface
275  ssoIPTX_Data);
276 
277  //-- OUTPUT STREAM CASTING -------------------------------------------------
278  pAxisRawCast(ssoIPTX_Data, soIPTX_Data);
279 }
280 #endif
281 
282 
287 int main(int argc, char* argv[]) {
288 
289  //------------------------------------------------------
290  //-- TESTBENCH GLOBAL VARIABLES
291  //------------------------------------------------------
292  gTraceEvent = false;
293  gFatalError = false;
294  gSimCycCnt = 0;
296 
297  //------------------------------------------------------
298  //-- TESTBENCH LOCAL VARIABLES
299  //------------------------------------------------------
300  int nrErr = 0; // Total number of testbench errors
301  int tbRun = 0; // Total duration of the test (in clock cycles0
302  Ip4Addr myIp4Address = 0x01010101; // Might be overwritten by the content of the DAT file.
303 
304  string ofsICMP_IPTX_Data_FileName = "../../../../test/simOutFiles/soIPTX_Data.dat";
305  string ofsICMP_IPTX_Gold_FileName = "../../../../test/simOutFiles/soIPTX_Gold.dat";
306 
307  //------------------------------------------------------
308  //-- DUT STREAM INTERFACES and RELATED VARIABLEs
309  //------------------------------------------------------
310  //-- From IPRX
311  stream<AxisIp4> ssIPRX_ICMP_Data ("ssIPRX_ICMP_Data");
312  int nrIPRX_ICMP_Chunks = 0;
313  int nrIPRX_ICMP_Packets= 0;
314  int nrIPRX_ICMP_Bytes = 0;
315  stream<AxisIp4> ssIPRX_ICMP_Derr ("ssIPRX_ICMP_Derr");
316  //-- From UDP
317  stream<AxisIcmp> ssUDP_ICMP_Data ("ssUDP_ICMP_Data");
318  //-- To IPTX (via L3MUX)
319  stream<AxisIp4> ssICMP_IPTX_Data ("ssICMP_IPTX_Data");
320  int nrICMP_IPTX_Chunks = 0;
321  int nrICMP_IPTX_Packets= 0;
322  int nrICMP_IPTX_Bytes = 0;
323 
324  //------------------------------------------------------
325  //-- READ GLOBAL PARAMETERS FROM INPUT TEST VECTOR FILE
326  //------------------------------------------------------
327  if (argc != 2) {
328  printFatal(THIS_NAME, "Missing testbench parameter:\n\t Expecting an input test vector file.\n");
329  }
330  unsigned int param;
331  if (readTbParamFromFile("FpgaIp4Addr", string(argv[1]), param)) {
332  myIp4Address = param;
333  printIp4Addr(THIS_NAME, "The input test vector is setting the IP address of the FPGA to", myIp4Address);
334  }
335 
336  //------------------------------------------------------
337  //-- CREATE DUT INPUT TRAFFIC AS STREAMS
338  //------------------------------------------------------
339  // [TODO - Generate traffic for 'ssIPRX_ICMP_Derr' and 'ssUDP_ICMP_Data']
340  if (feedAxisFromFile<AxisIp4>(ssIPRX_ICMP_Data, "ssIPRX_ICMP_Data", string(argv[1]),
341  nrIPRX_ICMP_Chunks, nrIPRX_ICMP_Packets, nrIPRX_ICMP_Bytes)) {
342  printInfo(THIS_NAME, "Done with the creation of the input traffic as streams:\n");
343  printInfo(THIS_NAME, "\tGenerated %d chunks in %d frames, for a total of %d bytes.\n\n",
344  nrIPRX_ICMP_Chunks, nrIPRX_ICMP_Packets, nrIPRX_ICMP_Bytes);
345  }
346  else {
347  printError(THIS_NAME, "Failed to create traffic as input stream. \n");
348  nrErr++;
349  }
350 
351  //------------------------------------------------------
352  //-- CREATE DUT OUTPUT TRAFFIC AS STREAMS
353  //------------------------------------------------------
354  ofstream ofsIPTX_Data;
355  string ofsIPTX_Data_FileName = "../../../../test/simOutFiles/soIPTX_Data.dat";
356  string ofsIPTX_Gold_FileName = "../../../../test/simOutFiles/soIPTX_Gold.dat";
357 
358  //-- Remove previous file
359  remove(ofsIPTX_Data_FileName.c_str());
360  //-- Assess that file has ".dat" extension
361  if (not isDatFile(ofsIPTX_Data_FileName)) {
362  printError(THIS_NAME, "File \'%s\' is not of type \'DAT\'.\n", ofsIPTX_Data_FileName.c_str());
363  ofsIPTX_Data.close();
364  nrErr++;
365  }
366  //-- Open file
367  if (!ofsIPTX_Data.is_open()) {
368  ofsIPTX_Data.open(ofsIPTX_Data_FileName.c_str(), ofstream::out);
369  if (!ofsIPTX_Data) {
370  printError(THIS_NAME, "Cannot open the file: \'%s\'.\n", ofsIPTX_Data_FileName.c_str());
371  nrErr++;
372  }
373  }
374 
375  //------------------------------------------------------
376  //-- CREATE OUTPUT GOLD TRAFFIC
377  //------------------------------------------------------
378  if (not createGoldenFile(string(argv[1]), ofsIPTX_Gold_FileName, myIp4Address)) {
379  printError(THIS_NAME, "Failed to create golden file. \n");
380  nrErr++;
381  }
382 
383  printf("\n\n");
384  printInfo(THIS_NAME, "############################################################################\n");
385  printInfo(THIS_NAME, "## TESTBENCH 'test_icmp' STARTS HERE ##\n");
386  printInfo(THIS_NAME, "############################################################################\n");
387 
388  //-----------------------------------------------------
389  //-- MAIN LOOP : Handle incoming ICMP packets
390  //-----------------------------------------------------
391  tbRun = (nrErr == 0) ? (nrIPRX_ICMP_Chunks + TB_GRACE_TIME) : 0;
392  while (tbRun) {
393  //-- RUN DUT --------------------------------------
394  #if HLS_VERSION == 2017
395  icmp_top(
396  myIp4Address,
397  ssIPRX_ICMP_Data,
398  ssIPRX_ICMP_Derr,
399  ssUDP_ICMP_Data,
400  ssICMP_IPTX_Data);
401  #else
403  myIp4Address,
404  ssIPRX_ICMP_Data,
405  ssIPRX_ICMP_Derr,
406  ssUDP_ICMP_Data,
407  ssICMP_IPTX_Data);
408  #endif
409  tbRun--;
410  stepSim();
411  } // End-of: while()
412 
413  //---------------------------------------------------------------
414  //-- DRAIN ICMP-->IPTX OUTPUT STREAM
415  //---------------------------------------------------------------
416  if (not drainAxisToFile<AxisIp4>(ssICMP_IPTX_Data, "ssICMP_IPTX_Data",
417  ofsICMP_IPTX_Data_FileName, nrICMP_IPTX_Chunks,
418  nrICMP_IPTX_Packets, nrICMP_IPTX_Bytes)) {
419  printError(THIS_NAME, "Failed to drain ICMP-to-IPTX traffic from DUT. \n");
420  nrErr++;
421  }
422 
423  printInfo(THIS_NAME, "############################################################################\n");
424  printInfo(THIS_NAME, "## TESTBENCH 'test_icmp' ENDS HERE ##\n");
425  printInfo(THIS_NAME, "############################################################################\n");
426  stepSim();
427 
428  //---------------------------------------------------------------
429  //-- COMPARE OUTPUT DAT and GOLD STREAMS
430  //---------------------------------------------------------------
431  int res = system(("diff --brief -w " + std::string(ofsIPTX_Data_FileName) \
432  + " " + std::string(ofsIPTX_Gold_FileName) + " ").c_str());
433  if (res) {
434  printError(THIS_NAME, "File \'%s\' does not match \'%s\'.\n", \
435  ofsIPTX_Data_FileName.c_str(), ofsIPTX_Gold_FileName.c_str());
436  nrErr += 1;
437  }
438 
439  //---------------------------------------------------------------
440  //-- PRINT TESTBENCH STATUS
441  //---------------------------------------------------------------
442  printf("\n\n");
443  printInfo(THIS_NAME, "This testbench was executed with the following test-file: \n");
444  printInfo(THIS_NAME, "\t==> %s\n\n", argv[1]);
445 
446  if (nrErr) {
447  printError(THIS_NAME, "###########################################################\n");
448  printError(THIS_NAME, "#### TEST BENCH FAILED : TOTAL NUMBER OF ERROR(S) = %2d ####\n", nrErr);
449  printError(THIS_NAME, "###########################################################\n\n");
450 
451  printInfo(THIS_NAME, "FYI - You may want to check for \'ERROR\' and/or \'WARNING\' alarms in the LOG file...\n\n");
452  }
453  else {
454  printInfo(THIS_NAME, "#############################################################\n");
455  printInfo(THIS_NAME, "#### SUCCESSFUL END OF TEST ####\n");
456  printInfo(THIS_NAME, "#############################################################\n");
457  }
458 
459  return nrErr;
460 
461 }
462 
void setLE_TLast(LE_tLast last)
Definition: AxisRaw.hpp:280
int getLen() const
Definition: AxisRaw.hpp:411
void setLE_TKeep(LE_tKeep keep, int leHi=64/8-1, int leLo=0)
Definition: AxisRaw.hpp:276
bool isValid() const
Definition: AxisRaw.hpp:434
LE_tLast getLE_TLast() const
Definition: AxisRaw.hpp:268
Class ICMP Packet for simulation.
void setIcmpCode(IcmpCode code)
IcmpType getIcmpType()
IcmpCode getCode()
IcmpCsum calculateIcmpChecksum()
IcmpCsum reCalculateIcmpChecksum()
Recalculate the ICMP checksum of a packet.
void setIcmpType(IcmpType type)
IcmpCsum getIcmpChecksum()
Class IPv4 Packet for simulation.
void setIpSourceAddress(int addr)
bool isWellFormed(const char *callerName, bool checkIp4TotLen=true, bool checkIp4HdrCsum=true, bool checkUdpLen=true, bool checkLy4Csum=true)
Checks if the IP header and embedded protocol fields are properly set.
Ip4Addr getIpSourceAddress()
void pushChunk(AxisIp4 ip4Chunk)
void cloneHeader(SimIp4Packet &ipPkt)
Clone the header of an IP packet.
void setIpDestinationAddress(int addr)
Ip4Addr getIpDestinationAddress()
bool addIpPayload(SimUdpDatagram &udpDgm, int len=-1)
Append some data to this packet from a UDP datagram.
bool writeToDatFile(ofstream &outFileStream)
Dump this IP packet as AxisIp4 chunks into a file.
SimIcmpPacket getIcmpPacket()
unsigned int gSimCycCnt
Definition: tb_nal.cpp:150
bool gTraceEvent
Definition: tb_nal.cpp:151
bool gFatalError
Definition: tb_nal.cpp:152
#define TB_STARTUP_DELAY
Definition: test_arp.hpp:53
#define TB_GRACE_TIME
Definition: test_arp.hpp:54
#define TB_MAX_SIM_CYCLES
Definition: test_arp.hpp:52
unsigned int gMaxSimCycles
Definition: test_arp.hpp:69
int main(int argc, char *argv[])
Main function.
Definition: test_icmp.cpp:287
void icmp_top_wrap(Ip4Addr piMMIO_Ip4Address, stream< AxisIp4 > &siIPRX_Data, stream< AxisIp4 > &siIPRX_Derr, stream< AxisIcmp > &siUOE_Data, stream< AxisIp4 > &soIPTX_Data)
A wrapper for Toplevel of Internet Control Message Protocol (ICMP).
Definition: test_icmp.cpp:243
void stepSim()
Increment the simulation counter.
Definition: test_icmp.cpp:50
int createGoldenFile(string inpDAT_FileName, string outDAT_GoldName, Ip4Addr myIp4Address)
Create the golden reference file from an input test file.
Definition: test_icmp.cpp:70
#define THIS_NAME
Definition: test_icmp.cpp:39
void icmp_top(Ip4Addr piMMIO_Ip4Address, stream< AxisRaw > &siIPRX_Data, stream< AxisRaw > &siIPRX_Derr, stream< AxisRaw > &siUOE_Data, stream< AxisRaw > &soIPTX_Data)
Top of the Internet Control Message Protocol (ICMP) Server.
Definition: icmp.cpp:832
const IcmpType ICMP_ECHO_REPLY
Definition: icmp.hpp:65
const Ip4Prot ICMP_PROTOCOL
Definition: icmp.hpp:73
const IcmpType ICMP_ECHO_REQUEST
Definition: icmp.hpp:67
bool isDatFile(string fileName)
Checks if a file has a ".dat" extension.
Definition: SimNtsUtils.cpp:52
bool readTbParamFromFile(const string paramName, const string datFile, unsigned int &paramVal)
Retrieve a testbench parameter from a DAT file.
bool readAxisRawFromFile(AxisRaw &axisRaw, ifstream &inpFileStream)
Retrieve an Axis raw data chunk from a file.
#define NTS_KO
Definition: nts_types.hpp:56
ap_uint< 8 > Ip4Prot
Definition: AxisIp4.hpp:164
#define printError(callerName, format,...)
A macro to print an error message.
Definition: nts_utils.hpp:195
ap_uint< 32 > Ip4Addr
Definition: AxisIp4.hpp:169
ap_uint< 16 > IcmpCsum
Definition: AxisIcmp.hpp:93
void pAxisRawCast(hls::stream< TypeIn > &si, hls::stream< TypeOut > &so)
AxisRaw cast - Casts an AxisRaw stream to/from an AxisRaw derived class.
Definition: AxisRaw.hpp:148
#define NTS_OK
Definition: nts_types.hpp:55
#define printInfo(callerName, format,...)
A macro to print an information message.
Definition: nts_utils.hpp:169
void printIp4Addr(const char *callerName, const char *message, Ip4Addr ip4Addr)
Print an IPv4 address prepended with a message (used for debugging).
Definition: nts_utils.cpp:205
#define printWarn(callerName, format,...)
A macro to print a warning message.
Definition: nts_utils.hpp:182
#define concat3(firstCharConst, secondCharConst, thirdCharConst)
Definition: nts_utils.hpp:161
#define printFatal(callerName, format,...)
A macro to print a fatal error message and exit.
Definition: nts_utils.hpp:208
#define TLAST
Definition: AxisRaw.hpp:116
out
Definition: test.py:12