[ESP32] ESP32のhimemってなに？

# 一
The ESP32 can access external SPI RAM transparently, so you can use it as normal memory in your program code. However, because the address space for external memory is limited in size, only the first 4MiB can be used as such. Access to the remaining memory is still possible, however this needs to go through a bankswitching scheme controlled by the himem API.

ESP32は外部SPIRAMに透過的にアクセスできるため、プログラムコードで通常のメモリとして使用できます。ただし、外部メモリのアドレス空間はサイズに制限があるため、最初の4MiBのみをそのまま使用できます。残りのメモリへのアクセスは引き続き可能ですが、これは、himemAPIによって制御されるバンク切り替えスキームを経由する必要があります。

# 二

Specifically, what is implemented by the himem API is a bankswitching scheme. Hardware-wise, the 4MiB region for external SPI RAM is mapped into the CPU address space by a MMU, which maps a configurable 32K bank/page of external SPI RAM into each of the 32K pages in the 4MiB region accessed by the CPU. For external memories that are <=4MiB, this MMU is configured to unity mapping, effectively mapping each CPU address 1-to-1 to the external SPI RAM address.


具体的には、himem APIによって実装されるのは、バンク切り替えスキームです。ハードウェア的には、外部SPI RAMの4MiB領域は、MMUによってCPUアドレス空間にマップされます。MMUは、構成可能な32Kバンク/ページの外部SPI RAMを、CPUがアクセスする4MiB領域の各32Kページにマップします。 <= 4MiBの外部メモリの場合、このMMUはユニティマッピングに構成され、各CPUアドレスを1対1で外部SPIRAMアドレスに効果的にマッピングします。

# 三
In order to use the himem API, you have to enable it in the menuconfig using CONFIG_SPIRAM_BANKSWITCH_ENABLE, as well as set the amount of banks reserved for this in CONFIG_SPIRAM_BANKSWITCH_RESERVE. This decreases the amount of external memory allocated by functions like malloc(), but it allows you to use the himem api to map any of the remaining memory into the reserved banks.

himem APIを使用するには、CONFIG_SPIRAM_BANKSWITCH_ENABLEを使用してmenuconfigで有効にする必要があります。また、CONFIG_SPIRAM_BANKSWITCH_RESERVEでこのために予約されているバンクの数を設定する必要があります。これにより、malloc（）などの関数によって割り当てられる外部メモリの量が減りますが、himem apiを使用して、残りのメモリを予約済みバンクにマップできます。

# 四
The himem API is more-or-less an abstraction of the bankswitching scheme: it allows you to claim one or more banks of address space (called ‘regions’ in the API) as well as one or more of banks of memory to map into the ranges.

himem APIは、多かれ少なかれバンク切り替えスキームを抽象化したものです。これにより、1つ以上のアドレス空間バンク（APIでは「リージョン」と呼ばれます）と、1つ以上のメモリバンクを要求してマッピングできます。範囲。

# himem.h
- https://github.com/espressif/esp-idf/blob/1cb31e5/components/esp32/include/esp32/himem.h

# Arduino esp32用のHimen
- https://github.com/TridentTD/arduino_esp32_himem


# 分析
- 何が違いますかね。
- 比べてみましょう。

## esp32-hal-psram.c

### 基本1.0.6バージョン

```c++:esp32-hal-psram.h
// Copyright 2015-2016 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.

#ifndef _ESP32_HAL_PSRAM_H_
#define _ESP32_HAL_PSRAM_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "sdkconfig.h"

#ifndef BOARD_HAS_PSRAM
#ifdef CONFIG_SPIRAM_SUPPORT
#undef CONFIG_SPIRAM_SUPPORT
#endif
#ifdef CONFIG_SPIRAM
#undef CONFIG_SPIRAM
#endif
#endif

bool psramInit();
bool psramFound();

void *ps_malloc(size_t size);
void *ps_calloc(size_t n, size_t size);
void *ps_realloc(void *ptr, size_t size);

#ifdef __cplusplus
}
#endif

#endif /* _ESP32_HAL_PSRAM_H_ */
```

### arduino_esp32_himem

```c++:esp32-hal-psram.h
// Copyright 2015-2016 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.

#ifndef _ESP32_HAL_PSRAM_H_
#define _ESP32_HAL_PSRAM_H_

#ifdef __cplusplus
extern "C" {
#endif
#include "himem_support/spiram.h" //TridentTD
#include "himem_support/himem.h"  //TridentTD

bool psramInit();
bool psramFound();

void *ps_malloc(size_t size);
void *ps_calloc(size_t n, size_t size);
void *ps_realloc(void *ptr, size_t size);

#ifdef __cplusplus
}
#endif

#endif /* _ESP32_HAL_PSRAM_H_ */
```

## esp32-hal-psram.c
### 基本1.0.6バージョン

```c++:esp32-hal-psram.c
// Copyright 2015-2016 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 "esp32-hal.h"

#if CONFIG_SPIRAM_SUPPORT || CONFIG_SPIRAM
#include "soc/efuse_reg.h"
#include "esp_heap_caps.h"

#include "esp_system.h"
#ifdef ESP_IDF_VERSION_MAJOR // IDF 4+
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
#include "esp32/spiram.h"
#elif CONFIG_IDF_TARGET_ESP32S2
#include "esp32s2/spiram.h"
#include "esp32s2/rom/cache.h"
#else 
#error Target CONFIG_IDF_TARGET is not supported
#endif
#else // ESP32 Before IDF 4.0
#include "esp_spiram.h"
#endif

static volatile bool spiramDetected = false;
static volatile bool spiramFailed = false;

bool psramInit(){
    if (spiramDetected) {
        return true;
    }
#ifndef CONFIG_SPIRAM_BOOT_INIT
    if (spiramFailed) {
        return false;
    }
#if CONFIG_IDF_TARGET_ESP32
    uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG);
    uint32_t pkg_ver = chip_ver & 0x7;
    if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
        spiramFailed = true;
        log_w("PSRAM not supported!");
        return false;
    }
#elif CONFIG_IDF_TARGET_ESP32S2
    extern void esp_config_data_cache_mode(void);
    esp_config_data_cache_mode();
    Cache_Enable_DCache(0);
#endif
    if (esp_spiram_init() != ESP_OK) {
        spiramFailed = true;
        log_w("PSRAM init failed!");
#if CONFIG_IDF_TARGET_ESP32
        pinMatrixOutDetach(16, false, false);
        pinMatrixOutDetach(17, false, false);
#endif
        return false;
    }
    esp_spiram_init_cache();
    if (!esp_spiram_test()) {
        spiramFailed = true;
        log_e("PSRAM test failed!");
        return false;
    }
    if (esp_spiram_add_to_heapalloc() != ESP_OK) {
        spiramFailed = true;
        log_e("PSRAM could not be added to the heap!");
        return false;
    }
#if CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL && !CONFIG_ARDUINO_ISR_IRAM
        heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL);
#endif
#endif
    spiramDetected = true;
    log_d("PSRAM enabled");
    return true;
}

bool ARDUINO_ISR_ATTR psramFound(){
    return spiramDetected;
}

void ARDUINO_ISR_ATTR *ps_malloc(size_t size){
    if(!spiramDetected){
        return NULL;
    }
    return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}

void ARDUINO_ISR_ATTR *ps_calloc(size_t n, size_t size){
    if(!spiramDetected){
        return NULL;
    }
    return heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}

void ARDUINO_ISR_ATTR *ps_realloc(void *ptr, size_t size){
    if(!spiramDetected){
        return NULL;
    }
    return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}

#else

bool psramInit(){
    return false;
}

bool ARDUINO_ISR_ATTR psramFound(){
    return false;
}

void ARDUINO_ISR_ATTR *ps_malloc(size_t size){
    return NULL;
}

void ARDUINO_ISR_ATTR *ps_calloc(size_t n, size_t size){
    return NULL;
}

void ARDUINO_ISR_ATTR *ps_realloc(void *ptr, size_t size){
    return NULL;
}

#endif
```

### arduino_esp32_himem

```c++:esp32-hal-psram.c

#include "esp32-hal.h"

#if CONFIG_SPIRAM_SUPPORT
// #include "esp_spiram.h"
#include "himem_support/spiram.h" //TridentTD
#include "soc/efuse_reg.h"
#include "esp_heap_caps.h"

static volatile bool spiramDetected = false;
static volatile bool spiramFailed = false;

bool psramInit(){
    if (spiramDetected) {
        return true;
    }
#ifndef CONFIG_SPIRAM_BOOT_INIT
    if (spiramFailed) {
        return false;
    }
    uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG);
    uint32_t pkg_ver = chip_ver & 0x7;
    if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 || pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
        spiramFailed = true;
        log_w("PSRAM not supported!");
        return false;
    }
    esp_spiram_init_cache();
    if (esp_spiram_init() != ESP_OK) {
        spiramFailed = true;
        log_w("PSRAM init failed!");
        pinMatrixOutDetach(16, false, false);
        pinMatrixOutDetach(17, false, false);
        return false;
    }
    if (!esp_spiram_test()) {
        spiramFailed = true;
        log_e("PSRAM test failed!");
        return false;
    }
    if (esp_spiram_add_to_heapalloc() != ESP_OK) {
        spiramFailed = true;
        log_e("PSRAM could not be added to the heap!");
        return false;
    }
#endif
    spiramDetected = true;
    log_d("PSRAM enabled");

    esp_himem_init();     //TridentTD
    return true;
}

bool IRAM_ATTR psramFound(){
    return spiramDetected;
}

void IRAM_ATTR *ps_malloc(size_t size){
    if(!spiramDetected){
        return NULL;
    }
    return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}

void IRAM_ATTR *ps_calloc(size_t n, size_t size){
    if(!spiramDetected){
        return NULL;
    }
    return heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}

void IRAM_ATTR *ps_realloc(void *ptr, size_t size){
    if(!spiramDetected){
        return NULL;
    }
    return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
}

#else

bool psramInit(){
    return false;
}

bool IRAM_ATTR psramFound(){
    return false;
}

void IRAM_ATTR *ps_malloc(size_t size){
    return NULL;
}

void IRAM_ATTR *ps_calloc(size_t n, size_t size){
    return NULL;
}

void IRAM_ATTR *ps_realloc(void *ptr, size_t size){
    return NULL;
}

#endif
```

# 参考

- https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/himem.html