INFO: This is an 4GA TX timestamp inject and FCS generation example.
This source file is an example of how to control TX time stamp inject and FCS generation per packet using dynamic descriptor 3.
One bit in the TX packet descriptor is used to control TX time stamp inject, i.e. if the packet descriptor bit is 1 a TX time stamp will be inserted in the packet.
Two bits in the TX packet descriptor is used to control FCS generation, i.e. 0: Insert good FCS, 1: Insert bad FCS, 2: Reserved 3: Don't change FCS
This program consists of a TX and RX part.
The TX part will transmit 6 packets in a loop. The number of loops is a command line option. To start the TX part use the -t <txport> option.
The six packets in a loop are based on the same packet template. Time stamp inject and FCS generation are then controlled per packet to be:
The RX part will receive packets from one port. It will check for the a TX time stamp in received packets and - if present - calculate and print the TX-to-RX latency. The RX part also dumps the received packet. To start the RX part use the -r <rxport> option.
will transmit on port 0 and receive on port 1. The transmitter will execute the loop 5 times, i.e. 30 packets will be transmitted.
#include <atomic>
#include <iostream>
#include <thread>
#include <unistd.h>
#include <signal.h>
};
{
switch (descrType)
{
printf("Descriptor type is PCAP.\n");
break;
printf("Descriptor type is NT.\n");
break;
printf("Descriptor type is NT extended.\n");
break;
printf("Descriptor type is Dynamic\n");
break;
default:
printf("Unknown descriptor type.\n");
break;
}
}
{
public:
uint32_t streamId;
std::thread thr;
streamId = (uint32_t) port;
printf("Created PacketReaderThread, port=%lu, stream=%u\n", rxPort, streamId);
}
void processPackets(void) {
if (status != 0) {
printf("NT_NetRxOpen() failed: %s\n", errorBuffer);
return;
}
printf("Receiving packets on stream %u\n", streamId);
{
{
printf("-------------------------------------------------\n");
printf("Packet received:\n");
{
printf("Error: RX descriptor expected to be NT_PACKET_DESCRIPTOR_TYPE_NT\n");
printf(" Shutting down...\n");
break;
}
if (captureLength < (16 + 80 + 2 + 8))
{
printf("Packet is too small. Can't extract time stamp\n");
}
else
{
uint64_t txTime = *((uint64_t*)(data + 80 + 2));
if (txTime == 0)
{
printf("No TX time stamp in packet.\n");
}
else
{
printf("txTime=%lu, rxTime=%lu, latency=%lu\n", txTime, rxTime, rxTime - txTime);
}
uint64_t
wireLength = NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf);
{
if ((g % 16) == 0) printf("\n");
printf("%02X ", *(data + g));
}
printf("\n");
}
}
else if ((status != NT_STATUS_TIMEOUT) && (status != NT_STATUS_TRYAGAIN))
{
printf("NT_NetRxGet() failed: %s\n", errorBuffer);
break;
}
}
}
void run(void) {
}
void join(void) {
if (thr.joinable())
{
thr.join();
}
}
};
{
0x94, 0xC6, 0x91, 0x1C, 0x68, 0x1D, 0x94, 0xC6, 0x91, 0x1C, 0x68, 0xC3, 0x08, 0x00, 0x45, 0x00,
0x00, 0x78, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xB5, 0xCF, 0xC0, 0xA8, 0x00, 0x02, 0x01, 0x02,
0x03, 0x04, 0x00, 0x14, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
0x20, 0x00, 0x42, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
{
public:
uint64_t transmittedPackets {0};
uint32_t txPort;
int64_t txLoops;
std::thread thr;
const unsigned char NO_TS_INJECT = 0;
const unsigned char TS_INJECT = 1;
const unsigned char FCS_GOOD = 0;
const unsigned char FCS_BAD = 1;
const unsigned char FCS_RESERVED = 2;
const unsigned char FCS_UNCHANGED = 3;
printf("Created PacketTransmitterThread port=%u\n", txPort);
}
void run(void) {
}
void join(void) {
if (thr.joinable())
{
thr.join();
}
}
private:
void txPacket(
NtNetStreamTx_t hNetTx,
unsigned char tsi,
unsigned char fcs) {
uint32_t sleep_time = 1000000;
{
fprintf(stderr, "NT_NetTxGet() failed: %s\n", errorBuffer);
return;
}
packet_ptr->offset1 = 34;
overlay_ptr->frame_type = 0x4;
overlay_ptr->checksum_cmd = 0;
overlay_ptr->tsiCmd = tsi & 1;
overlay_ptr->fcsCmd = fcs & 3;
{
fprintf(stderr, "NT_NetTxRelease() failed: %s\n", errorBuffer);
return;
}
++transmittedPackets;
if (sleep_time)
{
usleep(sleep_time);
}
}
void transmitPackets(void) {
printf("============================================\n");
printf("Transmitter configuration\n");
printf("============================================\n");
printf("TX port : %u\n", txPort);
printf("============================================\n");
if (status != NT_SUCCESS)
{
fprintf(stderr, "NT_NetTxOpenAttrSetDescriptorPosFrameType failed: %s\n", errorBuffer);
exit(1);
}
if (status != NT_SUCCESS)
{
fprintf(stderr, "NT_NetTxOpenAttrSetDescriptorPosChecksumCmd failed: %s\n", errorBuffer);
exit(1);
}
if (status != NT_SUCCESS)
{
fprintf(stderr, "NT_NetTxOpenAttrSetTxtDescriptorPosTimestampInjectCmd failed: %s\n", errorBuffer);
exit(1);
}
if (status != NT_SUCCESS)
{
fprintf(stderr, "NT_NetTxOpenAttrSetTxtDescriptorPosFcs failed: %s\n", errorBuffer);
exit(1);
}
if (status != NT_SUCCESS) {
fprintf(stderr, "NT_NetTxOpen() failed: %s\n", errorBuffer);
return;
}
{
if (txLoops > 0) --txLoops;
txPacket(hNetTx, NO_TS_INJECT, FCS_UNCHANGED);
txPacket(hNetTx, NO_TS_INJECT, FCS_GOOD);
txPacket(hNetTx, NO_TS_INJECT, FCS_BAD);
txPacket(hNetTx, TS_INJECT, FCS_UNCHANGED);
txPacket(hNetTx, TS_INJECT, FCS_GOOD);
txPacket(hNetTx, TS_INJECT, FCS_BAD);
}
printf("Packets transmitted %lu\n ", transmittedPackets);
printf("Shutting down TX\n");
{
fprintf(stderr, "NT_NetTxClose() failed: %s\n", errorBuffer);
return;
}
}
};
{
}
{
fprintf(stderr, formatstr, errorBuffer);
}
{
char ntplStr[400];
snprintf(ntplStr, 400,
"assign[streamid=%lu] = Port==%lu", rxPort, rxPort);
if ((status =
NT_ConfigOpen(&hCfgStream,
"flowtest")) != NT_SUCCESS) {
printError(status,
"NT_ConfigOpen() failed: %s\n");
exit(1);
}
printf("\nDoing NTPL:\n");
printf("%s\n\n", ntplStr);
exit(1);
}
}
{
if ((status =
NT_InfoOpen(&hInfo,
"tsi")) != NT_SUCCESS) {
fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer);
return false;
}
if ((status =
NT_InfoRead(hInfo, &infoRead)) != NT_SUCCESS) {
fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
return false;
}
if (
)
{
return false;
}
return true;
}
static void usage(
const char *argv0)
{
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [options]\n", argv0);
fprintf(stderr, "\n");
fprintf(stderr, "This program is an example of how to control TX time stamp inject\n");
fprintf(stderr, "and FCS generation per packet using dynamic descriptor 3.\n");
fprintf(stderr, "\n");
fprintf(stderr, " Options:\n"
" -r <rx port> : RX port.\n"
" -t <tx port> : TX port.\n"
" -n <tx loops> : Number of TX loops\n"
" -h : Print help message\n");
fprintf(stderr, "\n");
}
int main(
int argc,
char **argv)
{
int opt;
int64_t txLoops = -1;
uint64_t txPort = 0;
uint64_t txActive = false;
uint64_t rxActive = false;
char *endptr;
while ((opt = getopt(argc, argv, "n:t:r:")) != -1) {
switch (opt) {
case 'r':
rxPort = strtoul(optarg, &endptr, 10);
rxActive = true;
if (*optarg == '\0' || *endptr != '\0') {
fprintf(stderr, "Could not parse RX port, i.e. -r option\n");
exit(1);
}
break;
case 't':
txPort = strtoul(optarg, &endptr, 10);
txActive = true;
if (*optarg == '\0' || *endptr != '\0') {
fprintf(stderr, "Could not parse TX port, i.e. -t option\n");
exit(1);
}
break;
case 'n':
txLoops = strtol(optarg, &endptr, 10);
if (*optarg == '\0' || *endptr != '\0') {
fprintf(stderr, "Could not parse number of TX loops, , i.e. -n option\n");
exit(1);
}
break;
case 'h':
exit(0);
break;
default:
exit(1);
}
}
{
printf("\nRequired Test&Measurement features not supported on port %lu\n", txPort);
return 0;
}
if (txActive)
{
}
if (rxActive)
{
}
if (rxThread == nullptr && txThread == nullptr)
{
printf("\nUse at least one of the options -t, -r\n");
return 0;
}
if (rxThread != nullptr)
{
}
usleep(1000000);
if (txThread != nullptr)
{
}
printf("Threads started. Press CTRL-C to terminate program.\n");
{
usleep(1000);
};
if (txThread != nullptr)
{
delete txThread;
if (rxThread !=
nullptr &&
running)
{
printf("TX thread stopped. Use CTRL-C to terminate RX thread\n");
}
}
if (rxThread != nullptr)
{
delete rxThread;
}
return 0;
}