Integrator Resources

The official home for NAI Support

Not sure where to start? Try Quick Start Guide or ask a question below!

Toggle Components with Visual Button
JavaScript Form Processing

CTS DIF SAMPLE APP

CTS DIF Sample Application (SSK 1.x)

Overview

The CTS DIF Sample Application demonstrates a complete automated Coordinated Time Sequencing (CTS) workflow on DIF (Differential Digital I/O) modules using the NAI Software Support Kit (SSK 1.x). Unlike the CTS_DIF_Ethernet sample that provides an interactive menu, this sample runs a fully automated sequence: it opens the board directly (no board menu), initializes one or two DF3 modules, loads event tables from a CSV file, sets up interrupts to synchronize module counters, and monitors events as they fire. It is designed to demonstrate end-to-end CTS operation in a production-like scenario where two DIF modules must drive a combined output in precise synchronization.

This sample supports the DF3 module type on NAI 75G5 boards (or compatible). It is typically used with two DF3 modules — one in slot 2 (first) and one in slot 3 (second) — though command-line arguments allow other slot configurations.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board (e.g., 75G5) with one or two DF3 modules installed.

  • SSK 1.x installed on your development host.

  • The sample applications built. Refer to the SSK 1.x build instructions for your platform if you have not already compiled them.

  • An event data file (eventdata.txt by default) in the working directory containing timed events in CSV format.

How to Run

Launch the CTS_DIF_SAMPLE_APP executable from your build output directory. Command-line arguments control the configuration:

  • No arguments — defaults to DF3 modules in slots 2 and 3.

  • Slot numbers — e.g., CTS_DIF_SAMPLE_APP 2 3 to specify module slots explicitly.

  • -s — silent mode. Loads events into the modules but does not monitor or report event status. Useful for configuring modules as part of a larger test harness.

  • -d — direct mode. Disables channel-to-bit mapping. Use when logical and physical bit positions already match your wiring.

  • -f <filename> — specifies a custom event data file instead of the default eventdata.txt.

Board Connection

This sample does not use the standard naiapp_RunBoardMenu() startup. Instead, it opens card index 0 directly using naibrd_OpenDevice():

status = naibrd_OpenDevice(g_CardIndex, NAIBRD_COMM_ONBOARD, NAI_DEV_NAME_75G5);
  • g_CardIndex — always 0 in this sample.

  • NAIBRD_COMM_ONBOARD — onboard communication interface.

  • NAI_DEV_NAME_75G5 — the board type identifier.

In your own application, you would typically use naiapp_RunBoardMenu() for flexible configuration. This sample hardcodes the board connection because it is designed for a specific hardware test setup.

Important

Common connection errors you may encounter:

  • Board open fails — the board is not powered, not present at card index 0, or the device name does not match. Verify hardware is present and the board type is correct.

  • Module not detected at slot — the specified slot does not contain a DF3 module. Verify module installation and adjust command-line slot arguments.

Program Structure

Entry Point

On standard platforms the entry point is main(int argc, const char *argv[]). On VxWorks the entry point is DIF_CTS_Sample(). The entry point parses command-line arguments for slot numbers, silent mode, direct mode, and event filename, then calls run_DF3_SAMPLE_APP().

#if defined (__VXWORKS__)
int32_t DIF_CTS_Sample(int argc, const char *argv[])
#else
int32_t main(int argc, const char *argv[])
#endif

Automated Workflow

The run_DF3_SAMPLE_APP() function executes the following sequence:

  1. Open the board at card index 0 via naibrd_OpenDevice().

  2. Clear any leftover event interrupts with naibrd_DIF_ClearGroupStatusRaw().

  3. Set up interrupts on the first DF3 module to synchronize counters across modules (the ISR fires once per second on the CTS 1PPS pulse).

  4. Initialize each active DF3 module via init_DF3_Module(): set all channels as outputs, assign the module slot, flush queues, load events from the CSV file.

  5. Set coarse counters to a value just before the first event.

  6. Monitor the latched status register for event detection (unless in silent mode).

  7. On each detected event, read back the I/O state, reverse-map channel values, and compare against expected values.

  8. Close the board when all events have fired or on exit.

Event Data File Format

Events are loaded from a CSV file where each line contains three hexadecimal values separated by commas:

0x00000900,0x00000000,0x000000FF
0x00000901,0x00000000,0x0000FF00
0x00000902,0x00000000,0x00000000
  • First value: coarse counter (when the event fires).

  • Second value: subsecond counter (fine timing within the coarse tick).

  • Third value: output bit pattern (the value driven to the output pins).

The fill_Event_Struct() function parses each line into an _EventStruct containing courseCounter, subSecCounter, value, moduleSlotRawValue, and rawCompositeValue fields.

Module Initialization

Initialize a DF3 Module

The init_DF3_Module() function prepares a single DF3 module for CTS operation:

/* Set all channels as outputs */
naibrd_WriteReg32(cardIndex, DF3ModuleSlot, 0x00001038, 0x0000FFFF);

/* Tell the module what slot it is in */
naibrd_WriteReg32(cardIndex, DF3ModuleSlot, 0x00001108, DF3ModuleSlot);

/* Flush all queues */
naibrd_DIF_CTS_FlushQueues(cardIndex, DF3ModuleSlot);

After configuration, the function opens the event data file and loads each event:

naibrd_DIF_CTS_WriteEvent(cardIndex, DF3ModuleSlot,
   DF3Events[count-1].courseCounter,
   DF3Events[count-1].subSecCounter,
   DF3Events[count-1].value);
  • The register at 0x00001038 controls I/O direction — writing 0x0000FFFF sets all 16 channels to output.

  • The register at 0x00001108 stores the module slot identifier, which the hardware uses for multi-module synchronization.

  • naibrd_DIF_CTS_FlushQueues() clears any previously loaded events.

  • naibrd_DIF_CTS_WriteEvent() loads a single timed event into the FIFO.

Note
The maximum number of events this sample supports is 5000 (MAX_EVENTS). If the event file contains more, the sample stops loading and prints a warning.

Channel-to-Bit Mapping

The map_DF3_EventValue() function translates logical event bit positions to physical channel numbers. This mapping accounts for hardware wiring differences between module slots. For example, in slot 2:

  • Logical BIT 0 maps to Channel 1 (physical BIT 0)

  • Logical BIT 2 maps to Channel 4 (physical BIT 3)

  • Logical BIT 5 maps to Channel 8 (physical BIT 7)

The reverse mapping (reverseMap_DF3_EventValue()) translates read-back values from physical channel numbers back to logical bit positions for comparison against expected values.

The -d command-line flag disables this mapping. In direct mode, the function still isolates the bits relevant to each module slot but does not rearrange them. Use direct mode when your wiring matches the logical-to-physical bit assignment.

In your own application, define a mapping appropriate to your wiring configuration, or pass event data directly if no mapping is needed.

Interrupt-Based Counter Synchronization

Setting Up Interrupts

The setup_DF3_Interrupts() function configures the first DF3 module to generate an interrupt on the CTS 1PPS (one pulse per second) signal. This interrupt is used to synchronize the coarse counters across both modules at a precise moment:

/* Install the ISR */
naibrd_InstallISR(cardIndex, NAIBRD_IRQ_ID_ON_BOARD_0, (nai_isr_t)my_DF3_Isr, NULL);

/* Set edge-driven interrupts for the CTS Run Status */
for (chan = 1; chan <= 16; chan++)
{
   naibrd_DIF_SetEdgeLevelInterrupt(cardIndex, DF3ModuleSlot, chan,
      NAI_DIF_CTS_STATUS_RUN_LATCHED, NAI_DIF_EDGE_INTERRUPT);
}

/* Set interrupt steering to onboard IRQ 0 */
naibrd_DIF_SetGroupInterruptSteering(cardIndex, DF3ModuleSlot, 1,
   NAI_DIF_CTS_STATUS_RUN_LATCHED, NAIBRD_INT_STEERING_ON_BOARD_0);

/* Enable the CTS 1PPS Pulse interrupt */
naibrd_DIF_CTS_SetInterruptEnable(cardIndex, DF3ModuleSlot,
   NAI_DIF_CTS_INT_EXT_PULSE, 1);

The ISR: Counter Initialization

The my_DF3_Isr() interrupt service routine fires on each 1PPS pulse. It checks the g_InitCounters flag to determine whether to set the counters:

  • g_InitCounters == 1: Sets both modules' coarse counters to 0x00004000 (well outside the event range) so events do not fire while the event table is being loaded.

  • g_InitCounters == 2: Sets both modules' coarse counters to a value just before the first event, so events begin firing shortly after.

if (g_InitCounters == 1)
   naibrd_DIF_CTS_SetCourseCounter(g_CardIndex, g_DF3_FirstSlot, 0x00004000);
else
   naibrd_DIF_CTS_SetCourseCounter(g_CardIndex, g_DF3_FirstSlot,
      (g_firstDF3Events[0].courseCounter - 5));

naibrd_DIF_CTS_SetSubsecCounter(g_CardIndex, g_DF3_FirstSlot, 0x00000000);

By setting counters inside the ISR (which fires at a precise 1-second boundary), both modules receive their counter values at the same moment, achieving tight synchronization.

After the event tables are loaded, the sample disables the CTS pulse interrupt since synchronization is complete:

naibrd_DIF_CTS_SetInterruptEnable(g_CardIndex, firstDF3ModuleSlot,
   NAI_DIF_CTS_INT_EXT_PULSE, 0);

Event Monitoring

After initialization, the sample enters a polling loop that checks for fired events by reading the latched status register:

naibrd_ReadReg32(g_CardIndex, firstDF3ModuleSlot, 0x00000844, &moduleSlotStatus);
if ((moduleSlotStatus & EVENT_DETECTED) == EVENT_DETECTED)
{
   /* Clear the latch */
   naibrd_WriteReg32(g_CardIndex, firstDF3ModuleSlot, 0x00000844, EVENT_DETECTED);

   /* Read back the I/O state */
   naibrd_DIF_GetGroupInputStateRaw(g_CardIndex, firstDF3ModuleSlot, 1, &readBackValue);

   /* Reverse-map and compare */
   readBackValue = reverseMap_DF3_EventValue(firstDF3ModuleSlot, readBackValue, g_DF3UseMapping);
}
  • The register at 0x00000844 is the CTS run latched status register. The EVENT_DETECTED bit (0x00000040) indicates that a CTS event has fired.

  • After detecting an event, the sample clears the latch by writing the detected bit back.

  • The I/O state is read with naibrd_DIF_GetGroupInputStateRaw() and reverse-mapped to logical bit positions for comparison against the expected event value.

The sample compares the detected event’s value and counter against the expected values from the loaded event table and prints PASS or FAILURE for each event.

Important

Common Errors

  • Event file not found — The event data file must be in the working directory or specified with -f. The sample returns without firing events if the file cannot be opened.

  • Different number of detected events between modules — Both modules should fire events in lockstep. A mismatch indicates a synchronization or hardware issue.

  • Event value mismatch — The channel mapping does not match your wiring. Use -d for direct mode or verify the mapping function.

  • Events never detected — The coarse counter was not set close enough to the first event, or the CTS clock was not initiated.

Troubleshooting Reference

This table summarizes common errors and symptoms. Consult your module’s manual for hardware-specific diagnostic procedures.

Error / Symptom Possible Causes Suggested Resolution

Board open fails

Board not powered, not at card index 0, or wrong device name

Verify board is present at index 0 and that the board type matches NAI_DEV_NAME_75G5

Module not detected at slot

Wrong slot number specified or no DF3 module installed

Verify module installation; adjust command-line slot arguments

Event file not found

File not in working directory or wrong filename

Place eventdata.txt in the working directory or use -f to specify the path

Events never detected

CTS clock not initiated, counters not set near first event

Verify the initialization sequence sets counters close to the first event’s timestamp

Wrong outputs driven

Channel mapping mismatch with hardware wiring

Use -d flag for direct mode or verify the mapping function matches your wiring

Different event counts between modules

Synchronization failure between the two DF3 modules

Verify both modules are initialized identically and counters are set in the ISR

Event value or counter mismatch (FAILURE)

Event data file has unexpected values, or read-back timing is off

Verify event file format; ensure coarse counter values are spaced far enough apart for reliable detection

MAX_EVENTS exceeded

Event file contains more than 5000 events

Reduce the number of events or increase MAX_EVENTS in the source

Full Source

The complete source for this sample is provided below for reference. The sections above explain each part in detail.

Full Source — CTS_DIF_SAMPLE_APP.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* Common Sample Program include files */
#include "include/naiapp_interrupt_ether.h"
#include "include/naiapp_boardaccess_menu.h"
#include "include/naiapp_boardaccess_query.h"
#include "include/naiapp_boardaccess_access.h"
#include "include/naiapp_boardaccess_display.h"
#include "include/naiapp_boardaccess_utils.h"

/* naibrd include files */
#include "naibrd.h"
#include "boards/naibrd_gen5.h"
#include "functions/naibrd_dif_cts.h"

#define _ONLY_BITS_RELEVANT_TO_MODULE_SLOT 1

static const int8_t *SAMPLE_PGM_NAME = (int8_t *)"CTS_DIF";
static const uint32_t EVENT_DETECTED = 0x00000040;

#define MAX_EVENTS 5000
struct _EventStruct
{
   int32_t courseCounter;
   int32_t subSecCounter;
   int32_t value;
   int32_t moduleSlotRawValue;
   int32_t rawCompositeValue;
};

static struct _EventStruct g_firstDF3Events[MAX_EVENTS];
static struct _EventStruct g_secondDF3Events[MAX_EVENTS];

/* Function prototypes */
void run_DF3_SAMPLE_APP(void);
static int32_t map_DF3_EventValue(int32_t DF3ModuleSlot, int32_t nEventValue, int8_t DF3UseMapping);
static int32_t reverseMap_DF3_EventValue(int32_t DF3ModuleSlot, int32_t nEventValue, int8_t DF3UseMapping);
static void fill_Event_Struct(int32_t DF3ModuleSlot, char* line, struct _EventStruct *eventStruct);
void init_DF3_Module(int32_t cardIndex, int32_t DF3ModuleSlot, char const* DF3EventDataFile, struct _EventStruct *DF3Events);
nai_status_t setup_DF3_Interrupts(int32_t cardIndex, int32_t DF3ModuleSlot);
void my_DF3_Isr(void* param, uint32_t vector);

/* Global Variables */
uint32_t g_TotalEventCount = 0;
uint32_t g_CardIndex = 0;
int32_t g_DF3_FirstSlot  = 0;
int32_t g_DF3_SecondSlot = 0;
int8_t g_ActiveSlotCount = 0;
int8_t g_SilentMode = 0;
int8_t g_InitCounters = 0;
int8_t g_DF3UseMapping = 1;
char g_DF3EventDataFile[20];

#if defined (__VXWORKS__)
int32_t DIF_CTS_Sample(int argc, const char *argv[])
#else
int32_t main(int argc, const char *argv[])
#endif
{
   int i = 0;
   printf("\n");
   printf("NAI Sample Program: %s\n", SAMPLE_PGM_NAME);
   printf("=============================================================================\n\n");

   sprintf(g_DF3EventDataFile, "%s", "eventdata.txt");

   if (argc == 1)
   {
      g_DF3_FirstSlot = 2;
      g_DF3_SecondSlot = 3;
      g_ActiveSlotCount = 2;
   }
   else
   {
      for (i = 1; i < argc; i++)
      {
         if (strcmp(argv[i], "-s") == 0)
            g_SilentMode = 1;
         else if (strcmp(argv[i], "-d") == 0)
            g_DF3UseMapping = 0;
         else if (strcmp(argv[i], "-f") == 0)
         {
            i++;
            sprintf(g_DF3EventDataFile, "%s", argv[i]);
         }
         else if (g_DF3_FirstSlot == 0)
         {
            g_DF3_FirstSlot = atoi(argv[i]);
            g_ActiveSlotCount++;
         }
         else if (g_DF3_SecondSlot == 0)
         {
            g_DF3_SecondSlot = atoi(argv[i]);
            g_ActiveSlotCount++;
         }
      }
   }

   if (g_DF3_FirstSlot == 0 && g_DF3_SecondSlot == 0)
   {
      g_DF3_FirstSlot = 2;
      g_DF3_SecondSlot = 3;
      g_ActiveSlotCount = 2;
   }

   if (g_SilentMode)
      printf("Silent Mode Detected - Event tables will be loaded but this application will not attempt to detect event status.\n");

   run_DF3_SAMPLE_APP();

   printf("Exiting DF3_SAMPLE_APP!\n");
   return 0;
}

/* ... remaining functions: run_DF3_SAMPLE_APP, init_DF3_Module, */
/* setup_DF3_Interrupts, my_DF3_Isr, map_DF3_EventValue, */
/* reverseMap_DF3_EventValue, fill_Event_Struct */
/* See the SSK installation directory for the complete source. */

Help Bot

X