007 - mCTC Debugging and Trouble Shooting

Introduction

Some general question regarding the product are covered in the Frequently Asked Questions section: https://portgmbh.atlassian.net/wiki/spaces/IRJ45SOM/pages/131629572 . In this document methods for debugging and checking proper functionality of the communication between application and communication module shall be shown.

Communication

Basics

Following table shows the basic structure of the SPI frame.

Range

Length

Description

Range

Length

Description

0 .. 3

4 byte

MCTC SPI header

4 .. 76

73 byte

cyclic data (data mapper)

77 .. 126

50 byte

RPC data

127

1 byte

reserved

The block MCTC SPI Header contains the following elements:

1 2 3 4 5 6 /**< SPI cyclic data header */ typedef struct { uint16_t crc_le16; /**< checksum */ uint8_t seq; /**< sequence number */ uint8_t lenData; /**< data length */ } GOAL_MI_MCTC_SPI_CYCLIC_T;

The field crc_le16 contains a checksum ( Fletcher-16, Wikipedia ) with a starting value of 0x0007.

The field seq contains a sequence counter which increases with each updated frame.

The field lenData contains the length of valid data after the MCTC header (e.g. 124).

Is the application controller receiving valid frames?

If incoming frames from the communication module can be checked by content, following hints should determine wether valid frames are received:

  • The first 2 bytes (crc) contains a value != 0

  • The 3rd byte changes with each frame (cyclic counter)

  • The 4th byte (length) has a value != 0

Programmatically, valid frames are detected by checking the CRC field in the header.

For uGOAL this is determined within the SAPI module:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 /****************************************************************************/ /** Basic cyclic communication function * * This function initiates SPI transfer and dispatches received data to * upper layers data callback and RPC handling. * */ void sapi_loop( void ) { /* update frame header */ sapi_pdUpdateHeader(sendData, GOAL_MI_MCTC_LEN - sizeof(GOAL_MI_MCTC_SPI_CYCLIC_T)); /* transfer data via SPI */ plat_spiTransfer((const char *) &sendData[0], (char *) receiveData, BUF_LENGTH); /* control LED if Connection valid or Wink active */ if (true == sapi_pdCheckHeader(receiveData, GOAL_MI_MCTC_LEN - sizeof(GOAL_MI_MCTC_SPI_CYCLIC_T))) { /* inform application */ som_dataCb(&receiveData[0], &sendData[0]); /* trigger RPC */ goal_miMctcDmCbRead(); } }

If line 22 within the function api_loop is executed, valid frames from the communication module are received.

For GOAL based applications this can be determined in the function goal_miMctcSpiProcessTask within the goal_mi_mctc_spi.c module:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 do { /* check frame CRC */ res = goal_miMctcCrc(&pBufSpi[sizeof(GOAL_MI_MCTC_SPI_CYCLIC_T)], pHdr->lenData, GOAL_le16toh_p(&pHdr->crc_le16)); if (GOAL_RES_ERR(res)) { break; } /* check sequence counter */ if (pMiMctcSpi->seqCyclicRem != pHdr->seq) { /* inform MI MCTC that connection is running */ goal_miMctcMonitorRx(pMiMctc, GOAL_TRUE); /* re-arm watchdog */ pMiMctcSpi->tsTout = tsNow + pMiMctc->cfgToutValue; /* update seen sequence */ pMiMctcSpi->seqCyclicRem = pHdr->seq; } if (pHdr->lenData) { /* process MI DM incoming data */ goal_miDmReadSync(pMiMctcSpi->pMiDmRead, &pBufSpi[sizeof(GOAL_MI_MCTC_SPI_CYCLIC_T)]); } } while (0);

If the checks shown in the code are performed successfully a valid frame was received.

Manually this could be checked by evaluating the crc from the SPI frame and calculating the expected value over the given data with the given length from the header. The header itself is skipped from crc calculation.

Is the communication module receiving the frames from the application controller?

The communication module will provide valid mCTC frames even if the application controller does not. Thus it is not that easy to determine, if the frames sent by the application controller are evaluated by the communication module.

A good measurement whether the communication module receives the frames from the application controller is the capability to perform the RPC synchronisation. This is executed initially with beginning of the communication.

The RPC synchronisation follows a state machine with the following states:

State

Description

 

State

Description

 

GOAL_MI_MCTC_RPC_STATE_INIT

Initial state

Entered initially or in error case

GOAL_MI_MCTC_RPC_STATE_SYNC_REQ

Initial request state

Entered after INIT state

GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_LOCAL

Initial acknowledge state

Entered after SYNC REQ state

GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_REMOTE

Initial sync request was acknowledged

Entered if SYNC request was acknowledged, the peer module received our request!

GOAL_MI_MCTC_RPC_STATE_RUN_ONCE

Setup state after Sync

Entered when SYNC was successful

GOAL_MI_MCTC_RPC_STATE_RUN

Normal operating state

Entered after SYNC is done

So if the state GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_REMOTE is entered, the communication module received the frames sent by the application controller.

Following code shows the state machine in exceprts:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 /****************************************************************************/ /** MCTC MI - RPC Sync Loop */ void goal_miMctcRpcSyncLoop( MCTC_INST_T *pMiMctc /**< MI MCTC handle */ ) { GOAL_STATUS_T res = GOAL_OK; /* result */ GOAL_BOOL_T flgSetupLocal = GOAL_FALSE; /* local setup done flag */ GOAL_BOOL_T flgSetupRemote = GOAL_FALSE; /* remote setup done flag */ GOAL_MCTC_T *pHdlMctcTx = NULL; /* call handle */ /* leave if not in sync-mode */ if ((GOAL_MI_MCTC_RPC_STATE_RUN == pMiMctc->stateRpc) || (GOAL_MI_MCTC_RPC_STATE_STOP == pMiMctc->stateRpc)) { return; } /* check if send is permitted */ if (GOAL_TRUE != goal_miMctcRpcSendAllow(pMiMctc)) { return; } /* handle state machine */ switch (pMiMctc->stateRpc) { case GOAL_MI_MCTC_RPC_STATE_RUN_ONCE: /* .... */ break; /* initialize synchronisation */ case GOAL_MI_MCTC_RPC_STATE_SYNC_INIT: goal_logDbg("state: SYNC_INIT"); /* reset sync state */ GOAL_MASK_SET(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_SYNC_REQ); GOAL_MASK_CLR(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_SYNC_ACK); pMiMctc->seqRpcLocal = 0; pMiMctc->seqRpcLocalAck = 0; pMiMctc->seqRpcRemoteAck = 0; pMiMctc->stateRpc = GOAL_MI_MCTC_RPC_STATE_SYNC_REQ; /* intended fallthrough */ GOAL_TARGET_FALLTHROUGH; /* request synchronisation from partner */ case GOAL_MI_MCTC_RPC_STATE_SYNC_REQ: goal_logDbg("state: SYNC_REQ"); /* send RPC sync frame until ACK is received */ if (!(GOAL_MI_MCTC_RPC_FLG_SYNC_ACK & pMiMctc->flgRpcRemote)) { /* send sync request frame */ goal_miMctcRpcSend(pMiMctc, NULL, 0); break; } /* request acknowledge of first sequence */ GOAL_MASK_CLR(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_SYNC_REQ); GOAL_MASK_SET(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_REQ_ACK); pMiMctc->seqRpcLocal = 1; pMiMctc->stateRpc = GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_LOCAL; /* intended fallthrough */ GOAL_TARGET_FALLTHROUGH; case GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_LOCAL: goal_logDbg("state: SYNC_ACK_LOCAL"); /* send empty sequence 1 until ACK is received and we confirmed remote sequence */ if (1 != pMiMctc->seqRpcLocalAck) { /* send empty RPC request */ goal_miMctcRpcSend(pMiMctc, NULL, 0); break; } /* wait for remote sync */ pMiMctc->stateRpc = GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_REMOTE; break; case GOAL_MI_MCTC_RPC_STATE_SYNC_ACK_REMOTE: goal_logDbg("state: SYNC_ACK_REMOTE"); if (0 == pMiMctc->seqRpcRemoteAck) { /* send empty RPC request */ goal_miMctcRpcSend(pMiMctc, NULL, 0); break; } /* enter run mode */ GOAL_MASK_CLR(pMiMctc->flgRpc, GOAL_MI_MCTC_RPC_FLG_REQ_ACK); pMiMctc->stateRpc = GOAL_MI_MCTC_RPC_STATE_RUN_ONCE; break; } }

Debugging

By default both the basic SPI communication and the RPC communication are monitored and in case of loss of communication or a missing response an error is detected and propagated. This will stop the system from working any further. This can be disabled for debugging purpose. However this should never be done in a productive system, since this will mask potential communication problems.

 

For uGOAL application, the following defines need to be set in goal_config.h:

1 2 3 /* for debugging purpose disable any timeout detection on the peer */ #define CONFIG_UGOAL_TIMEOUT_RPC 0 #define CONFIG_UGOAL_TIMEOUT_MEDIA 0

or using the make system, using the command in a project directory, like ugoal/projects/ugoal/02_profinet/gcc:

1 2 3 user: ~/.../02_profinet/gcc: make debug removed directory: build/ *** Debugging Mode enabled ***

and building the project:

1 2 3 4 5 6 7 8 9 10 11 12 user: ~/ugoal/projects/ugoal/02_profinet/gcc: make PLATFORM=stm32f429zi *** Debugging Mode enabled - note that: *** > this mode should only be used for debugging purposes > peer-loss detection on application controller is disabled > logging might be enabled *** Disabling per 'make debug' *** platform pre [CC] plat/stm32/plat.c ...

To switch the Debugging Mode off, use ‘make debug’ too

 

For GOAL based application, following code needs to be called before any other functions. So best place the following calls in appl_init():

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <goal_media/goal_mi_mctc_spi.h> /****************************************************************************/ /** Application Init * * Build up the device structure and initialize the Profinet stack. */ GOAL_STATUS_T appl_init( void ) { GOAL_STATUS_T res = GOAL_OK; /* result */ /* disable media and RPC timeout */ goal_miMctcCfgTout(0); goal_miMctcSpiCfgTout(0); /* initialize DD */ res = goal_ddInit(); if (GOAL_RES_ERR(res)) { goal_logErr("Initialization of DD failed"); } /* ..... */ }

 

This will disable both the RPC timeout and the SPI communication timeout. If the application controller is halted for an arbitrary time, operation can be resumed without any error.