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.

SIMPLELINK-WIFI-CC3120-SDK-PLUGIN: Can't get multi-threaded operation to work?

Part Number: SIMPLELINK-WIFI-CC3120-SDK-PLUGIN
Other Parts Discussed in Thread: CC3120

I'm fairly sure that my problem is a basic one, i.e. I'm missing some configuration setting or something, so hopefully someone will immediately spot the issue.

I'm building and running the SL SDK WiFi plugin version 1.50.00 on an NXP K64 running FreeRTOS.  I've got the portation layer written (user.h attached).  I have sl_Task() launching in its own thread.  I've also instrumented some of my cc_pal routines to assist with debug, and the MCUxpresso debugger is showing me the state of each of my FreeRTOS threads.  The issue is with starting the device, and invoking the synchronization objects (semaphores).

After sl_Task() is up and running in its own thread, sl_Task() sleeps on g_SlInternalSpawnCB.SyncObj.  I then call sl_Start().  That disables the devices, registers the interrupt handler, and enables the device.  It then waits on a sync object for initialization to complete.  I can also see that my GPIO IRQ handler has indeed been invoked.  Because the SL IRQ handler was properly registered, my ISR calls _SlDrvRxIrqHandler().

Here's where things seem to go off the rails.  The IRQ handler decides that g_pCB->WaitForCmdResp is false, so rather than calling OSI_RET_OK_CHECK(sl_SyncObjSignalFromIRQ(&g_pCB->CmdSyncObj)), it calls sl_Spawn(), with a flag indicating it's being called from the IRQ handler.  _SlInternalSpawn() then runs this:

/* Increment the counter that specifies that async event has recived
   from interrupt context and should be handled by the internal spawn task */
if(flags & SL_SPAWN_FLAG_FROM_SL_IRQ_HANDLER)
{
    g_SlInternalSpawnCB.IrqWriteCnt++;
    g_SlInternalSpawnCB.pIrqFuncValue = pValue;
    SL_DRV_SYNC_OBJ_SIGNAL(&g_SlInternalSpawnCB.SyncObj);
    return(Res);
}

Problem is, we're still in an ISR context, but SL_DRV_SYNC_OBJ_SIGNAL() eventually calls xSemaphoreGive(), not xSemaphoreGiveFromISR().  This causes the system to hang (but not crash, thankfully).

So, what am I not configuring correctly?  I defined SL_PLATFORM_MULTI_THREADED in user.h so that sl_Task() would run as a proper background task.  But I don't see any other settings for me to adjust.  Is it possible that _SlInternalSpawn() should be calling sl_SyncObjSignalFromIRQ() instead of the regular version?  Let me know what I've screwed up, thanks.

Here's a screenshot of the debugger in this state, with relevant stack traces:

And here's my user.h:

6622.user.h

  • Hello,

    This is expected that there is no command waiting and it goes to the spawn code as upon startup, the driver simply turns the device ON and waits for an interrupt back. No command is sent over SPI.

    You are not using an external spawn as I cannot see it on your user.h and this is fine.

    What should happen is that SL_DRV_SYNC_OBJ_SIGNAL() should free _SlInternalSpawnTaskEntry() which is waiting on that signal.

    What are all the Nwp prefix from user.h?

    The original SDK user.h file has  SemaphoreP as prefix.

    And what if you manually make it invoke the ISR version. Does it work?

    Regards,

    Shlomi

  • Right, I'm not yet expecting to see a command over SPI.  Clearly, the system is seeing an interrupt from the CC3120 after the device is disabled and enabled, as PORTE_IRQHandler() is being invoked in an ISR context, so the ISR certainly seems to be working.

    I agree that SL_DRV_SYNC_OBJ_SIGNAL() from the ISR should free sl_task, which is waiting on that signal.  But as I pointed out, that signal is being sent from an ISR context, and this is locking up the system.  My question is, why is the ordinary non-ISR SL_DRV_SYNC_OBJ_SIGNAL() being called, when this is clearly being sent from an ISR context?  I mean, the function is even checking the flag SL_SPAWN_FLAG_FROM_SL_IRQ_HANDLER.  If it knows that it's being invoked from an ISR, why would it not call the ISR-safe sl_SyncObjSignalFromIRQ() method?  This seems like a bug to me.

    There is no SL_DRV_SYNC_OBJ_SIGNAL_FROM_IRQ() macro, even though there are separate sl_SyncObjSignalFromIRQ() and sl_SyncObjSignal() functions in the API.  I'm loathe to make any modifications to the driver code itself, but I'm trying something right now....

    ... yes, that seemed to fix it.  Here's my new code in spawn.c:

    {
        _i16 Res = 0;
        _SlInternalSpawnEntry_t*    pSpawnEntry;
    
        /* Increment the counter that specifies that async event has recived
           from interrupt context and should be handled by the internal spawn task */
        if (flags & SL_SPAWN_FLAG_FROM_SL_IRQ_HANDLER)
        {
            g_SlInternalSpawnCB.IrqWriteCnt++;
            g_SlInternalSpawnCB.pIrqFuncValue = pValue;
            /*SL_DRV_SYNC_OBJ_SIGNAL*/ sl_SyncObjSignalFromIRQ(&g_SlInternalSpawnCB.SyncObj);
            return(Res);
        }
        [...]
    }

    This really looks like a bug to me.  _SlInternalSpawn() is checking specifically that it's being called from an ISR, yet it's not calling the ISR-safe sl_SyncObjSignalFromIRQ() function from within that branch.  Once I made the change, the program no longer hung.

    Now, it's still not working right (ISR seems to go off before the NWP is powered on, which makes no sense).  So I'm working on that.  But I wanted to post this reply to see what you think.

    Regarding the "Nwp" monikers in user.h (NWP = NetWork Processor), here's my cc_pal.h for reference.

    cc_pal.h

  • Thanks for the update.

    What freeRTOS version are you using?

    Shlomi

  • I'm using FreeRTOS v9.0.0, as provided in the NXP MCUxpresso SDK.

    I can now report that the SL host driver appears to be in some sensible mode of communication with the CC3120, after fixing my SPI driver.  I now get the following debug output:

    nwp: SPI open
    nwp: power off
    nwp: install IRQ handler 0x000010b9(0x00000000)
    nwp: power on
    nwp: tx 4 bytes - 65 87 78 56
    nwp: rx 8 bytes - BC DC CD AB 08 00 14 00
    nwp: rx 8 bytes - 28 00 04 06 00 00 00 00
    nwp: rx 12 bytes - 11 11 11 11 00 00 00 31 00 00 00 00
    Result = 0
    nwp: tx 4 bytes - 65 87 78 56
    nwp: rx 8 bytes - BD DC CD AB 9A 08 30 00
    nwp: rx 8 bytes - 28 00 04 06 00 00 00 00
    nwp: rx 40 bytes - 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    mgr: provisioning status Abort: HTTP server disabled, WLAN status Disconnected, role STA, SSID ""

    I got a longer string of operations on a previous iteration, I think because it was the first time ever initializing the module... judging by the length of operations, I think it ran sl_Start(), found it wasn't in the right role, reset the role and other params, and then re-initialized the CC3120.  Anyhow, this now looks like the host driver is doing what it's supposed to.  The last line is from catching a SL_WLAN_EVENT_PROVISIONING_STATUS event.

    So, I think my original assertion holds... I think you have a bug in your host driver in spawn.c, in function _SlInternalSpawn():

    _i16 _SlInternalSpawn(_SlSpawnEntryFunc_t pEntry,
                          void* pValue,
                          _u32 flags)
    {
        _i16 Res = 0;
        _SlInternalSpawnEntry_t * pSpawnEntry;
    
        /* Increment the counter that specifies that async event has recived
           from interrupt context and should be handled by the internal spawn task */
        if (flags & SL_SPAWN_FLAG_FROM_SL_IRQ_HANDLER)
        {
            g_SlInternalSpawnCB.IrqWriteCnt++;
            g_SlInternalSpawnCB.pIrqFuncValue = pValue;
            /*SL_DRV_SYNC_OBJ_SIGNAL*/ sl_SyncObjSignalFromIRQ(&g_SlInternalSpawnCB.SyncObj);
            return(Res);
        }
        [...]
    }

    With the above modification, my program now seems to operate exactly as intended.  I would like to know your opinion as to whether this is in fact a bug or not, thanks.

  • Hello and thanks again,

    It indeed looks like a bug although it is implemented correctly.

    It just seems as the API that asks for the HWI doesn't behave as it should.

    I will open a bug internally to the SDK team.

    Regards,

    Shlomi

  • Thank you, I appreciate you looking into this.  I'm presuming that your internal bug report has a link to this forum post; the image in my original post should show exactly what's going on inside the RTOS task structure when the bug occurs.  As far as I can tell, I've architected my implementation as one should for a FreeRTOS implementation... I define SL_PLATFORM_MULTI_THREADED, I give sl_Task() its own thread and stack, I implement all of the FreeRTOS semaphore and mutex primitives the same way that they're implemented in the MSP432 SDK, and I call the registered interrupt handler directly from my ISR (and thus in an ISR context).

    In _SlDrvRxIrqHandler(), if WaitForCmdResp is set true, then it calls sl_SyncObjSignalFromIRQ(), else it calls sl_Spawn() with the SL_SPAWN_FLAG_FROM_SL_IRQ_HANDLER flag set.  That flag has to mean "hey, I'm invoking you from within an ISR", so surely that means that sl_Spawn() must also call sl_SyncObjSignalFromIRQ() if that flag is set.

    If you or your team need any additional information from me, please let me know, thanks.  If you want additional source code, I can email that to your team directly.  Thanks.

  • yes, I added the link.
    Regards
  • I've also just had this exact issue with SDK version 1_55_00. Changing it as proposed by DR216 to use the semaphore from IRQ function seems to work as a fix. But I'm yet to really stress test the Wifi.

    NOTE: I'm using FreeRTOS 10.0.0 and I've been porting the source over to remove the POSIX abstraction layer and dependency on the TI hardware drivers.

    I also had to change driver.c line 55 (pthread_self) to FreeRTOS equivalent (xTaskGetApplicationTaskTag) as the driver assumes POSIX abstraction. This should probably be in the abstraction layer (e.g. with all the semaphore stuff).
  • Stewart,

    I too am dismayed at how much dependence there is on TI software components for a device (CC3120) and driver that supposedly is designed to be readily integrated into any capable microcontroller platform.  At one point I tried porting in the JSON and MQTT libraries provided with the SDK driver, and I fell into a rabbit hole of API dependencies and straight-up conflicts with existing system header files.  I eventually decided that it just wasn't feasible for me to port anything but the core Wi-Fi driver to my NXP Kinetis FreeRTOS platform.

    If TI really cared about cross-platform compatibility with its CC3120, then they would offer some sort of an example project for platforms other than their own... an NXP FRDM board, an ST Discovery kit, an Atmel Xplained board, something.  That they haven't fixed this simple issue in v1.55.00 of the plugin is disappointing.  It's obviously an issue that doesn't affect their own reference implementation.

  • Hi DR216

    I know what you mean. I haven't even tried porting over the json, mqtt and other stuff (I think it's all new to 1_55 since 1_50 was compiling) - I actually just removed it from the core driver as it wouldn't compile after changing a few basic things. I think the compiler was missing a standard c header file or something. At least the core driver it working and this is only a prototype system for me. I shall persevere for now - still better than when I was using an old microchip MRF24!