EtherBlockCommands
Edit this on GitLab
EtherBlockCommands Sample Application (SSK 1.x)
Overview
The EtherBlockCommands sample application demonstrates how to use Ethernet Block Commands to perform grouped register reads and writes on NAI boards using the NAI Software Support Kit (SSK 1.x). Block commands let you define a named collection of non-contiguous register addresses as a "block," then read or write all of them in a single Ethernet transaction. This reduces network round trips and improves throughput when you need to access multiple scattered registers on a regular basis — for example, polling status registers from both the motherboard common area and module common area in one operation.
This is a board-level sample — it is not specific to any module type. It works with any NAI board that supports Generation 4 (or later) Ethernet commands. The block command protocol is handled entirely by the board’s Ethernet interface; no module firmware involvement is required.
Prerequisites
Before running this sample, make sure you have:
-
An NAI board connected via Ethernet that supports Gen4 Ethernet commands.
-
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.
How to Run
Launch the EtherBlockCommands executable from your build output directory. On startup the application looks for a configuration file (default_Ether_Block.txt). On the first run, this file will not exist — the application will present an interactive board menu where you configure a board connection and card index. You can save this configuration so that subsequent runs skip the menu and connect automatically. Once connected, a command menu lets you exercise each block operation.
Board Connection
|
Note
|
This startup sequence is common to all NAI sample applications. The board connection code shown here is not specific to any module type. For details on board connection configuration, see the First Time Setup Guide. |
The main() function follows the standard SSK 1.x startup flow. No module selection is needed because block commands operate at the board level — they access registers by absolute address rather than through a module API.
-
Call
naiapp_RunBoardMenu()to load a saved configuration file (if one exists) or present the interactive board menu. The configuration file (default_Ether_Block.txt) is not included with the SSK — it is created when you save your connection settings from the board menu. On the first run, the menu will always appear. -
Query the user for a card index with
naiapp_query_CardIndex(). -
Check Gen4 support with
SupportsGen4Ether()and enter the command loop.
#if defined (__VXWORKS__)
int32_t EtherBlockCommands(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
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)
{
Run_EtherBlockCommands(cardIndex);
}
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;
}
|
Important
|
Common connection errors you may encounter at this stage:
|
Program Structure
Entry Point
On standard platforms the entry point is main(). On VxWorks the entry point is EtherBlockCommands() — the SSK 1.x build system selects the correct variant via a preprocessor guard:
#if defined (__VXWORKS__)
int32_t EtherBlockCommands(void)
#else
int32_t main(void)
#endif
The startup flow is the same in both cases:
-
Attempt to load the saved configuration file via
naiapp_RunBoardMenu(CONFIG_FILE). If the file does not yet exist, the interactive board menu is presented instead. -
Enter a loop that queries for card index.
-
Call
Run_EtherBlockCommands()to enter the interactive command loop. -
On exit, close all open board connections with
naiapp_access_CloseAllOpenCards().
Application Parameters
The Run_EtherBlockCommands() function populates an naiapp_AppParameters_t struct that is passed to every command handler. For block commands, only the card index is needed — there is no module or channel selection:
naiapp_AppParameters_t eth_params;
p_naiapp_AppParameters_t etherblock_params = ð_params;
etherblock_params->cardIndex = cardIndex;
The function also calls SupportsGen4Ether() to determine whether the board supports Generation 4 Ethernet commands. Block commands are only available on Gen4+ boards — if this check fails, the block operations will report that they are not supported.
Command Loop
Run_EtherBlockCommands() drives the interactive command loop. On each iteration it displays the command menu and dispatches the user’s selection to the matching handler function:
| Command | Description |
|---|---|
Set |
Define a block configuration (assign register addresses to a block ID) |
Get |
Retrieve the register addresses currently configured for a block ID |
Clear |
Remove a block configuration |
Read |
Read the current values of all registers in a block |
Write |
Write data to all registers in a block |
The menu-driven structure is a convenience of the sample application. In your own application, you would call the same underlying naibrd_Ether_*Block() API functions directly.
Block Configuration
This section covers defining, retrieving, and clearing block configurations. A block is a named collection of register addresses identified by a numeric block ID. Once a block is configured, you can read or write all of its registers in a single Ethernet transaction.
Set Block Configuration
To define a block of register addresses in your own application, call naibrd_Ether_SetBlock(). This tells the board’s Ethernet interface to associate a set of register addresses with a block ID. The board stores this mapping internally — subsequent read and write operations reference the block by ID rather than listing individual addresses each time.
uint32_t address[MAX_ETHER_BLOCK_REG_CNT];
nai_intf_t inf;
uint32_t regcount = 0;
InitBlockRegisters(&inf, ®count, &address[0]);
nai_status_t status = naibrd_Ether_SetBlock(cardIndex, (uint16_t)blockid,
inf, (uint32_t)DEF_REGISTER_SIZE, regcount, address);
-
cardIndex— identifies which board to configure. -
blockid— a numeric ID (1 toMAX_ETHER_BLOCK_ID) that identifies this block. You choose the ID; it is an arbitrary label. -
inf— the interface type for register access.NAI_INTF_ONBOARDspecifies onboard access. -
DEF_REGISTER_SIZE— register width in bytes. The sample uses 4 (32-bit registers). -
regcount— the number of registers in the block. -
address[]— an array of register addresses to include in the block.
The sample’s InitBlockRegisters() function populates the address array with 18 registers spanning the motherboard common area (addresses like 0x017C, 0x0180, 0x0190, etc.) and the first module’s common area (addresses like 0x40D0, 0x40E0, etc.). In your own application, populate this array with whatever register addresses you need to read or write as a group.
Get Block Configuration
To retrieve the register addresses currently assigned to a block in your own application, call naibrd_Ether_GetBlock(). This lets you verify what was configured or inspect a block set up by another part of your system.
uint32_t arraysize = MAX_ETHER_BLOCK_REG_CNT;
uint32_t address[MAX_ETHER_BLOCK_REG_CNT];
uint32_t regcount;
nai_status_t status = naibrd_Ether_GetBlock(cardIndex, (uint16_t)blockid,
arraysize, address, ®count);
-
arraysize— the size of theaddress[]buffer you are providing. Must be large enough to hold all register addresses in the block. -
regcount— on return, the number of registers actually configured in the block. If zero, the block ID has not been configured.
Clear Block Configuration
To remove a block configuration from the board, call naibrd_Ether_ClearBlock(). After clearing, the block ID is available for reuse.
nai_status_t status = naibrd_Ether_ClearBlock(cardIndex, (uint16_t)blockid);
|
Important
|
Common Errors
|
Block Read and Write
Once a block is configured, you can read all of its registers or write data to all of them in a single transaction. This is the primary performance benefit of block commands — instead of issuing individual register read/write commands over Ethernet, one block operation handles the entire set.
Read Block Registers
To read the current values of all registers in a block in your own application, call naibrd_Ether_ReadBlock(). The board reads each register in the block and returns all values in a single response.
uint32_t arraysize = MAX_ETHER_BLOCK_REG_CNT;
uint32_t data[MAX_ETHER_BLOCK_REG_CNT];
uint32_t datacount = 0;
nai_status_t status = naibrd_Ether_ReadBlock(cardIndex, (uint16_t)blockid,
(uint32_t)DEF_REGISTER_SIZE, arraysize, data, &datacount);
-
DEF_REGISTER_SIZE— register width in bytes (4 for 32-bit). -
arraysize— the size of thedata[]buffer. -
data[]— on return, contains the values read from each register in the block, in the same order as the addresses were configured. -
datacount— on return, the number of values actually read. If zero, the block is not configured.
Write Block Registers
To write data to all registers in a block in your own application, call naibrd_Ether_WriteBlock(). The board writes each value to the corresponding register in the block in a single transaction.
uint32_t data[MAX_ETHER_BLOCK_REG_CNT];
/* Populate data[] with the values to write */
InitBlockRegistersData(DEF_REGISTER_COUNT, &data[0]);
nai_status_t status = naibrd_Ether_WriteBlock(cardIndex, (uint16_t)blockid,
(uint32_t)DEF_REGISTER_SIZE, DEF_REGISTER_COUNT, data);
-
data[]— an array of values to write. The order must match the order of addresses in the block configuration. -
DEF_REGISTER_COUNT— the number of registers to write (must match the number configured in the block).
The sample’s InitBlockRegistersData() function fills the data array with test values (0xABCD0001, 0xABCD0002, etc.) for demonstration purposes. In your own application, populate the array with the actual values you need to write.
|
Important
|
Common Errors
|
Troubleshooting Reference
This table summarizes common errors and symptoms covered in the sections above. For detailed context on each entry, refer to the relevant section.
| Error / Symptom | Possible Causes | Suggested Resolution |
|---|---|---|
No board found or connection timeout |
Board not powered, incorrect or missing configuration file, network issue |
Verify hardware is powered and connected via Ethernet. If |
Gen4 not supported |
Board uses pre-Generation 4 Ethernet protocol |
Block commands require Gen4 or later. Verify your board’s firmware version and upgrade if necessary. |
Block read returns no data |
Block ID not configured |
Call |
Write fails or produces unexpected values |
Invalid register addresses in block, or data count does not match register count |
Verify that all addresses in the block are valid for your board. Ensure the data array length matches the block’s register count. |
|
One or more register addresses in the block do not exist on the board |
Review the address list passed to |
"Ethernet Block Command Support Prior to Generation 4 Ethernet commands currently not supported" |
Board does not support Gen4 Ethernet |
Use a board with Gen4+ Ethernet support. |
Full Source
The complete source for this sample is provided below for reference. The sections above explain each part in detail.
Full Source — EtherBlockCommands.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 "naibrd_ether.h"
#include "advanced/nai_ether_adv.h"
static const int8_t *CONFIG_FILE = (const int8_t *)"default_Ether_Block.txt";
/* Function prototypes */
static bool_t Run_EtherBlockCommands(int32_t cardIndex);
static bool_t QueryEthBlockID(int32_t *blockid);
static void InitBlockRegisters(nai_intf_t *inf, uint32_t *count, uint32_t address[]);
static void InitBlockRegistersData(uint32_t count, uint32_t data[]);
static nai_status_t SetEthBlockConfig(int32_t paramCount, int32_t* p_params);
static nai_status_t GetEthBlockConfig(int32_t paramCount, int32_t* p_params);
static nai_status_t ClearEthBlockConfig(int32_t paramCount, int32_t* p_params);
static nai_status_t ReadEthBlockRegisters(int32_t paramCount, int32_t* p_params);
static nai_status_t WriteEthBlockRegisters(int32_t paramCount, int32_t* p_params);
static bool_t bGen4BlockCommands = FALSE;
/****** Command Table *******/
enum eth_block_commands
{
ETH_BLOCK_CMD_SET,
ETH_BLOCK_CMD_GET,
ETH_BLOCK_CMD_CLEAR,
ETH_BLOCK_CMD_READ,
ETH_BLOCK_CMD_WRITE,
ETH_BLOCK_CMD_COUNT
};
/****** Command Tables *******/
naiapp_cmdtbl_params_t ETH_BlockcmdMenuCmds[] = {
{"Set", "Set Block Configuration", ETH_BLOCK_CMD_SET, SetEthBlockConfig},
{"Get", "Get Block Configuration", ETH_BLOCK_CMD_GET, GetEthBlockConfig},
{"Clear", "Clear Block Configuration", ETH_BLOCK_CMD_CLEAR, ClearEthBlockConfig},
{"Read", "Read Block Registers", ETH_BLOCK_CMD_READ, ReadEthBlockRegisters},
{"Write", "Write to Block Registers", ETH_BLOCK_CMD_WRITE, WriteEthBlockRegisters},
};
#define DEF_REGISTER_COUNT 18
#define DEF_REGISTER_SIZE 4
/**************************************************************************************************************/
#if defined (__VXWORKS__)
int32_t EtherBlockCommands(void)
#else
int32_t main(void)
#endif
{
bool_t stop = FALSE;
int32_t cardIndex;
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)
{
Run_EtherBlockCommands(cardIndex);
}
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;
}
/**************************************************************************************************************/
static bool_t Run_EtherBlockCommands(int32_t cardIndex)
{
bool_t bQuit = FALSE;
bool_t bContinue = TRUE;
bool_t bCmdFound = FALSE;
int32_t cmd;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
naiapp_AppParameters_t eth_params;
p_naiapp_AppParameters_t etherblock_params = ð_params;
etherblock_params->cardIndex = cardIndex;
bGen4BlockCommands = SupportsGen4Ether(cardIndex);
while (bContinue)
{
naiapp_utils_LoadParamMenuCommands(ETH_BLOCK_CMD_COUNT, ETH_BlockcmdMenuCmds);
naiapp_display_ParamMenuCommands((int8_t *)"Ethernet Block Command Menu");
printf("\nType Ethernet Block command or %c to quit : ", NAI_QUIT_CHAR);
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt > 0)
{
bCmdFound = naiapp_utils_GetParamMenuCmdNum(inputResponseCnt, inputBuffer, &cmd);
if (bCmdFound)
{
switch (cmd)
{
case ETH_BLOCK_CMD_SET:
case ETH_BLOCK_CMD_GET:
case ETH_BLOCK_CMD_CLEAR:
case ETH_BLOCK_CMD_READ:
case ETH_BLOCK_CMD_WRITE:
ETH_BlockcmdMenuCmds[cmd].func(APP_PARAM_COUNT, (int32_t*)etherblock_params);
break;
default:
printf("Invalid command entered\n");
break;
}
}
else
printf("Invalid command entered\n");
}
}
else
bContinue = FALSE;
}
return bQuit;
}
/**************************************************************************************************************/
static bool_t QueryEthBlockID(int32_t *blockid)
{
bool_t bQuit = FALSE;
int8_t inputBuffer[80];
int32_t inputResponseCnt;
printf("Enter the Block ID: (default: 1) > ");
bQuit = naiapp_query_ForQuitResponse(sizeof(inputBuffer), NAI_QUIT_CHAR, inputBuffer, &inputResponseCnt);
if (!bQuit)
{
if (inputResponseCnt == 0)
*blockid = 1;
else
{
*blockid = atol((const char *)inputBuffer);
}
}
return bQuit;
}
/**************************************************************************************************************/
static void InitBlockRegisters(nai_intf_t *inf, uint32_t *count, uint32_t address[])
{
int32_t index = 0;
*inf = NAI_INTF_ONBOARD;
*count = DEF_REGISTER_COUNT;
if (bGen4BlockCommands)
{
/* Motherboard Common area */
address[index++] = 0x017C;
address[index++] = 0x0180;
address[index++] = 0x0190;
address[index++] = 0x0480;
address[index++] = 0x0494;
address[index++] = 0x04A0;
address[index++] = 0x04B8;
address[index++] = 0x04C4;
address[index++] = 0x04D0;
/* Module Common area */
address[index++] = 0x40D0;
address[index++] = 0x40E0;
address[index++] = 0x40F0;
address[index++] = 0x4100;
address[index++] = 0x4104;
address[index++] = 0x4114;
address[index++] = 0x4128;
address[index++] = 0x4130;
address[index++] = 0x4134;
}
else
{
printf("Ethernet Block Command Support Prior to Generation 4 Ethernet commands currently not supported\n");
}
}
/**************************************************************************************************************/
static void InitBlockRegistersData(uint32_t count, uint32_t data[])
{
uint32_t index = 0;
for (index = 0; index < count; index++)
{
if (bGen4BlockCommands)
data[index] = 0xABCD0000 + index + 1;
else
data[index] = 0x1000 + index + 1;
}
}
/**************************************************************************************************************/
static nai_status_t SetEthBlockConfig(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
nai_status_t status;
uint32_t address[MAX_ETHER_BLOCK_REG_CNT];
nai_intf_t inf;
uint32_t regcount = 0;
p_naiapp_AppParameters_t p_ethblock_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = p_ethblock_params->cardIndex;
int32_t blockid;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
bQuit = QueryEthBlockID(&blockid);
if (!bQuit)
{
InitBlockRegisters(&inf,®count, &address[0]);
status = check_status(naibrd_Ether_SetBlock(cardIndex,(uint16_t)blockid,inf,(uint32_t)DEF_REGISTER_SIZE,regcount,address));
if (status == NAI_SUCCESS)
{
printf("Block ID = %d configured.\n", blockid);
}
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
/**************************************************************************************************************/
static nai_status_t GetEthBlockConfig(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
nai_status_t status;
uint32_t arraysize = MAX_ETHER_BLOCK_REG_CNT;
uint32_t address[MAX_ETHER_BLOCK_REG_CNT];
uint32_t regcount;
int32_t i;
p_naiapp_AppParameters_t p_ethblock_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = p_ethblock_params->cardIndex;
int32_t blockid;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
bQuit = QueryEthBlockID(&blockid);
if (!bQuit)
{
status = check_status(naibrd_Ether_GetBlock(cardIndex, (uint16_t)blockid, arraysize, address, ®count));
if (status == NAI_SUCCESS)
{
if (regcount > 0)
{
printf("\n\nBlock ID = %d - %d Registers with Addresses:\n", blockid, regcount);
for (i = 0; i < (int32_t)regcount; i++)
{
printf("%3d: 0x%08X\n", i+1, address[i]);
}
}
else
printf("\n\nNo Registers configured for Block ID = %d\n", blockid);
}
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
/**************************************************************************************************************/
static nai_status_t ClearEthBlockConfig(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
nai_status_t status;
p_naiapp_AppParameters_t p_ethblock_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = p_ethblock_params->cardIndex;
int32_t blockid;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
bQuit = QueryEthBlockID(&blockid);
if (!bQuit)
{
status = check_status(naibrd_Ether_ClearBlock(cardIndex, (uint16_t)blockid));
if (status == NAI_SUCCESS)
{
printf("Block ID = %d cleared\n", blockid);
}
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
/**************************************************************************************************************/
static nai_status_t ReadEthBlockRegisters(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
nai_status_t status;
uint32_t arraysize = MAX_ETHER_BLOCK_REG_CNT;
uint32_t data[MAX_ETHER_BLOCK_REG_CNT];
uint32_t datacount = 0;
int32_t i;
p_naiapp_AppParameters_t p_ethblock_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = p_ethblock_params->cardIndex;
int32_t blockid;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
bQuit = QueryEthBlockID(&blockid);
if (!bQuit)
{
status = check_status(naibrd_Ether_ReadBlock(cardIndex, (uint16_t)blockid, (uint32_t)DEF_REGISTER_SIZE, arraysize, data, &datacount));
if (status == NAI_SUCCESS)
{
if (datacount > 0)
{
printf("\n\nBlock ID = %d - %d Register Values:\n", blockid, datacount);
for (i = 0; i < (int32_t)datacount; i++)
{
printf("%3d: 0x%08X\n", i+1, data[i]);
}
}
else
printf("\n\nNo Registers read for Block ID = %d\n", blockid);
}
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}
/**************************************************************************************************************/
static nai_status_t WriteEthBlockRegisters(int32_t paramCount, int32_t* p_params)
{
bool_t bQuit = FALSE;
nai_status_t status;
uint32_t data[MAX_ETHER_BLOCK_REG_CNT];
p_naiapp_AppParameters_t p_ethblock_params = (p_naiapp_AppParameters_t)p_params;
int32_t cardIndex = p_ethblock_params->cardIndex;
int32_t blockid;
#if defined (WIN32)
UNREFERENCED_PARAMETER(paramCount);
#endif
bQuit = QueryEthBlockID(&blockid);
if (!bQuit)
{
InitBlockRegistersData(DEF_REGISTER_COUNT, &data[0]);
status = check_status(naibrd_Ether_WriteBlock(cardIndex,(uint16_t)blockid,(uint32_t)DEF_REGISTER_SIZE,DEF_REGISTER_COUNT,data));
if (status == NAI_SUCCESS)
{
printf("Block ID = %d Data written.\n", blockid);
}
}
return (bQuit) ? NAI_ERROR_UNKNOWN : NAI_SUCCESS;
}