Just don't add it to the primary heap:  Add the region below and the region above the DMA buffers with two calls to mm_addregion().

addregion (DMABUF_END, SRAM4_END - DMABUF_END, "SRAM4-A");

addregion (SRAM4_START, DMABUF_START- SRAM4_START, "SRAM4-B");

You can also create a different heap for the DMA region (or probably better, and granule allocator for the DMA region.  See mm/mm_gram.)  Or just use the DMA region directly if you don't need allocations from it.  For big things like frame buffers, I usually put them at the beginning or end of the memory region to minimize the number of regions.

addregion (SRAM4_START, SRAM4_END - SRAM4_START - 128, "SRAM4");

You can see how that was done for most boards that need framebuffers like SAMA5Dx (the boards use Kconfig settings to determine the size of the memory regions).

This DMA buffer is rather tiny.

I suppose memalign() won't work because you can't guarantee that the memory lies in SRAM4.

On 3/25/2021 5:27 PM, Nathan Hartman wrote:
tl;dr: How do I statically allocate DMA buffers and prevent the heap
from overlapping them?

Details: On STM32H7, BDMA can only reach SRAM4 (64 KB starting at
0x3800:0000). The armv7m DCache line size means the buffers must be
allocated on a cache line boundary. So a DMA buffer must meet 2
requirements: (1) be in SRAM4, and (2) be aligned and padded to 32
byte increments.

Ok, I can do this easily with:

static volatile uint8_t my_bdma_buf[128] __attribute__ ((section
(".sram4"))) __attribute__ ((aligned (32)));

or equivalent.

But then the heap will overlap this memory and my buffer will be
clobbered because arch/arm/src/stm32h7/stm32_allocateheap.c
arm_addregion() does this:

addregion (SRAM4_START, SRAM4_END - SRAM4_START, "SRAM4");

Looking into it, I see that in arch/arm/src/stm32h7/stm32_spi.c,
g_spi6_txbuf[] and g_spi6_rxbuf[] are declared like this:

static uint8_t g_spi6_txbuf[SPI6_DMABUFSIZE_ADJUSTED]
SPI6_DMABUFSIZE_ALGN locate_data(".sram4");
static uint8_t g_spi6_rxbuf[SPI6_DMABUFSIZE_ADJUSTED]
SPI6_DMABUFSIZE_ALGN locate_data(".sram4");

So they will suffer that same problem. (I'm pretty sure that's a bug,
unless I missed something.)

I discovered this the hard way, i.e., on my board, NSH suddenly
wouldn't start. In reality, it does start but it turns out that when
it tries to print the greeting message, it fails on the very first
character ('\n') and exits. Why? I single-stepped all the way into the
guts of the serial logic, and found that in fs/vfs/fs_write.c,
file_write(), it fetches the inode and tests if it's valid (!inode ||
!inode->u.i_ops || !inode->u.i_ops->write) or returns -EBADF. That's
where it fails, because it seems that the inode is allocated at
0x3800:0070, right on top of my BDMA buffers!

I don't know which question to ask, but I think it's one of these:

(1) Is there a standard(-ish) way to allocate a buffer that is both
aligned to a specified boundary AND in a specified region of memory?

(2) Is there a standard way to make a hole in the heap so that nothing
else in NuttX will overwrite the DMA buffer?

(Yes, I see that I can fiddle with CONFIG_MM_REGIONS, but then only
the 512 KB of AXI SRAM will be in the heap, and none of the other
regions, SRAM1,2,3, etc., and I don't think I want that.)

Thanks,
Nathan

Reply via email to