At certain points in the code a call to a known good subroutine jumps into a .word disassembly and fails to execute

I have a subroutine that exercises the SPI interface and only at certain points in the code it won't execute the SPI subroutine and goes into the disassembly when single stepping to see what's going wrong. I presume it has something to do with the memory management, but I have not been able to fix it by playing around with those settings. 

  • Hello,

    Looking at your screenshot, there are some hints that suggests that you are using the Softdevice (Bluetooth Low Energy stack), is that correct?

    Do I understand you correct if you say that this only happens when you start stepping in a debug session? If that is the case, then this is caused because you can't do stepping while the softdevice is enabled*

    *You can, and you can use it to debug, but when you stop the execution, the Softdevice will not handle this very well. You can typically do some steps before the Softdevice realizes that it has missed several time critical operations, and then it stops the application. Is that what you are seeing?

    If that is the case, perhaps we can rather look into the problem that you had, that made you start trying to step through the code? Can you elaborate on that?

    Best regards,

    Edvin

  • Humm, interesting. Yes, definitely using the BTLE stack, but I've been debugging Nordic devices using SES in this way for years and yes periodically have encountered occasions where the code makes an unexpected context switch if I just let the session sit without single stepping. But this is different in the sense that if I set a break point on the offending method call and then "step over" this call, the code continues to execute to the next line as expected but the method doesn't actually run. On to your point about why I'm having issues - I'm interfacing to a Decawave UWB module through the SPI interface and it's not doing what I expect. I've had some difficulties porting the SPI part of the Decawave driver code to the Nordic SDK so I suspected this as root cause. In this case the offending method is a 32-bit read from a register on the UWB module. This function works fine earlier in the code (I can read the device ID register) but when the UWB timer starts running (to periodically execute the initiator/responder handshake) I get this behavior. I know the method isn't running because I don't see any activity on the logic analyzer. And when I single step through the code, I run into this .word disassembly. If I was to "turn off" the BTLE stuff to test that theory, what would be the easiest way to do that? Here's the routine that reads from the SPI port.

    int readfromspi(uint16_t headerLength,
                    const uint8_t *headerBuffer,
                    unsigned long readLength,
                    const uint8_t *readBuffer)
    {
    
      uint8_t j;
      uint8_t addrBuff[3];
      uint8_t *headerBufferIndex = headerBuffer;
      uint8_t *readBufferIndex = readBuffer;
      uint8_t tempReadBuffer[16];
      uint8_t command = *headerBuffer;
      for (j=0; j<3; j++) {
        addrBuff[j] = 0;
      }
    
      for (j=0; j<headerLength; j++) {
        addrBuff[j] = *headerBufferIndex;
        headerBufferIndex++;
      }
      // Now, just send it
      nrfx_spim_xfer_desc_t xfer_desc = NRFX_SPIM_XFER_TRX(addrBuff, headerLength, tempReadBuffer, readLength+headerLength);
      nrfx_spim_xfer(&spi2, &xfer_desc, NULL);
      while (!spi_xfer_done)
      {
          __WFE();
      }
      nrf_delay_ms(2);
      // now copy the returned value into the passed by reference read buffer
      // for some reason the nordic SPI read is mis-registered only for reads that don't have a subaddress
      if (command & 0x40) { //bit 6 (subaddress) is set
        for (j=0; j<readLength; j++) {
          *readBufferIndex = tempReadBuffer[j+2];
          readBufferIndex++;
        }
      }
      else { //bit 6 is not set
        for (j=0; j<readLength; j++) {
          *readBufferIndex = tempReadBuffer[j+1];
          readBufferIndex++;
        }
      }
      return 0;
    } // end readfromspi()

    This code is used to initialize the UWB module and then is run as part of the two-way ranging ISR when the timer expires. Thanks so much for the help!

  • An alternative to stepping is to use the logging module to see what code that is executed, and the outcome. You should also check your nrfx_spim_xfer() return value. What does it return?

    I think the easiest way to test this without the BTLE stack is to just comment out the advertising start function (or scanning, if you are using that). I think it will behave better as long as it the stack doesn't have any active tasks. 

    When you say that it isn't working, is readfromspi() called at all? If not, where should it be called from? If it is called, what is the issue? You don't get the data you expect?

    Reading your reply once more, I noticed.

    apdobaj said:
    function works fine earlier in the code (I can read the device ID register) but when the UWB timer starts running (to periodically execute the initiator/responder handshake) I get this behavior

    Does this mean that this function (readfromspi) is called from a timer interrupt? If that is the case, does the timer have the same or higher priority than the SPI? In that case, the SPI will not be able to execute, and you would be waiting for the spi_xfer_done() forever, because the SPI is not able to trigger the callback that is supposed to set spi_xfer_done to true, because you are already in an interrupt with the same IRQ priority.

    If that is the case, try increasing the priority of the SPI (or lowering the priority of the timer). If that is not the case, can you describe what you see? Is this function called at all?

    Best regards,

    Edvin

  • I've bypassed the timer for the TWR code and now have that running in an infinite loop just like the Decawave examples. I've also commented out the advertising start and do not appear to be going into the .word disassembly now. I'm going to focus now on getting the TWR up a nd running and then tackle getting the BTLE stack running with the TWR. I'll keep this open for now.

  • If the issue was indeed related to priorities, I suggest you check out the Using a Timer Expiry Function section in the k_timer documentation. By doing it as shown in the example snippet there, the task that is done inside the work handler can run even though the timer interrupt priority is higher than the task performed in the work handler. 

    BR,
    Edvin

Related