Problem Using SPI Slave Mode With DMA (IDFGH-7468)

berlinetta
Posts: 41
Joined: Tue May 21, 2019 8:33 pm

Problem Using SPI Slave Mode With DMA (IDFGH-7468)

Postby berlinetta » Fri May 20, 2022 5:37 pm

Hello All,

I have been attempting to get the ESP32 to function as a slave on the SPI bus of my design. The bus is currently occupied by a serial flash device as well (separate chip select signal, of course). I have poured over all the documentation, errata and sample code to get this functionality going. I believe I have configured the SPI3 peripheral properly, including placing all buffers in DMA capable memory and utilizing the default peripheral pin locations to ensure reliable throughput, yet I am seeing some strange behavior.

If I operate the SPI bus without any attempts to access the other serial flash device, the ESP32 appears to respond as expected. However, if I access the serial flash device at all, proper operation of the ESP32 SPI peripheral is not achieved.

The first thing I noticed is that the ESP32 appears to be driving the MISO pin when its chip select is not active (bus access was intended for the serial flash). This is a fundamental failure of the hardware... a search of the forum does not reveal any other complaints about this, so I have to wonder if other users are using a dedicated bus for SPI traffic?

Secondly, I noticed that my post-transfer callback is being triggered after 8192 clocks (1024 bytes) when the bus traffic is 100% serial flash transactions (chip select for the ESP32 is not active). I can replicate this phenomenon if I simply perform a READ of the serial flash contents (the contents of the serial flash are non-zero). The post-transfer callback is receiving spi_slave_transaction_t structure information indicating the total data length was 8192 bits and the transaction data length was 8192 bits, yet the data pointed to by the rx_buffer pointer is mostly zeroes and not representative of the SPI bus data presented by the serial flash. I later determined that the data included some content which reflects the attempted flash addresses that are accessed.

In an experiment, I read 128-byte blocks of data from the serial flash, starting at address 0x00000000. If I execute eight of these operations (total of 1024 bytes read from flash), I get consistent data content in the post transaction callback. A diagnostic dump of this data from my code reveals evidence of what appears to be the incremental address occurring in the data. The triggered event appears to always start with 0x03000000, followed by values of 0x03000080, 0x03000100, 0x03000180, 0x03000200, 0x03000280, 0x03000300 and 0x03000380. These address values correlate with the base address the host was attempting to access on each 128-byte read. A capture of the first block of data from my diagnostic code is shown below.

Could this be a clue to what the hardware is doing internally?
Can anyone corroborate this behavior when operating on a multi-slave bus configuration?
Is there any work-around?

Thanks in advance!
Mark
  1. SPI3 length: 8192, trans length: 8192, pTx: 1073539608, pRx: 1073543708, RxData:
  2. 03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

berlinetta
Posts: 41
Joined: Tue May 21, 2019 8:33 pm

Re: Problem Using SPI Slave Mode With DMA

Postby berlinetta » Fri May 20, 2022 8:01 pm

Quick correction on my initial post...

The data reported in the rx buffer is indeed correlating with the MOSI data from the bus... I was mistakenly expecting the data to track the MISO information (data from the serial flash) rather than the data transmitted by the master.

Regardless... the question regarding why the ESP32 device is responding to clocks on the bus when its chip select is NOT active remains.

Thanks,
Mark

ESP_Sprite
Posts: 9711
Joined: Thu Nov 26, 2015 4:08 am

Re: Problem Using SPI Slave Mode With DMA

Postby ESP_Sprite » Sun May 22, 2022 6:36 am

That is interesting behaviour... First things first, are you sure the CS pin of the ESP32 is indeed kept high? Could you hardwire it to a high level if in doubt? Secondly, would you be able to post your code, or ideally a version whittled down to the minimum that displays the issue?

berlinetta
Posts: 41
Joined: Tue May 21, 2019 8:33 pm

Re: Problem Using SPI Slave Mode With DMA

Postby berlinetta » Mon May 23, 2022 1:34 pm

Hello ESP_Sprite...

Yes, I have confirmed the chip select is not active when monitoring things with the logic analyzer. I have also experimented with moving the CS signal to other GPIO and I have even tied the selected GPIO to the 3.3V supply to ensure it is never activated, yet the behavior remains the same. I can try to extricate the SPI- specific code from my source and provide that to you.

Can you think of any reason the peripheral would operate in this manner when DMA is active? Is there anything that could cause the device to ignore the assigned CS signal?

Thanks,
Mark

berlinetta
Posts: 41
Joined: Tue May 21, 2019 8:33 pm

Re: Problem Using SPI Slave Mode With DMA

Postby berlinetta » Mon May 23, 2022 3:08 pm

Hello,

I have attached some source code for your review. The "esp_spi" module is basically a wrapper that I have written to bridge the gap between the ESP IDF and our modular source code.

The main operational state machine file has been redacted to simply show the main SPI communications related functions. If you need further information, please let me know.

Best Regards,
Mark
Attachments
esp_spi.h
(3.69 KiB) Downloaded 278 times
esp_spi.c
(15.07 KiB) Downloaded 319 times
ctlOpStateRadioHif.c
(17.56 KiB) Downloaded 271 times

berlinetta
Posts: 41
Joined: Tue May 21, 2019 8:33 pm

Re: Problem Using SPI Slave Mode With DMA

Postby berlinetta » Mon May 23, 2022 9:44 pm

Hello ESP_Sprite...

I have been digging into the spi_slave code module, and I discovered a commented section of code within the spi_slave_initialize() function as seen below:
  1.     spicommon_cs_initialize(host, slave_config->spics_io_num, 0, !bus_is_iomux(spihost[host]));
  2.     // The slave DMA suffers from unexpected transactions. Forbid reading if DMA is enabled by disabling the CS line.
  3.     if (use_dma) freeze_cs(spihost[host]);
In my configuration, we are using DMA, so this code would be executed. The spi_intr() routine also "freezes" the chip select activity when the function is entered, and then it will "restore" the CS before it exits if another transaction is queued. I was expecting to see a "restore" call in the spi_slave_queue_trans() function, but it does not contain that call. Perhaps something deeper in the queue management is "restoring" the chip select?

In my case, it appears the DMA-driven operation is not qualifying any of the clocks with the CS signal. Is it possible the freeze is forcing the signal HIGH, and some register configuration is interpreting the chip select as active high? If the "restore" is never called (leaving the signal high), an interpretation of an active-high chip select could explain the behavior I am seeing.

I have verified that the SPI_PIN_REG register is configured to enable CS0 only (CS1 and CS2 are disabled - not used). I cannot find any other configuration register settings which might influence chip select activity when operating in slave mode. The SPI_SLAVE_REG contains a SPI_CS_I_MODE configuration, but it is marked as "reserved" with no other explanation. Is there something else I should check / verify?

Best Regards,
Mark

berlinetta
Posts: 41
Joined: Tue May 21, 2019 8:33 pm

Re: Problem Using SPI Slave Mode With DMA

Postby berlinetta » Tue May 24, 2022 8:54 pm

Hello ESP_Sprite,

I have been doing some more research, and I have determined the VSPICS0_in signal is considered peripheral signal #68. This signal is configured with the GPIO_FUNC68_IN_SEL_CFG_REG register, located at address 0x3FF44240.

Following initialization of my code, which includes the queueing of the first slave SPI transaction, this register reports its content as 0x00000038. According to the data sheet, this indicates the signal is being forced to a HIGH state, which in turn should be presenting an INACTIVE chip select state to the SPI3 peripheral. This follows what I saw the code doing in the spi_slave module - namely, invoking the freeze_cs() call when the SPI peripheral is initialized with a specified DMA channel. I am still trying to determine where the code would call the restore_cs() function to re-activate the normal Chip Select operation once a transfer has been queued. Does the enabling of the SPI interrupt at the end of the queue function automatically trigger an interrupt? If so, the interrupt handler does appear to be the only spot the restore_cs() function is called - and only if there is a queued transaction ready.

I have characterized the operation of the SPI module when in this state - it will trigger the post transfer callback following 8192 clocks from the master (equivalent to the transfer of 1024 bytes). This happens to be the length specified in the queued transaction.

At some previous point when I had SPI communications working with the ESP32 (accesses to the other serial flash device on my bus were disabled) I was seeing the SPI peripheral function as expected... The de-activation of the chip select would trigger the post-transfer callback and my code would queue the next slave transfer request as expected.

So based on observations of the peripheral activity during my testing, I am presuming that the SPI peripheral will declare a transfer done when either the associated chip select is de-activated or when the peripheral has seen a quantity of master clocks equivalent to the specified transaction length. Is it possible the peripheral actually operates in this fashion?

I am now attempting to recollect what I had changed in the SPI configuration which suddenly made this behavior change. My platformio.ini file is configured to specify "platform = espressif32@3.0.0", so I should not have encountered any change to the ESP IDF code between these two runs. Have you been able to detect any problems with the source code I have provided?

Best Regards,
Mark

berlinetta
Posts: 41
Joined: Tue May 21, 2019 8:33 pm

Re: Problem Using SPI Slave Mode With DMA

Postby berlinetta » Wed May 25, 2022 2:00 pm

ESP_Sprite,

Another strange thing... if I manually modify the GPIO_FUNC68_IN_SEL_CFG_REG to change from a value of 0x38 (forced HIGH) to a value of 0x05 (routes GPIO5 directly to the VSPICS0_in signal), I get no change in behavior on the SPI peripheral. I still need to clock the bus 8192 times to get the post-transfer event to trigger.

Still scratching my head as to what I am doing wrong... :(

Best Regards,
Mark

ESP_michael
Posts: 37
Joined: Mon Aug 28, 2017 10:25 am

Re: Problem Using SPI Slave Mode With DMA (IDFGH-7468)

Postby ESP_michael » Fri May 27, 2022 8:57 am

Add issue track ID.

ESP_michael
Posts: 37
Joined: Mon Aug 28, 2017 10:25 am

Re: Problem Using SPI Slave Mode With DMA (IDFGH-7468)

Postby ESP_michael » Fri May 27, 2022 9:00 am

Hi @berlinetta,
Firstly I would like to ask... Are you trying to let SPI flash driver and SPI slave driver taking turns using the same VSPI peripheral?
If so, I'm supurised the driver you are using didn't complain about anything..

Michael

berlinetta wrote:
Fri May 20, 2022 5:37 pm
Hello All,

I have been attempting to get the ESP32 to function as a slave on the SPI bus of my design. The bus is currently occupied by a serial flash device as well (separate chip select signal, of course). I have poured over all the documentation, errata and sample code to get this functionality going. I believe I have configured the SPI3 peripheral properly, including placing all buffers in DMA capable memory and utilizing the default peripheral pin locations to ensure reliable throughput, yet I am seeing some strange behavior.

If I operate the SPI bus without any attempts to access the other serial flash device, the ESP32 appears to respond as expected. However, if I access the serial flash device at all, proper operation of the ESP32 SPI peripheral is not achieved.

The first thing I noticed is that the ESP32 appears to be driving the MISO pin when its chip select is not active (bus access was intended for the serial flash). This is a fundamental failure of the hardware... a search of the forum does not reveal any other complaints about this, so I have to wonder if other users are using a dedicated bus for SPI traffic?

Secondly, I noticed that my post-transfer callback is being triggered after 8192 clocks (1024 bytes) when the bus traffic is 100% serial flash transactions (chip select for the ESP32 is not active). I can replicate this phenomenon if I simply perform a READ of the serial flash contents (the contents of the serial flash are non-zero). The post-transfer callback is receiving spi_slave_transaction_t structure information indicating the total data length was 8192 bits and the transaction data length was 8192 bits, yet the data pointed to by the rx_buffer pointer is mostly zeroes and not representative of the SPI bus data presented by the serial flash. I later determined that the data included some content which reflects the attempted flash addresses that are accessed.

In an experiment, I read 128-byte blocks of data from the serial flash, starting at address 0x00000000. If I execute eight of these operations (total of 1024 bytes read from flash), I get consistent data content in the post transaction callback. A diagnostic dump of this data from my code reveals evidence of what appears to be the incremental address occurring in the data. The triggered event appears to always start with 0x03000000, followed by values of 0x03000080, 0x03000100, 0x03000180, 0x03000200, 0x03000280, 0x03000300 and 0x03000380. These address values correlate with the base address the host was attempting to access on each 128-byte read. A capture of the first block of data from my diagnostic code is shown below.

Could this be a clue to what the hardware is doing internally?
Can anyone corroborate this behavior when operating on a multi-slave bus configuration?
Is there any work-around?

Thanks in advance!
Mark
  1. SPI3 length: 8192, trans length: 8192, pTx: 1073539608, pRx: 1073543708, RxData:
  2. 03000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000280000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Who is online

Users browsing this forum: No registered users and 46 guests