YOGYUI

ESP-Matter::ESP32-WROOM-32E-N8R2 외부 메모리(PSRAM) 연동 결과 본문

홈네트워크(IoT)/Matter

ESP-Matter::ESP32-WROOM-32E-N8R2 외부 메모리(PSRAM) 연동 결과

요겨 2023. 10. 24. 01:28
반응형

ESP-Matter SDK with ESP32-WROOM-32E-N8R2 External Memory (PSRAM)

지난 글에서 ESP-Matter SDK의 매터 관련 인스턴스, Task 스택 등을 외부 메모리 (Pseudo SRAM, PSRAM)에 할당하는 방법에 대해 알아봤다

지난 글 링크: ESP-Matter::External Memory(PSRAM) 관련 설정

 

ESP-Matter::External Memory(PSRAM) 관련 설정

Utilize PSRAM on ESP32 SoC using ESP-Matter ESP-Matter SDK를 이용해서 ESP32 계열 칩에 Matter 어플리케이션을 굉장히 손쉽게 작성할 수 있다 ESP32 SoC는 대부분 수백KB 수준의 SRAM을 탑재하고 있는데, 단일 엔드포

yogyui.tistory.com

CONFIG_SPIRAM=y
CONFIG_SPIRAM_TYPE_AUTO=y
CONFIG_SPIRAM_BOOT_INIT=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
CONFIG_SPIRAM_USE_MALLOC=y
CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=n
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_MEMTEST=y
CONFIG_SPIRAM_2T_MODE=n
CONFIG_D0WD_PSRAM_CS_IO=16
CONFIG_D0WD_PSRAM_CLK_IO=6

CONFIG_ESP_MATTER_MEM_ALLOC_MODE_EXTERNAL=y

안타깝게도, ESP32 모듈인 ESP32-WROOM-32E모듈 하단의 SPI 핀아웃이 모두 NC 처리되어서 SPI RAM을 추가로 장착하여 사용할 수가 없다 (ㅠㅠ)

https://devicein.tistory.com/45

IoT 장비에 사용되는 MCU, SoC에 적용되는 보안 규칙이 시간이 지날수록 강화되고 있다
ESP32-WROOM-32D만 해도 하부의 SPI 핀들을 통해 펌웨어 및 non-volatile 데이터가 저장되어 있는 플래시(Flash) 메모리에 다이렉트로 접근할 수 있었는데, 32E 모듈부터는 이런 위험성을 원천적으로 차단할 수 있다
ESP-IDF의 sdkconfig 설정 파일을 통해 UART0 통신 경로 및 JTAG를 모두 차단해 해커가 펌웨어에 접근하는 것을 막기 위한 조치를 취할 수도 있다!

32D 모듈은 단종되었기에 결국 32E 모듈에 PSRAM을 적용하기 위해서는 Espressif 사에서 제조 공정 시 PSRAM을 내장한 모듈을 구매하는 수 밖에 없었다..

esp32-wroom-32e_esp32-wroom-32ue_datasheet_en.pdf
0.99MB

플래시 사이즈에 따라 품명이 달라지긴 하지만, PSRAM은 2MB 장착된 모듈이 유일하다

나는 펌웨어 사이즈가 좀 큰지라 8MB Flash와 PSRAM 2MB가 장착된 ESP32-WROOM-32E-N8R2 모듈을 구했다

※ Digi-key 등에서 구매하기에는 납기가 26주가량으로 너무 길어서, Matter DAC pre-provisioned 샘플 칩을 우선 보내달라고 하니 1주일만에 도착했다!

 

테스트에 사용된 소프트웨어 환경은 다음과 같다

  • [esp-idf] tag v5.1.1 (commit id = e088c3766ba440e72268b458a68f27b6e7d63986)
  • [esp-matter] (commit id = 79dfcc7b9db37d80105e2bbb39a680a0670296d5)
  • [connectedhomeip] tag SVE_23_09/rc1-52-gecc0d63cf7 (commit id = ecc0d63cf7eb91f4017bf8c264b53cf690420eb5)

1. 하드웨어 제약 사항

데이터시트에 따르면, PSRAM의 Chip Enable 핀이 ESP32의 GPIO16번(모듈 핀아웃 27) 핀과 연결되어 있으며, PSRAM을 정상적으로 동작시키기 위해서는 풀업(Pull-up) 저항만 장착해두고 다른 용도로 사용해서는 안된다 (PSRAM의 클럭, SCLK 라인은 플래시 메모리의 SCLK 라인을 공유하며 이는 GPIO6 핀이다)

 

하드웨어는 다음과 같이 준비해준다

단순히 IO16 풀업만 보여주기 위한 회로도

펌웨어 상에서는 sdkconfig에서 다음 항목을 반드시 기입해줘야 한다 

(설정하지 않으면 CONFIG_D0WD_PSRAM_CLK_IO가 17로 설정돼 PSRAM이 정상동작하지 않는다)

CONFIG_D0WD_PSRAM_CS_IO=16
CONFIG_D0WD_PSRAM_CLK_IO=6

2. ESP32 부트로더 부팅 로그 (Bootloader Log)

하드웨어를 준비하기만 하면 기존의 펌웨어를 sdkconfig만 바꿔서 빌드하면 된다

esp-idf의 idf.py로 플래시 후 부트로더의 부팅 로그를 확인해보자

로그의 태그 명으로 'quad_psram'과 'esp_psram'이 사용되며, 'Found 2MB PSRAM device' 로그와 같이 PSRAM을 정상적으로 발견하며 초기화한 것을 알 수 있다

 

로그를 좀 더 살펴보면,

"Adding pool of 2048KB of PSRAM memory to heap allocator", 즉 2MB의 PSRAM을 힙 영역 메모리 풀(memory pool)로 추가해 어플리케이션에서 접근할 수 있게 된다

3. 힙 메모리 영역(Heap memory) 사이즈 체크

동적으로 할당할 수 있는 힙 (Heap) 메모리 영역의 크기를 확인해보자

esp-idf의 heap_caps_get_total_size 함수를 사용하면 전체 힙 영역 크기를 알 수 있으며, heap_caps_get_free_size 함수를 사용하면 사용 가능한 여분의 힙 사이즈를 알 수 있다

#include "esp_heap_caps.h"
#include "esp_log.h"

static const char *TAG = "logger";

void print_heap_info()
{
    size_t heap_total_size = heap_caps_get_total_size(MALLOC_CAP_8BIT);
    size_t heap_free_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
    
    ESP_LOGI(TAG, "Heap total size: %d", heap_total_size);
    ESP_LOGI(TAG, "Heap free size: %d (%g%% used)", 
        heap_free_size, 
        float(heap_total_size - heap_free_size) / float(heap_total_size) * 100.f);
}

힙 전체 사이즈가 2,339,447 바이트로 약 2.23MB!

고작 520KB 크기의 SRAM만 내장되었고, 그마저도 일부만 사용가능했었던 지난날을 돌이켜보면 눈물날만큼 많은 메모리를 확보하게 됐다 (거의 14배 수준)

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/memory-types.html

4. Matter 인스턴스 할당 테스트

esp-matter sdk에서 매터 관련 변수/인스턴스/태스크 스택 등을 PSRAM에 할당할 수 있게 하기 위해서는 sdkconfig의 CONFIG_ESP_MATTER_MEM_ALLOC_MODE_EXTERNAL 항목을 활성화해줘야 한다

CONFIG_ESP_MATTER_MEM_ALLOC_MODE_EXTERNAL=y

이 옵션을 활성화하면 esp_matter_mem.cpp 소스코드에서 확인할 수 있듯이, 메모리 할당을 위해 heap_caps_malloc 혹은 heap_caps_calloc 함수 호출 시 MALLOC_CAP_SPIRAM 플래그를 사용해서 PSRAM을 활용할 수 있게 된다

/* esp_matter_mem.cpp 소스코드 중 일부 */
IRAM_ATTR void *esp_matter_mem_calloc(size_t n, size_t size)
{
#if CONFIG_ESP_MATTER_MEM_ALLOC_MODE_INTERNAL
    return heap_caps_calloc(n, size, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
#elif CONFIG_ESP_MATTER_MEM_ALLOC_MODE_EXTERNAL
    return heap_caps_calloc(n, size, MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT);
#elif CONFIG_ESP_MATTER_MEM_ALLOC_MODE_IRAM_8BIT
    return heap_caps_calloc_prefer(n, size, 2, MALLOC_CAP_INTERNAL|MALLOC_CAP_IRAM_8BIT, MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT);
#else
    return calloc(n, size);
#endif
}

IRAM_ATTR void *esp_matter_mem_realloc(void *ptr, size_t size)
{
#if CONFIG_ESP_MATTER_MEM_ALLOC_MODE_INTERNAL
    return heap_caps_realloc(ptr, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#elif CONFIG_ESP_MATTER_MEM_ALLOC_MODE_EXTERNAL
    return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#elif CONFIG_ESP_MATTER_MEM_ALLOC_MODE_IRAM_8BIT
    return heap_caps_realloc_prefer(ptr, size, 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_IRAM_8BIT, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#else
    return realloc(ptr, size);
#endif
}

 

테스트를 위해 Matter endpoint 2개를 만든 뒤, %p 출력 포맷을 통해 메모리 주소값을 로그로 찍어보자

(하나는 root node, 다른 하나는 브릿지 디바이스를 위한 aggregator 엔드포인트)

#include "esp_matter_endpoint.h"
#include "esp_matter_attribute_utils.h"
#include "esp_matter_identify.h"
#include "CHIPDeviceEvent.h"
#include "esp_log.h"

static const char *TAG = "logger";

esp_err_t matter_attribute_update_callback(esp_matter::attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data)
{
    return ESP_OK;
}

esp_err_t matter_identification_callback(esp_matter::identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id, uint8_t effect_variant, void *priv_data)
{
    return ESP_OK;
}

void matter_event_callback(const ChipDeviceEvent *event, intptr_t arg)
{
}

void initialize() 
{
    /* ... */
    
    esp_matter::node::config_t node_config;
    esp_matter::node_t* root_node = esp_matter::node::create(&node_config, matter_attribute_update_callback, matter_identification_callback);
    ESP_LOGI(TAG, "Root node (endpoint 0) added (address: %p)", root_node);
    
    esp_matter::endpoint::aggregator::config_t config_aggregator;
    esp_matter::endpoint_t* ep_aggregator = esp_matter::endpoint::aggregator::create(root_node, &config_aggregator, esp_matter::ENDPOINT_FLAG_NONE, nullptr);
    uint16_t ep_aggregator_id = esp_matter::endpoint::get_id(ep_aggregator);
    ESP_LOGI(TAG, "Aggregator node (endpoint %d) added (address: %p)", ep_aggregator_id, ep_aggregator);
    
    /* ... */
    esp_matter::start(matter_event_callback);
}

로그를 보면 두 엔드포인트 인스턴스의 메모리 주소값이 각각 0x3F800B18, 0x3F8024F0 이다

 

PSRAM의 메모리 주소 매핑을 알아보기 위해 esp32 기술문서를 열어보자

esp32_technical_reference_manual_en.pdf
9.18MB

기술 문서에 따르면 External SRAM, 즉 R/W 가능한 최대 4MB PSRAM의 주소값은 0x3F800000 ~ 0x3FBFFFFF 까지 매핑되어 있다

따라서 Matter 관련 인스턴스들이 정상적으로 PSRAM 영역에 할당된 것을 알 수 있다!

 

정확히 측정해보진 않았지만, 경험적으로 매터 엔드포인트 하나당 메모리는 6000바이트 정도가 필요하므로, 2MB PSRAM 장착 후에는 200 ~ 300개 정도의 엔드포인트는 문제없이 추가할 수 있다 ㅎㅎ

단순한 전등이나 스위치 같이 1~2개의 엔드포인트만 필요한 장치의 경우 오버스펙... ㅋㅋ

 

하지만 현재 개발중인 bridged 디바이스의 경우 수십개의 엔드포인트가 추가될 수 있기에 기존의 200KB 언저리의 SRAM은 상품화가 불가능했었지만, Espressif 측으로부터 PSRAM이 내장된 모듈을 주문할 수 있기 때문에 PSRAM 칩 선정 및 PCB 변경 과정을 생략할 수 있게 됐다

 

다만 아쉬운건 현재는 PSRAM 사이즈가 2MB로 한정된다는 점 (이왕 만드는 거 4MB 모듈도 만들지... -_-)

 

 

 

반응형