Weird SPI read behaviour

Hi guys,

I’m having a strange problem here accessing a sd card. I have the same code running on a ATSAM device, so the code is fine. I’m trying to track down the issue a couple of days now. At the moment I’m anaylizing the SPI data with an oscilloscope capable of serial decoding.

It turns out that for the first couple of commands the SPI read is ok i.e. the MOSI is high during read. But at some point the situation changes and even when I when I execute a SPI read the Apollo3 seems to do a SPI write with value 0.

See the attached screenshot of the oscilloscope.

The low MOSI during read seems to confe the SD card because the following commands fail then.

Does anyone of you have a clue on that?

I have attached to screenshots of the oscilloscope. Both are captured by executing the code

uint32_t SPI::R(uint8_t cs, uint8_t *data, uint32_t num_bytes) {

    uint32_t status = AM_HAL_STATUS_SUCCESS;

    memset(&m_xfer_rx,0,sizeof(m_xfer_rx));

    m_xfer_rx.ui32NumBytes = num_bytes;
    m_xfer_rx.eDirection = AM_HAL_IOM_RX;
    m_xfer_rx.pui32TxBuffer = NULL;
    m_xfer_rx.pui32RxBuffer = (uint32_t*)data;;
    status = am_hal_iom_blocking_transfer(m_iom_handle, &m_xfer_rx);
    if(status != AM_HAL_STATUS_SUCCESS)
    { 
      report(status); 
      return 0;
    }

    return m_xfer_rx.ui32NumBytes;

The first is as expected. The second seems to be a write. I have already debugged into the AmbiqSuite SDK and in both cases a read is constructed.

ui32Cmd = build_cmd(ui32Cmd, ui32Dir,  ui32Cont, ui32Offset, ui32OffsetCnt, ui32Bytes);

with ui32Dir =1.

This is a real blocker for me. So please write me something, even if you don’t have any ideas :slight_smile:

interesting issue… but no real idea… only thoughts

  • Looking at the hal-code… An AM_HAL_IOM_RX is only overturned to AM_HAL_IOM_TX if the number of bytes to read is zero. Could that happen in your code?

  • Not sure what board you have but is there maybe another SPI channel to try.

  • Could the MOSI GPIO be set low somewhere else in the code? E.g. like with CS?

  • Looking at the Artemis datasheet,in chapter 8.2 it talks about SPI flowcontrol, and one possibility is to use MOSI. I don’t see anywhere in the code where that could be set (it is reset during init)

  • Is the TX FIFO not empty from a previous write or maybe still pending 0x0?

  • What is send just before it fails. Say the last bit was zero, is that being repeated with the next read (should not …)

  • Any chance MOSI is pulled low by the SD ?
  • I have just connected my SPI analyzer on a BME280 while working in SPI and providing data in the sketch.

    You clearly see on MOSI line the Artemis requests a register ( 0x8F, 0x8E, 0x91) and during each read the MOSI is set low, where the MISO provides the data.

    What version of HAL/SDK are you using? If I remember right, Ambiq made some changes to SPI operation in regards to full-duplex/half-duplex as the versions got released.

    I used 1.20 sparkfun version.

    How does correlate with the actual Ambiq SDK version? I think the latest Ambiq SDK is 2.5.1.

    need to check… dropped you a PM…

    When describing an SPI read transfer that includes a register address write phase, section 8.8.4 of the data sheet says: “After the transfer of the last address bit (bit 0), the I2C/SPI Master stops driving the MOSI line”. If that is literally true, then MOSI is floating while the IOM is reading. In section 8.8.6, the data sheet indicates that if the transfer is a raw read with no write of a register address to begin the transfer, then MOSI appears to be in a Hi-Z state for the whole duration. Maybe MOSI is just floating to 0 during your read. What if you added a 10K to 100K pullup on MOSI? If you still see a ‘0’ on MOSI after adding the resistor, then someone has to be driving it.

    I just ran an experiment. I have nothing connected to my IOM port, so the read transfers is going into oblivion. But that means that no one is possibly driving MOSI except the IOM itself. I did a single byte ‘read register’ transfer. The read register transfer begins with a write of the [fake] register address on MOSI, then the read is performed after the register address terminates. I see the register address go out, followed 8 more clocks while the IOM reads from the non-existent device. As you have observed, MOSI is ‘0’ during the read operation. I then added a 20K pullup to MOSI, and it is still ‘0’ during the read byte. When the transfer completes, MOSI goes high again. The only possible reason is that the IOM itself wants MOSI at ‘0’ during the read operation.

    If you really need MOSI at ‘1’ during a read, maybe you need to do a full duplex read transfer where the outgoing TX buffer is all 0xFF bytes.

    I think the later is the case… you need to write a 0xff:

    http://elm-chan.org/docs/mmc/mmc_e.html, command and response :

    Because the data transfer is driven by serial clock generated by host controller, the host controller must continue to read data, send a 0xFF and get received byte, until a valid response is detected. The DI signal must be kept high during read transfer (send a 0xFF and get the received data).

    I looked at the code for 2.0.5 (which is using Mbed) but in the end it is using the call spi_master_block_write() which handles works in

    paulvha:
    I think the later is the case… you need to write a 0xff:

    http://elm-chan.org/docs/mmc/mmc_e.html, command and response :

    Because the data transfer is driven by serial clock generated by host controller, the host controller must continue to read data, send a 0xFF and get received byte, until a valid response is detected. The DI signal must be kept high during read transfer (send a 0xFF and get the received data).

    I looked at the code for 2.0.5 (which is using Mbed) but in the end it is using the call spi_master_block_write() which handles works in

    I did more work during the day and this is exactly what the Arduino SD-library does… sending 0xff

    Thank for looking into that issue. I implemented your suggestion to use the full duplex transfer with a write buffer filled with 0xFF.

    First I thought everything is working fine, but unfortunately I ran into another problem with the Apollo 3.

    The initialization works and also FatFS runs fine for a while. E.g. I can do thousands of file open, write, close calls. But then suddenly the SPI data connection gets unstable.

    When I try to reinit the card it fails because the data sent by the card are not received correctly. I have verified with the oscilloscope that the card sends e.g. 0x000001AA. The data is decoded by the scope without any problems. But the full duplex transfer receives somehow corrupted data like 0x000000AA or 0xFF0000AA00. Removing the card and reinitializing the IOM doesn’t help. The only solution is to reset the Apollo 3.

    Has anyone else of you experienced that the SPI behaves weird after some time? I have no clue how to resolve this, because I need the full duplex transfer to get the MOSI high during read.

    I have not seen read issues because I have only used SPI so far as a write-only mechanism to update LCD displays. But the SPI unit seems to work fine for gazillions of writes in a row.

    From your description, it is not clear that your instability issue is related to the original MOSI issue. But just in case, it might be an interesting experiment to ‘manually’ drive MOSI to ‘1’ during your SPI read operations. By that, I mean putting a wrapper around your SPI read call so that it:

  • - Reassigns the MOSI pad function setting from the IOM/MOSI function back to a GPIO function
  • - Drives the GPIO pad corresponding to MOSI to '1'
  • - Performs the IOM SPI read operation where MOSI is now known to be '1' for the whole duration
  • - Reassigns the MOSI pad function back to the IOM
  • This should work as long as your SPI read does not call the SPI pin configuration mechanism on each read or write call.

    Other things I would check would be the electrical environment of your SPI signals. Are there any long wires, or do your SPI signals running beside other noisy PCB traces? If you are trying to run the SPI interface really fast, a false clock event can have completely devastating results. If you run the SPI clock slower, do things get better? Of course, one would think that after a false clock, the SPI unit could be recovered by reinitializing the IOM.

    One last thing: once the system is in this weird state, can you power cycle just the IOM in question by turning it off via the DEVPWREN register, then on again, and then reinitialize it?

    Meanwhile I have tested manually setting the MOSI to 1 during the read operation. This works perfectly. Over the night it performed 25.000 file open, write, close operations without any error.

    It should also work with a pullup resistor on the MOSI the blocking read call. But I havent tested that up to now.

    I’m believe that the SPI fullduplex operation has a bug on the Apollo3 with SDK 2.5.1. It appears to be the case that the data in the read FIFO is somehow corrupted when using the blocking fullduplex mode.

    Maybe the issue ERR018 in Apollo2 has never been really fixed and appears also in the Apollo3?

    https://ambiq.com/wp-content/uploads/20 … Errata.pdf

    The question is how MBed OS and the Arduino Core can work with this faulty behaviour.

    I havent tried to power cycle the IOM because I didn’t know that this is possible. It would have been a good idea to do that.

    A pullup will not help. I tried that back in this post: viewtopic.php?p=223968#p223968. My test showed that the IOM is clearly driving MOSI during an SPI read transfer and not letting it float.