soc: Allow components to reserve fixed memory ranges that they need

No longer necessary to keep all reserved addresses in 'soc'.

Means 'soc' does not need to know about 'bt', for example.

Also means that Bluetooth can be enabled in config without any memory being reserved for BT
controller. Only if code calling the BT controller is linked in, will this memory be reserved...
This commit is contained in:
Angus Gratton 2018-06-22 17:32:58 +10:00 committed by bot
parent f2f2744b29
commit a5ae5ac4b3
7 changed files with 283 additions and 92 deletions

View File

@ -149,6 +149,11 @@ static btdm_dram_available_region_t btdm_dram_available_region[] = {
{ESP_BT_MODE_BTDM, 0x3ffbdb28, 0x3ffc0000},
};
/* Reserve the full memory region used by Bluetooth Controller,
some may be released later at runtime. */
SOC_RESERVE_MEMORY_REGION(0x3ffb0000, 0x3ffc0000, bt_hardware_shared_mem);
SOC_RESERVE_MEMORY_REGION(0x3ffae6e0, 0x3ffaff10, rom_bt_data);
#if CONFIG_SPIRAM_USE_MALLOC
typedef struct {
QueueHandle_t handle;

View File

@ -52,6 +52,7 @@ SECTIONS
/* Send .iram0 code to iram */
.iram0.vectors :
{
_iram_start = ABSOLUTE(.);
/* Vectors go to IRAM */
_init_start = ABSOLUTE(.);
/* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */
@ -85,11 +86,7 @@ SECTIONS
*(.init.literal)
*(.init)
_init_end = ABSOLUTE(.);
/* This goes here, not at top of linker script, so addr2line finds it last,
and uses it in preference to the first symbol in IRAM */
_iram_start = ABSOLUTE(0);
} > iram0_0_seg
} > iram0_0_seg
.iram0.text :
{
@ -111,8 +108,9 @@ SECTIONS
*libgcov.a:(.literal .text .literal.* .text.*)
INCLUDE esp32.spiram.rom-functions-iram.ld
_iram_text_end = ABSOLUTE(.);
_iram_end = ABSOLUTE(.);
} > iram0_0_seg
.dram0.data :
{
_data_start = ABSOLUTE(.);
@ -231,6 +229,11 @@ SECTIONS
*(.xt_except_desc_end)
*(.dynamic)
*(.gnu.version_d)
/* Addresses of memory regions reserved via
SOC_RESERVE_MEMORY_REGION() */
soc_reserved_memory_region_start = ABSOLUTE(.);
KEEP (*(.reserved_memory_address))
soc_reserved_memory_region_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);

View File

@ -52,74 +52,21 @@ void heap_caps_enable_nonos_stack_heaps()
}
}
//Modify regions array to disable the given range of memory.
static void disable_mem_region(soc_memory_region_t *regions, intptr_t from, intptr_t to)
{
//Align from and to on word boundaries
from = from & ~3;
to = (to + 3) & ~3;
for (int i = 0; i < soc_memory_region_count; i++) {
soc_memory_region_t *region = &regions[i];
intptr_t regStart = region->start;
intptr_t regEnd = region->start + region->size;
if (regStart >= from && regEnd <= to) {
//Entire region falls in the range. Disable entirely.
regions[i].type = -1;
} else if (regStart >= from && regEnd > to && regStart < to) {
//Start of the region falls in the range. Modify address/len.
intptr_t overlap = to - regStart;
region->start += overlap;
region->size -= overlap;
if (region->iram_address) {
region->iram_address += overlap;
}
} else if (regStart < from && regEnd > from && regEnd <= to) {
//End of the region falls in the range. Modify length.
region->size -= regEnd - from;
} else if (regStart < from && regEnd > to) {
//Range punches a hole in the region! We do not support this.
ESP_EARLY_LOGE(TAG, "region %d: hole punching is not supported!", i);
regions->type = -1; //Just disable memory region. That'll teach them!
}
}
}
/*
Warning: These variables are assumed to have the start and end of the data and iram
area used statically by the program, respectively. These variables are defined in the ld
file.
*/
extern int _data_start, _heap_start, _init_start, _iram_text_end;
/*
Initialize the heap allocator. We pass it a bunch of region descriptors, but we need to modify those first to accommodate for
the data as loaded by the bootloader.
ToDo: The regions are different when stuff like trace memory, BT, ... is used. Modify the regions struct on the fly for this.
Same with loading of apps. Same with using SPI RAM.
*/
/* Initialize the heap allocator to use all of the memory not
used by static data or reserved for other purposes
*/
void heap_caps_init()
{
/* Copy the soc_memory_regions data to the stack, so we can
manipulate it. */
soc_memory_region_t regions[soc_memory_region_count];
memcpy(regions, soc_memory_regions, sizeof(soc_memory_region_t)*soc_memory_region_count);
//Disable the bits of memory where this code is loaded.
disable_mem_region(regions, (intptr_t)&_data_start, (intptr_t)&_heap_start); //DRAM used by bss/data static variables
disable_mem_region(regions, (intptr_t)&_init_start, (intptr_t)&_iram_text_end); //IRAM used by code
// Disable all regions reserved on this SoC
for (int i = 0; i < soc_reserved_region_count; i++) {
disable_mem_region(regions, soc_reserved_regions[i].start,
soc_reserved_regions[i].end);
}
/* Get the array of regions that we can use for heaps
(with reserved memory removed already.)
*/
size_t num_regions = soc_get_available_memory_region_max_count();
soc_memory_region_t regions[num_regions];
num_regions = soc_get_available_memory_regions(regions);
//The heap allocator will treat every region given to it as separate. In order to get bigger ranges of contiguous memory,
//it's useful to coalesce adjacent regions that have the same type.
for (int i = 1; i < soc_memory_region_count; i++) {
for (int i = 1; i < num_regions; i++) {
soc_memory_region_t *a = &regions[i - 1];
soc_memory_region_t *b = &regions[i];
if (b->start == a->start + a->size && b->type == a->type ) {
@ -131,7 +78,7 @@ void heap_caps_init()
/* Count the heaps left after merging */
size_t num_heaps = 0;
for (int i = 0; i < soc_memory_region_count; i++) {
for (int i = 0; i < num_regions; i++) {
if (regions[i].type != -1) {
num_heaps++;
}
@ -145,7 +92,7 @@ void heap_caps_init()
size_t heap_idx = 0;
ESP_EARLY_LOGI(TAG, "Initializing. RAM available for dynamic allocation:");
for (int i = 0; i < soc_memory_region_count; i++) {
for (int i = 0; i < num_regions; i++) {
soc_memory_region_t *region = &regions[i];
const soc_memory_type_desc_t *type = &soc_memory_types[region->type];
heap_t *heap = &temp_heaps[heap_idx];

View File

@ -1,5 +1,6 @@
# currently the only SoC supported; to be moved into Kconfig
SOC_NAME := esp32
COMPONENT_SRCDIRS := $(SOC_NAME)
COMPONENT_SRCDIRS := $(SOC_NAME) src/
COMPONENT_ADD_INCLUDEDIRS := $(SOC_NAME)/include include

View File

@ -60,8 +60,10 @@ const soc_memory_type_desc_t soc_memory_types[] = {
{ "PID5DRAM", { MALLOC_CAP_PID5|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
{ "PID6DRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
{ "PID7DRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false},
#ifdef CONFIG_SPIRAM_SUPPORT
//Type 15: SPI SRAM data
{ "SPIRAM", { MALLOC_CAP_SPIRAM|MALLOC_CAP_DEFAULT, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false, false},
#endif
};
const size_t soc_memory_type_count = sizeof(soc_memory_types)/sizeof(soc_memory_type_desc_t);
@ -73,7 +75,9 @@ Because of requirements in the coalescing code which merges adjacent regions, th
from low to high start address.
*/
const soc_memory_region_t soc_memory_regions[] = {
#ifdef CONFIG_SPIRAM_SUPPORT
{ 0x3F800000, 0x400000, 15, 0}, //SPI SRAM, if available
#endif
{ 0x3FFAE000, 0x2000, 0, 0}, //pool 16 <- used for rom code
{ 0x3FFB0000, 0x8000, 0, 0}, //pool 15 <- if BT is enabled, used as BT HW shared memory
{ 0x3FFB8000, 0x8000, 0, 0}, //pool 14 <- if BT is enabled, used data memory for BT ROM functions.
@ -126,9 +130,8 @@ const size_t soc_memory_region_count = sizeof(soc_memory_regions)/sizeof(soc_mem
These are removed from the soc_memory_regions array when heaps are created.
*/
const soc_reserved_region_t soc_reserved_regions[] = {
{ 0x40070000, 0x40078000 }, //CPU0 cache region
{ 0x40078000, 0x40080000 }, //CPU1 cache region
SOC_RESERVE_MEMORY_REGION(0x40070000, 0x40078000, cpu0_cache);
SOC_RESERVE_MEMORY_REGION(0x40078000, 0x40080000, cpu1_cache);
/* Warning: The ROM stack is located in the 0x3ffe0000 area. We do not specifically disable that area here because
after the scheduler has started, the ROM stack is not used anymore by anything. We handle it instead by not allowing
@ -146,27 +149,21 @@ const soc_reserved_region_t soc_reserved_regions[] = {
list entries happen to end up in a region that is not touched by the stack; they can be placed safely there.
*/
{ 0x3ffe0000, 0x3ffe0440 }, //Reserve ROM PRO data region
{ 0x3ffe4000, 0x3ffe4350 }, //Reserve ROM APP data region
SOC_RESERVE_MEMORY_REGION(0x3ffe0000, 0x3ffe0440, rom_pro_data); //Reserve ROM PRO data region
SOC_RESERVE_MEMORY_REGION(0x3ffe4000, 0x3ffe4350, rom_app_data); //Reserve ROM APP data region
#if CONFIG_BT_ENABLED
{ 0x3ffb0000, 0x3ffc0000 }, //Reserve BT hardware shared memory & BT data region
{ 0x3ffae000, 0x3ffaff10 }, //Reserve ROM data region, inc region needed for BT ROM routines
#else
{ 0x3ffae000, 0x3ffae6e0 }, //Reserve ROM data region
#endif
SOC_RESERVE_MEMORY_REGION(0x3ffae000, 0x3ffae6e0, rom_data);
#if CONFIG_MEMMAP_TRACEMEM
#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS
{ 0x3fff8000, 0x40000000 }, //Reserve trace mem region
SOC_RESERVE_MEMORY_REGION(0x3fff8000, 0x40000000, trace_mem); //Reserve trace mem region
#else
{ 0x3fff8000, 0x3fffc000 }, //Reserve trace mem region
SOC_RESERVE_MEMORY_REGION(0x3fff8000, 0x3fffc000, trace_mem); //Reserve trace mem region
#endif
#endif
{ 0x3f800000, 0x3fC00000 }, //SPI RAM gets added later if needed, in spiram.c; reserve it for now
};
const size_t soc_reserved_region_count = sizeof(soc_reserved_regions)/sizeof(soc_reserved_region_t);
#ifdef CONFIG_SPIRAM_SUPPORT
SOC_RESERVE_MEMORY_REGION(0x3f800000, 0x3fC00000, spi_ram); //SPI RAM gets added later if needed, in spiram.c; reserve it for now
#endif
#endif /* BOOTLOADER_BUILD */

View File

@ -57,8 +57,55 @@ typedef struct
intptr_t end;
} soc_reserved_region_t;
extern const soc_reserved_region_t soc_reserved_regions[];
extern const size_t soc_reserved_region_count;
/* Use this macro to reserved a fixed region of RAM (hardcoded addresses)
* for a particular purpose.
*
* Usually used to mark out memory addresses needed for hardware or ROM code
* purposes.
*
* Don't call this macro from user code which can use normal C static allocation
* instead.
*
* @param START Start address to be reserved.
* @param END One after the address of the last byte to be reserved. (ie length of
* the reserved region is (END - START) in bytes.
* @param NAME Name for the reserved region. Must be a valid variable name,
* unique to this source file.
*/
#define SOC_RESERVE_MEMORY_REGION(START, END, NAME) \
__attribute__((section(".reserved_memory_address"))) __attribute__((used)) \
static soc_reserved_region_t reserved_region_##NAME = { START, END };
/* Return available memory regions for this SoC. Each available memory
* region is a contiguous piece of memory which is not being used by
* static data, used by ROM code, or reserved by a component using
* the SOC_RESERVE_MEMORY_REGION() macro.
*
* This result is soc_memory_regions[] minus all regions reserved
* via the SOC_RESERVE_MEMORY_REGION() macro (which may also split
* some regions up.)
*
* At startup, all available memory returned by this function is
* registered as heap space.
*
* @note OS-level startup function only, not recommended to call from
* app code.
*
* @param regions Pointer to an array for reading available regions into.
* Size of the array should be at least the result of
* soc_get_available_memory_region_max_count(). Entries in the array
* will be ordered by memory address.
*
* @return Number of entries copied to 'regions'. Will be no greater than
* the result of soc_get_available_memory_region_max_count().
*/
size_t soc_get_available_memory_regions(soc_memory_region_t *regions);
/* Return the maximum number of available memory regions which could be
* returned by soc_get_available_memory_regions(). Used to size the
* array passed to that function.
*/
size_t soc_get_available_memory_region_max_count();
inline static bool IRAM_ATTR esp_ptr_dma_capable(const void *p)
{

View File

@ -0,0 +1,191 @@
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <string.h>
#include "esp_log.h"
#include "soc/soc_memory_layout.h"
static const char *TAG = "memory_layout";
/* These variables come from the linker script,
delimit the start and end of entries created via
SOC_RESERVE_MEMORY_REGION() macro.
*/
extern soc_reserved_region_t soc_reserved_memory_region_start;
extern soc_reserved_region_t soc_reserved_memory_region_end;
/*
These variables have the start and end of the data and static IRAM
area used by the program. Defined in the linker script.
*/
extern int _data_start, _bss_end, _iram_start, _iram_end;
/* static DRAM & IRAM chunks */
static const size_t EXTRA_RESERVED_REGIONS = 2;
static size_t s_get_num_reserved_regions()
{
return ( ( &soc_reserved_memory_region_end
- &soc_reserved_memory_region_start ) +
EXTRA_RESERVED_REGIONS );
}
size_t soc_get_available_memory_region_max_count()
{
/* Worst-case: each reserved memory region splits an available
region in two, so the maximum possible number of regions
is the number of regions of memory plus the number of reservations */
return soc_memory_region_count + s_get_num_reserved_regions();
}
static int s_compare_reserved_regions(const void *a, const void *b)
{
const soc_reserved_region_t *r_a = (soc_reserved_region_t *)a;
const soc_reserved_region_t *r_b = (soc_reserved_region_t *)b;
return (int)r_a->start - (int)r_b->start;
}
/* Initialize a mutable array of reserved regions in 'reserved',
then sort it by start address and check for overlapping
reserved regions (illegal).
*/
static void s_prepare_reserved_regions(soc_reserved_region_t *reserved, size_t count)
{
memcpy(reserved + EXTRA_RESERVED_REGIONS,
&soc_reserved_memory_region_start,
(count - EXTRA_RESERVED_REGIONS) * sizeof(soc_reserved_region_t));
/* Add the EXTRA_RESERVED_REGIONS at the beginning */
reserved[0].start = (intptr_t)&_data_start; /* DRAM used by data+bss */
reserved[0].end = (intptr_t)&_bss_end;
reserved[1].start = (intptr_t)&_iram_start; /* IRAM used by code */
reserved[1].end = (intptr_t)&_iram_end;
/* Sort by starting address */
qsort(reserved, count, sizeof(soc_reserved_region_t), s_compare_reserved_regions);
/* Validity checks */
ESP_EARLY_LOGV(TAG, "reserved range is %p - %p",
&soc_reserved_memory_region_start,
&soc_reserved_memory_region_end);
ESP_EARLY_LOGD(TAG, "Checking %d reserved memory ranges:", count);
for (size_t i = 0; i < count; i++)
{
ESP_EARLY_LOGD(TAG, "Reserved memory range 0x%08x - 0x%08x",
reserved[i].start, reserved[i].end);
reserved[i].start = reserved[i].start & ~3; /* expand all reserved areas to word boundaries */
reserved[i].end = (reserved[i].end + 3) & ~3;
assert(reserved[i].start < reserved[i].end);
if (i < count - 1) {
assert(reserved[i+1].start > reserved[i].start);
if (reserved[i].end > reserved[i+1].start) {
ESP_EARLY_LOGE(TAG, "SOC_RESERVE_MEMORY_REGION region range " \
"0x%08x - 0x%08x overlaps with 0x%08x - 0x%08x",
reserved[i].start, reserved[i].end, reserved[i+1].start,
reserved[i+1].end);
abort();
}
}
}
}
size_t soc_get_available_memory_regions(soc_memory_region_t *regions)
{
soc_memory_region_t *out_region = regions;
/* make a local copy of the "input" regions so we can modify them */
soc_memory_region_t in_regions[soc_memory_region_count];
memcpy(in_regions, soc_memory_regions, sizeof(in_regions));
soc_memory_region_t *in_region = in_regions;
size_t num_reserved = s_get_num_reserved_regions();
soc_reserved_region_t reserved[num_reserved];
s_prepare_reserved_regions(reserved, num_reserved);
/* Go through the "in" regions (full regions, with no reserved
sections removed from them) one at a time, trim off each reserved
region, and then copy them to an out_region once trimmed
*/
ESP_EARLY_LOGD(TAG, "Building list of available memory regions:");
while(in_region != in_regions + soc_memory_region_count) {
soc_memory_region_t in = *in_region;
ESP_EARLY_LOGV(TAG, "Examining memory region 0x%08x - 0x%08x", in.start, in.start + in.size);
intptr_t in_start = in.start;
intptr_t in_end = in_start + in.size;
bool copy_in_to_out = true;
bool move_to_next = true;
for (size_t i = 0; i < num_reserved; i++) {
if (reserved[i].end <= in_start) {
/* reserved region ends before 'in' starts */
continue;
}
else if (reserved[i].start >= in_end) {
/* reserved region starts after 'in' ends */
break;
}
else if (reserved[i].start <= in_start &&
reserved[i].end >= in_end) { /* reserved covers all of 'in' */
ESP_EARLY_LOGV(TAG, "Region 0x%08x - 0x%08x inside of reserved 0x%08x - 0x%08x",
in_start, in_end, reserved[i].start, reserved[i].end);
/* skip 'in' entirely */
copy_in_to_out = false;
break;
}
else if (in_start < reserved[i].start &&
in_end > reserved[i].end) { /* reserved contained inside 'in', need to "hole punch" */
ESP_EARLY_LOGV(TAG, "Region 0x%08x - 0x%08x contains reserved 0x%08x - 0x%08x",
in_start, in_end, reserved[i].start, reserved[i].end);
assert(in_start < reserved[i].start);
assert(in_end > reserved[i].end);
/* shrink this region to end where the reserved section starts */
in_end = reserved[i].start;
in.size = in_end - in_start;
/* update in_region so the 'next' iteration uses the region
after the reserved section */
in_region->size -= (reserved[i].end - in_region->start);
in_region->start = reserved[i].end;
/* add first region, then re-run while loop with the updated in_region */
move_to_next = false;
break;
}
else if (reserved[i].start <= in_start) { /* reserved overlaps start of 'in' */
ESP_EARLY_LOGV(TAG, "Start of region 0x%08x - 0x%08x overlaps reserved 0x%08x - 0x%08x",
in_start, in_end, reserved[i].start, reserved[i].end);
in.start = reserved[i].end;
in_start = in.start;
in.size = in_end - in_start;
}
else { /* reserved overlaps end of 'in' */
ESP_EARLY_LOGV(TAG, "End of region 0x%08x - 0x%08x overlaps reserved 0x%08x - 0x%08x",
in_start, in_end, reserved[i].start, reserved[i].end);
in_end = reserved[i].start;
in.size = in_end - in_start;
}
}
if (copy_in_to_out) {
ESP_EARLY_LOGD(TAG, "Available memory region 0x%08x - 0x%08x", in.start, in.start + in.size);
*out_region++ = in;
}
if (move_to_next) {
in_region++;
}
}
return (out_region - regions); /* return number of regions */
}