Allocate 2 buffers in specific address at different memory banks

Baldhead
Posts: 471
Joined: Sun Mar 31, 2019 5:16 am

Allocate 2 buffers in specific address at different memory banks

Postby Baldhead » Tue Oct 29, 2019 10:33 pm

Hi,

I read on "esp32_technical_reference_manual_en.pdf", page 31, this:

"Internally, the SRAM is organized in 32K-sized banks. Each CPU and DMA channel can simultaneously access
the SRAM at full speed, provided they access addresses in different memory banks."


How can i tell to the compiler to allocate 2 buffers in specific address at different memory banks ?
I dont want to use dynamic memory allocation.
I want to use static memory allocation like:

static uint8_t buf0[DMA_SIZE / 2];
static uint8_t buf1[DMA_SIZE / 2];


obs: i use esp-idf framework.

Thank's.

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

Re: Allocate 2 buffers in specific address at different memory banks

Postby ESP_Sprite » Fri Nov 01, 2019 9:56 am

Not easily.... you could either add the banks as reserved regions to the heap allocator somehow and then manually use pointers in there, or mess around with the ld scripts to dump the variables in different regions. Both are hacks. Is there a specific reason you think you need to do this? (Also, is there a specific reason to insist on statically allocated memory for this?)

Baldhead
Posts: 471
Joined: Sun Mar 31, 2019 5:16 am

Re: Allocate 2 buffers in specific address at different memory banks

Postby Baldhead » Fri Nov 01, 2019 8:08 pm

Hi,

"Is there a specific reason you think you need to do this ?"
i want to create two memory buffer of 32768 bytes or 30720 bytes each.
While dma is reading from one buffer(i2s dma), my application can write to another buffer at the same time.
This way the bus will not stall.

i still need to think of some way to switch between the two buffers.


Static allocated memory is less likely to crash in an application that will almost never restart.

Esp-idf leaves 160kB reserved for static memory allocation.
I thought i could tell the compiler where to put my variable in a simple way.
Also i don't know in which addresses are the ram memory banks change.
ie:
buffer_a -> 0x3FFA_E000
buffer_b -> 0x3FFA_E000 + 32768

Thank's.

Baldhead
Posts: 471
Joined: Sun Mar 31, 2019 5:16 am

Re: Allocate 2 buffers in specific address at different memory banks

Postby Baldhead » Fri Nov 01, 2019 8:51 pm

I would like to do something like this:

Code: Select all

 
    #define DMA_MAX (4096-4)
	
    volatile lldesc_t* head_a;    // dma descriptor a.
    volatile lldesc_t* head_b;    // dma descriptor b. 
	
    lldesc_t dma_desc_buf_a[8];     
    lldesc_t dma_desc_buf_b[8];    
	
    head_a = (lldesc_t*) &dma_desc_buf_a[0];
    head_b = (lldesc_t*) &dma_desc_buf_b[0];
	
	
    // buffer_a address 0x3FFA_E000 
    // buffer_b  address 0x3FFA_E000 + 32768	
    uint8_t buffer_a[30720];    // 32KBytes = 32768 bytes   ????  . Especificar o endereço de um banco de memoria de 32K.
    uint8_t buffer_b[30720];    // 32KBytes = 32768 bytes   ????  . Especificar o endereço de um banco de memoria de 32K.
		
	
    // 1/10 de um display( 480 linhas por 320 colunas, modo retrato ), 16 bits por pixel(2 bytes) eh igual a 48 linhas com 320 colunas.
    // 48 * 320 = 15360 pixels * 2 bytes/pixel = 15360*2 = 30720 bytes. 
    // 7 descriptors com 4092 bytes = 28644 bytes.
    // 1 descriptor  com 2076 bytes = 2076 bytes.
    // 28644 + 2076 = 30720 bytes.	
		
    // I will do the same with dma_desc_buf_b[i]
    	
    void createStaticLinkedList()
    {

        for ( int i = 0 ; i < 8-1 ; i++ )    // 0 à 6.
	{
	
	    dma_desc_buf_a[i].size = 4092;    // The size must be word-aligned. The size of the buffer corresponding to the current linked list.
            dma_desc_buf_a[i].length = 4092;  // The number of valid bytes in the buffer corresponding to the current linked list. The field value indicates the number of bytes to be transferred to/from the buffer denoted by word DW1.	
            dma_desc_buf_a[i].offset = 0;	
	    dma_desc_buf_a[i].sosf = 0;	
	    dma_desc_buf_a[i].eof = 0;	       // indica que não eh o final da lista. 
	    dma_desc_buf_a[i].owner = 1;      // the allowed operator is the DMA controller.		
	    dma_desc_buf_a[i].buf = (uint8_t*) ( ( &buffer_a ) + ( 4092 * i ) );  		
	    dma_desc_buf_a[i].qe.stqe_next = ( lldesc_t* ) &dma_desc_buf_a[ i+1 ];
	
        }
	
	dma_desc_buf_a[7].size = 2076;    // The size must be word-aligned. Eu posso definir 4092 aqui, porem o dma soh ira enviar 2076 bytes. Se eu definir 4092 aqui estarei alocando um banco de memoria ram(32KBytes) somente para este uso.
	dma_desc_buf_a[7].length = 2076;	
	dma_desc_buf_a[7].offset = 0;	
	dma_desc_buf_a[7].sosf = 0;	
	dma_desc_buf_a[7].eof = 1;	    // indica que eh o final da lista. 
	dma_desc_buf_a[7].owner = 1;    // the allowed operator is the DMA controller.		
	dma_desc_buf_a[7].buf = (uint8_t*) ( ( &buffer_a ) + ( 4092 * 7 ) );  		
	dma_desc_buf_a[7].qe.stqe_next = ( lldesc_t* ) NULL;    // ultimo nó da lista ligada.	
}	
In "esp32_technical_reference_manual_en", page 116 this is an explanation and a "figure 13: Linked List Structure".

I would like to know if this Linked List Structure stay in the hardware.
if it is in hardware, the i2s0 module has one or two such structures like this ?
ie:
I know that i can define in software as many Linked list structures i want (only limited by memory).
But only one such structure will be active at a time, in hardware i believe.

If the i2s0 module have 2 "Linked List Structure" in hardware, i could just switch the head of two linked lists structures created in software.

Or should i switch the 2 software linked list head by changing this pointer: "I2S_OUTLINK_ADDR" The address of first outlink descriptor. (R/W), in register I2S_OUT_LINK_REG (0x0030) ?


Thank's.

Baldhead
Posts: 471
Joined: Sun Mar 31, 2019 5:16 am

Re: Allocate 2 buffers in specific address at different memory banks

Postby Baldhead » Sat Nov 02, 2019 8:24 pm

Hi,

"Also, is there a specific reason to insist on statically allocated memory for this ?"

Another possible reason:

In "esp32_technical_reference_manual_en.pdf", page 37, "3.1 System Reset".
"The ESP32 has three reset levels: CPU reset, Core reset, and System reset. None of these reset levels clear the
RAM."

For example, if i enable watchdog, and watchdog reset based in some kind of reset set by me from the options provided, and i want to restore the state of my application before reset, as ram is not reset, i only restore the values from ram.

What assures me that after resetting, the address of my dynamically allocated memory will be the same ?
I think that with the statically allocated memory the address will not change.

Thank's.

Baldhead
Posts: 471
Joined: Sun Mar 31, 2019 5:16 am

Re: Allocate 2 buffers in specific address at different memory banks

Postby Baldhead » Sun Nov 03, 2019 3:51 pm

Hi,

With inline assembly i could allocate the buffer with static memory allocation ?

Thank's.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Allocate 2 buffers in specific address at different memory banks

Postby ESP_Angus » Mon Nov 04, 2019 4:10 am

Hi Baldhead,
Baldhead wrote:
Fri Nov 01, 2019 8:08 pm
While dma is reading from one buffer(i2s dma), my application can write to another buffer at the same time.
This way the bus will not stall.

i still need to think of some way to switch between the two buffers.
I believe the reason the current I2S driver doesn't already support this is that usually this isn't the limiting bandwidth bottleneck, even with the peripheral and the CPU both accessing the same bank. This is because the banks are clocked at the CPU speed, and it's rare for the CPU to be issuing loads or stores to RAM on every instruction, rather than (for example) every second cycle due to incrementing some counter register, testing a branch, etc. And I2S runs at the APB frequency which is usually 2/3x slower than the CPU.

I'd suggest getting to a point where your new driver works, and see if the performance is good enough even with possible bus contention. If you see that you really need this at that point, we can probably find a way for you to make it work.
Baldhead wrote:
Fri Nov 01, 2019 8:08 pm
Static allocated memory is less likely to crash in an application that will almost never restart.
Agree that this is a common axiom of embedded development, especially on smaller microcontroller based systems. Please excuse my bluntness, but consider also that rewriting a lot of stable code from scratch will probably lead to frequent crashes by itself, until the new code is fully debugged. (This isn't addressed at you personally, it's more of a general truism.)
Baldhead wrote:
Fri Nov 01, 2019 8:08 pm
Also i don't know in which addresses are the ram memory banks change.
ie:
buffer_a -> 0x3FFA_E000
buffer_b -> 0x3FFA_E000 + 32768
Consider the address modulo 0x8000 (32KB). So in this case the buffers would be partially in the same bank.
Baldhead wrote:
Fri Nov 01, 2019 8:08 pm
For example, if i enable watchdog, and watchdog reset based in some kind of reset set by me from the options provided, and i want to restore the state of my application before reset, as ram is not reset, i only restore the values from ram.
RAM is not reset by the hardware, but app stills needs to be loaded into RAM by the ROM & IDF bootloaders and these both use some RAM to do that. So there is no concrete guarantee that DRAM won't be overwritten during the early boot process.

You can use the _NOINIT_ATTR to prevent static memory being rewritten (or re-zeroed) during the boot process. This is only guaranteed to work as a performance tweak though (startup code doesn't waste clock cycles zeroing the buffers), rather than a guarantee the values will still be there after reset:
https://docs.espressif.com/projects/esp ... m-data-ram

Values which absolutely need to be persisted between soft resets can be placed in RTC memory.
Baldhead wrote:
Fri Nov 01, 2019 8:08 pm
With inline assembly i could allocate the buffer with static memory allocation ?
This isn't necessary. Static buffers can be allocated using standard C approaches.
Baldhead wrote:
Fri Nov 01, 2019 8:08 pm
Or should i switch the 2 software linked list head by changing this pointer: "I2S_OUTLINK_ADDR" The address of first outlink descriptor. (R/W), in register I2S_OUT_LINK_REG (0x0030) ?
Correct. The descriptors themselves live in DRAM, the address registers tell the peripheral where to access them.

Baldhead
Posts: 471
Joined: Sun Mar 31, 2019 5:16 am

Re: Allocate 2 buffers in specific address at different memory banks

Postby Baldhead » Mon Nov 04, 2019 11:17 pm

Hi ESP_Angus,

Today i tested the example "https://github.com/espressif/esp-iot-so ... /i2s_lcd.c", and i checked that the function "i2s_lcd_write_data" is self-locking (the function only returns after sending all data through i2s).
This way the use of dma is practically useless.

Would you have any suggestions?

Thank's.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Allocate 2 buffers in specific address at different memory banks

Postby ESP_Angus » Tue Nov 05, 2019 6:37 am

Hi Baldhead,

I'm not overly familiar with this code, but it appears if you set timetowait to 0 then it will only copy to the DMA buffer and then trigger the DMA operation, but not wait for it to complete. This lets you start filling the other buffer while the previous operation finishes via DMA.

Using CPU to copying data into each DMA framebuffer is still not the most CPU-efficient way to manage this though, so I imagine there would be ways to optimise the driver so that the caller's buffer is used directly for DMA.

Baldhead
Posts: 471
Joined: Sun Mar 31, 2019 5:16 am

Re: Allocate 2 buffers in specific address at different memory banks

Postby Baldhead » Tue Nov 05, 2019 7:02 pm

Hi ESP_Angus,

This is one of the reasons i want to write my own driver.

Unfortunately the espressif documentation is very bad.

Thank's.

Who is online

Users browsing this forum: Google [Bot], ok-home and 142 guests