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