Summary
The NAI Software Support Kit exposes two ways to communicate with a board over the NAI Ethernet Protocol from C code:
- High-level path — open the board over Ethernet and use the regular module API. Your application code looks the same whether the board is in a PCIe slot next to you or on a benchtop across the lab.
- Protocol-level path — build, send, and decode the raw protocol messages yourself. This is what you reach for when a feature has no higher-level wrapper — TDR, IDR, and Script commands in particular live only at the protocol layer.
The two paths aren’t mutually exclusive. You can open a board over Ethernet with the high-level API and still issue protocol-level commands on the same connection.
Tip
If you’re not sure which path you need, start with the high-level path. Drop down to the protocol-level API only when you reach for TDR, IDR, scripting, raw I2C, or a low-level diagnostic feature.
This page summarizes the function surface in each SSK version. For exhaustive parameter lists, refer to the SSK source headers directly.
Path 1: Open the Board over Ethernet
The high-level path is two calls. First, tell the SSK the board’s IP address and port:
/* SSK 1.x */
naibrd_SetIPAddress(cardIndex, "192.168.1.100", "52801");/* SSK 2.x */
naibrd_SetIPAddress(cardIndex, "192.168.1.100", "52801");Then open the device with Ethernet as the transport:
/* SSK 1.x */
naibrd_OpenDevice(cardIndex, NAIBRD_COMM_ETHER_TCP, "");/* SSK 2.x */
naibrd_OpenDevice(cardIndex, NAIBRD_COMM_ETHER_TCP, "");After that, every other naibrd_* call you make against cardIndex flows over the Ethernet connection — no other code change required. Module reads and writes (naibrd_AD_*, naibrd_FT_*, naibrd_DT_*, etc.) route automatically.
Note
The default ports for the NAI Ethernet Listener are TCP
52801and UDP52802. UseNAIBRD_COMM_ETHER_UDPinstead ofNAIBRD_COMM_ETHER_TCPfor UDP.
For more on opening boards and choosing a transport, see Opening a Software Handle to Your Board.
Path 2: Protocol-Level API
Both SSK versions expose helper APIs for building and decoding protocol messages directly. The shape of the API differs between the two versions.
SSK 1.x — naibrd_Ether_*
SSK 1.x bundles everything into a single namespace. Each function builds one full message, sends it over the open connection, waits for the response, and returns the decoded result — so there is one call per protocol operation.
Header: naibrd_ether.h.
Core calls (parameter lists are abridged; see the header for full signatures):
/* Generation selection */
naibrd_Ether_SetGen(int32_t cardIndex, uint16_t gen);
/* Register access */
naibrd_Ether_Read(/* ...flags, address, count, stride, buffer... */);
naibrd_Ether_Write(/* ... */);
naibrd_Ether_ReadFifo(int32_t cardIndex, nai_intf_t intf, uint32_t dataaddr,
uint32_t countaddr, uint32_t count, uint32_t* countremaining,
uint32_t timeout, uint32_t datawidth, void* data, uint32_t* outread);
/* Block commands */
naibrd_Ether_SetBlock(int32_t cardIndex, uint16_t blockid, nai_intf_t intf,
uint32_t regwidth, uint32_t count, uint32_t address[]);
naibrd_Ether_GetBlock(/* ... */);
naibrd_Ether_ClearBlock(int32_t cardIndex, uint16_t blockid);
naibrd_Ether_ReadBlock(int32_t cardIndex, uint16_t blockid, uint32_t regwidth,
uint32_t arraysize, uint32_t outdata[], uint32_t* datacount);
naibrd_Ether_WriteBlock(int32_t cardIndex, uint16_t blockid, uint32_t regwidth,
uint32_t count, uint32_t data[]);
/* ZBlock commands — same shape, with ZBlock variants */
naibrd_Ether_SetZBlock(...), naibrd_Ether_GetZBlock(...), naibrd_Ether_ClearZBlock(...),
naibrd_Ether_ReadZBlock(...), naibrd_Ether_WriteZBlock(...);
/* TDR commands */
naibrd_Ether_SetTDRConfig(...), naibrd_Ether_GetTDRConfig(...),
naibrd_Ether_ClearTDRConfig(...);
naibrd_Ether_StartTDR(int32_t cardIndex, uint16_t tdrid);
naibrd_Ether_StopTDR(int32_t cardIndex, uint16_t tdrid);
/* IDR commands */
naibrd_Ether_SetIDRConfig(...), naibrd_Ether_GetIDRConfig(...),
naibrd_Ether_ClearIDRConfig(...);
naibrd_Ether_StartIDR(int32_t cardIndex, uint16_t idrid); /* sends EnableIDR */
naibrd_Ether_StopIDR(int32_t cardIndex, uint16_t idrid); /* sends DisableIDR */
/* Unprompted-reply setup (TDR/IDR shorthand) */
naibrd_Ether_SetUPR(int32_t cardIndex, int32_t uprslot, nai_ether_upr_type_t upr_type,
uint8_t ip[6], uint16_t udpport, uint32_t vector_periodms,
const uint8_t uprmsg[], uint32_t uprmsglen);A single call sends one command end-to-end. You don’t see the message bytes.
SSK 2.x — naiether_*
SSK 2.x splits message construction from message transport. Instead of one call per protocol operation, you build the message into a buffer, send the buffer, and decode the response. This is more granular but lets you compose, modify, or inspect messages independently — and the same builders compose the commands you embed inside a TDR, IDR, or Script.
Header: naiether.h.
Core builders and decoders:
/* Message sizing */
naiether_GetMsgDataWidth(nai_ether_gen_t gen, int32_t reqWidth);
/* Building read commands (full message in one call) */
naiether_MakeReadMessage(nai_ether_gen_t gen, uint16_t seq, nai_intf_t intf,
int32_t dataWidth, /* ... */);
naiether_MakeFifoReadMessage(...);
naiether_MakeReadRawI2CMessage(...);
naiether_MakeReadRegI2CMessage(...);
/* Building write commands (header only — you append payload, then FinishMessage) */
naiether_MakeWriteMessageHeader(...);
naiether_MakeWriteRawI2CMessageHeader(...);
naiether_MakeWriteRegI2CMessageHeader(...);
/* Mask operations */
naiether_MakeMaskRegByOperationMessage(...);
naiether_MakeMaskRegByValueMessage(...);
/* Generic header for Block/TDR/IDR/Script construction */
naiether_MakeMessageHeader(nai_ether_gen_t gen, uint16_t seq,
nai_ether_typecode_t typeCode, uint8_t* p_msgBuf);
naiether_MakeSendRecvFTxMessageHeader(...);
naiether_FinishMessage(nai_ether_gen_t gen, int32_t* p_inoutoffset, uint8_t* p_msgBuf);
/* Decoding responses */
naiether_DecodeMessageHeader(...);
naiether_DecodeRead8Message(...);
naiether_DecodeRead16Message(...);
naiether_DecodeRead32Message(...);
naiether_DecodeFifoRead16Message(...);
naiether_DecodeFifoRead32Message(...);
/* Retrieving payload data after decoding */
naiether_RetrieveRead8Data(...);
naiether_RetrieveRead16Data(...);
naiether_RetrieveRead32Data(...);A typical SSK 2.x flow is: MakeMessageHeader → fill in the command-specific fields → FinishMessage → send the buffer → receive bytes → DecodeMessageHeader → DecodeRead*Message → RetrieveRead*Data. For simple register reads, MakeReadMessage builds the whole message in one call.
The advantage of this granularity is that the same builders that compose a standalone command also compose the commands carried inside TDR, IDR, and Script configurations. The bytes that make up “the four commands inside a TDR” are constructed by the same Make* calls used to send a standalone command.
Under the Hood — naiif_ether_*
SSK 2.x also exposes a small set of interface-management functions in naiif_ether.h:
naiif_Ether_SetIPAddress(int32_t cardIndex, const char* ipaddress, const char* port);
naiif_Ether_GetIPAddress(int32_t cardIndex, char* ipaddress, char* port);
naiif_Ether_SetGen(int32_t cardIndex, uint16_t gen);
naiif_Ether_GetGen(int32_t cardIndex, uint16_t* gen);
naiif_Ether_SetTimeout_msec(int32_t cardIndex, uint32_t timeout_msec);
naiif_Ether_GetTimeout_msec(int32_t cardIndex, uint32_t* timeout_msec);These manage the underlying Ethernet interface for a board (IP address, generation, request timeout). You rarely call them directly — naibrd_SetIPAddress and naibrd_OpenDevice handle the common cases. The naiif_* calls are useful primarily as reference for understanding what the high-level calls do, or for situations where you need to tune interface state outside the normal open flow (for example, adjusting the request timeout after the board is already open).
Same Operation, Side by Side
Reading from a previously-configured Block, in both SSK versions:
/* SSK 1.x — one call does it all */
uint32_t data[32];
uint32_t dataCount = 0;
nai_status_t status;
status = naibrd_Ether_ReadBlock(cardIndex,
blockId,
4, /* register size in bytes */
32, /* max registers expected */
data,
&dataCount);
if (status != NAI_SUCCESS) {
/* handle error */
}
/* `data` now holds the response; `dataCount` is how many regs came back *//* SSK 2.x — build, send, decode, retrieve */
uint8_t msgBuf[64];
uint8_t respBuf[256];
int32_t msgOffset = 0;
int32_t respLen = 0;
uint32_t data[32];
int32_t retrievedCount = 0;
nai_status_t status;
/* Build the ReadBlock request */
msgOffset = naiether_MakeMessageHeader(gen, seq,
NAIETHER_TYPECODE_READBLOCK,
msgBuf);
/* ... pack BlockId into msgBuf at msgOffset, advance msgOffset ... */
status = naiether_FinishMessage(gen, &msgOffset, msgBuf);
/* Send msgBuf over the open connection, receive into respBuf */
sendAndReceive(cardIndex, msgBuf, msgOffset, respBuf, &respLen);
/* Decode the response */
status = naiether_DecodeMessageHeader(gen, respLen, respBuf, /* &header */);
status = naiether_DecodeRead32Message(gen, /* respTypeCode */, /* dataWidth */,
respLen, respBuf, /* &decoded */);
naiether_RetrieveRead32Data(/* offset */, /* dataWidth */, respBuf,
32, data, &retrievedCount);The SSK 1.x version is shorter because the single call hides the build / send / receive / decode cycle. The SSK 2.x version is more code, but each step is something you can inspect, log, or stub for testing — and the same naiether_Make* builders compose the commands you embed in TDR, IDR, and Script definitions.
Note
Both code paths assume the board has already been opened over Ethernet with the high-level setup shown at the top of this page. The protocol-level API uses the connection that
naibrd_OpenDeviceopened.
When to Stay High-Level
You probably don’t need the protocol-level API if:
- You’re reading or writing module registers normally — the regular module API (
naibrd_AD_*,naibrd_FT_*, etc.) already routes over Ethernet once the board is opened withNAIBRD_COMM_ETHER_TCP. - You’re doing periodic polling — open the board over Ethernet and call the module API in a loop. You don’t need TDRs unless you specifically want the board to drive the cadence.
Reach for the protocol-level API when you need to:
- Define a TDR or IDR — there is no high-level equivalent, because the board’s timer- or interrupt-driven behavior is itself a protocol feature.
- Use Scripts — likewise, scripting is a protocol-level feature.
- Issue raw I2C transactions.
- Send
ReadBlock/WriteBlockfor noncontiguous register groups when the module API doesn’t expose a block helper for them. - Read or inspect raw protocol bytes for debugging, fuzz testing, or interop work with a non-NAI host.
Sample Apps
The SSK ships with sample applications that exercise the protocol-level API. Look in AppSrc/EtherInterface/ (SSK 1.x) or the equivalent ethernet samples in SSK 2.x for working examples of TDR, IDR, and Block usage.
Module-specific Ethernet samples also exist throughout the AppSrc/<module>/ tree — for example, TTL/TTL_Interrupt_Ethernet, SER/SER_EtherInterrupts_*, and IRIG/RG_EtherInterrupts.
Related Resources
- NAI Ethernet Protocol Overview — protocol concepts and command categories.
- NAI Ethernet Protocol Reference — byte-level command layouts.
- Opening a Software Handle to Your Board — opening boards over Ethernet, PCIe, VME, and other transports.
- Interrupts — interrupt vectors and steering, used by IDRs.
- Software Development Guide 1.X — broader SSK 1.x guidance.
- Software Development Guide 2.X — broader SSK 2.x guidance.
