feat(psram): add psram noinit segment support on S2/S3/P4/C5

Closes https://github.com/espressif/esp-idf/issues/14253
This commit is contained in:
Chen Jichang 2024-08-01 15:56:03 +08:00
parent 15825dc531
commit 1c1f536235
18 changed files with 142 additions and 26 deletions

View File

@ -3,6 +3,7 @@
components/esp_common/test_apps/esp_common: components/esp_common/test_apps/esp_common:
disable: disable:
- if: CONFIG_NAME == "psram" and SOC_SPIRAM_SUPPORTED != 1 - if: CONFIG_NAME == "psram" and SOC_SPIRAM_SUPPORTED != 1
- if: CONFIG_NAME == "psram" and IDF_TARGET in ["esp32c5"] - if: CONFIG_NAME == "psram_noinit" and SOC_SPIRAM_SUPPORTED != 1
- if: CONFIG_NAME == "psram_noinit" and IDF_TARGET in ["esp32c61"]
temporary: true temporary: true
reason: esp32c5 is not supported yet # TODO: IDF-8689 reason: esp32c61 is not supported yet # TODO: IDF-9293

View File

@ -1,4 +1,4 @@
idf_component_register(SRCS "test_app_main.c" "test_attr.c" idf_component_register(SRCS "test_app_main.c" "test_attr.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
PRIV_REQUIRES unity esp_psram PRIV_REQUIRES unity esp_mm esp_psram
WHOLE_ARCHIVE) WHOLE_ARCHIVE)

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -10,10 +10,17 @@
#include "esp_log.h" #include "esp_log.h"
#include "soc/soc.h" #include "soc/soc.h"
#include "esp_system.h" #include "esp_system.h"
#include "hal/cache_ll.h"
#include "hal/cache_hal.h"
#include "esp_cache.h"
#if CONFIG_IDF_TARGET_ESP32 #if CONFIG_IDF_TARGET_ESP32
#include "esp_private/esp_psram_extram.h" #include "esp_private/esp_psram_extram.h"
#endif #endif
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
static const char *TAG = "attr_test";
extern int _rtc_noinit_start; extern int _rtc_noinit_start;
extern int _rtc_noinit_end; extern int _rtc_noinit_end;
extern int _rtc_data_start; extern int _rtc_data_start;
@ -44,6 +51,7 @@ static EXT_RAM_NOINIT_ATTR uint32_t s_noinit_ext;
static bool data_in_segment(void *ptr, int *seg_start, int *seg_end) static bool data_in_segment(void *ptr, int *seg_start, int *seg_end)
{ {
ESP_LOGV(TAG, "ptr:%p seg_start:%p seg_end:%p", ptr, seg_start, seg_end);
return ((intptr_t)ptr < (intptr_t)seg_end) && \ return ((intptr_t)ptr < (intptr_t)seg_end) && \
((intptr_t)ptr >= (intptr_t)seg_start); ((intptr_t)ptr >= (intptr_t)seg_start);
} }
@ -94,7 +102,13 @@ static void write_spiram_and_reset(void)
} }
printf("Flushing cache\n"); printf("Flushing cache\n");
// Flush the cache out to SPIRAM before resetting. // Flush the cache out to SPIRAM before resetting.
#if CONFIG_IDF_TARGET_ESP32
esp_psram_extram_writeback_cache(); esp_psram_extram_writeback_cache();
#else
size_t psram_alignment = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
uint32_t ext_noinit_size = sizeof(s_noinit_buffer);
TEST_ESP_OK(esp_cache_msync(&s_noinit_buffer, ALIGN_UP(ext_noinit_size, psram_alignment), ESP_CACHE_MSYNC_FLAG_DIR_C2M));
#endif
printf("Restarting\n"); printf("Restarting\n");
// Reset to test that noinit memory is left intact. // Reset to test that noinit memory is left intact.

View File

@ -1,5 +1,7 @@
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
from typing import Any
import pytest import pytest
from pytest_embedded import Dut from pytest_embedded import Dut
@ -19,6 +21,10 @@ def test_esp_common(dut: Dut) -> None:
# psram noinit attr tests with psram enabled # psram noinit attr tests with psram enabled
@pytest.mark.esp32 @pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.esp32p4
@pytest.mark.esp32c5
@pytest.mark.generic @pytest.mark.generic
@pytest.mark.parametrize( @pytest.mark.parametrize(
'config', 'config',
@ -33,6 +39,10 @@ def test_esp_attr_psram_noinit(dut: Dut) -> None:
# psram noinit memory tests with psram enabled # psram noinit memory tests with psram enabled
@pytest.mark.esp32 @pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.esp32p4
@pytest.mark.esp32c5
@pytest.mark.generic @pytest.mark.generic
@pytest.mark.supported_targets @pytest.mark.supported_targets
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -42,8 +52,8 @@ def test_esp_attr_psram_noinit(dut: Dut) -> None:
], ],
indirect=True, indirect=True,
) )
def run_multiple_stages(dut: Dut, test_case_num: int, stages: int) -> None: def test_esp_attr_psram_noinit_multiple_stages(case_tester: Any) -> None:
dut.run_all_single_board_cases() case_tester.run_all_multi_stage_cases()
# psram attr tests with psram enabled # psram attr tests with psram enabled
@ -51,6 +61,7 @@ def run_multiple_stages(dut: Dut, test_case_num: int, stages: int) -> None:
@pytest.mark.esp32s2 @pytest.mark.esp32s2
@pytest.mark.esp32s3 @pytest.mark.esp32s3
@pytest.mark.esp32p4 @pytest.mark.esp32p4
@pytest.mark.esp32c5
@pytest.mark.generic @pytest.mark.generic
@pytest.mark.parametrize( @pytest.mark.parametrize(
'config', 'config',

View File

@ -1,6 +1,5 @@
# For EXT_RAM_NOINIT_ATTR # For EXT_RAM_NOINIT_ATTR
CONFIG_IDF_TARGET="esp32"
CONFIG_SPIRAM=y CONFIG_SPIRAM=y
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y

View File

@ -3,4 +3,6 @@
CONFIG_IDF_TARGET="esp32p4" CONFIG_IDF_TARGET="esp32p4"
CONFIG_SPIRAM=y CONFIG_SPIRAM=y
CONFIG_SPIRAM_XIP_FROM_PSRAM=y CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y

View File

@ -3,5 +3,6 @@
CONFIG_IDF_TARGET="esp32s2" CONFIG_IDF_TARGET="esp32s2"
CONFIG_SPIRAM=y CONFIG_SPIRAM=y
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_RODATA=y CONFIG_SPIRAM_RODATA=y
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y

View File

@ -3,5 +3,6 @@
CONFIG_IDF_TARGET="esp32s3" CONFIG_IDF_TARGET="esp32s3"
CONFIG_SPIRAM=y CONFIG_SPIRAM=y
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y
CONFIG_SPIRAM_RODATA=y CONFIG_SPIRAM_RODATA=y
CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y

View File

@ -23,7 +23,8 @@ config SPIRAM_IGNORE_NOTFOUND
ESP_WIFI_CACHE_TX_BUFFER_NUM and use static WiFi Tx buffer may cause potential memory exhaustion issues. ESP_WIFI_CACHE_TX_BUFFER_NUM and use static WiFi Tx buffer may cause potential memory exhaustion issues.
Suggest disable SPIRAM_TRY_ALLOCATE_WIFI_LWIP. Suggest disable SPIRAM_TRY_ALLOCATE_WIFI_LWIP.
Suggest disable ESP_WIFI_AMSDU_TX_ENABLED. Suggest disable ESP_WIFI_AMSDU_TX_ENABLED.
Suggest disable ESP_WIFI_CACHE_TX_BUFFER_NUM, need clear CONFIG_FEATURE_CACHE_TX_BUF_BIT of config->feature_caps. Suggest disable ESP_WIFI_CACHE_TX_BUFFER_NUM,
need clear CONFIG_FEATURE_CACHE_TX_BUF_BIT of config->feature_caps.
Suggest change ESP_WIFI_TX_BUFFER from static to dynamic. Also suggest to adjust some buffer numbers to the Suggest change ESP_WIFI_TX_BUFFER from static to dynamic. Also suggest to adjust some buffer numbers to the
values used without PSRAM case. Such as, ESP_WIFI_STATIC_TX_BUFFER_NUM, ESP_WIFI_DYNAMIC_TX_BUFFER_NUM. values used without PSRAM case. Such as, ESP_WIFI_STATIC_TX_BUFFER_NUM, ESP_WIFI_DYNAMIC_TX_BUFFER_NUM.
@ -109,7 +110,7 @@ config SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
config SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY config SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
bool "Allow .noinit segment placed in external memory" bool "Allow .noinit segment placed in external memory"
default n default n
depends on SPIRAM && IDF_TARGET_ESP32 depends on SPIRAM
help help
If enabled, noinit variables can be placed in PSRAM using EXT_RAM_NOINIT_ATTR. If enabled, noinit variables can be placed in PSRAM using EXT_RAM_NOINIT_ATTR.

View File

@ -78,6 +78,9 @@ MEMORY
The aim of this is to keep data that will not be moved around and have a fixed address. The aim of this is to keep data that will not be moved around and have a fixed address.
*/ */
lp_reserved_seg(RW) : org = 0x50000000 + 0x4000 - RESERVE_RTC_MEM, len = RESERVE_RTC_MEM lp_reserved_seg(RW) : org = 0x50000000 + 0x4000 - RESERVE_RTC_MEM, len = RESERVE_RTC_MEM
/* PSRAM seg */
extern_ram_seg(RWX) : org = 0x42000020, len = IDRAM0_2_SEG_SIZE - 0x20
} }
/* Heap ends at top of sram_seg */ /* Heap ends at top of sram_seg */

View File

@ -382,6 +382,45 @@ SECTIONS
} > default_rodata_seg } > default_rodata_seg
ASSERT_SECTIONS_GAP(.flash.rodata, .eh_frame_hdr) ASSERT_SECTIONS_GAP(.flash.rodata, .eh_frame_hdr)
/* External RAM */
/**
* This section is required to skip flash sections, because `extern_ram_seg`
* and `drom_seg` / `irom_seg` are on the same bus when app build use flash sections
*/
.ext_ram.dummy (NOLOAD):
{
. = ORIGIN(extern_ram_seg);
. = . + (_rodata_reserved_end - _flash_rodata_dummy_start);
. = ALIGN (0x10000);
} > extern_ram_seg
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
/* This section holds .ext_ram.bss data, and will be put in PSRAM */
.ext_ram.bss (NOLOAD) :
{
_ext_ram_bss_start = ABSOLUTE(.);
mapping[extern_ram]
ALIGNED_SYMBOL(4, _ext_ram_bss_end)
} > extern_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
/**
* This section holds data that won't be initialised when startup.
* This section locates in External RAM region.
*/
.ext_ram_noinit (NOLOAD) :
{
_ext_ram_noinit_start = ABSOLUTE(.);
*(.ext_ram_noinit*)
ALIGNED_SYMBOL(4, _ext_ram_noinit_end)
} > extern_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
.eh_frame_hdr : .eh_frame_hdr :
{ {
#if CONFIG_COMPILER_CXX_EXCEPTIONS || CONFIG_ESP_SYSTEM_USE_EH_FRAME #if CONFIG_COMPILER_CXX_EXCEPTIONS || CONFIG_ESP_SYSTEM_USE_EH_FRAME

View File

@ -475,7 +475,6 @@ SECTIONS
mapping[rodata_noload] mapping[rodata_noload]
} > rodata_seg_low } > rodata_seg_low
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
#if CONFIG_SPIRAM_XIP_FROM_PSRAM #if CONFIG_SPIRAM_XIP_FROM_PSRAM
/** /**
* This section is required to skip flash sections, because `extern_ram_seg` * This section is required to skip flash sections, because `extern_ram_seg`
@ -488,6 +487,7 @@ SECTIONS
} > ext_ram_seg } > ext_ram_seg
#endif //CONFIG_SPIRAM_XIP_FROM_PSRAM #endif //CONFIG_SPIRAM_XIP_FROM_PSRAM
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
/* This section holds .ext_ram.bss data, and will be put in PSRAM */ /* This section holds .ext_ram.bss data, and will be put in PSRAM */
.ext_ram.bss (NOLOAD) : .ext_ram.bss (NOLOAD) :
{ {
@ -497,6 +497,21 @@ SECTIONS
} > ext_ram_seg } > ext_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY #endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
/**
* This section holds data that won't be initialised when startup.
* This section locates in External RAM region.
*/
.ext_ram_noinit (NOLOAD) :
{
_ext_ram_noinit_start = ABSOLUTE(.);
*(.ext_ram_noinit*)
ALIGNED_SYMBOL(4, _ext_ram_noinit_end)
} > ext_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
.dram0.bss (NOLOAD) : .dram0.bss (NOLOAD) :
{ {
ALIGNED_SYMBOL(4, _bss_start_low) ALIGNED_SYMBOL(4, _bss_start_low)

View File

@ -113,8 +113,7 @@ _data_seg_org = ORIGIN(rtc_data_seg);
/* The lines below define location alias for .rtc.data section based on Kconfig option. /* The lines below define location alias for .rtc.data section based on Kconfig option.
When the option is not defined then use slow memory segment When the option is not defined then use slow memory segment
else the data will be placed in fast memory segment else the data will be placed in fast memory segment */
TODO: check whether the rtc_data_location is correct for esp32s2 - IDF-761 */
#ifndef CONFIG_ESP32S2_RTCDATA_IN_FAST_MEM #ifndef CONFIG_ESP32S2_RTCDATA_IN_FAST_MEM
REGION_ALIAS("rtc_data_location", rtc_slow_seg ); REGION_ALIAS("rtc_data_location", rtc_slow_seg );
#else #else

View File

@ -265,6 +265,7 @@ SECTIONS
ALIGNED_SYMBOL(4, _noinit_end) ALIGNED_SYMBOL(4, _noinit_end)
} > dram0_0_seg } > dram0_0_seg
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
/* External Memory BSS. (Variables with EXT_RAM_BSS_ATTR attribute). */ /* External Memory BSS. (Variables with EXT_RAM_BSS_ATTR attribute). */
.ext_ram.bss (NOLOAD) : .ext_ram.bss (NOLOAD) :
{ {
@ -274,6 +275,22 @@ SECTIONS
ALIGNED_SYMBOL(4, _ext_ram_bss_end) ALIGNED_SYMBOL(4, _ext_ram_bss_end)
} > extern_ram_seg } > extern_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
/**
* This section holds data that won't be initialised when startup.
* This section locates in External RAM region.
*/
.ext_ram_noinit (NOLOAD) :
{
_ext_ram_noinit_start = ABSOLUTE(.);
*(.ext_ram_noinit*)
ALIGNED_SYMBOL(4, _ext_ram_noinit_end)
} > extern_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
/* Shared RAM */ /* Shared RAM */
.dram0.bss (NOLOAD) : .dram0.bss (NOLOAD) :

View File

@ -439,6 +439,7 @@ SECTIONS
. = ALIGN (0x10000); . = ALIGN (0x10000);
} > extern_ram_seg } > extern_ram_seg
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
/* This section holds .ext_ram.bss data, and will be put in PSRAM */ /* This section holds .ext_ram.bss data, and will be put in PSRAM */
.ext_ram.bss (NOLOAD) : .ext_ram.bss (NOLOAD) :
{ {
@ -448,6 +449,22 @@ SECTIONS
ALIGNED_SYMBOL(4, _ext_ram_bss_end) ALIGNED_SYMBOL(4, _ext_ram_bss_end)
} > extern_ram_seg } > extern_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
/**
* This section holds data that won't be initialised when startup.
* This section locates in External RAM region.
*/
.ext_ram_noinit (NOLOAD) :
{
_ext_ram_noinit_start = ABSOLUTE(.);
*(.ext_ram_noinit*)
ALIGNED_SYMBOL(4, _ext_ram_noinit_end)
} > extern_ram_seg
#endif //CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
/* Marks the end of IRAM code segment */ /* Marks the end of IRAM code segment */
.iram0.text_end (NOLOAD) : .iram0.text_end (NOLOAD) :

View File

@ -123,16 +123,14 @@ This option reduces the internal static memory used by the BSS segment.
Remaining external RAM can also be added to the capability heap allocator using the method shown above. Remaining external RAM can also be added to the capability heap allocator using the method shown above.
.. only:: esp32 .. _external_ram_config_noinit:
.. _external_ram_config_noinit: Allow .noinit Segment to Be Placed in External Memory
--------------------------------------------------------------
Allow .noinit Segment to Be Placed in External Memory Enable this option by checking :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY`. If enabled, the region of the data virtual address space where the PSRAM is mapped to will be used to store non-initialized data. The values placed in this segment will not be initialized or modified even during startup or restart.
--------------------------------------------------------------
Enable this option by checking :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY`. If enabled, the region of the data virtual address space where the PSRAM is mapped to will be used to store non-initialized data. The values placed in this segment will not be initialized or modified even during startup or restart. By applying the macro ``EXT_RAM_NOINIT_ATTR``, data could be moved from the internal NOINIT segment to external RAM. Remaining external RAM can still be added to the capability heap allocator using the method shown above, :ref:`external_ram_config_capability_allocator`.
By applying the macro ``EXT_RAM_NOINIT_ATTR``, data could be moved from the internal NOINIT segment to external RAM. Remaining external RAM can still be added to the capability heap allocator using the method shown above, :ref:`external_ram_config_capability_allocator`.
.. only:: SOC_SPIRAM_XIP_SUPPORTED .. only:: SOC_SPIRAM_XIP_SUPPORTED

View File

@ -123,16 +123,14 @@ ESP-IDF 启动过程中,片外 RAM 被映射到数据虚拟地址空间,该
剩余的片外 RAM 也可以通过上述方法添加到堆分配器中。 剩余的片外 RAM 也可以通过上述方法添加到堆分配器中。
.. only:: esp32 .. _external_ram_config_noinit:
.. _external_ram_config_noinit: 允许 .noinit 段放入片外存储器
-------------------------------------
允许 .noinit 段放入片外存储器 通过勾选 :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` 启用该选项。启用该选项后PSRAM 被映射到的数据虚拟地址空间将用于存储未初始化的数据。即使在启动或重新启动期间,放置在该段中的值也不会被初始化或修改。
-------------------------------------
通过勾选 :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` 启用该选项。启用该选项后PSRAM 被映射到的数据虚拟地址空间将用于存储未初始化的数据。即使在启动或重新启动期间,放置在该段中的值也不会被初始化或修改。 通过应用 ``EXT_RAM_NOINIT_ATTR`` 宏,可以将数据从内部 NOINIT 段移到片外 RAM。剩余的片外 RAM 也可以通过上述方法添加到堆分配器中,具体请参考 :ref:`external_ram_config_capability_allocator`
通过应用 ``EXT_RAM_NOINIT_ATTR`` 宏,可以将数据从内部 NOINIT 段移到片外 RAM。剩余的片外 RAM 也可以通过上述方法添加到堆分配器中,具体请参考 :ref:`external_ram_config_capability_allocator`
.. only:: SOC_SPIRAM_XIP_SUPPORTED .. only:: SOC_SPIRAM_XIP_SUPPORTED