CTS DIF SAMPLE APP
Edit this on GitLab
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.txtby 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 3to 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 defaulteventdata.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:
|
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:
-
Open the board at card index 0 via
naibrd_OpenDevice(). -
Clear any leftover event interrupts with
naibrd_DIF_ClearGroupStatusRaw(). -
Set up interrupts on the first DF3 module to synchronize counters across modules (the ISR fires once per second on the CTS 1PPS pulse).
-
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. -
Set coarse counters to a value just before the first event.
-
Monitor the latched status register for event detection (unless in silent mode).
-
On each detected event, read back the I/O state, reverse-map channel values, and compare against expected values.
-
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
0x00001038controls I/O direction — writing0x0000FFFFsets all 16 channels to output. -
The register at
0x00001108stores 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 to0x00004000(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
0x00000844is the CTS run latched status register. TheEVENT_DETECTEDbit (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
|
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 |
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 |
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 |
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 |
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. */