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 Ethernet

CTS DIF Ethernet Sample Application (SSK 1.x)

Overview

The CTS DIF Ethernet sample application demonstrates how to perform Coordinated Time Sequencing (CTS) operations on DIF (Differential Digital I/O) modules over an Ethernet connection using the NAI Software Support Kit (SSK 1.x). CTS allows DIF modules to drive discrete outputs at precisely timed intervals controlled by a coarse/subsecond counter system and a FIFO event queue. This sample covers the full CTS workflow: setting default output values on flush, configuring coarse and subsecond counters, loading timed events into the FIFO, flushing queues, initiating the clock, filling the FIFO from a file, and running a multi-module synchronized CTS demonstration.

This sample supports the DF3 module type. It communicates with the board over Ethernet, and the interactive menu lets you exercise each CTS operation individually. It serves as a practical API reference — each menu command maps directly to one or more naibrd_DIF_CTS_*() API calls that you can lift into your own code.

Prerequisites

Before running this sample, make sure you have:

  • An NAI board with one or two DIF (DF3) modules installed, connected via Ethernet.

  • 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.

  • For the Demo command: an event data file (eventdata.txt) in the working directory containing timed events in CSV format (coarse counter, subsecond counter, output value per line).

How to Run

Launch the CTS_DIF_Ethernet executable from your build output directory. On startup the application looks for a configuration file (default_CTS_DIF_Ethernet.txt). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection, card index, and module slot. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected and a DIF module is selected, the CTS operation menu appears.

Board Connection and Module Selection

Note
This startup sequence is common to all NAI sample applications. The board connection and module selection code shown here is not specific to DIF.

The main() function follows a standard SSK 1.x startup flow:

  1. Call naiapp_RunBoardMenu() to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_CTS_DIF_Ethernet.txt) is not included with the SSK — it is created when the user saves their connection settings from the board menu. On the first run, the menu will always appear.

  2. Query the user for a card index with naiapp_query_CardIndex().

  3. Query for a module slot with naiapp_query_ModuleNumber().

  4. Retrieve the module ID with naibrd_GetModuleID() so downstream code can verify the module is a DIF type.

#if defined (__VXWORKS__)
int32_t DIF_CTS(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t cardIndex;
   int32_t moduleCnt;
   int32_t module;
   uint32_t moduleID = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            if (stop != TRUE)
            {
               moduleID = naibrd_GetModuleID(cardIndex, module);
               if ((moduleID != 0))
               {
                  Run_DIF_CTS(cardIndex, module, moduleID);
               }
            }
         }
         /* ... quit/restart prompt ... */
      }
   }
   naiapp_access_CloseAllOpenCards();
   return 0;
}

The Run_DIF_CTS() function checks whether the selected module is actually a DIF type by calling naibrd_DIF_GetChannelCount(). If the channel count is zero, the module is not a DIF and the sample prints an error. Otherwise it enters the CTS command loop via Cfg_DIF_CTS().

Important

Common connection errors you may encounter at this stage:

  • No board found — verify that the board is powered on and connected via Ethernet. Check that the configuration file lists the correct interface and address.

  • Connection timeout — confirm network settings. Firewalls and IP mismatches are frequent causes.

  • Invalid card or module index — indices are zero-based for cards and one-based for modules. Ensure the values you pass match your hardware setup.

  • Module not recognized as DIF — the selected slot does not contain a DIF module. naibrd_DIF_GetChannelCount() returns zero for non-DIF modules.

Program Structure

Entry Point

On standard platforms the entry point is main(). On VxWorks the entry point is DIF_CTS() — the SSK 1.x build system selects the correct variant via a preprocessor guard:

#if defined (__VXWORKS__)
int32_t DIF_CTS(void)
#else
int32_t main(void)
#endif

Application Parameters

The Cfg_DIF_CTS() function populates an naiapp_AppParameters_t struct that is passed to each command handler. Your application will need to track these same values:

cts_eth_params->cardIndex = cardIndex;
cts_eth_params->module = module;
cts_eth_params->channel = 0;
  • cardIndex — identifies which board in a multi-board system.

  • module — the slot number where the DIF module is installed.

  • channel — not used for CTS operations (CTS operates on the entire module, not individual channels).

Command Loop

Cfg_DIF_CTS() drives the interactive command loop. On each iteration it displays the current CTS configuration, prints the command menu, and dispatches the user’s selection:

Command Description

Default

Set the default output value driven when queues are flushed

Outputs

Set all channels to output mode

Flush

Flush event queues (drives default output immediately)

Init

Initiate the CTS clock

Course

Set the coarse counter value

Subsec

Set the subsecond counter value

Load

Load a single timed event into the FIFO

Fill

Fill the FIFO from an event data file and initiate the clock

Demo

Run a full multi-module CTS demonstration sequence

The menu-driven structure is a convenience of the sample application. In your own application, you would call the same underlying naibrd_DIF_CTS_*() API functions directly.

CTS Configuration Display

On each command loop iteration, Display_DIF_CTS_Cfg() reads and displays the current CTS state. To read the same information in your own application:

uint32_t outputState, courseCount, subsecCount, defaultVal;

naibrd_DIF_GetGroupInputStateRaw(cardIndex, module, 1, &outputState);
naibrd_DIF_CTS_GetCourseCounter(cardIndex, module, &courseCount);
naibrd_DIF_CTS_GetSubsecCounter(cardIndex, module, &subsecCount);
naibrd_DIF_CTS_GetSwitchDefVal(cardIndex, module, &defaultVal);
  • outputState — the current I/O state of all channels as a bitmask. Each bit represents one channel.

  • courseCount — the current value of the coarse counter (high-order timing).

  • subsecCount — the current value of the subsecond counter (fine-grained timing within each coarse tick).

  • defaultVal — the output bit pattern driven when queues are flushed.

The display also calls naibrd_GetModuleInfo() to retrieve the module ID, version, and revision for identification.

CTS Counter and Queue Operations

This section covers the API calls used to configure the CTS timing system: setting coarse and subsecond counters, managing the default output value, flushing event queues, and initiating the clock.

Set Default Output Value

The default output value is the bit pattern driven to the discrete outputs when the event queues are flushed (emptied). To set this value in your own application, call naibrd_DIF_CTS_SetSwitchDefVal():

uint32_t defaultVal = 0x0000FFFF;
naibrd_DIF_CTS_SetSwitchDefVal(cardIndex, module, defaultVal);
  • cardIndex — identifies the board.

  • module — the slot containing the DIF module.

  • defaultVal — the output bit pattern. Each bit corresponds to one discrete output channel.

The sample validates the module ID (must be NAI_MODULE_ID_DF3) before calling this function. On unsupported modules, the call is skipped.

Set All Channels to Output Mode

Before driving outputs, you must configure the DIF channels as outputs. The sample writes directly to the I/O direction register:

naibrd_WriteReg32(cardIndex, module, 0x00001038, 0x0000FFFF);
  • The register address 0x00001038 is the I/O direction register for the DIF module. Setting a bit to 1 configures that channel as an output.

  • The value 0x0000FFFF sets all 16 channels to output mode.

Note
The register address and value depend on the specific DIF module variant. Consult your module’s manual for the correct I/O direction register.

Flush Event Queues

To clear all pending events from the FIFO and drive the default output value to the pins, call naibrd_DIF_CTS_FlushQueues():

naibrd_DIF_CTS_FlushQueues(cardIndex, module);

Flushing discards all queued events and immediately drives the default output value (set by naibrd_DIF_CTS_SetSwitchDefVal()) to the output pins. This is useful for resetting the CTS system to a known state before loading a new event sequence.

Set Coarse Counter

The coarse counter provides the high-order timing reference for CTS events. Each coarse tick typically represents one second. To set the coarse counter in your own application:

uint32_t courseCount = 0x00001000;
naibrd_DIF_CTS_SetCourseCounter(cardIndex, module, courseCount);
  • cardIndex — identifies the board.

  • module — the slot containing the DIF module.

  • courseCount — the coarse counter value to load.

Set Subsecond Counter

The subsecond counter provides fine-grained timing within each coarse tick. To set the subsecond counter:

uint32_t subsecCount = 0x00000100;
naibrd_DIF_CTS_SetSubsecCounter(cardIndex, module, subsecCount);
  • cardIndex — identifies the board.

  • module — the slot containing the DIF module.

  • subsecCount — the subsecond counter value to load.

Initiate the CTS Clock

To start the CTS clock, which begins executing queued events when the counters reach the specified times:

naibrd_DIF_CTS_ClockInitiate(cardIndex, module);

After initiating the clock, the module begins incrementing its coarse and subsecond counters. When the counters match a queued event’s timestamp, the module drives the event’s output value to the pins.

Important

Common Errors

  • Events do not fire — The clock has not been initiated. Call naibrd_DIF_CTS_ClockInitiate() after loading events.

  • Outputs do not change — Channels are not configured as outputs. Write the I/O direction register first.

  • Unsupported function for this module — The selected module is not a DF3. CTS operations are only supported on DF3 modules.

Loading Events

Load a Single Event

To load a single timed event specifying when and what output values to drive, call naibrd_DIF_CTS_WriteEvent():

uint32_t courseCount = 0x00001000;
uint32_t subsecCount = 0x00000000;
uint32_t eventData = 0x0000FFFF;

naibrd_DIF_CTS_WriteEvent(cardIndex, module, courseCount, subsecCount, eventData);
  • cardIndex — identifies the board.

  • module — the slot containing the DIF module.

  • courseCount — the coarse counter value at which this event should fire.

  • subsecCount — the subsecond counter value at which this event should fire.

  • eventData — the output bit pattern to drive when the event fires.

Events are loaded into a FIFO and execute in sequence when the CTS clock reaches the specified time.

Fill FIFO from Event Data File

The Fill command reads events from a user-specified file and loads them into the FIFO, then initiates the clock. The load_DIF_FIFO() function opens the file, parses each line using fill_Event_Struct(), and calls naibrd_DIF_CTS_WriteEvent() for each event.

The event data file is a CSV file where each line contains three hexadecimal values separated by commas: coarse counter, subsecond counter, and output value. For example:

0x00000900,0x00000000,0x000000FF
0x00000901,0x00000000,0x0000FF00
0x00000902,0x00000000,0x00000000

After loading all events, the sample calls naibrd_DIF_CTS_ClockInitiate() to begin event execution.

Synchronized Multi-Module Demo

The Demo command (run_DIF_CTS_demo()) demonstrates a complete multi-module CTS workflow using two DIF modules in slots 2 and 3 (base and sync modules). The sequence:

  1. Verify both module slots contain DIF modules by checking naibrd_DIF_GetChannelCount().

  2. Set initial coarse counters on both modules to a value well outside the event range (0x00004000).

  3. Call init_DIF_System() to configure both modules: set all channels as outputs, flush queues, initiate the clock, and load events from the event data file.

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

  5. Set both modules' coarse counters to a value just before the first event (0x000008F3).

  6. Monitor the latched status register for event detection. When an event fires, the EVENT_DETECTED bit (0x00000040) is set.

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

/* Check for event detection */
naibrd_ReadReg32(cardIndex, baseModule, 0x00000844, &moduleSlotStatus);
if ((moduleSlotStatus & EVENT_DETECTED) == EVENT_DETECTED)
{
   /* Clear the latch */
   naibrd_WriteReg32(cardIndex, baseModule, 0x00000844, EVENT_DETECTED);

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

   /* Read counters for verification */
   naibrd_DIF_CTS_GetCourseCounter(cardIndex, baseModule, &courseCounter);
   naibrd_DIF_CTS_GetSubsecCounter(cardIndex, baseModule, &subsecCounter);
}

Channel-to-Bit Mapping

When using two DIF modules to drive a combined output, the sample maps logical event bit positions to physical channel numbers using map_DF3_EventValue(). The mapping accounts for hardware wiring differences between module slots. The reverse mapping (reverseMap_DF3_EventValue()) translates read-back values to the original logical representation for comparison.

In your own application, you would define a mapping appropriate to your wiring configuration, or pass event data directly if logical and physical bit positions already match.

Important

Common Errors

  • Event file not found — The event data file must be in the working directory. Verify the filename.

  • Demo reports FAILURE — The detected event value or counter does not match the expected value. Verify the event data file contents and that both modules are installed in the expected slots.

  • Second module not detected — If a sync module is expected in slot 3 but not present, verify hardware configuration.

Troubleshooting Reference

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

Error / Symptom Possible Causes Suggested Resolution

No board found or connection timeout

Board not powered, Ethernet not connected, incorrect configuration file

Verify hardware is powered and connected. If default_CTS_DIF_Ethernet.txt exists, check that it lists the correct interface and address.

Module not recognized as DIF

Selected slot does not contain a DF3 module

Verify DIF module installation and slot number

Events do not fire

CTS clock not initiated after loading events

Call naibrd_DIF_CTS_ClockInitiate() after loading events

Outputs stay at default value

Channels not configured as outputs

Write the I/O direction register before loading events

Counter values incorrect after Demo

Counters not set before clock initiation, or event file has unexpected values

Set counters before initiating; verify event file format

Unsupported function for this module

Module is not a DF3; CTS operations only work on DF3 modules

Select a slot containing a DF3 module

FIFO overflow

Too many events loaded without initiating the clock

Flush queues and reload a smaller event set, or initiate the clock to begin consuming events

Demo reports FAILURE

Detected event value or counter mismatch with expected values

Check event data file, verify both modules are in slots 2 and 3, verify channel mapping

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_Ethernet.c (SSK 1.x)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

/* Common Sample Program include files */
#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 "nai.h"
#include "naibrd.h"
#include "functions/naibrd_dif.h"
#include "functions/naibrd_dif_cts.h"
#include "advanced/nai_ether_adv.h"
#include "boards/naibrd_gen5.h"

static const int8_t *CONFIG_FILE = "default_CTS_DIF_Ethernet.txt";
static const uint32_t EVENT_DETECTED = 0x00000040;

/****** Command Table *******/
enum dif_cts_commands
{
   DIF_CTS_CMD_DEF,
   DIF_CTS_CMD_OUTPUT,
   DIF_CTS_CMD_FLUSH,
   DIF_CTS_CMD_INIT,
   DIF_CTS_CMD_COURSE,
   DIF_CTS_CMD_SUBSEC,
   DIF_CTS_CMD_LOAD,
   DIF_CTS_CMD_FILL,
   DIF_CTS_CMD_DEMO,
   DIF_CTS_CMD_COUNT
};

struct _EventStruct
{
   int32_t courseCounter;
   int32_t subSecCounter;
   int32_t value;
};

#define MAX_EVENTS 5000
static struct _EventStruct g_firstDF3Events[MAX_EVENTS];
static struct _EventStruct g_secondDF3Events[MAX_EVENTS];

/* Function prototypes */
int32_t Run_DIF_CTS(int32_t cardIndex, int32_t module, uint32_t moduleID);
static void Cfg_DIF_CTS(int32_t cardIndex, int32_t module, uint32_t ModuleID);
void Display_DIF_CTS_Cfg(int32_t cardIndex, int32_t module, uint32_t ModuleID);
nai_status_t Configure_DIF_CTS_Default(int32_t paramCount, int32_t* p_params);
nai_status_t Configure_DIF_CTS_Course(int32_t paramCount, int32_t* p_params);
nai_status_t Configure_DIF_CTS_Subsec(int32_t paramCount, int32_t* p_params);
nai_status_t Load_DIF_CTS_Event(int32_t paramCount, int32_t* p_params);
void run_DIF_CTS_demo(int32_t cardIndex);
bool_t load_DIF_FIFO(int32_t cardIndex, int32_t DIFModuleSlot);
bool_t init_DIF_System(int32_t cardIndex, int32_t DIFModuleSlot1, int32_t DIFModuleSlot2, char const* DIFEventDataFile);
static void fill_Event_Struct(char* line, struct _EventStruct *eventStruct);
static int32_t reverseMap_DF3_EventValue(int32_t DF3ModuleSlot, int32_t nEventValue);
static int32_t map_DF3_EventValue(int32_t DF3ModuleSlot, int32_t nEventValue);

/****** Command Tables *******/
naiapp_cmdtbl_params_t DIF_CTSMenuCmds[] = {
   {"Default",     "DIF Set Default Output on Flush",    DIF_CTS_CMD_DEF,           Configure_DIF_CTS_Default},
   {"Outputs",     "DIF Set All Output Mode",            DIF_CTS_CMD_OUTPUT,        NULL},
   {"Flush",       "DIF Flush Queues",                   DIF_CTS_CMD_FLUSH,         NULL},
   {"Init",        "DIF Clock Initiate",                 DIF_CTS_CMD_INIT,          NULL},
   {"Course",      "DIF Set Course Counter",             DIF_CTS_CMD_COURSE,        Configure_DIF_CTS_Course},
   {"Subsec",      "DIF Set Subsec Counter",             DIF_CTS_CMD_SUBSEC,        Configure_DIF_CTS_Subsec},
   {"Load",        "DIF Load Event",                     DIF_CTS_CMD_LOAD,          Load_DIF_CTS_Event},
   {"Fill",        "DIF Fill FIFO",                      DIF_CTS_CMD_FILL,          NULL},
   {"Demo",        "DIF Run Demo",                       DIF_CTS_CMD_DEMO,          NULL},
};

/* Global Variables */
static uint32_t g_TotalEventCount = 0;
static int8_t g_SilentMode = 0;
static int8_t g_InitCounters = 0;

#if defined (__VXWORKS__)
int32_t DIF_CTS(void)
#else
int32_t main(void)
#endif
{
   bool_t stop = FALSE;
   int32_t cardIndex;
   int32_t moduleCnt;
   int32_t module;
   uint32_t moduleID = 0;
   int8_t inputBuffer[80];
   int32_t inputResponseCnt;

   if (naiapp_RunBoardMenu(CONFIG_FILE) == TRUE)
   {
      while (stop != TRUE)
      {
         stop = naiapp_query_CardIndex(naiapp_GetBoardCnt(), 0, &cardIndex);
         if (stop != TRUE)
         {
            check_status(naibrd_GetModuleCount(cardIndex, &moduleCnt));
            stop = naiapp_query_ModuleNumber(moduleCnt, 1, &module);
            if (stop != TRUE)
            {
               moduleID = naibrd_GetModuleID(cardIndex, module);
               if ((moduleID != 0))
               {
                  Run_DIF_CTS(cardIndex, module, moduleID);
               }
            }
         }

         printf("\nType Q to quit or Enter key to restart application:\n");
         stop = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
      }
   }

   printf("\nType the Enter key to exit the program: ");
   naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
   naiapp_access_CloseAllOpenCards();

   return 0;
}

int32_t Run_DIF_CTS(int32_t cardIndex, int32_t module, uint32_t moduleID)
{
   int32_t MaxChannel;
   MaxChannel = naibrd_DIF_GetChannelCount(moduleID);
   if (MaxChannel == 0)
      printf(" *** Module selection not recognized as DIF module. ***\n\n");
   else
      Cfg_DIF_CTS(cardIndex, module, moduleID);
   return cardIndex;
}

/* ... remaining functions: Cfg_DIF_CTS, Display_DIF_CTS_Cfg, */
/* Configure_DIF_CTS_Default, Configure_DIF_CTS_Course, Configure_DIF_CTS_Subsec, */
/* Load_DIF_CTS_Event, run_DIF_CTS_demo, init_DIF_System, load_DIF_FIFO, */
/* fill_Event_Struct, map_DF3_EventValue, reverseMap_DF3_EventValue */
/* See the SSK installation directory for the complete source. */

Help Bot

X