Post Go back to editing

ADIN6310 over SPI: FRER Stream Identification Failing (No R-TAGs, Listener Stats Zero)

Category: Software
Product Number: ADIN6310
Software Version: ADIN6310 TSN Driver Library Rev. 5.1.0-GA 5.1.0-GA

Hello!Raised hands

I am trying to implement IEEE 802.1CB (FRER) using two ADIN6310 evaluation kits connected to Texas Instruments AM62P MPUs over SPI in Linux.

Standard switching and ping work perfectly with my general configuration, but I am struggling to get the FRER Talker and Listener proxies to actually encapsulate/decapsulate the traffic. Normal TCP data is successfully received, but the FRER mechanisms appear to be bypassed.

Hardware & Topology Setup:

  • Host A (Talker Node): TI AM62P MPU connected via SPI to ADIN6310 Eval Kit 1.

  • Host B (Listener Node): TI AM62P MPU connected via SPI to ADIN6310 Eval Kit 2.

  • Topology:

    • Host 1 (AM62p - 1) Eth0 <---> [Port 1] Talker ADIN6310 -1

    • Talker ADIN6310 -1 [Port 4] <---> [Port 4] Listener ADIN6310 - 2 (Path A)

    • Talker ADIN6310 -1 [Port 5] <---> [Port 5] Listener ADIN6310 - 2 (Path B)

    • Listener ADIN6310 - 2 [Port 1] <---> Host 2 (AM62p - 2) Eth0

Software & Configuration Context:

  • VLAN: VLAN 100 allowed on all ports (SES_SetVlanMode(100, 0xFFF)).

  • MSTP is disabled. Using Unicast/Multicast miss return for basic loop protection between ports 4 and 5.

  • I am utilizing the sample code logic provided in the driver user guide for Talker/Listener stream configuration but I changed the redundant ports.

  • Config the VLAN 100 in both AM62p 

The Talker Script VLAN 

#!/bin/sh

echo "Configuring FRER Talker Network Interfaces..."

# 1. Bring up the physical interface
ip link set eth0 up

# 2. Create a VLAN 100 interface and assign an IP
ip link add link eth0 name eth0.100 type vlan id 100
ip addr add 192.168.100.1/24 dev eth0.100
ip link set eth0.100 up

# 3. Force the ARP table to map the Listener's IP to the hardcoded FRER Dest MAC
arp -s 192.168.100.2 C0:D6:0A:F9:6A:D8

echo "Talker setup complete! Ping traffic to 192.168.100.2 will now trigger FRER."

The Listener Script VLAN 

#!/bin/sh

echo "Configuring FRER Listener Network Interfaces..."

# 1. Bring up the physical interface
ip link set eth0 up

# 2. Create a VLAN 100 interface and assign an IP
ip link add link eth0 name eth0.100 type vlan id 100
ip addr add 192.168.100.2/24 dev eth0.100
ip link set eth0.100 up

echo "Listener setup complete! Ready to receive FRER traffic on 192.168.100.2."

The Issue: To test the FRER redundancy, I am running a simple unidirectional TCP client/server counter program from Host A to Host B. by VLAN's IP 192.168.100.2

  1. Data Arrives: The server on Host B successfully receives the counter data.

  2. No FRER Tags:  I connected talker end to AM62P's to 2 ethernet eth0 and etn1 for capture RTAG,  I perform a packet capture (pcap format) on the AM62P monitoring the FRER ports (Ports 4 & 5), there is no FRER ID / R-TAG sequence number injected into the packets.

  3. Zero Statistics: Polling SES_FrerGetSequenceRecoveryStatistics() continuously returns 0 for rxPassedPkts, rxDiscardedPkts, etc., despite data flowing.

Stream Identification Configuration code:

/*
 * Copyright (c) 2024 - 2026 Analog Devices, Inc. All Rights Reserved.
 */

#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "SES_PORT_interface.h"
#include "SES_interface_management.h"
#include "SES_codes.h"
#include "SES_switch.h"
#include "SES_port_api.h"
#include "EX_ses_example_app.h"
#include "EX_util.h"
#include "SES_app_debug.h"
#include "SES_frer.h" // Essential for FRER configurations

#define PORT_COUNT 6
#define SPI_SPEED 16000000

// Port Map Definitions
#define SES_PORT_0 0
#define SES_PORT_1 1
#define SES_PORT_2 2
#define SES_PORT_3 3
#define SES_PORTMAP_PORT_0 1
#define SES_PORTMAP_PORT_1 2
#define SES_PORTMAP_PORT_2 4
#define SES_PORTMAP_PORT_3 8
#define SES_PORTMAP_PORT_4 16
#define SES_PORTMAP_PORT_5 32

// Global Variables
uint8_t AppRunningStatus = 1;
struct sigaction sa;

// Function Prototypes
static bool EX_IsSpiModeValid(int spiMode);
static int EX_HardcodeBoardSettings(uint8_t * primaryMac, uint8_t * portCount, SES_portInit_t * initializePorts_p);
void handle_sigint(int sig);

// FRER & Loop Protection Prototypes
int32_t EX_ConfigureLoopPreventionMissReturn(void);
int32_t configure_adin6310_talker(int32_t * frerTableIndex);
int32_t configure_adin6310_listener(int32_t * listenerFrerTableIndex);
int32_t sesGetFrerListenerStatistics(int32_t listenerFrerTableIndex);

int main(int argc, char ** argv)
{
    int opt;
    int interfaceId = -1;
    int rv = SES_OK;
    char * interface_p = NULL;
    printf("Version :- FRER(Embedded AM62p)\n");
   
#if SPIDEV
    const char * readyPin_p = NULL;
#endif

    uint8_t primaryMac[6] = {0};
    uint8_t hostMac[6] = {0};
    uint8_t portCount;
    SES_portInit_t initializePorts_p[SES_MAX_PORTS] = {0};
    sesID_t deviceId = -1;
    SES_driverFunctions_t driverFunctions = {0};

#if SPIDEV
    SES_PORT_SpidevInitParams_t spiInitParams = {0};
    while ((opt = getopt(argc, argv, "p:g:s:lh")) != -1)
#else
    SES_PORT_ft4222InitParams_t spiInitParams = {{0}, 0, SES_PORT_SPI_notUsed};
    while ((opt = getopt(argc, argv, "p:s:lh")) != -1)
#endif
    {
        switch (opt)
        {
        case 'p':
            interface_p = optarg;
            break;
#if SPIDEV
        case 'g':
            readyPin_p = optarg;
            break;
#endif
        case 's':
            spiInitParams.spiMode = strtol(optarg, NULL, 10);
            break;
        case 'h':
        default:
            printf("Usage: %s -p <interface> [-s <mode>]\n", argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    // 1. Initialize Board Settings
    rv = EX_HardcodeBoardSettings(primaryMac, & portCount, initializePorts_p);
    if (rv != SES_OK)
        exit(EXIT_FAILURE);

    // 2. Initialize SES Library
    if (SES_Init() != SES_OK)
        return -1;

    // 3. YOUR SPI/Ethernet Selection Logic
    if (spiInitParams.spiMode == SES_PORT_SPI_notUsed)
    {
        if (EX_StringToMacAddress(interface_p, hostMac, ":") != SES_OK)
            exit(EXIT_FAILURE);

        driverFunctions.init_p = SES_PORT_ETH_Init;
        driverFunctions.release_p = SES_PORT_ETH_Release;
        driverFunctions.sendMessage_p = SES_PORT_ETH_SendMessage;
        driverFunctions.updateFilter_p = SES_PORT_ETH_UpdateFilter;
        rv = SES_AddHwInterface(hostMac, & driverFunctions, & interfaceId);
    } else
    {
#if SPIDEV
        if (interface_p)
            spiInitParams.spiDevice = interface_p;
        if (readyPin_p)
            spiInitParams.gpioPin = readyPin_p;
        spiInitParams.speed = SPI_SPEED;
#else
        if (interface_p)
            spiInitParams.deviceIndex = strtol(interface_p, NULL, 10);
#endif
        driverFunctions.init_p = SES_PORT_SPI_Init;
        driverFunctions.release_p = SES_PORT_SPI_Release;
        driverFunctions.sendMessage_p = SES_PORT_SPI_SendMessage;
        rv = SES_AddHwInterface( & spiInitParams, & driverFunctions, & interfaceId);
    }

    if (rv != SES_OK)
        return rv;

    // 4. Add Device and Initialize ALL Ports
    rv = SES_AddDevice((uint32_t) interfaceId, primaryMac, & deviceId);
    if (rv == SES_OK)
    {
        rv = SES_InitializePorts(portCount, initializePorts_p);
        if (rv == SES_OK) {
            EX_ConfigureLoopPreventionMissReturn();
        }
    }

    // Print the Firmware Version
    sesGetAppInfo();

    // 6. Apply FRER Configuration
    int32_t talkerFrerTableIndex = -1;
    int32_t listenerFrerTableIndex = -1;

    switch (Frer_Config_opt) {
    case 1: // Talker
        configure_adin6310_talker( & talkerFrerTableIndex);
        printf("SES Application Running: FRER Talker Proxy\n");
        break;
    case 2: // Listener
        configure_adin6310_listener( & listenerFrerTableIndex);
        printf("SES Application Running: FRER Listener Proxy\n");
        break;
    default:
        printf("No FRER Config\n");
    }

    // Setup ISR and Loop
    sa.sa_handler = & handle_sigint;
    sigaction(SIGINT, & sa, NULL);
    printf("Setup Finish\n");

    while (AppRunningStatus)
    {
        switch (Frer_Config_opt) {
        case 1: // Talker
            //printf(".\n"); // Keepalive marker for Talker
            break;
        case 2: // Listener
            sesGetStatisticsPortStatistics(6);
            sesGetFrerListenerStatistics(listenerFrerTableIndex);
            break;
        default: // No Frer config
            printf("No FRER Config");
        }
        sleep(2); // Wait 2 seconds before repolling stats
    }

    SES_RemoveHwInterface(interfaceId);
    return 0;
}

// ---------------------------------------------------------------------
// Loop Protection (Approach 2 - Miss Return)
// ---------------------------------------------------------------------
int32_t EX_ConfigureLoopPreventionMissReturn(void)
{
    int32_t rv = 0;
    uint8_t portMap;
    bool cutThrough;
    uint8_t lookupDone;
    uint8_t txFilter;
    bool txFilterValid;
    uint8_t rxFilter;
    bool rxFilterValid;

    rv = SES_GetUnicastMissReturn(SES_macPort4, & portMap, & cutThrough, & lookupDone, & txFilter, & txFilterValid, & rxFilter, & rxFilterValid);
    if (rv == SES_PORT_OK) {
        portMap = portMap & ~(1 << 5); // 1 << 5 represents Port 5
        SES_SetUnicastMissReturn(SES_macPort4, portMap, cutThrough, lookupDone, txFilter, txFilterValid, rxFilter, rxFilterValid);
    }
    rv = SES_GetMulticastMissReturn(SES_macPort4, & portMap, & cutThrough, & lookupDone, & txFilter, & txFilterValid, & rxFilter, & rxFilterValid);
    if (rv == SES_PORT_OK) {
        portMap = portMap & ~(1 << 5);
        SES_SetMulticastMissReturn(SES_macPort4, portMap, cutThrough, lookupDone, txFilter, txFilterValid, rxFilter, rxFilterValid);
    }

    rv = SES_GetUnicastMissReturn(SES_macPort5, & portMap, & cutThrough, & lookupDone, & txFilter, & txFilterValid, & rxFilter, & rxFilterValid);
    if (rv == SES_PORT_OK) {
        portMap = portMap & ~(1 << 4); // 1 << 4 represents Port 4
        SES_SetUnicastMissReturn(SES_macPort5, portMap, cutThrough, lookupDone, txFilter, txFilterValid, rxFilter, rxFilterValid);
    }
    rv = SES_GetMulticastMissReturn(SES_macPort5, & portMap, & cutThrough, & lookupDone, & txFilter, & txFilterValid, & rxFilter, & rxFilterValid);
    if (rv == SES_PORT_OK) {
        portMap = portMap & ~(1 << 4);
        SES_SetMulticastMissReturn(SES_macPort5, portMap, cutThrough, lookupDone, txFilter, txFilterValid, rxFilter, rxFilterValid);
    }

    printf("Loop Protection (Miss Return Approach) Applied between Ports 4 & 5.\n");
    return SES_PORT_OK;
}

// ---------------------------------------------------------------------
// FRER Talker Configuration
// ---------------------------------------------------------------------
int32_t configure_adin6310_talker(int32_t * frerTableIndex)
{
    int32_t rv = 0;
    uint8_t destinationMAC[6] = {0xC0, 0xD6, 0x0A, 0xF9, 0x6A, 0xD8};
    TSN_ieee802Dot1cbFrer_t frerParameters;
    TSN_ieee802Dot1cbStream_t streamParameters;
    TSN_ieee802Dot1cbMaskAndMatch_t maskAndMatchParameters;

    memset( & frerParameters, 0, sizeof(TSN_ieee802Dot1cbFrer_t));
    memset( & streamParameters, 0, sizeof(TSN_ieee802Dot1cbStream_t));
    memset( & maskAndMatchParameters, 0, sizeof(TSN_ieee802Dot1cbMaskAndMatch_t));

    rv = SES_SetVlanMode(100, 0xFFF); // Allow VLAN 100 on all switch ports
    if (rv < SES_PORT_OK) printf("SES_SetVlanMode Error :: %d\n", rv);

    // Stream table Parameters
    streamParameters.TSN_streamIdentity.identificationType = TSN_nullStream;
    for (int i = 0; i < 6; i++) {
        streamParameters.TSN_streamIdentity.TSN_parameters.TSN_nullStreamIdentification.destinationMac[i] = destinationMAC[i];
    }
    streamParameters.TSN_streamIdentity.TSN_parameters.TSN_nullStreamIdentification.vlan = 100;

    streamParameters.portMap = SES_PORTMAP_PORT_4 | SES_PORTMAP_PORT_5;
    streamParameters.TSN_HardwareTableIndices.dynTblIdx[0] = -1;
    streamParameters.TSN_HardwareTableIndices.dynTblIdx[1] = -1;
    streamParameters.TSN_HardwareTableIndices.exTblruleId = -1;
    streamParameters.useStreamtable = false;

    frerParameters.TSN_sequenceGeneration.reset = false;
    frerParameters.TSN_sequenceRecovery.individualRecovery = false;

    frerParameters.TSN_streamSplit.port = SES_PORT_1;
    frerParameters.TSN_HardwareTableIndices.individualRecoveryTblIdx = -1;
    frerParameters.TSN_HardwareTableIndices.sequenceGenerationTblIdx = -1;
    frerParameters.TSN_HardwareTableIndices.sequenceRecoveryTblIdx = -1;
    frerParameters.TSN_HardwareTableIndices.txTransformTblIdx = -1;

    rv = SES_FrerConfigureTalkerProxyForStream( & frerParameters, & streamParameters, & maskAndMatchParameters);
    if (rv == SES_PORT_OK) {
        * frerTableIndex = frerParameters.TSN_HardwareTableIndices.sequenceGenerationTblIdx;
    }
    return rv;
}

// ---------------------------------------------------------------------
// FRER Listener Configuration & Statistics
// ---------------------------------------------------------------------
int32_t configure_adin6310_listener(int32_t * listenerFrerTableIndex)
{
    int32_t rv = 0;
    uint8_t destinationMAC[6] = {0xC0, 0xD6, 0x0A, 0xF9, 0x6A, 0xD8};
    TSN_ieee802Dot1cbFrer_t frerParameters;
    TSN_ieee802Dot1cbStream_t streamParameters;
    TSN_ieee802Dot1cbMaskAndMatch_t maskAndMatchParameters;

    memset( & frerParameters, 0, sizeof(TSN_ieee802Dot1cbFrer_t));
    memset( & streamParameters, 0, sizeof(TSN_ieee802Dot1cbStream_t));
    memset( & maskAndMatchParameters, 0, sizeof(TSN_ieee802Dot1cbMaskAndMatch_t));

    rv = SES_SetVlanMode(100, 0xFFF);
    streamParameters.TSN_streamIdentity.identificationType = TSN_nullStream;
    for (int i = 0; i < 6; i++) {
        streamParameters.TSN_streamIdentity.TSN_parameters.TSN_nullStreamIdentification.destinationMac[i] = destinationMAC[i];
    }
    streamParameters.TSN_streamIdentity.TSN_parameters.TSN_nullStreamIdentification.vlan = 100;

    // Output reconstructed data strictly out of Port 1
    streamParameters.portMap = SES_PORTMAP_PORT_1;
    streamParameters.TSN_HardwareTableIndices.dynTblIdx[0] = -1;
    streamParameters.TSN_HardwareTableIndices.dynTblIdx[1] = -1;
    streamParameters.TSN_HardwareTableIndices.exTblruleId = -1;
    streamParameters.useStreamtable = false;

    frerParameters.TSN_sequenceRecovery.algorithm = TSN_vector; // Or TSN_match
    frerParameters.TSN_sequenceRecovery.historyLength = 5;
    frerParameters.TSN_sequenceRecovery.resetTimeout = 0xffff;
    frerParameters.TSN_sequenceRecovery.takeNoSequence = false;
    frerParameters.TSN_sequenceRecovery.individualRecovery = false;

    frerParameters.TSN_HardwareTableIndices.individualRecoveryTblIdx = -1;
    frerParameters.TSN_HardwareTableIndices.sequenceGenerationTblIdx = -1;
    frerParameters.TSN_HardwareTableIndices.sequenceRecoveryTblIdx = -1;
    frerParameters.TSN_HardwareTableIndices.txTransformTblIdx = -1;

    rv = SES_FrerConfigureListenerProxyForStream( & frerParameters, & streamParameters, & maskAndMatchParameters);
    if (rv == SES_PORT_OK) {
        * listenerFrerTableIndex = frerParameters.TSN_HardwareTableIndices.sequenceRecoveryTblIdx;
    }
    return rv;
}

int32_t sesGetFrerListenerStatistics(int32_t listenerFrerTableIndex)
{
    if (listenerFrerTableIndex == -1) return SES_OK;
    int rv = SES_OK;
    TSN_ieee802Dot1cbFrer_t monitorParams;

    memset( & monitorParams, 0, sizeof(TSN_ieee802Dot1cbFrer_t));
    monitorParams.TSN_HardwareTableIndices.sequenceRecoveryTblIdx = listenerFrerTableIndex;
    monitorParams.TSN_sequenceRecovery.port = 0;

    rv = SES_FrerGetSequenceRecoveryStatistics( & monitorParams);
    if (rv == SES_PORT_OK) {
        printf("\n--- FRER Listener Stats ---\n");
        printf(" Passed Packets (Valid)   :: %llu\n", monitorParams.TSN_perPortPerStreamCounters.rxPassedPkts);
        printf(" Discarded Packets (Dups) :: %llu\n", monitorParams.TSN_perPortPerStreamCounters.rxDiscardedPkts);
        printf(" Lost Packets             :: %llu\n", monitorParams.TSN_perPortPerStreamCounters.rxLostPkts);
        printf(" Out Of Order Packets     :: %llu\n", monitorParams.TSN_perPortPerStreamCounters.rxOutOfOrderPkts);
        printf(" Rogue Packets            :: %llu\n", monitorParams.TSN_perPortPerStreamCounters.rxRoguePkts);
    } else {
        printf("Failed to read FRER stats: %d\n", rv);
    }
    return rv;
}

// ---------------------------------------------------------------------
// Basic Environment Setup
// ---------------------------------------------------------------------
static int EX_HardcodeBoardSettings(uint8_t * primaryMac, uint8_t * portCount, SES_portInit_t * initializePorts_p)
{
    uint8_t mac_addr[6] = {0x7A, 0xC6, 0xBB, 0x11, 0x11, 0x11};
    memcpy(primaryMac, mac_addr, 6);
    * portCount = PORT_COUNT;

    for (int i = 0; i < * portCount; i++)
    {
        initializePorts_p[i].enablePort = 1;
        initializePorts_p[i].mii = SES_rgmiiMode;
        initializePorts_p[i].phyConfig.autoNegEnable = true;
        initializePorts_p[i].phyConfig.speed = SES_phySpeed1000;
        initializePorts_p[i].phyConfig.duplexMode = SES_phyDuplexModeFull;
        initializePorts_p[i].phyConfig.crossover = SES_autoMdi;
        initializePorts_p[i].miiClkDelays.enableRxDelay = SES_DISABLE_DELAY;
        initializePorts_p[i].miiClkDelays.enableTxDelay = SES_DISABLE_DELAY;
        initializePorts_p[i].miiClkDelays.clkSelection = SES_CLK_INTERNAL;
        initializePorts_p[i].phyConfig.phyPullupCtrl = SES_PHY_PULLUP_INTERNAL;
       
        uint8_t addrs[] = {0, 1, 2, 4, 8, 9};
        initializePorts_p[i].phyConfig.phyAddr = addrs[i];

        if (i == 0)
        {
            initializePorts_p[i].phyType = SES_phyUnmanaged;
            initializePorts_p[i].linkStatusPolarity = SES_LINK_ACTIVE_LOW;
        } else
        {
            initializePorts_p[i].phyType = SES_phyADIN1300;
        }
    }
    return SES_OK;
}

static bool EX_IsSpiModeValid(int spiMode)
{
    return (spiMode >= 0 && spiMode <= 4);
}

void handle_sigint(int sig)
{
    (void) sig; // Acknowledge parameter to stop compiler warning
    AppRunningStatus = 0;
}

From the below function I configured based on what given in driver guide 


int32_t EX_ConfigureLoopPreventionMissReturn(void);
int32_t configure_adin6310_talker(int32_t * frerTableIndex);
int32_t configure_adin6310_listener(int32_t * listenerFrerTableIndex);
int32_t sesGetFrerListenerStatistics(int32_t listenerFrerTableIndex);

I need to know is that anything I missed or I did some thing wrong.

Thank you.

Edit Notes

Updated Test cases
[edited by: prahadeeshDP at 4:28 AM (GMT -4) on 1 Jun 2026]
Parents
  • I'm glad you were able to configure and test FRER in your setup.

    In the listener configuration, the sequence recovery function is applied on the listener's configured egress port, where duplicate frames are evaluated and eliminated before the recovered stream is forwarded.

    As a result, if Ports 4 and 5 only receive redundant traffic and are not the point where sequence recovery is performed, FRER sequence recovery statistics would not be expected on those ports.

Reply
  • I'm glad you were able to configure and test FRER in your setup.

    In the listener configuration, the sequence recovery function is applied on the listener's configured egress port, where duplicate frames are evaluated and eliminated before the recovered stream is forwarded.

    As a result, if Ports 4 and 5 only receive redundant traffic and are not the point where sequence recovery is performed, FRER sequence recovery statistics would not be expected on those ports.

Children
No Data

Before You Switch


Switching languages will make ADI Explorer unavailable. Resume your session by switching back to English and reopening ADI Explorer.