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. 

Parents
  • 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!

Reply
  • 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!

Children
  • 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

Related