Skip to main content
Louis Loving
Senior
April 2, 2021
Question

During startup I occasionally get stuck in the function UTIL_SEQ_WaitEvt located in STM32_seq.c.

  • April 2, 2021
  • 13 replies
  • 4025 views

Here is the function running in my STM32WB55CG:

/**
 * this function can be nested
 */
void UTIL_SEQ_WaitEvt( UTIL_SEQ_bm_t evt_id_bm )
{
 UTIL_SEQ_bm_t event_waited_id_backup;
 UTIL_SEQ_bm_t current_task_id_bm;
 
 /** store in local the current_task_id_bm as the global variable CurrentTaskIdx
 * may be overwritten in case there are nested call of UTIL_SEQ_Run()
 */
 current_task_id_bm = (1 << CurrentTaskIdx);
 
 /** backup the event id that was currently waited */
 event_waited_id_backup = EvtWaited;
 EvtWaited = evt_id_bm;
 /**
 * wait for the new event
 * note: that means that if the previous waited event occurs, it will not exit
 * the while loop below.
 * The system is waiting only for the last waited event.
 * When it will go out, it will wait again fro the previous one.
 * It case it occurs while waiting for the second one, the while loop will exit immediately
 */
 while((EvtSet & EvtWaited) == 0)
 {
 UTIL_SEQ_EvtIdle(current_task_id_bm, EvtWaited);
 }
 EvtSet &= (~EvtWaited);
 EvtWaited = event_waited_id_backup;
 
 return;
}

The code repeatedly calls UTIL_SEQ_EvtIdle(current_task_id_bm, EvtWaited).

current_task_id_bm is set to: CFG_TASK_SYSTEM_HCI_ASYNCH_EVT_ID

EvtWaited is set to: CFG_IDLEEVT_HCI_CMD_EVT_RSP_ID

Since I am not getting stuck there every time, it feels like some kind of race condition.

The comment seems to mention never leaving the while loop but I am not understanding it if it is telling me how to fix or prevent it.

Thanks for any help.

This topic has been closed for replies.

13 replies

Piranha
Principal III
April 2, 2021
  1. The line 29 does a read–modify–write. If an interrupt happens between read and write operations and it uses UTIL_SEQ_SetEvt() or UTIL_SEQ_ClrEvt() to set/clear some event, then the result of that action is lost when the write at line 29 completes. A race condition!
  2. EvtSet is not defined as volatile and therefore the compiler is not required to read that variable every time it is tested at a line 25. Actually as the EvtSet is not volatile and it is already read at line 25, the compiler is not required to read it again at line 29. That means that the window for a race condition to happen is extended from the read operation at line 25 down to the write operation at line 29.
  3. EvtWaited also is not defined as volatile and therefore functions UTIL_SEQ_IsEvtPend() and UTIL_SEQ_Run() can read wrong values when called from UTIL_SEQ_EvtIdle().
  4. Function UTIL_SEQ_IsEvtPend() currently is not interrupt-safe because it does two read operations, which cannot be atomic. It's logic should be encapsulated in a critical section to allow it to be used from ISR.
  5. Function UTIL_SEQ_WaitEvt() also is not interrupt-safe and cannot be called from ISR. I'm guessing, but in this case it seems to be intentionally designed that way. Unfortunately it's not documented anywhere, which functions can be called from ISR!

@Christophe Arnal​, @Remi QUINTIN​, these are serious bugs in a fundamental software component. Also, if the SEQUENCER is used in CPU2 firmware, these bugs could be even related to this serious issue:

https://community.st.com/s/question/0D53W00000Y2gzOSAR/permanently-damaged-radio-rx-path-on-stm32wb55

ST Employee
April 6, 2021

​Hello,

Here are the feedback to the points raised :

  1. This is correct and will be fixed in the next version
  2. This is correct and will be fixed in the next version. In addition, for the same reason, TaskSet and TaskMask will be declared as volatile as they can me modified from an ISR while they can be cached in the UTIL_SEQ_Run() function
  3. This is not correct. EvtWaited  is updated only when moving out from the while loop in UTIL_SEQ_WaitEvt() so when either UTIL_SEQ_IsEvtPend() and UTIL_SEQ_Run() is called , it will report properly the current waited id.
  4. As long as EvtWaited  is not required to be volatile, UTIL_SEQ_IsEvtPend() is interrupt safe
  5. It would clearly be a mistake to call UTIL_SEQ_WaitEvt()  from an ISR. The header file of the sequencer will be updated to list which fucntion may be called from an ISR and which one shall not be.

Regards.

Louis Loving
Senior
April 5, 2021
void hci_cmd_resp_wait(uint32_t timeout)
{
 UTIL_SEQ_WaitEvt(1 << CFG_IDLEEVT_HCI_CMD_EVT_RSP_ID);
 return;
}

I have also noticed that the function that calls UTIL_SEQ_WaitEvt is called with a timeout that is never used.

Is this something that should be implemented?

ST Employee
April 6, 2021

​Hello,

This parameter is useless. It was introduced in the purpose of building a standard interface but in our STM32WB applications, it is not relevant.

Regards.

MeteHan
Associate II
April 6, 2021

Hi Louis,

This problem generally happens when you are using wrong wireless stack for your application. For example if you want to use a ble application you have to update your stack to one of compitable ble stack to M0 core. Please search for wireless stack update for more. For addition if you are using one of st's example, there is a readme file thats tell you which wireless stack you have to use.

Louis Loving
Senior
April 6, 2021

Thanks MeteHan, I am running a BLE application with the BLE Stack for the M0 core programmed per the release notes.

ST Employee
April 6, 2021

​Hello,

This is a bit tricky. As long as you are in the startup phase, I believe it is unlikely that you are facing one of the race conditions that have been spotted and confirmed in the sequencer ( unless you added your own events in addition to the existing one).

To my understanding, you just received the system ready event from CPU2 and within the process that has received this system ready event, you sent some HCI command and for one of them, the CPU2 is not responding.

That would be goof if you could :

1/ confirm if you added or not your own events

2/ Identify which HCI command is not getting an answer from CPU2 - You may screenshot the function calling tree with your debugger.

3/ Check if this is the first HCI command that is not responded

4/ Check the value reported at the start of SRAM2 @0x20030000 where the CPU2 reports a keyword in case it has either detected an error or is in hardfault. Otherwise, you should read back that address of the shared table.

Regards.

TomC
Senior
July 24, 2021

Hi Christophe,

When you say " unless you added your own events in addition to the existing one" and "confirm if you added or not your own events" are you saying that if you add your own events to the sequencer used in all BLE example projects for the STM32WB you are susceptible to the aforementioned race condition?

If yes, how do you suggest integrating your own functionality into say, the p2p_server_app structure that's loaded when you select the STM32WPAN peripheral in CUBEMX. Presently, I manifest much of the functionality for my custom application by registering and setting events with the sequencer's optimised super-loop and do occasionally experience the race condition described in the original question.

See related question

Kind regards,

Tom

Louis Loving
Senior
April 15, 2021

Thanks Christophe.

I have not added any events. I will work to get you answers for you other questions.

Unfortunately with our hardware we did not leave ourselves any way to connect a debugger. I am working on code to read the value reported at the start of SRAM2 and print it out via UART. Is AN5185, Table 5, the best description of what we should expect to see?

One item of note that is a concern of ours is that this only seems to be happening with our latest batch of STM32WB55CGs. Previously, we have not seen this issue when running the same code but we are now seeing this happen on several units.

ST Employee
April 19, 2021

Hello,

As long as you have only traces over UART available, you may use the embedded HCI traces to check which command is pending.

This can be enabled in tl_dbg_conf.h

I would set following configuration

#define TL_SHCI_CMD_DBG_EN 1 /* Reports System commands sent to CPU2 and the command response */
#define TL_SHCI_CMD_DBG_RAW_EN 0 /* Reports raw data System commands sent to CPU2 and the command response */
#define TL_SHCI_EVT_DBG_EN 1 /* Reports System Asynchronous Events received from CPU2 */
#define TL_SHCI_EVT_DBG_RAW_EN 0 /* Reports raw data System Asynchronous Events received from CPU2 */
 
#define TL_HCI_CMD_DBG_EN 1 /* Reports BLE command sent to CPU2 and the command response */
#define TL_HCI_CMD_DBG_RAW_EN 0 /* Reports raw data BLE command sent to CPU2 and the command response */
#define TL_HCI_EVT_DBG_EN 1 /* Reports BLE Asynchronous Events received from CPU2 */
#define TL_HCI_EVT_DBG_RAW_EN 0 /* Reports raw data BLE Asynchronous Events received from CPU2 */
 
#define TL_MM_DBG_EN 0 /* Reports the informations of the buffer released to CPU2 */

This should ouptut the system ready event received from CPU2 and all commands/events on either BLE or system channel exchanged between both CPUs.

Regards.

Louis Loving
Senior
April 19, 2021

@Christophe Arnal​, if I make change these values from what is generated by CubeMX, I don't see anything on UART1 TX.

Do I also need to make the following changes (particularly setting CFG_DEBUG_BLE_TRACE 1):

/******************************************************************************
 * Debug
 ******************************************************************************/
/**
 * When set, this resets some hw resources to set the device in the same state than the power up
 * The FW resets only register that may prevent the FW to run properly
 *
 * This shall be set to 0 in a final product
 *
 */
#define CFG_HW_RESET_BY_FW 0
 
/**
 * keep debugger enabled while in any low power mode when set to 1
 * should be set to 0 in production
 */
#define CFG_DEBUGGER_SUPPORTED 0
 
/**
 * When set to 1, the traces are enabled in the BLE services
 */
#define CFG_DEBUG_BLE_TRACE 1
 
/**
 * Enable or Disable traces in application
 */
#define CFG_DEBUG_APP_TRACE 0
 
#if (CFG_DEBUG_APP_TRACE != 0)
#define APP_DBG_MSG PRINT_MESG_DBG
#else
#define APP_DBG_MSG PRINT_NO_MESG
#endif
 
#if ( (CFG_DEBUG_BLE_TRACE != 0) || (CFG_DEBUG_APP_TRACE != 0) )
#define CFG_DEBUG_TRACE 1
#endif
 
#if (CFG_DEBUG_TRACE != 0)
#undef CFG_LPM_SUPPORTED
#undef CFG_DEBUGGER_SUPPORTED
#define CFG_LPM_SUPPORTED 0
#define CFG_DEBUGGER_SUPPORTED 1
#endif
 
/**
 * When CFG_DEBUG_TRACE_FULL is set to 1, the trace are output with the API name, the file name and the line number
 * When CFG_DEBUG_TRACE_LIGHT is set to 1, only the debug message is output
 *
 * When both are set to 0, no trace are output
 * When both are set to 1, CFG_DEBUG_TRACE_FULL is selected
 */
#define CFG_DEBUG_TRACE_LIGHT 0
#define CFG_DEBUG_TRACE_FULL 0
 
#if (( CFG_DEBUG_TRACE != 0 ) && ( CFG_DEBUG_TRACE_LIGHT == 0 ) && (CFG_DEBUG_TRACE_FULL == 0))
#undef CFG_DEBUG_TRACE_FULL
#undef CFG_DEBUG_TRACE_LIGHT
#define CFG_DEBUG_TRACE_FULL 0
#define CFG_DEBUG_TRACE_LIGHT 1
#endif
 
#if ( CFG_DEBUG_TRACE == 0 )
#undef CFG_DEBUG_TRACE_FULL
#undef CFG_DEBUG_TRACE_LIGHT
#define CFG_DEBUG_TRACE_FULL 0
#define CFG_DEBUG_TRACE_LIGHT 0
#endif

If I do, at this point, I start getting the linker error that HW_UART_Transmit_DMA is not defined.

HW_Uart.c was not generated for our project by CubeMX and that appears to be where that function is defined in other projects.

Am I missing a step in CubeMX?

ItachiThuan
Associate II
August 4, 2021

I was also trapped in the while loop in line 25 too. I tried to reproduce with the wrong wireless stack and it always happens (with EvtSet =0 and EvtWaited=1). Is there any way to break the loop so I can add further action (warning) instead of hanging in that while loop when the wireless stack is not appropriate?

Thanks in advance!

ST Employee
August 6, 2021

Hello Tom,

Yes, the only case to match the race spotted condition would be to add your own events to be waited for.

Note that the Sequencer has been fixed in V1.12.0. So, I would recommend to switch to it and check if the problem is still there.

Regards.