Wiced Smart Software Runtime -- Lots of Questions

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Anonymous
Not applicable

i would like to learn some more about the underlying runtime supporting BLE Smart applications -- the startup sequence, execution context, the tasking model, etc....  i realize this is a rather broad topic; but at least a general understanding will help me (and hopefully) others when developing/debugging their application code....

let's start with startup sequence -- and the APPLICATION_INIT function in particular....  from what i can tell, this is entered immediately after the basic C runtime has been established by spar_setup.c followed by sparinit.c....  and then from here, there is typically a single call to bleapp_set_cfg() which receives the application's "create" function; the latter is where the real use of the underlying libraries begins....

this then leads to a flurry of questions about the "execution context" at each of these points:

  • when are hardware interrupts actually enabled???
  • when does the watchdog timer become enabled; and how long is its interval???
  • when should i initialize my own periperhal HW, ideally *before* interrupts start coming in???
  • is the application "create" function executing within a special task context???
  • what happens if the "create" function doesn't return -- either for a very long or forever???
  • are functions enqueued by bleappevt_serialize() executed first-come, first-served???
  • tell me more about bleappevt_serialize() -- it's return value and the return value of the scheduled fxn???
  • are bleappevt_serialize() functions subject to the same constraints as "create"; do they run in the same context???
  • can interrupt handlers interrupt other interrupt handlers; and can tasks preempt one another???
  • the API has numerous callbacks -- from gatt attributes being ready to UART interrupts; what context do these execute in???
  • do any of the callbacks actually execute in interrupt context; and if so, what is the interrupt latency???
  • similarly, what is the latency from scheduling (my own) function via bleappevt_serialize()
  • what are the various "real-time" constraints imposed by the BLE stack, which my app should respect???

so sorry for being so pedantic, but the application code we're trying to port onto the BCM92073x requires us to know this sort of information....

1 Solution
asridharan
Employee
Employee
10 comments on KBA 5 comments on KBA First comment on KBA

The start up sequence is a bit more complicated, but in general it starts with the boot ROM detecting the NV type and location (EEPROM over I2C first, then reconfigures the lines to SPI and looks for a serial flash if EEPROM is not found). If no NV storage is identified/located, it will go into download mode. If an NV is found and it has a valid application image, this is loaded into the internal RAM. Then it goes through HW and FW initialization. All the system threads are created and started and once these are ready, the application thread is created and the APPLICATION_INIT() function is called (after app RW and ZI areas are initialized). Once this function returns the stack (GATT, ATT, SMP, GAP layers) is initialized followed by creation of the application with the call to the application_create function registered with bleapp_set_cfg. Note that the system is already multi-threaded by this time. This completes the bootup/initialization phase. Everything that follows from now on is event driven (timers expire, connect/disconnect, write to GATT db events occur, etc.).

> when are hardware interrupts actually enabled???

Application interrupts (GPIO, Keyscan and Quadrature sensor) are enabled by bleprofile_Init(). It is recommended you register your interrupt handlers before this.

> when does the watchdog timer become enabled; and how long is its interval???

The watchdog is enabled sometime between the call to APPLICATION_INIT() and application_create(). The watchdog is a 2s HW timer and fires in two phases. The idle thread (the lowest priority system thread) pets the watchdog and so this thread cannot starve for more than ~2s. So if you have a while(1) or similar construct anywhere in the app thread (including in application_create), the watchdog will trip in 2s and will execute code in the watchdog interrupt handler (this is a system handler and there is no application override) where the FW collects some information. Then 2s later, the chip will reset (so you will see the chip reboot in 4s).

> when should i initialize my own periperhal HW, ideally *before* interrupts start coming in???

Before bleprofile_Init() invoked in your application_create function if you plan to configure interrupts. For GPIOs, make sure that you have registered the interrupt handler before configuring a GPIO for interrupt (using gpio_configurePin).

> is the application "create" function executing within a special task context???

Yes, the application thread.

> what happens if the "create" function doesn't return -- either for a very long or forever???

See watchdog timer answer above.

> are functions enqueued by bleappevt_serialize() executed first-come, first-served???

Yes, bleappevt_serialize() enqueues a message (in the thread's message queue) to the application thread to call the function given as its parameter. So it is essentially a FIFO (and other threads and interrupts can enqueue messages to the app thread too - so don't be surprised if say an interrupt handler is invoked between two functions you serialized with bleappevt_serialize).

> tell me more about bleappevt_serialize() -- it's return value and the return value of the scheduled fxn???

/// This function lets you serialize a call onto the app

/// thread. Warning: there is no protection in case of overflow.

/// \param fn is the function to be called; it must take a void*

/// argument (which will be what was passed for \c data). The function

/// must return one of two values: BLE_APP_EVENT_FREE_BUFFER if it wants

/// \c data to be freed, with cfa_mm_Free(), when it returns; or

/// BLE_APP_EVENT__NO_ACTION if it doesn't want any action taken, e.g. , the

/// void* was NULL, or pointed to something that wasn't allocated

/// specifically for serialization).

/// \param data will be passed to the callback. If \c fn returns

/// BLE_APP_EVENT_FREE_BUFFER, it must have been allocated with

/// cfa_mm_Alloc(), and \c data will be freed upon return from \c fn.

/// \return TRUE if succeeded queuing, FALSE otherwise, in which case

/// the caller may have to free \c data if it was allocated

BOOL32 bleappevt_serialize(int (*fn)(void*), void* data);

> are bleappevt_serialize() functions subject to the same constraints as "create"; do they run in the same context???

> the API has numerous callbacks -- from gatt attributes being ready to UART interrupts; what context do these execute in???

Almost all application functions run in the same thread context - the application thread. Create, callbacks, serialized functions, application's interrupt handlers (GPIO, UART), everything except the callback registered with devlpm_registerForLowPowerQueries, which is called in the idle thread context. You are only expected to read 'state' information to decide sleep/don't sleep return values in this callback.

> can interrupt handlers interrupt other interrupt handlers; and can tasks preempt one another???

The interrupts have priorities, but are not nested. Application interrupts are always serialized to the application thread (GPIO interrupts, PUART interrupts etc.). There are other higher priority threads in the system to manage Bluetooth functions and these always preempt the application thread when needed.

> do any of the callbacks actually execute in interrupt context; and if so, what is the interrupt latency???

No, none of the application interrupt handlers execute in interrupt context. The depends on other (higher priority) interrupts and threads. The minimum is of the order of ~30uS.

> similarly, what is the latency from scheduling (my own) function via bleappevt_serialize()

You should be able to time the call to bleappevt_serialize() and I thin this takes ~15uS. If there is nothing else in the application thread's event queue, then the turnaround time for the call to the serialized function should be of the order of a few uS. If a context switch is required, the turnaround time is ~15uS.

> what are the various "real-time" constraints imposed by the BLE stack, which my app should respect???

All Bluetooth functionality has the highest priority. When any BT functionality is active (connection/adv/scan etc.) it is recommended that you don't lock interrupts (if you ever have to using bleapputil_cpuIntDisable/bleapputil_cpuIntEnable - generally used in the callback registered by devlpm_registerForLowPowerQueries because this is called in idle thread context) for more than ~200uS. All other application (and stack above HCI) functions run in the application thread context which will be preempted when required.

View solution in original post

10 Replies
asridharan
Employee
Employee
10 comments on KBA 5 comments on KBA First comment on KBA

The start up sequence is a bit more complicated, but in general it starts with the boot ROM detecting the NV type and location (EEPROM over I2C first, then reconfigures the lines to SPI and looks for a serial flash if EEPROM is not found). If no NV storage is identified/located, it will go into download mode. If an NV is found and it has a valid application image, this is loaded into the internal RAM. Then it goes through HW and FW initialization. All the system threads are created and started and once these are ready, the application thread is created and the APPLICATION_INIT() function is called (after app RW and ZI areas are initialized). Once this function returns the stack (GATT, ATT, SMP, GAP layers) is initialized followed by creation of the application with the call to the application_create function registered with bleapp_set_cfg. Note that the system is already multi-threaded by this time. This completes the bootup/initialization phase. Everything that follows from now on is event driven (timers expire, connect/disconnect, write to GATT db events occur, etc.).

> when are hardware interrupts actually enabled???

Application interrupts (GPIO, Keyscan and Quadrature sensor) are enabled by bleprofile_Init(). It is recommended you register your interrupt handlers before this.

> when does the watchdog timer become enabled; and how long is its interval???

The watchdog is enabled sometime between the call to APPLICATION_INIT() and application_create(). The watchdog is a 2s HW timer and fires in two phases. The idle thread (the lowest priority system thread) pets the watchdog and so this thread cannot starve for more than ~2s. So if you have a while(1) or similar construct anywhere in the app thread (including in application_create), the watchdog will trip in 2s and will execute code in the watchdog interrupt handler (this is a system handler and there is no application override) where the FW collects some information. Then 2s later, the chip will reset (so you will see the chip reboot in 4s).

> when should i initialize my own periperhal HW, ideally *before* interrupts start coming in???

Before bleprofile_Init() invoked in your application_create function if you plan to configure interrupts. For GPIOs, make sure that you have registered the interrupt handler before configuring a GPIO for interrupt (using gpio_configurePin).

> is the application "create" function executing within a special task context???

Yes, the application thread.

> what happens if the "create" function doesn't return -- either for a very long or forever???

See watchdog timer answer above.

> are functions enqueued by bleappevt_serialize() executed first-come, first-served???

Yes, bleappevt_serialize() enqueues a message (in the thread's message queue) to the application thread to call the function given as its parameter. So it is essentially a FIFO (and other threads and interrupts can enqueue messages to the app thread too - so don't be surprised if say an interrupt handler is invoked between two functions you serialized with bleappevt_serialize).

> tell me more about bleappevt_serialize() -- it's return value and the return value of the scheduled fxn???

/// This function lets you serialize a call onto the app

/// thread. Warning: there is no protection in case of overflow.

/// \param fn is the function to be called; it must take a void*

/// argument (which will be what was passed for \c data). The function

/// must return one of two values: BLE_APP_EVENT_FREE_BUFFER if it wants

/// \c data to be freed, with cfa_mm_Free(), when it returns; or

/// BLE_APP_EVENT__NO_ACTION if it doesn't want any action taken, e.g. , the

/// void* was NULL, or pointed to something that wasn't allocated

/// specifically for serialization).

/// \param data will be passed to the callback. If \c fn returns

/// BLE_APP_EVENT_FREE_BUFFER, it must have been allocated with

/// cfa_mm_Alloc(), and \c data will be freed upon return from \c fn.

/// \return TRUE if succeeded queuing, FALSE otherwise, in which case

/// the caller may have to free \c data if it was allocated

BOOL32 bleappevt_serialize(int (*fn)(void*), void* data);

> are bleappevt_serialize() functions subject to the same constraints as "create"; do they run in the same context???

> the API has numerous callbacks -- from gatt attributes being ready to UART interrupts; what context do these execute in???

Almost all application functions run in the same thread context - the application thread. Create, callbacks, serialized functions, application's interrupt handlers (GPIO, UART), everything except the callback registered with devlpm_registerForLowPowerQueries, which is called in the idle thread context. You are only expected to read 'state' information to decide sleep/don't sleep return values in this callback.

> can interrupt handlers interrupt other interrupt handlers; and can tasks preempt one another???

The interrupts have priorities, but are not nested. Application interrupts are always serialized to the application thread (GPIO interrupts, PUART interrupts etc.). There are other higher priority threads in the system to manage Bluetooth functions and these always preempt the application thread when needed.

> do any of the callbacks actually execute in interrupt context; and if so, what is the interrupt latency???

No, none of the application interrupt handlers execute in interrupt context. The depends on other (higher priority) interrupts and threads. The minimum is of the order of ~30uS.

> similarly, what is the latency from scheduling (my own) function via bleappevt_serialize()

You should be able to time the call to bleappevt_serialize() and I thin this takes ~15uS. If there is nothing else in the application thread's event queue, then the turnaround time for the call to the serialized function should be of the order of a few uS. If a context switch is required, the turnaround time is ~15uS.

> what are the various "real-time" constraints imposed by the BLE stack, which my app should respect???

All Bluetooth functionality has the highest priority. When any BT functionality is active (connection/adv/scan etc.) it is recommended that you don't lock interrupts (if you ever have to using bleapputil_cpuIntDisable/bleapputil_cpuIntEnable - generally used in the callback registered by devlpm_registerForLowPowerQueries because this is called in idle thread context) for more than ~200uS. All other application (and stack above HCI) functions run in the application thread context which will be preempted when required.

To clariy:

> are bleappevt_serialize() functions subject to the same constraints as "create"; do they run in the same context???

> the API has numerous callbacks -- from gatt attributes being ready to UART interrupts; what context do these execute in???

Almost all application functions run in the same thread context - the application thread. This includes create, callbacks, serialized functions, application's interrupt handlers (GPIO, UART), everything. The one exception is the callback registered with devlpm_registerForLowPowerQueries, which is called in the idle thread context. You are only expected to read 'state' information to decide sleep/don't sleep return values in this callback. Use bleapputil_cpuIntDisable/bleapputil_cpuIntEnable to lock/unlock interrupts if you want to protect critical data any time you need to ensure atomic access.

Anonymous
Not applicable

first let me say:  excellent overview....  thank you for all the information....

just a few follow-up questions....

  • it appears that i still need to be somewhat mindful of the watchdog during "hardware setup"; that is, *before* i actually call bleprofile_Init(), any very long-running initialization operations could trigger the watchdog....  one answer would be to simply "don't do this" -- perform this work later on instead....  could i, however, effectively "yield" to the kernel by essentially scheduling a "chain" of functions???
  • in general, if i schedule a function call does the idle thread pet the watchdog????  if so, then this is a technique for supporting any long-running application operation -- by explicitly breaking the operation into functions of smaller duration that are effectively chained....
  • roughly speaking, how many serialized functions can i enqueue at once -- what's a very conservative upper-bound???
  • given that virtually all execution does happen in the application thread -- and i'm implicitly preempted by time-critical interrupts as well as higher-priority threads -- i really don't have to worry about synchronization within my own application thread....   said another way, all my code executes within its own thread; and i don't have any way to "preempt myself"....  the only "critical sections" that i might want to impose are *very short* sequences of instructions (read-modify-write some data) that *must* be executed atomically....  is my understanding correct here???
0 Likes

> it appears that i still need to be somewhat mindful of the watchdog during "hardware setup".........

As long as your long-running initialization is under ~2s, you should be fine, even in the application_create function. For any processing that could take longer than 2s, use state machines and/or timers if you can. As a last resort, you can pet the watchdog yourself with cfa_kickWatchdog (declared in cfa.h) and get another 2s, but you really have to be sure why you are doing this.

> in general, if i schedule a function call does the idle thread pet the watchdog????......

The application thread's priority is higher than the idle thread. So if you serialize a function call to the app thread from the app thread, context will never switch to the idle thread (because there is a pending message in the app thread's message queue). So the idle thread will never get a chance to run, and if this happens for long enough (2s since it pet the watchdog), it will trip the watchdog interrupt.

> roughly speaking, how many serialized functions can i enqueue at once -- what's a very conservative upper-bound???

The serialization queue used by bleappevt_serialize is 16 entries deep by default. But this queue is also used by the interrupt context handlers to serialize calls to the app thread context interrupt handlers. So not all 16 may be available to the app all the time. You can override this queue depth by setting bleappevt_srzn_q_depth (which is a UINT8) in your APPLICATION_INIT function (setting it in other functions has no effect), but note that each entry in this queue takes up 8 more bytes of RAM.

> given that virtually all execution does happen in the application thread.....

Yes, all correct.

0 Likes
Anonymous
Not applicable

My initialization is taking longer than 2s and the watchdog timer is tripping.  I want to try using cfa_kickWatchdog() between my long initialization functions.  I #include "cfa.h", but get "undefined reference to `cfa_kickWatchdog'" during linking.  How can I fix this?

0 Likes

Are you using SDK 2.0?

0 Likes
Anonymous
Not applicable

SDK 1.1.0

0 Likes

Try:

{

    extern void wdog_restart(void);

    wdog_restart();

}

0 Likes
Anonymous
Not applicable

Upon further debug, it looks like initialization time is not the issue.  It looks like I ran out of RAM.  A Total RAM footprint according to the build tool of 25176 bytes works for me; 25240 bytes does not work.

0 Likes


There's a good thread related to managing stack overruns here: Re: Stack overruns and max stack size

0 Likes