Anybus CompactCom B40 SPI

Hello together

I use the Brick Module (EtherCAT) in combination with the STM32H743ZI. The SPI interface uses DMA. And this again requires that the number of data bytes are divisible by 32 without remainder.

This means concretely that (spi_drv_iSpiFrameSize << 1) % 32 must be 0. Since spi_drv_iSpiFrameSize depends on spi_drv_iPdSize, it must be padded with dummy data depending on spi_drv_iPdSize.

Is there a good solution where and what has to be adjusted in the driver?

Thanks a lot
Jakob

Translated with www.DeepL.com/Translator (free version)

Hello,

This might be a bit out of typical support scope by discussing this with a developer he had recent worked on a similar issue. He made the following suggestion:

To support a 32-byte aligned buffer interface with the SPI driver and potentially D-Caching you could make the following changes:

static drv_SpiMosiFrameType         spi_drv_sMosiFrame __attribute__((aligned (32)));           /* Place holder for the MISO frame. */
static drv_SpiMosiFrameType         spi_drv_sMosiFrame __attribute__((aligned (32)));           /* Place holder for the MISO frame. */
  1. In abcc_sys_adapt.c, update ABCC_SYS_SpiSendReceive() to clean/invalidate D-Cache.

    /* Clean D-cache /
    SCB_CleanDCache_by_Addr( (uint32_t
    )( ( (uint32_t)pxSendDataBuffer ) & ~(uint32_t)0x1F ), iLength + 32 );
    /* Invalidate D-cache before reception /
    SCB_InvalidateDCache_by_Addr( (uint32_t
    )( ( (uint32_t)pxReceiveDataBuffer ) & ~(uint32_t)0x1F ), iLength + 32 );
    ret = HAL_SPI_TransmitReceive_DMA( &ABCC_SYS_SPI_HANDLE, ( uint8_t* )pxSendDataBuffer, ( uint8_t* )pxReceiveDataBuffer, iLength );

Hello,

Thank you for your answer anyway.

Are there any problems if after the PAD data of the MOSI frame and the CRC of the MISO frame further data are clocked? Please see the attached figrue.

Thank you
Jakob

What changes did you make? It looks like you are sending extra unnecessary data. What are you using for the transmit length? There could be an error in how you changed the driver.

Deryck

Hello,

We are using Keil uVision with the CMSIS drivers. As I wrote in my first post, the number of bytes must be a multiple of 32. Therefore unnecessary data is sent. Is this a problem for the NP40?

Thank you
Jakob

Hi Jakob,

I am the developer that Deryck was talking to regarding this issue. Could you explain why you are required to send SPI data in multiples of 32 bytes?

I had previously made a quick port to the STM32H7 using SPI with DMA and D-Cache enabled. In this configuration I was able to send the exact number of SPI bytes specified in the input arguments of ABCC_SYS_SpiSendReceive(). If this matches your configuration, then I would suggest reading through this article which covers various approaches for dealing with DMA and L1-cache:

  • It is worth noting the warnings in the section titled: “Explanation: handling DMA buffers with D-Cache enabled”. I believe the changes mentioned in Deryck’s post above should be sufficient, but you may want to consider additional adaptions around the spi_drv_sMosiFrame and spi_drv_sMisoFrame alignments to avoid placing other variables on the cache lines used by these buffers.

Also see this application note on M7-series caching:

  • It is worth pointing out that this app note recommends using non-cacheable regions for DMA buffers. If possible, you may want to reorganize your memory structure, but I have not encountered any (performance) issues with using the ABCC on D-Cached memory.
  • This document also states: “The L1-caches on all Cortex®-M7s are divided into lines of 32 bytes”. This is the reason behind the 32-byte alignment requirement.

I assume this SPI_Transfer() routine is your own implementation. Are you using our ABCC SDK which includes a port for STM3240G-EVAL as a reference?

  • Depending what exactly num and spi->xfer>dataSize correspond to, I can see there being a potential issue with this framework. If spi->xfer>dataSize corresponds to the minimum byte size to transfer via SPI and perhaps in your case you had set this to a value greater than 2 (the ABCC is designed for 8-bit or 16-bit char systems), such as 32, this would force you to send in multiples of 32 bytes. But this seems like a soft-issue imposed by your particular implementation and not a limitation of either ST’ HAL Library or the STM32H7 SPI with DMA functionality.
  • To be clear you should be setting your STM32H7’s SPI instance to a data size of either 8-bit or 16-bit and adapting the iLength parameter in ABCC_SYS_SpiSendReceive() accordingly to ensure the proper SPI packet length. For instance, if using CubeMX, your generated MX_SPI1_Init() may have something like:

hspi1.Init.DataSize = SPI_DATASIZE_8BIT;

or

hspi1.Init.DataSize = SPI_DATASIZE_16BIT;

The abcc_sys_adapt.c implementation used in STM3240G-EVAL port should more or less be what you need for the H7 with the addition of cache maintenance, as Deryck already specified above. Of course you also need to handle porting the physical pin mappings, peripheral configurations, etc. to match your actual target configuration. I have done simple ports to both STM32F7 and STM32H7 using this project as the basis. Also note that in this implementation, the initialization of the SPI instance that is normally handled by MX_SPI1_Init() is handled by ABCC_SYS_SpiInit().

Looking at the logic behind SCB_CleanDCache_by_Addr() and SCB_InvalidateDCache_by_Addr() you will see that linesize is 32 (matching what was mentioned in the app note). Each iteration in the while-loop will reduce op_size by this value until it is < 0. The “+ 32”, found in both the clean and invalidate function calls in ABCC_SYS_SpiSendReceive(), serves to counter this logic and ensures that the entire buffer space for spi_drv_sMosiFrame and spi_drv_sMisoFrame are cleaned/invalidated prior to calling HAL_SPI_TransmitReceive_DMA().

The main takeaway points:

  • The cache maintenance logic needs to clean/invalidate 32-byte aligned addresses.
  • The SPI buffers (spi_drv_sMosiFrame and spi_drv_sMisoFrame) used in ABCC_SYS_SpiSendReceive() should be 32-byte aligned.
  • The DMA controller on the H7 does not use caching and interfaces directly with (non-TCM) RAM (as can be seen in Figure 1 of the application note above).
  • SPI peripheral data size should be either 8-bit or 16-bit.
  • Consider adding pad after the SPI buffers’ declarations.
  • Consider using DMA on non-cached memory regions.

If you still feel you need to have an answer to your question on whether or not sending additional bytes in the SPI packet are allowed, then I will let Deryck handle getting an answer on this, as I do not think this is formally stated anywhere in documentation. But as I outlined above, it is not clear to me where you are running into this supposed requirement and I want to be sure that there is a common understanding; perhaps with the information provided above this question becomes irrelevant.

– Jon

Hi Jon

Thank you for your answer.

The reason why I require to send SPI data in multiples of 32 bytes is, the driver we use. The driver SPI_Transfer() is provieded by Keil uVision. And if SPI data is not a multiple of 32 bytes the driver uses HAL_SPI_TransmitReceive_IT() instead of HAL_SPI_TransmitRecieve_DMA(). So SPI_Transfer() is not my own implementation.

About the main takeaway points:

The cache maintenance logic needs to clean/invalidate 32-byte aligned addresses.
The SPI buffers ( spi_drv_sMosiFrame and spi_drv_sMisoFrame ) used in ABCC_SYS_SpiSendReceive() should be 32-byte aligned.

In abcc_spi_drv.c I added the alignment for the MISO and MOSI frame.

attribute((section (".DMA_SRAM1")))
__align(32) static drv_SpiMisoFrameType spi_drv_sMisoFrame;
attribute((section (".DMA_SRAM1")))
__align(32) static drv_SpiMosiFrameType spi_drv_sMosiFrame;

section(".DMA_SRAM1") defines the region in SRAM1. The mapping files shows the following:

.DMA_SRAM1                               0x30000240   Section     1152  abcc_spi_drv.o(.DMA_SRAM1)
spi_drv_sMisoFrame                       0x30000240   Data         576  abcc_spi_drv.o(.DMA_SRAM1)
spi_drv_sMosiFrame                       0x30000480   Data         576  abcc_spi_drv.o(.DMA_SRAM1)

The DMA controller on the H7 does not use caching and interfaces directly with (non-TCM) RAM (as can be seen in Figure 1 of the application note above).

I use SRAM1 and DM1 Stream 2 and 3.

SPI peripheral data size should be either 8-bit or 16-bit.

hspi2.Init.DataSize = SPI_DATASIZE_8BIT;

Consider adding pad after the SPI buffers’ declarations.
I don’t understand exactly what you mean by this.

Consider using DMA on non-cached memory regions.
OK.

Conclusion
The reason for SPI data in multiples of 32 bytes is the CMSIS driver provided by Keil uVision. I will contact Keil and ask if there is another solution here. I have the same problem with the UART driver.

And if I understand it correctly, it should not be a problem if data is still sent after the CRC. Is that correct?

Many thanks for the good support!

Regards
Jakob

Hi Jakob,
We use Keil uVision in our office as well, but I have never used the CMSIS SPI driver personally. I instead use the HAL library directly (which CMSIS appears to use anyways) or in the past I have also written custom low-level drivers.

The reason why I require to send SPI data in multiples of 32 bytes is, the driver we use.

It seems to me that this driver is inherently flawed for this particular use-case. I would suggest raising the issue to Keil for them to figure out a way to improve the practicality of this driver when using __DCACHE_PRESENT = 1U. Of course as a workaround you can easily resolve this in their driver by applying similar logic to the recommendations made above or perhaps set __DCACHE_PRESENT = 0U and incorporate the cache maintenance logic yourself in abcc_sys_adapt.c. (NOTE: I only looked through SPI_STM32H7xx.c so I am not sure if there is a larger scope to __DCACHE_PRESENT than what is indicated here).

Example:

  1. Call SPI_Control() such that the following hold true:
  • spi->h->Init.DataSize: This is the HAL layer structure. This value should be either SPI_DATASIZE_8BIT or SPI_DATASIZE_16BIT.
  • spi->xfer->dataSize: This is the the higher-layer CMSIS structure. This value should be either be 1 or 2.
  1. When calling SPI_Transfer(), num should take into account the “Data Size”.

And depending on whether or not you want to modify the CMSIS sources, you could either:
Update the calls SCB_CleanDCache_by_Addr() and SCB_InvalidateDCache_by_Addr() found in SPI_Transfer() and SPI_TransferComplete(), respectively, to include “+ 32” in the last input argument. Or you could incorporate the cache maintenance logic in abcc_sys_adapt.c as outlined in the previous posts.

Food for thought:
It would be extremely limiting to design such an SPI system that requires data transfers to always be in multiples of 32-bytes. Think of how many SPI systems would fail to work with such a requirement. SPI is a bit-level specification and it should always be the goal of the SPI controller implementation to be as flexible as possible to support daisy-chained, bus configurations, different CPHA/CPOL modes, and various register sizes (the vast majority of SPI devices are defined around 8-bit, 16-bit, or 32-bit register sizes). Such a restriction would greatly reduce the flexibility of what you can do on the SPI (complications with daisy-chaining and certain SPI devices that require strict data size lengths to work come to mind). I am currently under the impression that Keil’s requirement in this CMSIS implementation is an error/oversight. The most flexible implementation would be to allow any realizable value in the last argument to HAL_SPI_TransmitReceive_DMA() while requiring the data_out and data_in buffers, used in SPI_Transfer(), to exist on their own isolated cache lines such that there are no side effects with maintenance.

Consider adding pad after the SPI buffers’ declarations.
I don’t understand exactly what you mean by this.

My point here was in regards to cache-line size and the issues that can arise when other data resides on the same cache-line as the buffers which you are performing cache maintenance on. For the most predictable behavior it is advisable to isolate the SPI buffers from other variables so that when you invalidate or clean you are only impacting these buffers and nothing more. But this is likely a non-issue with the current state of the ABCC SPI driver layer.

And if I understand it correctly, it should not be a problem if data is still sent after the CRC. Is that correct?

I believe you are correct, but @deryck_hms will need to get confirmation on this. I believe past customer cases had sent unintended extra data which the system still worked. But that said this could be an undefined behavior and for future proof designs this may be unsupported behavior which we may officially recommend against. Thus, this is a question which the product manager must answer.

Hi Jon

Thank for your support.

I agree with you about Food for thought. The same is true for the UART driver. I will get in touch with Keil.

Thank you.
Jakob

Hello,

I checked with the developers regarding this,

and they confirmed with the current implementation they do not see any issue with extra data after the CRC. He did warn that the specification does not say anything about this, so we can not be 100% sure it will stay like this in the future.

Hello Deryck

Thank you.

Regards Jakob