diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index af3904fbb..597cadcce 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -245,7 +245,12 @@ bool AppsContainer::switchTo(App::Snapshot * snapshot) { } void AppsContainer::run() { - window()->setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height)); + KDRect screenRect = KDRect(0, 0, Ion::Display::Width, Ion::Display::Height); + window()->setFrame(screenRect); + /* We push a white screen here, because fetching the exam mode takes some time + * and it is visible when reflashing a N0100 (there is some noise on the + * screen before the logo appears). */ + Ion::Display::pushRectUniform(screenRect, KDColorWhite); if (GlobalPreferences::sharedGlobalPreferences()->examMode()) { activateExamMode(); } diff --git a/ion/src/device/n0100/drivers/config/exam_mode.h b/ion/src/device/n0100/drivers/config/exam_mode.h new file mode 100644 index 000000000..7dfcd5517 --- /dev/null +++ b/ion/src/device/n0100/drivers/config/exam_mode.h @@ -0,0 +1,32 @@ +#ifndef ION_DEVICE_N0100_CONFIG_EXAM_MODE_H +#define ION_DEVICE_N0100_CONFIG_EXAM_MODE_H + +namespace Ion { +namespace ExamMode { +namespace Config { + +// TODO: factorize the macro with equivalent macro on N110 + +#define byte4 0xFF, 0xFF, 0xFF, 0xFF +#define byte8 byte4, byte4 +#define byte16 byte8, byte8 +#define byte32 byte16, byte16 +#define byte64 byte32, byte32 +#define byte128 byte64, byte64 +#define byte256 byte128, byte128 +#define byte512 byte256, byte256 +#define byte1K byte512, byte512 +#define byte2K byte1K, byte1K +#define byte4K byte2K, byte2K +#define byte8K byte4K, byte4K +#define byte16K byte8K, byte8K + +#define EXAM_BUFFER_CONTENT byte16K + +constexpr static int ExamModeBufferSize = 16*1024; + +} +} +} + +#endif diff --git a/ion/src/device/n0100/flash.ld b/ion/src/device/n0100/flash.ld index 6c449809a..e9b073bb2 100644 --- a/ion/src/device/n0100/flash.ld +++ b/ion/src/device/n0100/flash.ld @@ -9,16 +9,16 @@ * This will let us use shortcuts such as ">FLASH" to ask for a given section to * be stored in Flash. */ MEMORY { - FLASH_FIRST_SECTOR (rx) : ORIGIN = 0x08000000, LENGTH = 16K - FLASH_SECOND_SECTOR (rx) : ORIGIN = (0x08000000 + 16K), LENGTH = 16K - FLASH_LAST_SECTORS (rx) : ORIGIN = (0x08000000 + 32K), LENGTH = (1024K - 32K) + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K } STACK_SIZE = 32K; +FLASH_SECOND_SECTOR_OFFSET = 16K; +FLASH_SECOND_SECTOR_SIZE = 16K; SECTIONS { - .isr_vector_table ORIGIN(FLASH_FIRST_SECTOR) : { + .isr_vector_table ORIGIN(FLASH) : { /* When booting, the STM32F412 fetches the content of address 0x0, and * extracts from it various key infos: the initial value of the PC register * (program counter), the initial value of the stack pointer, and various @@ -33,37 +33,39 @@ SECTIONS { * convenient: using function pointers, we can easily point to the service * routine for each interrupt. */ KEEP(*(.isr_vector_table)) - } >FLASH_FIRST_SECTOR + } >FLASH .header : { KEEP(*(.header)) - } >FLASH_FIRST_SECTOR + ASSERT ((. < ORIGIN(FLASH) + FLASH_SECOND_SECTOR_OFFSET), "Error: ISR table or HEADER overflows in the flash sector reserved for the exam mode"); + } >FLASH - .exam_mode_persistence ORIGIN(FLASH_SECOND_SECTOR): { - _exam_mode_persistence_start = .; + .exam_mode_buffer ORIGIN(FLASH) + FLASH_SECOND_SECTOR_OFFSET : { + _exam_mode_buffer_start = .; + KEEP(*(.exam_mode_buffer)) /* Note: We don't increment "." here, we set it. */ - . = (ORIGIN(FLASH_SECOND_SECTOR) + LENGTH(FLASH_SECOND_SECTOR)); - _exam_mode_persistence_end = .; - } >FLASH_SECOND_SECTOR + . = ORIGIN(FLASH) + FLASH_SECOND_SECTOR_OFFSET + FLASH_SECOND_SECTOR_SIZE; + _exam_mode_buffer_end = .; + } >FLASH .text : { . = ALIGN(4); *(.text) *(.text.*) - } >FLASH_LAST_SECTORS + } >FLASH .init_array : { . = ALIGN(4); _init_array_start = .; KEEP (*(.init_array*)) _init_array_end = .; - } >FLASH_LAST_SECTORS + } >FLASH .rodata : { . = ALIGN(4); *(.rodata) *(.rodata.*) - } >FLASH_LAST_SECTORS + } >FLASH .data : { /* The data section is written to Flash but linked as if it were in RAM. @@ -84,7 +86,7 @@ SECTIONS { *(.data) *(.data.*) _data_section_end_ram = .; - } >SRAM AT> FLASH_LAST_SECTORS + } >SRAM AT> FLASH .bss : { /* The bss section contains data for all uninitialized variables diff --git a/ion/src/device/n0110/drivers/cache.cpp b/ion/src/device/n0110/drivers/cache.cpp index 1614d528b..af907386f 100644 --- a/ion/src/device/n0110/drivers/cache.cpp +++ b/ion/src/device/n0110/drivers/cache.cpp @@ -41,6 +41,7 @@ void privateCleanInvalidateDisableDCache(bool clean, bool invalidate, bool disab dcisw.setWAY(w); CORTEX.DCISW()->set(dcisw); } + __asm volatile("nop"); } while (w-- != 0); } while (sets-- != 0); diff --git a/ion/src/device/n0110/drivers/config/exam_mode.h b/ion/src/device/n0110/drivers/config/exam_mode.h new file mode 100644 index 000000000..e2a2e2aba --- /dev/null +++ b/ion/src/device/n0110/drivers/config/exam_mode.h @@ -0,0 +1,30 @@ +#ifndef ION_DEVICE_N0110_CONFIG_EXAM_MODE_H +#define ION_DEVICE_N0110_CONFIG_EXAM_MODE_H + +namespace Ion { +namespace ExamMode { +namespace Config { + +// TODO: factorize the macro with equivalent macro on N100 + +#define byte4 0xFF, 0xFF, 0xFF, 0xFF +#define byte8 byte4, byte4 +#define byte16 byte8, byte8 +#define byte32 byte16, byte16 +#define byte64 byte32, byte32 +#define byte128 byte64, byte64 +#define byte256 byte128, byte128 +#define byte512 byte256, byte256 +#define byte1K byte512, byte512 +#define byte2K byte1K, byte1K +#define byte4K byte2K, byte2K + +#define EXAM_BUFFER_CONTENT byte4K + +constexpr static int ExamModeBufferSize = 4*1024; + +} +} +} + +#endif diff --git a/ion/src/device/n0110/flash.ld b/ion/src/device/n0110/flash.ld index 42ac5350a..7994dcd70 100644 --- a/ion/src/device/n0110/flash.ld +++ b/ion/src/device/n0110/flash.ld @@ -12,8 +12,7 @@ MEMORY { INTERNAL_FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 64K SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K - EXTERNAL_FLASH_FIRST_SECTOR (rx) : ORIGIN = 0x90000000, LENGTH = 4K - EXTERNAL_FLASH_NEXT_SECTORS (rx) : ORIGIN = (0x90000000 + 4K), LENGTH = (8M - 4K) + EXTERNAL_FLASH (rx) : ORIGIN = 0x90000000, LENGTH = 8M /* ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 16K DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K @@ -23,6 +22,7 @@ MEMORY { } STACK_SIZE = 32K; +FIRST_EXTERNAL_FLASH_SECTOR_SIZE = 4K; SECTIONS { .isr_vector_table ORIGIN(INTERNAL_FLASH) : { @@ -56,13 +56,6 @@ SECTIONS { *(.text._ZL22jump_to_external_flashv) } >INTERNAL_FLASH - .exam_mode_persistence ORIGIN(EXTERNAL_FLASH_FIRST_SECTOR): { - _exam_mode_persistence_start = .; - /* Note: We don't increment "." here, we set it. */ - . = (ORIGIN(EXTERNAL_FLASH_FIRST_SECTOR) + LENGTH(EXTERNAL_FLASH_FIRST_SECTOR)); - _exam_mode_persistence_end = .; - } >EXTERNAL_FLASH_FIRST_SECTOR - /* Use boot routine and required dependencies */ /* We're relying on symbols being in their own sub-section. On GCC, this is * done with -fdata-sections -ffunction-sections */ @@ -123,17 +116,25 @@ SECTIONS { *(.rodata._ZN3Ion6Device5Board4initEv.str1.4) } >INTERNAL_FLASH + .exam_mode_buffer ORIGIN(EXTERNAL_FLASH) : { + _exam_mode_buffer_start = .; + KEEP(*(.exam_mode_buffer)) + /* Note: We don't increment "." here, we set it. */ + . = ORIGIN(EXTERNAL_FLASH) + FIRST_EXTERNAL_FLASH_SECTOR_SIZE; + _exam_mode_buffer_end = .; + } >EXTERNAL_FLASH + /* External flash memory */ .text.external : { . = ALIGN(4); *(.text) *(.text.*) - } >EXTERNAL_FLASH_NEXT_SECTORS + } >EXTERNAL_FLASH .rodata.external : { *(.rodata) *(.rodata.*) - } >EXTERNAL_FLASH_NEXT_SECTORS + } >EXTERNAL_FLASH .init_array : { . = ALIGN(4); @@ -211,7 +212,7 @@ NOCROSSREFS_TO(.rodata.external .isr_vector_table); NOCROSSREFS_TO(.text.external .header); NOCROSSREFS_TO(.rodata.external .header); -NOCROSSREFS_TO(.exam_mode_persistence .text.internal); -NOCROSSREFS_TO(.exam_mode_persistence .rodata.internal); -NOCROSSREFS_TO(.exam_mode_persistence .isr_vector_table); -NOCROSSREFS_TO(.exam_mode_persistence .header); +NOCROSSREFS_TO(.exam_mode_buffer .text.internal); +NOCROSSREFS_TO(.exam_mode_buffer .rodata.internal); +NOCROSSREFS_TO(.exam_mode_buffer .isr_vector_table); +NOCROSSREFS_TO(.exam_mode_buffer .header); diff --git a/ion/src/device/shared/drivers/exam_mode.cpp b/ion/src/device/shared/drivers/exam_mode.cpp index 3096ad526..19b62b4df 100644 --- a/ion/src/device/shared/drivers/exam_mode.cpp +++ b/ion/src/device/shared/drivers/exam_mode.cpp @@ -1,4 +1,5 @@ #include +#include #include "flash.h" #include @@ -6,10 +7,15 @@ namespace Ion { namespace ExamMode { extern "C" { - extern char _exam_mode_persistence_start; - extern char _exam_mode_persistence_end; + extern char _exam_mode_buffer_start; + extern char _exam_mode_buffer_end; } +char ones[Config::ExamModeBufferSize] + __attribute__((section(".exam_mode_buffer"))) + __attribute__((used)) += {EXAM_BUFFER_CONTENT}; + /* The exam mode is written in flash so that it is resilient to resets. * We erase the dedicated flash sector (all bits written to 1) and, upon * activating or deactivating the exam mode we write one bit to 0. To determine @@ -22,16 +28,16 @@ extern "C" { * sector. */ uint32_t * SignificantExamModeAddress() { - uint32_t * persitence_start = (uint32_t *)&_exam_mode_persistence_start; - uint32_t * persitence_end = (uint32_t *)&_exam_mode_persistence_end; + uint32_t * persitence_start = (uint32_t *)&_exam_mode_buffer_start; + uint32_t * persitence_end = (uint32_t *)&_exam_mode_buffer_end; while (persitence_start < persitence_end && *persitence_start == 0x0) { // Skip even number of zero bits persitence_start++; } if (persitence_start == persitence_end) { - assert(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_persistence_start) >= 0); - Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_persistence_start)); - return (uint32_t *)&_exam_mode_persistence_start; + assert(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start) >= 0); + Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start)); + return (uint32_t *)&_exam_mode_buffer_start; } return persitence_start; } diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index 6b8a3db5c..7c8c3b674 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -74,9 +74,15 @@ mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t * args) { KDColor backgroundColor = (n_args >= 5) ? ColorForTuple(args[4]) : KDColorWhite; MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->drawString(text, point, KDFont::LargeFont, textColor, backgroundColor); - /* drawString function might take some time to execute so we add an extra check - * for user interruption (to avoid freezing in an infinite loop calling - * 'drawString' for instance). */ + /* Before and after execution of "modkandinsky_draw_string", + * "micropython_port_vm_hook_loop" is called by "mp_execute_bytecode" and will + * call "micropython_port_interrupt_if_needed" every 20000 calls. + * However, "drawString" function might take some time to execute leading to + * drastically decrease the frequency of calls to + * "micropython_port_vm_hook_loop" and thus to + * "micropython_port_interrupt_if_needed". So we add an extra + * check for user interruption here. This way the user can still interrupt an + * infinite loop calling 'drawString' for instance. */ micropython_port_interrupt_if_needed(); return mp_const_none; } @@ -91,9 +97,7 @@ mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) { KDColor color = ColorForTuple(args[4]); MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); KDIonContext::sharedContext()->fillRect(rect, color); - /* fillRect function might take some time to execute so we add an extra check - * for user interruption (to avoid freezing in an infinite loop calling - * 'fillRect' for instance). */ + // Cf comment on modkandinsky_draw_string micropython_port_interrupt_if_needed(); return mp_const_none; }