ソースを参照

Merge branch 'feature/basic_mmu_framework' into 'master'

mmu: basic mmu driver framework

Closes IDFGH-6659 and IDF-5825

See merge request espressif/esp-idf!19547
Armando (Dou Yiwen) 3 年 前
コミット
ac63c0afd7
33 ファイル変更1095 行追加318 行削除
  1. 5 17
      components/esp_hw_support/esp_memory_utils.c
  2. 2 1
      components/esp_psram/CMakeLists.txt
  3. 204 83
      components/esp_psram/esp_psram.c
  4. 77 0
      components/esp_psram/ext_mem_layout.c
  5. 32 0
      components/esp_psram/ext_mem_layout.h
  6. 5 22
      components/esp_psram/include/esp_private/esp_psram_extram.h
  7. 28 30
      components/esp_psram/include/esp_private/mmu_psram_flash.h
  8. 1 1
      components/esp_psram/linker.lf
  9. 209 113
      components/esp_psram/mmu.c
  10. 76 0
      components/esp_psram/mmu.h
  11. 127 9
      components/esp_psram/mmu_psram_flash.c
  12. 0 1
      components/esp_psram/test_apps/psram/main/CMakeLists.txt
  13. 2 1
      components/esp_psram/test_apps/psram/main/test_himem.c
  14. 74 25
      components/esp_psram/test_apps/psram/main/test_psram.c
  15. 1 0
      components/esp_psram/test_apps/psram/sdkconfig.defaults
  16. 4 0
      components/esp_system/ld/esp32/sections.ld.in
  17. 2 2
      components/esp_system/ld/esp32s2/memory.ld.in
  18. 4 4
      components/esp_system/ld/esp32s2/sections.ld.in
  19. 4 4
      components/esp_system/ld/esp32s3/sections.ld.in
  20. 32 0
      components/hal/esp32/include/hal/mmu_ll.h
  21. 33 0
      components/hal/esp32s2/include/hal/mmu_ll.h
  22. 32 0
      components/hal/esp32s3/include/hal/mmu_ll.h
  23. 8 0
      components/hal/include/hal/mmu_types.h
  24. 4 0
      components/soc/esp32/include/soc/Kconfig.soc_caps.in
  25. 37 3
      components/soc/esp32/include/soc/ext_mem_defs.h
  26. 4 1
      components/soc/esp32/include/soc/soc_caps.h
  27. 4 0
      components/soc/esp32s2/include/soc/Kconfig.soc_caps.in
  28. 33 0
      components/soc/esp32s2/include/soc/ext_mem_defs.h
  29. 3 0
      components/soc/esp32s2/include/soc/soc_caps.h
  30. 4 0
      components/soc/esp32s3/include/soc/Kconfig.soc_caps.in
  31. 40 0
      components/soc/esp32s3/include/soc/ext_mem_defs.h
  32. 3 0
      components/soc/esp32s3/include/soc/soc_caps.h
  33. 1 1
      components/spi_flash/flash_mmap.c

+ 5 - 17
components/esp_hw_support/esp_memory_utils.c

@@ -23,10 +23,7 @@ bool esp_ptr_dma_ext_capable(const void *p)
     return false;
 #endif  //!SOC_PSRAM_DMA_CAPABLE
 #if CONFIG_SPIRAM
-    intptr_t vaddr_start = 0;
-    intptr_t vaddr_end = 0;
-    esp_psram_extram_get_mapped_range(&vaddr_start, &vaddr_end);
-    return (intptr_t)p >= vaddr_start && (intptr_t)p < vaddr_end;
+    return esp_psram_check_ptr_addr(p);
 #else
     return false;
 #endif  //CONFIG_SPIRAM
@@ -44,10 +41,7 @@ bool esp_ptr_byte_accessible(const void *p)
     r |= (ip >= SOC_RTC_DRAM_LOW && ip < SOC_RTC_DRAM_HIGH);
 #endif
 #if CONFIG_SPIRAM
-    intptr_t vaddr_start = 0;
-    intptr_t vaddr_end = 0;
-    esp_psram_extram_get_mapped_range(&vaddr_start, &vaddr_end);
-    r |= (ip >= vaddr_start && ip < vaddr_end);
+    r |= esp_psram_check_ptr_addr(p);
 #endif
     return r;
 }
@@ -58,10 +52,7 @@ bool esp_ptr_external_ram(const void *p)
     return false;
 #endif  //!SOC_SPIRAM_SUPPORTED
 #if CONFIG_SPIRAM
-    intptr_t vaddr_start = 0;
-    intptr_t vaddr_end = 0;
-    esp_psram_extram_get_mapped_range(&vaddr_start, &vaddr_end);
-    return (intptr_t)p >= vaddr_start && (intptr_t)p < vaddr_end;
+    return esp_psram_check_ptr_addr(p);
 #else
     return false;
 #endif  //CONFIG_SPIRAM
@@ -70,10 +61,7 @@ bool esp_ptr_external_ram(const void *p)
 #if CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
 bool esp_stack_ptr_in_extram(uint32_t sp)
 {
-    intptr_t vaddr_start = 0;
-    intptr_t vaddr_end = 0;
-    esp_psram_extram_get_mapped_range(&vaddr_start, &vaddr_end);
-    //Check if stack ptr is in between SOC_EXTRAM_DATA_LOW and SOC_EXTRAM_DATA_HIGH, and 16 byte aligned.
-    return !(sp < vaddr_start + 0x10 || sp > vaddr_end - 0x10 || ((sp & 0xF) != 0));
+    //Check if stack ptr is on PSRAM, and 16 byte aligned.
+    return (esp_psram_check_ptr_addr((void *)sp) && ((sp & 0xF) == 0));
 }
 #endif

+ 2 - 1
components/esp_psram/CMakeLists.txt

@@ -14,7 +14,8 @@ set(srcs)
 if(CONFIG_SPIRAM)
     list(APPEND srcs "esp_psram.c"
                      "mmu.c"
-                     "mmu_psram.c")
+                     "mmu_psram_flash.c"
+                     "ext_mem_layout.c")
 
     if(${target} STREQUAL "esp32")
         list(APPEND srcs "esp32/esp_psram_extram_cache.c"

+ 204 - 83
components/esp_psram/esp_psram.c

@@ -23,9 +23,10 @@
 #include "hal/cache_ll.h"
 #include "esp_private/esp_psram_io.h"
 #include "esp_private/esp_psram_extram.h"
-#include "esp_private/mmu.h"
+#include "esp_private/mmu_psram_flash.h"
 #include "esp_psram_impl.h"
 #include "esp_psram.h"
+#include "mmu.h"
 
 #if CONFIG_IDF_TARGET_ESP32
 #include "esp32/himem.h"
@@ -43,8 +44,14 @@
 #define PSRAM_MODE PSRAM_VADDR_MODE_NORMAL
 #endif
 
-
-#if CONFIG_SPIRAM
+/**
+ * Two types of PSRAM memory regions for now:
+ * - 8bit aligned
+ * - 32bit aligned
+ */
+#define PSRAM_MEM_TYPE_NUM          2
+#define PSRAM_MEM_8BIT_ALIGNED      0
+#define PSRAM_MEM_32BIT_ALIGNED     1
 
 #if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
 extern uint8_t _ext_ram_bss_start;
@@ -56,13 +63,33 @@ extern uint8_t _ext_ram_noinit_start;
 extern uint8_t _ext_ram_noinit_end;
 #endif  //#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
 
-//These variables are in bytes
-static intptr_t s_allocable_vaddr_start;
-static intptr_t s_allocable_vaddr_end;
-static intptr_t s_mapped_vaddr_start;
-static intptr_t s_mapped_vaddr_end;
+typedef struct {
+    intptr_t vaddr_start;
+    intptr_t vaddr_end;
+    size_t size;        //in bytes
+} psram_mem_t;
+
+typedef struct {
+    bool is_initialised;
+    /**
+     * @note 1
+     * As we can't use heap allocator during this stage, we need to statically declare these regions.
+     * Luckily only S2 has two different types of memory regions:
+     * - byte-aligned memory
+     * - word-aligned memory
+     * On the other hand, the type number usually won't be very big
+     *
+     * On other chips, only one region is needed.
+     * So for chips other than S2, size of `regions_to_heap[1]` and `mapped_regions[1]`will always be zero.
+     *
+     * If in the future, this condition is worse (dbus memory isn't consecutive), we need to delegate this context
+     * to chip-specific files, and only keep a (void *) pointer here pointing to those chip-specific contexts
+     */
+    psram_mem_t regions_to_heap[PSRAM_MEM_TYPE_NUM];     //memory regions that are available to be added to the heap allocator
+    psram_mem_t mapped_regions[PSRAM_MEM_TYPE_NUM];      //mapped memory regions
+} psram_ctx_t;
 
-static bool s_spiram_inited;
+static psram_ctx_t s_psram_ctx;
 static const char* TAG = "esp_psram";
 
 
@@ -86,26 +113,28 @@ static void IRAM_ATTR s_mapping(int v_start, int size)
 }
 #endif  //CONFIG_IDF_TARGET_ESP32
 
+
 esp_err_t esp_psram_init(void)
 {
-    if (s_spiram_inited) {
+    if (s_psram_ctx.is_initialised) {
         return ESP_ERR_INVALID_STATE;
     }
-    esp_err_t ret;
+
+    esp_err_t ret = ESP_FAIL;
     ret = esp_psram_impl_enable(PSRAM_MODE);
     if (ret != ESP_OK) {
 #if CONFIG_SPIRAM_IGNORE_NOTFOUND
-        ESP_EARLY_LOGE(TAG, "SPI RAM enabled but initialization failed. Bailing out.");
+        ESP_EARLY_LOGE(TAG, "PSRAM enabled but initialization failed. Bailing out.");
 #endif
         return ret;
     }
-    s_spiram_inited = true;
+    s_psram_ctx.is_initialised = true;
 
     uint32_t psram_physical_size = 0;
     ret = esp_psram_impl_get_physical_size(&psram_physical_size);
     assert(ret == ESP_OK);
 
-    ESP_EARLY_LOGI(TAG, "Found %dMB SPI RAM device", psram_physical_size / (1024 * 1024));
+    ESP_EARLY_LOGI(TAG, "Found %dMB PSRAM device", psram_physical_size / (1024 * 1024));
     ESP_EARLY_LOGI(TAG, "Speed: %dMHz", CONFIG_SPIRAM_SPEED);
 #if CONFIG_IDF_TARGET_ESP32
     ESP_EARLY_LOGI(TAG, "PSRAM initialized, cache is in %s mode.", \
@@ -155,99 +184,168 @@ esp_err_t esp_psram_init(void)
     ESP_EARLY_LOGV(TAG, "after copy .rodata, used page is %d, start_page is %d, psram_available_size is %d B", used_page, start_page, psram_available_size);
 #endif  //#if CONFIG_SPIRAM_RODATA
 
+    /**
+     * For now,
+     * - we only need to use MMU driver when PSRAM is enabled
+     * - MMU driver isn't public
+     *
+     * So we call `esp_mmu_init()` here, instead of calling it in startup code.
+     */
+    esp_mmu_init();
 
     //----------------------------------Map the PSRAM physical range to MMU-----------------------------//
-    intptr_t vaddr_start = mmu_get_psram_vaddr_start();
-    if (vaddr_start + psram_available_size > mmu_get_psram_vaddr_end()) {
-        psram_available_size = mmu_get_psram_vaddr_end() - vaddr_start;
-        ESP_EARLY_LOGV(TAG, "Virtual address not enough for PSRAM, map as much as we can. %dMB is mapped", psram_available_size / 1024 / 1024);
-    }
+    /**
+     * @note 2
+     * Similarly to @note 1, we expect HW DBUS memory to be consecutive.
+     *
+     * If situation is worse in the future (memory region isn't consecutive), we need to put these logics into chip-specific files
+     */
+    size_t total_mapped_size = 0;
+    size_t size_to_map = 0;
+    size_t byte_aligned_size = 0;
+    ret = esp_mmu_get_largest_free_block(MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_8BIT | MMU_MEM_CAP_32BIT, &byte_aligned_size);
+    assert(ret == ESP_OK);
+    size_to_map = MIN(byte_aligned_size, psram_available_size);
+
+    const void *v_start_8bit_aligned = NULL;
+    ret = esp_mmu_find_vaddr_range(size_to_map, MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_8BIT | MMU_MEM_CAP_32BIT, &v_start_8bit_aligned);
+    assert(ret == ESP_OK);
 
 #if CONFIG_IDF_TARGET_ESP32
-    s_mapping(vaddr_start, psram_available_size);
+    s_mapping((int)v_start_8bit_aligned, size_to_map);
 #else
     uint32_t actual_mapped_len = 0;
-    mmu_hal_map_region(0, MMU_TARGET_PSRAM0, vaddr_start, MMU_PAGE_TO_BYTES(start_page), psram_available_size, &actual_mapped_len);
-    ESP_EARLY_LOGV(TAG, "actual_mapped_len is 0x%x bytes", actual_mapped_len);
+    mmu_hal_map_region(0, MMU_TARGET_PSRAM0, (intptr_t)v_start_8bit_aligned, MMU_PAGE_TO_BYTES(start_page), size_to_map, &actual_mapped_len);
+    start_page += BYTES_TO_MMU_PAGE(actual_mapped_len);
+    ESP_EARLY_LOGV(TAG, "8bit-aligned-region: actual_mapped_len is 0x%x bytes", actual_mapped_len);
 
-    cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, vaddr_start, actual_mapped_len);
+    cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, (uint32_t)v_start_8bit_aligned, actual_mapped_len);
     cache_ll_l1_enable_bus(0, bus_mask);
 #if !CONFIG_FREERTOS_UNICORE
-    bus_mask = cache_ll_l1_get_bus(1, vaddr_start, actual_mapped_len);
+    bus_mask = cache_ll_l1_get_bus(1, (uint32_t)v_start_8bit_aligned, actual_mapped_len);
     cache_ll_l1_enable_bus(1, bus_mask);
 #endif
 #endif  //#if CONFIG_IDF_TARGET_ESP32
 
+    s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].size = size_to_map;
+    s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_start = (intptr_t)v_start_8bit_aligned;
+    s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_end = (intptr_t)v_start_8bit_aligned + size_to_map;
+    s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].size = size_to_map;
+    s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].vaddr_start = (intptr_t)v_start_8bit_aligned;
+    s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].vaddr_end = (intptr_t)v_start_8bit_aligned + size_to_map;
+    ESP_EARLY_LOGV(TAG, "8bit-aligned-range: 0x%x B, starting from: 0x%x", s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].size, v_start_8bit_aligned);
+    total_mapped_size += size_to_map;
+
+#if CONFIG_IDF_TARGET_ESP32S2
+    /**
+     * On ESP32S2, there are 2 types of DBUS memory:
+     * - byte-aligned-memory
+     * - word-aligned-memory
+     *
+     * If byte-aligned-memory isn't enough, we search for word-aligned-memory to do mapping
+     */
+    if (total_mapped_size < psram_available_size) {
+        size_to_map = psram_available_size - total_mapped_size;
+
+        size_t word_aligned_size = 0;
+        ret = esp_mmu_get_largest_free_block(MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_32BIT, &word_aligned_size);
+        assert(ret == ESP_OK);
+        size_to_map = MIN(word_aligned_size, size_to_map);
+
+        const void *v_start_32bit_aligned = NULL;
+        ret = esp_mmu_find_vaddr_range(size_to_map, MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_32BIT, &v_start_32bit_aligned);
+        assert(ret == ESP_OK);
+
+        mmu_hal_map_region(0, MMU_TARGET_PSRAM0, (intptr_t)v_start_32bit_aligned, MMU_PAGE_TO_BYTES(start_page), size_to_map, &actual_mapped_len);
+        ESP_EARLY_LOGV(TAG, "32bit-aligned-region: actual_mapped_len is 0x%x bytes", actual_mapped_len);
+
+        cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, (uint32_t)v_start_32bit_aligned, actual_mapped_len);
+        cache_ll_l1_enable_bus(0, bus_mask);
+
+        s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].size = size_to_map;
+        s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_start = (intptr_t)v_start_32bit_aligned;
+        s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_end = (intptr_t)v_start_32bit_aligned + size_to_map;
+        s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].size = size_to_map;
+        s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].vaddr_start = (intptr_t)v_start_32bit_aligned;
+        s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].vaddr_end = (intptr_t)v_start_32bit_aligned + size_to_map;
+        ESP_EARLY_LOGV(TAG, "32bit-aligned-range: 0x%x B, starting from: 0x%x", s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].size, v_start_32bit_aligned);
+        total_mapped_size += size_to_map;
+    }
+#endif  //  #if CONFIG_IDF_TARGET_ESP32S2
+
+    if (total_mapped_size < psram_available_size) {
+        ESP_EARLY_LOGW(TAG, "Virtual address not enough for PSRAM, map as much as we can. %dMB is mapped", total_mapped_size / 1024 / 1024);
+    }
+
     /*------------------------------------------------------------------------------
     * After mapping, we DON'T care about the PSRAM PHYSICAL ADDRESSS ANYMORE!
     *----------------------------------------------------------------------------*/
-    s_mapped_vaddr_start = vaddr_start;
-    s_mapped_vaddr_end = vaddr_start + psram_available_size;
-    s_allocable_vaddr_start = vaddr_start;
-    s_allocable_vaddr_end = vaddr_start + psram_available_size;
 
     //------------------------------------Configure .bss in PSRAM-------------------------------------//
 #if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
     //should never be negative number
     uint32_t ext_bss_size = ((intptr_t)&_ext_ram_bss_end - (intptr_t)&_ext_ram_bss_start);
     ESP_EARLY_LOGV(TAG, "ext_bss_size is %d", ext_bss_size);
-    s_allocable_vaddr_start += ext_bss_size;
+    s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].vaddr_start += ext_bss_size;
+    s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].size -= ext_bss_size;
 #endif  //#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
 
 #if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
     uint32_t ext_noinit_size = ((intptr_t)&_ext_ram_noinit_end - (intptr_t)&_ext_ram_noinit_start);
     ESP_EARLY_LOGV(TAG, "ext_noinit_size is %d", ext_noinit_size);
-    s_allocable_vaddr_start += ext_noinit_size;
+    s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].vaddr_start += ext_noinit_size;
+    s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].size -= ext_noinit_size;
 #endif
 
 #if CONFIG_IDF_TARGET_ESP32
-    s_allocable_vaddr_end -= esp_himem_reserved_area_size() - 1;
+    s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].size -= esp_himem_reserved_area_size() - 1;
 #endif
 
-    ESP_EARLY_LOGV(TAG, "s_allocable_vaddr_start is 0x%x, s_allocable_vaddr_end is 0x%x", s_allocable_vaddr_start, s_allocable_vaddr_end);
     return ESP_OK;
 }
 
-/**
- * Add the PSRAM available region to heap allocator. Heap allocator knows the capabilities of this type of memory,
- * so there's no need to explicitly specify them.
- */
+
 esp_err_t esp_psram_extram_add_to_heap_allocator(void)
 {
-    ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (s_allocable_vaddr_end - s_allocable_vaddr_start) / 1024);
-    return heap_caps_add_region(s_allocable_vaddr_start, s_allocable_vaddr_end);
-}
+    esp_err_t ret = ESP_FAIL;
 
-esp_err_t IRAM_ATTR esp_psram_extram_get_mapped_range(intptr_t *out_vstart, intptr_t *out_vend)
-{
-    if (!out_vstart || !out_vend) {
-        return ESP_ERR_INVALID_ARG;
+    uint32_t byte_aligned_caps[] = {MALLOC_CAP_SPIRAM|MALLOC_CAP_DEFAULT, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT};
+    ret = heap_caps_add_region_with_caps(byte_aligned_caps,
+                                         s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].vaddr_start,
+                                         s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].vaddr_end);
+    if (ret != ESP_OK) {
+        return ret;
     }
 
-    if (!s_spiram_inited) {
-        return ESP_ERR_INVALID_STATE;
+    if (s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].size) {
+        assert(s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].vaddr_start);
+        uint32_t word_aligned_caps[] = {MALLOC_CAP_SPIRAM|MALLOC_CAP_DEFAULT, 0, MALLOC_CAP_32BIT};
+        ret = heap_caps_add_region_with_caps(word_aligned_caps,
+                                             s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].vaddr_start,
+                                             s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].vaddr_end);
+        if (ret != ESP_OK) {
+            return ret;
+        }
     }
 
-    *out_vstart = s_mapped_vaddr_start;
-    *out_vend = s_mapped_vaddr_end;
+    ESP_EARLY_LOGI(TAG, "Adding pool of %dK of PSRAM memory to heap allocator",
+                   (s_psram_ctx.regions_to_heap[PSRAM_MEM_8BIT_ALIGNED].size + s_psram_ctx.regions_to_heap[PSRAM_MEM_32BIT_ALIGNED].size) / 1024);
+
     return ESP_OK;
 }
 
-esp_err_t esp_psram_extram_get_alloced_range(intptr_t *out_vstart, intptr_t *out_vend)
-{
-    if (!out_vstart || !out_vend) {
-        return ESP_ERR_INVALID_ARG;
-    }
 
-    if (!s_spiram_inited) {
-        return ESP_ERR_INVALID_STATE;
+bool IRAM_ATTR esp_psram_check_ptr_addr(const void *p)
+{
+    if (!s_psram_ctx.is_initialised) {
+        return false;
     }
 
-    *out_vstart = s_allocable_vaddr_start;
-    *out_vend = s_allocable_vaddr_end;
-    return ESP_OK;
+    return ((intptr_t)p >= s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_start && (intptr_t)p < s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_end) ||
+           ((intptr_t)p >= s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_start && (intptr_t)p < s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_end);
 }
 
+
 esp_err_t esp_psram_extram_reserve_dma_pool(size_t size)
 {
     if (size == 0) {
@@ -276,9 +374,9 @@ esp_err_t esp_psram_extram_reserve_dma_pool(size_t size)
     return ESP_OK;
 }
 
-bool IRAM_ATTR esp_psram_is_initialized(void)
+bool IRAM_ATTR __attribute__((pure)) esp_psram_is_initialized(void)
 {
-    return s_spiram_inited;
+    return s_psram_ctx.is_initialised;
 }
 
 size_t esp_psram_get_size(void)
@@ -302,45 +400,68 @@ uint8_t esp_psram_io_get_cs_io(void)
  true when RAM seems OK, false when test fails. WARNING: Do not run this before the 2nd cpu has been
  initialized (in a two-core system) or after the heap allocator has taken ownership of the memory.
 */
-bool esp_psram_extram_test(void)
+static bool s_test_psram(intptr_t v_start, size_t size, intptr_t reserved_start, intptr_t reserved_end)
 {
-#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
-    const void *keepout_addr_low = (const void*)&_ext_ram_noinit_start;
-    const void *keepout_addr_high = (const void*)&_ext_ram_noinit_end;
-#else
-    const void *keepout_addr_low = 0;
-    const void *keepout_addr_high = 0;
-#endif
-
-    volatile int *spiram = (volatile int *)s_mapped_vaddr_start;
+    volatile int *spiram = (volatile int *)v_start;
     size_t p;
-    size_t s = s_mapped_vaddr_end - s_mapped_vaddr_start;
-    int errct=0;
-    int initial_err=-1;
-    for (p=0; p<(s/sizeof(int)); p+=8) {
-        const void *addr = (const void *)&spiram[p];
-        if ((keepout_addr_low <= addr) && (addr < keepout_addr_high)) {
+    int errct = 0;
+    int initial_err = -1;
+    for (p = 0; p < (size / sizeof(int)); p += 8) {
+        intptr_t addr = (intptr_t)&spiram[p];
+        if ((reserved_start <= addr) && (addr < reserved_end)) {
             continue;
         }
-        spiram[p]=p^0xAAAAAAAA;
+        spiram[p] = p ^ 0xAAAAAAAA;
     }
-    for (p=0; p<(s/sizeof(int)); p+=8) {
-        const void *addr = (const void *)&spiram[p];
-        if ((keepout_addr_low <= addr) && (addr < keepout_addr_high)) {
+    for (p = 0; p < (size / sizeof(int)); p += 8) {
+        intptr_t addr = (intptr_t)&spiram[p];
+        if ((reserved_start <= addr) && (addr < reserved_end)) {
             continue;
         }
-        if (spiram[p]!=(p^0xAAAAAAAA)) {
+        if (spiram[p] != (p ^ 0xAAAAAAAA)) {
             errct++;
-            if (errct==1) initial_err=p*4;
+            if (errct == 1) {
+                initial_err = p * 4;
+            }
         }
     }
     if (errct) {
-        ESP_EARLY_LOGE(TAG, "SPI SRAM memory test fail. %d/%d writes failed, first @ %X\n", errct, s/32, initial_err + s_mapped_vaddr_start);
+        ESP_EARLY_LOGE(TAG, "SPI SRAM memory test fail. %d/%d writes failed, first @ %X\n", errct, size/32, initial_err + v_start);
         return false;
     } else {
         ESP_EARLY_LOGI(TAG, "SPI SRAM memory test OK");
         return true;
     }
+
 }
 
-#endif  //#if CONFIG_SPIRAM
+bool esp_psram_extram_test(void)
+{
+    bool test_success = false;
+#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
+    intptr_t noinit_vstart = (intptr_t)&_ext_ram_noinit_start;
+    intptr_t noinit_vend = (intptr_t)&_ext_ram_noinit_end;
+#else
+    intptr_t noinit_vstart = 0;
+    intptr_t noinit_vend = 0;
+#endif
+    test_success = s_test_psram(s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].vaddr_start,
+                                s_psram_ctx.mapped_regions[PSRAM_MEM_8BIT_ALIGNED].size,
+                                noinit_vstart,
+                                noinit_vend);
+    if (!test_success) {
+        return false;
+    }
+
+    if (s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].size) {
+        test_success = s_test_psram(s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].vaddr_start,
+                                    s_psram_ctx.mapped_regions[PSRAM_MEM_32BIT_ALIGNED].size,
+                                    0,
+                                    0);
+    }
+    if (!test_success) {
+        return false;
+    }
+
+    return true;
+}

+ 77 - 0
components/esp_psram/ext_mem_layout.c

@@ -0,0 +1,77 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include <stdlib.h>
+#include <stdint.h>
+#include "sdkconfig.h"
+#include "soc/ext_mem_defs.h"
+#include "ext_mem_layout.h"
+#include "mmu.h"
+
+
+#if CONFIG_IDF_TARGET_ESP32
+/**
+ * These regions is referring to linear address
+ * The start addresses in this list should always be sorted from low to high, as MMU driver will need to
+ * coalesce adjacent regions
+ */
+const mmu_mem_region_t g_mmu_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM] = {
+
+    /*linear start                     linear end                         bus size                        bus ID,          bus capabilities */
+
+    //Can be used for text
+    {SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW, SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_IRAM0_LINEAR), CACHE_BUS_IBUS0, MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT},
+    //Can be used for text
+    {SOC_MMU_IRAM1_LINEAR_ADDRESS_LOW, SOC_MMU_IRAM1_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_IRAM1_LINEAR), CACHE_BUS_IBUS1, MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT},
+    //Can be used for text
+    {SOC_MMU_IROM0_LINEAR_ADDRESS_LOW, SOC_MMU_IROM0_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_IROM0_LINEAR), CACHE_BUS_IBUS2, MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT},
+    //Can be used for rodata
+    {SOC_MMU_DROM0_LINEAR_ADDRESS_LOW, SOC_MMU_DROM0_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_DROM0_LINEAR), CACHE_BUS_DBUS0, MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT},
+    //Can be used for PSRAM
+    {SOC_MMU_DRAM1_LINEAR_ADDRESS_LOW, SOC_MMU_DRAM1_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_DRAM1_LINEAR), CACHE_BUS_DBUS1, MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT},
+};
+
+
+#elif CONFIG_IDF_TARGET_ESP32S2
+/**
+ * These regions is referring to linear address
+ * The start addresses in this list should always be sorted from low to high, as MMU driver will need to
+ * coalesce adjacent regions
+ */
+const mmu_mem_region_t g_mmu_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM] = {
+
+    /*linear start                     linear end                         bus size                        bus ID,          bus capabilities */
+
+    //Can be used for text
+    {SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW, SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_IRAM0_LINEAR), CACHE_BUS_IBUS0, MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT},
+    //Can be used for text
+    {SOC_MMU_IRAM1_LINEAR_ADDRESS_LOW, SOC_MMU_IRAM1_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_IRAM1_LINEAR), CACHE_BUS_IBUS1, MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT},
+    //Can be used for Flash rodata, connected by IBUS
+    {SOC_MMU_DROM0_LINEAR_ADDRESS_LOW, SOC_MMU_DROM0_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_DROM0_LINEAR), CACHE_BUS_IBUS2, MMU_MEM_CAP_READ | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT},
+    //Can be used for PSRAM
+    {SOC_MMU_DPORT_LINEAR_ADDRESS_LOW, SOC_MMU_DPORT_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_DPORT_LINEAR), CACHE_BUS_DBUS2, MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_32BIT},
+    //Can be used for PSRAM
+    {SOC_MMU_DRAM1_LINEAR_ADDRESS_LOW, SOC_MMU_DRAM1_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_DRAM1_LINEAR), CACHE_BUS_DBUS1, MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT},
+    //Can be used for PSRAM
+    {SOC_MMU_DRAM0_LINEAR_ADDRESS_LOW, SOC_MMU_DRAM0_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_DRAM0_LINEAR), CACHE_BUS_DBUS0, MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT},
+};
+
+
+#elif CONFIG_IDF_TARGET_ESP32S3
+/**
+ * The start addresses in this list should always be sorted from low to high, as MMU driver will need to
+ * coalesce adjacent regions
+ */
+const mmu_mem_region_t g_mmu_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM] = {
+
+    /*linear start                     linear end                         bus size                        bus ID,                            bus capabilities */
+
+    /**
+     * Can be used for Flash text, rodata, and PSRAM
+     * IRAM0 linear address should be always the same as DRAM0 linear address
+     */
+    {SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW, SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH, BUS_SIZE(SOC_MMU_IRAM0_LINEAR), CACHE_BUS_IBUS0 | CACHE_BUS_DBUS0, MMU_MEM_CAP_EXEC | MMU_MEM_CAP_READ | MMU_MEM_CAP_WRITE | MMU_MEM_CAP_32BIT | MMU_MEM_CAP_8BIT},
+};
+#endif

+ 32 - 0
components/esp_psram/ext_mem_layout.h

@@ -0,0 +1,32 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "sdkconfig.h"
+#include "soc/soc_caps.h"
+#include "hal/cache_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct {
+    intptr_t start;
+    intptr_t end;
+    size_t size;
+    cache_bus_mask_t bus_id;
+    uint32_t caps;
+} mmu_mem_region_t;
+
+//These regions is referring to linear address
+extern const mmu_mem_region_t g_mmu_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM];
+
+#ifdef __cplusplus
+}
+#endif

+ 5 - 22
components/esp_psram/include/esp_private/esp_psram_extram.h

@@ -15,32 +15,15 @@ extern "C" {
 #endif
 
 /**
- * @brief Get the psram mapped vaddr range
+ * @brief Check if the pointer is on PSRAM
  *
- * @param[out] out_vstart PSRAM virtual address start
- * @param[out] out_vend   PSRAM virtual address end
- *
- * @note [out_vstart, out_vend), `out_vend` isn't included.
- *
- * @return
- *        - ESP_OK                  On success
- *        - ESP_ERR_INVALID_STATE   PSRAM is not initialized successfully
- */
-esp_err_t esp_psram_extram_get_mapped_range(intptr_t *out_vstart, intptr_t *out_vend);
-
-/**
- * @brief Get the psram alloced vaddr range
- *
- * @param[out] out_vstart PSRAM virtual address start
- * @param[out] out_vend   PSRAM virtual address end
- *
- * @note [out_vstart, out_vend), `out_vend` isn't included.
+ * @param[in] p  The pointer to check
  *
  * @return
- *        - ESP_OK                  On success
- *        - ESP_ERR_INVALID_STATE   PSRAM is not initialized successfully
+ *        - False: the pointer isn't on PSRAM, or PSRAM isn't initialised successfully
+ *        - True:  the pointer is on PSRAM
  */
-esp_err_t esp_psram_extram_get_alloced_range(intptr_t *out_vstart, intptr_t *out_vend);
+bool esp_psram_check_ptr_addr(const void *p);
 
 /**
  * @brief Add the initialized PSRAM to the heap allocator.

+ 28 - 30
components/esp_psram/include/esp_private/mmu.h → components/esp_psram/include/esp_private/mmu_psram_flash.h

@@ -5,13 +5,16 @@
  */
 
 /**
- * This file will be redesigned into MMU driver, to maintain all the external
- * memory contexts including:
- * - Flash
- * - PSRAM
- * - DDR
+ * @Backgrounds
  *
- * Now only MMU-PSRAM related private APIs
+ * This file contains 2 parts:
+ * 1. Feature: Copy Flash content to PSRAM. Related APIs are private:
+ *    - mmu_config_psram_text_segment()
+ *    - mmu_config_psram_rodata_segment()
+ *
+ * 2. Private APIs used by `flash_mmap.c` and `cache_utils.c`
+ *    APIs in 2 are due to lack of MMU driver. There will be an MMU driver to maintain vaddr range.
+ *    APIs in 2 will be refactored when MMU driver is ready
  */
 
 #pragma once
@@ -32,31 +35,36 @@ extern "C" {
 #define BYTES_TO_MMU_PAGE(bytes)        ((bytes) / MMU_PAGE_SIZE)
 #endif
 
+/*----------------------------------------------------------------------------
+                    Part 1 APIs (See @Backgrounds on top of this file)
+-------------------------------------------------------------------------------*/
+#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
 /**
- * @brief Get the vaddr start for PSRAM
- *
- * @return PSRAM vaddr start address
- */
-intptr_t mmu_get_psram_vaddr_start(void);
-
-/**
- * @brief Get the vaddr end for PSRAM
+ * @brief Copy Flash texts to PSRAM
  *
- * @return PSRAM vaddr end address
+ * @param[in]  start_page    PSRAM physical start page
+ * @param[in]  psram_size    PSRAM available size
+ * @param[out] out_page      Used pages
  */
-intptr_t mmu_get_psram_vaddr_end(void);
-
+esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page);
+#endif  //#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
 
-#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
+#if CONFIG_SPIRAM_RODATA
 /**
- * @brief Copy Flash texts to PSRAM
+ * @brief Copy Flash rodata to PSRAM
  *
  * @param[in]  start_page    PSRAM physical start page
  * @param[in]  psram_size    PSRAM available size
  * @param[out] out_page      Used pages
  */
-esp_err_t mmu_config_psram_text_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page);
+esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page);
+#endif  //#if CONFIG_SPIRAM_RODATA
+
 
+/*----------------------------------------------------------------------------
+                    Part 2 APIs (See @Backgrounds on top of this file)
+-------------------------------------------------------------------------------*/
+#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
 /**
  * @brief Init other file requested MMU variables
  *
@@ -90,17 +98,7 @@ uint32_t instruction_flash_end_page_get(void);
 int instruction_flash2spiram_offset(void);
 #endif  // #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
 
-
 #if CONFIG_SPIRAM_RODATA
-/**
- * @brief Copy Flash rodata to PSRAM
- *
- * @param[in]  start_page    PSRAM physical start page
- * @param[in]  psram_size    PSRAM available size
- * @param[out] out_page      Used pages
- */
-esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_size, uint32_t *out_page);
-
 /**
  * @brief Init other file requested MMU variables
  *

+ 1 - 1
components/esp_psram/linker.lf

@@ -14,4 +14,4 @@ entries:
                 esp_psram_impl_octal (noflash)
 
         if IDF_TARGET_ESP32S2 = y || IDF_TARGET_ESP32S3 = y:
-            mmu_psram (noflash)
+            mmu_psram_flash (noflash)

+ 209 - 113
components/esp_psram/mmu.c

@@ -15,157 +15,253 @@
  */
 
 #include <stdint.h>
+#include <string.h>
 #include <sys/param.h>
 #include "sdkconfig.h"
 #include "esp_attr.h"
 #include "esp_log.h"
-#include "soc/ext_mem_defs.h"
-#include "esp_private/mmu.h"
-
-#if CONFIG_IDF_TARGET_ESP32S2
-#include "soc/extmem_reg.h"
-#include "esp32s2/rom/cache.h"
-#elif CONFIG_IDF_TARGET_ESP32S3
-#include "soc/extmem_reg.h"
-#include "esp32s3/rom/cache.h"
-#endif
+#include "esp_check.h"
+#include "soc/soc_caps.h"
+#include "ext_mem_layout.h"
+#include "freertos/FreeRTOS.h"
+#include "hal/cache_types.h"
+#include "hal/cache_ll.h"
+#include "hal/mmu_types.h"
+#include "hal/mmu_ll.h"
+#include "mmu.h"
 
 
 #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
-__attribute__((unused)) static const char *TAG = "mmu";
+#define MMU_PAGE_SIZE           CONFIG_MMU_PAGE_SIZE
+
+//This flag indicates the memory region is merged, we don't care about it anymore
+#define MEM_REGION_MERGED             -1
+
+static const char *TAG = "mmu";
 extern int _instruction_reserved_start;
 extern int _instruction_reserved_end;
 extern int _rodata_reserved_start;
 extern int _rodata_reserved_end;
 
+typedef struct mmu_linear_mem_ {
+    cache_bus_mask_t bus_id;
+    intptr_t start;
+    intptr_t end;
+    size_t pool_size;
+    intptr_t free_head;
+    size_t free_size;
+    int caps;
+} mmu_linear_mem_t;
 
-intptr_t mmu_get_psram_vaddr_start(void)
-{
-#if CONFIG_IDF_TARGET_ESP32S3
+typedef struct {
+    /**
+     * number of memory regions that are available, after coalescing, this number should be smaller than or equal to `SOC_MMU_LINEAR_ADDRESS_REGION_NUM`
+     */
+    uint32_t num_regions;
+    /**
+     * This saves the available MMU linear address regions,
+     * after reserving flash .rodata and .text, and after coalescing.
+     * Only the first `num_regions` items are valid
+     */
+    mmu_linear_mem_t mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM];
+} mmu_ctx_t;
 
-    intptr_t rodata_end_aligned = ALIGN_UP_BY((intptr_t)&_rodata_reserved_end, MMU_PAGE_SIZE);
-    ESP_EARLY_LOGV(TAG, "rodata_end_aligned is 0x%x bytes", rodata_end_aligned);
-    return rodata_end_aligned;
+static mmu_ctx_t s_mmu_ctx;
 
-#elif CONFIG_IDF_TARGET_ESP32S2
-    return DPORT_CACHE_ADDRESS_LOW;
-#else   //CONFIG_IDF_TARGET_ESP32
-    return DRAM1_CACHE_ADDRESS_LOW;
-#endif
-}
 
-intptr_t mmu_get_psram_vaddr_end(void)
+static void s_reserve_irom_region(mmu_linear_mem_t *hw_mem_regions, int region_nums)
 {
-#if CONFIG_IDF_TARGET_ESP32S3
-    return DRAM0_CACHE_ADDRESS_HIGH;
-#elif CONFIG_IDF_TARGET_ESP32S2
-    return DRAM0_CACHE_ADDRESS_HIGH;
-#else   //CONFIG_IDF_TARGET_ESP32
-    return DRAM1_CACHE_ADDRESS_HIGH;
-#endif
-}
+    /**
+     * We follow the way how 1st bootloader load flash .text:
+     *
+     * - Now IBUS addresses (between `_instruction_reserved_start` and `_instruction_reserved_end`) are consecutive on all chips,
+     *   we strongly rely on this to calculate the .text length
+     */
+    size_t irom_len_to_reserve = (uint32_t)&_instruction_reserved_end - (uint32_t)&_instruction_reserved_start;
+    assert((mmu_ll_vaddr_to_laddr((uint32_t)&_instruction_reserved_end) - mmu_ll_vaddr_to_laddr((uint32_t)&_instruction_reserved_start)) == irom_len_to_reserve);
 
-//------------------------------------Copy Flash .text to PSRAM-------------------------------------//
-#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
-static uint32_t instruction_in_spiram;
-static uint32_t instr_start_page;
-static uint32_t instr_end_page;
-static int instr_flash2spiram_offs;
+    irom_len_to_reserve = ALIGN_UP_BY(irom_len_to_reserve, MMU_PAGE_SIZE);
+    cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, (uint32_t)&_instruction_reserved_start, irom_len_to_reserve);
 
-/**
- * - These logics are abstracted from the PSRAM driver
- * - These functions are only required by `flash_mmap.c` for converting paddr to vaddr, and vice versa
- * - The `flash_mmpa.c` will be rewritten into MMU driver
- *
- * Therefore, keep the APIs here for now
- */
-void instruction_flash_page_info_init(uint32_t psram_start_physical_page)
-{
-#if CONFIG_IDF_TARGET_ESP32S2
-    uint32_t instr_page_cnt = ((uint32_t)&_instruction_reserved_end - (uint32_t)&_instruction_reserved_start + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
-    uint32_t instr_mmu_offset = ((uint32_t)&_instruction_reserved_start & MMU_VADDR_MASK) / MMU_PAGE_SIZE;
-    instr_start_page = ((volatile uint32_t *)(DR_REG_MMU_TABLE + PRO_CACHE_IBUS0_MMU_START))[instr_mmu_offset];
-#elif CONFIG_IDF_TARGET_ESP32S3
-    uint32_t instr_page_cnt = ((uint32_t)&_instruction_reserved_end - SOC_IROM_LOW + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
-    instr_start_page = *((volatile uint32_t *)(DR_REG_MMU_TABLE + CACHE_IROM_MMU_START));
-#endif
-    instr_start_page &= MMU_VALID_VAL_MASK;
-    instr_end_page = instr_start_page + instr_page_cnt - 1;
-    instr_flash2spiram_offs = instr_start_page - psram_start_physical_page;
-    instruction_in_spiram = 1;
-    ESP_DRAM_LOGV("mmu_psram", "Instructions from flash page%d copy to SPIRAM page%d, Offset: %d", instr_start_page, psram_start_physical_page, instr_flash2spiram_offs);
+    for (int i = 0; i < SOC_MMU_LINEAR_ADDRESS_REGION_NUM; i++) {
+        if (bus_mask & hw_mem_regions[i].bus_id) {
+            if (hw_mem_regions[i].pool_size <= irom_len_to_reserve) {
+                hw_mem_regions[i].free_head = hw_mem_regions[i].end;
+                hw_mem_regions[i].free_size = 0;
+                irom_len_to_reserve -= hw_mem_regions[i].pool_size;
+            } else {
+                hw_mem_regions[i].free_head = hw_mem_regions[i].free_head + irom_len_to_reserve;
+                hw_mem_regions[i].free_size -= irom_len_to_reserve;
+            }
+        }
+    }
 }
 
-uint32_t esp_spiram_instruction_access_enabled(void)
+static void s_reserve_drom_region(mmu_linear_mem_t *hw_mem_regions, int region_nums)
 {
-    return instruction_in_spiram;
-}
+    /**
+     * Similarly, we follow the way how 1st bootloader load flash .rodata:
+     */
+    size_t drom_len_to_reserve = (uint32_t)&_rodata_reserved_end - (uint32_t)&_rodata_reserved_start;
+    assert((mmu_ll_vaddr_to_laddr((uint32_t)&_rodata_reserved_end) - mmu_ll_vaddr_to_laddr((uint32_t)&_rodata_reserved_start)) == drom_len_to_reserve);
 
-int instruction_flash2spiram_offset(void)
-{
-    return instr_flash2spiram_offs;
-}
+    drom_len_to_reserve = ALIGN_UP_BY(drom_len_to_reserve, MMU_PAGE_SIZE);
+    cache_bus_mask_t bus_mask = cache_ll_l1_get_bus(0, (uint32_t)&_rodata_reserved_start, drom_len_to_reserve);
 
-uint32_t instruction_flash_start_page_get(void)
-{
-    return instr_start_page;
+    for (int i = 0; i < SOC_MMU_LINEAR_ADDRESS_REGION_NUM; i++) {
+        if (bus_mask & hw_mem_regions[i].bus_id) {
+            if (hw_mem_regions[i].pool_size <= drom_len_to_reserve) {
+                hw_mem_regions[i].free_head = hw_mem_regions[i].end;
+                hw_mem_regions[i].free_size = 0;
+                drom_len_to_reserve -= hw_mem_regions[i].pool_size;
+            } else {
+                hw_mem_regions[i].free_head = hw_mem_regions[i].free_head + drom_len_to_reserve;
+                hw_mem_regions[i].free_size -= drom_len_to_reserve;
+            }
+        }
+    }
 }
 
-uint32_t instruction_flash_end_page_get(void)
+void esp_mmu_init(void)
 {
-    return instr_end_page;
-}
-#endif  //CONFIG_SPIRAM_FETCH_INSTRUCTIONS
+    mmu_linear_mem_t hw_mem_regions[SOC_MMU_LINEAR_ADDRESS_REGION_NUM] = {};
+
+    for (int i = 0; i < SOC_MMU_LINEAR_ADDRESS_REGION_NUM; i++) {
+        hw_mem_regions[i].start = g_mmu_mem_regions[i].start;
+        hw_mem_regions[i].end = g_mmu_mem_regions[i].end;
+        hw_mem_regions[i].pool_size = g_mmu_mem_regions[i].size;
+        hw_mem_regions[i].free_size = g_mmu_mem_regions[i].size;
+        hw_mem_regions[i].free_head = g_mmu_mem_regions[i].start;
+        hw_mem_regions[i].bus_id = g_mmu_mem_regions[i].bus_id;
+        hw_mem_regions[i].caps = g_mmu_mem_regions[i].caps;
+#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
+        assert(__builtin_popcount(hw_mem_regions[i].bus_id) == 1);
+#endif
+        assert(hw_mem_regions[i].pool_size % MMU_PAGE_SIZE == 0);
+    }
 
+    //First reserve memory regions used for irom and drom, as we must follow the way how 1st bootloader load them
+    s_reserve_irom_region(hw_mem_regions, SOC_MMU_LINEAR_ADDRESS_REGION_NUM);
+    s_reserve_drom_region(hw_mem_regions, SOC_MMU_LINEAR_ADDRESS_REGION_NUM);
 
-#if CONFIG_SPIRAM_RODATA
-//------------------------------------Copy Flash .rodata to PSRAM-------------------------------------//
-static uint32_t rodata_in_spiram;
-static int rodata_flash2spiram_offs;
-static uint32_t rodata_start_page;
-static uint32_t rodata_end_page;
+    if (SOC_MMU_LINEAR_ADDRESS_REGION_NUM > 1) {
+        //Now we can coalesce adjacent regions
+        for (int i = 1; i < SOC_MMU_LINEAR_ADDRESS_REGION_NUM; i++) {
+            mmu_linear_mem_t *a = &hw_mem_regions[i - 1];
+            mmu_linear_mem_t *b = &hw_mem_regions[i];
+            if ((b->free_head == a->end) && (b->caps == a->caps)) {
+                a->caps = MEM_REGION_MERGED;
+                b->bus_id |= a->bus_id;
+                b->start = a->start;
+                b->pool_size += a->pool_size;
+                b->free_head = a->free_head;
+                b->free_size += a->free_size;
+            }
+        }
+    }
 
-/**
- * - These logics are abstracted from the PSRAM driver
- * - These functions are only required by `flash_mmap.c` for converting paddr to vaddr, and vice versa
- * - The `flash_mmpa.c` will be rewritten into MMU driver
- *
- * Therefore, keep the APIs here for now
- */
-void rodata_flash_page_info_init(uint32_t psram_start_physical_page)
-{
-#if CONFIG_IDF_TARGET_ESP32S2
-    uint32_t rodata_page_cnt = ((uint32_t)&_rodata_reserved_end - (uint32_t)&_rodata_reserved_start + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
-    uint32_t rodata_mmu_offset = ((uint32_t)&_rodata_reserved_start & MMU_VADDR_MASK) / MMU_PAGE_SIZE;
-    rodata_start_page = ((volatile uint32_t *)(DR_REG_MMU_TABLE + PRO_CACHE_IBUS2_MMU_START))[rodata_mmu_offset];
-#elif CONFIG_IDF_TARGET_ESP32S3
-    uint32_t rodata_page_cnt = ((uint32_t)&_rodata_reserved_end - ((uint32_t)&_rodata_reserved_start & ~ (MMU_PAGE_SIZE - 1)) + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
-    rodata_start_page = *(volatile uint32_t *)(DR_REG_MMU_TABLE + CACHE_DROM_MMU_START);
-#endif
-    rodata_start_page &= MMU_VALID_VAL_MASK;
-    rodata_end_page = rodata_start_page + rodata_page_cnt - 1;
-    rodata_flash2spiram_offs = rodata_start_page - psram_start_physical_page;
-    rodata_in_spiram = 1;
-    ESP_DRAM_LOGV("mmu_psram", "Rodata from flash page%d copy to SPIRAM page%d, Offset: %d", rodata_start_page, psram_start_physical_page, rodata_flash2spiram_offs);
-}
+    //Count the mem regions left after coalescing
+    uint32_t region_num = 0;
+    for (int i = 0; i < SOC_MMU_LINEAR_ADDRESS_REGION_NUM; i++) {
+        if(hw_mem_regions[i].caps != MEM_REGION_MERGED) {
+            region_num++;
+        }
+    }
+    ESP_EARLY_LOGV(TAG, "after coalescing, %d regions are left", region_num);
 
-uint32_t esp_spiram_rodata_access_enabled(void)
-{
-    return rodata_in_spiram;
+    //Initialise `s_mmu_ctx.mem_regions[]`, as we've done all static allocation, to prepare available virtual memory regions
+    uint32_t available_region_idx = 0;
+    s_mmu_ctx.num_regions = region_num;
+    for (int i = 0; i < SOC_MMU_LINEAR_ADDRESS_REGION_NUM; i++) {
+        if (hw_mem_regions[i].caps == MEM_REGION_MERGED) {
+            continue;
+        }
+
+        memcpy(&s_mmu_ctx.mem_regions[available_region_idx], &hw_mem_regions[i], sizeof(mmu_linear_mem_t));
+        available_region_idx++;
+    }
+
+    assert(available_region_idx == region_num);
 }
 
-int rodata_flash2spiram_offset(void)
+esp_err_t esp_mmu_get_largest_free_block(int caps, size_t *out_len)
 {
-    return rodata_flash2spiram_offs;
+    ESP_RETURN_ON_FALSE(out_len, ESP_ERR_INVALID_ARG, TAG, "null pointer");
+    if (caps & MMU_MEM_CAP_EXEC) {
+        if ((caps & MMU_MEM_CAP_8BIT) || (caps & MMU_MEM_CAP_WRITE)) {
+            //None of the executable memory are expected to be 8-bit accessible or writable.
+            return ESP_ERR_INVALID_ARG;
+        }
+    }
+    *out_len = 0;
+
+    size_t max = 0;
+
+    for (int i = 0; i < s_mmu_ctx.num_regions; i++) {
+        if ((s_mmu_ctx.mem_regions[i].caps & caps) == caps) {
+            if (s_mmu_ctx.mem_regions[i].free_size > max) {
+                max = s_mmu_ctx.mem_regions[i].free_size;
+            }
+        }
+    }
+
+    *out_len = max;
+
+    return ESP_OK;
 }
 
-uint32_t rodata_flash_start_page_get(void)
+esp_err_t esp_mmu_find_vaddr_range(size_t size, uint32_t caps, const void **out_ptr)
 {
-    return rodata_start_page;
+    ESP_RETURN_ON_FALSE(out_ptr, ESP_ERR_INVALID_ARG, TAG, "null pointer");
+    if (caps & MMU_MEM_CAP_EXEC) {
+        if ((caps & MMU_MEM_CAP_8BIT) || (caps & MMU_MEM_CAP_WRITE)) {
+            //None of the executable memory are expected to be 8-bit accessible or writable.
+            return ESP_ERR_INVALID_ARG;
+        }
+        caps |= MMU_MEM_CAP_32BIT;
+    }
+
+    size_t aligned_size = ALIGN_UP_BY(size, MMU_PAGE_SIZE);
+    bool is_match = false;
+    uint32_t laddr = 0;
+
+    for (int i = 0; i < s_mmu_ctx.num_regions; i++) {
+        if ((s_mmu_ctx.mem_regions[i].caps & caps) == caps) {
+            if (s_mmu_ctx.mem_regions[i].free_size < aligned_size) {
+                continue;
+            } else {
+                laddr = (uint32_t)s_mmu_ctx.mem_regions[i].free_head;
+                s_mmu_ctx.mem_regions[i].free_head += aligned_size;
+                s_mmu_ctx.mem_regions[i].free_size -= aligned_size;
+                is_match = true;
+                break;
+            }
+        }
+    }
+    ESP_RETURN_ON_FALSE(is_match, ESP_ERR_NOT_FOUND, TAG, "no such vaddr range");
+    ESP_EARLY_LOGV(TAG, "found laddr is 0x%x", laddr);
+
+    if (caps & MMU_MEM_CAP_EXEC) {
+        laddr = mmu_ll_laddr_to_vaddr(laddr, MMU_VADDR_INSTRUCTION);
+    } else {
+        laddr = mmu_ll_laddr_to_vaddr(laddr, MMU_VADDR_DATA);
+    }
+    *out_ptr = (void *)laddr;
+
+    return ESP_OK;
 }
 
-uint32_t rodata_flash_end_page_get(void)
+esp_err_t esp_mmu_dump_region_usage(void)
 {
-    return rodata_end_page;
+    for (int i = 0; i < s_mmu_ctx.num_regions; i++) {
+        ESP_EARLY_LOGI(TAG, "bus_id: 0x%x", s_mmu_ctx.mem_regions[i].bus_id);
+        ESP_EARLY_LOGI(TAG, "start: 0x%x", s_mmu_ctx.mem_regions[i].start);
+        ESP_EARLY_LOGI(TAG, "end: 0x%x", s_mmu_ctx.mem_regions[i].end);
+        ESP_EARLY_LOGI(TAG, "pool_size: 0x%x", s_mmu_ctx.mem_regions[i].pool_size);
+        ESP_EARLY_LOGI(TAG, "free_head: 0x%x", s_mmu_ctx.mem_regions[i].free_head);
+        ESP_EARLY_LOGI(TAG, "free_size: 0x%x", s_mmu_ctx.mem_regions[i].free_size);
+        ESP_EARLY_LOGI(TAG, "caps: 0x%x\n", s_mmu_ctx.mem_regions[i].caps);
+    }
+
+    return ESP_OK;
 }
-#endif  //#if CONFIG_SPIRAM_RODATA

+ 76 - 0
components/esp_psram/mmu.h

@@ -0,0 +1,76 @@
+/*
+ * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#pragma once
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "esp_err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This file will be moved out of `esp_psram` component. And will be
+ * future MMU driver, to maintain all the external memory contexts including:
+ * - Flash
+ * - PSRAM
+ * - DDR
+ *
+ * Now only support ESP32, ESP32S2, ESP32S3 virtual address maintenance, and is internal
+ */
+
+
+#define MMU_MEM_CAP_EXEC        (1<<0)
+#define MMU_MEM_CAP_READ        (1<<1)
+#define MMU_MEM_CAP_WRITE       (1<<2)
+#define MMU_MEM_CAP_32BIT       (1<<3)
+#define MMU_MEM_CAP_8BIT        (1<<4)
+
+/**
+ * @brief Initialise the MMU driver
+ *
+ * This is called once in the IDF startup code. Don't call it in applications
+ */
+void esp_mmu_init(void);
+
+/**
+ * @brief Get largest consecutive free external virtual memory block, with given capabilities
+ *
+ * @param[in] caps      Bitwise OR of MMU_MEM_CAP_* flags indicating the memory block
+ * @param[out] out_len  Largest free block length, in bytes.
+ *
+ * @return
+ *        - ESP_OK:              On success
+ *        - ESP_ERR_INVALID_ARG: Invalid arguments, could be null pointer
+ */
+esp_err_t esp_mmu_get_largest_free_block(int caps, size_t *out_len);
+
+/**
+ * @brief Find a consecutive external virtual memory range, with given capabilities and size
+ *
+ * @param[in] size      Size, in bytes, the amount of memory to find
+ * @param[in] caps      Bitwise OR of MMU_MEM_CAP_* flags indicating the memory block
+ * @param[out] out_ptr  Pointer to the memory range found
+ *
+ * @return
+ *        - ESP_OK:              On success
+ *        - ESP_ERR_INVALID_ARG: Invalid arguments, could be wrong caps makeup, or null pointer
+ *        - ESP_ERR_NOT_FOUND:   Didn't find enough memory with give caps
+ */
+esp_err_t esp_mmu_find_vaddr_range(size_t size, uint32_t caps, const void **out_ptr);
+
+/**
+ * @brief Dump internal memory region usage
+ *
+ * @return
+ *        - ESP_OK: On success
+ */
+esp_err_t esp_mmu_dump_region_usage(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 127 - 9
components/esp_psram/mmu_psram.c → components/esp_psram/mmu_psram_flash.c

@@ -3,15 +3,17 @@
  *
  * SPDX-License-Identifier: Apache-2.0
  */
-
 /**
- * This file will be redesigned into MMU driver, to maintain all the external
- * memory contexts including:
- * - Flash
- * - PSRAM
- * - DDR
+ * @Backgrounds
+ *
+ * This file contains 2 parts:
+ * 1. Feature: Copy Flash content to PSRAM. Related APIs are private:
+ *    - mmu_config_psram_text_segment()
+ *    - mmu_config_psram_rodata_segment()
  *
- * Now only MMU-PSRAM related private APIs
+ * 2. Private APIs used by `flash_mmap.c` and `cache_utils.c`
+ *    APIs in 2 are due to lack of MMU driver. There will be an MMU driver to maintain vaddr range.
+ *    APIs in 2 will be refactored when MMU driver is ready
  */
 
 #include <sys/param.h>
@@ -21,7 +23,7 @@
 #include "soc/ext_mem_defs.h"
 #include "hal/cache_types.h"
 #include "hal/cache_ll.h"
-#include "esp_private/mmu.h"
+#include "esp_private/mmu_psram_flash.h"
 
 #if CONFIG_IDF_TARGET_ESP32S2
 #include "esp32s2/rom/cache.h"
@@ -29,7 +31,9 @@
 #include "esp32s3/rom/cache.h"
 #endif
 
-
+/*----------------------------------------------------------------------------
+                    Part 1 APIs (See @Backgrounds on top of this file)
+-------------------------------------------------------------------------------*/
 #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS || CONFIG_SPIRAM_RODATA
 //page_size - 1, where page_size on s2 and s3 is always 0x10000. To be refactored by MMU driver
 #define INVALID_PHY_PAGE 0xffff
@@ -144,3 +148,117 @@ esp_err_t mmu_config_psram_rodata_segment(uint32_t start_page, uint32_t psram_si
     return ESP_OK;
 }
 #endif  //#if CONFIG_SPIRAM_RODATA
+
+
+/*----------------------------------------------------------------------------
+                    Part 2 APIs (See @Backgrounds on top of this file)
+-------------------------------------------------------------------------------*/
+extern int _instruction_reserved_start;
+extern int _instruction_reserved_end;
+extern int _rodata_reserved_start;
+extern int _rodata_reserved_end;
+
+//------------------------------------Copy Flash .text to PSRAM-------------------------------------//
+#if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
+static uint32_t instruction_in_spiram;
+static uint32_t instr_start_page;
+static uint32_t instr_end_page;
+static int instr_flash2spiram_offs;
+
+/**
+ * - These logics are abstracted from the PSRAM driver
+ * - These functions are only required by `flash_mmap.c` for converting paddr to vaddr, and vice versa
+ * - The `flash_mmpa.c` will be rewritten into MMU driver
+ *
+ * Therefore, keep the APIs here for now
+ */
+void instruction_flash_page_info_init(uint32_t psram_start_physical_page)
+{
+#if CONFIG_IDF_TARGET_ESP32S2
+    uint32_t instr_page_cnt = ((uint32_t)&_instruction_reserved_end - (uint32_t)&_instruction_reserved_start + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
+    uint32_t instr_mmu_offset = ((uint32_t)&_instruction_reserved_start & MMU_VADDR_MASK) / MMU_PAGE_SIZE;
+    instr_start_page = ((volatile uint32_t *)(DR_REG_MMU_TABLE + PRO_CACHE_IBUS0_MMU_START))[instr_mmu_offset];
+#elif CONFIG_IDF_TARGET_ESP32S3
+    uint32_t instr_page_cnt = ((uint32_t)&_instruction_reserved_end - SOC_IROM_LOW + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
+    instr_start_page = *((volatile uint32_t *)(DR_REG_MMU_TABLE + CACHE_IROM_MMU_START));
+#endif
+    instr_start_page &= MMU_VALID_VAL_MASK;
+    instr_end_page = instr_start_page + instr_page_cnt - 1;
+    instr_flash2spiram_offs = instr_start_page - psram_start_physical_page;
+    instruction_in_spiram = 1;
+    ESP_DRAM_LOGV("mmu_psram", "Instructions from flash page%d copy to SPIRAM page%d, Offset: %d", instr_start_page, psram_start_physical_page, instr_flash2spiram_offs);
+}
+
+uint32_t esp_spiram_instruction_access_enabled(void)
+{
+    return instruction_in_spiram;
+}
+
+int instruction_flash2spiram_offset(void)
+{
+    return instr_flash2spiram_offs;
+}
+
+uint32_t instruction_flash_start_page_get(void)
+{
+    return instr_start_page;
+}
+
+uint32_t instruction_flash_end_page_get(void)
+{
+    return instr_end_page;
+}
+#endif  //CONFIG_SPIRAM_FETCH_INSTRUCTIONS
+
+
+#if CONFIG_SPIRAM_RODATA
+//------------------------------------Copy Flash .rodata to PSRAM-------------------------------------//
+static uint32_t rodata_in_spiram;
+static int rodata_flash2spiram_offs;
+static uint32_t rodata_start_page;
+static uint32_t rodata_end_page;
+
+/**
+ * - These logics are abstracted from the PSRAM driver
+ * - These functions are only required by `flash_mmap.c` for converting paddr to vaddr, and vice versa
+ * - The `flash_mmpa.c` will be rewritten into MMU driver
+ *
+ * Therefore, keep the APIs here for now
+ */
+void rodata_flash_page_info_init(uint32_t psram_start_physical_page)
+{
+#if CONFIG_IDF_TARGET_ESP32S2
+    uint32_t rodata_page_cnt = ((uint32_t)&_rodata_reserved_end - (uint32_t)&_rodata_reserved_start + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
+    uint32_t rodata_mmu_offset = ((uint32_t)&_rodata_reserved_start & MMU_VADDR_MASK) / MMU_PAGE_SIZE;
+    rodata_start_page = ((volatile uint32_t *)(DR_REG_MMU_TABLE + PRO_CACHE_IBUS2_MMU_START))[rodata_mmu_offset];
+#elif CONFIG_IDF_TARGET_ESP32S3
+    uint32_t rodata_page_cnt = ((uint32_t)&_rodata_reserved_end - ((uint32_t)&_rodata_reserved_start & ~ (MMU_PAGE_SIZE - 1)) + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
+    rodata_start_page = *(volatile uint32_t *)(DR_REG_MMU_TABLE + CACHE_DROM_MMU_START);
+#endif
+    rodata_start_page &= MMU_VALID_VAL_MASK;
+    rodata_end_page = rodata_start_page + rodata_page_cnt - 1;
+    rodata_flash2spiram_offs = rodata_start_page - psram_start_physical_page;
+    rodata_in_spiram = 1;
+    ESP_DRAM_LOGV("mmu_psram", "Rodata from flash page%d copy to SPIRAM page%d, Offset: %d", rodata_start_page, psram_start_physical_page, rodata_flash2spiram_offs);
+}
+
+uint32_t esp_spiram_rodata_access_enabled(void)
+{
+    return rodata_in_spiram;
+}
+
+int rodata_flash2spiram_offset(void)
+{
+    return rodata_flash2spiram_offs;
+}
+
+uint32_t rodata_flash_start_page_get(void)
+{
+    return rodata_start_page;
+}
+
+uint32_t rodata_flash_end_page_get(void)
+{
+    return rodata_end_page;
+}
+#endif  //#if CONFIG_SPIRAM_RODATA

+ 0 - 1
components/esp_psram/test_apps/psram/main/CMakeLists.txt

@@ -11,4 +11,3 @@ endif()
 # the component can be registered as WHOLE_ARCHIVE
 idf_component_register(SRCS ${srcs}
                        WHOLE_ARCHIVE)
-target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

+ 2 - 1
components/esp_psram/test_apps/psram/main/test_himem.c

@@ -7,6 +7,7 @@
 #include <stdio.h>
 #include <stdbool.h>
 #include <stdint.h>
+#include "inttypes.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "esp_system.h"
@@ -36,7 +37,7 @@ static bool check_mem_seed(int seed, void *mem, int len)
     for (int i = 0; i < len / 4; i++) {
         uint32_t ex = rand_r(&rseed);
         if (ex != *p) {
-            printf("check_mem_seed: %p @ %p has 0x%08x expected 0x%08x\n", mem, p, *p, ex);
+            printf("check_mem_seed: %p @ %p has 0x%08"PRIx32" expected 0x%08"PRIx32"\n", mem, p, *p, ex);
             return false;
         }
         p++;

+ 74 - 25
components/esp_psram/test_apps/psram/main/test_psram.c

@@ -7,6 +7,7 @@
 #include "sdkconfig.h"
 #include <sys/param.h>
 #include <string.h>
+#include "inttypes.h"
 #include "esp_log.h"
 #include "esp_attr.h"
 #include "freertos/FreeRTOS.h"
@@ -20,35 +21,24 @@
 __attribute__((unused)) const static char *TAG = "PSRAM";
 
 
-#if CONFIG_SPIRAM_MODE_OCT
-#define TEST_ALLOC_SIZE    (4 * 1024 * 1024)
-#else
-#define TEST_ALLOC_SIZE    (1 * 1024 * 1024)
-#endif
-
-static bool s_check_valid_psram_alloced_range(const void *p)
-{
-    intptr_t vaddr_start = 0;
-    intptr_t vaddr_end = 0;
-    esp_psram_extram_get_alloced_range(&vaddr_start, &vaddr_end);
-    return (intptr_t)p >= vaddr_start && (intptr_t)p < vaddr_end;
-}
-
 TEST_CASE("test psram heap allocable","[psram]")
 {
-    uint32_t *ext_buffer = (uint32_t *)heap_caps_calloc(TEST_ALLOC_SIZE, 1, MALLOC_CAP_SPIRAM);
+    size_t largest_size = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
+    ESP_LOGI(TAG, "largest size is %zu", largest_size);
+
+    uint32_t *ext_buffer = (uint32_t *)heap_caps_calloc(largest_size, 1, MALLOC_CAP_SPIRAM);
     TEST_ASSERT(ext_buffer);
 
-    uintptr_t start = (uintptr_t)ext_buffer;
-    uintptr_t end = (uintptr_t)ext_buffer + TEST_ALLOC_SIZE;
-    ESP_LOGI(TAG, "test ext buffer start addr is 0x%x, end addr is 0x%x", start, end);
-    TEST_ASSERT(s_check_valid_psram_alloced_range((void *)start) && s_check_valid_psram_alloced_range((void *)end));
+    intptr_t start = (intptr_t)ext_buffer;
+    intptr_t end = (intptr_t)ext_buffer + largest_size;
+    ESP_LOGI(TAG, "test ext buffer start addr is 0x%"PRIxPTR", end addr is 0x%"PRIxPTR, start, end);
+    TEST_ASSERT(esp_psram_check_ptr_addr((void *)start) && esp_psram_check_ptr_addr((void *)end));
 
-    for (int i = 0; i < TEST_ALLOC_SIZE / sizeof(uint32_t); i++) {
+    for (int i = 0; i < largest_size / sizeof(uint32_t); i++) {
         ext_buffer[i] = (i + 1) ^ 0xaaaaaaaa;
     }
 
-    for (int i = 0; i < TEST_ALLOC_SIZE / sizeof(uint32_t); i++) {
+    for (int i = 0; i < largest_size / sizeof(uint32_t); i++) {
         TEST_ASSERT(ext_buffer[i] == ((i + 1) ^ 0xaaaaaaaa));
     }
 
@@ -92,7 +82,7 @@ TEST_CASE("test spi1 flash operation after putting .text and .rodata into psram"
 {
     //Get the partition used for SPI1 erase operation
     const esp_partition_t *part = s_get_partition();
-    ESP_LOGI(TAG, "found partition '%s' at offset 0x%x with size 0x%x", part->label, part->address, part->size);
+    ESP_LOGI(TAG, "found partition '%s' at offset 0x%"PRIx32" with size 0x%"PRIx32, part->label, part->address, part->size);
     //Erase whole region
     TEST_ESP_OK(esp_flash_erase_region(part->flash_chip, part->address, part->size));
 
@@ -117,8 +107,8 @@ TEST_CASE("test spi1 flash operation after putting .text and .rodata into psram"
     TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, NULL));
 
     esp_rom_spiflash_result_t ret;
-    size_t start = part->address;
-    ESP_LOGI(TAG, "test data partition: 0x%x", start);
+    uint32_t start = part->address;
+    ESP_LOGI(TAG, "test data partition: 0x%"PRIx32, start);
     uint32_t sector_num = start / SECTOR_LEN;
 
     TEST_ESP_OK(gptimer_enable(gptimer));
@@ -132,10 +122,69 @@ TEST_CASE("test spi1 flash operation after putting .text and .rodata into psram"
 
     TEST_ESP_OK(gptimer_stop(gptimer));
     TEST_ASSERT(s_timer_cb_exe_times > 0);
-    printf("timer callback runs %d times\n", s_timer_cb_exe_times);
+    printf("timer callback runs %"PRId32" times\n", s_timer_cb_exe_times);
 
     ESP_LOGI(TAG, "Finish");
     TEST_ESP_OK(gptimer_disable(gptimer));
     TEST_ESP_OK(gptimer_del_timer(gptimer));
 }
 #endif  //CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA
+
+
+TEST_CASE("test psram unaligned access", "[psram]")
+{
+    size_t largest_size = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+    ESP_LOGI(TAG, "largest size is %zu", largest_size);
+
+    uint8_t *ext_buffer = (uint8_t *)heap_caps_calloc(largest_size, 1, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+    for (int i = 0; i < largest_size; i++) {
+        ext_buffer[i] = i & 0xff;
+    }
+
+    for (int i = 0; i < largest_size - 4; i += 4) {
+
+        uint8_t *ptr_base = (uint8_t *)(ext_buffer + i);
+
+        for (int j = 1; j < 4; j++) {
+            uint8_t *unaligned_ptr = (uint8_t *)(ptr_base + j);
+            ESP_LOGV(TAG, "i is %d, j is %d, unaligned_ptr addr is %p", i, j, unaligned_ptr);
+
+            uint8_t val_8bit = *unaligned_ptr;
+            ESP_LOGV(TAG, "i is %d, j is %d, val_8bit val  is 0x%"PRIx8, i, j, val_8bit);
+            uint8_t first_byte = (i + j) & 0xff;
+            uint8_t expected_val_8bit = first_byte;
+            TEST_ASSERT(val_8bit == expected_val_8bit);
+
+            /**
+             * If the vaddr doesn't support unaligned access, below codes will generate `LoadStoreAlignment` error.
+             *
+             * This is because below lines includes 16-bit load and 32-bit load:
+             * - l16ui
+             * - l32i.n
+             *
+             * Whereas we use an `add.n` to adding an offset (from 0 to 3) to the original buffer address.
+             *
+             * Therefore we get unaligned access
+             */
+
+            uint16_t val_16bit = *(uint16_t *)unaligned_ptr;
+            ESP_LOGV(TAG, "i is %d, j is %d, val_16bit val  is 0x%"PRIx16, i, j, val_16bit);
+            uint32_t val_32bit = *(uint32_t *)unaligned_ptr;
+            ESP_LOGV(TAG, "i is %d, j is %d, val_32bit val  is 0x%"PRIx32, i, j, val_32bit);
+
+
+            uint8_t second_byte = ((i + j) & 0xff) + 1;
+            uint8_t third_byte = ((i + j) & 0xff) + 2;
+            uint8_t fourth_byte = ((i + j) & 0xff) + 3;
+
+            uint16_t expected_val_16bit = (second_byte << 8) | first_byte;
+            ESP_LOGV(TAG, "i is %d, j is %d, expected_val_16bit val  is 0x%"PRIx16, i, j, expected_val_16bit);
+            TEST_ASSERT(val_16bit == expected_val_16bit);
+            uint32_t expected_val_32bit = (fourth_byte << 24) | (third_byte << 16) | (second_byte << 8) | first_byte;
+            ESP_LOGV(TAG, "i is %d, j is %d, expected_val_32bit val  is 0x%"PRIx32"\n", i, j, expected_val_32bit);
+            TEST_ASSERT(val_32bit == expected_val_32bit);
+        }
+    }
+
+    heap_caps_free(ext_buffer);
+}

+ 1 - 0
components/esp_psram/test_apps/psram/sdkconfig.defaults

@@ -1,2 +1,3 @@
 CONFIG_FREERTOS_HZ=1000
 CONFIG_ESP_TASK_WDT=n
+CONFIG_SPIRAM=y

+ 4 - 0
components/esp_system/ld/esp32/sections.ld.in

@@ -245,6 +245,7 @@ SECTIONS
 
   .flash.appdesc : ALIGN(0x10)
   {
+    _rodata_reserved_start = ABSOLUTE(.);  /* This is a symbol marking the flash.rodata start, this can be used for mmu driver to maintain virtual address */
     _rodata_start = ABSOLUTE(.);
 
     *(.rodata_desc .rodata_desc.*)               /* Should be the first.  App version info.        DO NOT PUT ANYTHING BEFORE IT! */
@@ -318,6 +319,7 @@ SECTIONS
     *(.tbss)
     *(.tbss.*)
     _thread_local_end = ABSOLUTE(.);
+    _rodata_reserved_end = ABSOLUTE(.);  /* This is a symbol marking the flash.rodata end, this can be used for mmu driver to maintain virtual address */
     . = ALIGN(4);
   } >default_rodata_seg
 
@@ -332,6 +334,7 @@ SECTIONS
   .flash.text :
   {
     _stext = .;
+    _instruction_reserved_start = ABSOLUTE(.);  /* This is a symbol marking the flash.text start, this can be used for mmu driver to maintain virtual address */
     _text_start = ABSOLUTE(.);
 
     mapping[flash_text]
@@ -350,6 +353,7 @@ SECTIONS
     . += _esp_flash_mmap_prefetch_pad_size;
 
     _text_end = ABSOLUTE(.);
+    _instruction_reserved_end = ABSOLUTE(.);  /* This is a symbol marking the flash.text end, this can be used for mmu driver to maintain virtual address */
     _etext = .;
 
     /* Similar to _iram_start, this symbol goes here so it is

+ 2 - 2
components/esp_system/ld/esp32s2/memory.ld.in

@@ -114,8 +114,8 @@ MEMORY
   rtc_data_seg(RW) :                 org = 0x3ff9e000, len = 0x2000 - ESP_BOOTLOADER_RESERVE_RTC
 
   /* external memory, covers the dport, dram0, dram1 cacheable address space */
-  extern_ram_seg(RWX)  :             org = 0x3F500000,
-                                     len = 0xA80000
+  extern_ram_seg(RWX)  :             org = 0x3F800000,
+                                     len = 0x780000
 }
 
 #if defined(CONFIG_ESP32S2_USE_FIXED_STATIC_RAM_SIZE)

+ 4 - 4
components/esp_system/ld/esp32s2/sections.ld.in

@@ -264,7 +264,7 @@ SECTIONS
 
   .flash.appdesc : ALIGN(0x10)
   {
-    _rodata_reserved_start = ABSOLUTE(.);
+    _rodata_reserved_start = ABSOLUTE(.);  /* This is a symbol marking the flash.rodata start, this can be used for mmu driver to maintain virtual address */
     _rodata_start = ABSOLUTE(.);
 
     *(.rodata_desc .rodata_desc.*)               /* Should be the first.  App version info.        DO NOT PUT ANYTHING BEFORE IT! */
@@ -336,7 +336,7 @@ SECTIONS
     *(.tbss)
     *(.tbss.*)
     _thread_local_end = ABSOLUTE(.);
-    _rodata_reserved_end = ABSOLUTE(.);
+    _rodata_reserved_end = ABSOLUTE(.);  /* This is a symbol marking the flash.rodata end, this can be used for mmu driver to maintain virtual address */
     . = ALIGN(4);
   } >default_rodata_seg
 
@@ -351,7 +351,7 @@ SECTIONS
   .flash.text :
   {
     _stext = .;
-    _instruction_reserved_start = ABSOLUTE(.);
+    _instruction_reserved_start = ABSOLUTE(.);  /* This is a symbol marking the flash.text start, this can be used for mmu driver to maintain virtual address */
     _text_start = ABSOLUTE(.);
 
     mapping[flash_text]
@@ -370,7 +370,7 @@ SECTIONS
     . += _esp_flash_mmap_prefetch_pad_size;
 
     _text_end = ABSOLUTE(.);
-    _instruction_reserved_end = ABSOLUTE(.);
+    _instruction_reserved_end = ABSOLUTE(.);  /* This is a symbol marking the flash.text end, this can be used for mmu driver to maintain virtual address */
     _etext = .;
 
     /* Similar to _iram_start, this symbol goes here so it is

+ 4 - 4
components/esp_system/ld/esp32s3/sections.ld.in

@@ -244,7 +244,7 @@ SECTIONS
   .flash.text :
   {
     _stext = .;
-    _instruction_reserved_start = ABSOLUTE(.);
+    _instruction_reserved_start = ABSOLUTE(.);  /* This is a symbol marking the flash.text start, this can be used for mmu driver to maintain virtual address */
     _text_start = ABSOLUTE(.);
 
     mapping[flash_text]
@@ -263,7 +263,7 @@ SECTIONS
     . += _esp_flash_mmap_prefetch_pad_size;
 
     _text_end = ABSOLUTE(.);
-    _instruction_reserved_end = ABSOLUTE(.);
+    _instruction_reserved_end = ABSOLUTE(.);  /* This is a symbol marking the flash.text end, this can be used for mmu driver to maintain virtual address */
     _etext = .;
 
     /**
@@ -288,7 +288,7 @@ SECTIONS
     /* Prepare the alignment of the section above. Few bytes (0x20) must be
      * added for the mapping header. */
     . = ALIGN(0x10000) + 0x20;
-    _rodata_reserved_start = .;
+    _rodata_reserved_start = .;  /* This is a symbol marking the flash.rodata start, this can be used for mmu driver to maintain virtual address */
   } > default_rodata_seg
 
   .flash.appdesc : ALIGN(0x10)
@@ -361,7 +361,7 @@ SECTIONS
     *(.tbss)
     *(.tbss.*)
     _thread_local_end = ABSOLUTE(.);
-    _rodata_reserved_end = ABSOLUTE(.);
+    _rodata_reserved_end = ABSOLUTE(.);  /* This is a symbol marking the flash.rodata end, this can be used for mmu driver to maintain virtual address */
     . = ALIGN(4);
   } > default_rodata_seg
 

+ 32 - 0
components/hal/esp32/include/hal/mmu_ll.h

@@ -18,6 +18,38 @@
 extern "C" {
 #endif
 
+/**
+ * Convert MMU virtual address to linear address
+ *
+ * @param vaddr  virtual address
+ *
+ * @return linear address
+ */
+static inline uint32_t mmu_ll_vaddr_to_laddr(uint32_t vaddr)
+{
+    return vaddr & SOC_MMU_LINEAR_ADDR_MASK;
+}
+
+/**
+ * Convert MMU linear address to virtual address
+ *
+ * @param laddr       linear address
+ * @param vaddr_type  virtual address type, could be instruction type or data type. See `mmu_vaddr_t`
+ *
+ * @return virtual address
+ */
+static inline uint32_t mmu_ll_laddr_to_vaddr(uint32_t laddr, mmu_vaddr_t vaddr_type)
+{
+    uint32_t vaddr_base = 0;
+    if (vaddr_type == MMU_VADDR_DATA) {
+        vaddr_base = SOC_MMU_DBUS_VADDR_BASE;
+    } else {
+        vaddr_base = SOC_MMU_IBUS_VADDR_BASE;
+    }
+
+    return vaddr_base | laddr;
+}
+
 /**
  * Get MMU page size
  *

+ 33 - 0
components/hal/esp32s2/include/hal/mmu_ll.h

@@ -8,6 +8,7 @@
 
 #pragma once
 
+#include "stdint.h"
 #include "soc/extmem_reg.h"
 #include "soc/ext_mem_defs.h"
 #include "hal/assert.h"
@@ -18,6 +19,38 @@
 extern "C" {
 #endif
 
+/**
+ * Convert MMU virtual address to linear address
+ *
+ * @param vaddr  virtual address
+ *
+ * @return linear address
+ */
+static inline uint32_t mmu_ll_vaddr_to_laddr(uint32_t vaddr)
+{
+    return vaddr & SOC_MMU_LINEAR_ADDR_MASK;
+}
+
+/**
+ * Convert MMU linear address to virtual address
+ *
+ * @param laddr       linear address
+ * @param vaddr_type  virtual address type, could be instruction type or data type. See `mmu_vaddr_t`
+ *
+ * @return virtual address
+ */
+static inline uint32_t mmu_ll_laddr_to_vaddr(uint32_t laddr, mmu_vaddr_t vaddr_type)
+{
+    uint32_t vaddr_base = 0;
+    if (vaddr_type == MMU_VADDR_DATA) {
+        vaddr_base = SOC_MMU_DBUS_VADDR_BASE;
+    } else {
+        vaddr_base = SOC_MMU_IBUS_VADDR_BASE;
+    }
+
+    return vaddr_base | laddr;
+}
+
 /**
  * Get MMU page size
  *

+ 32 - 0
components/hal/esp32s3/include/hal/mmu_ll.h

@@ -18,6 +18,38 @@
 extern "C" {
 #endif
 
+/**
+ * Convert MMU virtual address to linear address
+ *
+ * @param vaddr  virtual address
+ *
+ * @return linear address
+ */
+static inline uint32_t mmu_ll_vaddr_to_laddr(uint32_t vaddr)
+{
+    return vaddr & SOC_MMU_LINEAR_ADDR_MASK;
+}
+
+/**
+ * Convert MMU linear address to virtual address
+ *
+ * @param laddr       linear address
+ * @param vaddr_type  virtual address type, could be instruction type or data type. See `mmu_vaddr_t`
+ *
+ * @return virtual address
+ */
+static inline uint32_t mmu_ll_laddr_to_vaddr(uint32_t laddr, mmu_vaddr_t vaddr_type)
+{
+    uint32_t vaddr_base = 0;
+    if (vaddr_type == MMU_VADDR_DATA) {
+        vaddr_base = SOC_MMU_DBUS_VADDR_BASE;
+    } else {
+        vaddr_base = SOC_MMU_IBUS_VADDR_BASE;
+    }
+
+    return vaddr_base | laddr;
+}
+
 /**
  * Get MMU page size
  *

+ 8 - 0
components/hal/include/hal/mmu_types.h

@@ -20,6 +20,14 @@ typedef enum {
     MMU_PAGE_64KB = 0x10000,
 } mmu_page_size_t;
 
+/**
+ * MMU virtual address type
+ */
+typedef enum {
+    MMU_VADDR_DATA,
+    MMU_VADDR_INSTRUCTION,
+} mmu_vaddr_t;
+
 /**
  * External physical memory
  */

+ 4 - 0
components/soc/esp32/include/soc/Kconfig.soc_caps.in

@@ -207,6 +207,10 @@ config SOC_SHARED_IDCACHE_SUPPORTED
     bool
     default y
 
+config SOC_MMU_LINEAR_ADDRESS_REGION_NUM
+    int
+    default 5
+
 config SOC_CPU_CORES_NUM
     int
     default 2

+ 37 - 3
components/soc/esp32/include/soc/ext_mem_defs.h

@@ -29,6 +29,7 @@ extern "C" {
 #define DROM0_CACHE_ADDRESS_HIGH    0x3F800000
 
 
+#define BUS_SIZE(bus_name)                 (bus_name##_ADDRESS_HIGH - bus_name##_ADDRESS_LOW)
 #define ADDRESS_IN_BUS(bus_name, vaddr)    ((vaddr) >= bus_name##_ADDRESS_LOW && (vaddr) < bus_name##_ADDRESS_HIGH)
 #define ADDRESS_IN_IRAM0_CACHE(vaddr)      ADDRESS_IN_BUS(IRAM0_CACHE, vaddr)
 #define ADDRESS_IN_IRAM1_CACHE(vaddr)      ADDRESS_IN_BUS(IRAM1_CACHE, vaddr)
@@ -36,10 +37,43 @@ extern "C" {
 #define ADDRESS_IN_DRAM1_CACHE(vaddr)      ADDRESS_IN_BUS(DRAM1_CACHE, vaddr)
 #define ADDRESS_IN_DROM0_CACHE(vaddr)      ADDRESS_IN_BUS(DROM0_CACHE, vaddr)
 
-#define MMU_INVALID                 BIT(8)
+#define MMU_INVALID                           BIT(8)
+
+//MMU entry num, 384 entries that are used in IDF
+#define MMU_ENTRY_NUM                         384
+
+
+#define SOC_MMU_DBUS_VADDR_BASE               0x3E000000
+#define SOC_MMU_IBUS_VADDR_BASE               0x40000000
+
+/*------------------------------------------------------------------------------
+ * MMU Linear Address
+ *----------------------------------------------------------------------------*/
+/**
+ * - 64KB MMU page size: the last 0xFFFF, which is the offset
+ * - 384 MMU entries, needs 0x1FF to hold it.
+ *
+ * Therefore, 0x1FF,FFFF
+ */
+#define SOC_MMU_LINEAR_ADDR_MASK              0x1FFFFFF
+
+#define SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW      (IRAM0_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH     (IRAM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_IRAM1_LINEAR_ADDRESS_LOW      (IRAM1_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_IRAM1_LINEAR_ADDRESS_HIGH     (IRAM1_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_IROM0_LINEAR_ADDRESS_LOW      (IROM0_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_IROM0_LINEAR_ADDRESS_HIGH     (IROM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_DROM0_LINEAR_ADDRESS_LOW      (DROM0_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_DROM0_LINEAR_ADDRESS_HIGH     (DROM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_DRAM1_LINEAR_ADDRESS_LOW      (DRAM1_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_DRAM1_LINEAR_ADDRESS_HIGH     (DRAM1_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+
 
-//MMU entry num
-#define MMU_ENTRY_NUM    256
 
 #ifdef __cplusplus
 }

+ 4 - 1
components/soc/esp32/include/soc/soc_caps.h

@@ -130,8 +130,11 @@
 #define SOC_BROWNOUT_RESET_SUPPORTED 1
 #endif
 
-/*-------------------------- CACHE CAPS --------------------------------------*/
+
+/*-------------------------- CACHE/MMU CAPS ----------------------------------*/
 #define SOC_SHARED_IDCACHE_SUPPORTED            1   //Shared Cache for both instructions and data
+#define SOC_MMU_LINEAR_ADDRESS_REGION_NUM       5
+
 
 /*-------------------------- CPU CAPS ----------------------------------------*/
 #define SOC_CPU_CORES_NUM               2

+ 4 - 0
components/soc/esp32s2/include/soc/Kconfig.soc_caps.in

@@ -235,6 +235,10 @@ config SOC_BROWNOUT_RESET_SUPPORTED
     bool
     default y
 
+config SOC_MMU_LINEAR_ADDRESS_REGION_NUM
+    int
+    default 6
+
 config SOC_CP_DMA_MAX_BUFFER_SIZE
     int
     default 4095

+ 33 - 0
components/soc/esp32s2/include/soc/ext_mem_defs.h

@@ -137,6 +137,39 @@ extern "C" {
 #define CACHE_MEMORY_BANK2_ADDR        0x3FFB4000
 #define CACHE_MEMORY_BANK3_ADDR        0x3FFB6000
 
+
+#define SOC_MMU_DBUS_VADDR_BASE               0x3E000000
+#define SOC_MMU_IBUS_VADDR_BASE               0x40000000
+
+/*------------------------------------------------------------------------------
+ * MMU Linear Address
+ *----------------------------------------------------------------------------*/
+/**
+ * - 64KB MMU page size: the last 0xFFFF, which is the offset
+ * - 384 MMU entries, needs 0x1FF to hold it.
+ *
+ * Therefore, 0x1FF,FFFF
+ */
+#define SOC_MMU_LINEAR_ADDR_MASK              0x1FFFFFF
+
+#define SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW      (IRAM0_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH     (IRAM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_IRAM1_LINEAR_ADDRESS_LOW      (IRAM1_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_IRAM1_LINEAR_ADDRESS_HIGH     (IRAM1_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_DROM0_LINEAR_ADDRESS_LOW      (DROM0_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_DROM0_LINEAR_ADDRESS_HIGH     (DROM0_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_DPORT_LINEAR_ADDRESS_LOW      (DPORT_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_DPORT_LINEAR_ADDRESS_HIGH     (DPORT_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_DRAM1_LINEAR_ADDRESS_LOW      (DRAM1_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_DRAM1_LINEAR_ADDRESS_HIGH     (DRAM1_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
+#define SOC_MMU_DRAM0_LINEAR_ADDRESS_LOW      (DRAM0_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#define SOC_MMU_DRAM0_LINEAR_ADDRESS_HIGH     (DRAM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+
 #ifdef __cplusplus
 }
 #endif

+ 3 - 0
components/soc/esp32s2/include/soc/soc_caps.h

@@ -113,6 +113,9 @@
 /*-------------------------- BROWNOUT CAPS -----------------------------------*/
 #define SOC_BROWNOUT_RESET_SUPPORTED 1
 
+/*-------------------------- CACHE/MMU CAPS ----------------------------------*/
+#define SOC_MMU_LINEAR_ADDRESS_REGION_NUM        6
+
 /*-------------------------- CP-DMA CAPS -------------------------------------*/
 #define SOC_CP_DMA_MAX_BUFFER_SIZE (4095) /*!< Maximum size of the buffer that can be attached to descriptor */
 

+ 4 - 0
components/soc/esp32s3/include/soc/Kconfig.soc_caps.in

@@ -299,6 +299,10 @@ config SOC_BROWNOUT_RESET_SUPPORTED
     bool
     default y
 
+config SOC_MMU_LINEAR_ADDRESS_REGION_NUM
+    int
+    default 1
+
 config SOC_CPU_CORES_NUM
     int
     default 2

+ 40 - 0
components/soc/esp32s3/include/soc/ext_mem_defs.h

@@ -104,6 +104,46 @@ extern "C" {
 #define CACHE_MEMORY_DBANK0_ADDR        0x3fcf0000
 #define CACHE_MEMORY_DBANK1_ADDR        0x3fcf8000
 
+
+#define SOC_MMU_DBUS_VADDR_BASE               0x3C000000
+#define SOC_MMU_IBUS_VADDR_BASE               0x42000000
+
+/*------------------------------------------------------------------------------
+ * MMU Linear Address
+ *----------------------------------------------------------------------------*/
+/**
+ * - 64KB MMU page size: the last 0xFFFF, which is the offset
+ * - 512 MMU entries, needs 0x1FF to hold it.
+ *
+ * Therefore, 0x1FF,FFFF
+ */
+#define SOC_MMU_LINEAR_ADDR_MASK              0x1FFFFFF
+
+/**
+ * - If high linear address isn't 0, this means MMU can recognize these addresses
+ * - If high linear address is 0, this means MMU linear address range is equal or smaller than vaddr range.
+ *   Under this condition, we use the max linear space.
+ */
+#define SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW      (IRAM0_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#if ((IRAM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK) > 0)
+#define SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH     (IRAM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+#else
+#define SOC_MMU_IRAM0_LINEAR_ADDRESS_HIGH     (SOC_MMU_LINEAR_ADDR_MASK + 1)
+#endif
+
+#define SOC_MMU_DRAM0_LINEAR_ADDRESS_LOW      (DRAM0_CACHE_ADDRESS_LOW & SOC_MMU_LINEAR_ADDR_MASK)
+#if ((DRAM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK) > 0)
+#define SOC_MMU_DRAM0_LINEAR_ADDRESS_HIGH     (DRAM0_CACHE_ADDRESS_HIGH & SOC_MMU_LINEAR_ADDR_MASK)
+#else
+#define SOC_MMU_DRAM0_LINEAR_ADDRESS_HIGH     (SOC_MMU_LINEAR_ADDR_MASK + 1)
+#endif
+
+/**
+ * I/D share the MMU linear address range
+ */
+_Static_assert(SOC_MMU_IRAM0_LINEAR_ADDRESS_LOW == SOC_MMU_DRAM0_LINEAR_ADDRESS_LOW, "IRAM0 and DRAM0 linear address should be same");
+
+
 #ifdef __cplusplus
 }
 #endif

+ 3 - 0
components/soc/esp32s3/include/soc/soc_caps.h

@@ -110,6 +110,9 @@
 /*-------------------------- BROWNOUT CAPS -----------------------------------*/
 #define SOC_BROWNOUT_RESET_SUPPORTED 1
 
+/*-------------------------- CACHE/MMU CAPS ----------------------------------*/
+#define SOC_MMU_LINEAR_ADDRESS_REGION_NUM       (1U)
+
 /*-------------------------- CPU CAPS ----------------------------------------*/
 #define SOC_CPU_CORES_NUM               2
 #define SOC_CPU_INTR_NUM                32

+ 1 - 1
components/spi_flash/flash_mmap.c

@@ -42,7 +42,7 @@
 
 #if CONFIG_SPIRAM
 #include "esp_private/esp_psram_extram.h"
-#include "esp_private/mmu.h"
+#include "esp_private/mmu_psram_flash.h"
 #endif
 
 #ifndef NDEBUG