diff --git a/esp8266/main.c b/esp8266/main.c index 5087c7d6a3..1e988ef33a 100644 --- a/esp8266/main.c +++ b/esp8266/main.c @@ -58,6 +58,10 @@ STATIC void mp_reset(void) { MP_STATE_PORT(mp_kbd_exception) = mp_obj_new_exception(&mp_type_KeyboardInterrupt); MP_STATE_PORT(term_obj) = MP_OBJ_NULL; MP_STATE_PORT(dupterm_arr_obj) = MP_OBJ_NULL; + #if MICROPY_EMIT_XTENSA + extern void esp_native_code_init(void); + esp_native_code_init(); + #endif pin_init0(); readline_init0(); dupterm_task_init(); diff --git a/esp8266/modesp.c b/esp8266/modesp.c index 207422b67d..302bc01edc 100644 --- a/esp8266/modesp.c +++ b/esp8266/modesp.c @@ -699,6 +699,99 @@ STATIC mp_obj_t esp_esf_free_bufs(mp_obj_t idx_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_esf_free_bufs_obj, esp_esf_free_bufs); +#if MICROPY_EMIT_XTENSA + +// We provide here a way of committing executable data to a region from +// which it can be executed by the CPU. There are 2 such writable regions: +// - iram1, which may have some space left at the end of it +// - memory-mapped flash rom +// +// By default the iram1 region (the space at the end of it) is used. The +// user can select iram1 or a section of flash by calling the +// esp.set_native_code_location() function; see below. If flash is selected +// then it is erased as needed. + +#define IRAM1_END (0x40108000) +#define FLASH_START (0x40200000) +#define FLASH_END (0x40300000) +#define FLASH_SEC_SIZE (4096) + +#define ESP_NATIVE_CODE_IRAM1 (0) +#define ESP_NATIVE_CODE_FLASH (1) + +extern uint32_t _lit4_end; +STATIC uint32_t esp_native_code_location; +STATIC uint32_t esp_native_code_start; +STATIC uint32_t esp_native_code_end; +STATIC uint32_t esp_native_code_cur; +STATIC uint32_t esp_native_code_erased; + +void esp_native_code_init(void) { + esp_native_code_location = ESP_NATIVE_CODE_IRAM1; + esp_native_code_start = (uint32_t)&_lit4_end; + esp_native_code_end = IRAM1_END; + esp_native_code_cur = esp_native_code_start; + esp_native_code_erased = 0; +} + +void *esp_native_code_commit(void *buf, size_t len) { + //printf("COMMIT(buf=%p, len=%u, start=%08x, cur=%08x, end=%08x, erased=%08x)\n", buf, len, esp_native_code_start, esp_native_code_cur, esp_native_code_end, esp_native_code_erased); + + len = (len + 3) & ~3; + if (esp_native_code_cur + len > esp_native_code_end) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_MemoryError, + "memory allocation failed, allocating %u bytes for native code", (uint)len)); + } + + void *dest; + if (esp_native_code_location == ESP_NATIVE_CODE_IRAM1) { + dest = (void*)esp_native_code_cur; + memcpy(dest, buf, len); + } else { + SpiFlashOpResult res; + while (esp_native_code_erased < esp_native_code_cur + len) { + res = spi_flash_erase_sector(esp_native_code_erased / FLASH_SEC_SIZE); + if (res != SPI_FLASH_RESULT_OK) { + break; + } + esp_native_code_erased += FLASH_SEC_SIZE; + } + if (res == SPI_FLASH_RESULT_OK) { + res = spi_flash_write(esp_native_code_cur, buf, len); + } + if (res != SPI_FLASH_RESULT_OK) { + mp_raise_OSError(res == SPI_FLASH_RESULT_TIMEOUT ? MP_ETIMEDOUT : MP_EIO); + } + dest = (void*)(FLASH_START + esp_native_code_cur); + } + + esp_native_code_cur += len; + + return dest; +} + +STATIC mp_obj_t esp_set_native_code_location(mp_obj_t start_in, mp_obj_t len_in) { + if (start_in == mp_const_none && len_in == mp_const_none) { + // use end of iram1 region + esp_native_code_init(); + } else { + // use flash; input params are byte offsets from start of flash + esp_native_code_location = ESP_NATIVE_CODE_FLASH; + esp_native_code_start = mp_obj_get_int(start_in); + esp_native_code_end = esp_native_code_start + mp_obj_get_int(len_in); + esp_native_code_cur = esp_native_code_start; + esp_native_code_erased = esp_native_code_start; + // memory-mapped flash is limited in extents to 1MByte + if (esp_native_code_end > FLASH_END - FLASH_START) { + mp_raise_ValueError("flash location must be below 1MByte"); + } + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_set_native_code_location_obj, esp_set_native_code_location); + +#endif + STATIC const mp_map_elem_t esp_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_esp) }, @@ -729,6 +822,9 @@ STATIC const mp_map_elem_t esp_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_malloc), (mp_obj_t)&esp_malloc_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_free), (mp_obj_t)&esp_free_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_esf_free_bufs), (mp_obj_t)&esp_esf_free_bufs_obj }, + #if MICROPY_EMIT_XTENSA + { MP_OBJ_NEW_QSTR(MP_QSTR_set_native_code_location), (mp_obj_t)&esp_set_native_code_location_obj }, + #endif #if MODESP_INCLUDE_CONSTANTS { MP_OBJ_NEW_QSTR(MP_QSTR_SLEEP_NONE), diff --git a/esp8266/mpconfigport.h b/esp8266/mpconfigport.h index 03d664e570..40550024b6 100644 --- a/esp8266/mpconfigport.h +++ b/esp8266/mpconfigport.h @@ -10,9 +10,7 @@ #define MICROPY_ALLOC_PARSE_RESULT_INC (8) #define MICROPY_ALLOC_PARSE_CHUNK_INIT (64) #define MICROPY_PERSISTENT_CODE_LOAD (1) -#define MICROPY_EMIT_X64 (0) -#define MICROPY_EMIT_THUMB (0) -#define MICROPY_EMIT_INLINE_THUMB (0) +#define MICROPY_EMIT_XTENSA (1) #define MICROPY_MEM_STATS (0) #define MICROPY_DEBUG_PRINTERS (1) #define MICROPY_DEBUG_PRINTER_DEST mp_debug_print @@ -131,6 +129,8 @@ typedef uint32_t sys_prot_t; // for modlwip #include #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) +void *esp_native_code_commit(void*, size_t); +#define MP_PLAT_COMMIT_EXEC(buf, len) esp_native_code_commit(buf, len) #define mp_type_fileio fatfs_type_fileio #define mp_type_textio fatfs_type_textio diff --git a/esp8266/mpconfigport_512k.h b/esp8266/mpconfigport_512k.h index ffdc95dbd4..1bb4524054 100644 --- a/esp8266/mpconfigport_512k.h +++ b/esp8266/mpconfigport_512k.h @@ -1,5 +1,8 @@ #include +#undef MICROPY_EMIT_XTENSA +#define MICROPY_EMIT_XTENSA (0) + #undef MICROPY_FSUSERMOUNT #define MICROPY_FSUSERMOUNT (0) #undef MICROPY_VFS_FAT