This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMDS243EVM: Profinet example stuck in PNIO_device_open

Part Number: TMDS243EVM
Other Parts Discussed in Thread: DP83869, SYSCONFIG

Tool/software:

Dear TI team,

we're trying to work with the Profinet device example from the Industrial Communications SDK 09.02.00.15.

On some of our EVM boards the application is stuck in PNIO_device_open while polling for pnpb_get_state, similar to an issue we and others observed a couple of months ago in the linked thread.

At that time we were testing with a particular AM64x EVM, and observed the same issue as that thread's OP.  provided a pre-compiled binary of a fixed version that we successfully verified on that AM64x board. I'm not 100% sure if we ever tested that AM64x EVM with a binary built from SDK 09.02.00.15, and unfortunately that board is now "electrically" dead, so I can't verify.

Our current issue (stuck in PNIO_device_open) is that we're now seeing exactly the same issue with one AM24x EVM whereas the same binary works on a different AM24x EVM.

The hardware itself should be fine, as it works e.g. with an EtherCAT slave or an Ethernet/IP slave.

Laxman mentioned in the referenced thread that you "found out that there are few causes for the application getting stuck".

  • What could cause the application to get stuck in PNIO_device_open?
  • Are there any known issues left in 09.02.00.15?
  • When we can expect a newer version of the industrial communications SDK?
  • How can we debug this issue?

Our ultimate goal is to port the profinet device stack to a third-party SoM & board, and it would really help to understand how to debug issues in binary-only parts of the stack (the application just calls "PN_API_IOD_startup", and then gets stuck "somewhere" in there).

Regards,

Dominic

  • A brief update:

    On the third-party SoM we're failing in EDDP_GlbFatalError called from the "EDD" task. There are no error messages visible on the UART. It would be great if you had some suggestions on how to debug that issue, too.

    Regards,

    Dominic

  • We will prioritize this next week; I found this thread to be similar : (+) AM2432: Problem with acyclic data in Profinet - Arm-based microcontrollers forum - Arm-based microcontrollers - TI E2E support forums and they had some success with aligning to MPU and linker configuration of example.

  • We've fixed an issue in our code adapting to the 3rd party SoM, and now we're stuck in the same location as with the AM24x EVM (stuck in PNIO_device_open polling pnpb_get_state).

    The way I read this other thread it seems that a change that fixed an issue on one board caused problems on a different board:

    If I correct this the stack works correctly on one board, while using a different board (probably of different revision) it still does not work and to get it working I had to copy the demo configuration of the MPU and linker.

    Not sure how helpful that is.

    Looking at the linker script / memory regions and the MPU settings, it is rather difficult to understand how the various memory regions are used. The names for the MPU sections don't seem to match their use, and there are several unused linker regions.

    Is there any documentation available that explains what kind of memory regions the profinet stack requires?

    Once we have the stack up and running on the 3rd party SoM we intend to move to a Linux (A53) + FreeRTOS (R5f) setup where the stack is expected to run on an R5f alongside Linux. For that it would be good to understand what the stack requires, instead of having to blindly adapt existing regions that may or may not be used.

    Regards,

    Dominic

  • will start on addressing this tomorrow

  • Hi Dominic,

    We're trying to see if we can reproduce this problem here. Would you please share your linker script and syscfg file?

    Thanks.
    Kind regards,
    Kamil

  • Hello Kamil,

    on the AM24x EVM we're using the linker script and syscfg file as-is from the industrial communications SDK 09.02.00.15 example.

    The linker script is actually generated from the syscfg file. I've attached the syscfg file for your reference:

    1016.example.zip

    Please note that we might well be facing two different problems:

    • The software fails on 1 out of 3 AM24x EVMs
      • The failing board was modified to use an external PCIe reference clock instead of the on-board clock generator
      • Other industrial communications examples work just fine on this board, e.g. the EtherCAT and Ethernet/IP examples
    • The software fails on the 3rd party AM64x SoM
      • We've modified the example application to match the 3rd party SoM, mainly:
        • Use of ICSSG0 instead of ICSSG1
        • Use of DP83867 PHY instead of DP83869
        • We use an EEPROM instead of a flash for NVM (change in pn_app_iod_bsp.h)
      • The PHYs are detected as alive and configured for MII operation

    In both failing cases the application is stuck in PNIO_device_open.

    Regards,

    Dominic

  • Hello Dominic,

    PNIO_device_open() is where many threads start to work so it's hard to tell at this point.

    1. Based on the shared sysconfig file, I see you're using our default configuration (ICSSG1 with DP83869). Can you clarify more about this point? 
    In order to reproduce the issue I need the sysconfig file that reflects all your changes with this regard.

    2. The PHYs are detected as alive but have you detected any messages sent over them to the network (wireshark, etc.)? Can you try to generate any random IP traffic from the application to verify that? (before PN_APP_IOD_startup is called)

    3. Switching to EEPROM instead of flash should not cause any problems.

    4. For testing purposes I've shared a .out file for AM243x_EVM. Would you please try to load it on the failing board and tell me the outcome?
    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/908/profinetio_5F00_device_5F00_am243x_5F00_evm_5F00_r5f_5F00_freertos_5F00_mii_5F00_2_5F00_Release.out
    Thanks.
    Kind regards,
    Kamil 

  • Hello Kamil,


    1. Based on the shared sysconfig file, I see you're using our default configuration (ICSSG1 with DP83869). Can you clarify more about this point? 
    In order to reproduce the issue I need the sysconfig file that reflects all your changes with this regard.

    That was from the AM24x EVM. On the AM24x EVM we're using the example from the industrial communications SDK without any changes.

    On the 3rd-party SoM we've got a couple of changes, especially those pointed out above (ICSSG0, DP83867, EEPROM instead of flash). I've attached the SysConfig file for that, too:

    example_3rdparty_som.zip

    2. The PHYs are detected as alive but have you detected any messages sent over them to the network (wireshark, etc.)? Can you try to generate any random IP traffic from the application to verify that? (before PN_APP_IOD_startup is called)

    on the AM24x EVM that's showing the problem (stuck in PNIO_device_open) there is no link after calling into PN_API_IOD_startup. For the first time after power-cycling the board, there is a 1 Gbit/s link until I call into PN_API_IOD_startup, at which point the link is lost. If I then just "system reset" the board (which I normally do), there is no link at all.

    On an AM24x EVM that's working there is a 1 Gbit/s link first time after power-cycling, until the call to PN_API_IOD_startup, at which point the link is briefly lost and comes back up at 100 Mbit/s.

    On the working AM24x EVM the call to PN_API_IOD_startup returns even if there's no Ethernet cable connected, so I'm assuming that a (missing) Ethernet link is NOT a blocking point for PN_API_IOD_startup.

    I'll have to ask my colleague to verify the link status on the 3rd-party SoM.

    3. Switching to EEPROM instead of flash should not cause any problems.

    That was my expectation, thanks for confirming. Please note we've only switched to EEPROM on the 3rd-party SoM. On the AM24x EVM we're using flash.

    4. For testing purposes I've shared a .out file for AM243x_EVM. Would you please try to load it on the failing board and tell me the outcome?
    profinetio_device_am243x_evm_r5f_freertos_mii_2_Release.out

    That binary isn't working on the failing EVM either.

    (null bootloader output ...)
    INFO: Bootloader_runSelfCpu:217: All done, reseting self ...
    
    Restore remanent memory indicated.
    Using user-defined submodule configuration (not from remanent memory).
    Restore remanent memory indicated.
    Restore remanent memory indicated.
    Restore remanent memory indicated.
    Restore remanent memory indicated.
    Restore remanent memory indicated.
    [APP] INFO: Initializing PRU instance ...
                                             DP83869 detected
    DP83869 detected
    Phy 15 : Disable RGMII mode
    Phy 15 : Disable GBit ANEG
    Phy 3 : Disable RGMII mode
    Phy 3 : Disable GBit ANEG

    With your binary I'm not getting a link and I'm still stuck in PNIO_device_open. The output on the UART is pretty much the same between the working and non-working case. Above output is from your binary on the non-working board.

    Best Regards,

    Dominic

  • Hello Kamil,

    I spent some more time looking into the difference between the Ethernet link on the working and the non-working AM24x EVM.

    I can see that the profinet stack code puts the PHY into power-down mode on both boards, but on the non-working board the PHY is never powered up again.

    I can also see that the code calls CUST_PHY_DP83869_setLinkConfig on the working board, but never calls that function on the non-working board.

    No idea if that is just a symptom of a blocking point much earlier, or an indication of why we're having a problem on the one board.

    Are there any other points I could check for to understand how far the initialization gets on the non-working board?

    Regards,

    Dominic

  • Hello Kamil,

    do you have any update for us?

    This is currently blocking us, and with the binary-only Profinet stack our ability to debug this ourselves is severly limited.

    It would be great if we could concentrate on the EVM issue first, then look at the 3rd-party SoM.

    Regards,

    Dominic

  • Hello Dominic,

    Thanks for your responses and patience.
    We're working on this issue with another team along some other tasks. I will get back to you very soon.
    I'm also trying to check if we can provide you with debugging capabilities so you can help us on your side as well.

    Kind regards,
    Kamil

  • Hi Dominic,

    The failing board was modified to use an external PCIe reference clock instead of the on-board clock generator

    On the AM243 EVM, have you made only this change on top of the out of box application?
    Have you tried using the on-board clock generator and check if the startup issue still occurs on the same board? 

    Regards,
    Laxman

  • Hello Laxman,

    that is a hardware modification (see https://software-dl.ti.com/mcu-plus-sdk/esd/AM64X/10_00_00_20/exports/docs/api_guide_am64x/EXAMPLES_DRIVERS_PCIE_ENUMERATE_EP.html) that requires several 0-Ohm resistors to be removed and others added. Unless you have a compelling explanation why that affects the Profinet example I'd prefer not to mess with this board.

    The application (software) is completely unmodified.

    Regards,

    Dominic

  • Small update:

    I've tested with an AM64x EVM that has the same hardware modification, and this board doesn't show the problem.

    -> I doubt the hardware modification is reponsible for this issue

    Regards,

    Dominic

  • Hello Dominic,

    you will receive shortly an email with a binary to be tested.

    Thank you.
    Kind regards,
    Kamil

  • Hi Dominic,

    We will send you the binary tomorrow. For now we need to verify something... Have you tried to reset the NVM? if not, please do the following:

    1. Replace main.c with the corresponding file attached
    2. Replace pn_app_iod_bsp.c with the corresponding file attached
    3. Rebuild and run the program once and send me the UART log.
    4. Load the previous program (the one that hangs) and send me the UART log.

    Thanks.
    Kind regards,
    Kamil

    /*!
     *  \file main.c
     *
     *  \brief
     *  Demo application
     *
     *  \author
     *  Texas Instruments Incorporated
     *
     *  \copyright
     *  Copyright (C) 2023 Texas Instruments Incorporated
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include <stdarg.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "drivers/pinmux.h"
    #include "hwal.h"
    #include "osal.h"
    #include "ti_board_open_close.h"
    #include "ti_drivers_open_close.h"
    
    #include "pn_api_iod_callbacks.h"
    #include "pn_api_iod_error.h"
    #include "pn_api_iod_handle.h"
    #include "pn_api_iod_startup.h"
    
    #include "pn_app_iod_alarm.h"
    #include "pn_app_iod_bsp.h"
    #include "pn_app_iod_communication.h"
    #include "pn_app_iod_data.h"
    #include "pn_app_iod_device.h"
    #include "pn_app_iod_mod_cfg.h"
    #include "pn_app_iod_record.h"
    #include "pn_app_iod_settings.h"
    #include "pn_app_iod_utils.h"
    
    #define PN_APP_MAIN_TASK_PRIO                   22 /* TASK_PRIO_MAIN */
    #define PN_APP_UART_TASK_PRIO                   2
    #define PN_APP_MAIN_TASK_STACK_SIZE             (2048+256)
    #define PN_APP_UART_TASK_STACK_SIZE             1024
    #define ASYNC_REC_RSP_WAITING_COUNTER           5000 /* Number of counts before sending a response */
    
    /*!
     * \ingroup PN_APP_IOD_TYPES_DOXY_GROUP
     */
    typedef struct
    {
        uint32_t asyncRecReadTimer;     /*!< For asynchronous record read responses */
        uint32_t asyncRecWriteTimer;    /*!< For asynchronous record write responses  */
    } PN_APP_IOD_asyncRecRspTimer_t;
    
    static char aOutStream_s[1024] = { 0 };
    static uint32_t uartWritePos = 0;
    static uint32_t uartReadPos = 0;
    void *uartSignal;
    void *uartMutex;
    static void* PN_APP_mainHandle;
    static void* PN_APP_uartTaskHandle;
    uint8_t PN_APP_mainTaskStack[PN_APP_MAIN_TASK_STACK_SIZE]__attribute__((aligned(32), section(".threadstack")));
    
    /* Handles of asynchronous record read/write request */
    extern void *asyncRecReadHandle;
    extern void *asyncRecWriteHandle;
    
    
    /*!
     * \brief
     * Callback function to handle system errors.
     *
     * \details
     * This function is called in response to a system error. It informs the user
     * of the error, and in case the error is fatal, it terminates the program.
     *
     * \param[in]       errorCode           Error code.
     * \param[in]       fatal               Fatal error indicator.
     * \param[in]       paramCnt            Number of parameters received for the error.
     * \param[in]       argptr              List of error parameters (arguments).
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    void PN_APP_IOD_errorHandlerCallback(uint32_t errorCode, bool fatal, uint8_t paramCnt, va_list argptr)
    {
        int32_t indexArg;
        uint32_t arg;
    
        OSAL_printf("\r\nError: 0x%8.8x, Fatal: %s", errorCode, fatal ? "yes" : "no");
        for (indexArg = 0; indexArg < paramCnt; indexArg++)
        {
            arg = va_arg(argptr, uint32_t);
            OSAL_printf(", P%d: 0x%8.8x", indexArg, arg);
        }
    
        if (fatal == true)
        {
            /* It's possible to introduce an infinite while loop here */
            exit(1);
        }
    }
    
    /*!
     * \brief
     * Initialize application data.
     *
     * \details
     * This function initializes all global variables and array that are required
     * for further operations. This includes, AR information, IO data arrays and
     * record lists and modes.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_init(void)
    {
        bool asyncRead = false;
        bool asyncWrite = false;
    
        /* Initialize AR information */
        PN_APP_IOD_initArInfo();
    
        /* Initialize application IO datasets */
        PN_APP_IOD_initAppData();
    
        /* Initialize user record list and information */
        PN_APP_IOD_initRecordInfo(asyncRead, asyncWrite);
    }
    
    /*!
     * \brief
     * Initialize device instance.
     *
     * \details
     * This function initializes the device instance that is later passed to Profinet stack.
     *
     * \param[in]       iodInst            Device instance.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_buildDevInstance(PN_API_IOD_IodInstance_t *iodInst)
    {
        /* Set IO-device instance info according to the user configuration found in pn_app_iod_cfg.h */
        iodInst->vendorId = PN_API_IOD_VENDOR_ID;
        iodInst->deviceId = PN_API_IOD_DEVICE_ID;
        iodInst->pDevType = (int8_t *)PN_API_IOD_DEV_TYPE;
        iodInst->maxNumOfSubslots = PN_API_IOD_MAX_NUM_OF_SUBSLOTS;
        iodInst->maxNumOfBytesPerSubslot = PN_API_IOD_NUM_OF_BYTES_PER_SUBSLOT;
    }
    
    /*!
     * \brief
     * Initialize device annotation.
     *
     * \details
     * This function initializes the device annotation that is later passed to Profinet stack.
     *
     * \param[in]       devAnnotation      Device annotation.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_buildDevAnnotation(
        PN_API_IOD_Handle_t *const pnHandle,
        PN_API_IOD_DevAnnotation_t *devAnnotation)
    {
    #if SOC_AM243X
        PN_API_IOD_setDeviceType(pnHandle, "AM243EVM");
    #elif SOC_AM64X
        PN_API_IOD_setDeviceType(pnHandle, "AM64EVM");
    #else
    #error Unsupported target type
    #endif
        PN_API_IOD_setDeviceOrderId(pnHandle, "TI-Sitara EVM");
        PN_API_IOD_setDeviceSerialNumber(pnHandle, "1234567890");
        PN_API_IOD_setDeviceHWRevision(pnHandle, 0x101C);
        PN_API_IOD_setDeviceVersionNumber(pnHandle, "V", 4, 1, 0, 0);
    
        /* Set IO-device annotation info according to the user configuration found in pn_app_iod_cfg.h */
        OSAL_MEMORY_memset(devAnnotation, ' ', sizeof(PN_API_IOD_DevAnnotation_t));
    
        OSAL_MEMORY_memcpy(
            &devAnnotation->deviceType,
            PN_API_IOD_DEV_TYPE,
            PN_API_IOD_DEV_TYPE_SIZE);
    
        OSAL_MEMORY_memcpy(
            &devAnnotation->orderId,
            PN_API_IOD_DEV_ANNOTATION_ORDER_ID,
            PN_API_IOD_DEV_ANNOTATION_ORDER_ID_SIZE);
    
        OSAL_MEMORY_memcpy(
            &devAnnotation->serialNum,
            PN_API_IOD_IM0_SERIAL_NUM,
            PN_API_IOD_IM0_SERIAL_NUM_SIZE);
    
        devAnnotation->hwRevision  = PN_API_IOD_HW_REV;
        devAnnotation->swRevisionPrefix = PN_API_IOD_VER_PREFIX;
        devAnnotation->swRevision1 = PN_API_IOD_VER_HH;
        devAnnotation->swRevision2 = PN_API_IOD_VER_H;
        devAnnotation->swRevision3 = PN_API_IOD_VER_L;
    }
    
    /*!
     * \brief
     * Build the list of callback functions.
     *
     * \details
     * This function assigns the functions implemented in the application to the
     * corresponding elements in the callback function list and register them.
     *
     * \param[in]      pnHandle            Profinet API Handle.
     *
     * \return         result of the operation as uint32_t
     * \retval         #PN_API_OK          Success.
     * \retval         #PN_API_NOT_OK      Something went wrong.
     * \retval         #PN_API_ERR_PARAM   Invalid parameter.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static uint32_t PN_APP_IOD_buildCallbacksList(PN_API_IOD_Handle_t *const pnHandle)
    {
        uint32_t status = PN_API_NOT_OK;
        if (NULL != pnHandle)
        {
            /* register app callback functions */
            PN_API_IOD_Callbacks_t callbacks = {
                .arConnect = PN_APP_IOD_cbArConnectInd,
                .arDisconn = PN_APP_IOD_cbArDisconn,
                .reportNewIpAddr = PN_APP_IOD_cbReportipAddr,
                .resetToFactory = PN_APP_IOD_cbResetToFactory,
                .storeRemaMem = PN_APP_IOD_cbStoreRemaMem,
                .restoreRemaMem = PN_APP_IOD_cbRestoreRemaMem,
                .freeRemaMem = PN_APP_IOD_cbFreeRemaMem,
                .setLed = PN_APP_IOD_cbSetLed,
                .stopLedBlink = PN_APP_IOD_cbStopLedBlink,
                .startLedBlink = PN_APP_IOD_cbStartLedBlink,
                .arOwnership = PN_APP_IOD_cbArOwnershipInd,
                .paramEnd = PN_APP_IOD_cbParamEnd,
                .readData = PN_APP_IOD_cbDataRead,
                .writeData = PN_APP_IOD_cbDataWrite,
                .recordRead = PN_APP_IOD_cbRecordRead,
                .arInData = PN_APP_IOD_cbArInData,
                .reportArFsuRecord = PN_APP_IOD_cbReportArFSURecord,
                .recordWrite = PN_APP_IOD_cbRecordWrite,
                .newModPull = PN_APP_IOD_cbNewModPull,
                .readIOxSDataOnly = PN_APP_IOD_cbDataReadIOxSOnly,
                .writeIOxSDataOnly = PN_APP_IOD_cbDataWriteIOxSOnly,
                .readyForInputUpdate = PN_APP_IOD_cbReadyForInputUpdate,
                .newModPlug = PN_APP_IOD_cbNewModPlug,
                .asyncReqDone = PN_APP_IOD_cbAsyncReqDone,
                .devAlarm = PN_APP_IOD_cbDevAlarm,
                .outSubmodSubstValRead = PN_APP_IOD_cbOutSubmodSubstValRead,
                .errorLog = PN_APP_IOD_cbErrorLog,
                .updateAppCycle = PN_APP_IOD_updateAppCycle
            };
    
            /* Register callback functions */
            status = PN_API_IOD_registerCallbacks(pnHandle, &callbacks);
        }
        return status;
    }
    
    /*!
     * \brief
     * Startup Profinet process.
     *
     * \details
     * This function calls for initialing all profinet-related data, creates a Profinet
     * device and starts up Profinet stack. It then runs infinitely trying to handle
     * asynchronous record read/write requests, if there's any.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_startup(void)
    {
        PN_API_IOD_IodInstance_t iodInst = { 0 };
        PN_API_IOD_DevAnnotation_t devAnnotation = { 0 };
        PN_API_IOD_SubmodListEntry_t *pIoSubmodList = NULL;
        PN_API_IOD_Im0ListEntry_t *pIm0List = NULL;
        uint32_t ioSubmodListSize = 0;
        uint32_t im0ListSize = 0;
        bool checkRemaData = true;
        uint32_t status = PN_API_OK;
        PN_API_IOD_Handle_t* pHandle = NULL;
        PN_APP_IOD_asyncRecRspTimer_t asyncRecRspTimer;
    
        /* Initialize asynchronous record response timers */
        asyncRecRspTimer.asyncRecReadTimer = 0;
        asyncRecRspTimer.asyncRecWriteTimer = 0;
    
        /* Initialize remanent storage */
        PN_APP_IOD_remaInit();
    
        /* Initialize LED handling */
        PN_APP_IOD_ledInit();
    
    
        /* Initialize output hardware signal */
        PN_APP_IOD_outHwSignalInit();
    
        /* Initialize PNIO user application */
        PN_APP_IOD_init();
    
        /* Initialize PNIO stack */
        PN_API_IOD_initStack();
    
        /* Build IO-device instance */
        PN_APP_IOD_buildDevInstance(&iodInst);
    
        /* Create a device handle */
        pHandle = PN_API_IOD_new();
    
        /* Build IO-device Annotation */
        PN_APP_IOD_buildDevAnnotation(pHandle, &devAnnotation);
        if(NULL != pHandle)
        {
            /* Load submodule configuration data */
     //       status = PN_APP_IOD_loadSubmodConfig(pHandle, &pIoSubmodList, &ioSubmodListSize,
     //                                         &pIm0List, &im0ListSize, checkRemaData);
            if (status == PN_API_OK)
            {
                /* Create callback functions instance and register it */
     //           status = PN_APP_IOD_buildCallbacksList(pHandle);
            }
    
            if (status == PN_API_OK)
            {
     //           status = PN_API_IOD_startup(pHandle, &iodInst, &devAnnotation, pIoSubmodList,
     //                                   ioSubmodListSize, pIm0List, im0ListSize);
            }
    
            if (status == PN_API_OK)
            {
                status = PN_APP_IOD_initCyclicDataExchange(pHandle);
            }
        }
    
        while(1)
        {
            OSAL_SCHED_sleep(1);
    
            /* An asynchronous read request has been registered? */
            if (NULL != asyncRecReadHandle)
            {
                /* Wait for a few seconds, then respond */
                asyncRecRspTimer.asyncRecReadTimer++;
                if (ASYNC_REC_RSP_WAITING_COUNTER == asyncRecRspTimer.asyncRecReadTimer)
                {
                    PN_APP_IOD_recordReadAsyncResponse();
                    asyncRecRspTimer.asyncRecReadTimer = 0;
                }
            }
    
            /* An asynchronous write request has been registered? */
            if (NULL != asyncRecWriteHandle)
            {
                /* Wait for a few seconds, then respond */
                asyncRecRspTimer.asyncRecWriteTimer++;
                if (ASYNC_REC_RSP_WAITING_COUNTER == asyncRecRspTimer.asyncRecWriteTimer)
                {
                    PN_APP_IOD_recordWriteAsyncResponse();
                    asyncRecRspTimer.asyncRecWriteTimer = 0;
                }
            }
        }
    }
    
    /*!
     * \brief
     * Callback function to print.
     *
     * \details
     * This function is called when the system needs to print out something. It
     * initializes a UART transaction and writes the received data to it.
     *
     * \param[in]       pContext            Context (not used).
     * \param[in]       pFormat             Format of the printed data.
     * \param[in]       arg                 List of parameters (arguments) to be printed.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    void PN_APP_IOD_printf(void *pContext, const char *__restrict pFormat, va_list arg)
    {
        int lengthWritten;
        char tmpString[256];
        char *tmpStringPos = tmpString;
    
        OSALUNREF_PARM(pContext);
    
        lengthWritten = vsnprintf(tmpString, sizeof(tmpString), pFormat, arg);
    
        OSAL_lockNamedMutex(uartMutex, OSAL_WAIT_INFINITE);
        while (lengthWritten > 0)
        {
            uint32_t lengthAvailable;
            uint32_t lengthWrite = lengthWritten;
    
            if (uartReadPos > uartWritePos)
            {
                lengthAvailable = uartReadPos - uartWritePos - 1;
            }
            else
            {
                lengthAvailable = sizeof(aOutStream_s) - uartWritePos;
                if (uartReadPos == 0)
                    lengthAvailable -= 1;
            }
    
            if (lengthAvailable == 0)
            {
                break;
            }
    
            if (lengthWrite > lengthAvailable)
            {
                lengthWrite = lengthAvailable;
            }
    
            memcpy(&aOutStream_s[uartWritePos], tmpStringPos, lengthWrite);
    
            uartWritePos += lengthWrite;
            tmpStringPos += lengthWrite;
            lengthWritten -= lengthWrite;
            if (uartWritePos >= sizeof(aOutStream_s))
            {
                uartWritePos -= sizeof(aOutStream_s);
            }
        }
        OSAL_unLockNamedMutex(uartMutex);
        OSAL_postSignal(uartSignal);
    }
    
    /*!
     * \brief
     * Start the system.
     *
     * \details
     * This function opens SoC and board drivers, then calls Profinet startup function.
     *
     * \param[in]       pvTaskArg           Arguments passed during task initialization.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_mainTask(void *pvTaskArg)
    {
        uint32_t systemRetVal = SystemP_FAILURE;
    
        (void)pvTaskArg;
    
        /* Open SoC and board drivers. */
        Drivers_open();
        systemRetVal = Board_driversOpen();
        DebugP_assert(systemRetVal == SystemP_SUCCESS);
    
    
        PN_APP_IOD_startup();
    
        OSAL_SCHED_exitTask(NULL);
    }
    
    
    /*!
     * \brief
     *  uart print task
     *
     * \details
     * This function, is used to do printf on a low prio thread
     *
     */
    static void PN_APP_IOD_uartTask(void *pvTaskArg)
    {
        (void)pvTaskArg;
    
        while(1)
        {
            uint32_t bytesToWrite;
    
            static UART_Transaction transaction; // in Interrupt mode this needs to be static
    
            while (uartReadPos == uartWritePos)
            {
                OSAL_waitSignal(uartSignal, 2);
            }
    
            UART_flushTxFifo(gUartHandle[CONFIG_UART_CONSOLE]);
            UART_Transaction_init(&transaction);
    
            bytesToWrite = uartWritePos; // read uartWrite only once to prevent changes due to higher prio
            if (bytesToWrite > uartReadPos)
            {
                transaction.count = bytesToWrite - uartReadPos;
            }
            else
            {
                transaction.count = sizeof(aOutStream_s) - uartReadPos;
            }
    
            transaction.buf = (void *)&aOutStream_s[uartReadPos];
            transaction.args = NULL;
    
            OSAL_lockNamedMutex(uartMutex, OSAL_WAIT_INFINITE);
            uartReadPos += transaction.count;
            if (uartReadPos == sizeof(aOutStream_s))
            {
                uartReadPos = 0;
            }
            OSAL_unLockNamedMutex(uartMutex);
    
            (void)UART_write(gUartHandle[CONFIG_UART_CONSOLE], &transaction);
    
        }
    
        OSAL_SCHED_exitTask(NULL);
    }
    
    /*!
     * \brief
     *  Main entry point.
     *
     * \details
     * This function, initializes and configures SoC hardware and specific OSAL and HWAL modules,
     * which are essential for PROFINET communication. Afterwards, it creates and starts the main
     * application task.
     *
     * \return         result of the operation as uint32_t
     * \retval         #PN_API_OK          Success.
     * \retval         #PN_API_NOT_OK      Something went wrong.
     *
     */
    int main(void)
    {
        uint32_t status = PN_API_OK;
        uint8_t syncEvent = SYNC_OUT0;
    
        /* Initialize SoC specific modules. */
        System_init();
    
        /* Additional configuration to route the sync0 or sync1 signal to SYNC0_OUT PIN */
        PN_APP_IOD_tsrConfig(syncEvent);
    
        Board_init();
        /*
         * Both HWAL and OSAL use OSAL error handler for error reporting.
         * Therefore error handler should be registered prior the initialization.
         *
         * OSAL_init() must be called PRIOR to HWAL_init()
         */
        OSAL_registerErrorHandler(PN_APP_IOD_errorHandlerCallback);
    
        status = OSAL_init();
        if (PN_API_OK != status)
        {
            status = PN_API_NOT_OK;
        }
    
        status = HWAL_init();
        if (PN_API_OK != status)
        {
            status = PN_API_NOT_OK;
        }
    
        OSAL_registerPrintOut(NULL, PN_APP_IOD_printf);
    
        PN_APP_mainHandle = OSAL_SCHED_startTask(
            PN_APP_IOD_mainTask,
            NULL,
            PN_APP_MAIN_TASK_PRIO,
            PN_APP_mainTaskStack,
            sizeof(PN_APP_mainTaskStack),
            OSAL_OS_START_TASK_FLG_NONE,
            "app_main");
    
        if (NULL == PN_APP_mainHandle)
        {
            status = PN_API_NOT_OK;
        }
    
        uartSignal = OSAL_createSignal("uartSignal");
        uartMutex = OSAL_createNamedMutex("uartMutex");
        PN_APP_uartTaskHandle = OSAL_SCHED_startTask(
            PN_APP_IOD_uartTask,
            NULL,
            PN_APP_UART_TASK_PRIO,
            NULL,
            PN_APP_UART_TASK_STACK_SIZE,
            OSAL_OS_START_TASK_FLG_NONE,
            "uart_task");
    
        OSAL_startOs();
    
        return status;
    }
    
    /*!
     *  \file pn_app_iod_bsp.c
     *
     *  \brief
     *  Functions and callbacks for handling the board support package like memory and LED control.
     *
     *  \author
     *  Texas Instruments Incorporated
     *
     *  \copyright
     *  Copyright (C) 2023 Texas Instruments Incorporated
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include "pn_app_iod_bsp.h"
    
    #include <string.h>
    #include <ti_board_open_close.h>
    /* Enable Sync jitter measurement */
    #include "drivers/pinmux.h"
    #include <drivers/hw_include/cslr_soc.h>
    /**/
    #include <nvm.h>
    
    #include "osal.h"
    
    #include "pn_api_iod_bsp.h"
    #include "pn_api_iod_error.h"
    
    /*!
     * \brief Profinet rema data entry
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    typedef struct PN_APP_IOD_RemaEntry
    {
        uint8_t  *data;        /*!< Rema data address */
        uint16_t  maxlen;      /*!< Max data length */
        uint16_t *actuallen;   /*!< Data legnth */
    } PN_APP_IOD_RemaEntry_t;
    
    /*!
     * \brief Profinet rema data context
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    typedef struct PN_APP_IOD_RemaContext
    {
        PN_API_IOD_Handle_t *handle;                    /*!< Device handle */
        uint32_t             bytesToWrite;              /*!< Bytes to write */
        bool                 retriggerAfterComplete;    /*!< Retrigger read/write; nvm status = busy */
    } PN_APP_IOD_RemaContext_t;
    
    /*!
     * \brief Profinet LED monitoring struct
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    typedef struct PN_APP_IOD_Led
    {
        uint32_t setState;                             /*!< New state: ON, OFF, BLINK*/
        bool     actualState;                          /*!< Current state */
    }PN_APP_IOD_Led_t;
    
    
    /*!
     * \brief Profinet LED context
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    typedef struct PN_APP_IOD_LedContext
    {
        void *threadHandle;                             /*!< Led Task handle */
        uint32_t state;                                 /*!< Current state */
        PN_APP_IOD_Led_t led[PN_API_IOD_LedMaxLeds];    /*!< Led list */
    }PN_APP_IOD_LedContext_t;
    
    /*!< Profinet application rema data context instance */
    PN_APP_IOD_RemaContext_t remaContext;
    
    /*!< Profinet application rema data instance */
    PN_APP_IOD_Nvdata_t ramNvData;
    
    /*!< Profinet application LED context instance */
    PN_APP_IOD_LedContext_t ledContext;
    
    /*
     * PAD configuration for Ball.D18
     * Required to configure SYNC0_OUT as pin out
     */
    static Pinmux_PerCfg_t gTsrPinMuxMainDomainCfg[] = {
        {
            PIN_ECAP0_IN_APWM_OUT,
            ( PIN_MODE(1) | PIN_PULL_DISABLE ) /* PIN_MODE 1 is SYNC0_OUT */
        },
        {PINMUX_END, PINMUX_END}
    };
    
    void PN_APP_IOD_tsrConfig(uint8_t syncSignal)
    {
        Pinmux_config(gTsrPinMuxMainDomainCfg, PINMUX_DOMAIN_ID_MAIN);
        /* PRU IEP Enable SYNC MODE */
        CSL_REG32_WR(CSL_PRU_ICSSG1_PR1_CFG_SLV_BASE + CSL_ICSSCFG_IEPCLK, 1);
        CSL_REG32_WR(CSL_TIMESYNC_EVENT_INTROUTER0_CFG_BASE + 0x64, 0x0001001D + syncSignal);
    }
    
    
    uint32_t gpioOutHwBaseAddr = GPIO_OUT_HW_SIGNAL_BASE_ADDR;
    uint32_t gpioOutHwPinNum = GPIO_OUT_HW_SIGNAL_PIN;
    
    extern uint8_t inDataCounter;
    extern bool inDataChanged;
    
    /*!
     * \brief
     * Get LED instance
     *
     * \details
     * In this function, the application returns LED instance.
     *
     * \param[in]      led                 Led type
     *
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static void PN_APP_IOD_getLed(PN_API_IOD_Led_t led, uint32_t *instance, uint32_t *index)
    {
        *instance = CONFIG_LED_NUM_INSTANCES;
        *index    = 0;
    
        switch (led)
        {
            case PN_API_IOD_LedBlink:
                if (ledContext.led[PN_API_IOD_LedBlink].setState != PN_APP_IOD_LED_OFF)
                {
                    // overwrite run led
                    *instance = CONFIG_DCP_SIGNAL_LED;
                    *index    = 0;
                }
                break;
            case PN_API_IOD_LedRun:
                if (ledContext.led[PN_API_IOD_LedBlink].setState == PN_APP_IOD_LED_OFF)
                {
                    // ignore in case of blinking enabled
                    *instance = CONFIG_DCP_SIGNAL_LED;
                    *index    = 0;
                }
                break;
            case PN_API_IOD_LedMaint:
                break;
            case PN_API_IOD_LedError:
                *instance = CONFIG_SYS_FAILURE_LED;
                *index    = 0;
                break;
            case PN_API_IOD_LedUser00:
            case PN_API_IOD_LedUser01:
            case PN_API_IOD_LedUser02:
            case PN_API_IOD_LedUser03:
            case PN_API_IOD_LedUser04:
            case PN_API_IOD_LedUser05:
            case PN_API_IOD_LedUser06:
            case PN_API_IOD_LedUser07:
                *instance = CONFIG_CYCLIC_LEDS;
                *index    = led - PN_API_IOD_LedUser00;
                break;
            case PN_API_IOD_LedSync:
            default:
                // undefined led
                break;
        }
    }
    
    /*!
     * \brief
     * Update LED state.
     *
     * \details
     * In this function, the application updates LED states.
     *
     * \param[in]      led                 Led type
     *
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static void PN_APP_IOD_updateLed(PN_API_IOD_Led_t led)
    {
        uint32_t instance;
        uint32_t index;
    
        PN_APP_IOD_getLed(led, &instance, &index);
    
        if (instance != CONFIG_LED_NUM_INSTANCES)
        {
            bool ledEnable = false;
    
            if (ledContext.led[led].setState & ledContext.state)
            {
                // check if led is enabled in this state
                ledEnable = true;
            }
    
            if (ledContext.led[led].actualState != ledEnable)
            {
                // switch only if state has changed
                if (ledEnable)
                {
                    LED_on(gLedHandle[instance], index);
                }
                else
                {
                    LED_off(gLedHandle[instance], index);
                }
                ledContext.led[led].actualState = ledEnable;
            }
        }
    }
    
    
    /*!
     * \brief
     * Led task.
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    void PN_APP_IOD_ledTask(void *arg)
    {
        OSALUNREF_PARM(arg);
    
        while(1)
        {
            uint32_t led;
            OSAL_SCHED_sleep(PN_APP_IOD_LED_TASK_INTERVAL_MS/PN_APP_IOD_LED_TICKS);
    
            // increment current led global state
            ledContext.state <<= 1;
            if (ledContext.state >= (1<<PN_APP_IOD_LED_TICKS) || ledContext.state == 0)
            {
                ledContext.state = 1;
            }
    
            for (led = 0; led < PN_API_IOD_LedMaxLeds; led++)
            {
                PN_APP_IOD_updateLed(led);
            }
        }
    }
    
    uint32_t PN_APP_IOD_ledInit(void)
    {
        uint32_t status = PN_API_OK;
    
        memset(&ledContext, 0, sizeof(ledContext));
    
        ledContext.threadHandle = OSAL_SCHED_startTask(
            PN_APP_IOD_ledTask,
            &ledContext,
            PN_APP_IOD_LED_TASK_PRIO,
            NULL,
            PN_APP_IOD_LED_TASK_STACK,
            0,
            "LED Task");
    
        if (ledContext.threadHandle == NULL)
        {
            status = PN_API_NOT_OK;
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_cbSetLed(
        PN_API_IOD_Handle_t *const pnHandle,
        const PN_API_IOD_Led_t     led,
        const bool                 state)
    {
        // OSAL_printf("Set Led request received on Led %u to %u\r\n", led, state);
        OSALUNREF_PARM(pnHandle);
    
        if (led < PN_API_IOD_LedMaxLeds)
        {
            if (state)
            {
                ledContext.led[led].setState = PN_APP_IOD_LED_ON;
            }
            else
            {
                ledContext.led[led].setState = PN_APP_IOD_LED_OFF;
            }
        }
    
        return PN_API_OK;
    }
    
    uint32_t PN_APP_IOD_cbStartLedBlink(
        PN_API_IOD_Handle_t *const pnHandle,
        const uint32_t             portNum,
        const uint32_t             frequency)
    {
        OSAL_printf("DCP signal request received on Port %u\r\n", portNum);
        OSALUNREF_PARM(pnHandle);
        OSALUNREF_PARM(portNum);
        OSALUNREF_PARM(frequency);
    
        ledContext.led[PN_API_IOD_LedBlink].setState = PN_APP_IOD_LED_BLINK;
        ledContext.led[PN_API_IOD_LedBlink].actualState = ledContext.led[PN_API_IOD_LedRun].actualState;
    
        return PN_API_OK;
    }
    
    uint32_t PN_APP_IOD_cbStopLedBlink(PN_API_IOD_Handle_t *const pnHandle, const uint32_t portNum)
    {
        OSAL_printf("DCP signal request stop received.\r\n");
        OSALUNREF_PARM(pnHandle);
        OSALUNREF_PARM(portNum);
    
        ledContext.led[PN_API_IOD_LedBlink].setState = PN_APP_IOD_LED_OFF;
        ledContext.led[PN_API_IOD_LedRun].actualState = ledContext.led[PN_API_IOD_LedBlink].actualState;
    
        return PN_API_OK;
    }
    
    
    uint32_t PN_APP_IOD_outHwSignalInit(void)
    {
        uint32_t status = PN_API_OK;
    
        gpioOutHwBaseAddr = (uint32_t) AddrTranslateP_getLocalAddr(gpioOutHwBaseAddr);
        GPIO_setDirMode(gpioOutHwBaseAddr, gpioOutHwPinNum, GPIO_OUT_HW_SIGNAL_DIR);
    
        return status;
    }
    
    uint32_t PN_APP_IOD_setOutHwSignal(uint8_t value)
    {
        uint32_t status = PN_API_OK;
    
        if(0 == value)
        {
            GPIO_pinWriteLow(gpioOutHwBaseAddr, gpioOutHwPinNum);
        }
        else
        {
            GPIO_pinWriteHigh(gpioOutHwBaseAddr, gpioOutHwPinNum);
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_cbFreeRemaMem(PN_API_IOD_Handle_t *const pnHandle, uint8_t *const destMem)
    {
        uint32_t status = PN_API_OK;
    
        OSALUNREF_PARM(pnHandle);
        OSALUNREF_PARM(destMem);
    
        return status;
    }
    
    /*!
     * \brief
     * Read device rema data entries
     *
     * \return         result of the operation as uint32_t.
     * \retval         #PN_API_OK            Success.
     * \retval         #PN_API_NOT_OK        Something went wrong.
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static uint32_t PN_APP_IOD_getRemaEntry(
        PN_APP_IOD_RemaEntry_t       *entry,
        PN_API_IOD_RemaDataType_t type,
        const uint32_t            instance)
    {
        uint32_t status = PN_API_OK;
        memset(entry, 0, sizeof(*entry));
    
        switch (type)
        {
            case PN_API_IOD_RemaDataDevName:
                entry->data      = ramNvData.devName;
                entry->maxlen    = sizeof(ramNvData.devName);
                entry->actuallen = &ramNvData.devNameLen;
                break;
            case PN_API_IOD_RemaDataIpSuite:
                entry->data      = (uint8_t *)&ramNvData.ipSuite;
                entry->maxlen    = sizeof(ramNvData.ipSuite);
                entry->actuallen = &ramNvData.ipSuiteLen;
                break;
            case PN_API_IOD_RemaDataIm1:
                if (instance < PN_API_IOD_MAX_NUM_OF_SUBSLOTS)
                {
                    entry->data      = (uint8_t *)&ramNvData.im1[instance];
                    entry->maxlen    = sizeof(ramNvData.im1[instance]);
                    entry->actuallen = &entry->maxlen;
                }
                break;
            case PN_API_IOD_RemaDataIm2:
                if (instance < PN_API_IOD_MAX_NUM_OF_SUBSLOTS)
                {
                    entry->data      = (uint8_t *)&ramNvData.im2[instance];
                    entry->maxlen    = sizeof(ramNvData.im2[instance]);
                    entry->actuallen = &entry->maxlen;
                }
                break;
            case PN_API_IOD_RemaDataIm3:
                if (instance < PN_API_IOD_MAX_NUM_OF_SUBSLOTS)
                {
                    entry->data      = (uint8_t *)&ramNvData.im3[instance];
                    entry->maxlen    = sizeof(ramNvData.im3[instance]);
                    entry->actuallen = &entry->maxlen;
                }
                break;
            case PN_API_IOD_RemaDataIm4:
                if (instance < PN_API_IOD_MAX_NUM_OF_SUBSLOTS)
                {
                    entry->data      = (uint8_t *)&ramNvData.im4[instance];
                    entry->maxlen    = sizeof(ramNvData.im4[instance]);
                    entry->actuallen = &entry->maxlen;
                }
                break;
            case PN_API_IOD_RemaDataSnmpSysName:
                entry->data      = ramNvData.snmpSysName;
                entry->maxlen    = sizeof(ramNvData.snmpSysName);
                entry->actuallen = &ramNvData.snmpSysNameLen;
                break;
            case PN_API_IOD_RemaDataSnmpSysLoc:
                entry->data      = ramNvData.snmpSysLoc;
                entry->maxlen    = sizeof(ramNvData.snmpSysLoc);
                entry->actuallen = &ramNvData.snmpSysLocLen;
                break;
            case PN_API_IOD_RemaDataSnmpSysCont:
                entry->data      = ramNvData.snmpSysCont;
                entry->maxlen    = sizeof(ramNvData.snmpSysCont);
                entry->actuallen = &ramNvData.snmpSysContLen;
                break;
            case PN_API_IOD_RemaDataPdevRecord:
                entry->data      = ramNvData.pdev;
                entry->maxlen    = sizeof(ramNvData.pdev);
                entry->actuallen = &ramNvData.pdevLen;
                break;
            case PN_API_IOD_RemaDataArFsu:
                entry->data      = ramNvData.arfsu;
                entry->maxlen    = sizeof(ramNvData.arfsu);
                entry->actuallen = &ramNvData.arfsuLen;
                break;
            case PN_API_IOD_RemaDataSubmodCfg:
                entry->data      = (uint8_t *)&ramNvData.subCfgList[instance];
                entry->maxlen    = sizeof(ramNvData.subCfgList);
                entry->actuallen = &ramNvData.subCfgLen;
                break;
            default:
                // unknown data
                break;
        }
    
        if (entry->data == NULL || entry->actuallen == NULL)
        {
            status = PN_API_NOT_OK;
        }
        else if (*entry->actuallen == 0xFFFF)
        {
            // memory not initialized
            *entry->actuallen = 0;
        }
    
        return status;
    }
    
    
    /*!
     * \brief
     * Generate checksum for rema data
     *
     * \return         result of the operation as uint32_t.
     * \retval         #checksum        Rema data checksum.
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static uint32_t PN_APP_IOD_remaChecksum(void)
    {
        uint32_t const *data     = ((uint32_t *)&ramNvData) + 1; // ignore crc
        uint32_t        length   = sizeof(ramNvData) / sizeof(uint32_t) - 1;
        uint32_t        checksum = PN_APP_IOD_BSP_CHECKSUM_SEED;
    
        while (length--)
        {
            checksum ^= *data;
            checksum = (checksum << 1) | (checksum >> 31); // rotation
            data++;
        }
    
        return checksum;
    }
    
    /*!
     * \brief
     * Trigger storage of rema data
     *
     * \details
     * In this function the data is written in the NVram, the write is retriggered in case of
     * multiple store requests
     *
     * \return         result of the operation as uint32_t.
     * \retval         #PN_API_OK          Success.
     * \retval         #PN_API_NOT_OK      Something went wrong.
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static uint32_t PN_APP_IOD_remaTriggerStore(void)
    {
        uint32_t status = PN_API_OK;
        uint32_t nvmstatus;
        uint32_t checksum;
    
        checksum = PN_APP_IOD_remaChecksum();
    
        if(ramNvData.checkSum != checksum)
        {
            /* Update checksum*/
            ramNvData.checkSum = checksum;
    
            /* Trigger store */
            nvmstatus = NVM_APP_writeAsync(
            PN_APP_IOD_NVM_TYPE,
            PN_APP_IOD_NVM_INSTANCE,
            PN_APP_IOD_NVM_OFFSET,
            sizeof(ramNvData),
            (uint8_t *)&ramNvData);
    
            if (nvmstatus == NVM_ERR_BUSY)
            {
                //OSAL_printf("Multiple fast store requests detected.\r\n");
                remaContext.retriggerAfterComplete = true;
            }
            else if (nvmstatus != NVM_ERR_SUCCESS)
            {
                status = PN_API_NOT_OK;
            }
    
        }
        else
        {
            PN_API_IOD_dataStoreComplete(remaContext.handle, remaContext.bytesToWrite);
        }
        return status;
    }
    
    /*!
     * \brief
     * Callback function to store rema data
     *
     * \param[in]      status   Indicates the write task status
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    void PN_APP_IOD_remaNvmCallback(uint32_t status)
    {
        OSALUNREF_PARM(status);
    
        if (remaContext.handle)
        {
            PN_API_IOD_dataStoreComplete(remaContext.handle, remaContext.bytesToWrite);
        }
    
        if (remaContext.retriggerAfterComplete)
        {
            remaContext.retriggerAfterComplete = false;
            ramNvData.checkSum =~ ramNvData.checkSum;
            PN_APP_IOD_remaTriggerStore();
        }
    }
    
    uint32_t PN_APP_IOD_remaInit(void)
    {
        uint32_t nvmstatus;
        uint32_t status = PN_API_OK;
    
        memset(&remaContext, 0, sizeof(remaContext));
    
        nvmstatus = NVM_APP_init(OSAL_TASK_Prio_2);
    
        if (nvmstatus == NVM_ERR_SUCCESS)
        {
            nvmstatus = NVM_APP_read(
                PN_APP_IOD_NVM_TYPE,
                PN_APP_IOD_NVM_INSTANCE,
                PN_APP_IOD_NVM_OFFSET,
                sizeof(ramNvData),
                (uint8_t *)&ramNvData);
        }
    
        if (nvmstatus == NVM_ERR_SUCCESS)
        {
            nvmstatus = NVM_APP_registerCallback(PN_APP_IOD_remaNvmCallback);
        }
    
        if (nvmstatus != NVM_ERR_SUCCESS)
        {
            status = PN_API_NOT_OK;
        }
    //    else if (
    //        ramNvData.checkSum != PN_APP_IOD_remaChecksum()
    //        || ramNvData.version != PN_APP_IOD_NV_STRUCT_VERSION)
        {
            memset(&ramNvData, 0, sizeof(ramNvData));
    
            /* IMx data need to be initialized to white spaces not zeros */
            memset(&ramNvData.im1, ' ', (uint32_t)&ramNvData.snmpSysNameLen - (uint32_t)&ramNvData.im1);
    
            ramNvData.version = PN_APP_IOD_NV_STRUCT_VERSION;
            ramNvData.checkSum = PN_APP_IOD_remaChecksum();
    
            /* Trigger store */
            nvmstatus = NVM_APP_writeAsync(
                    PN_APP_IOD_NVM_TYPE,
                    PN_APP_IOD_NVM_INSTANCE,
                    PN_APP_IOD_NVM_OFFSET,
                    sizeof(ramNvData),
                    (uint8_t *)&ramNvData);
            if (nvmstatus != NVM_ERR_SUCCESS)
            {
                status = PN_API_NOT_OK;
                OSAL_printf("DEBUG: NVM is not cleared.\r\n");
            }
            else
            {
                status = PN_API_OK;
                OSAL_printf("DEBUG: NVM is cleared.\r\n");
            }
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_cbStoreRemaMem(
        PN_API_IOD_Handle_t *const pnHandle,
        PN_API_IOD_RemaDataType_t  type,
        const uint32_t             instance,
        const uint32_t             memSize,
        uint8_t *const             srcMem)
    {
        uint32_t           status = PN_API_OK;
        PN_APP_IOD_RemaEntry_t entry;
    
        OSAL_printf("Save remanent memory indicated (%u, %u).\r\n", type, instance);
    
        PN_APP_IOD_getRemaEntry(&entry, type, instance);
    
        if (srcMem == NULL || entry.actuallen == NULL || entry.maxlen < memSize)
        {
            // not enough space
            status = PN_API_NOT_OK;
        }
        else
        {
            memcpy(entry.data, srcMem, memSize);
            *entry.actuallen = (uint16_t)memSize;
    
            remaContext.handle       = pnHandle;
            remaContext.bytesToWrite = memSize;
            status                   = PN_APP_IOD_remaTriggerStore();
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_cbRestoreRemaMem(
        PN_API_IOD_Handle_t *const pnHandle,
        PN_API_IOD_RemaDataType_t  type,
        const uint32_t             instance,
        uint8_t **const            destMem,
        uint32_t *const            memSize)
    {
        uint32_t           status = PN_API_OK;
        PN_APP_IOD_RemaEntry_t entry;
    
        OSAL_printf("Restore remanent memory indicated.\r\n");
    
        PN_APP_IOD_getRemaEntry(&entry, type, instance);
    
        OSALUNREF_PARM(pnHandle);
    
        if (destMem == NULL || memSize == NULL || entry.actuallen == NULL || *entry.actuallen == 0)
        {
            // no data available
            status = PN_API_NOT_OK;
        }
        else
        {
            *destMem = entry.data;
            *memSize = *entry.actuallen;
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_factoryResetRemaMem(
        PN_API_IOD_Handle_t         *pnHandle,
        const PN_API_IOD_RtfOption_t rtfOption)
    {
        uint32_t status = PN_API_OK;
        OSAL_printf("Factory Reset memory indicated.\r\n");
    
        OSALUNREF_PARM(pnHandle);
    
        switch (rtfOption)
        {
            case PN_API_IOD_RtfResetAppParam:
            case PN_API_IOD_RtfResetEngParam:
            case PN_API_IOD_RtfResetFwUpgradeParam:
                // delegate to responsible modules
                break;
            case PN_API_IOD_RtfResetCommParam:
                // delete everything but Comm Param
                memset(&ramNvData, 0, (uint32_t)&ramNvData.im1 - (uint32_t)&ramNvData);
                memset(
                    &ramNvData.snmpSysNameLen,
                    0,
                    (uint32_t)(&ramNvData + 1) - (uint32_t)&ramNvData.snmpSysNameLen);
                ramNvData.version = PN_APP_IOD_NV_STRUCT_VERSION;
                PN_APP_IOD_remaTriggerStore();
                break;
            case PN_API_IOD_RtfResetAll:
            default:
                memset(&ramNvData, 0, sizeof(ramNvData));
    
                /* IMx data need to be reset to white spaces not zeros */
                memset(&ramNvData.im1, ' ', (uint32_t)&ramNvData.snmpSysNameLen - (uint32_t)&ramNvData.im1);
    
                ramNvData.version = PN_APP_IOD_NV_STRUCT_VERSION;
                PN_APP_IOD_remaTriggerStore();
                break;
        }
        return status;
    }
    
    uint32_t PN_APP_IOD_updateAppCycle(PN_API_IOD_Handle_t *pnHandle, uint32_t timeNs)
    {
        uint32_t      baseAddr = gTimerBaseAddr[CONFIG_TIMER1];
        TimerP_Params timerParams;
    
        OSALUNREF_PARM(pnHandle);
    
        TimerP_stop(baseAddr);
    
        TimerP_Params_init(&timerParams);
        timerParams.inputPreScaler    = CONFIG_TIMER1_INPUT_PRE_SCALER;
        timerParams.inputClkHz        = CONFIG_TIMER1_INPUT_CLK_HZ;
        timerParams.periodInNsec      = timeNs;
        timerParams.oneshotMode       = 0U;
        timerParams.enableOverflowInt = 1U;
        timerParams.enableDmaTrigger  = 0U;
        TimerP_setup(baseAddr, &timerParams);
        HwiP_clearInt(CONFIG_TIMER1_INT_NUM);
    
        TimerP_start(baseAddr);
    
        return PN_API_OK;
    }
    

  • Hello Kamil,

    that code doesn't build as-is, due to some missing defines:

    SYNC_OUT0
    GPIO_OUT_HW_SIGNAL_BASE_ADDR
    GPIO_OUT_HW_SIGNAL_PIN
    GPIO_OUT_HW_SIGNAL_DIR

    Does it make sense to try and fix those, or do you have another version of your changes that is closer to 09.02.00.15?

    Regards,

    Dominic

  • Hi Dominic,

    ah yeah. These defines belong to a newer version than what you have but they're all irrelevant, so any code related to them can be removed.
    I tried to do it on my side (new files are attached), but you can do it yourself as well.

    Thanks.
    Kind regards,
    Kamil

    /*!
     *  \file main.c
     *
     *  \brief
     *  Demo application
     *
     *  \author
     *  Texas Instruments Incorporated
     *
     *  \copyright
     *  Copyright (C) 2023 Texas Instruments Incorporated
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include <stdarg.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "drivers/pinmux.h"
    #include "hwal.h"
    #include "osal.h"
    #include "ti_board_open_close.h"
    #include "ti_drivers_open_close.h"
    
    #include "pn_api_iod_callbacks.h"
    #include "pn_api_iod_error.h"
    #include "pn_api_iod_handle.h"
    #include "pn_api_iod_startup.h"
    
    #include "pn_app_iod_alarm.h"
    #include "pn_app_iod_bsp.h"
    #include "pn_app_iod_communication.h"
    #include "pn_app_iod_data.h"
    #include "pn_app_iod_device.h"
    #include "pn_app_iod_mod_cfg.h"
    #include "pn_app_iod_record.h"
    #include "pn_app_iod_settings.h"
    #include "pn_app_iod_utils.h"
    
    #define PN_APP_MAIN_TASK_PRIO                   22 /* TASK_PRIO_MAIN */
    #define PN_APP_UART_TASK_PRIO                   2
    #define PN_APP_MAIN_TASK_STACK_SIZE             (2048+256)
    #define PN_APP_UART_TASK_STACK_SIZE             1024
    #define ASYNC_REC_RSP_WAITING_COUNTER           5000 /* Number of counts before sending a response */
    
    /*!
     * \ingroup PN_APP_IOD_TYPES_DOXY_GROUP
     */
    typedef struct
    {
        uint32_t asyncRecReadTimer;     /*!< For asynchronous record read responses */
        uint32_t asyncRecWriteTimer;    /*!< For asynchronous record write responses  */
    } PN_APP_IOD_asyncRecRspTimer_t;
    
    static char aOutStream_s[1024] = { 0 };
    static uint32_t uartWritePos = 0;
    static uint32_t uartReadPos = 0;
    void *uartSignal;
    void *uartMutex;
    static void* PN_APP_mainHandle;
    static void* PN_APP_uartTaskHandle;
    uint8_t PN_APP_mainTaskStack[PN_APP_MAIN_TASK_STACK_SIZE]__attribute__((aligned(32), section(".threadstack")));
    
    /* Handles of asynchronous record read/write request */
    extern void *asyncRecReadHandle;
    extern void *asyncRecWriteHandle;
    
    
    /*!
     * \brief
     * Callback function to handle system errors.
     *
     * \details
     * This function is called in response to a system error. It informs the user
     * of the error, and in case the error is fatal, it terminates the program.
     *
     * \param[in]       errorCode           Error code.
     * \param[in]       fatal               Fatal error indicator.
     * \param[in]       paramCnt            Number of parameters received for the error.
     * \param[in]       argptr              List of error parameters (arguments).
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    void PN_APP_IOD_errorHandlerCallback(uint32_t errorCode, bool fatal, uint8_t paramCnt, va_list argptr)
    {
        int32_t indexArg;
        uint32_t arg;
    
        OSAL_printf("\r\nError: 0x%8.8x, Fatal: %s", errorCode, fatal ? "yes" : "no");
        for (indexArg = 0; indexArg < paramCnt; indexArg++)
        {
            arg = va_arg(argptr, uint32_t);
            OSAL_printf(", P%d: 0x%8.8x", indexArg, arg);
        }
    
        if (fatal == true)
        {
            /* It's possible to introduce an infinite while loop here */
            exit(1);
        }
    }
    
    /*!
     * \brief
     * Initialize application data.
     *
     * \details
     * This function initializes all global variables and array that are required
     * for further operations. This includes, AR information, IO data arrays and
     * record lists and modes.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_init(void)
    {
        bool asyncRead = false;
        bool asyncWrite = false;
    
        /* Initialize AR information */
        PN_APP_IOD_initArInfo();
    
        /* Initialize application IO datasets */
        PN_APP_IOD_initAppData();
    
        /* Initialize user record list and information */
        PN_APP_IOD_initRecordInfo(asyncRead, asyncWrite);
    }
    
    /*!
     * \brief
     * Initialize device instance.
     *
     * \details
     * This function initializes the device instance that is later passed to Profinet stack.
     *
     * \param[in]       iodInst            Device instance.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_buildDevInstance(PN_API_IOD_IodInstance_t *iodInst)
    {
        /* Set IO-device instance info according to the user configuration found in pn_app_iod_cfg.h */
        iodInst->vendorId = PN_API_IOD_VENDOR_ID;
        iodInst->deviceId = PN_API_IOD_DEVICE_ID;
        iodInst->pDevType = (int8_t *)PN_API_IOD_DEV_TYPE;
        iodInst->maxNumOfSubslots = PN_API_IOD_MAX_NUM_OF_SUBSLOTS;
        iodInst->maxNumOfBytesPerSubslot = PN_API_IOD_NUM_OF_BYTES_PER_SUBSLOT;
    }
    
    /*!
     * \brief
     * Initialize device annotation.
     *
     * \details
     * This function initializes the device annotation that is later passed to Profinet stack.
     *
     * \param[in]       devAnnotation      Device annotation.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_buildDevAnnotation(
        PN_API_IOD_Handle_t *const pnHandle,
        PN_API_IOD_DevAnnotation_t *devAnnotation)
    {
    #if SOC_AM243X
        PN_API_IOD_setDeviceType(pnHandle, "AM243EVM");
    #elif SOC_AM64X
        PN_API_IOD_setDeviceType(pnHandle, "AM64EVM");
    #else
    #error Unsupported target type
    #endif
        PN_API_IOD_setDeviceOrderId(pnHandle, "TI-Sitara EVM");
        PN_API_IOD_setDeviceSerialNumber(pnHandle, "1234567890");
        PN_API_IOD_setDeviceHWRevision(pnHandle, 0x101C);
        PN_API_IOD_setDeviceVersionNumber(pnHandle, "V", 4, 1, 0, 0);
    
        /* Set IO-device annotation info according to the user configuration found in pn_app_iod_cfg.h */
        OSAL_MEMORY_memset(devAnnotation, ' ', sizeof(PN_API_IOD_DevAnnotation_t));
    
        OSAL_MEMORY_memcpy(
            &devAnnotation->deviceType,
            PN_API_IOD_DEV_TYPE,
            PN_API_IOD_DEV_TYPE_SIZE);
    
        OSAL_MEMORY_memcpy(
            &devAnnotation->orderId,
            PN_API_IOD_DEV_ANNOTATION_ORDER_ID,
            PN_API_IOD_DEV_ANNOTATION_ORDER_ID_SIZE);
    
        OSAL_MEMORY_memcpy(
            &devAnnotation->serialNum,
            PN_API_IOD_IM0_SERIAL_NUM,
            PN_API_IOD_IM0_SERIAL_NUM_SIZE);
    
        devAnnotation->hwRevision  = PN_API_IOD_HW_REV;
        devAnnotation->swRevisionPrefix = PN_API_IOD_VER_PREFIX;
        devAnnotation->swRevision1 = PN_API_IOD_VER_HH;
        devAnnotation->swRevision2 = PN_API_IOD_VER_H;
        devAnnotation->swRevision3 = PN_API_IOD_VER_L;
    }
    
    /*!
     * \brief
     * Build the list of callback functions.
     *
     * \details
     * This function assigns the functions implemented in the application to the
     * corresponding elements in the callback function list and register them.
     *
     * \param[in]      pnHandle            Profinet API Handle.
     *
     * \return         result of the operation as uint32_t
     * \retval         #PN_API_OK          Success.
     * \retval         #PN_API_NOT_OK      Something went wrong.
     * \retval         #PN_API_ERR_PARAM   Invalid parameter.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static uint32_t PN_APP_IOD_buildCallbacksList(PN_API_IOD_Handle_t *const pnHandle)
    {
        uint32_t status = PN_API_NOT_OK;
        if (NULL != pnHandle)
        {
            /* register app callback functions */
            PN_API_IOD_Callbacks_t callbacks = {
                .arConnect = PN_APP_IOD_cbArConnectInd,
                .arDisconn = PN_APP_IOD_cbArDisconn,
                .reportNewIpAddr = PN_APP_IOD_cbReportipAddr,
                .resetToFactory = PN_APP_IOD_cbResetToFactory,
                .storeRemaMem = PN_APP_IOD_cbStoreRemaMem,
                .restoreRemaMem = PN_APP_IOD_cbRestoreRemaMem,
                .freeRemaMem = PN_APP_IOD_cbFreeRemaMem,
                .setLed = PN_APP_IOD_cbSetLed,
                .stopLedBlink = PN_APP_IOD_cbStopLedBlink,
                .startLedBlink = PN_APP_IOD_cbStartLedBlink,
                .arOwnership = PN_APP_IOD_cbArOwnershipInd,
                .paramEnd = PN_APP_IOD_cbParamEnd,
                .readData = PN_APP_IOD_cbDataRead,
                .writeData = PN_APP_IOD_cbDataWrite,
                .recordRead = PN_APP_IOD_cbRecordRead,
                .arInData = PN_APP_IOD_cbArInData,
                .reportArFsuRecord = PN_APP_IOD_cbReportArFSURecord,
                .recordWrite = PN_APP_IOD_cbRecordWrite,
                .newModPull = PN_APP_IOD_cbNewModPull,
                .readIOxSDataOnly = PN_APP_IOD_cbDataReadIOxSOnly,
                .writeIOxSDataOnly = PN_APP_IOD_cbDataWriteIOxSOnly,
                .readyForInputUpdate = PN_APP_IOD_cbReadyForInputUpdate,
                .newModPlug = PN_APP_IOD_cbNewModPlug,
                .asyncReqDone = PN_APP_IOD_cbAsyncReqDone,
                .devAlarm = PN_APP_IOD_cbDevAlarm,
                .outSubmodSubstValRead = PN_APP_IOD_cbOutSubmodSubstValRead,
                .errorLog = PN_APP_IOD_cbErrorLog,
                .updateAppCycle = PN_APP_IOD_updateAppCycle
            };
    
            /* Register callback functions */
            status = PN_API_IOD_registerCallbacks(pnHandle, &callbacks);
        }
        return status;
    }
    
    /*!
     * \brief
     * Startup Profinet process.
     *
     * \details
     * This function calls for initialing all profinet-related data, creates a Profinet
     * device and starts up Profinet stack. It then runs infinitely trying to handle
     * asynchronous record read/write requests, if there's any.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_startup(void)
    {
        PN_API_IOD_IodInstance_t iodInst = { 0 };
        PN_API_IOD_DevAnnotation_t devAnnotation = { 0 };
        PN_API_IOD_SubmodListEntry_t *pIoSubmodList = NULL;
        PN_API_IOD_Im0ListEntry_t *pIm0List = NULL;
        uint32_t ioSubmodListSize = 0;
        uint32_t im0ListSize = 0;
        bool checkRemaData = true;
        uint32_t status = PN_API_OK;
        PN_API_IOD_Handle_t* pHandle = NULL;
        PN_APP_IOD_asyncRecRspTimer_t asyncRecRspTimer;
    
        /* Initialize asynchronous record response timers */
        asyncRecRspTimer.asyncRecReadTimer = 0;
        asyncRecRspTimer.asyncRecWriteTimer = 0;
    
        /* Initialize remanent storage */
        PN_APP_IOD_remaInit();
    
        /* Initialize LED handling */
        PN_APP_IOD_ledInit();
    
        /* Initialize PNIO user application */
        PN_APP_IOD_init();
    
        /* Initialize PNIO stack */
        PN_API_IOD_initStack();
    
        /* Build IO-device instance */
        PN_APP_IOD_buildDevInstance(&iodInst);
    
        /* Create a device handle */
        pHandle = PN_API_IOD_new();
    
        /* Build IO-device Annotation */
        PN_APP_IOD_buildDevAnnotation(pHandle, &devAnnotation);
        if(NULL != pHandle)
        {
            /* Load submodule configuration data */
     //       status = PN_APP_IOD_loadSubmodConfig(pHandle, &pIoSubmodList, &ioSubmodListSize,
     //                                         &pIm0List, &im0ListSize, checkRemaData);
            if (status == PN_API_OK)
            {
                /* Create callback functions instance and register it */
     //           status = PN_APP_IOD_buildCallbacksList(pHandle);
            }
    
            if (status == PN_API_OK)
            {
     //           status = PN_API_IOD_startup(pHandle, &iodInst, &devAnnotation, pIoSubmodList,
     //                                   ioSubmodListSize, pIm0List, im0ListSize);
            }
    
            if (status == PN_API_OK)
            {
                status = PN_APP_IOD_initCyclicDataExchange(pHandle);
            }
        }
    
        while(1)
        {
            OSAL_SCHED_sleep(1);
    
            /* An asynchronous read request has been registered? */
            if (NULL != asyncRecReadHandle)
            {
                /* Wait for a few seconds, then respond */
                asyncRecRspTimer.asyncRecReadTimer++;
                if (ASYNC_REC_RSP_WAITING_COUNTER == asyncRecRspTimer.asyncRecReadTimer)
                {
                    PN_APP_IOD_recordReadAsyncResponse();
                    asyncRecRspTimer.asyncRecReadTimer = 0;
                }
            }
    
            /* An asynchronous write request has been registered? */
            if (NULL != asyncRecWriteHandle)
            {
                /* Wait for a few seconds, then respond */
                asyncRecRspTimer.asyncRecWriteTimer++;
                if (ASYNC_REC_RSP_WAITING_COUNTER == asyncRecRspTimer.asyncRecWriteTimer)
                {
                    PN_APP_IOD_recordWriteAsyncResponse();
                    asyncRecRspTimer.asyncRecWriteTimer = 0;
                }
            }
        }
    }
    
    /*!
     * \brief
     * Callback function to print.
     *
     * \details
     * This function is called when the system needs to print out something. It
     * initializes a UART transaction and writes the received data to it.
     *
     * \param[in]       pContext            Context (not used).
     * \param[in]       pFormat             Format of the printed data.
     * \param[in]       arg                 List of parameters (arguments) to be printed.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    void PN_APP_IOD_printf(void *pContext, const char *__restrict pFormat, va_list arg)
    {
        int lengthWritten;
        char tmpString[256];
        char *tmpStringPos = tmpString;
    
        OSALUNREF_PARM(pContext);
    
        lengthWritten = vsnprintf(tmpString, sizeof(tmpString), pFormat, arg);
    
        OSAL_lockNamedMutex(uartMutex, OSAL_WAIT_INFINITE);
        while (lengthWritten > 0)
        {
            uint32_t lengthAvailable;
            uint32_t lengthWrite = lengthWritten;
    
            if (uartReadPos > uartWritePos)
            {
                lengthAvailable = uartReadPos - uartWritePos - 1;
            }
            else
            {
                lengthAvailable = sizeof(aOutStream_s) - uartWritePos;
                if (uartReadPos == 0)
                    lengthAvailable -= 1;
            }
    
            if (lengthAvailable == 0)
            {
                break;
            }
    
            if (lengthWrite > lengthAvailable)
            {
                lengthWrite = lengthAvailable;
            }
    
            memcpy(&aOutStream_s[uartWritePos], tmpStringPos, lengthWrite);
    
            uartWritePos += lengthWrite;
            tmpStringPos += lengthWrite;
            lengthWritten -= lengthWrite;
            if (uartWritePos >= sizeof(aOutStream_s))
            {
                uartWritePos -= sizeof(aOutStream_s);
            }
        }
        OSAL_unLockNamedMutex(uartMutex);
        OSAL_postSignal(uartSignal);
    }
    
    /*!
     * \brief
     * Start the system.
     *
     * \details
     * This function opens SoC and board drivers, then calls Profinet startup function.
     *
     * \param[in]       pvTaskArg           Arguments passed during task initialization.
     *
     * \ingroup PN_APP_IOD_MAIN_DOXY_GROUP
     *
     */
    static void PN_APP_IOD_mainTask(void *pvTaskArg)
    {
        uint32_t systemRetVal = SystemP_FAILURE;
    
        (void)pvTaskArg;
    
        /* Open SoC and board drivers. */
        Drivers_open();
        systemRetVal = Board_driversOpen();
        DebugP_assert(systemRetVal == SystemP_SUCCESS);
    
    
        PN_APP_IOD_startup();
    
        OSAL_SCHED_exitTask(NULL);
    }
    
    
    /*!
     * \brief
     *  uart print task
     *
     * \details
     * This function, is used to do printf on a low prio thread
     *
     */
    static void PN_APP_IOD_uartTask(void *pvTaskArg)
    {
        (void)pvTaskArg;
    
        while(1)
        {
            uint32_t bytesToWrite;
    
            static UART_Transaction transaction; // in Interrupt mode this needs to be static
    
            while (uartReadPos == uartWritePos)
            {
                OSAL_waitSignal(uartSignal, 2);
            }
    
            UART_flushTxFifo(gUartHandle[CONFIG_UART_CONSOLE]);
            UART_Transaction_init(&transaction);
    
            bytesToWrite = uartWritePos; // read uartWrite only once to prevent changes due to higher prio
            if (bytesToWrite > uartReadPos)
            {
                transaction.count = bytesToWrite - uartReadPos;
            }
            else
            {
                transaction.count = sizeof(aOutStream_s) - uartReadPos;
            }
    
            transaction.buf = (void *)&aOutStream_s[uartReadPos];
            transaction.args = NULL;
    
            OSAL_lockNamedMutex(uartMutex, OSAL_WAIT_INFINITE);
            uartReadPos += transaction.count;
            if (uartReadPos == sizeof(aOutStream_s))
            {
                uartReadPos = 0;
            }
            OSAL_unLockNamedMutex(uartMutex);
    
            (void)UART_write(gUartHandle[CONFIG_UART_CONSOLE], &transaction);
    
        }
    
        OSAL_SCHED_exitTask(NULL);
    }
    
    /*!
     * \brief
     *  Main entry point.
     *
     * \details
     * This function, initializes and configures SoC hardware and specific OSAL and HWAL modules,
     * which are essential for PROFINET communication. Afterwards, it creates and starts the main
     * application task.
     *
     * \return         result of the operation as uint32_t
     * \retval         #PN_API_OK          Success.
     * \retval         #PN_API_NOT_OK      Something went wrong.
     *
     */
    int main(void)
    {
        uint32_t status = PN_API_OK;
    
        /* Initialize SoC specific modules. */
        System_init();
    
        Board_init();
        /*
         * Both HWAL and OSAL use OSAL error handler for error reporting.
         * Therefore error handler should be registered prior the initialization.
         *
         * OSAL_init() must be called PRIOR to HWAL_init()
         */
        OSAL_registerErrorHandler(PN_APP_IOD_errorHandlerCallback);
    
        status = OSAL_init();
        if (PN_API_OK != status)
        {
            status = PN_API_NOT_OK;
        }
    
        status = HWAL_init();
        if (PN_API_OK != status)
        {
            status = PN_API_NOT_OK;
        }
    
        OSAL_registerPrintOut(NULL, PN_APP_IOD_printf);
    
        PN_APP_mainHandle = OSAL_SCHED_startTask(
            PN_APP_IOD_mainTask,
            NULL,
            PN_APP_MAIN_TASK_PRIO,
            PN_APP_mainTaskStack,
            sizeof(PN_APP_mainTaskStack),
            OSAL_OS_START_TASK_FLG_NONE,
            "app_main");
    
        if (NULL == PN_APP_mainHandle)
        {
            status = PN_API_NOT_OK;
        }
    
        uartSignal = OSAL_createSignal("uartSignal");
        uartMutex = OSAL_createNamedMutex("uartMutex");
        PN_APP_uartTaskHandle = OSAL_SCHED_startTask(
            PN_APP_IOD_uartTask,
            NULL,
            PN_APP_UART_TASK_PRIO,
            NULL,
            PN_APP_UART_TASK_STACK_SIZE,
            OSAL_OS_START_TASK_FLG_NONE,
            "uart_task");
    
        OSAL_startOs();
    
        return status;
    }
    
    /*!
     *  \file pn_app_iod_bsp.c
     *
     *  \brief
     *  Functions and callbacks for handling the board support package like memory and LED control.
     *
     *  \author
     *  Texas Instruments Incorporated
     *
     *  \copyright
     *  Copyright (C) 2023 Texas Instruments Incorporated
     *
     *  Redistribution and use in source and binary forms, with or without
     *  modification, are permitted provided that the following conditions
     *  are met:
     *
     *    Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     *    Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the
     *    distribution.
     *
     *    Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    #include "pn_app_iod_bsp.h"
    
    #include <string.h>
    #include <ti_board_open_close.h>
    /* Enable Sync jitter measurement */
    #include "drivers/pinmux.h"
    #include <drivers/hw_include/cslr_soc.h>
    /**/
    #include <nvm.h>
    
    #include "osal.h"
    
    #include "pn_api_iod_bsp.h"
    #include "pn_api_iod_error.h"
    
    /*!
     * \brief Profinet rema data entry
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    typedef struct PN_APP_IOD_RemaEntry
    {
        uint8_t  *data;        /*!< Rema data address */
        uint16_t  maxlen;      /*!< Max data length */
        uint16_t *actuallen;   /*!< Data legnth */
    } PN_APP_IOD_RemaEntry_t;
    
    /*!
     * \brief Profinet rema data context
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    typedef struct PN_APP_IOD_RemaContext
    {
        PN_API_IOD_Handle_t *handle;                    /*!< Device handle */
        uint32_t             bytesToWrite;              /*!< Bytes to write */
        bool                 retriggerAfterComplete;    /*!< Retrigger read/write; nvm status = busy */
    } PN_APP_IOD_RemaContext_t;
    
    /*!
     * \brief Profinet LED monitoring struct
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    typedef struct PN_APP_IOD_Led
    {
        uint32_t setState;                             /*!< New state: ON, OFF, BLINK*/
        bool     actualState;                          /*!< Current state */
    }PN_APP_IOD_Led_t;
    
    
    /*!
     * \brief Profinet LED context
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    typedef struct PN_APP_IOD_LedContext
    {
        void *threadHandle;                             /*!< Led Task handle */
        uint32_t state;                                 /*!< Current state */
        PN_APP_IOD_Led_t led[PN_API_IOD_LedMaxLeds];    /*!< Led list */
    }PN_APP_IOD_LedContext_t;
    
    /*!< Profinet application rema data context instance */
    PN_APP_IOD_RemaContext_t remaContext;
    
    /*!< Profinet application rema data instance */
    PN_APP_IOD_Nvdata_t ramNvData;
    
    /*!< Profinet application LED context instance */
    PN_APP_IOD_LedContext_t ledContext;
    
    /*
     * PAD configuration for Ball.D18
     * Required to configure SYNC0_OUT as pin out
     */
    static Pinmux_PerCfg_t gTsrPinMuxMainDomainCfg[] = {
        {
            PIN_ECAP0_IN_APWM_OUT,
            ( PIN_MODE(1) | PIN_PULL_DISABLE ) /* PIN_MODE 1 is SYNC0_OUT */
        },
        {PINMUX_END, PINMUX_END}
    };
    
    extern uint8_t inDataCounter;
    extern bool inDataChanged;
    
    /*!
     * \brief
     * Get LED instance
     *
     * \details
     * In this function, the application returns LED instance.
     *
     * \param[in]      led                 Led type
     *
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static void PN_APP_IOD_getLed(PN_API_IOD_Led_t led, uint32_t *instance, uint32_t *index)
    {
        *instance = CONFIG_LED_NUM_INSTANCES;
        *index    = 0;
    
        switch (led)
        {
            case PN_API_IOD_LedBlink:
                if (ledContext.led[PN_API_IOD_LedBlink].setState != PN_APP_IOD_LED_OFF)
                {
                    // overwrite run led
                    *instance = CONFIG_DCP_SIGNAL_LED;
                    *index    = 0;
                }
                break;
            case PN_API_IOD_LedRun:
                if (ledContext.led[PN_API_IOD_LedBlink].setState == PN_APP_IOD_LED_OFF)
                {
                    // ignore in case of blinking enabled
                    *instance = CONFIG_DCP_SIGNAL_LED;
                    *index    = 0;
                }
                break;
            case PN_API_IOD_LedMaint:
                break;
            case PN_API_IOD_LedError:
                *instance = CONFIG_SYS_FAILURE_LED;
                *index    = 0;
                break;
            case PN_API_IOD_LedUser00:
            case PN_API_IOD_LedUser01:
            case PN_API_IOD_LedUser02:
            case PN_API_IOD_LedUser03:
            case PN_API_IOD_LedUser04:
            case PN_API_IOD_LedUser05:
            case PN_API_IOD_LedUser06:
            case PN_API_IOD_LedUser07:
                *instance = CONFIG_CYCLIC_LEDS;
                *index    = led - PN_API_IOD_LedUser00;
                break;
            case PN_API_IOD_LedSync:
            default:
                // undefined led
                break;
        }
    }
    
    /*!
     * \brief
     * Update LED state.
     *
     * \details
     * In this function, the application updates LED states.
     *
     * \param[in]      led                 Led type
     *
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static void PN_APP_IOD_updateLed(PN_API_IOD_Led_t led)
    {
        uint32_t instance;
        uint32_t index;
    
        PN_APP_IOD_getLed(led, &instance, &index);
    
        if (instance != CONFIG_LED_NUM_INSTANCES)
        {
            bool ledEnable = false;
    
            if (ledContext.led[led].setState & ledContext.state)
            {
                // check if led is enabled in this state
                ledEnable = true;
            }
    
            if (ledContext.led[led].actualState != ledEnable)
            {
                // switch only if state has changed
                if (ledEnable)
                {
                    LED_on(gLedHandle[instance], index);
                }
                else
                {
                    LED_off(gLedHandle[instance], index);
                }
                ledContext.led[led].actualState = ledEnable;
            }
        }
    }
    
    
    /*!
     * \brief
     * Led task.
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    void PN_APP_IOD_ledTask(void *arg)
    {
        OSALUNREF_PARM(arg);
    
        while(1)
        {
            uint32_t led;
            OSAL_SCHED_sleep(PN_APP_IOD_LED_TASK_INTERVAL_MS/PN_APP_IOD_LED_TICKS);
    
            // increment current led global state
            ledContext.state <<= 1;
            if (ledContext.state >= (1<<PN_APP_IOD_LED_TICKS) || ledContext.state == 0)
            {
                ledContext.state = 1;
            }
    
            for (led = 0; led < PN_API_IOD_LedMaxLeds; led++)
            {
                PN_APP_IOD_updateLed(led);
            }
        }
    }
    
    uint32_t PN_APP_IOD_ledInit(void)
    {
        uint32_t status = PN_API_OK;
    
        memset(&ledContext, 0, sizeof(ledContext));
    
        ledContext.threadHandle = OSAL_SCHED_startTask(
            PN_APP_IOD_ledTask,
            &ledContext,
            PN_APP_IOD_LED_TASK_PRIO,
            NULL,
            PN_APP_IOD_LED_TASK_STACK,
            0,
            "LED Task");
    
        if (ledContext.threadHandle == NULL)
        {
            status = PN_API_NOT_OK;
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_cbSetLed(
        PN_API_IOD_Handle_t *const pnHandle,
        const PN_API_IOD_Led_t     led,
        const bool                 state)
    {
        // OSAL_printf("Set Led request received on Led %u to %u\r\n", led, state);
        OSALUNREF_PARM(pnHandle);
    
        if (led < PN_API_IOD_LedMaxLeds)
        {
            if (state)
            {
                ledContext.led[led].setState = PN_APP_IOD_LED_ON;
            }
            else
            {
                ledContext.led[led].setState = PN_APP_IOD_LED_OFF;
            }
        }
    
        return PN_API_OK;
    }
    
    uint32_t PN_APP_IOD_cbStartLedBlink(
        PN_API_IOD_Handle_t *const pnHandle,
        const uint32_t             portNum,
        const uint32_t             frequency)
    {
        OSAL_printf("DCP signal request received on Port %u\r\n", portNum);
        OSALUNREF_PARM(pnHandle);
        OSALUNREF_PARM(portNum);
        OSALUNREF_PARM(frequency);
    
        ledContext.led[PN_API_IOD_LedBlink].setState = PN_APP_IOD_LED_BLINK;
        ledContext.led[PN_API_IOD_LedBlink].actualState = ledContext.led[PN_API_IOD_LedRun].actualState;
    
        return PN_API_OK;
    }
    
    uint32_t PN_APP_IOD_cbStopLedBlink(PN_API_IOD_Handle_t *const pnHandle, const uint32_t portNum)
    {
        OSAL_printf("DCP signal request stop received.\r\n");
        OSALUNREF_PARM(pnHandle);
        OSALUNREF_PARM(portNum);
    
        ledContext.led[PN_API_IOD_LedBlink].setState = PN_APP_IOD_LED_OFF;
        ledContext.led[PN_API_IOD_LedRun].actualState = ledContext.led[PN_API_IOD_LedBlink].actualState;
    
        return PN_API_OK;
    }
    
    
    uint32_t PN_APP_IOD_cbFreeRemaMem(PN_API_IOD_Handle_t *const pnHandle, uint8_t *const destMem)
    {
        uint32_t status = PN_API_OK;
    
        OSALUNREF_PARM(pnHandle);
        OSALUNREF_PARM(destMem);
    
        return status;
    }
    
    /*!
     * \brief
     * Read device rema data entries
     *
     * \return         result of the operation as uint32_t.
     * \retval         #PN_API_OK            Success.
     * \retval         #PN_API_NOT_OK        Something went wrong.
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static uint32_t PN_APP_IOD_getRemaEntry(
        PN_APP_IOD_RemaEntry_t       *entry,
        PN_API_IOD_RemaDataType_t type,
        const uint32_t            instance)
    {
        uint32_t status = PN_API_OK;
        memset(entry, 0, sizeof(*entry));
    
        switch (type)
        {
            case PN_API_IOD_RemaDataDevName:
                entry->data      = ramNvData.devName;
                entry->maxlen    = sizeof(ramNvData.devName);
                entry->actuallen = &ramNvData.devNameLen;
                break;
            case PN_API_IOD_RemaDataIpSuite:
                entry->data      = (uint8_t *)&ramNvData.ipSuite;
                entry->maxlen    = sizeof(ramNvData.ipSuite);
                entry->actuallen = &ramNvData.ipSuiteLen;
                break;
            case PN_API_IOD_RemaDataIm1:
                if (instance < PN_API_IOD_MAX_NUM_OF_SUBSLOTS)
                {
                    entry->data      = (uint8_t *)&ramNvData.im1[instance];
                    entry->maxlen    = sizeof(ramNvData.im1[instance]);
                    entry->actuallen = &entry->maxlen;
                }
                break;
            case PN_API_IOD_RemaDataIm2:
                if (instance < PN_API_IOD_MAX_NUM_OF_SUBSLOTS)
                {
                    entry->data      = (uint8_t *)&ramNvData.im2[instance];
                    entry->maxlen    = sizeof(ramNvData.im2[instance]);
                    entry->actuallen = &entry->maxlen;
                }
                break;
            case PN_API_IOD_RemaDataIm3:
                if (instance < PN_API_IOD_MAX_NUM_OF_SUBSLOTS)
                {
                    entry->data      = (uint8_t *)&ramNvData.im3[instance];
                    entry->maxlen    = sizeof(ramNvData.im3[instance]);
                    entry->actuallen = &entry->maxlen;
                }
                break;
            case PN_API_IOD_RemaDataIm4:
                if (instance < PN_API_IOD_MAX_NUM_OF_SUBSLOTS)
                {
                    entry->data      = (uint8_t *)&ramNvData.im4[instance];
                    entry->maxlen    = sizeof(ramNvData.im4[instance]);
                    entry->actuallen = &entry->maxlen;
                }
                break;
            case PN_API_IOD_RemaDataSnmpSysName:
                entry->data      = ramNvData.snmpSysName;
                entry->maxlen    = sizeof(ramNvData.snmpSysName);
                entry->actuallen = &ramNvData.snmpSysNameLen;
                break;
            case PN_API_IOD_RemaDataSnmpSysLoc:
                entry->data      = ramNvData.snmpSysLoc;
                entry->maxlen    = sizeof(ramNvData.snmpSysLoc);
                entry->actuallen = &ramNvData.snmpSysLocLen;
                break;
            case PN_API_IOD_RemaDataSnmpSysCont:
                entry->data      = ramNvData.snmpSysCont;
                entry->maxlen    = sizeof(ramNvData.snmpSysCont);
                entry->actuallen = &ramNvData.snmpSysContLen;
                break;
            case PN_API_IOD_RemaDataPdevRecord:
                entry->data      = ramNvData.pdev;
                entry->maxlen    = sizeof(ramNvData.pdev);
                entry->actuallen = &ramNvData.pdevLen;
                break;
            case PN_API_IOD_RemaDataArFsu:
                entry->data      = ramNvData.arfsu;
                entry->maxlen    = sizeof(ramNvData.arfsu);
                entry->actuallen = &ramNvData.arfsuLen;
                break;
            case PN_API_IOD_RemaDataSubmodCfg:
                entry->data      = (uint8_t *)&ramNvData.subCfgList[instance];
                entry->maxlen    = sizeof(ramNvData.subCfgList);
                entry->actuallen = &ramNvData.subCfgLen;
                break;
            default:
                // unknown data
                break;
        }
    
        if (entry->data == NULL || entry->actuallen == NULL)
        {
            status = PN_API_NOT_OK;
        }
        else if (*entry->actuallen == 0xFFFF)
        {
            // memory not initialized
            *entry->actuallen = 0;
        }
    
        return status;
    }
    
    
    /*!
     * \brief
     * Generate checksum for rema data
     *
     * \return         result of the operation as uint32_t.
     * \retval         #checksum        Rema data checksum.
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static uint32_t PN_APP_IOD_remaChecksum(void)
    {
        uint32_t const *data     = ((uint32_t *)&ramNvData) + 1; // ignore crc
        uint32_t        length   = sizeof(ramNvData) / sizeof(uint32_t) - 1;
        uint32_t        checksum = PN_APP_IOD_BSP_CHECKSUM_SEED;
    
        while (length--)
        {
            checksum ^= *data;
            checksum = (checksum << 1) | (checksum >> 31); // rotation
            data++;
        }
    
        return checksum;
    }
    
    /*!
     * \brief
     * Trigger storage of rema data
     *
     * \details
     * In this function the data is written in the NVram, the write is retriggered in case of
     * multiple store requests
     *
     * \return         result of the operation as uint32_t.
     * \retval         #PN_API_OK          Success.
     * \retval         #PN_API_NOT_OK      Something went wrong.
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    static uint32_t PN_APP_IOD_remaTriggerStore(void)
    {
        uint32_t status = PN_API_OK;
        uint32_t nvmstatus;
        uint32_t checksum;
    
        checksum = PN_APP_IOD_remaChecksum();
    
        if(ramNvData.checkSum != checksum)
        {
            /* Update checksum*/
            ramNvData.checkSum = checksum;
    
            /* Trigger store */
            nvmstatus = NVM_APP_writeAsync(
            PN_APP_IOD_NVM_TYPE,
            PN_APP_IOD_NVM_INSTANCE,
            PN_APP_IOD_NVM_OFFSET,
            sizeof(ramNvData),
            (uint8_t *)&ramNvData);
    
            if (nvmstatus == NVM_ERR_BUSY)
            {
                //OSAL_printf("Multiple fast store requests detected.\r\n");
                remaContext.retriggerAfterComplete = true;
            }
            else if (nvmstatus != NVM_ERR_SUCCESS)
            {
                status = PN_API_NOT_OK;
            }
    
        }
        else
        {
            PN_API_IOD_dataStoreComplete(remaContext.handle, remaContext.bytesToWrite);
        }
        return status;
    }
    
    /*!
     * \brief
     * Callback function to store rema data
     *
     * \param[in]      status   Indicates the write task status
     *
     * \ingroup PN_APP_IOD_BSP_DOXY_GROUP
     */
    void PN_APP_IOD_remaNvmCallback(uint32_t status)
    {
        OSALUNREF_PARM(status);
    
        if (remaContext.handle)
        {
            PN_API_IOD_dataStoreComplete(remaContext.handle, remaContext.bytesToWrite);
        }
    
        if (remaContext.retriggerAfterComplete)
        {
            remaContext.retriggerAfterComplete = false;
            ramNvData.checkSum =~ ramNvData.checkSum;
            PN_APP_IOD_remaTriggerStore();
        }
    }
    
    uint32_t PN_APP_IOD_remaInit(void)
    {
        uint32_t nvmstatus;
        uint32_t status = PN_API_OK;
    
        memset(&remaContext, 0, sizeof(remaContext));
    
        nvmstatus = NVM_APP_init(OSAL_TASK_Prio_2);
    
        if (nvmstatus == NVM_ERR_SUCCESS)
        {
            nvmstatus = NVM_APP_read(
                PN_APP_IOD_NVM_TYPE,
                PN_APP_IOD_NVM_INSTANCE,
                PN_APP_IOD_NVM_OFFSET,
                sizeof(ramNvData),
                (uint8_t *)&ramNvData);
        }
    
        if (nvmstatus == NVM_ERR_SUCCESS)
        {
            nvmstatus = NVM_APP_registerCallback(PN_APP_IOD_remaNvmCallback);
        }
    
        if (nvmstatus != NVM_ERR_SUCCESS)
        {
            status = PN_API_NOT_OK;
        }
    //    else if (
    //        ramNvData.checkSum != PN_APP_IOD_remaChecksum()
    //        || ramNvData.version != PN_APP_IOD_NV_STRUCT_VERSION)
        {
            memset(&ramNvData, 0, sizeof(ramNvData));
    
            /* IMx data need to be initialized to white spaces not zeros */
            memset(&ramNvData.im1, ' ', (uint32_t)&ramNvData.snmpSysNameLen - (uint32_t)&ramNvData.im1);
    
            ramNvData.version = PN_APP_IOD_NV_STRUCT_VERSION;
            ramNvData.checkSum = PN_APP_IOD_remaChecksum();
    
            /* Trigger store */
            nvmstatus = NVM_APP_writeAsync(
                    PN_APP_IOD_NVM_TYPE,
                    PN_APP_IOD_NVM_INSTANCE,
                    PN_APP_IOD_NVM_OFFSET,
                    sizeof(ramNvData),
                    (uint8_t *)&ramNvData);
            if (nvmstatus != NVM_ERR_SUCCESS)
            {
                status = PN_API_NOT_OK;
                OSAL_printf("DEBUG: NVM is not cleared.\r\n");
            }
            else
            {
                status = PN_API_OK;
                OSAL_printf("DEBUG: NVM is cleared.\r\n");
            }
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_cbStoreRemaMem(
        PN_API_IOD_Handle_t *const pnHandle,
        PN_API_IOD_RemaDataType_t  type,
        const uint32_t             instance,
        const uint32_t             memSize,
        uint8_t *const             srcMem)
    {
        uint32_t           status = PN_API_OK;
        PN_APP_IOD_RemaEntry_t entry;
    
        OSAL_printf("Save remanent memory indicated (%u, %u).\r\n", type, instance);
    
        PN_APP_IOD_getRemaEntry(&entry, type, instance);
    
        if (srcMem == NULL || entry.actuallen == NULL || entry.maxlen < memSize)
        {
            // not enough space
            status = PN_API_NOT_OK;
        }
        else
        {
            memcpy(entry.data, srcMem, memSize);
            *entry.actuallen = (uint16_t)memSize;
    
            remaContext.handle       = pnHandle;
            remaContext.bytesToWrite = memSize;
            status                   = PN_APP_IOD_remaTriggerStore();
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_cbRestoreRemaMem(
        PN_API_IOD_Handle_t *const pnHandle,
        PN_API_IOD_RemaDataType_t  type,
        const uint32_t             instance,
        uint8_t **const            destMem,
        uint32_t *const            memSize)
    {
        uint32_t           status = PN_API_OK;
        PN_APP_IOD_RemaEntry_t entry;
    
        OSAL_printf("Restore remanent memory indicated.\r\n");
    
        PN_APP_IOD_getRemaEntry(&entry, type, instance);
    
        OSALUNREF_PARM(pnHandle);
    
        if (destMem == NULL || memSize == NULL || entry.actuallen == NULL || *entry.actuallen == 0)
        {
            // no data available
            status = PN_API_NOT_OK;
        }
        else
        {
            *destMem = entry.data;
            *memSize = *entry.actuallen;
        }
    
        return status;
    }
    
    uint32_t PN_APP_IOD_factoryResetRemaMem(
        PN_API_IOD_Handle_t         *pnHandle,
        const PN_API_IOD_RtfOption_t rtfOption)
    {
        uint32_t status = PN_API_OK;
        OSAL_printf("Factory Reset memory indicated.\r\n");
    
        OSALUNREF_PARM(pnHandle);
    
        switch (rtfOption)
        {
            case PN_API_IOD_RtfResetAppParam:
            case PN_API_IOD_RtfResetEngParam:
            case PN_API_IOD_RtfResetFwUpgradeParam:
                // delegate to responsible modules
                break;
            case PN_API_IOD_RtfResetCommParam:
                // delete everything but Comm Param
                memset(&ramNvData, 0, (uint32_t)&ramNvData.im1 - (uint32_t)&ramNvData);
                memset(
                    &ramNvData.snmpSysNameLen,
                    0,
                    (uint32_t)(&ramNvData + 1) - (uint32_t)&ramNvData.snmpSysNameLen);
                ramNvData.version = PN_APP_IOD_NV_STRUCT_VERSION;
                PN_APP_IOD_remaTriggerStore();
                break;
            case PN_API_IOD_RtfResetAll:
            default:
                memset(&ramNvData, 0, sizeof(ramNvData));
    
                /* IMx data need to be reset to white spaces not zeros */
                memset(&ramNvData.im1, ' ', (uint32_t)&ramNvData.snmpSysNameLen - (uint32_t)&ramNvData.im1);
    
                ramNvData.version = PN_APP_IOD_NV_STRUCT_VERSION;
                PN_APP_IOD_remaTriggerStore();
                break;
        }
        return status;
    }
    
    uint32_t PN_APP_IOD_updateAppCycle(PN_API_IOD_Handle_t *pnHandle, uint32_t timeNs)
    {
        uint32_t      baseAddr = gTimerBaseAddr[CONFIG_TIMER1];
        TimerP_Params timerParams;
    
        OSALUNREF_PARM(pnHandle);
    
        TimerP_stop(baseAddr);
    
        TimerP_Params_init(&timerParams);
        timerParams.inputPreScaler    = CONFIG_TIMER1_INPUT_PRE_SCALER;
        timerParams.inputClkHz        = CONFIG_TIMER1_INPUT_CLK_HZ;
        timerParams.periodInNsec      = timeNs;
        timerParams.oneshotMode       = 0U;
        timerParams.enableOverflowInt = 1U;
        timerParams.enableDmaTrigger  = 0U;
        TimerP_setup(baseAddr, &timerParams);
        HwiP_clearInt(CONFIG_TIMER1_INT_NUM);
    
        TimerP_start(baseAddr);
    
        return PN_API_OK;
    }
    

  • Hello Kamil,

    I've commented out all the references to the missing #defines.

    The application built and ran with the following output:

    DEBUG: NVM is cleared.

    Afterwards I ran the application you shared before:

    4. For testing purposes I've shared a .out file for AM243x_EVM. Would you please try to load it on the failing board and tell me the outcome?

    The output was the following:

    Restore remanent memory indicated.
    Using user-defined submodule configuration (not from remanent memory).
    Restore remanent memory indicated.
    Restore remanent memory indicated.
    Restore remanent memory indicated.
    Restore remanent memory indicated.
    Restore remanent memory indicated.
    [APP] INFO: Initializing PRU instance ...
                                             DP83869 detected
    DP83869 detected
    Phy 15 : Disable RGMII mode
    Phy 15 : Disable GBit ANEG
    Phy 3 : Disable RGMII mode
    Phy 3 : Disable GBit ANEG

    Afterwards I had the same problem as before (stuck in PNIO_device_open polling pnpb_get_state).

    Regards,

    Dominic

  • Hello Dominic,

    please check your email. Another binary has been sent there for testing.

    Thanks.
    Kind regards,
    Kamil

  • Hi Dominic,

    thanks for your cooperation on debugging the issues over email. The first problem ("The software fails on 1 out of 3 AM24x EVMs") was caused by an invalid MAC address assigned to the failing boards causing the stack not to proceed with startup. This problem is resolved by fixing the MAC address.

    Now your update on the second issue ("The software fails on the 3rd party AM64x SoM") is required.

    Thanks.
    Kind regards,
    Kamil

  • Hello Kamil,

    maybe some additional infos for future readers:

    The profinet example application reads the MAC address from the Board ID EEPROM on the EVM. On the non-working board, the Board ID EEPROM had a MAC address with all zeroes, but was otherwise "ok", e.g. it had a valid magic number in the header, and it included the board name AM243-EVM. It is not clear why this particular board didn't include a valid MAC address in the Board ID EEPROM, e.g. if it came that way, or if it was erased at some point later.

    For we've fixed that by manually initializing the 'mac' field of APP_HW_BOARD_INFO_data_g instead of reading that structure from the EEPROM.

    On the 3rd party AM64x SoM we had the same issue, and after fixing some other minor issue on our end the SoM is now detected as a Profinet device, too.

    Regards,

    Dominic

  • Hello Dominic,

    thanks for providing more details for future readers. Have a nice holiday!

    Kind regards,
    Kamil