From cf33f84106110f0098637be53dc4e98cb7bd9c5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Thu, 11 Nov 2021 22:13:07 +0100 Subject: [PATCH 01/37] [config.mak] Omega 1.23.0 --- build/config.mak | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/config.mak b/build/config.mak index 054484cbe..ee15ea61c 100644 --- a/build/config.mak +++ b/build/config.mak @@ -5,9 +5,9 @@ DEBUG ?= 0 HOME_DISPLAY_EXTERNALS ?= 1 EPSILON_VERSION ?= 15.5.0 -OMEGA_VERSION ?= 1.22.0 +OMEGA_VERSION ?= 1.23.0 # OMEGA_USERNAME ?= N/A -OMEGA_STATE ?= dev +OMEGA_STATE ?= public EPSILON_APPS ?= calculation rpn graph code statistics probability solver atomic sequence regression settings external SUBMODULES_APPS = atomic rpn EPSILON_I18N ?= en fr nl pt it de es hu From 61673cd1b5f2a1726ad0dac6d7a9d885c68b20a4 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 20 Feb 2022 23:12:15 +0300 Subject: [PATCH 02/37] [apps] Reduced heap to make room for SVC table --- apps/code/app.h | 2 +- apps/external/app.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/code/app.h b/apps/code/app.h index 494f5c0f8..41f542eb4 100644 --- a/apps/code/app.h +++ b/apps/code/app.h @@ -75,7 +75,7 @@ public: VariableBoxController * variableBoxController() { return &m_variableBoxController; } - static constexpr int k_pythonHeapSize = 100000; + static constexpr int k_pythonHeapSize = 99000; private: /* Python delegate: diff --git a/apps/external/app.h b/apps/external/app.h index 8077289f5..d1d38487b 100644 --- a/apps/external/app.h +++ b/apps/external/app.h @@ -29,7 +29,7 @@ private: MainController m_mainController; StackViewController m_stackViewController; Window * m_window; - static constexpr int k_externalHeapSize = 100000; + static constexpr int k_externalHeapSize = 99000; char m_externalHeap[k_externalHeapSize]; }; From ffb4c39e042a5666a6c3ee90166157a8015bd092 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Tue, 22 Feb 2022 22:05:06 +0300 Subject: [PATCH 03/37] [ion] Added bootloader device --- build/platform.device.bootloader.mak | 3 + build/targets.device.bootloader.mak | 30 + build/targets.device.mak | 18 - build/targets.device.n0100.mak | 15 + build/targets.device.n0110.mak | 26 +- ion/Makefile | 3 +- ion/src/device/Makefile | 2 +- ion/src/device/bootloader/Makefile | 31 ++ ion/src/device/bootloader/boot/rt0.cpp | 136 +++++ ion/src/device/bootloader/bootloader.A.ld | 144 +++++ ion/src/device/bootloader/bootloader.B.ld | 144 +++++ ion/src/device/bootloader/drivers/board.cpp | 402 ++++++++++++++ ion/src/device/bootloader/drivers/cache.cpp | 104 ++++ ion/src/device/bootloader/drivers/cache.h | 46 ++ .../bootloader/drivers/config/backlight.h | 25 + .../bootloader/drivers/config/battery.h | 30 + .../device/bootloader/drivers/config/clocks.h | 68 +++ .../bootloader/drivers/config/console.h | 32 ++ .../bootloader/drivers/config/display.h | 38 ++ .../bootloader/drivers/config/exam_mode.h | 30 + .../drivers/config/external_flash.h | 45 ++ .../drivers/config/internal_flash.h | 31 ++ .../bootloader/drivers/config/keyboard.h | 75 +++ .../device/bootloader/drivers/config/led.h | 28 + .../bootloader/drivers/config/serial_number.h | 18 + .../device/bootloader/drivers/config/swd.h | 24 + .../device/bootloader/drivers/config/timing.h | 19 + .../device/bootloader/drivers/config/usb.h | 31 ++ .../bootloader/drivers/external_flash.cpp | 520 ++++++++++++++++++ ion/src/device/bootloader/drivers/led.cpp | 23 + ion/src/device/bootloader/drivers/power.cpp | 61 ++ ion/src/device/bootloader/drivers/power.h | 16 + ion/src/device/bootloader/drivers/reset.cpp | 13 + ion/src/device/bootloader/drivers/usb.cpp | 27 + ion/src/device/bootloader/platform_info.cpp | 74 +++ .../device/bootloader/regs/config/cortex.h | 6 + ion/src/device/bootloader/regs/config/crc.h | 6 + ion/src/device/bootloader/regs/config/flash.h | 6 + ion/src/device/bootloader/regs/config/pwr.h | 6 + ion/src/device/bootloader/regs/config/rcc.h | 7 + .../device/bootloader/regs/config/syscfg.h | 6 + ion/src/device/bootloader/regs/config/usart.h | 12 + ion/src/device/n0100/Makefile | 8 + ion/src/device/{shared => n0100}/boot/rt0.cpp | 10 +- .../n0100}/platform_info.cpp | 0 ion/src/device/n0110/Makefile | 8 + ion/src/device/n0110/boot/rt0.cpp | 126 +++++ ion/src/device/n0110/platform_info.cpp | 109 ++++ ion/src/device/shared/boot/Makefile | 1 - ion/src/simulator/Makefile | 1 + ion/src/simulator/shared/platform_info.cpp | 109 ++++ 51 files changed, 2719 insertions(+), 34 deletions(-) create mode 100644 build/platform.device.bootloader.mak create mode 100644 build/targets.device.bootloader.mak create mode 100644 ion/src/device/bootloader/Makefile create mode 100644 ion/src/device/bootloader/boot/rt0.cpp create mode 100644 ion/src/device/bootloader/bootloader.A.ld create mode 100644 ion/src/device/bootloader/bootloader.B.ld create mode 100644 ion/src/device/bootloader/drivers/board.cpp create mode 100644 ion/src/device/bootloader/drivers/cache.cpp create mode 100644 ion/src/device/bootloader/drivers/cache.h create mode 100644 ion/src/device/bootloader/drivers/config/backlight.h create mode 100644 ion/src/device/bootloader/drivers/config/battery.h create mode 100644 ion/src/device/bootloader/drivers/config/clocks.h create mode 100644 ion/src/device/bootloader/drivers/config/console.h create mode 100644 ion/src/device/bootloader/drivers/config/display.h create mode 100644 ion/src/device/bootloader/drivers/config/exam_mode.h create mode 100644 ion/src/device/bootloader/drivers/config/external_flash.h create mode 100644 ion/src/device/bootloader/drivers/config/internal_flash.h create mode 100644 ion/src/device/bootloader/drivers/config/keyboard.h create mode 100644 ion/src/device/bootloader/drivers/config/led.h create mode 100644 ion/src/device/bootloader/drivers/config/serial_number.h create mode 100644 ion/src/device/bootloader/drivers/config/swd.h create mode 100644 ion/src/device/bootloader/drivers/config/timing.h create mode 100644 ion/src/device/bootloader/drivers/config/usb.h create mode 100644 ion/src/device/bootloader/drivers/external_flash.cpp create mode 100644 ion/src/device/bootloader/drivers/led.cpp create mode 100644 ion/src/device/bootloader/drivers/power.cpp create mode 100644 ion/src/device/bootloader/drivers/power.h create mode 100644 ion/src/device/bootloader/drivers/reset.cpp create mode 100644 ion/src/device/bootloader/drivers/usb.cpp create mode 100644 ion/src/device/bootloader/platform_info.cpp create mode 100644 ion/src/device/bootloader/regs/config/cortex.h create mode 100644 ion/src/device/bootloader/regs/config/crc.h create mode 100644 ion/src/device/bootloader/regs/config/flash.h create mode 100644 ion/src/device/bootloader/regs/config/pwr.h create mode 100644 ion/src/device/bootloader/regs/config/rcc.h create mode 100644 ion/src/device/bootloader/regs/config/syscfg.h create mode 100644 ion/src/device/bootloader/regs/config/usart.h rename ion/src/device/{shared => n0100}/boot/rt0.cpp (97%) rename ion/src/{shared => device/n0100}/platform_info.cpp (100%) create mode 100644 ion/src/device/n0110/boot/rt0.cpp create mode 100644 ion/src/device/n0110/platform_info.cpp create mode 100644 ion/src/simulator/shared/platform_info.cpp diff --git a/build/platform.device.bootloader.mak b/build/platform.device.bootloader.mak new file mode 100644 index 000000000..256dd180d --- /dev/null +++ b/build/platform.device.bootloader.mak @@ -0,0 +1,3 @@ +TOOLCHAIN ?= arm-gcc-m7f +ION_KEYBOARD_LAYOUT = layout_B3 +PCB_LATEST = 343 # PCB version 3.43 diff --git a/build/targets.device.bootloader.mak b/build/targets.device.bootloader.mak new file mode 100644 index 000000000..102885e58 --- /dev/null +++ b/build/targets.device.bootloader.mak @@ -0,0 +1,30 @@ +HANDY_TARGETS += test.external_flash.write test.external_flash.read + +$(BUILD_DIR)/test.external_flash.%.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld +test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src) +$(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_read_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_read_src)) +$(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src)) + +.PHONY: %_flash +%_flash: $(BUILD_DIR)/%.dfu + @echo "DFU $@" + @echo "INFO About to flash your device. Please plug your device to your computer" + @echo " using an USB cable and press at the same time the 6 key and the RESET" + @echo " button on the back of your device." + $(Q) until $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11" > /dev/null 2>&1; do sleep 2;done + $(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^) + +.PHONY: %.two_binaries +%.two_binaries: %.elf + @echo "Building an external binary for $<" + $(Q) $(OBJCOPY) -O binary $< $(basename $<).external.bin + @echo "Padding $(basename $<).external.bin" + $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin + +.PHONY: binpack +binpack: $(BUILD_DIR)/epsilon.onboarding.two_binaries + rm -rf $(BUILD_DIR)/binpack + mkdir -p $(BUILD_DIR)/binpack + cp $(BUILD_DIR)/epsilon.onboarding.external.bin $(BUILD_DIR)/binpack + cd $(BUILD_DIR) && for binary in epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done + cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/* diff --git a/build/targets.device.mak b/build/targets.device.mak index 9498371c4..891b1d097 100644 --- a/build/targets.device.mak +++ b/build/targets.device.mak @@ -54,21 +54,3 @@ $(BUILD_DIR)/bench.ram.$(EXE): LDFLAGS += -Lion/src/$(PLATFORM)/bench $(BUILD_DIR)/bench.ram.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld $(BUILD_DIR)/bench.flash.$(EXE): $(call flavored_object_for,$(bench_src),consoleuart usbxip) $(BUILD_DIR)/bench.flash.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/$(MODEL)/internal_flash.ld - -.PHONY: %.two_binaries -%.two_binaries: %.elf - @echo "Building an internal and an external binary for $<" - $(Q) $(OBJCOPY) -O binary -j .text.external -j .rodata.external -j .exam_mode_buffer $< $(basename $<).external.bin - $(Q) $(OBJCOPY) -O binary -R .text.external -R .rodata.external -R .exam_mode_buffer $< $(basename $<).internal.bin - @echo "Padding $(basename $<).external.bin and $(basename $<).internal.bin" - $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin - $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).internal.bin - -.PHONY: binpack -binpack: $(BUILD_DIR)/flasher.light.bin $(BUILD_DIR)/epsilon.onboarding.two_binaries - rm -rf $(BUILD_DIR)/binpack - mkdir -p $(BUILD_DIR)/binpack - cp $(BUILD_DIR)/flasher.light.bin $(BUILD_DIR)/binpack - cp $(BUILD_DIR)/epsilon.onboarding.internal.bin $(BUILD_DIR)/epsilon.onboarding.external.bin $(BUILD_DIR)/binpack - cd $(BUILD_DIR) && for binary in flasher.light.bin epsilon.onboarding.internal.bin epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done - cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/* diff --git a/build/targets.device.n0100.mak b/build/targets.device.n0100.mak index aff59a280..ae70afbdb 100644 --- a/build/targets.device.n0100.mak +++ b/build/targets.device.n0100.mak @@ -5,3 +5,18 @@ @echo " using an USB cable and press the RESET button the back of your device." $(Q) until $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11" > /dev/null 2>&1; do sleep 2;done $(Q) $(PYTHON) build/device/dfu.py -m -u $< + +.PHONY: %.two_binaries +%.two_binaries: %.elf + @echo "Building an internal binary for $<" + $(Q) $(OBJCOPY) -O binary -R .text.external -R .rodata.external -R .exam_mode_buffer $< $(basename $<).internal.bin + @echo "Padding $(basename $<).internal.bin" + $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).internal.bin + +.PHONY: binpack +binpack: $(BUILD_DIR)/epsilon.onboarding.two_binaries + rm -rf $(BUILD_DIR)/binpack + mkdir -p $(BUILD_DIR)/binpack + cp $(BUILD_DIR)/epsilon.onboarding.internal.bin $(BUILD_DIR)/binpack + cd $(BUILD_DIR) && for binary in epsilon.onboarding.internal.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done + cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/* diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index 77bc1bf8c..c0da7d7bb 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -6,16 +6,28 @@ $(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ext $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src)) .PHONY: %_flash -%_flash: $(BUILD_DIR)/%.dfu $(BUILD_DIR)/flasher.light.dfu +%_flash: $(BUILD_DIR)/%.dfu @echo "DFU $@" @echo "INFO About to flash your device. Please plug your device to your computer" @echo " using an USB cable and press at the same time the 6 key and the RESET" @echo " button on the back of your device." $(Q) until $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11" > /dev/null 2>&1; do sleep 2;done - $(eval DFU_SLAVE := $(shell $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11")) - $(Q) if expr "$(DFU_SLAVE)" : ".*0483:df11.*" > /dev/null; \ - then \ - $(PYTHON) build/device/dfu.py -u $(word 2,$^); \ - sleep 2; \ - fi $(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^) + +.PHONY: %.two_binaries +%.two_binaries: %.elf + @echo "Building an internal and an external binary for $<" + $(Q) $(OBJCOPY) -O binary -j .text.external -j .rodata.external -j .exam_mode_buffer $< $(basename $<).external.bin + $(Q) $(OBJCOPY) -O binary -R .text.external -R .rodata.external -R .exam_mode_buffer $< $(basename $<).internal.bin + @echo "Padding $(basename $<).external.bin and $(basename $<).internal.bin" + $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin + $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).internal.bin + +.PHONY: binpack +binpack: $(BUILD_DIR)/flasher.light.bin $(BUILD_DIR)/epsilon.onboarding.two_binaries + rm -rf $(BUILD_DIR)/binpack + mkdir -p $(BUILD_DIR)/binpack + cp $(BUILD_DIR)/flasher.light.bin $(BUILD_DIR)/binpack + cp $(BUILD_DIR)/epsilon.onboarding.internal.bin $(BUILD_DIR)/epsilon.onboarding.external.bin $(BUILD_DIR)/binpack + cd $(BUILD_DIR) && for binary in flasher.light.bin epsilon.onboarding.internal.bin epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done + cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/* diff --git a/ion/Makefile b/ion/Makefile index 45461e1c7..da9057572 100644 --- a/ion/Makefile +++ b/ion/Makefile @@ -22,7 +22,7 @@ include ion/src/shared/tools/Makefile # char test[4]= "ab"; is valid and should initialize test to 'a','b',0,0). # Older versions of GCC are not conformant so we resort to an initializer list. initializer_list = $(shell echo $(1) | sed "s/\(.\)/'\1',/g")0 -$(call object_for,ion/src/shared/platform_info.cpp): SFLAGS += -DPATCH_LEVEL="$(call initializer_list,$(PATCH_LEVEL))" -DEPSILON_VERSION="$(call initializer_list,$(EPSILON_VERSION))" -DOMEGA_VERSION="$(call initializer_list,$(OMEGA_VERSION))" -DOMEGA_USERNAME="$(call initializer_list,$(OMEGA_USERNAME))" +$(call object_for,ion/src/simulator/platform_info.cpp ion/src/device/n0100/platform_info.cpp ion/src/device/n0110/platform_info.cpp ion/src/device/bootloader/platform_info.cpp): SFLAGS += -DPATCH_LEVEL="$(call initializer_list,$(PATCH_LEVEL))" -DEPSILON_VERSION="$(call initializer_list,$(EPSILON_VERSION))" -DOMEGA_VERSION="$(call initializer_list,$(OMEGA_VERSION))" -DOMEGA_USERNAME="$(call initializer_list,$(OMEGA_USERNAME))" ion_src += $(addprefix ion/src/shared/, \ console_line.cpp \ @@ -31,7 +31,6 @@ ion_src += $(addprefix ion/src/shared/, \ events.cpp \ events_keyboard.cpp \ events_modifier.cpp \ - platform_info.cpp \ rtc.cpp \ stack_position.cpp \ storage.cpp \ diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index 963690440..cc5594242 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -4,7 +4,7 @@ include ion/src/device/bench/Makefile include ion/src/device/flasher/Makefile include ion/src/device/$(MODEL)/Makefile -$(call object_for,ion/src/shared/platform_info.cpp): SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" +$(call object_for,ion/src/device/n0100/platform_info.cpp ion/src/device/n0110/platform_info.cpp ion/src/device/bootloader/platform_info.cpp): SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" ifeq ($(EPSILON_TELEMETRY),1) ion_src += ion/src/shared/telemetry_console.cpp diff --git a/ion/src/device/bootloader/Makefile b/ion/src/device/bootloader/Makefile new file mode 100644 index 000000000..87c6b4e54 --- /dev/null +++ b/ion/src/device/bootloader/Makefile @@ -0,0 +1,31 @@ +ion_device_src += $(addprefix ion/src/device/bootloader/drivers/, \ + board.cpp \ + cache.cpp \ + external_flash.cpp \ + led.cpp \ + power.cpp \ + reset.cpp \ + usb.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/bootloader/drivers/, \ + board.cpp \ + cache.cpp \ + external_flash.cpp \ + led.cpp \ + power.cpp \ + reset.cpp \ + usb.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/bootloader/boot/, \ + rt0.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/bootloader/, \ + platform_info.cpp \ +) + +SLOT ?= A + +LDSCRIPT ?= ion/src/device/bootloader/bootloader.$(SLOT).ld diff --git a/ion/src/device/bootloader/boot/rt0.cpp b/ion/src/device/bootloader/boot/rt0.cpp new file mode 100644 index 000000000..a631356bb --- /dev/null +++ b/ion/src/device/bootloader/boot/rt0.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*cxx_constructor)(); + +extern "C" { + extern char _data_section_start_flash; + extern char _data_section_start_ram; + extern char _data_section_end_ram; + extern char _bss_section_start_ram; + extern char _bss_section_end_ram; + extern cxx_constructor _init_array_start; + extern cxx_constructor _init_array_end; + + extern char _isr_vector_table_start_flash; + extern char _isr_vector_table_start_ram; + extern char _isr_vector_table_end_ram; +} + +void __attribute__((noinline)) abort() { +#ifdef NDEBUG + Ion::Device::Reset::core(); +#else + while (1) { + } +#endif +} + +/* In order to ensure that this method is execute from the external flash, we + * forbid inlining it.*/ + +static void __attribute__((noinline)) external_flash_start() { + /* Init the peripherals. We do not initialize the backlight in case there is + * an on boarding app: indeed, we don't want the user to see the LCD tests + * happening during the on boarding app. The backlight will be initialized + * after the Power-On Self-Test if there is one or before switching to the + * home app otherwise. */ + Ion::Device::Board::initPeripherals(false); + + return ion_main(0, nullptr); +} + +/* This additional function call 'jump_to_external_flash' serves two purposes: + * - By default, the compiler is free to inline any function call he wants. If + * the compiler decides to inline some functions that make use of VFP + * registers, it will need to push VFP them onto the stack in calling + * function's prologue. + * Problem: in start()'s prologue, we would never had a chance to enable the + * FPU since this function is the first thing called after reset. + * We can safely assume that neither memcpy, memset, nor any Ion::Device::init* + * method will use floating-point numbers, but ion_main very well can. + * To make sure ion_main's potential usage of VFP registers doesn't bubble-up to + * start(), we isolate it in its very own non-inlined function call. + * - To avoid jumping on the external flash when it is shut down, we ensure + * there is no symbol references from the internal flash to the external + * flash except this jump. In order to do that, we isolate this + * jump in a symbol that we link in a special section separated from the + * internal flash section. We can than forbid cross references from the + * internal flash to the external flash. */ + +static void __attribute__((noinline)) jump_to_external_flash() { + external_flash_start(); +} + +/* When 'start' is executed, the external flash is supposed to be shutdown. We + * thus forbid inlining to prevent executing this code from external flash + * (just in case 'start' was to be called from the external flash). */ + +void __attribute__((noinline)) start() { + /* This is where execution starts after reset. + * Many things are not initialized yet so the code here has to pay attention. */ + + /* Initialize the FPU as early as possible. + * For example, static C++ objects are very likely to manipulate float values */ + Ion::Device::Board::initFPU(); + + /* Copy data section to RAM + * The data section is R/W but its initialization value matters. It's stored + * in Flash, but linked as if it were in RAM. Now's our opportunity to copy + * it. Note that until then the data section (e.g. global variables) contains + * garbage values and should not be used. */ + size_t dataSectionLength = (&_data_section_end_ram - &_data_section_start_ram); + memcpy(&_data_section_start_ram, &_data_section_start_flash, dataSectionLength); + + /* Zero-out the bss section in RAM + * Until we do, any uninitialized global variable will be unusable. */ + size_t bssSectionLength = (&_bss_section_end_ram - &_bss_section_start_ram); + memset(&_bss_section_start_ram, 0, bssSectionLength); + + /* Call static C++ object constructors + * The C++ compiler creates an initialization function for each static object. + * The linker then stores the address of each of those functions consecutively + * between _init_array_start and _init_array_end. So to initialize all C++ + * static objects we just have to iterate between theses two addresses and + * call the pointed function. */ +#define SUPPORT_CPP_GLOBAL_CONSTRUCTORS 0 +#if SUPPORT_CPP_GLOBAL_CONSTRUCTORS + for (cxx_constructor * c = &_init_array_start; c<&_init_array_end; c++) { + (*c)(); + } +#else + /* In practice, static initialized objects are a terrible idea. Since the init + * order is not specified, most often than not this yields the dreaded static + * init order fiasco. How about bypassing the issue altogether? */ + if (&_init_array_start != &_init_array_end) { + abort(); + } +#endif + + /* Copy isr_vector_table section to RAM + * The isr table must be within the memory mapped by the microcontroller (it + * can't live in the external flash). */ + size_t isrSectionLength = (&_isr_vector_table_end_ram - &_isr_vector_table_start_ram); + memcpy(&_isr_vector_table_start_ram, &_isr_vector_table_start_flash, isrSectionLength); + + Ion::Device::Board::init(); + + /* At this point, we initialized clocks and the external flash but no other + * peripherals. */ + + jump_to_external_flash(); + + abort(); +} + +void __attribute__((interrupt, noinline)) isr_systick() { + auto t = Ion::Device::Timing::MillisElapsed; + t++; + Ion::Device::Timing::MillisElapsed = t; +} diff --git a/ion/src/device/bootloader/bootloader.A.ld b/ion/src/device/bootloader/bootloader.A.ld new file mode 100644 index 000000000..df8ad59f8 --- /dev/null +++ b/ion/src/device/bootloader/bootloader.A.ld @@ -0,0 +1,144 @@ +/* Linker script + * The role of this script is to take all the object files built by the compiler + * and produce a single binary suitable for execution. + * Without an explicit linker script, the linker will produce a binary file that + * would not match some of our requirements (for example, we want the code to be + * written at a specific address (in Flash ROM) and the data at another. */ + +/* Let's instruct the linker about our memory layout. + * This will let us use shortcuts such as ">FLASH" to ask for a given section to + * be stored in Flash. */ + +MEMORY { + SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K + FLASH (rx) : ORIGIN = 0x90000000, LENGTH = 4M + /* + ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 16K + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K + SRAM1 (rwx) : ORIGIN = 0x20010000, LENGTH = 176K + SRAM2 (rwx) : ORIGIN = 0x2003C000, LENGTH = 16K + */ +} + +STACK_SIZE = 32K; +FIRST_FLASH_SECTOR_SIZE = 4K; +SIGNED_PAYLOAD_LENGTH = 8; + +SECTIONS { + .signed_payload_prefix ORIGIN(FLASH) : { + FILL(0xFF); + BYTE(0xFF) + . = ORIGIN(FLASH) + SIGNED_PAYLOAD_LENGTH; + } >FLASH + + .header : { + KEEP(*(.header)) + } >FLASH + + .isr_vector_table : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header)) { + /* 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 + * entry points to interrupt service routines. This data is called the ISR + * vector table. + * + * Note that address 0x0 is always an alias. It points to the beginning of + * Flash, SRAM, or integrated bootloader depending on the boot mode chosen. + * (This mode is chosen by setting the BOOTn pins on the chip). + * + * We're generating the ISR vector table in code because it's very + * convenient: using function pointers, we can easily point to the service + * routine for each interrupt. */ + _isr_vector_table_start_flash = LOADADDR(.isr_vector_table); + _isr_vector_table_start_ram = .; + KEEP(*(.isr_vector_table)) + _isr_vector_table_end_ram = .; + } >SRAM + + .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) : { + . = ALIGN(4K); + _exam_mode_buffer_start = .; + KEEP(*(.exam_mode_buffer)) + /* Note: We don't increment "." here, we set it. */ + . = . + FIRST_FLASH_SECTOR_SIZE; + _exam_mode_buffer_end = .; + } >FLASH + + /* External flash memory */ + .text ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) + SIZEOF(.exam_mode_buffer) : { + . = ALIGN(4); + *(.text) + *(.text.*) + } >FLASH + + .rodata : { + *(.rodata) + *(.rodata.*) + } >FLASH + + .init_array : { + . = ALIGN(4); + _init_array_start = .; + KEEP (*(.init_array*)) + _init_array_end = .; + } >FLASH + + .data : { + /* The data section is written to Flash but linked as if it were in RAM. + * + * This is required because its initial value matters (so it has to be in + * persistant memory in the first place), but it is a R/W area of memory + * so it will have to live in RAM upon execution (in linker lingo, that + * translates to the data section having a LMA in Flash and a VMA in RAM). + * + * This means we'll have to copy it from Flash to RAM on initialization. + * To do this, we'll need to know the source location of the data section + * (in Flash), the target location (in RAM), and the size of the section. + * That's why we're defining three symbols that we'll use in the initial- + * -ization routine. */ + . = ALIGN(4); + _data_section_start_flash = LOADADDR(.data); + _data_section_start_ram = .; + *(.data) + *(.data.*) + _data_section_end_ram = .; + } >SRAM AT> FLASH + + .bss : { + /* The bss section contains data for all uninitialized variables + * So like the .data section, it will go in RAM, but unlike the data section + * we don't care at all about an initial value. + * + * Before execution, crt0 will erase that section of memory though, so we'll + * need pointers to the beginning and end of this section. */ + . = ALIGN(4); + _bss_section_start_ram = .; + *(.bss) + *(.bss.*) + /* The compiler may choose to allocate uninitialized global variables as + * COMMON blocks. This can be disabled with -fno-common if needed. */ + *(COMMON) + _bss_section_end_ram = .; + } >SRAM + + .heap : { + _heap_start = .; + /* Note: We don't increment "." here, we set it. */ + . = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE); + _heap_end = .; + } >SRAM + + .stack : { + . = ALIGN(8); + _stack_end = .; + . += (STACK_SIZE - 8); + . = ALIGN(8); + _stack_start = .; + } >SRAM + + /DISCARD/ : { + /* exidx and extab are needed for unwinding, which we don't use */ + *(.ARM.exidx*) + *(.ARM.extab*) + } +} diff --git a/ion/src/device/bootloader/bootloader.B.ld b/ion/src/device/bootloader/bootloader.B.ld new file mode 100644 index 000000000..25cbd2b22 --- /dev/null +++ b/ion/src/device/bootloader/bootloader.B.ld @@ -0,0 +1,144 @@ +/* Linker script + * The role of this script is to take all the object files built by the compiler + * and produce a single binary suitable for execution. + * Without an explicit linker script, the linker will produce a binary file that + * would not match some of our requirements (for example, we want the code to be + * written at a specific address (in Flash ROM) and the data at another. */ + +/* Let's instruct the linker about our memory layout. + * This will let us use shortcuts such as ">FLASH" to ask for a given section to + * be stored in Flash. */ + +MEMORY { + SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K + FLASH (rx) : ORIGIN = 0x90400000, LENGTH = 4M + /* + ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 16K + DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K + SRAM1 (rwx) : ORIGIN = 0x20010000, LENGTH = 176K + SRAM2 (rwx) : ORIGIN = 0x2003C000, LENGTH = 16K + */ +} + +STACK_SIZE = 32K; +FIRST_FLASH_SECTOR_SIZE = 4K; +SIGNED_PAYLOAD_LENGTH = 8; + +SECTIONS { + .signed_payload_prefix ORIGIN(FLASH) : { + FILL(0xFF); + BYTE(0xFF) + . = ORIGIN(FLASH) + SIGNED_PAYLOAD_LENGTH; + } >FLASH + + .header : { + KEEP(*(.header)) + } >FLASH + + .isr_vector_table : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header)) { + /* 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 + * entry points to interrupt service routines. This data is called the ISR + * vector table. + * + * Note that address 0x0 is always an alias. It points to the beginning of + * Flash, SRAM, or integrated bootloader depending on the boot mode chosen. + * (This mode is chosen by setting the BOOTn pins on the chip). + * + * We're generating the ISR vector table in code because it's very + * convenient: using function pointers, we can easily point to the service + * routine for each interrupt. */ + _isr_vector_table_start_flash = LOADADDR(.isr_vector_table); + _isr_vector_table_start_ram = .; + KEEP(*(.isr_vector_table)) + _isr_vector_table_end_ram = .; + } >SRAM + + .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) : { + . = ALIGN(4K); + _exam_mode_buffer_start = .; + KEEP(*(.exam_mode_buffer)) + /* Note: We don't increment "." here, we set it. */ + . = . + FIRST_FLASH_SECTOR_SIZE; + _exam_mode_buffer_end = .; + } >FLASH + + /* External flash memory */ + .text ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) + SIZEOF(.exam_mode_buffer) : { + . = ALIGN(4); + *(.text) + *(.text.*) + } >FLASH + + .rodata : { + *(.rodata) + *(.rodata.*) + } >FLASH + + .init_array : { + . = ALIGN(4); + _init_array_start = .; + KEEP (*(.init_array*)) + _init_array_end = .; + } >FLASH + + .data : { + /* The data section is written to Flash but linked as if it were in RAM. + * + * This is required because its initial value matters (so it has to be in + * persistant memory in the first place), but it is a R/W area of memory + * so it will have to live in RAM upon execution (in linker lingo, that + * translates to the data section having a LMA in Flash and a VMA in RAM). + * + * This means we'll have to copy it from Flash to RAM on initialization. + * To do this, we'll need to know the source location of the data section + * (in Flash), the target location (in RAM), and the size of the section. + * That's why we're defining three symbols that we'll use in the initial- + * -ization routine. */ + . = ALIGN(4); + _data_section_start_flash = LOADADDR(.data); + _data_section_start_ram = .; + *(.data) + *(.data.*) + _data_section_end_ram = .; + } >SRAM AT> FLASH + + .bss : { + /* The bss section contains data for all uninitialized variables + * So like the .data section, it will go in RAM, but unlike the data section + * we don't care at all about an initial value. + * + * Before execution, crt0 will erase that section of memory though, so we'll + * need pointers to the beginning and end of this section. */ + . = ALIGN(4); + _bss_section_start_ram = .; + *(.bss) + *(.bss.*) + /* The compiler may choose to allocate uninitialized global variables as + * COMMON blocks. This can be disabled with -fno-common if needed. */ + *(COMMON) + _bss_section_end_ram = .; + } >SRAM + + .heap : { + _heap_start = .; + /* Note: We don't increment "." here, we set it. */ + . = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE); + _heap_end = .; + } >SRAM + + .stack : { + . = ALIGN(8); + _stack_end = .; + . += (STACK_SIZE - 8); + . = ALIGN(8); + _stack_start = .; + } >SRAM + + /DISCARD/ : { + /* exidx and extab are needed for unwinding, which we don't use */ + *(.ARM.exidx*) + *(.ARM.extab*) + } +} diff --git a/ion/src/device/bootloader/drivers/board.cpp b/ion/src/device/bootloader/drivers/board.cpp new file mode 100644 index 000000000..1d61eef86 --- /dev/null +++ b/ion/src/device/bootloader/drivers/board.cpp @@ -0,0 +1,402 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void(*ISR)(void); +extern ISR InitialisationVector[]; + +// Public Ion methods + +const char * Ion::fccId() { + return "2ALWP-N0110"; +} + +// Private Ion::Device methods + +namespace Ion { +namespace Device { +namespace Board { + +using namespace Regs; + +void initMPU() { + // 1. Disable the MPU + // 1.1 Memory barrier + Cache::dmb(); + + // 1.2 Disable fault exceptions + CORTEX.SHCRS()->setMEMFAULTENA(false); + + // 1.3 Disable the MPU and clear the control register + MPU.CTRL()->setENABLE(false); + + // 2. MPU settings + // 2.1 Configure a MPU region for the FMC memory area + /* This is needed for interfacing with the LCD + * We define the whole FMC memory bank 1 as strongly ordered, non-executable + * and not accessible. We define the FMC command and data addresses as + * writeable non-cachable, non-buffereable and non shareable. */ + int sector = 0; + MPU.RNR()->setREGION(sector++); + MPU.RBAR()->setADDR(0x60000000); + MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_256MB); + MPU.RASR()->setAP(MPU::RASR::AccessPermission::NoAccess); + MPU.RASR()->setXN(true); + MPU.RASR()->setTEX(2); + MPU.RASR()->setS(0); + MPU.RASR()->setC(0); + MPU.RASR()->setB(0); + MPU.RASR()->setENABLE(true); + + MPU.RNR()->setREGION(sector++); + MPU.RBAR()->setADDR(0x60000000); + MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32B); + MPU.RASR()->setXN(true); + MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW); + MPU.RASR()->setTEX(2); + MPU.RASR()->setS(0); + MPU.RASR()->setC(0); + MPU.RASR()->setB(0); + MPU.RASR()->setENABLE(true); + + MPU.RNR()->setREGION(sector++); + MPU.RBAR()->setADDR(0x60000000+0x20000); + MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32B); + MPU.RASR()->setXN(true); + MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW); + MPU.RASR()->setTEX(2); + MPU.RASR()->setS(0); + MPU.RASR()->setC(0); + MPU.RASR()->setB(0); + MPU.RASR()->setENABLE(true); + + // 2.2 Configure MPU regions for the QUADSPI peripheral + /* L1 Cache can issue speculative reads to any memory address. But, when the + * Quad-SPI is in memory-mapped mode, if an access is made to an address + * outside of the range defined by FSIZE but still within the 256Mbytes range, + * then an AHB error is given (AN4760). To prevent this to happen, we + * configure the MPU to define the whole Quad-SPI addressable space as + * strongly ordered, non-executable and not accessible. Plus, we define the + * Quad-SPI region corresponding to the Expternal Chip as executable and + * fully accessible (AN4861). */ + MPU.RNR()->setREGION(sector++); + MPU.RBAR()->setADDR(0x90000000); + MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_256MB); + MPU.RASR()->setAP(MPU::RASR::AccessPermission::NoAccess); + MPU.RASR()->setXN(true); + MPU.RASR()->setTEX(0); + MPU.RASR()->setS(0); + MPU.RASR()->setC(0); + MPU.RASR()->setB(0); + MPU.RASR()->setENABLE(true); + + MPU.RNR()->setREGION(sector++); + MPU.RBAR()->setADDR(0x90000000); + MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_8MB); + MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW); + MPU.RASR()->setXN(false); + MPU.RASR()->setTEX(0); + MPU.RASR()->setS(0); + MPU.RASR()->setC(1); + MPU.RASR()->setB(0); + MPU.RASR()->setENABLE(true); + + // 2.3 Enable MPU + MPU.CTRL()->setPRIVDEFENA(true); + MPU.CTRL()->setENABLE(true); + + // 3. Data/instruction synchronisation barriers to ensure that the new MPU configuration is used by subsequent instructions. + Cache::dsb(); + Cache::isb(); +} + +void init() { + initMPU(); + initClocks(); + + // Ensure right location of interrupt vectors + CORTEX.VTOR()->setVTOR((void*)&InitialisationVector); + + // Initiate L1 cache after initiating the external flash + Cache::enable(); +} + +void initClocks() { + /* System clock + * Configure the CPU at 192 MHz and USB at 48 MHz. */ + + /* After reset, the device is using the high-speed internal oscillator (HSI) + * as a clock source, which runs at a fixed 16 MHz frequency. The HSI is not + * accurate enough for reliable USB operation, so we need to use the external + * high-speed oscillator (HSE). */ + + // Enable the HSI and wait for it to be ready + RCC.CR()->setHSION(true); + while(!RCC.CR()->getHSIRDY()) { + } + + // Enable the HSE and wait for it to be ready + RCC.CR()->setHSEON(true); + while(!RCC.CR()->getHSERDY()) { + } + + // Enable PWR peripheral clock + RCC.APB1ENR()->setPWREN(true); + + /* To pass electromagnetic compatibility tests, we activate the Spread + * Spectrum clock generation, which adds jitter to the PLL clock in order to + * "lower peak-energy on the central frequency" and its harmonics. + * It must be done before enabling the PLL. */ + class RCC::SSCGR sscgr(0); // Reset value + sscgr.setMODPER(Clocks::Config::SSCG_MODPER); + sscgr.setINCSTEP(Clocks::Config::SSCG_INCSTEP); + sscgr.setSPREADSEL(RCC::SSCGR::SPREADSEL::CenterSpread); + sscgr.setSSCGEN(true); + RCC.SSCGR()->set(sscgr); + + /* Given the crystal used on our device, the HSE will oscillate at 8 MHz. By + * piping it through a phase-locked loop (PLL) we can derive other frequencies + * for use in different parts of the system. */ + + // Configure the PLL ratios and use HSE as a PLL input + RCC.PLLCFGR()->setPLLM(Clocks::Config::PLL_M); + RCC.PLLCFGR()->setPLLN(Clocks::Config::PLL_N); + RCC.PLLCFGR()->setPLLQ(Clocks::Config::PLL_Q); + RCC.PLLCFGR()->setPLLSRC(RCC::PLLCFGR::PLLSRC::HSE); + + // Enable the PLL and wait for it to be ready + RCC.CR()->setPLLON(true); + + // Enable Over-drive + PWR.CR()->setODEN(true); + while(!PWR.CSR()->getODRDY()) { + } + + PWR.CR()->setODSWEN(true); + while(!PWR.CSR()->getODSWRDY()) { + } + + // Choose Voltage scale 1 + PWR.CR()->setVOS(PWR::CR::Voltage::Scale1); + while (!PWR.CSR()->getVOSRDY()) {} + + /* After reset the Flash runs as fast as the CPU. When we clock the CPU faster + * the flash memory cannot follow and therefore flash memory accesses need to + * wait a little bit. + * The spec tells us that at 2.8V and over 210MHz the flash expects 7 WS. */ + FLASH.ACR()->setLATENCY(7); + + /* Enable prefetching flash instructions */ + /* Fetching instructions increases slightly the power consumption but the + * increase is negligible compared to the screen consumption. */ + FLASH.ACR()->setPRFTEN(true); + + /* Enable the ART */ + FLASH.ACR()->setARTEN(true); + + // 192 MHz is too fast for APB1. Divide it by four to reach 48 MHz + RCC.CFGR()->setPPRE1(Clocks::Config::APB1PrescalerReg); + // 192 MHz is too fast for APB2. Divide it by two to reach 96 MHz + RCC.CFGR()->setPPRE2(Clocks::Config::APB2PrescalerReg); + + while(!RCC.CR()->getPLLRDY()) { + } + + // Use the PLL output as a SYSCLK source + RCC.CFGR()->setSW(RCC::CFGR::SW::PLL); + while (RCC.CFGR()->getSWS() != RCC::CFGR::SW::PLL) { + } + + // Now that we don't need use it anymore, turn the HSI off + RCC.CR()->setHSION(false); + + // Peripheral clocks + + // AHB1 bus + // Our peripherals are using GPIO A, B, C, D and E. + // We're not using the CRC nor DMA engines. + class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value + ahb1enr.setGPIOAEN(true); + ahb1enr.setGPIOBEN(true); + ahb1enr.setGPIOCEN(true); + ahb1enr.setGPIODEN(true); + ahb1enr.setGPIOEEN(true); + ahb1enr.setDMA2EN(true); + RCC.AHB1ENR()->set(ahb1enr); + + // AHB2 bus + RCC.AHB2ENR()->setOTGFSEN(true); + + // AHB3 bus + RCC.AHB3ENR()->setFSMCEN(true); + + // APB1 bus + // We're using TIM3 for the LEDs + RCC.APB1ENR()->setTIM3EN(true); + RCC.APB1ENR()->setPWREN(true); + RCC.APB1ENR()->setRTCAPB(true); + + // APB2 bus + class RCC::APB2ENR apb2enr(0); // Reset value + apb2enr.setADC1EN(true); + apb2enr.setSYSCFGEN(true); + apb2enr.setUSART6EN(true); // TODO required if building bench target only? + RCC.APB2ENR()->set(apb2enr); + + // Configure clocks in sleep mode + // AHB1 peripheral clock enable in low-power mode register + class RCC::AHB1LPENR ahb1lpenr(0x7EF7B7FF); // Reset value + ahb1lpenr.setGPIOALPEN(true); // Enable IO port A for Charging/USB plug/Keyboard pins + ahb1lpenr.setGPIOBLPEN(true); // Enable IO port B for LED pins + ahb1lpenr.setGPIOCLPEN(true); // Enable IO port C for LED/Keyboard pins + ahb1lpenr.setGPIODLPEN(false); // Disable IO port D (LCD...) + ahb1lpenr.setGPIOELPEN(true); // Enable IO port E for Keyboard/Battery pins + ahb1lpenr.setGPIOFLPEN(false); // Disable IO port F + ahb1lpenr.setGPIOGLPEN(false); // Disable IO port G + ahb1lpenr.setGPIOHLPEN(false); // Disable IO port H + ahb1lpenr.setGPIOILPEN(false); // Disable IO port I + ahb1lpenr.setCRCLPEN(false); + ahb1lpenr.setFLITFLPEN(false); + ahb1lpenr.setSRAM1LPEN(false); + ahb1lpenr.setDMA1LPEN(false); + ahb1lpenr.setDMA2LPEN(false); + ahb1lpenr.setAXILPEN(false); + ahb1lpenr.setSRAM2LPEN(false); + ahb1lpenr.setBKPSRAMLPEN(false); + ahb1lpenr.setDTCMLPEN(false); + ahb1lpenr.setOTGHSLPEN(false); + ahb1lpenr.setOTGHSULPILPEN(false); + RCC.AHB1LPENR()->set(ahb1lpenr); + + // AHB2 peripheral clock enable in low-power mode register + class RCC::AHB2LPENR ahb2lpenr(0x000000F1); // Reset value + ahb2lpenr.setOTGFSLPEN(false); + ahb2lpenr.setRNGLPEN(false); + ahb2lpenr.setAESLPEN(false); + RCC.AHB2LPENR()->set(ahb2lpenr); + + // AHB3 peripheral clock enable in low-power mode register + class RCC::AHB3LPENR ahb3lpenr(0x00000003); // Reset value + ahb3lpenr.setFMCLPEN(false); + ahb3lpenr.setQSPILPEN(false); + RCC.AHB3LPENR()->set(ahb3lpenr); + + // APB1 peripheral clock enable in low-power mode register + class RCC::APB1LPENR apb1lpenr(0xFFFFCBFF); // Reset value + apb1lpenr.setTIM2LPEN(false); + apb1lpenr.setTIM3LPEN(true); // Enable TIM3 in sleep mode for LEDs + apb1lpenr.setTIM4LPEN(false); + apb1lpenr.setTIM5LPEN(false); + apb1lpenr.setTIM6LPEN(false); + apb1lpenr.setTIM7LPEN(false); + apb1lpenr.setTIM12LPEN(false); + apb1lpenr.setTIM13LPEN(false); + apb1lpenr.setTIM14LPEN(false); + apb1lpenr.setRTCAPBLPEN(false); + apb1lpenr.setWWDGLPEN(false); + apb1lpenr.setSPI2LPEN(false); + apb1lpenr.setSPI3LPEN(false); + apb1lpenr.setUSART2LPEN(false); + apb1lpenr.setUSART3LPEN(false); + apb1lpenr.setI2C1LPEN(false); + apb1lpenr.setI2C2LPEN(false); + apb1lpenr.setI2C3LPEN(false); + apb1lpenr.setCAN1LPEN(false); + apb1lpenr.setPWRLPEN(false); + apb1lpenr.setLPTIM1LPEN(false); + apb1lpenr.setUSART4LPEN(false); + apb1lpenr.setUSART5LPEN(false); + apb1lpenr.setOTGHSLPEN(false); + apb1lpenr.setOTGHSULPILPEN(false); + RCC.APB1LPENR()->set(apb1lpenr); + + // APB2 peripheral clock enable in low-power mode register + class RCC::APB2LPENR apb2lpenr(0x04F77F33); // Reset value + apb2lpenr.setTIM1LPEN(false); + apb2lpenr.setTIM8LPEN(false); + apb2lpenr.setUSART1LPEN(false); + apb2lpenr.setUSART6LPEN(false); + apb2lpenr.setADC1LPEN(false); + apb2lpenr.setSPI1LPEN(false); + apb2lpenr.setSPI4LPEN(false); + apb2lpenr.setSYSCFGLPEN(false); + apb2lpenr.setTIM9LPEN(false); + apb2lpenr.setTIM10LPEN(false); + apb2lpenr.setTIM11LPEN(false); + apb2lpenr.setSPI5LPEN(false); + apb2lpenr.setSDMMC2LPEN(false); + apb2lpenr.setADC2LPEN(false); + apb2lpenr.setADC3LPEN(false); + apb2lpenr.setSAI1LPEN(false); + apb2lpenr.setSAI2LPEN(false); + RCC.APB2LPENR()->set(apb2lpenr); +} + +void shutdownClocks(bool keepLEDAwake) { + // APB2 bus + RCC.APB2ENR()->set(0); // Reset value + + // AHB2 bus + RCC.AHB2ENR()->set(0); // Reset value + + // AHB3 bus + RCC.AHB3ENR()->set(0); // Reset value + + // APB1 + class RCC::APB1ENR apb1enr(0); // Reset value + // AHB1 bus + class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value + if (keepLEDAwake) { + apb1enr.setTIM3EN(true); + ahb1enr.setGPIOBEN(true); + } + RCC.APB1ENR()->set(apb1enr); + RCC.AHB1ENR()->set(ahb1enr); +} + +constexpr int k_pcbVersionOTPIndex = 0; + +/* As we want the PCB versions to be in ascending order chronologically, and + * because the OTP are initialized with 1s, we store the bitwise-not of the + * version number. This way, devices with blank OTP are considered version 0. */ + +PCBVersion pcbVersion() { +#if IN_FACTORY + /* When flashing for the first time, we want all systems that depend on the + * PCB version to function correctly before flashing the PCB version. This + * way, flashing the PCB version can be done last. */ + return PCB_LATEST; +#else + PCBVersion version = readPCBVersionInMemory(); + return (version == k_alternateBlankVersion ? 0 : version); +#endif +} + +PCBVersion readPCBVersionInMemory() { + return ~(*reinterpret_cast(InternalFlash::Config::OTPAddress(k_pcbVersionOTPIndex))); +} + +void writePCBVersion(PCBVersion version) { + uint8_t * destination = reinterpret_cast(InternalFlash::Config::OTPAddress(k_pcbVersionOTPIndex)); + PCBVersion formattedVersion = ~version; + InternalFlash::WriteMemory(destination, reinterpret_cast(&formattedVersion), sizeof(formattedVersion)); +} + +void lockPCBVersion() { + uint8_t * destination = reinterpret_cast(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)); + uint8_t zero = 0; + InternalFlash::WriteMemory(destination, &zero, sizeof(zero)); +} + +bool pcbVersionIsLocked() { + return *reinterpret_cast(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)) == 0; +} + +} +} +} diff --git a/ion/src/device/bootloader/drivers/cache.cpp b/ion/src/device/bootloader/drivers/cache.cpp new file mode 100644 index 000000000..0d16f5261 --- /dev/null +++ b/ion/src/device/bootloader/drivers/cache.cpp @@ -0,0 +1,104 @@ +#include "cache.h" + +namespace Ion { +namespace Device { +namespace Cache { + +using namespace Regs; + +void privateCleanInvalidateDisableDCache(bool clean, bool invalidate, bool disable) { + // Select Level 1 data cache + CORTEX.CSSELR()->set(0); + dsb(); + + // Disable D-Cache + if (disable) { + CORTEX.CCR()->setDC(false); + dsb(); + } + + // Pick the right DC??SW register according to invalidate/disable parameters + volatile CORTEX::DCSW * target = nullptr; + if (clean && invalidate) { + target = CORTEX.DCCISW(); + } else if (clean) { + target = CORTEX.DCCSW(); + } else { + assert(invalidate); + target = CORTEX.DCISW(); + } + + class CORTEX::CCSIDR ccsidr = CORTEX.CCSIDR()->get(); + uint32_t sets = ccsidr.getNUMSETS(); + uint32_t ways = ccsidr.getASSOCIATIVITY(); + + for (int set = sets; set >= 0; set--) { + for (int way = ways; way >= 0; way--) { + class CORTEX::DCSW dcsw; + dcsw.setSET(set); + dcsw.setWAY(way); + target->set(dcsw); + } + } + + dsb(); + isb(); +} + +void enable() { + enableICache(); + enableDCache(); +} + +void disable() { + disableICache(); + disableDCache(); +} + +void invalidateDCache() { + privateCleanInvalidateDisableDCache(false, true, false); +} + +void cleanDCache() { + privateCleanInvalidateDisableDCache(true, false, false); +} + +void enableDCache() { + invalidateDCache(); + CORTEX.CCR()->setDC(true); // Enable D-cache + dsb(); + isb(); +} + +void disableDCache() { + privateCleanInvalidateDisableDCache(true, true, true); +} + +void invalidateICache() { + dsb(); + isb(); + CORTEX.ICIALLU()->set(0); // Invalidate I-cache + dsb(); + isb(); +} + +void enableICache() { + invalidateICache(); + CORTEX.CCR()->setIC(true); // Enable I-cache + dsb(); + isb(); +} + +void disableICache() { + dsb(); + isb(); + CORTEX.CCR()->setIC(false); // Disable I-cache + CORTEX.ICIALLU()->set(0); // Invalidate I-cache + dsb(); + isb(); +} + + +} +} +} diff --git a/ion/src/device/bootloader/drivers/cache.h b/ion/src/device/bootloader/drivers/cache.h new file mode 100644 index 000000000..cc047743b --- /dev/null +++ b/ion/src/device/bootloader/drivers/cache.h @@ -0,0 +1,46 @@ +#ifndef ION_DEVICE_N0110_CACHE_H +#define ION_DEVICE_N0110_CACHE_H + +#include + +namespace Ion { +namespace Device { +namespace Cache { + +/* Data memory barrier + * Ensures that all explicit memory accesses that appear in program order before + * the DMB instruction are observed before any explicit memory accesses that + * appear in program order after the DMB instruction */ +inline void dmb() { + asm volatile("dmb 0xF":::"memory"); +} + +/* Data synchronisation barrier + * Ensures that the processor stalls until the memory write is complete */ +inline void dsb() { + asm volatile("dsb 0xF":::"memory"); +} + +/* Instructions synchronisation barrier + * Ensures that the subsequent instructions are loaded in the new context */ +inline void isb() { + asm volatile("isb 0xF":::"memory"); +} + +void enable(); +void disable(); + +void invalidateDCache(); +void cleanDCache(); +void enableDCache(); +void disableDCache(); + +void invalidateICache(); +void enableICache(); +void disableICache(); + +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/backlight.h b/ion/src/device/bootloader/drivers/config/backlight.h new file mode 100644 index 000000000..6f9483869 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/backlight.h @@ -0,0 +1,25 @@ +#ifndef ION_DEVICE_N0110_CONFIG_BACKLIGHT_H +#define ION_DEVICE_N0110_CONFIG_BACKLIGHT_H + +#include + +/* Pin | Role | Mode | Function + * -----+-------------------+-----------------------+---------- + * PE0 | Backlight Enable | Output | + */ + +namespace Ion { +namespace Device { +namespace Backlight { +namespace Config { + +using namespace Regs; + +constexpr static GPIOPin BacklightPin = GPIOPin(GPIOE, 0); + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/battery.h b/ion/src/device/bootloader/drivers/config/battery.h new file mode 100644 index 000000000..07851149b --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/battery.h @@ -0,0 +1,30 @@ +#ifndef ION_DEVICE_N0110_CONFIG_BATTERY_H +#define ION_DEVICE_N0110_CONFIG_BATTERY_H + +#include + +/* Pin | Role | Mode | Function + * -----+-------------------+-----------------------+---------- + * PE3 | BAT_CHRG | Input, pulled up | Low = charging, high = full + * PB1 | VBAT_SNS | Analog | ADC1_1 + */ + +namespace Ion { +namespace Device { +namespace Battery { +namespace Config { + +using namespace Regs; + +constexpr static GPIOPin ChargingPin = GPIOPin(GPIOE, 3); +constexpr static GPIOPin ADCPin = GPIOPin(GPIOB, 1); +constexpr uint8_t ADCChannel = 9; +constexpr float ADCReferenceVoltage = 2.8f; +constexpr float ADCDividerBridgeRatio = 2.0f; + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/clocks.h b/ion/src/device/bootloader/drivers/config/clocks.h new file mode 100644 index 000000000..0cc551cb0 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/clocks.h @@ -0,0 +1,68 @@ +#ifndef ION_DEVICE_N0110_CONFIG_CLOCKS_H +#define ION_DEVICE_N0110_CONFIG_CLOCKS_H + +#include + +namespace Ion { +namespace Device { +namespace Clocks { +namespace Config { + +/* If you want to considerably slow down the whole machine uniformely, which + * can be very useful to diagnose performance issues, change the PLL + * configuration to: + * PLL_M = 8 + * PLL_N = 192 + * PLL_P_Reg = Regs::RCC::PLLCFGR::PLLP::PLLP8 + * PLL_Q = 4 + * + * SYSCLK and HCLK will be set to 24 MHz. + * Note that even booting takes a few seconds, so don't be surprised + * if the screen is black for a short while upon booting. */ + +constexpr static int HSE = 8; +constexpr static int PLL_M = 8; +constexpr static int PLL_N = 384; +constexpr static Regs::RCC::PLLCFGR::PLLP PLL_P_Reg = Regs::RCC::PLLCFGR::PLLP::PLLP2; +constexpr static int PLL_P = ((int)PLL_P_Reg | 1) << 1; +constexpr static int PLL_Q = 8; +constexpr static int SYSCLKFrequency = ((HSE/PLL_M)*PLL_N)/PLL_P; +constexpr static int AHBPrescaler = 1; +/* To slow down the whole system, we prescale the AHB clock. + * We could divide the system clock by 512. However, the HCLK clock + * frequency must be >= 14.2MHz and <=216 MHz which forces the + * AHBPrescaler to be below 192MHz/14.2MHz~13.5. */ +constexpr static Regs::RCC::CFGR::AHBPrescaler AHBLowFrequencyPrescalerReg = Regs::RCC::CFGR::AHBPrescaler::SysClkDividedBy8; +constexpr static int AHBLowFrequencyPrescaler = 8; +constexpr static int HCLKFrequency = SYSCLKFrequency/AHBPrescaler; +static_assert(HCLKFrequency == 192, "HCLK frequency changed!"); +constexpr static int HCLKLowFrequency = SYSCLKFrequency/AHBLowFrequencyPrescaler; +constexpr static int AHBFrequency = HCLKFrequency; +//constexpr static int AHBLowFrequency = HCLKLowFrequency; +constexpr static Regs::RCC::CFGR::APBPrescaler APB1PrescalerReg = Regs::RCC::CFGR::APBPrescaler::AHBDividedBy4; +constexpr static int APB1Prescaler = 4; +//constexpr static int APB1Frequency = HCLKFrequency/APB1Prescaler; +constexpr static int APB1LowFrequency = HCLKLowFrequency/APB1Prescaler; +//constexpr static int APB1TimerFrequency = 2*APB1Frequency; +constexpr static int APB1TimerLowFrequency = 2*APB1LowFrequency; + +constexpr static Regs::RCC::CFGR::APBPrescaler APB2PrescalerReg = Regs::RCC::CFGR::APBPrescaler::AHBDividedBy2; + +/* According to AN4850 about Spread Spectrum clock generation + * MODPER = round[HSE/(4 x fMOD)] with fMOD the target modulation frequency. */ +constexpr static int fMod = 8; // in KHz. Must be <= 10KHz +constexpr static uint32_t SSCG_MODPER = HSE*1000/(4*fMod); // *1000 to put HSE in KHz +/* According to the USB specification 2, "For full-speed only functions, the + * required data-rate when transmitting (TFDRATE) is 12.000 Mb/s ±0.25%". */ +constexpr static double modulationDepth = 0.25; // Must be (0.25% <= md <= 2%) +// INCSTEP = round[(2^15 -1)xmdxPLLN)/(100x5xMODPER) +constexpr static uint32_t SSCG_INCSTEP = (32767*modulationDepth*PLL_N)/(1.0*100*5*SSCG_MODPER); +static_assert(SSCG_MODPER == 250, "SSCG_MODPER changed"); +static_assert(SSCG_INCSTEP == 25, "SSCG_INCSTEP changed"); +static_assert(SSCG_INCSTEP * SSCG_MODPER < 32767, "Wrong values for the Spread spectrun clock generator"); +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/console.h b/ion/src/device/bootloader/drivers/config/console.h new file mode 100644 index 000000000..58a527201 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/console.h @@ -0,0 +1,32 @@ +#ifndef ION_DEVICE_N0110_CONFIG_CONSOLE_H +#define ION_DEVICE_N0110_CONFIG_CONSOLE_H + +#include + +namespace Ion { +namespace Device { +namespace Console { +namespace Config { + +using namespace Regs; + +constexpr static USART Port = USART(6); +constexpr static GPIOPin RxPin = GPIOPin(GPIOC, 7); +constexpr static GPIOPin TxPin = GPIOPin(GPIOC, 6); +constexpr static GPIO::AFR::AlternateFunction AlternateFunction = GPIO::AFR::AlternateFunction::AF8; + +/* The baud rate of the UART is set by the following equation: + * BaudRate = f/USARTDIV, where f is the clock frequency and USARTDIV a divider. + * In other words, USARTDIV = f/BaudRate. All frequencies in Hz. + * + * In our case, we configure the minicom to use a 115200 BaudRate and + * f = fAPB2 = 96 MHz, so USARTDIV = 833.333 */ +constexpr static int USARTDIVValue = 833; + + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/display.h b/ion/src/device/bootloader/drivers/config/display.h new file mode 100644 index 000000000..c14f0d21b --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/display.h @@ -0,0 +1,38 @@ +#ifndef ION_DEVICE_N0110_CONFIG_DISPLAY_H +#define ION_DEVICE_N0110_CONFIG_DISPLAY_H + +#include + +namespace Ion { +namespace Device { +namespace Display { +namespace Config { + +using namespace Regs; + +constexpr static GPIOPin FSMCPins[] = { + GPIOPin(GPIOD, 0), GPIOPin(GPIOD, 1), GPIOPin(GPIOD, 4), GPIOPin(GPIOD, 5), + GPIOPin(GPIOD, 7), GPIOPin(GPIOD, 8), GPIOPin(GPIOD, 9), GPIOPin(GPIOD, 10), + GPIOPin(GPIOD, 11), GPIOPin(GPIOD, 14), GPIOPin(GPIOD, 15), GPIOPin(GPIOE, 7), + GPIOPin(GPIOE, 8), GPIOPin(GPIOE, 9), GPIOPin(GPIOE, 10), GPIOPin(GPIOE, 11), + GPIOPin(GPIOE, 12), GPIOPin(GPIOE, 13), GPIOPin(GPIOE, 14), GPIOPin(GPIOE, 15), +}; + +constexpr static GPIOPin PowerPin = GPIOPin(GPIOC, 8); +constexpr static GPIOPin ResetPin = GPIOPin(GPIOE, 1); +constexpr static GPIOPin ExtendedCommandPin = GPIOPin(GPIOD, 6); +constexpr static GPIOPin TearingEffectPin = GPIOPin(GPIOB, 11); + +constexpr static DMA DMAEngine = DMA2; +constexpr static int DMAStream = 0; + +constexpr static int HCLKFrequencyInMHz = 192; + +constexpr static bool DisplayInversion = true; + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/exam_mode.h b/ion/src/device/bootloader/drivers/config/exam_mode.h new file mode 100644 index 000000000..e2a2e2aba --- /dev/null +++ b/ion/src/device/bootloader/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/bootloader/drivers/config/external_flash.h b/ion/src/device/bootloader/drivers/config/external_flash.h new file mode 100644 index 000000000..f245dca20 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/external_flash.h @@ -0,0 +1,45 @@ +#ifndef ION_DEVICE_N0110_CONFIG_EXTERNAL_FLASH_H +#define ION_DEVICE_N0110_CONFIG_EXTERNAL_FLASH_H + +#include + +/* Pin | Role | Mode | Function + * -----+----------------------+-----------------------+----------------- + * PB2 | QUADSPI CLK | Alternate Function 9 | QUADSPI_CLK + * PB6 | QUADSPI BK1_NCS | Alternate Function 10 | QUADSPI_BK1_NCS + * PE2 | QUADSPI BK1_IO2/WP | Alternate Function 9 | QUADSPI_BK1_IO2 + * PC9 | QUADSPI BK1_IO0/SO | Alternate Function 9 | QUADSPI_BK1_IO0 + * PD12 | QUADSPI BK1_IO1/SI | Alternate Function 9 | QUADSPI_BK1_IO1 + * PD13 | QUADSPI BK1_IO3/HOLD | Alternate Function 9 | QUADSPI_BK1_IO3 + */ + +namespace Ion { +namespace Device { +namespace ExternalFlash { +namespace Config { + +using namespace Regs; + +constexpr static uint32_t StartAddress = 0x90000000; +constexpr static uint32_t EndAddress = 0x90800000; + +constexpr static int NumberOf4KSectors = 8; +constexpr static int NumberOf32KSectors = 1; +constexpr static int NumberOf64KSectors = 128 - 1; +constexpr static int NumberOfSectors = NumberOf4KSectors + NumberOf32KSectors + NumberOf64KSectors; + +constexpr static AFGPIOPin Pins[] = { + AFGPIOPin(GPIOB, 2, GPIO::AFR::AlternateFunction::AF9, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), + AFGPIOPin(GPIOB, 6, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), + AFGPIOPin(GPIOC, 9, GPIO::AFR::AlternateFunction::AF9, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), + AFGPIOPin(GPIOD, 12, GPIO::AFR::AlternateFunction::AF9, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), + AFGPIOPin(GPIOD, 13, GPIO::AFR::AlternateFunction::AF9, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), + AFGPIOPin(GPIOE, 2, GPIO::AFR::AlternateFunction::AF9, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), +}; + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/internal_flash.h b/ion/src/device/bootloader/drivers/config/internal_flash.h new file mode 100644 index 000000000..9fcbeed14 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/internal_flash.h @@ -0,0 +1,31 @@ +#ifndef ION_DEVICE_N0110_CONFIG_INTERNAL_FLASH_H +#define ION_DEVICE_N0110_CONFIG_INTERNAL_FLASH_H + +#include + +namespace Ion { +namespace Device { +namespace InternalFlash { +namespace Config { + +constexpr static uint32_t StartAddress = 0x08000000; +constexpr static uint32_t EndAddress = 0x08010000; +constexpr static int NumberOfSectors = 4; +constexpr static uint32_t SectorAddresses[NumberOfSectors+1] = { + 0x08000000, 0x08004000, 0x08008000, 0x0800C000, + 0x08010000 +}; + +constexpr static uint32_t OTPStartAddress = 0x1FF07800; +constexpr static uint32_t OTPLocksAddress = 0x1FF07A00; +constexpr static int NumberOfOTPBlocks = 16; +constexpr static uint32_t OTPBlockSize = 0x20; +constexpr uint32_t OTPAddress(int block) { return OTPStartAddress + block * OTPBlockSize; }; +constexpr uint32_t OTPLockAddress(int block) { return OTPLocksAddress + block; } + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/keyboard.h b/ion/src/device/bootloader/drivers/config/keyboard.h new file mode 100644 index 000000000..2c98f84a3 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/keyboard.h @@ -0,0 +1,75 @@ +#ifndef ION_DEVICE_N0110_CONFIG_KEYBOARD_H +#define ION_DEVICE_N0110_CONFIG_KEYBOARD_H + +#include +#include + +/* Pin | Role | Mode + * -----+-------------------+-------------------- + * PC0 | Keyboard column 1 | Input, pulled up + * PC1 | Keyboard column 2 | Input, pulled up + * PC2 | Keyboard column 3 | Input, pulled up + * PC3 | Keyboard column 4 | Input, pulled up + * PC4 | Keyboard column 5 | Input, pulled up + * PC5 | Keyboard column 6 | Input, pulled up + * PA1 | Keyboard row A | Output, open drain + * PA0 | Keyboard row B | Output, open drain + * PA2 | Keyboard row C | Output, open drain + * PA3 | Keyboard row D | Output, open drain + * PA4 | Keyboard row E | Output, open drain + * PA5 | Keyboard row F | Output, open drain + * PA6 | Keyboard row G | Output, open drain + * PA7 | Keyboard row H | Output, open drain + * PA8 | Keyboard row I | Output, open drain + * + * The keyboard is a matrix that is laid out as follow: + * + * -+------+------+------+------+------+------+ + * | K_A1 | K_A2 | K_A3 | K_A4 | K_A5 | K_A6 | + * -+------+------+------+------+------+------+ + * | K_B1 | | K_B3 | | | | + * -+------+------+------+------+------+------+ + * | K_C1 | K_C2 | K_C3 | K_C4 | K_C5 | K_C6 | + * -+------+------+------+------+------+------+ + * | K_D1 | K_D2 | K_D3 | K_D4 | K_D5 | K_D6 | + * -+------+------+------+------+------+------+ + * | K_E1 | K_E2 | K_E3 | K_E4 | K_E5 | K_E6 | + * -+------+------+------+------+------+------+ + * | K_F1 | K_F2 | K_F3 | K_F4 | K_F5 | | + * -+------+------+------+------+------+------+ + * | K_G1 | K_G2 | K_G3 | K_G4 | K_G5 | | + * -+------+------+------+------+------+------+ + * | K_H1 | K_H2 | K_H3 | K_H4 | K_H5 | | + * -+------+------+------+------+------+------+ + * | K_I1 | K_I2 | K_I3 | K_I4 | K_I5 | | + * -+------+------+------+------+------+------| + */ + +namespace Ion { +namespace Device { +namespace Keyboard { +namespace Config { + +using namespace Regs; + +constexpr GPIO RowGPIO = GPIOA; +constexpr uint8_t numberOfRows = 9; +constexpr uint8_t RowPins[numberOfRows] = {1, 0, 2, 3, 4, 5, 6, 7, 8}; + +constexpr GPIO ColumnGPIO = GPIOC; +constexpr uint8_t numberOfColumns = 6; +constexpr uint8_t ColumnPins[numberOfColumns] = {0, 1, 2, 3, 4, 5}; + +/* Undefined keys numbers are: 7, 9, 10, 11, 35, 41, 47 and 53 + * Therefore we want to make sure those bits are forced to zero in + * whatever value we return. */ +inline uint64_t ValidKeys(uint64_t state) { + return state & 0x1F7DF7FFFFF17F; +} + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/led.h b/ion/src/device/bootloader/drivers/config/led.h new file mode 100644 index 000000000..1fea36d4a --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/led.h @@ -0,0 +1,28 @@ +#ifndef ION_DEVICE_N0110_CONFIG_LED_H +#define ION_DEVICE_N0110_CONFIG_LED_H + +#include + +namespace Ion { +namespace Device { +namespace LED { +namespace Config { + +using namespace Regs; + +static constexpr int RedChannel = 1; +static constexpr int GreenChannel = 2; +static constexpr int BlueChannel = 3; + +constexpr static AFGPIOPin RGBPins[] = { + AFGPIOPin(GPIOB, 4, GPIO::AFR::AlternateFunction::AF2, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Low), // RED + AFGPIOPin(GPIOB, 5, GPIO::AFR::AlternateFunction::AF2, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Low), // GREEN + AFGPIOPin(GPIOB, 0, GPIO::AFR::AlternateFunction::AF2, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Low) // BLUE +}; + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/serial_number.h b/ion/src/device/bootloader/drivers/config/serial_number.h new file mode 100644 index 000000000..c5ad127c3 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/serial_number.h @@ -0,0 +1,18 @@ +#ifndef ION_DEVICE_N0110_CONFIG_SERIAL_NUMBER_H +#define ION_DEVICE_N0110_CONFIG_SERIAL_NUMBER_H + +#include + +namespace Ion { +namespace Device { +namespace SerialNumber { +namespace Config { + +constexpr uint32_t UniqueDeviceIDAddress = 0x1FF07A10; + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/swd.h b/ion/src/device/bootloader/drivers/config/swd.h new file mode 100644 index 000000000..1b9fcaa20 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/swd.h @@ -0,0 +1,24 @@ +#ifndef ION_DEVICE_N0110_CONFIG_SWD_H +#define ION_DEVICE_N0110_CONFIG_SWD_H + +#include + +namespace Ion { +namespace Device { +namespace SWD { +namespace Config { + +using namespace Regs; + +constexpr static AFGPIOPin Pins[] = { + AFGPIOPin(GPIOA, 13, GPIO::AFR::AlternateFunction::AF0, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High), + AFGPIOPin(GPIOA, 14, GPIO::AFR::AlternateFunction::AF0, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High), + AFGPIOPin(GPIOB, 3, GPIO::AFR::AlternateFunction::AF0, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::High), +}; + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/timing.h b/ion/src/device/bootloader/drivers/config/timing.h new file mode 100644 index 000000000..fa4502000 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/timing.h @@ -0,0 +1,19 @@ +#ifndef ION_DEVICE_N0110_CONFIG_TIMING_H +#define ION_DEVICE_N0110_CONFIG_TIMING_H + +#include + +namespace Ion { +namespace Device { +namespace Timing { +namespace Config { + +constexpr static int LoopsPerMillisecond = 4811; +constexpr static int LoopsPerMicrosecond = 38; + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/config/usb.h b/ion/src/device/bootloader/drivers/config/usb.h new file mode 100644 index 000000000..0c63ed474 --- /dev/null +++ b/ion/src/device/bootloader/drivers/config/usb.h @@ -0,0 +1,31 @@ +#ifndef ION_DEVICE_N0110_CONFIG_USB_H +#define ION_DEVICE_N0110_CONFIG_USB_H + +#include + +namespace Ion { +namespace Device { +namespace USB { +namespace Config { + +using namespace Regs; + +/* On the STM32F730, PA9 does not actually support alternate function 10. + * However, because of the wiring of the USB connector on old N0110, detection + * of when the device is plugged required the use of this undocumented setting. + * After the revision of the USB connector and ESD protection, we can now + * follow the specification and configure the Vbus pin as a floating-input GPIO. + */ +constexpr static AFGPIOPin VbusPin = AFGPIOPin(GPIOA, 9, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast); + +constexpr static AFGPIOPin DmPin = AFGPIOPin(GPIOA, 11, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast); +constexpr static AFGPIOPin DpPin = AFGPIOPin(GPIOA, 12, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast); + +constexpr static const char * InterfaceStringDescriptor = "@Flash/0x08000000/04*016Kg/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg"; + +} +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/external_flash.cpp b/ion/src/device/bootloader/drivers/external_flash.cpp new file mode 100644 index 000000000..34de8b591 --- /dev/null +++ b/ion/src/device/bootloader/drivers/external_flash.cpp @@ -0,0 +1,520 @@ +#include +#include +#include +#include +#include + +namespace Ion { +namespace Device { +namespace ExternalFlash { + +using namespace Regs; + +/* The external flash and the Quad-SPI peripheral support several operating + * modes, corresponding to different numbers of signals used to communicate + * during each phase of the command sequence. + * + * Mode name for | Number of signals used during each phase: + * external flash | Instruction | Address | Alt. bytes | Data + * ----------------+-------------+---------+------------+------ + * Standard SPI | 1 | 1 | 1 | 1 + * Dual-Output SPI | 1 | 1 | 1 | 2 + * Dual-I/O SPI | 1 | 2 | 2 | 2 + * Quad-Output SPI | 1 | 1 | 1 | 4 + * Quad-I/O SPI | 1 | 4 | 4 | 4 + * QPI | 4 | 4 | 4 | 4 + * + * The external flash supports clock frequencies up to 104MHz for all + * instructions, except for Read Data (0x03) which is supported up to 50Mhz. + * + * + * Quad-SPI block diagram + * + * +----------------------+ +------------+ + * | Quad-SPI | | | + * | peripheral | | External | + * | | read | flash | + * AHB <-- | data <-- 32-byte | <-- | memory | + * matrix --> | register --> FIFO | --> | | + * +----------------------+ write +------------+ + * + * Any data transmitted to or from the external flash memory go through a + * 32-byte FIFO. + * + * Read or write operations are performed in burst mode, that is, after any data + * byte is transmitted between the Quad-SPI and the flash memory, the latter + * automatically increments the specified address and the next byte to read or + * write is respectively pushed in or popped from the FIFO. + * And so on, as long as the clock continues. + * + * If the FIFO gets full in a read operation or + * if the FIFO gets empty in a write operation, + * the operation stalls and CLK stays low until firmware services the FIFO. + * + * If the FIFO gets full in a write operation, the operation is stalled until + * the FIFO has enough space to accept the amount of data being written. + * If the FIFO does not have as many bytes as requested by the read operation + * and if BUSY=1, the operation is stalled until enough data is present or until + * the transfer is complete, whichever happens first. */ + +enum class Command : uint8_t { + WriteStatusRegister = 0x01, + PageProgram = 0x02, // Program previously erased memory areas as being "0" + ReadData = 0x03, + ReadStatusRegister1 = 0x05, + WriteEnable = 0x06, + Erase4KbyteBlock = 0x20, + WriteStatusRegister2 = 0x31, + QuadPageProgramW25Q64JV = 0x32, + QuadPageProgramAT25F641 = 0x33, + ReadStatusRegister2 = 0x35, + Erase32KbyteBlock = 0x52, + EnableReset = 0x66, + Reset = 0x99, + ReadJEDECID = 0x9F, + ReleaseDeepPowerDown = 0xAB, + DeepPowerDown = 0xB9, + ChipErase = 0xC7, // Erase the whole chip or a 64-Kbyte block as being "1" + Erase64KbyteBlock = 0xD8, + FastReadQuadIO = 0xEB +}; + +static constexpr uint8_t NumberOfAddressBitsIn64KbyteBlock = 16; +static constexpr uint8_t NumberOfAddressBitsIn32KbyteBlock = 15; +static constexpr uint8_t NumberOfAddressBitsIn4KbyteBlock = 12; + +class ExternalFlashStatusRegister { +public: + class StatusRegister1 : public Register8 { + public: + using Register8::Register8; + REGS_BOOL_FIELD_R(BUSY, 0); + }; + class StatusRegister2 : public Register8 { + public: + using Register8::Register8; + REGS_BOOL_FIELD(QE, 1); + }; +}; + +class OperatingModes { +public: + constexpr OperatingModes( + QUADSPI::CCR::OperatingMode instruction, + QUADSPI::CCR::OperatingMode address, + QUADSPI::CCR::OperatingMode data) : + m_instructionOperatingMode(instruction), + m_addressOperatingMode(address), + m_dataOperatingMode(data) + {} + QUADSPI::CCR::OperatingMode instructionOperatingMode() const { return m_instructionOperatingMode; } + QUADSPI::CCR::OperatingMode addressOperatingMode() const { return m_addressOperatingMode; } + QUADSPI::CCR::OperatingMode dataOperatingMode() const { return m_dataOperatingMode; } +private: + QUADSPI::CCR::OperatingMode m_instructionOperatingMode; + QUADSPI::CCR::OperatingMode m_addressOperatingMode; + QUADSPI::CCR::OperatingMode m_dataOperatingMode; +}; + +/* W25Q64JV does not implement QPI-4-4-4, so we always send the instructions on + * one wire only.*/ +static constexpr OperatingModes sOperatingModes100(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData, QUADSPI::CCR::OperatingMode::NoData); +static constexpr OperatingModes sOperatingModes101(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData, QUADSPI::CCR::OperatingMode::Single); +static constexpr OperatingModes sOperatingModes110(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData); +static constexpr OperatingModes sOperatingModes111(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single); +static constexpr OperatingModes sOperatingModes114(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad); +static constexpr OperatingModes sOperatingModes144(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad, QUADSPI::CCR::OperatingMode::Quad); + +static QUADSPI::CCR::OperatingMode sOperatingMode = QUADSPI::CCR::OperatingMode::Single; + +static constexpr int ClockFrequencyDivisor = 2; // F(QUADSPI) = F(AHB) / ClockFrequencyDivisor +static constexpr int FastReadQuadIODummyCycles = 4; // Must be 4 for W25Q64JV (Fig 24.A page 34) and for AT25F641 (table 7.19 page 28) +/* According to datasheets, the CS signal should stay high (deselect the device) + * for t_SHSL = 50ns at least. + * -> Max of 30ns (see AT25F641 Sections 8.7 and 8.8), + * 10ns and 50ns (see W25Q64JV Section 9.6). */ +static constexpr float ChipSelectHighTimeInNanoSeconds = 50.0f; + +static void send_command_full( + QUADSPI::CCR::FunctionalMode functionalMode, + OperatingModes operatingModes, + Command c, + uint8_t * address, + uint32_t altBytes, + size_t numberOfAltBytes, + uint8_t dummyCycles, + uint8_t * data, + size_t dataLength); + +static inline void send_command(Command c) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectWrite, + sOperatingModes100, + c, + reinterpret_cast(FlashAddressSpaceSize), + 0, 0, + 0, + nullptr, 0); +} + +static inline void send_write_command(Command c, uint8_t * address, const uint8_t * data, size_t dataLength, OperatingModes operatingModes) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectWrite, + operatingModes, + c, + address, + 0, 0, + 0, + const_cast(data), dataLength); +} + +static inline void send_read_command(Command c, uint8_t * address, uint8_t * data, size_t dataLength) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectRead, + sOperatingModes101, + c, + address, + 0, 0, + 0, + data, dataLength); +} + +static inline void wait() { + /* The DSB instruction guarantees the completion of a write operation before + * polling the status register. */ + Cache::dsb(); + ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0); + do { + send_read_command(Command::ReadStatusRegister1, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&statusRegister1), sizeof(statusRegister1)); + } while (statusRegister1.getBUSY()); +} + +static void set_as_memory_mapped() { + /* In memory-mapped mode, all AHB masters may access the external flash memory as an internal one: + * the programmed instruction is sent automatically whenever an AHB master reads in the Quad-SPI flash bank area. + * (The QUADSPI_DLR register has no meaning and any access to QUADSPI_DR returns zero.) + * + * To anticipate sequential reads, the nCS signal is maintained low so as to + * keep the read operation active and prefetch the subsequent bytes in the FIFO. + * + * It goes low, only if the low-power timeout counter is enabled. + * (Flash memories tend to consume more when nCS is held low.) */ + send_command_full( + QUADSPI::CCR::FunctionalMode::MemoryMapped, + sOperatingModes144, + Command::FastReadQuadIO, + reinterpret_cast(FlashAddressSpaceSize), + 0xA0, 1, + FastReadQuadIODummyCycles, + nullptr, 0 + ); +} + +static void unset_memory_mapped_mode() { + /* Reset Continuous Read Mode Bits before issuing normal instructions. */ + uint8_t dummyData; + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectRead, + sOperatingModes144, + Command::FastReadQuadIO, + 0, + ~(0xA0), 1, + FastReadQuadIODummyCycles, + &dummyData, 1 + ); +} + +static void send_command_full(QUADSPI::CCR::FunctionalMode functionalMode, OperatingModes operatingModes, Command c, uint8_t * address, uint32_t altBytes, size_t numberOfAltBytes, uint8_t dummyCycles, uint8_t * data, size_t dataLength) { + /* According to ST's Errata Sheet ES0360, "Wrong data can be read in + * memory-mapped after an indirect mode operation". This is the workaround. */ + if (functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + QUADSPI::CCR::FunctionalMode previousMode = QUADSPI.CCR()->getFMODE(); + if (previousMode == QUADSPI::CCR::FunctionalMode::IndirectWrite || previousMode == QUADSPI::CCR::FunctionalMode::IndirectRead) { + // Reset the address register + QUADSPI.AR()->set(0); // No write to DR should be done after this + if (previousMode == QUADSPI::CCR::FunctionalMode::IndirectRead) { + // Make an abort request to stop the reading and clear the busy bit + QUADSPI.CR()->setABORT(true); + while (QUADSPI.CR()->getABORT()) { + } + } + } + } else if (QUADSPI.CCR()->getFMODE() == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + /* "BUSY goes high as soon as the first memory-mapped access occurs. Because + * of the prefetch operations, BUSY does not fall until there is a timeout, + * there is an abort, or the peripheral is disabled". (From the Reference + * Manual) + * If we are leaving memory-mapped mode, we send an abort to clear BUSY. */ + QUADSPI.CR()->setABORT(true); + while (QUADSPI.CR()->getABORT()) { + } + } + + assert(QUADSPI.CCR()->getFMODE() != QUADSPI::CCR::FunctionalMode::MemoryMapped || QUADSPI.SR()->getBUSY() == 0); + + class QUADSPI::CCR ccr(0); + ccr.setFMODE(functionalMode); + if (data != nullptr || functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + ccr.setDMODE(operatingModes.dataOperatingMode()); + } + if (functionalMode != QUADSPI::CCR::FunctionalMode::MemoryMapped) { + QUADSPI.DLR()->set((dataLength > 0) ? dataLength-1 : 0); + } + ccr.setDCYC(dummyCycles); + if (numberOfAltBytes > 0) { + ccr.setABMODE(operatingModes.addressOperatingMode()); // Seems to always be the same as address mode + ccr.setABSIZE(static_cast(numberOfAltBytes - 1)); + QUADSPI.ABR()->set(altBytes); + } + if (address != reinterpret_cast(FlashAddressSpaceSize) || functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + ccr.setADMODE(operatingModes.addressOperatingMode()); + ccr.setADSIZE(QUADSPI::CCR::Size::ThreeBytes); + } + ccr.setIMODE(operatingModes.instructionOperatingMode()); + ccr.setINSTRUCTION(static_cast(c)); + if (functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + ccr.setSIOO(true); + /* If the SIOO bit is set, the instruction is sent only for the first command following a write to QUADSPI_CCR. + * Subsequent command sequences skip the instruction phase, until there is a write to QUADSPI_CCR. */ + } + QUADSPI.CCR()->set(ccr); + if (address != reinterpret_cast(FlashAddressSpaceSize)) { + QUADSPI.AR()->set(reinterpret_cast(address)); + } + + if (functionalMode == QUADSPI::CCR::FunctionalMode::IndirectWrite) { + for (size_t i=0; iset(data[i]); + } + } else if (functionalMode == QUADSPI::CCR::FunctionalMode::IndirectRead) { + for (size_t i=0; iget(); + } + } + + /* Wait for the command to be sent. + * "When configured in memory-mapped mode, because of the prefetch operations, + * BUSY does not fall until there is a timeout, there is an abort, or the + * peripheral is disabled.", so we do not wait if the device is in + * memory-mapped mode. */ + if (functionalMode != QUADSPI::CCR::FunctionalMode::MemoryMapped) { + while (QUADSPI.SR()->getBUSY()) { + } + } +} + +static void initGPIO() { + for(const AFGPIOPin & p : Config::Pins) { + p.init(); + } +} + +static void initQSPI() { + // Enable QUADSPI AHB3 peripheral clock + RCC.AHB3ENR()->setQSPIEN(true); + + // Configure controller for target device + class QUADSPI::DCR dcr(0); + dcr.setFSIZE(NumberOfAddressBitsInChip - 1); + constexpr int ChipSelectHighTimeCycles = (ChipSelectHighTimeInNanoSeconds * static_cast(Clocks::Config::AHBFrequency)) / (static_cast(ClockFrequencyDivisor) * 1000.0f) + 1.0f; + dcr.setCSHT(ChipSelectHighTimeCycles - 1); + dcr.setCKMODE(true); + QUADSPI.DCR()->set(dcr); + class QUADSPI::CR cr(0); + cr.setPRESCALER(ClockFrequencyDivisor - 1); + cr.setEN(true); + QUADSPI.CR()->set(cr); +} + +static void initChip() { + // Release sleep deep + send_command(Command::ReleaseDeepPowerDown); + Timing::usleep(3); + + /* The chip initially expects commands in SPI mode. We need to use SPI to tell + * it to switch to QuadSPI/QPI. */ + if (sOperatingMode == QUADSPI::CCR::OperatingMode::Single) { + send_command(Command::WriteEnable); + ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0); + statusRegister2.setQE(true); + wait(); + send_write_command(Command::WriteStatusRegister2, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&statusRegister2), sizeof(statusRegister2), sOperatingModes101); + wait(); + sOperatingMode = QUADSPI::CCR::OperatingMode::Quad; + } + set_as_memory_mapped(); +} + +void init() { + if (Config::NumberOfSectors == 0) { + return; + } + initGPIO(); + initQSPI(); + initChip(); +} + +static void shutdownGPIO() { + for(const AFGPIOPin & p : Config::Pins) { + p.group().OSPEEDR()->setOutputSpeed(p.pin(), GPIO::OSPEEDR::OutputSpeed::Low); + p.group().MODER()->setMode(p.pin(), GPIO::MODER::Mode::Analog); + p.group().PUPDR()->setPull(p.pin(), GPIO::PUPDR::Pull::None); + } +} + +static void shutdownChip() { + unset_memory_mapped_mode(); + // Reset + send_command(Command::EnableReset); + send_command(Command::Reset); + sOperatingMode = QUADSPI::CCR::OperatingMode::Single; + Timing::usleep(30); + + // Sleep deep + send_command(Command::DeepPowerDown); + Timing::usleep(3); +} + +static void shutdownQSPI() { + // Reset the controller + RCC.AHB3RSTR()->setQSPIRST(true); + RCC.AHB3RSTR()->setQSPIRST(false); + + RCC.AHB3ENR()->setQSPIEN(false); // TODO: move in Device::shutdownClocks +} + +void shutdown() { + if (Config::NumberOfSectors == 0) { + return; + } + shutdownChip(); + shutdownQSPI(); + shutdownGPIO(); +} + +int SectorAtAddress(uint32_t address) { + /* WARNING: this code assumes that the flash sectors are of increasing size: + * first all 4K sectors, then all 32K sectors, and finally all 64K sectors. */ + int i = address >> NumberOfAddressBitsIn64KbyteBlock; + if (i > Config::NumberOf64KSectors) { + return -1; + } + if (i >= 1) { + return Config::NumberOf4KSectors + Config::NumberOf32KSectors + i - 1; + } + i = address >> NumberOfAddressBitsIn32KbyteBlock; + if (i >= 1) { + i = Config::NumberOf4KSectors + i - 1; + assert(i >= 0 && i <= Config::NumberOf32KSectors); + return i; + } + i = address >> NumberOfAddressBitsIn4KbyteBlock; + assert(i <= Config::NumberOf4KSectors); + return i; +} + +void unlockFlash() { + // Warning: unset_memory_mapped_mode must be called before + send_command(Command::WriteEnable); + wait(); + ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0); + ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0); + ExternalFlashStatusRegister::StatusRegister2 currentStatusRegister2(0); + send_read_command(Command::ReadStatusRegister2, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(¤tStatusRegister2), sizeof(currentStatusRegister2)); + statusRegister2.setQE(currentStatusRegister2.getQE()); + + uint8_t registers[] = {statusRegister1.get(), statusRegister2.get()}; + send_write_command(Command::WriteStatusRegister, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(registers), sizeof(registers), sOperatingModes101); + wait(); +} + +void MassErase() { + if (Config::NumberOfSectors == 0) { + return; + } + unset_memory_mapped_mode(); + unlockFlash(); + send_command(Command::WriteEnable); + wait(); + send_command(Command::ChipErase); + wait(); + set_as_memory_mapped(); +} + +void __attribute__((noinline)) EraseSector(int i) { + assert(i >= 0 && i < Config::NumberOfSectors); + unset_memory_mapped_mode(); + unlockFlash(); + send_command(Command::WriteEnable); + wait(); + /* WARNING: this code assumes that the flash sectors are of increasing size: + * first all 4K sectors, then all 32K sectors, and finally all 64K sectors. */ + if (i < Config::NumberOf4KSectors) { + send_write_command(Command::Erase4KbyteBlock, reinterpret_cast(i << NumberOfAddressBitsIn4KbyteBlock), nullptr, 0, sOperatingModes110); + } else if (i < Config::NumberOf4KSectors + Config::NumberOf32KSectors) { + /* If the sector is the number Config::NumberOf4KSectors, we want to write + * at the address 1 << NumberOfAddressBitsIn32KbyteBlock, hence the formula + * (i - Config::NumberOf4KSectors + 1). */ + send_write_command(Command::Erase32KbyteBlock, reinterpret_cast((i - Config::NumberOf4KSectors + 1) << NumberOfAddressBitsIn32KbyteBlock), nullptr, 0, sOperatingModes110); + } else { + /* If the sector is the number + * Config::NumberOf4KSectors - Config::NumberOf32KSectors, we want to write + * at the address 1 << NumberOfAddressBitsIn32KbyteBlock, hence the formula + * (i - Config::NumberOf4KSectors - Config::NumberOf32KSectors + 1). */ + send_write_command(Command::Erase64KbyteBlock, reinterpret_cast((i - Config::NumberOf4KSectors - Config::NumberOf32KSectors + 1) << NumberOfAddressBitsIn64KbyteBlock), nullptr, 0, sOperatingModes110); + } + wait(); + set_as_memory_mapped(); +} + +void __attribute__((noinline)) WriteMemory(uint8_t * destination, const uint8_t * source, size_t length) { + if (Config::NumberOfSectors == 0) { + return; + } + unset_memory_mapped_mode(); + /* Each 256-byte page of the external flash memory (contained in a previously erased area) + * may be programmed in burst mode with a single Page Program instruction. + * However, when the end of a page is reached, the addressing wraps to the beginning. + * Hence a Page Program instruction must be issued for each page. */ + static constexpr size_t PageSize = 256; + uint8_t offset = reinterpret_cast(destination) & (PageSize - 1); + size_t lengthThatFitsInPage = PageSize - offset; + while (length > 0) { + if (lengthThatFitsInPage > length) { + lengthThatFitsInPage = length; + } + send_command(Command::WriteEnable); + wait(); + + /* Some chips implement 0x32 only, others 0x33 only, we call both. This does + * not seem to affect the writing. */ + send_write_command(Command::QuadPageProgramAT25F641, destination, source, lengthThatFitsInPage, sOperatingModes144); + send_write_command(Command::QuadPageProgramW25Q64JV, destination, source, lengthThatFitsInPage, sOperatingModes114); + + length -= lengthThatFitsInPage; + destination += lengthThatFitsInPage; + source += lengthThatFitsInPage; + lengthThatFitsInPage = PageSize; + wait(); + } + set_as_memory_mapped(); +} + +void JDECid(uint8_t * manufacturerID, uint8_t * memoryType, uint8_t * capacityType) { + unset_memory_mapped_mode(); + struct JEDECId { + uint8_t manufacturerID; + uint8_t memoryType; + uint8_t capacityType; + }; + JEDECId id; + send_read_command(Command::ReadJEDECID, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&id), sizeof(id)); + *manufacturerID = id.manufacturerID; + *memoryType = id.memoryType; + *capacityType = id.capacityType; + set_as_memory_mapped(); +} + +} +} +} diff --git a/ion/src/device/bootloader/drivers/led.cpp b/ion/src/device/bootloader/drivers/led.cpp new file mode 100644 index 000000000..676464827 --- /dev/null +++ b/ion/src/device/bootloader/drivers/led.cpp @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +namespace Ion { +namespace LED { + +KDColor updateColorWithPlugAndCharge() { + KDColor ledColor = getColor(); + if (ExamMode::FetchExamMode() == 0) { // If exam mode is on, we do not update the LED with the plugged/charging state + if (USB::isPlugged()) { + ledColor = Battery::isCharging() ? KDColorOrange : KDColorGreen; + } else { + ledColor = KDColorBlack; + } + setColor(ledColor); + } + return ledColor; +} + +} +} diff --git a/ion/src/device/bootloader/drivers/power.cpp b/ion/src/device/bootloader/drivers/power.cpp new file mode 100644 index 000000000..f8f657dea --- /dev/null +++ b/ion/src/device/bootloader/drivers/power.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +namespace Ion { +namespace Power { + +/* We isolate the standby code that needs to be executed from the internal + * flash (because the external flash is then shut down). We forbid inlining to + * avoid inlining these instructions in the external flash. */ + +void standby() { + Device::Power::waitUntilOnOffKeyReleased(); + Device::Power::standbyConfiguration(); + Device::Board::shutdownPeripherals(); + Device::Power::internalFlashStandby(); +} + +} +} + +namespace Ion { +namespace Device { +namespace Power { + +void configWakeUp() { + Device::WakeUp::onOnOffKeyDown(); + Device::WakeUp::onUSBPlugging(); + Device::WakeUp::onChargingEvent(); +} + +// Public Power methods +using namespace Device::Regs; + +void standbyConfiguration() { + PWR.CR()->setPPDS(true); // Select standby when the CPU enters deepsleep + PWR.CR()->setCSBF(true); // Clear Standby flag + PWR.CSR()->setBRE(false); // Unable back up RAM (lower power consumption in standby) + PWR.CSR()->setEIWUP(false); // Unable RTC (lower power consumption in standby) + + /* The pin A0 is about to be configured as a wakeup pin. However, the matrix + * keyboard connects pin A0 (row B) with other pins (column 1, column 3...). + * We thus shutdown this pins to avoid the potential pull-up on pin A0 due to + * a keyboard event. For example, if the "Home" key is down, pin A0 is + * pulled-up so enabling it as the wake up pin would trigger a wake up flag + * instantly. */ + Device::Keyboard::shutdown(); +#if REGS_PWR_CONFIG_ADDITIONAL_FIELDS + PWR.CSR2()->setEWUP1(true); // Enable PA0 as wakeup pin + PWR.CR2()->setWUPP1(false); // Define PA0 (wakeup) pin polarity (rising edge) + PWR.CR2()->setCWUPF1(true); // Clear wakeup pin flag for PA0 (if device has already been in standby and woke up) +#endif + + CORTEX.SCR()->setSLEEPDEEP(true); // Allow Cortex-M7 deepsleep state +} + +} +} +} diff --git a/ion/src/device/bootloader/drivers/power.h b/ion/src/device/bootloader/drivers/power.h new file mode 100644 index 000000000..a3d142bc1 --- /dev/null +++ b/ion/src/device/bootloader/drivers/power.h @@ -0,0 +1,16 @@ +#ifndef ION_DEVICE_N0110_POWER_H +#define ION_DEVICE_N0110_POWER_H + +#include + +namespace Ion { +namespace Device { +namespace Power { + +void standbyConfiguration(); + +} +} +} + +#endif diff --git a/ion/src/device/bootloader/drivers/reset.cpp b/ion/src/device/bootloader/drivers/reset.cpp new file mode 100644 index 000000000..f8c30d7e6 --- /dev/null +++ b/ion/src/device/bootloader/drivers/reset.cpp @@ -0,0 +1,13 @@ +#include + +namespace Ion { +namespace Device { +namespace Reset { + +void coreWhilePlugged() { + core(); +} + +} +} +} diff --git a/ion/src/device/bootloader/drivers/usb.cpp b/ion/src/device/bootloader/drivers/usb.cpp new file mode 100644 index 000000000..d56cee475 --- /dev/null +++ b/ion/src/device/bootloader/drivers/usb.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +namespace Ion { +namespace Device { + +using namespace Regs; + +namespace USB { + +bool useAlternateFunctionVbus() { + return Board::pcbVersion() == 0; +} + +void initVbus() { + if (useAlternateFunctionVbus()) { + Config::VbusPin.init(); + } else { + Config::VbusPin.group().MODER()->setMode(Config::VbusPin.pin(), GPIO::MODER::Mode::Input); + Config::VbusPin.group().PUPDR()->setPull(Config::VbusPin.pin(), GPIO::PUPDR::Pull::None); + } +} + +} +} +} diff --git a/ion/src/device/bootloader/platform_info.cpp b/ion/src/device/bootloader/platform_info.cpp new file mode 100644 index 000000000..4ca5052cc --- /dev/null +++ b/ion/src/device/bootloader/platform_info.cpp @@ -0,0 +1,74 @@ +#include +#include + +#ifndef PATCH_LEVEL +#error This file expects PATCH_LEVEL to be defined +#endif + +#ifndef EPSILON_VERSION +#error This file expects EPSILON_VERSION to be defined +#endif + +#ifndef OMEGA_VERSION +#error This file expects OMEGA_VERSION to be defined +#endif + +#ifndef HEADER_SECTION +#define HEADER_SECTION +#endif + +namespace Ion { +extern char staticStorageArea[]; +} +constexpr void * storageAddress = &(Ion::staticStorageArea); + +class PlatformInfo { +public: + constexpr PlatformInfo() : + m_header(Magic), + m_version{EPSILON_VERSION}, + m_patchLevel{PATCH_LEVEL}, + m_footer(Magic) { } + const char * version() const { + assert(m_header == Magic); + assert(m_footer == Magic); + return m_version; + } + const char * patchLevel() const { + assert(m_header == Magic); + assert(m_footer == Magic); + return m_patchLevel; + } +private: + constexpr static uint32_t Magic = 0xDEC00DF0; + constexpr static uint32_t OmegaMagic = 0xEFBEADDE; + uint32_t m_header; + const char m_version[8]; + const char m_patchLevel[8]; + uint32_t m_footer; +}; + +const PlatformInfo HEADER_SECTION platform_infos; + +const char * Ion::softwareVersion() { + return platform_infos.version(); +} + +static const char s_omegaVersion[16] = {OMEGA_VERSION}; +#ifdef OMEGA_USERNAME + static const char s_username[16] = {OMEGA_USERNAME}; +#else + static const char s_username[16] = {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}; +#endif + +const char * Ion::omegaVersion() { + return s_omegaVersion; +} + +const volatile char * Ion::username() { + return s_username; +} + +const char * Ion::patchLevel() { + return platform_infos.patchLevel(); +} diff --git a/ion/src/device/bootloader/regs/config/cortex.h b/ion/src/device/bootloader/regs/config/cortex.h new file mode 100644 index 000000000..faee2a9ce --- /dev/null +++ b/ion/src/device/bootloader/regs/config/cortex.h @@ -0,0 +1,6 @@ +#ifndef ION_DEVICE_N0110_REGS_CONFIG_CORTEX_H +#define ION_DEVICE_N0110_REGS_CONFIG_CORTEX_H + +#define REGS_CORTEX_CONFIG_CACHE 1 + +#endif diff --git a/ion/src/device/bootloader/regs/config/crc.h b/ion/src/device/bootloader/regs/config/crc.h new file mode 100644 index 000000000..faa0a263b --- /dev/null +++ b/ion/src/device/bootloader/regs/config/crc.h @@ -0,0 +1,6 @@ +#ifndef ION_DEVICE_N0110_REGS_CONFIG_CRC_H +#define ION_DEVICE_N0110_REGS_CONFIG_CRC_H + +#define REGS_CRC_CONFIG_BYTE_ACCESS 1 + +#endif diff --git a/ion/src/device/bootloader/regs/config/flash.h b/ion/src/device/bootloader/regs/config/flash.h new file mode 100644 index 000000000..770fcc37a --- /dev/null +++ b/ion/src/device/bootloader/regs/config/flash.h @@ -0,0 +1,6 @@ +#ifndef ION_DEVICE_N0110_REGS_CONFIG_FLASH_H +#define ION_DEVICE_N0110_REGS_CONFIG_FLASH_H + +#define REGS_FLASH_CONFIG_ART 1 + +#endif diff --git a/ion/src/device/bootloader/regs/config/pwr.h b/ion/src/device/bootloader/regs/config/pwr.h new file mode 100644 index 000000000..85f7c0e9d --- /dev/null +++ b/ion/src/device/bootloader/regs/config/pwr.h @@ -0,0 +1,6 @@ +#ifndef ION_DEVICE_N0110_REGS_CONFIG_PWR_H +#define ION_DEVICE_N0110_REGS_CONFIG_PWR_H + +#define REGS_PWR_CONFIG_ADDITIONAL_FIELDS 1 + +#endif diff --git a/ion/src/device/bootloader/regs/config/rcc.h b/ion/src/device/bootloader/regs/config/rcc.h new file mode 100644 index 000000000..54db5f211 --- /dev/null +++ b/ion/src/device/bootloader/regs/config/rcc.h @@ -0,0 +1,7 @@ +#ifndef ION_DEVICE_N0110_REGS_CONFIG_RCC_H +#define ION_DEVICE_N0110_REGS_CONFIG_RCC_H + +#define REGS_RCC_CONFIG_F730 1 +#define REGS_RCC_CONFIG_F412 0 + +#endif diff --git a/ion/src/device/bootloader/regs/config/syscfg.h b/ion/src/device/bootloader/regs/config/syscfg.h new file mode 100644 index 000000000..1165be7c9 --- /dev/null +++ b/ion/src/device/bootloader/regs/config/syscfg.h @@ -0,0 +1,6 @@ +#ifndef ION_DEVICE_N0110_REGS_CONFIG_SYSCFG_H +#define ION_DEVICE_N0110_REGS_CONFIG_SYSCFG_H + +#define REGS_SYSCFG_CONFIG_F412 0 + +#endif diff --git a/ion/src/device/bootloader/regs/config/usart.h b/ion/src/device/bootloader/regs/config/usart.h new file mode 100644 index 000000000..5de196af2 --- /dev/null +++ b/ion/src/device/bootloader/regs/config/usart.h @@ -0,0 +1,12 @@ +#ifndef ION_DEVICE_N0110_REGS_CONFIG_USART_H +#define ION_DEVICE_N0110_REGS_CONFIG_USART_H + +#define REGS_USART_SR_OFFSET 0x1C +#define REGS_USART_RDR_OFFSET 0x24 +#define REGS_USART_TDR_OFFSET 0x28 +#define REGS_USART_BRR_OFFSET 0x0C +#define REGS_USART_CR1_OFFSET 0x00 + +#define REGS_USART_CR1_UE_BIT 0 + +#endif diff --git a/ion/src/device/n0100/Makefile b/ion/src/device/n0100/Makefile index ccacc84ab..83d810c10 100644 --- a/ion/src/device/n0100/Makefile +++ b/ion/src/device/n0100/Makefile @@ -7,4 +7,12 @@ ion_device_src += $(addprefix ion/src/device/n0100/drivers/, \ usb.cpp \ ) +ion_device_src += $(addprefix ion/src/device/n0100/boot/, \ + rt0.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0100/, \ + platform_info.cpp \ +) + LDSCRIPT ?= ion/src/device/n0100/flash.ld diff --git a/ion/src/device/shared/boot/rt0.cpp b/ion/src/device/n0100/boot/rt0.cpp similarity index 97% rename from ion/src/device/shared/boot/rt0.cpp rename to ion/src/device/n0100/boot/rt0.cpp index 4cb671496..454d41c7e 100644 --- a/ion/src/device/shared/boot/rt0.cpp +++ b/ion/src/device/n0100/boot/rt0.cpp @@ -1,11 +1,11 @@ -#include "isr.h" #include #include #include -#include "../drivers/board.h" -#include "../drivers/rtc.h" -#include "../drivers/reset.h" -#include "../drivers/timing.h" +#include +#include +#include +#include +#include typedef void (*cxx_constructor)(); diff --git a/ion/src/shared/platform_info.cpp b/ion/src/device/n0100/platform_info.cpp similarity index 100% rename from ion/src/shared/platform_info.cpp rename to ion/src/device/n0100/platform_info.cpp diff --git a/ion/src/device/n0110/Makefile b/ion/src/device/n0110/Makefile index 3e6297f6a..11c3b224f 100644 --- a/ion/src/device/n0110/Makefile +++ b/ion/src/device/n0110/Makefile @@ -8,4 +8,12 @@ ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \ usb.cpp \ ) +ion_device_src += $(addprefix ion/src/device/n0110/boot/, \ + rt0.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110/, \ + platform_info.cpp \ +) + LDSCRIPT ?= ion/src/device/n0110/flash.ld diff --git a/ion/src/device/n0110/boot/rt0.cpp b/ion/src/device/n0110/boot/rt0.cpp new file mode 100644 index 000000000..454d41c7e --- /dev/null +++ b/ion/src/device/n0110/boot/rt0.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*cxx_constructor)(); + +extern "C" { + extern char _data_section_start_flash; + extern char _data_section_start_ram; + extern char _data_section_end_ram; + extern char _bss_section_start_ram; + extern char _bss_section_end_ram; + extern cxx_constructor _init_array_start; + extern cxx_constructor _init_array_end; +} + +void __attribute__((noinline)) abort() { +#ifdef NDEBUG + Ion::Device::Reset::core(); +#else + while (1) { + } +#endif +} + +/* In order to ensure that this method is execute from the external flash, we + * forbid inlining it.*/ + +static void __attribute__((noinline)) external_flash_start() { + /* Init the peripherals. We do not initialize the backlight in case there is + * an on boarding app: indeed, we don't want the user to see the LCD tests + * happening during the on boarding app. The backlight will be initialized + * after the Power-On Self-Test if there is one or before switching to the + * home app otherwise. */ + Ion::Device::Board::initPeripherals(false); + + return ion_main(0, nullptr); +} + +/* This additional function call 'jump_to_external_flash' serves two purposes: + * - By default, the compiler is free to inline any function call he wants. If + * the compiler decides to inline some functions that make use of VFP + * registers, it will need to push VFP them onto the stack in calling + * function's prologue. + * Problem: in start()'s prologue, we would never had a chance to enable the + * FPU since this function is the first thing called after reset. + * We can safely assume that neither memcpy, memset, nor any Ion::Device::init* + * method will use floating-point numbers, but ion_main very well can. + * To make sure ion_main's potential usage of VFP registers doesn't bubble-up to + * start(), we isolate it in its very own non-inlined function call. + * - To avoid jumping on the external flash when it is shut down, we ensure + * there is no symbol references from the internal flash to the external + * flash except this jump. In order to do that, we isolate this + * jump in a symbol that we link in a special section separated from the + * internal flash section. We can than forbid cross references from the + * internal flash to the external flash. */ + +static void __attribute__((noinline)) jump_to_external_flash() { + external_flash_start(); +} + +/* When 'start' is executed, the external flash is supposed to be shutdown. We + * thus forbid inlining to prevent executing this code from external flash + * (just in case 'start' was to be called from the external flash). */ + +void __attribute__((noinline)) start() { + /* This is where execution starts after reset. + * Many things are not initialized yet so the code here has to pay attention. */ + + /* Copy data section to RAM + * The data section is R/W but its initialization value matters. It's stored + * in Flash, but linked as if it were in RAM. Now's our opportunity to copy + * it. Note that until then the data section (e.g. global variables) contains + * garbage values and should not be used. */ + size_t dataSectionLength = (&_data_section_end_ram - &_data_section_start_ram); + memcpy(&_data_section_start_ram, &_data_section_start_flash, dataSectionLength); + + /* Zero-out the bss section in RAM + * Until we do, any uninitialized global variable will be unusable. */ + size_t bssSectionLength = (&_bss_section_end_ram - &_bss_section_start_ram); + memset(&_bss_section_start_ram, 0, bssSectionLength); + + /* Initialize the FPU as early as possible. + * For example, static C++ objects are very likely to manipulate float values */ + Ion::Device::Board::initFPU(); + + /* Call static C++ object constructors + * The C++ compiler creates an initialization function for each static object. + * The linker then stores the address of each of those functions consecutively + * between _init_array_start and _init_array_end. So to initialize all C++ + * static objects we just have to iterate between theses two addresses and + * call the pointed function. */ +#define SUPPORT_CPP_GLOBAL_CONSTRUCTORS 0 +#if SUPPORT_CPP_GLOBAL_CONSTRUCTORS + for (cxx_constructor * c = &_init_array_start; c<&_init_array_end; c++) { + (*c)(); + } +#else + /* In practice, static initialized objects are a terrible idea. Since the init + * order is not specified, most often than not this yields the dreaded static + * init order fiasco. How about bypassing the issue altogether? */ + if (&_init_array_start != &_init_array_end) { + abort(); + } +#endif + + Ion::Device::Board::init(); + + /* At this point, we initialized clocks and the external flash but no other + * peripherals. */ + + jump_to_external_flash(); + + abort(); +} + +void __attribute__((interrupt, noinline)) isr_systick() { + auto t = Ion::Device::Timing::MillisElapsed; + t++; + Ion::Device::Timing::MillisElapsed = t; +} diff --git a/ion/src/device/n0110/platform_info.cpp b/ion/src/device/n0110/platform_info.cpp new file mode 100644 index 000000000..cdf676a0c --- /dev/null +++ b/ion/src/device/n0110/platform_info.cpp @@ -0,0 +1,109 @@ +#include +#include + +#ifndef PATCH_LEVEL +#error This file expects PATCH_LEVEL to be defined +#endif + +#ifndef EPSILON_VERSION +#error This file expects EPSILON_VERSION to be defined +#endif + +#ifndef OMEGA_VERSION +#error This file expects OMEGA_VERSION to be defined +#endif + +#ifndef HEADER_SECTION +#define HEADER_SECTION +#endif + +namespace Ion { +extern char staticStorageArea[]; +} +constexpr void * storageAddress = &(Ion::staticStorageArea); + +class PlatformInfo { +public: + constexpr PlatformInfo() : + m_header(Magic), + m_version{EPSILON_VERSION}, + m_patchLevel{PATCH_LEVEL}, + m_storageAddress(storageAddress), + m_storageSize(Ion::Storage::k_storageSize), + m_footer(Magic), + m_ohm_header(OmegaMagic), + m_omegaVersion{OMEGA_VERSION}, +#ifdef OMEGA_USERNAME + m_username{OMEGA_USERNAME}, +#else + m_username{"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, +#endif + m_ohm_footer(OmegaMagic) { } + const char * version() const { + assert(m_storageAddress != nullptr); + assert(m_storageSize != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_version; + } + const char * omegaVersion() const { + assert(m_storageAddress != nullptr); + assert(m_storageSize != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_omegaVersion; + } + const volatile char * username() const volatile { + assert(m_storageAddress != nullptr); + assert(m_storageSize != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_username; + } + const char * patchLevel() const { + assert(m_storageAddress != nullptr); + assert(m_storageSize != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_patchLevel; + } +private: + constexpr static uint32_t Magic = 0xDEC00DF0; + constexpr static uint32_t OmegaMagic = 0xEFBEADDE; + uint32_t m_header; + const char m_version[8]; + const char m_patchLevel[8]; + void * m_storageAddress; + size_t m_storageSize; + uint32_t m_footer; + uint32_t m_ohm_header; + const char m_omegaVersion[16]; + const volatile char m_username[16]; + uint32_t m_ohm_footer; +}; + +const PlatformInfo HEADER_SECTION platform_infos; + +const char * Ion::softwareVersion() { + return platform_infos.version(); +} + +const char * Ion::omegaVersion() { + return platform_infos.omegaVersion(); +} + +const volatile char * Ion::username() { + return platform_infos.username(); +} + +const char * Ion::patchLevel() { + return platform_infos.patchLevel(); +} diff --git a/ion/src/device/shared/boot/Makefile b/ion/src/device/shared/boot/Makefile index 2bb8d22d7..17dc4a4ad 100644 --- a/ion/src/device/shared/boot/Makefile +++ b/ion/src/device/shared/boot/Makefile @@ -1,4 +1,3 @@ ion_device_src += $(addprefix ion/src/device/shared/boot/, \ isr.c \ - rt0.cpp \ ) diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index f98981af2..caa9c1028 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -25,6 +25,7 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ keyboard.cpp \ layout.cpp \ main.cpp \ + platform_info.cpp \ random.cpp \ timing.cpp \ window.cpp \ diff --git a/ion/src/simulator/shared/platform_info.cpp b/ion/src/simulator/shared/platform_info.cpp new file mode 100644 index 000000000..cdf676a0c --- /dev/null +++ b/ion/src/simulator/shared/platform_info.cpp @@ -0,0 +1,109 @@ +#include +#include + +#ifndef PATCH_LEVEL +#error This file expects PATCH_LEVEL to be defined +#endif + +#ifndef EPSILON_VERSION +#error This file expects EPSILON_VERSION to be defined +#endif + +#ifndef OMEGA_VERSION +#error This file expects OMEGA_VERSION to be defined +#endif + +#ifndef HEADER_SECTION +#define HEADER_SECTION +#endif + +namespace Ion { +extern char staticStorageArea[]; +} +constexpr void * storageAddress = &(Ion::staticStorageArea); + +class PlatformInfo { +public: + constexpr PlatformInfo() : + m_header(Magic), + m_version{EPSILON_VERSION}, + m_patchLevel{PATCH_LEVEL}, + m_storageAddress(storageAddress), + m_storageSize(Ion::Storage::k_storageSize), + m_footer(Magic), + m_ohm_header(OmegaMagic), + m_omegaVersion{OMEGA_VERSION}, +#ifdef OMEGA_USERNAME + m_username{OMEGA_USERNAME}, +#else + m_username{"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, +#endif + m_ohm_footer(OmegaMagic) { } + const char * version() const { + assert(m_storageAddress != nullptr); + assert(m_storageSize != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_version; + } + const char * omegaVersion() const { + assert(m_storageAddress != nullptr); + assert(m_storageSize != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_omegaVersion; + } + const volatile char * username() const volatile { + assert(m_storageAddress != nullptr); + assert(m_storageSize != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_username; + } + const char * patchLevel() const { + assert(m_storageAddress != nullptr); + assert(m_storageSize != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_patchLevel; + } +private: + constexpr static uint32_t Magic = 0xDEC00DF0; + constexpr static uint32_t OmegaMagic = 0xEFBEADDE; + uint32_t m_header; + const char m_version[8]; + const char m_patchLevel[8]; + void * m_storageAddress; + size_t m_storageSize; + uint32_t m_footer; + uint32_t m_ohm_header; + const char m_omegaVersion[16]; + const volatile char m_username[16]; + uint32_t m_ohm_footer; +}; + +const PlatformInfo HEADER_SECTION platform_infos; + +const char * Ion::softwareVersion() { + return platform_infos.version(); +} + +const char * Ion::omegaVersion() { + return platform_infos.omegaVersion(); +} + +const volatile char * Ion::username() { + return platform_infos.username(); +} + +const char * Ion::patchLevel() { + return platform_infos.patchLevel(); +} From 2b5a77399307439cf0fcd5f9a6ad7596ce39512c Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Tue, 22 Feb 2022 23:06:08 +0300 Subject: [PATCH 04/37] [ion/bootloader] Made exam mode work --- ion/src/device/bootloader/Makefile | 12 +- .../drivers/external_flash_tramp.cpp | 466 ++++++++++++++++++ .../device/bootloader/drivers/trampoline.cpp | 14 + .../device/bootloader/drivers/trampoline.h | 22 + 4 files changed, 504 insertions(+), 10 deletions(-) create mode 100644 ion/src/device/bootloader/drivers/external_flash_tramp.cpp create mode 100644 ion/src/device/bootloader/drivers/trampoline.cpp create mode 100644 ion/src/device/bootloader/drivers/trampoline.h diff --git a/ion/src/device/bootloader/Makefile b/ion/src/device/bootloader/Makefile index 87c6b4e54..f614b6fba 100644 --- a/ion/src/device/bootloader/Makefile +++ b/ion/src/device/bootloader/Makefile @@ -1,20 +1,12 @@ -ion_device_src += $(addprefix ion/src/device/bootloader/drivers/, \ - board.cpp \ - cache.cpp \ - external_flash.cpp \ - led.cpp \ - power.cpp \ - reset.cpp \ - usb.cpp \ -) ion_device_src += $(addprefix ion/src/device/bootloader/drivers/, \ board.cpp \ cache.cpp \ - external_flash.cpp \ + external_flash_tramp.cpp \ led.cpp \ power.cpp \ reset.cpp \ + trampoline.cpp \ usb.cpp \ ) diff --git a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp new file mode 100644 index 000000000..f42cdbd5b --- /dev/null +++ b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp @@ -0,0 +1,466 @@ +#include +#include +#include +#include +#include +#include + +namespace Ion { +namespace Device { +namespace ExternalFlash { + +using namespace Regs; + +/* The external flash and the Quad-SPI peripheral support several operating + * modes, corresponding to different numbers of signals used to communicate + * during each phase of the command sequence. + * + * Mode name for | Number of signals used during each phase: + * external flash | Instruction | Address | Alt. bytes | Data + * ----------------+-------------+---------+------------+------ + * Standard SPI | 1 | 1 | 1 | 1 + * Dual-Output SPI | 1 | 1 | 1 | 2 + * Dual-I/O SPI | 1 | 2 | 2 | 2 + * Quad-Output SPI | 1 | 1 | 1 | 4 + * Quad-I/O SPI | 1 | 4 | 4 | 4 + * QPI | 4 | 4 | 4 | 4 + * + * The external flash supports clock frequencies up to 104MHz for all + * instructions, except for Read Data (0x03) which is supported up to 50Mhz. + * + * + * Quad-SPI block diagram + * + * +----------------------+ +------------+ + * | Quad-SPI | | | + * | peripheral | | External | + * | | read | flash | + * AHB <-- | data <-- 32-byte | <-- | memory | + * matrix --> | register --> FIFO | --> | | + * +----------------------+ write +------------+ + * + * Any data transmitted to or from the external flash memory go through a + * 32-byte FIFO. + * + * Read or write operations are performed in burst mode, that is, after any data + * byte is transmitted between the Quad-SPI and the flash memory, the latter + * automatically increments the specified address and the next byte to read or + * write is respectively pushed in or popped from the FIFO. + * And so on, as long as the clock continues. + * + * If the FIFO gets full in a read operation or + * if the FIFO gets empty in a write operation, + * the operation stalls and CLK stays low until firmware services the FIFO. + * + * If the FIFO gets full in a write operation, the operation is stalled until + * the FIFO has enough space to accept the amount of data being written. + * If the FIFO does not have as many bytes as requested by the read operation + * and if BUSY=1, the operation is stalled until enough data is present or until + * the transfer is complete, whichever happens first. */ + +enum class Command : uint8_t { + WriteStatusRegister = 0x01, + PageProgram = 0x02, // Program previously erased memory areas as being "0" + ReadData = 0x03, + ReadStatusRegister1 = 0x05, + WriteEnable = 0x06, + Erase4KbyteBlock = 0x20, + WriteStatusRegister2 = 0x31, + QuadPageProgramW25Q64JV = 0x32, + QuadPageProgramAT25F641 = 0x33, + ReadStatusRegister2 = 0x35, + Erase32KbyteBlock = 0x52, + EnableReset = 0x66, + Reset = 0x99, + ReadJEDECID = 0x9F, + ReleaseDeepPowerDown = 0xAB, + DeepPowerDown = 0xB9, + ChipErase = 0xC7, // Erase the whole chip or a 64-Kbyte block as being "1" + Erase64KbyteBlock = 0xD8, + FastReadQuadIO = 0xEB +}; + +static constexpr uint8_t NumberOfAddressBitsIn64KbyteBlock = 16; +static constexpr uint8_t NumberOfAddressBitsIn32KbyteBlock = 15; +static constexpr uint8_t NumberOfAddressBitsIn4KbyteBlock = 12; + +class ExternalFlashStatusRegister { +public: + class StatusRegister1 : public Register8 { + public: + using Register8::Register8; + REGS_BOOL_FIELD_R(BUSY, 0); + }; + class StatusRegister2 : public Register8 { + public: + using Register8::Register8; + REGS_BOOL_FIELD(QE, 1); + }; +}; + +class OperatingModes { +public: + constexpr OperatingModes( + QUADSPI::CCR::OperatingMode instruction, + QUADSPI::CCR::OperatingMode address, + QUADSPI::CCR::OperatingMode data) : + m_instructionOperatingMode(instruction), + m_addressOperatingMode(address), + m_dataOperatingMode(data) + {} + QUADSPI::CCR::OperatingMode instructionOperatingMode() const { return m_instructionOperatingMode; } + QUADSPI::CCR::OperatingMode addressOperatingMode() const { return m_addressOperatingMode; } + QUADSPI::CCR::OperatingMode dataOperatingMode() const { return m_dataOperatingMode; } +private: + QUADSPI::CCR::OperatingMode m_instructionOperatingMode; + QUADSPI::CCR::OperatingMode m_addressOperatingMode; + QUADSPI::CCR::OperatingMode m_dataOperatingMode; +}; + +/* W25Q64JV does not implement QPI-4-4-4, so we always send the instructions on + * one wire only.*/ +static constexpr OperatingModes sOperatingModes100(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData, QUADSPI::CCR::OperatingMode::NoData); +static constexpr OperatingModes sOperatingModes101(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData, QUADSPI::CCR::OperatingMode::Single); +static constexpr OperatingModes sOperatingModes110(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData); +static constexpr OperatingModes sOperatingModes111(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single); +static constexpr OperatingModes sOperatingModes114(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad); +static constexpr OperatingModes sOperatingModes144(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad, QUADSPI::CCR::OperatingMode::Quad); + +static QUADSPI::CCR::OperatingMode sOperatingMode = QUADSPI::CCR::OperatingMode::Single; + +static constexpr int ClockFrequencyDivisor = 2; // F(QUADSPI) = F(AHB) / ClockFrequencyDivisor +static constexpr int FastReadQuadIODummyCycles = 4; // Must be 4 for W25Q64JV (Fig 24.A page 34) and for AT25F641 (table 7.19 page 28) +/* According to datasheets, the CS signal should stay high (deselect the device) + * for t_SHSL = 50ns at least. + * -> Max of 30ns (see AT25F641 Sections 8.7 and 8.8), + * 10ns and 50ns (see W25Q64JV Section 9.6). */ +static constexpr float ChipSelectHighTimeInNanoSeconds = 50.0f; + +static void send_command_full( + QUADSPI::CCR::FunctionalMode functionalMode, + OperatingModes operatingModes, + Command c, + uint8_t * address, + uint32_t altBytes, + size_t numberOfAltBytes, + uint8_t dummyCycles, + uint8_t * data, + size_t dataLength); + +static inline void send_command(Command c) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectWrite, + sOperatingModes100, + c, + reinterpret_cast(FlashAddressSpaceSize), + 0, 0, + 0, + nullptr, 0); +} + +static inline void send_write_command(Command c, uint8_t * address, const uint8_t * data, size_t dataLength, OperatingModes operatingModes) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectWrite, + operatingModes, + c, + address, + 0, 0, + 0, + const_cast(data), dataLength); +} + +static inline void send_read_command(Command c, uint8_t * address, uint8_t * data, size_t dataLength) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectRead, + sOperatingModes101, + c, + address, + 0, 0, + 0, + data, dataLength); +} + +static inline void wait() { + /* The DSB instruction guarantees the completion of a write operation before + * polling the status register. */ + Cache::dsb(); + ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0); + do { + send_read_command(Command::ReadStatusRegister1, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&statusRegister1), sizeof(statusRegister1)); + } while (statusRegister1.getBUSY()); +} + +static void set_as_memory_mapped() { + /* In memory-mapped mode, all AHB masters may access the external flash memory as an internal one: + * the programmed instruction is sent automatically whenever an AHB master reads in the Quad-SPI flash bank area. + * (The QUADSPI_DLR register has no meaning and any access to QUADSPI_DR returns zero.) + * + * To anticipate sequential reads, the nCS signal is maintained low so as to + * keep the read operation active and prefetch the subsequent bytes in the FIFO. + * + * It goes low, only if the low-power timeout counter is enabled. + * (Flash memories tend to consume more when nCS is held low.) */ + send_command_full( + QUADSPI::CCR::FunctionalMode::MemoryMapped, + sOperatingModes144, + Command::FastReadQuadIO, + reinterpret_cast(FlashAddressSpaceSize), + 0xA0, 1, + FastReadQuadIODummyCycles, + nullptr, 0 + ); +} + +static void unset_memory_mapped_mode() { + /* Reset Continuous Read Mode Bits before issuing normal instructions. */ + uint8_t dummyData; + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectRead, + sOperatingModes144, + Command::FastReadQuadIO, + 0, + ~(0xA0), 1, + FastReadQuadIODummyCycles, + &dummyData, 1 + ); +} + +static void send_command_full(QUADSPI::CCR::FunctionalMode functionalMode, OperatingModes operatingModes, Command c, uint8_t * address, uint32_t altBytes, size_t numberOfAltBytes, uint8_t dummyCycles, uint8_t * data, size_t dataLength) { + /* According to ST's Errata Sheet ES0360, "Wrong data can be read in + * memory-mapped after an indirect mode operation". This is the workaround. */ + if (functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + QUADSPI::CCR::FunctionalMode previousMode = QUADSPI.CCR()->getFMODE(); + if (previousMode == QUADSPI::CCR::FunctionalMode::IndirectWrite || previousMode == QUADSPI::CCR::FunctionalMode::IndirectRead) { + // Reset the address register + QUADSPI.AR()->set(0); // No write to DR should be done after this + if (previousMode == QUADSPI::CCR::FunctionalMode::IndirectRead) { + // Make an abort request to stop the reading and clear the busy bit + QUADSPI.CR()->setABORT(true); + while (QUADSPI.CR()->getABORT()) { + } + } + } + } else if (QUADSPI.CCR()->getFMODE() == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + /* "BUSY goes high as soon as the first memory-mapped access occurs. Because + * of the prefetch operations, BUSY does not fall until there is a timeout, + * there is an abort, or the peripheral is disabled". (From the Reference + * Manual) + * If we are leaving memory-mapped mode, we send an abort to clear BUSY. */ + QUADSPI.CR()->setABORT(true); + while (QUADSPI.CR()->getABORT()) { + } + } + + assert(QUADSPI.CCR()->getFMODE() != QUADSPI::CCR::FunctionalMode::MemoryMapped || QUADSPI.SR()->getBUSY() == 0); + + class QUADSPI::CCR ccr(0); + ccr.setFMODE(functionalMode); + if (data != nullptr || functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + ccr.setDMODE(operatingModes.dataOperatingMode()); + } + if (functionalMode != QUADSPI::CCR::FunctionalMode::MemoryMapped) { + QUADSPI.DLR()->set((dataLength > 0) ? dataLength-1 : 0); + } + ccr.setDCYC(dummyCycles); + if (numberOfAltBytes > 0) { + ccr.setABMODE(operatingModes.addressOperatingMode()); // Seems to always be the same as address mode + ccr.setABSIZE(static_cast(numberOfAltBytes - 1)); + QUADSPI.ABR()->set(altBytes); + } + if (address != reinterpret_cast(FlashAddressSpaceSize) || functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + ccr.setADMODE(operatingModes.addressOperatingMode()); + ccr.setADSIZE(QUADSPI::CCR::Size::ThreeBytes); + } + ccr.setIMODE(operatingModes.instructionOperatingMode()); + ccr.setINSTRUCTION(static_cast(c)); + if (functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + ccr.setSIOO(true); + /* If the SIOO bit is set, the instruction is sent only for the first command following a write to QUADSPI_CCR. + * Subsequent command sequences skip the instruction phase, until there is a write to QUADSPI_CCR. */ + } + QUADSPI.CCR()->set(ccr); + if (address != reinterpret_cast(FlashAddressSpaceSize)) { + QUADSPI.AR()->set(reinterpret_cast(address)); + } + + if (functionalMode == QUADSPI::CCR::FunctionalMode::IndirectWrite) { + for (size_t i=0; iset(data[i]); + } + } else if (functionalMode == QUADSPI::CCR::FunctionalMode::IndirectRead) { + for (size_t i=0; iget(); + } + } + + /* Wait for the command to be sent. + * "When configured in memory-mapped mode, because of the prefetch operations, + * BUSY does not fall until there is a timeout, there is an abort, or the + * peripheral is disabled.", so we do not wait if the device is in + * memory-mapped mode. */ + if (functionalMode != QUADSPI::CCR::FunctionalMode::MemoryMapped) { + while (QUADSPI.SR()->getBUSY()) { + } + } +} + +static void initGPIO() { + for(const AFGPIOPin & p : Config::Pins) { + p.init(); + } +} + +static void initQSPI() { + // Enable QUADSPI AHB3 peripheral clock + RCC.AHB3ENR()->setQSPIEN(true); + + // Configure controller for target device + class QUADSPI::DCR dcr(0); + dcr.setFSIZE(NumberOfAddressBitsInChip - 1); + constexpr int ChipSelectHighTimeCycles = (ChipSelectHighTimeInNanoSeconds * static_cast(Clocks::Config::AHBFrequency)) / (static_cast(ClockFrequencyDivisor) * 1000.0f) + 1.0f; + dcr.setCSHT(ChipSelectHighTimeCycles - 1); + dcr.setCKMODE(true); + QUADSPI.DCR()->set(dcr); + class QUADSPI::CR cr(0); + cr.setPRESCALER(ClockFrequencyDivisor - 1); + cr.setEN(true); + QUADSPI.CR()->set(cr); +} + +static void initChip() { + // Release sleep deep + send_command(Command::ReleaseDeepPowerDown); + Timing::usleep(3); + + /* The chip initially expects commands in SPI mode. We need to use SPI to tell + * it to switch to QuadSPI/QPI. */ + if (sOperatingMode == QUADSPI::CCR::OperatingMode::Single) { + send_command(Command::WriteEnable); + ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0); + statusRegister2.setQE(true); + wait(); + send_write_command(Command::WriteStatusRegister2, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&statusRegister2), sizeof(statusRegister2), sOperatingModes101); + wait(); + sOperatingMode = QUADSPI::CCR::OperatingMode::Quad; + } + set_as_memory_mapped(); +} + +void init() { + if (Config::NumberOfSectors == 0) { + return; + } + initGPIO(); + initQSPI(); + initChip(); +} + +static void shutdownGPIO() { + for(const AFGPIOPin & p : Config::Pins) { + p.group().OSPEEDR()->setOutputSpeed(p.pin(), GPIO::OSPEEDR::OutputSpeed::Low); + p.group().MODER()->setMode(p.pin(), GPIO::MODER::Mode::Analog); + p.group().PUPDR()->setPull(p.pin(), GPIO::PUPDR::Pull::None); + } +} + +static void shutdownChip() { + unset_memory_mapped_mode(); + // Reset + send_command(Command::EnableReset); + send_command(Command::Reset); + sOperatingMode = QUADSPI::CCR::OperatingMode::Single; + Timing::usleep(30); + + // Sleep deep + send_command(Command::DeepPowerDown); + Timing::usleep(3); +} + +static void shutdownQSPI() { + // Reset the controller + RCC.AHB3RSTR()->setQSPIRST(true); + RCC.AHB3RSTR()->setQSPIRST(false); + + RCC.AHB3ENR()->setQSPIEN(false); // TODO: move in Device::shutdownClocks +} + +void shutdown() { + if (Config::NumberOfSectors == 0) { + return; + } + shutdownChip(); + shutdownQSPI(); + shutdownGPIO(); +} + +int SectorAtAddress(uint32_t address) { + /* WARNING: this code assumes that the flash sectors are of increasing size: + * first all 4K sectors, then all 32K sectors, and finally all 64K sectors. */ + int i = address >> NumberOfAddressBitsIn64KbyteBlock; + if (i > Config::NumberOf64KSectors) { + return -1; + } + if (i >= 1) { + return Config::NumberOf4KSectors + Config::NumberOf32KSectors + i - 1; + } + i = address >> NumberOfAddressBitsIn32KbyteBlock; + if (i >= 1) { + i = Config::NumberOf4KSectors + i - 1; + assert(i >= 0 && i <= Config::NumberOf32KSectors); + return i; + } + i = address >> NumberOfAddressBitsIn4KbyteBlock; + assert(i <= Config::NumberOf4KSectors); + return i; +} + +void unlockFlash() { + // Warning: unset_memory_mapped_mode must be called before + send_command(Command::WriteEnable); + wait(); + ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0); + ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0); + ExternalFlashStatusRegister::StatusRegister2 currentStatusRegister2(0); + send_read_command(Command::ReadStatusRegister2, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(¤tStatusRegister2), sizeof(currentStatusRegister2)); + statusRegister2.setQE(currentStatusRegister2.getQE()); + + uint8_t registers[] = {statusRegister1.get(), statusRegister2.get()}; + send_write_command(Command::WriteStatusRegister, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(registers), sizeof(registers), sOperatingModes101); + wait(); +} + +void JDECid(uint8_t * manufacturerID, uint8_t * memoryType, uint8_t * capacityType) { + unset_memory_mapped_mode(); + struct JEDECId { + uint8_t manufacturerID; + uint8_t memoryType; + uint8_t capacityType; + }; + JEDECId id; + send_read_command(Command::ReadJEDECID, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&id), sizeof(id)); + *manufacturerID = id.manufacturerID; + *memoryType = id.memoryType; + *capacityType = id.capacityType; + set_as_memory_mapped(); +} + +void MassErase() { + // Mass erase is not enabled on kernel + assert(false); +} + +void WriteMemory(uint8_t * destination, const uint8_t * source, size_t length) { + asm("cpsid if"); + reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashWriteMemory))(destination + ExternalFlash::Config::StartAddress, source, length); + asm("cpsie if"); +} + +void EraseSector(int i) { + asm("cpsid if"); + reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashEraseSector))(i); + asm("cpsie if"); +} + +} +} +} diff --git a/ion/src/device/bootloader/drivers/trampoline.cpp b/ion/src/device/bootloader/drivers/trampoline.cpp new file mode 100644 index 000000000..be12ea05e --- /dev/null +++ b/ion/src/device/bootloader/drivers/trampoline.cpp @@ -0,0 +1,14 @@ +#include +#include + +namespace Ion { +namespace Device { +namespace Trampoline { + +uint32_t address(int index) { + return 0x0020E000 + sizeof(void *) * index; +} + +} +} +} diff --git a/ion/src/device/bootloader/drivers/trampoline.h b/ion/src/device/bootloader/drivers/trampoline.h new file mode 100644 index 000000000..db10ce4bc --- /dev/null +++ b/ion/src/device/bootloader/drivers/trampoline.h @@ -0,0 +1,22 @@ +#ifndef ION_DEVICE_BOOTLOADER_DRIVERS_TRAMPOLINE_H +#define ION_DEVICE_BOOTLOADER_DRIVERS_TRAMPOLINE_H + +#include + +namespace Ion { +namespace Device { +namespace Trampoline { + +constexpr int Suspend = 0; +constexpr int ExternalFlashEraseSector = 1; +constexpr int ExternalFlashWriteMemory = 2; + +// TODO: Use the other available trampolines instead of liba's functions + +uint32_t address(int index); + +} +} +} + +#endif From 5e130cc4f28f7ee481862b1762bdacb067c36529 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Tue, 22 Feb 2022 23:47:53 +0300 Subject: [PATCH 05/37] [ion/bootloader] Made suspend work --- ion/src/device/bootloader/drivers/board.cpp | 10 ++- .../drivers/external_flash_tramp.cpp | 88 +------------------ ion/src/device/bootloader/drivers/power.cpp | 26 ++++++ ion/src/device/n0100/drivers/power.cpp | 33 +++++++ ion/src/device/n0110/drivers/power.cpp | 39 ++++++++ ion/src/device/shared/drivers/power.cpp | 37 -------- 6 files changed, 111 insertions(+), 122 deletions(-) diff --git a/ion/src/device/bootloader/drivers/board.cpp b/ion/src/device/bootloader/drivers/board.cpp index 1d61eef86..3db56ee2a 100644 --- a/ion/src/device/bootloader/drivers/board.cpp +++ b/ion/src/device/bootloader/drivers/board.cpp @@ -345,12 +345,20 @@ void shutdownClocks(bool keepLEDAwake) { RCC.AHB2ENR()->set(0); // Reset value // AHB3 bus - RCC.AHB3ENR()->set(0); // Reset value + class RCC::AHB3ENR ahb3enr(0); // Reset value + // Required by external flash + ahb3enr.setQSPIEN(true); + RCC.AHB3ENR()->set(ahb3enr); // Reset value // APB1 class RCC::APB1ENR apb1enr(0); // Reset value // AHB1 bus class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value + // GPIO B, C, D, E are used the by external flash + ahb1enr.setGPIOBEN(true); + ahb1enr.setGPIOCEN(true); + ahb1enr.setGPIODEN(true); + ahb1enr.setGPIOEEN(true); if (keepLEDAwake) { apb1enr.setTIM3EN(true); ahb1enr.setGPIOBEN(true); diff --git a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp index f42cdbd5b..031722356 100644 --- a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp +++ b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace Ion { namespace Device { @@ -126,7 +127,7 @@ static constexpr OperatingModes sOperatingModes111(QUADSPI::CCR::OperatingMode:: static constexpr OperatingModes sOperatingModes114(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad); static constexpr OperatingModes sOperatingModes144(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad, QUADSPI::CCR::OperatingMode::Quad); -static QUADSPI::CCR::OperatingMode sOperatingMode = QUADSPI::CCR::OperatingMode::Single; +// static QUADSPI::CCR::OperatingMode sOperatingMode = QUADSPI::CCR::OperatingMode::Single; static constexpr int ClockFrequencyDivisor = 2; // F(QUADSPI) = F(AHB) / ClockFrequencyDivisor static constexpr int FastReadQuadIODummyCycles = 4; // Must be 4 for W25Q64JV (Fig 24.A page 34) and for AT25F641 (table 7.19 page 28) @@ -304,93 +305,12 @@ static void send_command_full(QUADSPI::CCR::FunctionalMode functionalMode, Opera } } -static void initGPIO() { - for(const AFGPIOPin & p : Config::Pins) { - p.init(); - } -} - -static void initQSPI() { - // Enable QUADSPI AHB3 peripheral clock - RCC.AHB3ENR()->setQSPIEN(true); - - // Configure controller for target device - class QUADSPI::DCR dcr(0); - dcr.setFSIZE(NumberOfAddressBitsInChip - 1); - constexpr int ChipSelectHighTimeCycles = (ChipSelectHighTimeInNanoSeconds * static_cast(Clocks::Config::AHBFrequency)) / (static_cast(ClockFrequencyDivisor) * 1000.0f) + 1.0f; - dcr.setCSHT(ChipSelectHighTimeCycles - 1); - dcr.setCKMODE(true); - QUADSPI.DCR()->set(dcr); - class QUADSPI::CR cr(0); - cr.setPRESCALER(ClockFrequencyDivisor - 1); - cr.setEN(true); - QUADSPI.CR()->set(cr); -} - -static void initChip() { - // Release sleep deep - send_command(Command::ReleaseDeepPowerDown); - Timing::usleep(3); - - /* The chip initially expects commands in SPI mode. We need to use SPI to tell - * it to switch to QuadSPI/QPI. */ - if (sOperatingMode == QUADSPI::CCR::OperatingMode::Single) { - send_command(Command::WriteEnable); - ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0); - statusRegister2.setQE(true); - wait(); - send_write_command(Command::WriteStatusRegister2, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&statusRegister2), sizeof(statusRegister2), sOperatingModes101); - wait(); - sOperatingMode = QUADSPI::CCR::OperatingMode::Quad; - } - set_as_memory_mapped(); -} - void init() { - if (Config::NumberOfSectors == 0) { - return; - } - initGPIO(); - initQSPI(); - initChip(); -} - -static void shutdownGPIO() { - for(const AFGPIOPin & p : Config::Pins) { - p.group().OSPEEDR()->setOutputSpeed(p.pin(), GPIO::OSPEEDR::OutputSpeed::Low); - p.group().MODER()->setMode(p.pin(), GPIO::MODER::Mode::Analog); - p.group().PUPDR()->setPull(p.pin(), GPIO::PUPDR::Pull::None); - } -} - -static void shutdownChip() { - unset_memory_mapped_mode(); - // Reset - send_command(Command::EnableReset); - send_command(Command::Reset); - sOperatingMode = QUADSPI::CCR::OperatingMode::Single; - Timing::usleep(30); - - // Sleep deep - send_command(Command::DeepPowerDown); - Timing::usleep(3); -} - -static void shutdownQSPI() { - // Reset the controller - RCC.AHB3RSTR()->setQSPIRST(true); - RCC.AHB3RSTR()->setQSPIRST(false); - - RCC.AHB3ENR()->setQSPIEN(false); // TODO: move in Device::shutdownClocks + assert(false); } void shutdown() { - if (Config::NumberOfSectors == 0) { - return; - } - shutdownChip(); - shutdownQSPI(); - shutdownGPIO(); + assert(false); } int SectorAtAddress(uint32_t address) { diff --git a/ion/src/device/bootloader/drivers/power.cpp b/ion/src/device/bootloader/drivers/power.cpp index f8f657dea..f38f9e978 100644 --- a/ion/src/device/bootloader/drivers/power.cpp +++ b/ion/src/device/bootloader/drivers/power.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include @@ -56,6 +58,30 @@ void standbyConfiguration() { CORTEX.SCR()->setSLEEPDEEP(true); // Allow Cortex-M7 deepsleep state } +void __attribute__((noinline)) internalFlashSuspend(bool isLEDActive) { + // Shutdown all clocks (except the ones used by LED if active and the one used by the flash) + Device::Board::shutdownClocks(isLEDActive); + + Device::Power::enterLowPowerMode(); + + /* A hardware event triggered a wake up, we determine if the device should + * wake up. We wake up when: + * - only the power key was down + * - the unplugged device was plugged + * - the battery stopped charging */ + Device::Board::initClocks(); +} + +void __attribute__((noinline)) internalFlashStandby() { + Device::Board::shutdownClocks(); + Device::Power::enterLowPowerMode(); + Device::Reset::coreWhilePlugged(); +} + +void enterLowPowerMode() { + reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::Suspend))(); +} + } } } diff --git a/ion/src/device/n0100/drivers/power.cpp b/ion/src/device/n0100/drivers/power.cpp index 4f7a5b8fd..e14012b71 100644 --- a/ion/src/device/n0100/drivers/power.cpp +++ b/ion/src/device/n0100/drivers/power.cpp @@ -1,5 +1,6 @@ #include #include +#include #include namespace Ion { @@ -29,6 +30,38 @@ void configWakeUp() { Device::WakeUp::onUSBPlugging(); } +void __attribute__((noinline)) internalFlashSuspend(bool isLEDActive) { + // Shutdown all clocks (except the ones used by LED if active) + Device::Board::shutdownClocks(isLEDActive); + + Device::Power::enterLowPowerMode(); + + /* A hardware event triggered a wake up, we determine if the device should + * wake up. We wake up when: + * - only the power key was down + * - the unplugged device was plugged + * - the battery stopped charging */ + Device::Board::initClocks(); +} + +void __attribute__((noinline)) internalFlashStandby() { + Device::Board::shutdownClocks(); + Device::Power::enterLowPowerMode(); + Device::Reset::coreWhilePlugged(); +} + +void enterLowPowerMode() { + /* To enter sleep, we need to issue a WFE instruction, which waits for the + * event flag to be set and then clears it. However, the event flag might + * already be on. So the safest way to make sure we actually wait for a new + * event is to force the event flag to on (SEV instruction), use a first WFE + * to clear it, and then a second WFE to wait for a _new_ event. */ + asm("sev"); + asm("wfe"); + asm("nop"); + asm("wfe"); +} + } } } diff --git a/ion/src/device/n0110/drivers/power.cpp b/ion/src/device/n0110/drivers/power.cpp index f8f657dea..1286bc883 100644 --- a/ion/src/device/n0110/drivers/power.cpp +++ b/ion/src/device/n0110/drivers/power.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include @@ -56,6 +58,43 @@ void standbyConfiguration() { CORTEX.SCR()->setSLEEPDEEP(true); // Allow Cortex-M7 deepsleep state } +void __attribute__((noinline)) internalFlashSuspend(bool isLEDActive) { + // Shutdown the external flash + Device::ExternalFlash::shutdown(); + // Shutdown all clocks (except the ones used by LED if active) + Device::Board::shutdownClocks(isLEDActive); + + Device::Power::enterLowPowerMode(); + + /* A hardware event triggered a wake up, we determine if the device should + * wake up. We wake up when: + * - only the power key was down + * - the unplugged device was plugged + * - the battery stopped charging */ + Device::Board::initClocks(); + // Init external flash + Device::ExternalFlash::init(); +} + +void __attribute__((noinline)) internalFlashStandby() { + Device::ExternalFlash::shutdown(); + Device::Board::shutdownClocks(); + Device::Power::enterLowPowerMode(); + Device::Reset::coreWhilePlugged(); +} + +void enterLowPowerMode() { + /* To enter sleep, we need to issue a WFE instruction, which waits for the + * event flag to be set and then clears it. However, the event flag might + * already be on. So the safest way to make sure we actually wait for a new + * event is to force the event flag to on (SEV instruction), use a first WFE + * to clear it, and then a second WFE to wait for a _new_ event. */ + asm("sev"); + asm("wfe"); + asm("nop"); + asm("wfe"); +} + } } } diff --git a/ion/src/device/shared/drivers/power.cpp b/ion/src/device/shared/drivers/power.cpp index 0e5b35267..bba6444c3 100644 --- a/ion/src/device/shared/drivers/power.cpp +++ b/ion/src/device/shared/drivers/power.cpp @@ -100,31 +100,6 @@ namespace Power { // Public Power methods using namespace Device::Regs; -void __attribute__((noinline)) internalFlashSuspend(bool isLEDActive) { - // Shutdown the external flash - Device::ExternalFlash::shutdown(); - // Shutdown all clocks (except the ones used by LED if active) - Device::Board::shutdownClocks(isLEDActive); - - Device::Power::enterLowPowerMode(); - - /* A hardware event triggered a wake up, we determine if the device should - * wake up. We wake up when: - * - only the power key was down - * - the unplugged device was plugged - * - the battery stopped charging */ - Device::Board::initClocks(); - // Init external flash - Device::ExternalFlash::init(); -} - -void __attribute__((noinline)) internalFlashStandby() { - Device::ExternalFlash::shutdown(); - Device::Board::shutdownClocks(); - Device::Power::enterLowPowerMode(); - Device::Reset::coreWhilePlugged(); -} - void stopConfiguration() { PWR.CR()->setMRUDS(true); // Main regulator in Low Voltage and Flash memory in Deep Sleep mode when the device is in Stop mode PWR.CR()->setLPUDS(true); // Low-power regulator in under-drive mode if LPDS bit is set and Flash memory in power-down when the device is in Stop under-drive mode @@ -163,18 +138,6 @@ void waitUntilOnOffKeyReleased() { Timing::msleep(100); } -void enterLowPowerMode() { - /* To enter sleep, we need to issue a WFE instruction, which waits for the - * event flag to be set and then clears it. However, the event flag might - * already be on. So the safest way to make sure we actually wait for a new - * event is to force the event flag to on (SEV instruction), use a first WFE - * to clear it, and then a second WFE to wait for a _new_ event. */ - asm("sev"); - asm("wfe"); - asm("nop"); - asm("wfe"); -} - } } } From 2da1610b2787150b5e15111c0ab542a9e37f9af8 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Thu, 24 Feb 2022 13:12:51 +0300 Subject: [PATCH 06/37] [build/bootloader] Include extrnal --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f83a5922f..48d3d1df5 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ include build/toolchain.$(TOOLCHAIN).mak include build/variants.mak include build/helpers.mk -ifeq (${MODEL}, n0110) +ifeq (${MODEL},$(filter ${MODEL},n0110 bootloader)) apps_list = ${EPSILON_APPS} else apps_list = $(foreach i, ${EPSILON_APPS}, $(if $(filter external, $(i)),,$(i))) From 3a391261c7cc645d87e0db0a8d214890541f4430 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Thu, 24 Feb 2022 13:13:31 +0300 Subject: [PATCH 07/37] [ion/bootloader/usb] Removed internal flash from descriptior --- ion/src/device/bootloader/drivers/config/usb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/device/bootloader/drivers/config/usb.h b/ion/src/device/bootloader/drivers/config/usb.h index 0c63ed474..61c1e739d 100644 --- a/ion/src/device/bootloader/drivers/config/usb.h +++ b/ion/src/device/bootloader/drivers/config/usb.h @@ -21,7 +21,7 @@ constexpr static AFGPIOPin VbusPin = AFGPIOPin(GPIOA, 9, GPIO::AFR::AlternateFun constexpr static AFGPIOPin DmPin = AFGPIOPin(GPIOA, 11, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast); constexpr static AFGPIOPin DpPin = AFGPIOPin(GPIOA, 12, GPIO::AFR::AlternateFunction::AF10, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast); -constexpr static const char * InterfaceStringDescriptor = "@Flash/0x08000000/04*016Kg/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg"; +constexpr static const char * InterfaceStringDescriptor = "@Flash/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg"; } } From c8ba2fd268e97bc32bac1be1f422af53d744db4b Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Fri, 25 Feb 2022 16:03:14 +0100 Subject: [PATCH 08/37] [build/bootloader] Allow building slot A and B --- build/targets.device.bootloader.mak | 33 ++++++++++++++++++++++------- ion/src/device/bootloader/Makefile | 4 ---- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/build/targets.device.bootloader.mak b/build/targets.device.bootloader.mak index 102885e58..d3644ca0f 100644 --- a/build/targets.device.bootloader.mak +++ b/build/targets.device.bootloader.mak @@ -1,9 +1,26 @@ -HANDY_TARGETS += test.external_flash.write test.external_flash.read -$(BUILD_DIR)/test.external_flash.%.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld -test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src) -$(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_read_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_read_src)) -$(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src)) +epsilon_flavors_bootloader = $(foreach floavor,$(epsilon_flavors),$(floavor).A $(floavor).B) + +define rule_for_epsilon_flavor_bootloader +$$(BUILD_DIR)/epsilon.$(1).A.$$(EXE): $$(call flavored_object_for,$$(epsilon_src),$(1)) +$$(BUILD_DIR)/epsilon.$(1).A.$$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.A.ld +$$(BUILD_DIR)/epsilon.$(1).B.$$(EXE): $$(call flavored_object_for,$$(epsilon_src),$(1)) +$$(BUILD_DIR)/epsilon.$(1).B.$$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.B.ld +endef + +$(BUILD_DIR)/epsilon.A.$(EXE): $(call flavored_object_for,$(epsilon_src)) +$(BUILD_DIR)/epsilon.A.$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.A.ld + +$(BUILD_DIR)/epsilon.B.$(EXE): $(call flavored_object_for,$(epsilon_src)) +$(BUILD_DIR)/epsilon.B.$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.B.ld + + +$(foreach flavor,$(epsilon_flavors),$(eval $(call rule_for_epsilon_flavor_bootloader,$(flavor)))) + + +HANDY_TARGETS = $(foreach flavor,$(epsilon_flavors_bootloader),epsilon.$(flavor)) +HANDY_TARGETS += epsilon.A epsilon.B + .PHONY: %_flash %_flash: $(BUILD_DIR)/%.dfu @@ -22,9 +39,9 @@ $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ex $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin .PHONY: binpack -binpack: $(BUILD_DIR)/epsilon.onboarding.two_binaries +binpack: $(BUILD_DIR)/epsilon.onboarding.A.two_binaries $(BUILD_DIR)/epsilon.onboarding.B.two_binaries rm -rf $(BUILD_DIR)/binpack mkdir -p $(BUILD_DIR)/binpack - cp $(BUILD_DIR)/epsilon.onboarding.external.bin $(BUILD_DIR)/binpack - cd $(BUILD_DIR) && for binary in epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done + cp $(BUILD_DIR)/epsilon.onboarding.A.external.bin $(BUILD_DIR)/epsilon.onboarding.B.external.bin $(BUILD_DIR)/binpack + cd $(BUILD_DIR) && for binary in epsilon.onboarding.A.external.bin epsilon.onboarding.B.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/* diff --git a/ion/src/device/bootloader/Makefile b/ion/src/device/bootloader/Makefile index f614b6fba..c5d7208f7 100644 --- a/ion/src/device/bootloader/Makefile +++ b/ion/src/device/bootloader/Makefile @@ -17,7 +17,3 @@ ion_device_src += $(addprefix ion/src/device/bootloader/boot/, \ ion_device_src += $(addprefix ion/src/device/bootloader/, \ platform_info.cpp \ ) - -SLOT ?= A - -LDSCRIPT ?= ion/src/device/bootloader/bootloader.$(SLOT).ld From c635f4e2b94c92572a0a1244e8998c753391bdc5 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Fri, 25 Feb 2022 16:05:38 +0100 Subject: [PATCH 09/37] [ci] Add building Omega for bootloader --- .github/workflows/ci-workflow.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 2173e1523..ebd27df3e 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -117,6 +117,24 @@ jobs: with: name: epsilon-binpack-n0110.tgz path: output/release/device/n0110/binpack-n0110.tgz + bootloader: + runs-on: ubuntu-latest + steps: + - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config + - uses: numworks/setup-arm-toolchain@2020-q2 + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + - run: make MODEL=bootloader -j2 epsilon.A.dfu epsilon.B.dfu + - run: make MODEL=bootloader -j2 epsilon.onboarding.A.dfu epsilon.onboarding.B.dfu + - run: make MODEL=bootloader -j2 epsilon.onboarding.update.A.dfu epsilon.onboarding.update.B.dfu + - run: make MODEL=bootloader -j2 epsilon.onboarding.beta.A.dfu epsilon.onboarding.beta.B.dfu + - run: make -j2 binpack + - run: cp output/release/device/bootloader/binpack-bootloader-`git rev-parse HEAD | head -c 7`.tgz output/release/device/bootloader/binpack-bootloader.tgz + - uses: actions/upload-artifact@master + with: + name: epsilon-binpack-bootloader.tgz + path: output/release/device/bootloader/binpack-bootloader.tgz windows: runs-on: windows-latest defaults: From 272797f31f352c4e4cf41c82172fad8d665177d5 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Fri, 25 Feb 2022 16:17:38 +0100 Subject: [PATCH 10/37] [bootloader] Added bootloader --- Makefile | 1 + bootloader/Makefile | 6 ++++++ bootloader/main.cpp | 15 +++++++++++++++ build/targets.device.n0110.mak | 6 +++++- 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 bootloader/Makefile create mode 100644 bootloader/main.cpp diff --git a/Makefile b/Makefile index 48d3d1df5..80cb6fcd0 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,7 @@ include poincare/Makefile include python/Makefile include escher/Makefile # Executable Makefiles +include bootloader/Makefile include apps/Makefile include build/struct_layout/Makefile include build/scenario/Makefile diff --git a/bootloader/Makefile b/bootloader/Makefile new file mode 100644 index 000000000..c1b39faf4 --- /dev/null +++ b/bootloader/Makefile @@ -0,0 +1,6 @@ + +bootloader_src += $(addprefix bootloader/,\ + main.cpp \ +) + +bootloader_src += $(ion_src) $(kandinsky_src) $(liba_src) $(libaxx_src) diff --git a/bootloader/main.cpp b/bootloader/main.cpp new file mode 100644 index 000000000..0147279bb --- /dev/null +++ b/bootloader/main.cpp @@ -0,0 +1,15 @@ + +#include +#include +#include + +void ion_main(int argc, const char * const argv[]) { + // Initialize the backlight + Ion::Backlight::init(); + while (1) { + Ion::Display::pushRectUniform(KDRect(0,0,10,10), KDColorRed); + Ion::Timing::msleep(100); + Ion::Display::pushRectUniform(KDRect(0,0,10,10), KDColorBlue); + Ion::Timing::msleep(100); + } +} diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index c0da7d7bb..87508eac2 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -1,10 +1,14 @@ -HANDY_TARGETS += test.external_flash.write test.external_flash.read +HANDY_TARGETS += test.external_flash.write test.external_flash.read bootloader $(BUILD_DIR)/test.external_flash.%.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src) $(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_read_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_read_src)) $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src)) + +$(BUILD_DIR)/bootloader.$(EXE): $(call flavored_object_for,$(bootloader_src)) +$(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld + .PHONY: %_flash %_flash: $(BUILD_DIR)/%.dfu @echo "DFU $@" From 4fe84a4959390a50f4ced4ddc67321622a97aa28 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Fri, 25 Feb 2022 16:57:06 +0100 Subject: [PATCH 11/37] [bootloader] Added slot definition --- bootloader/slot.cpp | 8 ++++++++ bootloader/slot.h | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 bootloader/slot.cpp create mode 100644 bootloader/slot.h diff --git a/bootloader/slot.cpp b/bootloader/slot.cpp new file mode 100644 index 000000000..ff2020214 --- /dev/null +++ b/bootloader/slot.cpp @@ -0,0 +1,8 @@ +#include + +namespace Bootloader { + + const struct Slot* s_slotA = reinterpret_cast(0x90000000); + const struct Slot* s_slotB = reinterpret_cast(0x90400000); + +} diff --git a/bootloader/slot.h b/bootloader/slot.h new file mode 100644 index 000000000..6190fcf8d --- /dev/null +++ b/bootloader/slot.h @@ -0,0 +1,25 @@ +#ifndef BOOTLOADER_SLOT +#define BOOTLOADER_SLOT + +#include + +namespace Bootloader { + +struct Slot { + uint32_t unknown; + uint32_t signature_offset; + uint32_t magik_header; + char version[8]; + char patch_level[8]; + uint32_t magik_footer; + uint32_t* stack_pointer; + void(*main_pointer)(); +}; + +extern const struct Slot* s_slotA; +extern const struct Slot* s_slotB; + +} + + +#endif \ No newline at end of file From e1dcbad18adf0ed0b4f2950d1c468a7f5abbbe4d Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Fri, 25 Feb 2022 23:23:47 +0100 Subject: [PATCH 12/37] [bootloader] Run usb stack on Reset+4 --- bootloader/Makefile | 12 ++++- bootloader/interface.cpp | 47 ++++++++++++++++++ bootloader/interface.h | 22 ++++++++ bootloader/jump_to_firmware.s | 11 ++++ bootloader/main.cpp | 33 +++++++++--- bootloader/slot.cpp | 25 +++++++++- bootloader/slot.h | 34 ++++++++----- build/targets.device.n0110.mak | 2 +- themes/icons.json | 5 +- .../local/omega_light/bootloader/cable.png | Bin 0 -> 5586 bytes .../local/omega_light/bootloader/computer.png | Bin 0 -> 10338 bytes 11 files changed, 165 insertions(+), 26 deletions(-) create mode 100644 bootloader/interface.cpp create mode 100644 bootloader/interface.h create mode 100644 bootloader/jump_to_firmware.s create mode 100644 themes/themes/local/omega_light/bootloader/cable.png create mode 100644 themes/themes/local/omega_light/bootloader/computer.png diff --git a/bootloader/Makefile b/bootloader/Makefile index c1b39faf4..948599786 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -1,6 +1,16 @@ bootloader_src += $(addprefix bootloader/,\ main.cpp \ + slot.cpp \ + interface.cpp \ + jump_to_firmware.s \ ) -bootloader_src += $(ion_src) $(kandinsky_src) $(liba_src) $(libaxx_src) +bootloader_images = $(addprefix bootloader/, \ + cable.png \ + computer.png \ +) + +bootloader_src += $(ion_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(bootloader_images) + +$(eval $(call depends_on_image,bootloader/interface.cpp,$(bootloader_images))) diff --git a/bootloader/interface.cpp b/bootloader/interface.cpp new file mode 100644 index 000000000..3bab7e3d8 --- /dev/null +++ b/bootloader/interface.cpp @@ -0,0 +1,47 @@ + +#include +#include + +#include "interface.h" + +#include "computer.h" +#include "cable.h" + +namespace Bootloader { + +void Interface::drawImage(KDContext* ctx, const Image* image, int offset) { + const uint8_t* data; + size_t size; + size_t pixelBufferSize; + + if (image != nullptr) { + data = image->compressedPixelData(); + size = image->compressedPixelDataSize(); + pixelBufferSize = image->width() * image->height(); + } else { + return; + } + + KDColor pixelBuffer[4000]; + assert(pixelBufferSize <= 4000); + assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack + + Ion::decompress( + data, + reinterpret_cast(pixelBuffer), + size, + pixelBufferSize * sizeof(KDColor) + ); + + KDRect bounds((320 - image->width()) / 2, offset, image->width(), image->height()); + + ctx->fillRectWithPixels(bounds, pixelBuffer, nullptr); +} + +void Interface::draw() { + KDContext * ctx = KDIonContext::sharedContext(); + drawImage(ctx, ImageStore::Computer, 70); + drawImage(ctx, ImageStore::Cable, 172); +} + +} diff --git a/bootloader/interface.h b/bootloader/interface.h new file mode 100644 index 000000000..0a98c2b57 --- /dev/null +++ b/bootloader/interface.h @@ -0,0 +1,22 @@ +#ifndef BOOTLOADER_INTERFACE +#define BOOTLOADER_INTERFACE + +#include +#include +#include + +namespace Bootloader { + +class Interface { + +private: + static void drawImage(KDContext* ctx, const Image* image, int offset); + +public: + static void draw(); + +}; + +} + +#endif \ No newline at end of file diff --git a/bootloader/jump_to_firmware.s b/bootloader/jump_to_firmware.s new file mode 100644 index 000000000..c5b54bcd2 --- /dev/null +++ b/bootloader/jump_to_firmware.s @@ -0,0 +1,11 @@ + +.syntax unified +.section .text.jump_to_firmware +.align 2 +.thumb +.global jump_to_firmware +jump_to_firmware: + dsb 0xF + isb 0xF + msr msp, r0 + bx r1 diff --git a/bootloader/main.cpp b/bootloader/main.cpp index 0147279bb..119911199 100644 --- a/bootloader/main.cpp +++ b/bootloader/main.cpp @@ -1,15 +1,32 @@ -#include -#include -#include +#include +#include +#include + +#include "interface.h" void ion_main(int argc, const char * const argv[]) { + // Clear the screen + Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorBlack); // Initialize the backlight Ion::Backlight::init(); - while (1) { - Ion::Display::pushRectUniform(KDRect(0,0,10,10), KDColorRed); - Ion::Timing::msleep(100); - Ion::Display::pushRectUniform(KDRect(0,0,10,10), KDColorBlue); - Ion::Timing::msleep(100); + + uint64_t scan = Ion::Keyboard::scan(); + + if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Four)) { + Bootloader::Interface::draw(); + while (true) { + Ion::USB::enable(); + while (!Ion::USB::isEnumerated()) { + } + Ion::USB::DFU(false); + } } + + /* + KDContext * ctx = KDIonContext::sharedContext(); + ctx->drawString(Bootloader::s_slotA->version(), KDPoint(0, 20)); + + */ + Bootloader::s_slotA->boot(); } diff --git a/bootloader/slot.cpp b/bootloader/slot.cpp index ff2020214..3df80ac87 100644 --- a/bootloader/slot.cpp +++ b/bootloader/slot.cpp @@ -1,8 +1,29 @@ #include +extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void)); + namespace Bootloader { - const struct Slot* s_slotA = reinterpret_cast(0x90000000); - const struct Slot* s_slotB = reinterpret_cast(0x90400000); + +const Slot* s_slotA = reinterpret_cast(0x90000000); +const Slot* s_slotB = reinterpret_cast(0x90400000); + +const char * Slot::version() const { + return m_version; +} + +const char * Slot::patchLevel() const { + return m_patchLevel; +} + +const bool Slot::isValid() const { + return m_header == Magic && m_footer == Magic; +} + +[[ noreturn ]] void Slot::boot() const { + jump_to_firmware(m_stackPointer, m_startPointer); + for(;;); +} + } diff --git a/bootloader/slot.h b/bootloader/slot.h index 6190fcf8d..4bb7de8fd 100644 --- a/bootloader/slot.h +++ b/bootloader/slot.h @@ -5,21 +5,29 @@ namespace Bootloader { -struct Slot { - uint32_t unknown; - uint32_t signature_offset; - uint32_t magik_header; - char version[8]; - char patch_level[8]; - uint32_t magik_footer; - uint32_t* stack_pointer; - void(*main_pointer)(); +class Slot { +public: + const char * version() const; + const char * patchLevel() const; + const bool isValid() const; + [[ noreturn ]] void boot() const; + +private: + Slot(); + constexpr static uint32_t Magic = 0xDEC00DF0; + const uint32_t m_unknown; + const uint32_t m_signature; + const uint32_t m_header; + const char m_version[8]; + const char m_patchLevel[8]; + const uint32_t m_footer; + const uint32_t* m_stackPointer; + const void(*m_startPointer)(); }; -extern const struct Slot* s_slotA; -extern const struct Slot* s_slotB; +extern const Slot* s_slotA; +extern const Slot* s_slotB; } - -#endif \ No newline at end of file +#endif diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index 87508eac2..d5e7dd30e 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -6,7 +6,7 @@ $(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ext $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src)) -$(BUILD_DIR)/bootloader.$(EXE): $(call flavored_object_for,$(bootloader_src)) +$(BUILD_DIR)/bootloader.$(EXE): $(call flavored_object_for,$(bootloader_src),usbxip) $(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld .PHONY: %_flash diff --git a/themes/icons.json b/themes/icons.json index 3dc1ae52d..c3349673d 100644 --- a/themes/icons.json +++ b/themes/icons.json @@ -40,5 +40,8 @@ "apps/probability/images/normal_icon.png" : "probability/normal_icon.png", "apps/probability/images/poisson_icon.png" : "probability/poisson_icon.png", "apps/probability/images/student_icon.png" : "probability/student_icon.png", - "apps/probability/images/uniform_icon.png" : "probability/uniform_icon.png" + "apps/probability/images/uniform_icon.png" : "probability/uniform_icon.png", + + "bootloader/cable.png": "bootloader/cable.png", + "bootloader/computer.png": "bootloader/computer.png" } diff --git a/themes/themes/local/omega_light/bootloader/cable.png b/themes/themes/local/omega_light/bootloader/cable.png new file mode 100644 index 0000000000000000000000000000000000000000..cf2c2f8455e68d2fcd19efeee26dd2f7f32b4891 GIT binary patch literal 5586 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/omega_light/bootloader/computer.png b/themes/themes/local/omega_light/bootloader/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..0b5a578e6962685f14dc8c43866fec08c1ac2c71 GIT binary patch literal 10338 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 Vj8me+;?_!lmYTk5g|bcf{{Zqm*T(<= literal 0 HcmV?d00001 From 6eabe9acae6062c966be5c9378a0c9f99f2a9c47 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 26 Feb 2022 11:40:50 +0100 Subject: [PATCH 13/37] [ion/n0110] Fixed invalid assert in external flash driver --- ion/src/device/n0110/drivers/external_flash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/device/n0110/drivers/external_flash.cpp b/ion/src/device/n0110/drivers/external_flash.cpp index 34de8b591..1c9c4a1e9 100644 --- a/ion/src/device/n0110/drivers/external_flash.cpp +++ b/ion/src/device/n0110/drivers/external_flash.cpp @@ -405,7 +405,7 @@ int SectorAtAddress(uint32_t address) { i = address >> NumberOfAddressBitsIn32KbyteBlock; if (i >= 1) { i = Config::NumberOf4KSectors + i - 1; - assert(i >= 0 && i <= Config::NumberOf32KSectors); + assert(i >= Config::NumberOf4KSectors && i <= Config::NumberOf4KSectors + Config::NumberOf32KSectors); return i; } i = address >> NumberOfAddressBitsIn4KbyteBlock; From d16808183b47b1ec3adfc5280f9729f1a9b1df60 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 26 Feb 2022 11:42:03 +0100 Subject: [PATCH 14/37] [bootloader] MPU config --- bootloader/jump_to_firmware.s | 2 -- bootloader/main.cpp | 7 ++----- ion/src/device/n0110/drivers/board.cpp | 22 ++++++++++++++++++++++ ion/src/device/shared/drivers/board.h | 1 + 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/bootloader/jump_to_firmware.s b/bootloader/jump_to_firmware.s index c5b54bcd2..25a521713 100644 --- a/bootloader/jump_to_firmware.s +++ b/bootloader/jump_to_firmware.s @@ -5,7 +5,5 @@ .thumb .global jump_to_firmware jump_to_firmware: - dsb 0xF - isb 0xF msr msp, r0 bx r1 diff --git a/bootloader/main.cpp b/bootloader/main.cpp index 119911199..6f4e54237 100644 --- a/bootloader/main.cpp +++ b/bootloader/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "interface.h" @@ -23,10 +24,6 @@ void ion_main(int argc, const char * const argv[]) { } } - /* - KDContext * ctx = KDIonContext::sharedContext(); - ctx->drawString(Bootloader::s_slotA->version(), KDPoint(0, 20)); - - */ + Ion::Device::Board::bootloaderMPU(); Bootloader::s_slotA->boot(); } diff --git a/ion/src/device/n0110/drivers/board.cpp b/ion/src/device/n0110/drivers/board.cpp index 0ed24868c..23579c5a2 100644 --- a/ion/src/device/n0110/drivers/board.cpp +++ b/ion/src/device/n0110/drivers/board.cpp @@ -24,6 +24,28 @@ namespace Board { using namespace Regs; +void bootloaderMPU() { + // 1. Disable the MPU + // 1.1 Memory barrier + Cache::dmb(); + + // 1.3 Disable the MPU and clear the control register + MPU.CTRL()->setENABLE(false); + + MPU.RNR()->setREGION(7); + MPU.RBAR()->setADDR(0x90000000); + MPU.RASR()->setXN(false); + MPU.RASR()->setENABLE(true); + + // 2.3 Enable MPU + MPU.CTRL()->setENABLE(true); + + // 3. Data/instruction synchronisation barriers to ensure that the new MPU configuration is used by subsequent instructions. + Cache::disable(); + Cache::dsb(); + Cache::isb(); +} + void initMPU() { // 1. Disable the MPU // 1.1 Memory barrier diff --git a/ion/src/device/shared/drivers/board.h b/ion/src/device/shared/drivers/board.h index 61b63b2b3..8d1d1bbb6 100644 --- a/ion/src/device/shared/drivers/board.h +++ b/ion/src/device/shared/drivers/board.h @@ -9,6 +9,7 @@ namespace Board { void init(); +void bootloaderMPU(); void initFPU(); void initClocks(); void shutdownClocks(bool keepLEDAwake = false); From f2c17121d2dd45ed743fc33a39cabd9dbafd957d Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 26 Feb 2022 13:36:54 +0100 Subject: [PATCH 15/37] [bootloader] Added trampolines --- bootloader/Makefile | 1 + bootloader/trampoline.cpp | 33 ++++++++++++++++++++++++++ bootloader/trampoline.h | 12 ++++++++++ build/targets.device.n0110.mak | 2 +- ion/src/device/n0110/drivers/power.h | 1 + ion/src/device/n0110/internal_flash.ld | 6 +++++ 6 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 bootloader/trampoline.cpp create mode 100644 bootloader/trampoline.h diff --git a/bootloader/Makefile b/bootloader/Makefile index 948599786..8a4a0b0c6 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -4,6 +4,7 @@ bootloader_src += $(addprefix bootloader/,\ slot.cpp \ interface.cpp \ jump_to_firmware.s \ + trampoline.cpp \ ) bootloader_images = $(addprefix bootloader/, \ diff --git a/bootloader/trampoline.cpp b/bootloader/trampoline.cpp new file mode 100644 index 000000000..b69a79f18 --- /dev/null +++ b/bootloader/trampoline.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include + +#include "trampoline.h" + +namespace Bootloader { + +void __attribute__((noinline)) suspend() { + Ion::Device::Power::internalFlashSuspend(true); +} + +void* Trampolines[TRAMPOLINES_COUNT] + __attribute__((section(".trampolines_table"))) + __attribute__((used)) + = { + (void*) Bootloader::suspend, // Suspend + (void*) Ion::Device::Flash::EraseSector, // External erase + (void*) Ion::Device::Flash::WriteMemory, // External write + (void*) memcmp, + (void*) memcpy, + (void*) memmove, + (void*) memset, + (void*) strchr, + (void*) strcmp, + (void*) strlcat, + (void*) strlcpy, + (void*) strlen, + (void*) strncmp +}; + +} \ No newline at end of file diff --git a/bootloader/trampoline.h b/bootloader/trampoline.h new file mode 100644 index 000000000..49df37b6f --- /dev/null +++ b/bootloader/trampoline.h @@ -0,0 +1,12 @@ +#ifndef BOOTLOADER_TRAMPOLINE_H +#define BOOTLOADER_TRAMPOLINE_H + +namespace Bootloader { + +#define TRAMPOLINES_COUNT 13 + +extern void* Trampolines[TRAMPOLINES_COUNT]; + +} + +#endif \ No newline at end of file diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index d5e7dd30e..3199293f9 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -7,7 +7,7 @@ $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ex $(BUILD_DIR)/bootloader.$(EXE): $(call flavored_object_for,$(bootloader_src),usbxip) -$(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld +$(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/src/device/n0110/internal_flash.ld .PHONY: %_flash %_flash: $(BUILD_DIR)/%.dfu diff --git a/ion/src/device/n0110/drivers/power.h b/ion/src/device/n0110/drivers/power.h index a3d142bc1..1fbc55acf 100644 --- a/ion/src/device/n0110/drivers/power.h +++ b/ion/src/device/n0110/drivers/power.h @@ -8,6 +8,7 @@ namespace Device { namespace Power { void standbyConfiguration(); +void internalFlashSuspend(bool isLEDActive); } } diff --git a/ion/src/device/n0110/internal_flash.ld b/ion/src/device/n0110/internal_flash.ld index 76089e337..9eb193676 100644 --- a/ion/src/device/n0110/internal_flash.ld +++ b/ion/src/device/n0110/internal_flash.ld @@ -6,6 +6,7 @@ MEMORY { } STACK_SIZE = 32K; +TRAMPOLINES_OFFSET = 0xE000; SECTIONS { .isr_vector_table ORIGIN(INTERNAL_FLASH) : { @@ -69,6 +70,11 @@ SECTIONS { _data_section_end_ram = .; } >SRAM AT> INTERNAL_FLASH + .trampolines_table : { + . = ORIGIN(INTERNAL_FLASH) + TRAMPOLINES_OFFSET; + KEEP(*(.trampolines_table)); + } > INTERNAL_FLASH + .bss : { /* The bss section contains data for all uninitialized variables * So like the .data section, it will go in RAM, but unlike the data section From a63cbcf0c2b92ba99ba6a23017b95f103ceefd5a Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 26 Feb 2022 19:33:18 +0100 Subject: [PATCH 16/37] [bootloader] Make work with Numworks workshop --- build/targets.device.bootloader.mak | 2 +- ion/include/ion.h | 1 + ion/src/device/bootloader/bootloader.A.ld | 120 +--------------- ion/src/device/bootloader/bootloader.B.ld | 120 +--------------- .../device/bootloader/bootloader_common.ld | 128 ++++++++++++++++++ ion/src/device/bootloader/drivers/board.cpp | 15 +- ion/src/device/bootloader/platform_info.cpp | 118 +++++++++++++--- ion/src/device/n0100/platform_info.cpp | 4 + ion/src/device/n0110/platform_info.cpp | 4 + ion/src/device/shared/usb/dfu_relocated.cpp | 2 + ion/src/device/shared/usb/dfu_xip.cpp | 2 + 11 files changed, 260 insertions(+), 256 deletions(-) create mode 100644 ion/src/device/bootloader/bootloader_common.ld diff --git a/build/targets.device.bootloader.mak b/build/targets.device.bootloader.mak index d3644ca0f..3101a49ea 100644 --- a/build/targets.device.bootloader.mak +++ b/build/targets.device.bootloader.mak @@ -34,7 +34,7 @@ HANDY_TARGETS += epsilon.A epsilon.B .PHONY: %.two_binaries %.two_binaries: %.elf @echo "Building an external binary for $<" - $(Q) $(OBJCOPY) -O binary $< $(basename $<).external.bin + $(Q) $(OBJCOPY) -O binary -R .slot_info $< $(basename $<).external.bin @echo "Padding $(basename $<).external.bin" $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin diff --git a/ion/include/ion.h b/ion/include/ion.h index 854c3c539..ce44f84a3 100644 --- a/ion/include/ion.h +++ b/ion/include/ion.h @@ -39,6 +39,7 @@ const char * omegaVersion(); const char * patchLevel(); const char * fccId(); const char * pcbVersion(); +void updateSlotInfo(); // CRC32 : non xor-ed, non reversed, direct, polynomial 4C11DB7 uint32_t crc32Word(const uint32_t * data, size_t length); // Only accepts whole 32bit values diff --git a/ion/src/device/bootloader/bootloader.A.ld b/ion/src/device/bootloader/bootloader.A.ld index df8ad59f8..3e1188476 100644 --- a/ion/src/device/bootloader/bootloader.A.ld +++ b/ion/src/device/bootloader/bootloader.A.ld @@ -23,122 +23,6 @@ MEMORY { STACK_SIZE = 32K; FIRST_FLASH_SECTOR_SIZE = 4K; SIGNED_PAYLOAD_LENGTH = 8; +USERLAND_OFFSET = 64K; -SECTIONS { - .signed_payload_prefix ORIGIN(FLASH) : { - FILL(0xFF); - BYTE(0xFF) - . = ORIGIN(FLASH) + SIGNED_PAYLOAD_LENGTH; - } >FLASH - - .header : { - KEEP(*(.header)) - } >FLASH - - .isr_vector_table : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header)) { - /* 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 - * entry points to interrupt service routines. This data is called the ISR - * vector table. - * - * Note that address 0x0 is always an alias. It points to the beginning of - * Flash, SRAM, or integrated bootloader depending on the boot mode chosen. - * (This mode is chosen by setting the BOOTn pins on the chip). - * - * We're generating the ISR vector table in code because it's very - * convenient: using function pointers, we can easily point to the service - * routine for each interrupt. */ - _isr_vector_table_start_flash = LOADADDR(.isr_vector_table); - _isr_vector_table_start_ram = .; - KEEP(*(.isr_vector_table)) - _isr_vector_table_end_ram = .; - } >SRAM - - .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) : { - . = ALIGN(4K); - _exam_mode_buffer_start = .; - KEEP(*(.exam_mode_buffer)) - /* Note: We don't increment "." here, we set it. */ - . = . + FIRST_FLASH_SECTOR_SIZE; - _exam_mode_buffer_end = .; - } >FLASH - - /* External flash memory */ - .text ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) + SIZEOF(.exam_mode_buffer) : { - . = ALIGN(4); - *(.text) - *(.text.*) - } >FLASH - - .rodata : { - *(.rodata) - *(.rodata.*) - } >FLASH - - .init_array : { - . = ALIGN(4); - _init_array_start = .; - KEEP (*(.init_array*)) - _init_array_end = .; - } >FLASH - - .data : { - /* The data section is written to Flash but linked as if it were in RAM. - * - * This is required because its initial value matters (so it has to be in - * persistant memory in the first place), but it is a R/W area of memory - * so it will have to live in RAM upon execution (in linker lingo, that - * translates to the data section having a LMA in Flash and a VMA in RAM). - * - * This means we'll have to copy it from Flash to RAM on initialization. - * To do this, we'll need to know the source location of the data section - * (in Flash), the target location (in RAM), and the size of the section. - * That's why we're defining three symbols that we'll use in the initial- - * -ization routine. */ - . = ALIGN(4); - _data_section_start_flash = LOADADDR(.data); - _data_section_start_ram = .; - *(.data) - *(.data.*) - _data_section_end_ram = .; - } >SRAM AT> FLASH - - .bss : { - /* The bss section contains data for all uninitialized variables - * So like the .data section, it will go in RAM, but unlike the data section - * we don't care at all about an initial value. - * - * Before execution, crt0 will erase that section of memory though, so we'll - * need pointers to the beginning and end of this section. */ - . = ALIGN(4); - _bss_section_start_ram = .; - *(.bss) - *(.bss.*) - /* The compiler may choose to allocate uninitialized global variables as - * COMMON blocks. This can be disabled with -fno-common if needed. */ - *(COMMON) - _bss_section_end_ram = .; - } >SRAM - - .heap : { - _heap_start = .; - /* Note: We don't increment "." here, we set it. */ - . = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE); - _heap_end = .; - } >SRAM - - .stack : { - . = ALIGN(8); - _stack_end = .; - . += (STACK_SIZE - 8); - . = ALIGN(8); - _stack_start = .; - } >SRAM - - /DISCARD/ : { - /* exidx and extab are needed for unwinding, which we don't use */ - *(.ARM.exidx*) - *(.ARM.extab*) - } -} +INCLUDE ion/src/device/bootloader/bootloader_common.ld; diff --git a/ion/src/device/bootloader/bootloader.B.ld b/ion/src/device/bootloader/bootloader.B.ld index 25cbd2b22..f89ebca2f 100644 --- a/ion/src/device/bootloader/bootloader.B.ld +++ b/ion/src/device/bootloader/bootloader.B.ld @@ -23,122 +23,6 @@ MEMORY { STACK_SIZE = 32K; FIRST_FLASH_SECTOR_SIZE = 4K; SIGNED_PAYLOAD_LENGTH = 8; +USERLAND_OFFSET = 64K; -SECTIONS { - .signed_payload_prefix ORIGIN(FLASH) : { - FILL(0xFF); - BYTE(0xFF) - . = ORIGIN(FLASH) + SIGNED_PAYLOAD_LENGTH; - } >FLASH - - .header : { - KEEP(*(.header)) - } >FLASH - - .isr_vector_table : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header)) { - /* 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 - * entry points to interrupt service routines. This data is called the ISR - * vector table. - * - * Note that address 0x0 is always an alias. It points to the beginning of - * Flash, SRAM, or integrated bootloader depending on the boot mode chosen. - * (This mode is chosen by setting the BOOTn pins on the chip). - * - * We're generating the ISR vector table in code because it's very - * convenient: using function pointers, we can easily point to the service - * routine for each interrupt. */ - _isr_vector_table_start_flash = LOADADDR(.isr_vector_table); - _isr_vector_table_start_ram = .; - KEEP(*(.isr_vector_table)) - _isr_vector_table_end_ram = .; - } >SRAM - - .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) : { - . = ALIGN(4K); - _exam_mode_buffer_start = .; - KEEP(*(.exam_mode_buffer)) - /* Note: We don't increment "." here, we set it. */ - . = . + FIRST_FLASH_SECTOR_SIZE; - _exam_mode_buffer_end = .; - } >FLASH - - /* External flash memory */ - .text ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.header) + SIZEOF(.isr_vector_table) + SIZEOF(.exam_mode_buffer) : { - . = ALIGN(4); - *(.text) - *(.text.*) - } >FLASH - - .rodata : { - *(.rodata) - *(.rodata.*) - } >FLASH - - .init_array : { - . = ALIGN(4); - _init_array_start = .; - KEEP (*(.init_array*)) - _init_array_end = .; - } >FLASH - - .data : { - /* The data section is written to Flash but linked as if it were in RAM. - * - * This is required because its initial value matters (so it has to be in - * persistant memory in the first place), but it is a R/W area of memory - * so it will have to live in RAM upon execution (in linker lingo, that - * translates to the data section having a LMA in Flash and a VMA in RAM). - * - * This means we'll have to copy it from Flash to RAM on initialization. - * To do this, we'll need to know the source location of the data section - * (in Flash), the target location (in RAM), and the size of the section. - * That's why we're defining three symbols that we'll use in the initial- - * -ization routine. */ - . = ALIGN(4); - _data_section_start_flash = LOADADDR(.data); - _data_section_start_ram = .; - *(.data) - *(.data.*) - _data_section_end_ram = .; - } >SRAM AT> FLASH - - .bss : { - /* The bss section contains data for all uninitialized variables - * So like the .data section, it will go in RAM, but unlike the data section - * we don't care at all about an initial value. - * - * Before execution, crt0 will erase that section of memory though, so we'll - * need pointers to the beginning and end of this section. */ - . = ALIGN(4); - _bss_section_start_ram = .; - *(.bss) - *(.bss.*) - /* The compiler may choose to allocate uninitialized global variables as - * COMMON blocks. This can be disabled with -fno-common if needed. */ - *(COMMON) - _bss_section_end_ram = .; - } >SRAM - - .heap : { - _heap_start = .; - /* Note: We don't increment "." here, we set it. */ - . = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE); - _heap_end = .; - } >SRAM - - .stack : { - . = ALIGN(8); - _stack_end = .; - . += (STACK_SIZE - 8); - . = ALIGN(8); - _stack_start = .; - } >SRAM - - /DISCARD/ : { - /* exidx and extab are needed for unwinding, which we don't use */ - *(.ARM.exidx*) - *(.ARM.extab*) - } -} +INCLUDE ion/src/device/bootloader/bootloader_common.ld; diff --git a/ion/src/device/bootloader/bootloader_common.ld b/ion/src/device/bootloader/bootloader_common.ld new file mode 100644 index 000000000..e8f0c9bbc --- /dev/null +++ b/ion/src/device/bootloader/bootloader_common.ld @@ -0,0 +1,128 @@ + +SECTIONS { + .signed_payload_prefix ORIGIN(FLASH) : { + FILL(0xFF); + BYTE(0xFF) + . = ORIGIN(FLASH) + SIGNED_PAYLOAD_LENGTH; + } >FLASH + + .kernel_header : { + KEEP(*(.kernel_header)) + } >FLASH + + .slot_info : { + *(.slot_info*) + } >SRAM + + .isr_vector_table ORIGIN(SRAM) + 512 : AT(ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.kernel_header)) { + /* 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 + * entry points to interrupt service routines. This data is called the ISR + * vector table. + * + * Note that address 0x0 is always an alias. It points to the beginning of + * Flash, SRAM, or integrated bootloader depending on the boot mode chosen. + * (This mode is chosen by setting the BOOTn pins on the chip). + * + * We're generating the ISR vector table in code because it's very + * convenient: using function pointers, we can easily point to the service + * routine for each interrupt. */ + _isr_vector_table_start_flash = LOADADDR(.isr_vector_table); + _isr_vector_table_start_ram = .; + KEEP(*(.isr_vector_table)) + _isr_vector_table_end_ram = .; + } >SRAM + + .exam_mode_buffer ORIGIN(FLASH) + SIZEOF(.signed_payload_prefix) + SIZEOF(.kernel_header) + SIZEOF(.isr_vector_table) : { + . = ALIGN(4K); + _exam_mode_buffer_start = .; + KEEP(*(.exam_mode_buffer)) + /* Note: We don't increment "." here, we set it. */ + . = . + FIRST_FLASH_SECTOR_SIZE; + _exam_mode_buffer_end = .; + } >FLASH + + /* External flash memory */ + .userland_header : { + . = ORIGIN(FLASH) + USERLAND_OFFSET; + KEEP(*(.userland_header)); + } > FLASH + + .text : { + . = ALIGN(4); + *(.text) + *(.text.*) + } >FLASH + + .rodata : { + *(.rodata) + *(.rodata.*) + } >FLASH + + .init_array : { + . = ALIGN(4); + _init_array_start = .; + KEEP (*(.init_array*)) + _init_array_end = .; + } >FLASH + + .data : { + /* The data section is written to Flash but linked as if it were in RAM. + * + * This is required because its initial value matters (so it has to be in + * persistant memory in the first place), but it is a R/W area of memory + * so it will have to live in RAM upon execution (in linker lingo, that + * translates to the data section having a LMA in Flash and a VMA in RAM). + * + * This means we'll have to copy it from Flash to RAM on initialization. + * To do this, we'll need to know the source location of the data section + * (in Flash), the target location (in RAM), and the size of the section. + * That's why we're defining three symbols that we'll use in the initial- + * -ization routine. */ + . = ALIGN(4); + _data_section_start_flash = LOADADDR(.data); + _data_section_start_ram = .; + *(.data) + *(.data.*) + _data_section_end_ram = .; + } >SRAM AT> FLASH + + .bss : { + /* The bss section contains data for all uninitialized variables + * So like the .data section, it will go in RAM, but unlike the data section + * we don't care at all about an initial value. + * + * Before execution, crt0 will erase that section of memory though, so we'll + * need pointers to the beginning and end of this section. */ + . = ALIGN(4); + _bss_section_start_ram = .; + *(.bss) + *(.bss.*) + /* The compiler may choose to allocate uninitialized global variables as + * COMMON blocks. This can be disabled with -fno-common if needed. */ + *(COMMON) + _bss_section_end_ram = .; + } >SRAM + + .heap : { + _heap_start = .; + /* Note: We don't increment "." here, we set it. */ + . = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE); + _heap_end = .; + } >SRAM + + .stack : { + . = ALIGN(8); + _stack_end = .; + . += (STACK_SIZE - 8); + . = ALIGN(8); + _stack_start = .; + } >SRAM + + /DISCARD/ : { + /* exidx and extab are needed for unwinding, which we don't use */ + *(.ARM.exidx*) + *(.ARM.extab*) + } +} diff --git a/ion/src/device/bootloader/drivers/board.cpp b/ion/src/device/bootloader/drivers/board.cpp index 3db56ee2a..03e47bc2c 100644 --- a/ion/src/device/bootloader/drivers/board.cpp +++ b/ion/src/device/bootloader/drivers/board.cpp @@ -106,7 +106,20 @@ void initMPU() { MPU.RASR()->setB(0); MPU.RASR()->setENABLE(true); - // 2.3 Enable MPU + /* 2.3 Empty sector + * We have to override the sectors configured by the bootloader. */ + while(sector < 8) { + MPU.RNR()->setREGION(sector++); + MPU.RBAR()->setADDR(0); + MPU.RASR()->setENABLE(0); + } + + /* We assert that all sectors have been initialized. Otherwise, the bootloader + * configuration is still active on the last sectors when their configuration + * should be reset. */ + assert(sector == 8); + + // 2.4 Enable MPU MPU.CTRL()->setPRIVDEFENA(true); MPU.CTRL()->setENABLE(true); diff --git a/ion/src/device/bootloader/platform_info.cpp b/ion/src/device/bootloader/platform_info.cpp index 4ca5052cc..c1f83e36a 100644 --- a/ion/src/device/bootloader/platform_info.cpp +++ b/ion/src/device/bootloader/platform_info.cpp @@ -13,18 +13,14 @@ #error This file expects OMEGA_VERSION to be defined #endif -#ifndef HEADER_SECTION -#define HEADER_SECTION -#endif - namespace Ion { extern char staticStorageArea[]; } constexpr void * storageAddress = &(Ion::staticStorageArea); -class PlatformInfo { +class KernelHeader { public: - constexpr PlatformInfo() : + constexpr KernelHeader() : m_header(Magic), m_version{EPSILON_VERSION}, m_patchLevel{PATCH_LEVEL}, @@ -41,34 +37,120 @@ public: } private: constexpr static uint32_t Magic = 0xDEC00DF0; - constexpr static uint32_t OmegaMagic = 0xEFBEADDE; uint32_t m_header; const char m_version[8]; const char m_patchLevel[8]; uint32_t m_footer; }; -const PlatformInfo HEADER_SECTION platform_infos; +const KernelHeader __attribute__((section(".kernel_header"), used)) k_kernelHeader; -const char * Ion::softwareVersion() { - return platform_infos.version(); -} - -static const char s_omegaVersion[16] = {OMEGA_VERSION}; +class UserlandHeader { +public: + constexpr UserlandHeader(): + m_header(Magic), + m_expectedEpsilonVersion{EPSILON_VERSION}, + m_storageAddressRAM(storageAddress), + m_storageSizeRAM(Ion::Storage::k_storageSize), + m_externalAppsFlashStart(0xFFFFFFFF), + m_externalAppsFlashEnd(0xFFFFFFFF), + m_externalAppsRAMStart(0xFFFFFFFF), + m_externalAppsRAMEnd(0xFFFFFFFF), + m_footer(Magic), + m_ohm_header(OmegaMagic), + m_omegaVersion{OMEGA_VERSION}, #ifdef OMEGA_USERNAME - static const char s_username[16] = {OMEGA_USERNAME}; + m_username{OMEGA_USERNAME}, #else - static const char s_username[16] = {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}; + m_username{"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, #endif + m_ohm_footer(OmegaMagic) { } + + const char * omegaVersion() const { + assert(m_storageAddressRAM != nullptr); + assert(m_storageSizeRAM != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_omegaVersion; + } + const volatile char * username() const volatile { + assert(m_storageAddressRAM != nullptr); + assert(m_storageSizeRAM != 0); + assert(m_header == Magic); + assert(m_footer == Magic); + assert(m_ohm_header == OmegaMagic); + assert(m_ohm_footer == OmegaMagic); + return m_username; + } + +private: + constexpr static uint32_t Magic = 0xDEC0EDFE; + constexpr static uint32_t OmegaMagic = 0xEFBEADDE; + uint32_t m_header; + const char m_expectedEpsilonVersion[8]; + void * m_storageAddressRAM; + size_t m_storageSizeRAM; + /* We store the range addresses of external apps memory because storing the + * size is complicated due to c++11 constexpr. */ + uint32_t m_externalAppsFlashStart; + uint32_t m_externalAppsFlashEnd; + uint32_t m_externalAppsRAMStart; + uint32_t m_externalAppsRAMEnd; + uint32_t m_footer; + uint32_t m_ohm_header; + const char m_omegaVersion[16]; + const volatile char m_username[16]; + uint32_t m_ohm_footer; +}; + +const UserlandHeader __attribute__((section(".userland_header"), used)) k_userlandHeader; + +class SlotInfo { + +public: + SlotInfo() : + m_header(Magic), + m_footer(Magic) {} + + void update() { + m_header = Magic; + m_kernelHeaderAddress = &k_kernelHeader; + m_userlandHeaderAddress = &k_userlandHeader; + m_footer = Magic; + } + +private: + constexpr static uint32_t Magic = 0xEFEEDBBA; + uint32_t m_header; + const KernelHeader * m_kernelHeaderAddress; + const UserlandHeader * m_userlandHeaderAddress; + uint32_t m_footer; + +}; const char * Ion::omegaVersion() { - return s_omegaVersion; + return k_userlandHeader.omegaVersion(); } const volatile char * Ion::username() { - return s_username; + return k_userlandHeader.username(); +} + +const char * Ion::softwareVersion() { + return k_kernelHeader.version(); } const char * Ion::patchLevel() { - return platform_infos.patchLevel(); + return k_kernelHeader.patchLevel(); +} + +SlotInfo * slotInfo() { + static SlotInfo __attribute__((used)) __attribute__((section(".slot_info"))) slotInformation; + return &slotInformation; +} + +void Ion::updateSlotInfo() { + slotInfo()->update(); } diff --git a/ion/src/device/n0100/platform_info.cpp b/ion/src/device/n0100/platform_info.cpp index cdf676a0c..90d205352 100644 --- a/ion/src/device/n0100/platform_info.cpp +++ b/ion/src/device/n0100/platform_info.cpp @@ -107,3 +107,7 @@ const volatile char * Ion::username() { const char * Ion::patchLevel() { return platform_infos.patchLevel(); } + +void Ion::updateSlotInfo() { + +} \ No newline at end of file diff --git a/ion/src/device/n0110/platform_info.cpp b/ion/src/device/n0110/platform_info.cpp index cdf676a0c..90d205352 100644 --- a/ion/src/device/n0110/platform_info.cpp +++ b/ion/src/device/n0110/platform_info.cpp @@ -107,3 +107,7 @@ const volatile char * Ion::username() { const char * Ion::patchLevel() { return platform_infos.patchLevel(); } + +void Ion::updateSlotInfo() { + +} \ No newline at end of file diff --git a/ion/src/device/shared/usb/dfu_relocated.cpp b/ion/src/device/shared/usb/dfu_relocated.cpp index 82868a88c..fb001cef1 100644 --- a/ion/src/device/shared/usb/dfu_relocated.cpp +++ b/ion/src/device/shared/usb/dfu_relocated.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,6 +15,7 @@ namespace USB { typedef void (*PollFunctionPointer)(bool exitWithKeyboard); void DFU(bool exitWithKeyboard) { + Ion::updateSlotInfo(); /* DFU transfers can serve two purposes: * - Transfering RAM data between the machine and a host, e.g. Python scripts diff --git a/ion/src/device/shared/usb/dfu_xip.cpp b/ion/src/device/shared/usb/dfu_xip.cpp index 753948f73..fd0447c32 100644 --- a/ion/src/device/shared/usb/dfu_xip.cpp +++ b/ion/src/device/shared/usb/dfu_xip.cpp @@ -1,9 +1,11 @@ +#include #include "calculator.h" namespace Ion { namespace USB { void DFU(bool exitWithKeyboard) { + Ion::updateSlotInfo(); Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard); } From 377f4eee0c5d7f421e7a4821e9f5422412428e99 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 26 Feb 2022 19:33:32 +0100 Subject: [PATCH 17/37] [ci] Build bootloader --- .github/workflows/ci-workflow.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index ebd27df3e..7f8c48321 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -125,6 +125,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: 'recursive' + - run: make -j2 bootloader.dfu - run: make MODEL=bootloader -j2 epsilon.A.dfu epsilon.B.dfu - run: make MODEL=bootloader -j2 epsilon.onboarding.A.dfu epsilon.onboarding.B.dfu - run: make MODEL=bootloader -j2 epsilon.onboarding.update.A.dfu epsilon.onboarding.update.B.dfu From 63c2b9926027ebf7267b3a9f9aaf7da556c231a2 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 26 Feb 2022 19:52:54 +0100 Subject: [PATCH 18/37] [ion/simulator] Fixed simulator --- ion/Makefile | 2 +- ion/src/shared/dummy/usb.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ion/Makefile b/ion/Makefile index da9057572..8bcef599e 100644 --- a/ion/Makefile +++ b/ion/Makefile @@ -22,7 +22,7 @@ include ion/src/shared/tools/Makefile # char test[4]= "ab"; is valid and should initialize test to 'a','b',0,0). # Older versions of GCC are not conformant so we resort to an initializer list. initializer_list = $(shell echo $(1) | sed "s/\(.\)/'\1',/g")0 -$(call object_for,ion/src/simulator/platform_info.cpp ion/src/device/n0100/platform_info.cpp ion/src/device/n0110/platform_info.cpp ion/src/device/bootloader/platform_info.cpp): SFLAGS += -DPATCH_LEVEL="$(call initializer_list,$(PATCH_LEVEL))" -DEPSILON_VERSION="$(call initializer_list,$(EPSILON_VERSION))" -DOMEGA_VERSION="$(call initializer_list,$(OMEGA_VERSION))" -DOMEGA_USERNAME="$(call initializer_list,$(OMEGA_USERNAME))" +$(call object_for,ion/src/simulator/platform_info.cpp ion/src/device/n0100/platform_info.cpp ion/src/device/n0110/platform_info.cpp ion/src/device/bootloader/platform_info.cpp ion/src/simulator/shared/platform_info.cpp): SFLAGS += -DPATCH_LEVEL="$(call initializer_list,$(PATCH_LEVEL))" -DEPSILON_VERSION="$(call initializer_list,$(EPSILON_VERSION))" -DOMEGA_VERSION="$(call initializer_list,$(OMEGA_VERSION))" -DOMEGA_USERNAME="$(call initializer_list,$(OMEGA_USERNAME))" ion_src += $(addprefix ion/src/shared/, \ console_line.cpp \ diff --git a/ion/src/shared/dummy/usb.cpp b/ion/src/shared/dummy/usb.cpp index ce3c0fd2c..dfe12752e 100644 --- a/ion/src/shared/dummy/usb.cpp +++ b/ion/src/shared/dummy/usb.cpp @@ -1,3 +1,4 @@ +#include #include namespace Ion { @@ -25,3 +26,7 @@ void disable() { } } + +void Ion::updateSlotInfo() { + +} From 85ef57f7ed55698f1db234c4028b3bcb0cb40095 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sat, 26 Feb 2022 20:20:13 +0100 Subject: [PATCH 19/37] [bootloader] Added userland header, rewrote lernel header --- bootloader/Makefile | 3 +- bootloader/kernel_header.cpp | 29 ++++++++++++++++++ bootloader/{slot.h => kernel_header.h} | 12 ++++---- bootloader/main.cpp | 4 +-- bootloader/slot.cpp | 29 ------------------ bootloader/userland_header.cpp | 20 ++++++++++++ bootloader/userland_header.h | 42 ++++++++++++++++++++++++++ 7 files changed, 101 insertions(+), 38 deletions(-) create mode 100644 bootloader/kernel_header.cpp rename bootloader/{slot.h => kernel_header.h} (71%) delete mode 100644 bootloader/slot.cpp create mode 100644 bootloader/userland_header.cpp create mode 100644 bootloader/userland_header.h diff --git a/bootloader/Makefile b/bootloader/Makefile index 8a4a0b0c6..c59461600 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -1,7 +1,8 @@ bootloader_src += $(addprefix bootloader/,\ main.cpp \ - slot.cpp \ + kernel_header.cpp \ + userland_header.cpp \ interface.cpp \ jump_to_firmware.s \ trampoline.cpp \ diff --git a/bootloader/kernel_header.cpp b/bootloader/kernel_header.cpp new file mode 100644 index 000000000..9c771dd8f --- /dev/null +++ b/bootloader/kernel_header.cpp @@ -0,0 +1,29 @@ +#include + +extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void)); + +namespace Bootloader { + + +const KernelHeader* s_kernelHeaderA = reinterpret_cast(0x90000000); +const KernelHeader* s_kernelHeaderB = reinterpret_cast(0x90400000); + +const char * KernelHeader::version() const { + return m_version; +} + +const char * KernelHeader::patchLevel() const { + return m_patchLevel; +} + +const bool KernelHeader::isValid() const { + return m_header == Magic && m_footer == Magic; +} + +[[ noreturn ]] void KernelHeader::boot() const { + jump_to_firmware(m_stackPointer, m_startPointer); + for(;;); +} + + +} diff --git a/bootloader/slot.h b/bootloader/kernel_header.h similarity index 71% rename from bootloader/slot.h rename to bootloader/kernel_header.h index 4bb7de8fd..32d47347d 100644 --- a/bootloader/slot.h +++ b/bootloader/kernel_header.h @@ -1,11 +1,11 @@ -#ifndef BOOTLOADER_SLOT -#define BOOTLOADER_SLOT +#ifndef BOOTLOADER_KERNEL_HEADER_H +#define BOOTLOADER_KERNEL_HEADER_H #include namespace Bootloader { -class Slot { +class KernelHeader { public: const char * version() const; const char * patchLevel() const; @@ -13,7 +13,7 @@ public: [[ noreturn ]] void boot() const; private: - Slot(); + KernelHeader(); constexpr static uint32_t Magic = 0xDEC00DF0; const uint32_t m_unknown; const uint32_t m_signature; @@ -25,8 +25,8 @@ private: const void(*m_startPointer)(); }; -extern const Slot* s_slotA; -extern const Slot* s_slotB; +extern const KernelHeader* s_kernelHeaderA; +extern const KernelHeader* s_kernelHeaderB; } diff --git a/bootloader/main.cpp b/bootloader/main.cpp index 6f4e54237..b6d0fffb9 100644 --- a/bootloader/main.cpp +++ b/bootloader/main.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include @@ -25,5 +25,5 @@ void ion_main(int argc, const char * const argv[]) { } Ion::Device::Board::bootloaderMPU(); - Bootloader::s_slotA->boot(); + Bootloader::s_kernelHeaderA->boot(); } diff --git a/bootloader/slot.cpp b/bootloader/slot.cpp deleted file mode 100644 index 3df80ac87..000000000 --- a/bootloader/slot.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include - -extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void)); - -namespace Bootloader { - - -const Slot* s_slotA = reinterpret_cast(0x90000000); -const Slot* s_slotB = reinterpret_cast(0x90400000); - -const char * Slot::version() const { - return m_version; -} - -const char * Slot::patchLevel() const { - return m_patchLevel; -} - -const bool Slot::isValid() const { - return m_header == Magic && m_footer == Magic; -} - -[[ noreturn ]] void Slot::boot() const { - jump_to_firmware(m_stackPointer, m_startPointer); - for(;;); -} - - -} diff --git a/bootloader/userland_header.cpp b/bootloader/userland_header.cpp new file mode 100644 index 000000000..36963b589 --- /dev/null +++ b/bootloader/userland_header.cpp @@ -0,0 +1,20 @@ +#include + +namespace Bootloader { + +const UserlandHeader* s_UserlandHeaderA = reinterpret_cast(0x90010000); +const UserlandHeader* s_UserlandHeaderB = reinterpret_cast(0x90410000); + +const char * UserlandHeader::version() const { + return m_expectedEpsilonVersion; +} + +const bool UserlandHeader::isValid() const { + return m_header == Magic && m_footer == Magic; +} + +const bool UserlandHeader::isOmega() const { + return m_ohm_header == OmegaMagic && m_ohm_footer == OmegaMagic; +} + +} diff --git a/bootloader/userland_header.h b/bootloader/userland_header.h new file mode 100644 index 000000000..6dc1a6c02 --- /dev/null +++ b/bootloader/userland_header.h @@ -0,0 +1,42 @@ +#ifndef BOOTLOADER_USERLAND_HEADER_H +#define BOOTLOADER_USERLAND_HEADER_H + +#include +#include +#include + +namespace Bootloader { + +class UserlandHeader { +public: + const char * version() const; + const bool isValid() const; + const bool isOmega() const; + +private: + UserlandHeader(); + constexpr static uint32_t Magic = 0xDEC0EDFE; + constexpr static uint32_t OmegaMagic = 0xEFBEADDE; + uint32_t m_header; + const char m_expectedEpsilonVersion[8]; + void * m_storageAddressRAM; + size_t m_storageSizeRAM; + /* We store the range addresses of external apps memory because storing the + * size is complicated due to c++11 constexpr. */ + uint32_t m_externalAppsFlashStart; + uint32_t m_externalAppsFlashEnd; + uint32_t m_externalAppsRAMStart; + uint32_t m_externalAppsRAMEnd; + uint32_t m_footer; + uint32_t m_ohm_header; + const char m_omegaVersion[16]; + const volatile char m_username[16]; + uint32_t m_ohm_footer; +}; + +extern const UserlandHeader* s_userlandHeaderA; +extern const UserlandHeader* s_userlandHeaderB; + +} + +#endif From 5a7b076ebe9245c72e9062bab3bcdc8e3fd0a975 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 27 Feb 2022 00:05:30 +0100 Subject: [PATCH 20/37] [bootloader] Added dual boot --- bootloader/Makefile | 2 + bootloader/boot.cpp | 52 ++++++++++++++++++++++++++ bootloader/boot.h | 28 ++++++++++++++ bootloader/interface.cpp | 34 ++++++++++++++++- bootloader/kernel_header.cpp | 14 +++---- bootloader/kernel_header.h | 7 ++-- bootloader/main.cpp | 28 +++++++++----- bootloader/slot.cpp | 28 ++++++++++++++ bootloader/slot.h | 33 ++++++++++++++++ bootloader/trampoline.cpp | 2 +- bootloader/userland_header.cpp | 5 +++ bootloader/userland_header.h | 1 + ion/src/device/n0110/internal_flash.ld | 24 ++++++++---- 13 files changed, 226 insertions(+), 32 deletions(-) create mode 100644 bootloader/boot.cpp create mode 100644 bootloader/boot.h create mode 100644 bootloader/slot.cpp create mode 100644 bootloader/slot.h diff --git a/bootloader/Makefile b/bootloader/Makefile index c59461600..4a3209618 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -1,8 +1,10 @@ bootloader_src += $(addprefix bootloader/,\ + boot.cpp \ main.cpp \ kernel_header.cpp \ userland_header.cpp \ + slot.cpp \ interface.cpp \ jump_to_firmware.s \ trampoline.cpp \ diff --git a/bootloader/boot.cpp b/bootloader/boot.cpp new file mode 100644 index 000000000..268ee125b --- /dev/null +++ b/bootloader/boot.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include + +namespace Bootloader { + +BootMode Boot::mode() { + // We use the exam mode driver as storage for the boot mode + uint8_t mode = Ion::ExamMode::FetchExamMode(); + + if (mode > 3) + return Unknown; + + return (BootMode) mode; +} + +void Boot::setMode(BootMode mode) { + BootMode currentMode = Boot::mode(); + if (currentMode == mode) + return; + + assert(mode != BootMode::Unknown); + int8_t deltaMode = (int8_t)mode - (int8_t)currentMode; + deltaMode = deltaMode < 0 ? deltaMode + 4 : deltaMode; + assert(deltaMode > 0); + Ion::ExamMode::IncrementExamMode(deltaMode); +} + +[[ noreturn ]] void Boot::boot() { + assert(mode() != BootMode::Unknown); + + if (mode() == BootMode::SlotA) + Slot::A().boot(); + else if (mode() == BootMode::SlotB) + Slot::B().boot(); + + for(;;); +} + +[[ noreturn ]] void Boot::bootloader() { + Bootloader::Interface::draw(); + for(;;) { + Ion::USB::enable(); + while (!Ion::USB::isEnumerated()); + Ion::USB::DFU(false); + } +} + +} diff --git a/bootloader/boot.h b/bootloader/boot.h new file mode 100644 index 000000000..23967f87c --- /dev/null +++ b/bootloader/boot.h @@ -0,0 +1,28 @@ +#ifndef BOOTLOADER_BOOT_H +#define BOOTLOADER_BOOT_H + +#include + +namespace Bootloader { + +enum BootMode: uint8_t { + SlotA = 0, + SlotB = 1, + // These modes exists so that you can launch the bootloader from a running slot + // They mean "Launch bootloader then go back to slot X" + SlotABootloader = 2, + SlotBBootloader = 3, + Unknown +}; + +class Boot { +public: + static BootMode mode(); + static void setMode(BootMode mode); + [[ noreturn ]] static void boot(); + [[ norteurn ]] static void bootloader(); +}; + +} + +#endif \ No newline at end of file diff --git a/bootloader/interface.cpp b/bootloader/interface.cpp index 3bab7e3d8..7146e5b93 100644 --- a/bootloader/interface.cpp +++ b/bootloader/interface.cpp @@ -2,7 +2,9 @@ #include #include -#include "interface.h" +#include +#include +#include #include "computer.h" #include "cable.h" @@ -42,6 +44,36 @@ void Interface::draw() { KDContext * ctx = KDIonContext::sharedContext(); drawImage(ctx, ImageStore::Computer, 70); drawImage(ctx, ImageStore::Cable, 172); + + ctx->drawString("Slot A:", KDPoint(0, 0), KDFont::SmallFont, KDColorWhite, KDColorBlack); + ctx->drawString("Slot B:", KDPoint(0, 13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + ctx->drawString("Current:", KDPoint(0, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack); + + if (Boot::mode() == BootMode::SlotA) { + ctx->drawString("Slot A", KDPoint(63, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack); + } else if (Boot::mode() == BootMode::SlotB) { + ctx->drawString("Slot B", KDPoint(63, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack); + } + + Slot slots[2] = {Slot::A(), Slot::B()}; + + for(uint8_t i = 0; i < 2; i++) { + Slot slot = slots[i]; + + if (slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid()) { + if (slot.userlandHeader()->isOmega()) { + ctx->drawString("Omega", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + ctx->drawString(slot.userlandHeader()->omegaVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + } else { + ctx->drawString("Epsilon", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + ctx->drawString(slot.userlandHeader()->version(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + } + ctx->drawString(slot.kernelHeader()->patchLevel(), KDPoint(168, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + } else { + ctx->drawString("Invalid", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + } + } + } } diff --git a/bootloader/kernel_header.cpp b/bootloader/kernel_header.cpp index 9c771dd8f..7dac97a06 100644 --- a/bootloader/kernel_header.cpp +++ b/bootloader/kernel_header.cpp @@ -1,13 +1,7 @@ #include -extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void)); - namespace Bootloader { - -const KernelHeader* s_kernelHeaderA = reinterpret_cast(0x90000000); -const KernelHeader* s_kernelHeaderB = reinterpret_cast(0x90400000); - const char * KernelHeader::version() const { return m_version; } @@ -20,10 +14,12 @@ const bool KernelHeader::isValid() const { return m_header == Magic && m_footer == Magic; } -[[ noreturn ]] void KernelHeader::boot() const { - jump_to_firmware(m_stackPointer, m_startPointer); - for(;;); +const uint32_t* KernelHeader::stackPointer() const { + return m_stackPointer; } +const void(*KernelHeader::startPointer() const)() { + return m_startPointer; +} } diff --git a/bootloader/kernel_header.h b/bootloader/kernel_header.h index 32d47347d..017036b62 100644 --- a/bootloader/kernel_header.h +++ b/bootloader/kernel_header.h @@ -10,7 +10,9 @@ public: const char * version() const; const char * patchLevel() const; const bool isValid() const; - [[ noreturn ]] void boot() const; + + const uint32_t* stackPointer() const; + const void(*startPointer() const)(); private: KernelHeader(); @@ -25,9 +27,6 @@ private: const void(*m_startPointer)(); }; -extern const KernelHeader* s_kernelHeaderA; -extern const KernelHeader* s_kernelHeaderB; - } #endif diff --git a/bootloader/main.cpp b/bootloader/main.cpp index b6d0fffb9..9f237bf0c 100644 --- a/bootloader/main.cpp +++ b/bootloader/main.cpp @@ -1,10 +1,9 @@ #include -#include #include #include -#include "interface.h" +#include void ion_main(int argc, const char * const argv[]) { // Clear the screen @@ -12,18 +11,27 @@ void ion_main(int argc, const char * const argv[]) { // Initialize the backlight Ion::Backlight::init(); + if (Bootloader::Boot::mode() == Bootloader::BootMode::Unknown) + Bootloader::Boot::setMode(Bootloader::BootMode::SlotA); + + if (Bootloader::Boot::mode() == Bootloader::BootMode::SlotABootloader) { + Bootloader::Boot::setMode(Bootloader::BootMode::SlotA); + Bootloader::Boot::bootloader(); + } else if (Bootloader::Boot::mode() == Bootloader::BootMode::SlotBBootloader) { + Bootloader::Boot::setMode(Bootloader::BootMode::SlotB); + Bootloader::Boot::bootloader(); + } + uint64_t scan = Ion::Keyboard::scan(); if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Four)) { - Bootloader::Interface::draw(); - while (true) { - Ion::USB::enable(); - while (!Ion::USB::isEnumerated()) { - } - Ion::USB::DFU(false); - } + Bootloader::Boot::bootloader(); + } else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::One)) { + Bootloader::Boot::setMode(Bootloader::BootMode::SlotA); + } else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Two)) { + Bootloader::Boot::setMode(Bootloader::BootMode::SlotB); } Ion::Device::Board::bootloaderMPU(); - Bootloader::s_kernelHeaderA->boot(); + Bootloader::Boot::boot(); } diff --git a/bootloader/slot.cpp b/bootloader/slot.cpp new file mode 100644 index 000000000..4430807a0 --- /dev/null +++ b/bootloader/slot.cpp @@ -0,0 +1,28 @@ +#include + +extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void)); + +namespace Bootloader { + +const Slot Slot::A() { + return Slot(0x90000000); +} + +const Slot Slot::B() { + return Slot(0x90400000); +} + +const KernelHeader* Slot::kernelHeader() const { + return m_kernelHeader; +} + +const UserlandHeader* Slot::userlandHeader() const { + return m_userlandHeader; +} + +[[ noreturn ]] void Slot::boot() const { + jump_to_firmware(kernelHeader()->stackPointer(), kernelHeader()->startPointer()); + for(;;); +} + +} \ No newline at end of file diff --git a/bootloader/slot.h b/bootloader/slot.h new file mode 100644 index 000000000..15a883f39 --- /dev/null +++ b/bootloader/slot.h @@ -0,0 +1,33 @@ +#ifndef BOOTLOADER_SLOT_H +#define BOOTLOADER_SLOT_H + +#include + +#include "kernel_header.h" +#include "userland_header.h" + +namespace Bootloader { + +class Slot { + +public: + Slot(uint32_t address) : + m_kernelHeader(reinterpret_cast(address)), + m_userlandHeader(reinterpret_cast(address + 64 * 1024)) { } + + const KernelHeader* kernelHeader() const; + const UserlandHeader* userlandHeader() const; + [[ noreturn ]] void boot() const; + + static const Slot A(); + static const Slot B(); + +private: + const KernelHeader* m_kernelHeader; + const UserlandHeader* m_userlandHeader; + +}; + +} + +#endif \ No newline at end of file diff --git a/bootloader/trampoline.cpp b/bootloader/trampoline.cpp index b69a79f18..5d7c541c0 100644 --- a/bootloader/trampoline.cpp +++ b/bootloader/trampoline.cpp @@ -3,7 +3,7 @@ #include #include -#include "trampoline.h" +#include namespace Bootloader { diff --git a/bootloader/userland_header.cpp b/bootloader/userland_header.cpp index 36963b589..275743a0a 100644 --- a/bootloader/userland_header.cpp +++ b/bootloader/userland_header.cpp @@ -17,4 +17,9 @@ const bool UserlandHeader::isOmega() const { return m_ohm_header == OmegaMagic && m_ohm_footer == OmegaMagic; } + +const char * UserlandHeader::omegaVersion() const { + return m_omegaVersion; +} + } diff --git a/bootloader/userland_header.h b/bootloader/userland_header.h index 6dc1a6c02..e53b3eccd 100644 --- a/bootloader/userland_header.h +++ b/bootloader/userland_header.h @@ -12,6 +12,7 @@ public: const char * version() const; const bool isValid() const; const bool isOmega() const; + const char * omegaVersion() const; private: UserlandHeader(); diff --git a/ion/src/device/n0110/internal_flash.ld b/ion/src/device/n0110/internal_flash.ld index 9eb193676..8f97d4528 100644 --- a/ion/src/device/n0110/internal_flash.ld +++ b/ion/src/device/n0110/internal_flash.ld @@ -1,12 +1,14 @@ /* Same as flash.ld but everything is linked in internal flash */ MEMORY { - INTERNAL_FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 64K + INTERNAL_FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K } STACK_SIZE = 32K; TRAMPOLINES_OFFSET = 0xE000; +FLASH_SECOND_SECTOR_OFFSET = 16K; +FLASH_SECOND_SECTOR_SIZE = 16K; SECTIONS { .isr_vector_table ORIGIN(INTERNAL_FLASH) : { @@ -30,6 +32,20 @@ SECTIONS { KEEP(*(.header)) } >INTERNAL_FLASH + .rodata : { + . = ALIGN(4); + *(.rodata) + *(.rodata.*) + } >INTERNAL_FLASH + + .exam_mode_buffer ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET : { + _exam_mode_buffer_start = .; + KEEP(*(.exam_mode_buffer)) + /* Note: We don't increment "." here, we set it. */ + . = ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET + FLASH_SECOND_SECTOR_SIZE; + _exam_mode_buffer_end = .; + } >INTERNAL_FLASH + .text : { . = ALIGN(4); *(.text) @@ -43,12 +59,6 @@ SECTIONS { _init_array_end = .; } >INTERNAL_FLASH - .rodata : { - . = ALIGN(4); - *(.rodata) - *(.rodata.*) - } >INTERNAL_FLASH - .data : { /* The data section is written to Flash but linked as if it were in RAM. * From dee783065bb6bfa8f7f6f028208c4475ef0f6ac0 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 27 Feb 2022 00:28:46 +0100 Subject: [PATCH 21/37] [bootloader] Use custom USB descriptor --- bootloader/Makefile | 3 ++- bootloader/usb_desc.cpp | 12 ++++++++++++ ion/src/device/shared/drivers/Makefile | 1 + ion/src/device/shared/drivers/usb.h | 1 + ion/src/device/shared/drivers/usb_desc.cpp | 15 +++++++++++++++ ion/src/device/shared/usb/Makefile | 1 + ion/src/device/shared/usb/calculator.h | 3 ++- 7 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 bootloader/usb_desc.cpp create mode 100644 ion/src/device/shared/drivers/usb_desc.cpp diff --git a/bootloader/Makefile b/bootloader/Makefile index 4a3209618..519a847cd 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -8,6 +8,7 @@ bootloader_src += $(addprefix bootloader/,\ interface.cpp \ jump_to_firmware.s \ trampoline.cpp \ + usb_desc.cpp \ ) bootloader_images = $(addprefix bootloader/, \ @@ -15,6 +16,6 @@ bootloader_images = $(addprefix bootloader/, \ computer.png \ ) -bootloader_src += $(ion_src) $(kandinsky_src) $(liba_src) $(libaxx_src) $(bootloader_images) +bootloader_src += $(filter-out ion/src/device/shared/drivers/usb_desc.cpp,$(ion_src)) $(kandinsky_src) $(liba_src) $(libaxx_src) $(bootloader_images) $(eval $(call depends_on_image,bootloader/interface.cpp,$(bootloader_images))) diff --git a/bootloader/usb_desc.cpp b/bootloader/usb_desc.cpp new file mode 100644 index 000000000..be4930600 --- /dev/null +++ b/bootloader/usb_desc.cpp @@ -0,0 +1,12 @@ + +namespace Ion { +namespace Device { +namespace USB { + +const char* stringDescriptor() { + return "@Flash/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg"; +} + +} +} +} diff --git a/ion/src/device/shared/drivers/Makefile b/ion/src/device/shared/drivers/Makefile index 2db043c5f..67bfb6ff5 100644 --- a/ion/src/device/shared/drivers/Makefile +++ b/ion/src/device/shared/drivers/Makefile @@ -25,5 +25,6 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ swd.cpp \ timing.cpp \ usb.cpp \ + usb_desc.cpp \ wakeup.cpp \ ) diff --git a/ion/src/device/shared/drivers/usb.h b/ion/src/device/shared/drivers/usb.h index df27513e7..598aa4e9f 100644 --- a/ion/src/device/shared/drivers/usb.h +++ b/ion/src/device/shared/drivers/usb.h @@ -12,6 +12,7 @@ void initGPIO(); void shutdownGPIO(); void initOTG(); void shutdownOTG(); +const char* stringDescriptor(); } } diff --git a/ion/src/device/shared/drivers/usb_desc.cpp b/ion/src/device/shared/drivers/usb_desc.cpp new file mode 100644 index 000000000..34ea8527a --- /dev/null +++ b/ion/src/device/shared/drivers/usb_desc.cpp @@ -0,0 +1,15 @@ +#include "usb.h" +#include +#include + +namespace Ion { +namespace Device { +namespace USB { + +const char* stringDescriptor() { + return Config::InterfaceStringDescriptor; +} + +} +} +} \ No newline at end of file diff --git a/ion/src/device/shared/usb/Makefile b/ion/src/device/shared/usb/Makefile index a79991d74..02e931b6a 100644 --- a/ion/src/device/shared/usb/Makefile +++ b/ion/src/device/shared/usb/Makefile @@ -66,6 +66,7 @@ ion_device_dfu_src += $(addprefix ion/src/device/shared/drivers/, \ swd.cpp \ timing.cpp \ usb.cpp \ + usb_desc.cpp \ wakeup.cpp \ ) diff --git a/ion/src/device/shared/usb/calculator.h b/ion/src/device/shared/usb/calculator.h index cf083860c..e5eaba3a4 100644 --- a/ion/src/device/shared/usb/calculator.h +++ b/ion/src/device/shared/usb/calculator.h @@ -3,6 +3,7 @@ #include #include +#include #include #include "dfu_interface.h" #include "stack/device.h" @@ -94,7 +95,7 @@ public: m_manufacturerStringDescriptor("NumWorks"), m_productStringDescriptor("NumWorks Calculator"), m_serialNumberStringDescriptor(serialNumber), - m_interfaceStringDescriptor(Config::InterfaceStringDescriptor), + m_interfaceStringDescriptor(stringDescriptor()), //m_interfaceStringDescriptor("@SRAM/0x20000000/01*256Ke"), /* Switch to this descriptor to use dfu-util to write in the SRAM. * FIXME Should be an alternate Interface. */ From 88b4e904c0a79ed32bb405a9168e6ddb6d02ea23 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 27 Feb 2022 14:59:34 +0100 Subject: [PATCH 22/37] [ion/external_flash] Take absolute address for WriteMemory --- ion/src/device/bootloader/drivers/external_flash.cpp | 1 + ion/src/device/bootloader/drivers/external_flash_tramp.cpp | 2 +- ion/src/device/n0110/drivers/external_flash.cpp | 1 + ion/src/device/shared/drivers/flash.cpp | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ion/src/device/bootloader/drivers/external_flash.cpp b/ion/src/device/bootloader/drivers/external_flash.cpp index 34de8b591..1c7686cd4 100644 --- a/ion/src/device/bootloader/drivers/external_flash.cpp +++ b/ion/src/device/bootloader/drivers/external_flash.cpp @@ -471,6 +471,7 @@ void __attribute__((noinline)) WriteMemory(uint8_t * destination, const uint8_t if (Config::NumberOfSectors == 0) { return; } + destination -= ExternalFlash::Config::StartAddress; unset_memory_mapped_mode(); /* Each 256-byte page of the external flash memory (contained in a previously erased area) * may be programmed in burst mode with a single Page Program instruction. diff --git a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp index 031722356..29a2bf03e 100644 --- a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp +++ b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp @@ -371,7 +371,7 @@ void MassErase() { void WriteMemory(uint8_t * destination, const uint8_t * source, size_t length) { asm("cpsid if"); - reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashWriteMemory))(destination + ExternalFlash::Config::StartAddress, source, length); + reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashWriteMemory))(destination, source, length); asm("cpsie if"); } diff --git a/ion/src/device/n0110/drivers/external_flash.cpp b/ion/src/device/n0110/drivers/external_flash.cpp index 1c9c4a1e9..612efc017 100644 --- a/ion/src/device/n0110/drivers/external_flash.cpp +++ b/ion/src/device/n0110/drivers/external_flash.cpp @@ -471,6 +471,7 @@ void __attribute__((noinline)) WriteMemory(uint8_t * destination, const uint8_t if (Config::NumberOfSectors == 0) { return; } + destination -= ExternalFlash::Config::StartAddress; unset_memory_mapped_mode(); /* Each 256-byte page of the external flash memory (contained in a previously erased area) * may be programmed in burst mode with a single Page Program instruction. diff --git a/ion/src/device/shared/drivers/flash.cpp b/ion/src/device/shared/drivers/flash.cpp index 6f569cd89..692bd4df8 100644 --- a/ion/src/device/shared/drivers/flash.cpp +++ b/ion/src/device/shared/drivers/flash.cpp @@ -46,7 +46,7 @@ void WriteMemory(uint8_t * destination, uint8_t * source, size_t length) { if (SectorAtAddress((uint32_t)destination) < InternalFlash::Config::NumberOfSectors) { InternalFlash::WriteMemory(destination, source, length); } else { - ExternalFlash::WriteMemory(destination - ExternalFlash::Config::StartAddress, source, length); + ExternalFlash::WriteMemory(destination, source, length); } } From 93099ad8085ad254ee8c0e182bb6a48bad77ae47 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 27 Feb 2022 15:00:01 +0100 Subject: [PATCH 23/37] [bootloader] Expose ExternalFlash instead of Flash --- bootloader/trampoline.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bootloader/trampoline.cpp b/bootloader/trampoline.cpp index 5d7c541c0..f4f40db13 100644 --- a/bootloader/trampoline.cpp +++ b/bootloader/trampoline.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include @@ -16,8 +16,8 @@ void* Trampolines[TRAMPOLINES_COUNT] __attribute__((used)) = { (void*) Bootloader::suspend, // Suspend - (void*) Ion::Device::Flash::EraseSector, // External erase - (void*) Ion::Device::Flash::WriteMemory, // External write + (void*) Ion::Device::ExternalFlash::EraseSector, // External erase + (void*) Ion::Device::ExternalFlash::WriteMemory, // External write (void*) memcmp, (void*) memcpy, (void*) memmove, From 1c04949336102787194692fe668554ac3a493cf4 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Sun, 27 Feb 2022 15:01:23 +0100 Subject: [PATCH 24/37] [bootloader] Added custom trampolines --- bootloader/trampoline.cpp | 11 ++++++++++- bootloader/trampoline.h | 4 +++- ion/src/device/n0110/internal_flash.ld | 6 ++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/bootloader/trampoline.cpp b/bootloader/trampoline.cpp index f4f40db13..4d1013f34 100644 --- a/bootloader/trampoline.cpp +++ b/bootloader/trampoline.cpp @@ -4,6 +4,7 @@ #include #include +#include namespace Bootloader { @@ -30,4 +31,12 @@ void* Trampolines[TRAMPOLINES_COUNT] (void*) strncmp }; -} \ No newline at end of file +void* CustomTrampolines[CUSTOM_TRAMPOLINES_COUNT] + __attribute__((section(".custom_trampolines_table"))) + __attribute__((used)) + = { + (void*) Bootloader::Boot::mode, + (void*) Bootloader::Boot::setMode +}; + +} diff --git a/bootloader/trampoline.h b/bootloader/trampoline.h index 49df37b6f..0bff7a6d2 100644 --- a/bootloader/trampoline.h +++ b/bootloader/trampoline.h @@ -4,9 +4,11 @@ namespace Bootloader { #define TRAMPOLINES_COUNT 13 - extern void* Trampolines[TRAMPOLINES_COUNT]; +#define CUSTOM_TRAMPOLINES_COUNT 2 +extern void* CustomTrampolines[CUSTOM_TRAMPOLINES_COUNT]; + } #endif \ No newline at end of file diff --git a/ion/src/device/n0110/internal_flash.ld b/ion/src/device/n0110/internal_flash.ld index 8f97d4528..924c67f7d 100644 --- a/ion/src/device/n0110/internal_flash.ld +++ b/ion/src/device/n0110/internal_flash.ld @@ -7,6 +7,7 @@ MEMORY { STACK_SIZE = 32K; TRAMPOLINES_OFFSET = 0xE000; +CUSTOM_TRAMPOLINES_OFFSET = 64K - 64; FLASH_SECOND_SECTOR_OFFSET = 16K; FLASH_SECOND_SECTOR_SIZE = 16K; @@ -85,6 +86,11 @@ SECTIONS { KEEP(*(.trampolines_table)); } > INTERNAL_FLASH + .custom_trampolines_table : { + . = ORIGIN(INTERNAL_FLASH) + CUSTOM_TRAMPOLINES_OFFSET; + KEEP(*(.custom_trampolines_table)); + } > INTERNAL_FLASH + .bss : { /* The bss section contains data for all uninitialized variables * So like the .data section, it will go in RAM, but unlike the data section From 227ca616ca54884e6dd25992cb191f80cfd9a132 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Mon, 28 Feb 2022 09:13:29 +0100 Subject: [PATCH 25/37] [bootloader] Boot other slot if a slot is invalid --- bootloader/boot.cpp | 30 +++++++++++++++++++++++------- bootloader/boot.h | 4 ++-- bootloader/main.cpp | 10 +++++++--- bootloader/slot.cpp | 7 ++++++- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/bootloader/boot.cpp b/bootloader/boot.cpp index 268ee125b..1c965d180 100644 --- a/bootloader/boot.cpp +++ b/bootloader/boot.cpp @@ -29,18 +29,34 @@ void Boot::setMode(BootMode mode) { Ion::ExamMode::IncrementExamMode(deltaMode); } -[[ noreturn ]] void Boot::boot() { +__attribute__((noreturn)) void Boot::boot() { assert(mode() != BootMode::Unknown); - if (mode() == BootMode::SlotA) - Slot::A().boot(); - else if (mode() == BootMode::SlotB) + if (!Slot::A().kernelHeader()->isValid() && !Slot::B().kernelHeader()->isValid()) { + // Bootloader if both invalid + bootloader(); + } else if (!Slot::A().kernelHeader()->isValid()) { + // If slot A is invalid and B valid, boot B + setMode(BootMode::SlotB); Slot::B().boot(); - - for(;;); + } else if (!Slot::B().kernelHeader()->isValid()) { + // If slot B is invalid and A valid, boot A + setMode(BootMode::SlotA); + Slot::A().boot(); + } else { + // Both valid, boot the selected one + if (mode() == BootMode::SlotA) { + Slot::A().boot(); + } else if (mode() == BootMode::SlotB) { + Slot::B().boot(); + } + } + + // Achivement unlocked: How Did We Get Here? + bootloader(); } -[[ noreturn ]] void Boot::bootloader() { +__attribute__ ((noreturn)) void Boot::bootloader() { Bootloader::Interface::draw(); for(;;) { Ion::USB::enable(); diff --git a/bootloader/boot.h b/bootloader/boot.h index 23967f87c..bb5df17c1 100644 --- a/bootloader/boot.h +++ b/bootloader/boot.h @@ -19,8 +19,8 @@ class Boot { public: static BootMode mode(); static void setMode(BootMode mode); - [[ noreturn ]] static void boot(); - [[ norteurn ]] static void bootloader(); + __attribute__ ((noreturn)) static void boot(); + __attribute__ ((noreturn)) static void bootloader(); }; } diff --git a/bootloader/main.cpp b/bootloader/main.cpp index 9f237bf0c..8e07dc8a1 100644 --- a/bootloader/main.cpp +++ b/bootloader/main.cpp @@ -1,19 +1,20 @@ #include #include -#include #include -void ion_main(int argc, const char * const argv[]) { +__attribute__ ((noreturn)) void ion_main(int argc, const char * const argv[]) { // Clear the screen Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorBlack); // Initialize the backlight Ion::Backlight::init(); + // Set the mode to slot A if undefined if (Bootloader::Boot::mode() == Bootloader::BootMode::Unknown) Bootloader::Boot::setMode(Bootloader::BootMode::SlotA); + // Handle rebooting to bootloader if (Bootloader::Boot::mode() == Bootloader::BootMode::SlotABootloader) { Bootloader::Boot::setMode(Bootloader::BootMode::SlotA); Bootloader::Boot::bootloader(); @@ -24,14 +25,17 @@ void ion_main(int argc, const char * const argv[]) { uint64_t scan = Ion::Keyboard::scan(); + // Reset+4 => Launch bootloader if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Four)) { Bootloader::Boot::bootloader(); + // Reset+1 => Launch slot A } else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::One)) { Bootloader::Boot::setMode(Bootloader::BootMode::SlotA); + // Reset+2 => Launch slot B } else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Two)) { Bootloader::Boot::setMode(Bootloader::BootMode::SlotB); } - Ion::Device::Board::bootloaderMPU(); + // Boot the firmware Bootloader::Boot::boot(); } diff --git a/bootloader/slot.cpp b/bootloader/slot.cpp index 4430807a0..b23655aed 100644 --- a/bootloader/slot.cpp +++ b/bootloader/slot.cpp @@ -1,4 +1,5 @@ #include +#include extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void)); @@ -21,8 +22,12 @@ const UserlandHeader* Slot::userlandHeader() const { } [[ noreturn ]] void Slot::boot() const { + // Configure the MPU for the booted firmware + Ion::Device::Board::bootloaderMPU(); + + // Jump jump_to_firmware(kernelHeader()->stackPointer(), kernelHeader()->startPointer()); for(;;); } -} \ No newline at end of file +} From 7a2721c97949b79a3a7b431af3c5c1cf1444192b Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Mon, 28 Feb 2022 09:33:11 +0100 Subject: [PATCH 26/37] [bootloader] Allow quitting the DFU stack --- bootloader/boot.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/bootloader/boot.cpp b/bootloader/boot.cpp index 1c965d180..1ec899b4a 100644 --- a/bootloader/boot.cpp +++ b/bootloader/boot.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -57,11 +58,24 @@ __attribute__((noreturn)) void Boot::boot() { } __attribute__ ((noreturn)) void Boot::bootloader() { - Bootloader::Interface::draw(); for(;;) { + // Draw the interfaces and infos + Bootloader::Interface::draw(); + + // Enable USB Ion::USB::enable(); - while (!Ion::USB::isEnumerated()); - Ion::USB::DFU(false); + + // Wait for the device to be enumerated + do { + // If we pressed back while waiting, reset. + uint64_t scan = Ion::Keyboard::scan(); + if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) { + Ion::Device::Reset::core(); + } + } while (!Ion::USB::isEnumerated()); + + // Launch the DFU stack, allowing to press Back to quit and reset + Ion::USB::DFU(true); } } From 74ef528845523ab35722ad3c6ca00585a23c8a9a Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Mon, 28 Feb 2022 09:38:21 +0100 Subject: [PATCH 27/37] [build] Added "bootloader" target --- build/targets.device.n0110.mak | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index 3199293f9..687fb7a58 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -5,7 +5,8 @@ test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(kandinsky_src) $(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_read_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_read_src)) $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src)) - +.PHONY: bootloader +bootloader: $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/bootloader.$(EXE): $(call flavored_object_for,$(bootloader_src),usbxip) $(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/src/device/n0110/internal_flash.ld From 9fda743de9c604ddb57efec5b746743cd7329867 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Mon, 28 Feb 2022 10:29:09 +0100 Subject: [PATCH 28/37] [build/bootloader] Rewrote binpack --- build/rules.mk | 4 ++-- build/targets.device.bootloader.mak | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build/rules.mk b/build/rules.mk index 050c4b805..e6faac450 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -32,13 +32,13 @@ $(eval $(call rule_for, \ $(eval $(call rule_for, \ OBJCOPY, %.hex, %.elf, \ - $$(OBJCOPY) -O ihex $$< $$@, \ + $$(OBJCOPY) -R .slot_info -O ihex $$< $$@, \ local \ )) $(eval $(call rule_for, \ OBJCOPY, %.bin, %.elf, \ - $$(OBJCOPY) -O binary $$< $$@, \ + $$(OBJCOPY) -R .slot_info -O binary $$< $$@, \ local \ )) diff --git a/build/targets.device.bootloader.mak b/build/targets.device.bootloader.mak index 3101a49ea..c3a7dda42 100644 --- a/build/targets.device.bootloader.mak +++ b/build/targets.device.bootloader.mak @@ -6,6 +6,12 @@ $$(BUILD_DIR)/epsilon.$(1).A.$$(EXE): $$(call flavored_object_for,$$(epsilon_src $$(BUILD_DIR)/epsilon.$(1).A.$$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.A.ld $$(BUILD_DIR)/epsilon.$(1).B.$$(EXE): $$(call flavored_object_for,$$(epsilon_src),$(1)) $$(BUILD_DIR)/epsilon.$(1).B.$$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.B.ld +$$(BUILD_DIR)/epsilon.$(1).bin: $$(BUILD_DIR)/epsilon.$(1).A.bin $$(BUILD_DIR)/epsilon.$(1).B.bin + @echo "COMBINE $$@" + $(Q) cat $$(BUILD_DIR)/epsilon.$(1).A.bin >> $$(BUILD_DIR)/epsilon.$(1).bin + $(Q) truncate -s 4MiB $$(BUILD_DIR)/epsilon.$(1).bin + $(Q) cat $$(BUILD_DIR)/epsilon.$(1).B.bin >> $$(BUILD_DIR)/epsilon.$(1).bin + $(Q) truncate -s 8MiB $$(BUILD_DIR)/epsilon.$(1).bin endef $(BUILD_DIR)/epsilon.A.$(EXE): $(call flavored_object_for,$(epsilon_src)) @@ -14,6 +20,7 @@ $(BUILD_DIR)/epsilon.A.$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.A $(BUILD_DIR)/epsilon.B.$(EXE): $(call flavored_object_for,$(epsilon_src)) $(BUILD_DIR)/epsilon.B.$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.B.ld +$(BUILD_DIR)/epsilon.bin: $(BUILD_DIR)/epsilon.A.bin $(BUILD_DIR)/epsilon.B.bin $(foreach flavor,$(epsilon_flavors),$(eval $(call rule_for_epsilon_flavor_bootloader,$(flavor)))) @@ -31,17 +38,10 @@ HANDY_TARGETS += epsilon.A epsilon.B $(Q) until $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11" > /dev/null 2>&1; do sleep 2;done $(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^) -.PHONY: %.two_binaries -%.two_binaries: %.elf - @echo "Building an external binary for $<" - $(Q) $(OBJCOPY) -O binary -R .slot_info $< $(basename $<).external.bin - @echo "Padding $(basename $<).external.bin" - $(Q) printf "\xFF\xFF\xFF\xFF" >> $(basename $<).external.bin - .PHONY: binpack -binpack: $(BUILD_DIR)/epsilon.onboarding.A.two_binaries $(BUILD_DIR)/epsilon.onboarding.B.two_binaries +binpack: $(BUILD_DIR)/epsilon.onboarding.bin rm -rf $(BUILD_DIR)/binpack mkdir -p $(BUILD_DIR)/binpack - cp $(BUILD_DIR)/epsilon.onboarding.A.external.bin $(BUILD_DIR)/epsilon.onboarding.B.external.bin $(BUILD_DIR)/binpack - cd $(BUILD_DIR) && for binary in epsilon.onboarding.A.external.bin epsilon.onboarding.B.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done + cp $(BUILD_DIR)/epsilon.onboarding.bin $(BUILD_DIR)/binpack + cd $(BUILD_DIR) && for binary in epsilon.onboarding.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/* From fc3941511fb53741e694531d5f67a5cf7a2d7141 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Mon, 28 Feb 2022 20:47:49 +0100 Subject: [PATCH 29/37] [build] Fixed bootloader build system --- Makefile | 8 ++++++-- build/targets.device.bootloader.mak | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 80cb6fcd0..7d107fe9f 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,14 @@ include build/toolchain.$(TOOLCHAIN).mak include build/variants.mak include build/helpers.mk -ifeq (${MODEL},$(filter ${MODEL},n0110 bootloader)) +ifeq (${MODEL},n0110) apps_list = ${EPSILON_APPS} else - apps_list = $(foreach i, ${EPSILON_APPS}, $(if $(filter external, $(i)),,$(i))) + ifeq (${MODEL},n0110) + apps_list = ${EPSILON_APPS} + else + apps_list = $(foreach i, ${EPSILON_APPS}, $(if $(filter external, $(i)),,$(i))) + endif endif ifdef FORCE_EXTERNAL diff --git a/build/targets.device.bootloader.mak b/build/targets.device.bootloader.mak index c3a7dda42..48466b778 100644 --- a/build/targets.device.bootloader.mak +++ b/build/targets.device.bootloader.mak @@ -21,6 +21,11 @@ $(BUILD_DIR)/epsilon.B.$(EXE): $(call flavored_object_for,$(epsilon_src)) $(BUILD_DIR)/epsilon.B.$(EXE): LDSCRIPT = ion/src/device/bootloader/bootloader.B.ld $(BUILD_DIR)/epsilon.bin: $(BUILD_DIR)/epsilon.A.bin $(BUILD_DIR)/epsilon.B.bin + @echo "COMBINE $@" + $(Q) cat $(BUILD_DIR)/epsilon.A.bin >> $(BUILD_DIR)/epsilon.bin + $(Q) truncate -s 4MiB $(BUILD_DIR)/epsilon.bin + $(Q) cat $(BUILD_DIR)/epsilon.B.bin >> $(BUILD_DIR)/epsilon.bin + $(Q) truncate -s 8MiB $(BUILD_DIR)/epsilon.bin $(foreach flavor,$(epsilon_flavors),$(eval $(call rule_for_epsilon_flavor_bootloader,$(flavor)))) @@ -28,6 +33,9 @@ $(foreach flavor,$(epsilon_flavors),$(eval $(call rule_for_epsilon_flavor_bootlo HANDY_TARGETS = $(foreach flavor,$(epsilon_flavors_bootloader),epsilon.$(flavor)) HANDY_TARGETS += epsilon.A epsilon.B +.PHONY: epsilon +epsilon: $(BUILD_DIR)/epsilon.onboarding.bin +.DEFAULT_GOAL := epsilon .PHONY: %_flash %_flash: $(BUILD_DIR)/%.dfu From bf8f8bcb279e0062c17c784cc7a3f276bd1507fd Mon Sep 17 00:00:00 2001 From: lolocomotive Date: Fri, 24 Dec 2021 16:58:16 +0100 Subject: [PATCH 30/37] Fix web simulator --- .../video/emscripten/SDL_emscriptenevents.c | 13 +------ .../video/emscripten/SDL_emscriptenvideo.c | 34 +++---------------- 2 files changed, 5 insertions(+), 42 deletions(-) diff --git a/ion/src/simulator/external/sdl/src/video/emscripten/SDL_emscriptenevents.c b/ion/src/simulator/external/sdl/src/video/emscripten/SDL_emscriptenevents.c index 64a399bde..66328af32 100644 --- a/ion/src/simulator/external/sdl/src/video/emscripten/SDL_emscriptenevents.c +++ b/ion/src/simulator/external/sdl/src/video/emscripten/SDL_emscriptenevents.c @@ -587,24 +587,13 @@ Emscripten_HandleResize(int eventType, const EmscriptenUiEvent *uiEvent, void *u double w = window_data->window->w; double h = window_data->window->h; - if(window_data->external_size) { - emscripten_get_element_css_size(window_data->canvas_id, &w, &h); - } - - emscripten_set_canvas_element_size(window_data->canvas_id, w * window_data->pixel_ratio, h * window_data->pixel_ratio); - - /* set_canvas_size unsets this */ - if (!window_data->external_size && window_data->pixel_ratio != 1.0f) { - emscripten_set_element_css_size(window_data->canvas_id, w, h); - } - if (force) { /* force the event to trigger, so pixel ratio changes can be handled */ window_data->window->w = 0; window_data->window->h = 0; } - SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, w, h); + SDL_SendWindowEvent(window_data->window, SDL_WINDOWEVENT_RESIZED, 320, 240); } } diff --git a/ion/src/simulator/external/sdl/src/video/emscripten/SDL_emscriptenvideo.c b/ion/src/simulator/external/sdl/src/video/emscripten/SDL_emscriptenvideo.c index e20d7a985..ad9b33cbc 100644 --- a/ion/src/simulator/external/sdl/src/video/emscripten/SDL_emscriptenvideo.c +++ b/ion/src/simulator/external/sdl/src/video/emscripten/SDL_emscriptenvideo.c @@ -197,38 +197,12 @@ Emscripten_CreateWindow(_THIS, SDL_Window * window) } wdata->canvas_id = SDL_strdup("#canvas"); + wdata->pixel_ratio = 320 / 240; - if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) { - wdata->pixel_ratio = emscripten_get_device_pixel_ratio(); - } else { - wdata->pixel_ratio = 1.0f; - } + window->w = 320; + window->h = 240; - scaled_w = SDL_floor(window->w * wdata->pixel_ratio); - scaled_h = SDL_floor(window->h * wdata->pixel_ratio); - - /* set a fake size to check if there is any CSS sizing the canvas */ - emscripten_set_canvas_element_size(wdata->canvas_id, 1, 1); - emscripten_get_element_css_size(wdata->canvas_id, &css_w, &css_h); - - wdata->external_size = SDL_floor(css_w) != 1 || SDL_floor(css_h) != 1; - - if ((window->flags & SDL_WINDOW_RESIZABLE) && wdata->external_size) { - /* external css has resized us */ - scaled_w = css_w * wdata->pixel_ratio; - scaled_h = css_h * wdata->pixel_ratio; - - SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, css_w, css_h); - } - emscripten_set_canvas_element_size(wdata->canvas_id, scaled_w, scaled_h); - - /* if the size is not being controlled by css, we need to scale down for hidpi */ - if (!wdata->external_size) { - if (wdata->pixel_ratio != 1.0f) { - /*scale canvas down*/ - emscripten_set_element_css_size(wdata->canvas_id, window->w, window->h); - } - } + emscripten_set_canvas_element_size(wdata->canvas_id, 320, 240); #if SDL_VIDEO_OPENGL_EGL if (window->flags & SDL_WINDOW_OPENGL) { From 370eb14a0445926a30d99baf98af8e5f7432d857 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Tue, 1 Mar 2022 20:21:01 +0100 Subject: [PATCH 31/37] [build] Fixed wrong model comparaison --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7d107fe9f..67f626c9a 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ include build/helpers.mk ifeq (${MODEL},n0110) apps_list = ${EPSILON_APPS} else - ifeq (${MODEL},n0110) + ifeq (${MODEL},bootloader) apps_list = ${EPSILON_APPS} else apps_list = $(foreach i, ${EPSILON_APPS}, $(if $(filter external, $(i)),,$(i))) From 209428ca9d363627c0ec87bcab8213ab2fca2a83 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Tue, 1 Mar 2022 20:30:08 +0100 Subject: [PATCH 32/37] [bootloader] Made bootloader Upsilon-aware --- bootloader/interface.cpp | 6 +++++- bootloader/userland_header.cpp | 8 ++++++++ bootloader/userland_header.h | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/bootloader/interface.cpp b/bootloader/interface.cpp index 7146e5b93..672f86b7e 100644 --- a/bootloader/interface.cpp +++ b/bootloader/interface.cpp @@ -42,6 +42,7 @@ void Interface::drawImage(KDContext* ctx, const Image* image, int offset) { void Interface::draw() { KDContext * ctx = KDIonContext::sharedContext(); + ctx->fillRect(KDRect(0,0,320,240), KDColorBlack); drawImage(ctx, ImageStore::Computer, 70); drawImage(ctx, ImageStore::Cable, 172); @@ -61,7 +62,10 @@ void Interface::draw() { Slot slot = slots[i]; if (slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid()) { - if (slot.userlandHeader()->isOmega()) { + if (slot.userlandHeader()->isOmega() && slot.userlandHeader()->isUpsilon()) { + ctx->drawString("Upsilon", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + ctx->drawString(slot.userlandHeader()->upsilonVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); + } else if (slot.userlandHeader()->isOmega()) { ctx->drawString("Omega", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); ctx->drawString(slot.userlandHeader()->omegaVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); } else { diff --git a/bootloader/userland_header.cpp b/bootloader/userland_header.cpp index 275743a0a..5108b187d 100644 --- a/bootloader/userland_header.cpp +++ b/bootloader/userland_header.cpp @@ -22,4 +22,12 @@ const char * UserlandHeader::omegaVersion() const { return m_omegaVersion; } +const bool UserlandHeader::isUpsilon() const { + return m_ups_header == UpsilonMagic && m_ups_footer == UpsilonMagic; +} + +const char * UserlandHeader::upsilonVersion() const { + return m_UpsilonVersion; +} + } diff --git a/bootloader/userland_header.h b/bootloader/userland_header.h index e53b3eccd..97f4ff5e6 100644 --- a/bootloader/userland_header.h +++ b/bootloader/userland_header.h @@ -13,11 +13,14 @@ public: const bool isValid() const; const bool isOmega() const; const char * omegaVersion() const; + const bool isUpsilon() const; + const char * upsilonVersion() const; private: UserlandHeader(); constexpr static uint32_t Magic = 0xDEC0EDFE; constexpr static uint32_t OmegaMagic = 0xEFBEADDE; + constexpr static uint32_t UpsilonMagic = 0x55707369; uint32_t m_header; const char m_expectedEpsilonVersion[8]; void * m_storageAddressRAM; @@ -33,6 +36,10 @@ private: const char m_omegaVersion[16]; const volatile char m_username[16]; uint32_t m_ohm_footer; + uint32_t m_ups_header; + const char m_UpsilonVersion[16]; + uint32_t m_osType; + uint32_t m_ups_footer; }; extern const UserlandHeader* s_userlandHeaderA; From 23936a0c63ad229ba6caf8fc2690f9c7864d9ccf Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Tue, 1 Mar 2022 20:31:38 +0100 Subject: [PATCH 33/37] [themes] Fixed building bootloader --- .../local/epsilon_dark/bootloader/cable.png | Bin 0 -> 179 bytes .../local/epsilon_dark/bootloader/computer.png | Bin 0 -> 357 bytes .../local/epsilon_light/bootloader/cable.png | Bin 0 -> 179 bytes .../local/epsilon_light/bootloader/computer.png | Bin 0 -> 357 bytes .../local/omega_dark/bootloader/cable.png | Bin 0 -> 5586 bytes .../local/omega_dark/bootloader/computer.png | Bin 0 -> 10338 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 themes/themes/local/epsilon_dark/bootloader/cable.png create mode 100644 themes/themes/local/epsilon_dark/bootloader/computer.png create mode 100644 themes/themes/local/epsilon_light/bootloader/cable.png create mode 100644 themes/themes/local/epsilon_light/bootloader/computer.png create mode 100644 themes/themes/local/omega_dark/bootloader/cable.png create mode 100644 themes/themes/local/omega_dark/bootloader/computer.png diff --git a/themes/themes/local/epsilon_dark/bootloader/cable.png b/themes/themes/local/epsilon_dark/bootloader/cable.png new file mode 100644 index 0000000000000000000000000000000000000000..ac0996ca74685d68c185ee0bbc4cc3a05cdb6aec GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^Y(VV7!2~3A?JidUsVYww$B>FS$tej5D^{!s2nb9_ zNT?`y?&$8$F3il#+`^x3V3@$52{5#SsAJA*S1~lk!zb;$B{EN4o7dwGqi2xG= YtES2ht{+eQfv#ZiboFyt=akR{0N5@#iU0rr literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/bootloader/computer.png b/themes/themes/local/epsilon_dark/bootloader/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..d30a99af2af41be5ed19fed9df7ea01d38eb3acb GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^!9c9f!2~1)3=-cmFfi(Px;TbZ%z1lDvFNaYK7}4lH07&=AltcwxxJ!ozZ6At1t`aiZjyXlCbA z&vN@|r8b}I^4L0=aSo2B8^(o^QSwf_z?GwA>L^kc)t8E*qE z?Lzwar#^Om`XhiyW<9qeLqm|(|0z@NMCHYLyMC&+;D45|Yv1WjTJwYJz4UTqj=%aE zU;F*L_~++mPA$J=slN11l}}h{tTDrbIj`ql%a5}2R%Chp=eqg(ke-W{>CvzE{%0x` XaGfW8;hGOH_!vB0{an^LB{Ts5SyhFS$tej5D^{!s2nb9_ zNT?`y?&$8$F3il#+`^x3V3@$52{5#SsAJA*S1~lk!zb;$B{EN4o7dwGqi2xG= YtES2ht{+eQfv#ZiboFyt=akR{0N5@#iU0rr literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_light/bootloader/computer.png b/themes/themes/local/epsilon_light/bootloader/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..d30a99af2af41be5ed19fed9df7ea01d38eb3acb GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^!9c9f!2~1)3=-cmFfi(Px;TbZ%z1lDvFNaYK7}4lH07&=AltcwxxJ!ozZ6At1t`aiZjyXlCbA z&vN@|r8b}I^4L0=aSo2B8^(o^QSwf_z?GwA>L^kc)t8E*qE z?Lzwar#^Om`XhiyW<9qeLqm|(|0z@NMCHYLyMC&+;D45|Yv1WjTJwYJz4UTqj=%aE zU;F*L_~++mPA$J=slN11l}}h{tTDrbIj`ql%a5}2R%Chp=eqg(ke-W{>CvzE{%0x` XaGfW8;hGOH_!vB0{an^LB{Ts5Syhk!b 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/omega_dark/bootloader/computer.png b/themes/themes/local/omega_dark/bootloader/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..0b5a578e6962685f14dc8c43866fec08c1ac2c71 GIT binary patch literal 10338 zcmeI0XH-+$*7rm2MY`00NG~argc^D;0)l`bfdmM>gr@Y4AVol>S3wj3k=_v$l`0)+ zQlv@~Y0{qPIrrRi$NPM_W4zC|lZ?Hy_gZuQ)|~$}_ZrzN&QM>2iky`k002;FX{s9G zevNU5I4LpioM&{{1OPAq@0#4g8X^6F9$xPD7#B1U8{mNkqWv-U0D%AJr`AXIc(C z%A2&^&ygNeP&~izk(z5i;*XjDADyng%bUO()L{L=dTZbLY+k4o+Zs6j}JMQyuAAc?nJc8(0kZ@KOmse21kBBezisM zmC3!^w!B9aWN-AaHa3d~>q=(hoA{Bjmi&B6_*a3>V!V%YMCX`SNm198a>`C=&w&JD z*V~^ICvl^^j9cCM1>epVC#Z1mx2nuW!>&CGpj$Lgokh7)DCT6!y*w}J&~>HFgN za{@n>mF8UJ)|y#d9-qgQH=2!~^`{X(Ix6482)aM^2v04}P64?WURzQV%+GK2vVRf( zN!a^ihHGwxrbndC$0@ImELRhzPp~tV+X*3%=Y8iPU;ERJE$7ZcvfxFd=XNU>ebZ*< zmW2fY&_irNxD&x_kLSQt?GGa}R%lbM-{w&J% zQAko`#g}Qys{bX?(~iq~o3QSMF5Bp=LXmWeosFH0l7NS$*?_~ZfqqBe*$Vk@DQ|Km zv$>TQIH$pJ5PSH_4&TK=i(Np8Yww4eSlRCInJX{9bLsmX?L8S7-S2i7End#}RQ8g~ zQlNCiOfKSwo0Dv|@`FbsGYX2?6|eI`Gia{dd-E1(7&uK&&p0vtA$hsdfAiREPm0Un zlEoDRA$l<4Hd%fSUs+-Nktc5G?$b_Gskmg_c~5QbjOnK$&*jKE`K;xaXGLtyX3Max zN}kKjA9ozGCf4taZp0X!D;)0Bx&^m%D2_Iryq>&lwvY~CIt?+~T-kIwr@4|uHOaT; zOw5A+Go!2H(yd3c#y~%4y82vB;C;ZbYA2xlnsoa;tU3D?ZGLHiwBd)d$B8nH_MiL9 z<%CQrGOLHwwQKWh>aEbl@*)yZyd1Kt9Bi?2&U?jCWgS9bzbE|l?-pF>{P;0G_5x?c zt-XB@OuRkyYUh{*!c2LVwwbi5mSe0d&S z0_eXl|C8GhwT}E@kjnDQ^i(%HHb%5t>8)$)*4X#9L~u5!-NPzbdN%k$g%y z!kilh-LnUye5>pYTugIFrk`bxMoEP7(a#C^;RYgT@roToL0*uNm+<5YKDjNjt7mV3 zJ}iKIohzQAYuSNoht2$XYiT%rziT>swnm?!zc=$_OOjWzdTibkGr!o}M(@(d=nRwF zjkWo^GRuti5%%4SLI#ZeOY1T+=CcsNg9Uv`R3(i^snJwGS@q_C$ph!I!XUG6**fbC zX{18ExeQD>jY00W#Tl+XvxLoVA6g1#&`1)gF2_ak9M!%o(&o~8Ifnmo;f+De6^kd0 z@?nu6yn3zv+@|fQQ){Gyjrz+UH~Gtwv|*Sjqa3{QqK|3nN)5BYO7kmDUBng}0Biru z1KOJfUTZgV{bL?VweHWwtqhuGd-P|dfrdPto&B6%kkj5Slxc2tUoSeEpySp&UBnmc zCR3J_wi_t8%k!z^(KSc*K-V1fr^?_JB6a%)yP+A+$SN?wvE+TmD}F3B$=%2>{VpCL z<|t=kz{aM^+@{oM&H0+5UmF@2*SD6+0d;~WEF_umJ8DCv*wxt@+8RX@^PV9rYm|xQ zy?5CPC=!mP@V6|#uOQ=sq&dnycryUzsk{Q622;q3NSY|)35c&;06q7prSPs;T3o-O zFRznj;FMnf&hKY}f|bq)wf4ymvI^(8Df(;e^mV2B2s_Ha2S(SwQjH9#MFZ$26KuMA zt`Bi?-ekKm2A-Uvkc)S~OOfS^miW$l^?N8EypmdkQ4kMaK*35!0T{&>d4M7vUo%tw zLOh=9_RR7jI`?`kzR)mv>+(1UP3AdE6rn`V?XKsBzS>}$F;uJ5yDD^M)}h*Lj8%~w zkI4M6D$wI5Ie_nTjF4qM=B;jMJ|G-#@2r?MkqD`jKsmyc3z?KBhZdx6J%eN*W^05p zUMM51S+AxcE2xvf20gu0p{*6tT3nFCxL6Lhu(?ohglOVu8I51;OUm7wz7#bgUW}35 zx*aS>k{r2&IuH_+93etQQZ}wO+pcSVD_Az%cI{x0ud^c6mlgUhXtW+v=YgcYV;ma) zofTN|)Ac3%QD zLf}L`m?$l~}z%5RK9ArjJgwcu0F`0U3hn zkChBwQZb2*ZB*JK=pyLA1!o0I;Q}gho@|E1zWpo#O~VvVEizJ5To*<>PkFc!=*GY3CcaDVEQH&GZ@ z*lw#^b@$FQOPh+&_1W2}AKY0n#5T|Q7BjR|uJ2q8*HTU>uX`&e(GiiG*yCrCj~YQ} z^!q?ilv3e*ItnYg3~YG>de5|T$=igz_w#{*V`P{jTRf3iN@BO_JAFBYQv-~^w%i+E zcZkDBfgcJ)EdudZ-%Du|qq81&(}EHRYiVEGt9&rfI=&+8RWDtI=eG44rqOy8%ip4G z8-`C?DB|d2MHF$SO4YWQ&hOv!*z1PyFh?H3Awi`k^cHUx{H-U&GE3|Z!E1!MAK=kR zp?r#^+vkOp?GI+lqR+WBs}{)_RGzN+(a*YdKdYk&zn}0Xvzj*lRXgp@GJ`VD^A-%hj410@zdz=GXmvWb|n!e*QATm*rv>5MU zieV*Z;xj^n((Uvguwf_Pq^?g-P>?CY2kI{ZGxt-+B;2i2 z=`T~KmzgXQ)#riNS>^OKkZE44oi9G5#q^Lr5Z)_v4S;ve#Wneo4&oK0ial&7l%f*5}q2z zL)d{DRWPv57O|deJ#?Kg=Kbu$^<+T3I)RZ{TfF(uT4cBJDn*{Mmcb8M%~Ca{`;Pe{ z{EHKJjSPw;bO9$T%jzA1ijOlxNoL~*%HZ5J6Q4&LuqUd}3__Y1rBs3tT@{H&8}&^5 zqC~}3bt?V)MZmkY3d;STY#QuDU>zO%#O|--y5E6qF#VZ=@I+ciI0P~t?73Exw*sk1Rv0Q=Q35%P4h4olrsE&QO5D=v1$0jU} zzZTvLhfxGr7l?%&FOWK zHp5~M#qI{jdDEeO;(%9k6$35_?<94!9#$uGDUH2jMih%@FU&hz@4sFXe&Eu1V+NuIINt z2)nV_RLi@=}i z3r3fLDh^R2PpP3-n1>XsA+?evf{iX7mAdU#H!rYlYJQrn9Zad^^I_hh$)QjePvUyNX6Vy4UaS;NNhX>` z%3*7a7!!*bF7#e#tgd;kt7a89RIoH;bj$Y|CGEOHXSbCwa?N9Q--xhW4<6v0sOE`H zaPNUi1l@~NAI;1jw^&M{{?Ltxmr|+rUgD!^s63`>QDkkV{>Z%D{vs^mQZd+`JCn+> z9VjkT5y{w5Mo+jGtP|2b7Q{1D+>uH)`tZRN(5rLe?4Y}=l?^x0KR3k2>n9ee*K~JPldk6+Gn2@6iJ_| zMs=(KH-rLM8LrAPdfpnb!_wL~b`GeFzK3x$$^q`?Y;3!crO&Fj#9KJyH?*x9J1IuX z&EdJPe$bY4a57F;Ii0dsQmWMWhTuOmR}8L;V@L63`G|ASwwc`3PKGW5BoWTn@}$#& zki6-Eup7CKpHgeS&T=0mSgd_EKrpc}H;poU4L5DqF}!h4^@+DCY2O3mD20koglrvP?9ji0FCbG-El=WF~6+Uo?GxI4`#k$zT3^y@&dT%1}kQS zYUgjXs{vjLTnFcvZqOMiyOP)eSOh9XJWUNb`BFX*xzQB~p)YvN=0BfEJ3A@d8@YU! zS@;S6%TC=Woj~xn$!q#G@l1EvsH%uQX*T-A3>F8^X?Fh*k?rfOtP$_Eb4DgGn?IJ!ajr;X$8g5u-UIa(rk) zM8L?B)he183x=tpI7mBN`wn1uQ?j3wnFkwKJ~9y&mycaIt5y=Ad3jJT}Rw@a0@=6_^I(qM- zW@H!hs!PFJutC+Gx+*f7K__iLWroX~7u3N4-!jCkDuQnM)yVTOB0;bGA|N>$RtySJ z%Qv%=#xRvRgO!PC%{78=6Z(hw;Ad96nlC04st)yUM$#6HQz5n;)_G>mzg8+dRu~rB zpn4t@U)I_S+5rdQ`*oFw?ge+9aTix!TbU@REP7tqb#{OAh{&c*VOLL8k4Drg)`tg0Ri=FhQb?g$;+p%@D^ zE4#FKwj#dx(=5uV?Zs#%Iiv50)J~Q6VUUePGKj$LYKt@;cLF6#_`-8}`8c1+8&}?j zvtM*6qyvP7J_D1xB%EIJYn$a;H03tNR~OJMK-4)4UO~)cKPP|q{Q8G!-^!c5BPE`C zu0g-zvv)b`I>ZUpq@qxeG5q(&Sm)TSjjU2V>}c7Vt2vq%ZxndyFrLWD!lXQBg6}Qc?NWULkIWFgqwkUb73q{`QWsTFU)!4ik2W2|tCc8hb6d+A~_GC{qqy zvGZ^~Dy{qRMPDpDcI{qmd|51i5%B^@8XO9ffeSWi#$D==i`vS;wtJfIB{lnn8j3^v zH=Y0_W~Ged8lHTpxNlWS3yKWxrhSxw?$a`&Yg^jn#A};V3wU(dsHjny`I?26HqIdf z`MH~|dnBu_GawJvcfVRYP+!h%m?(~`vBLlCUE`PP4rrIXjHCfO9}|UZc@0=NlXZ{5 zQ*{jckr~4IBD)^u{8h+#F0~z$He&XuQQw=8OvILqPF3JpVI;&w6*H#wD!K5yAt_|? zKxf(L{Z0I9J@`UHz~J>vI0#@-Y02vSeRwLL;vQ>#-tGv zpq|xr_s|Fxn{vd|rozs)+s@WJwErR)kZ{fG70uX#=eRvxRt#?M_LiQmjGeoy7!u`f zix%^D^}y}z0swM~{vJp>XEYXQi*~@c$@AA&>j(ZTK35Mt zy}$5o-hZlq(?i@J=^+jl1Btu3ivQii8>{AvgZvrL|LWmwg4+TYH$r>6`*_)*)qK%z z*lT}BpzQwg_wey@`JE2RP8{umcEy=`<3DM%CTU=T~@Is`5O)#+O04+hI^Lzc0Zs zJCrR54i}ZQN7;%>putE{m;_i-6bgn*qu?kg90h~^4W;GgjYYcIp?{%pa4`&yBMpX2 zNW#hQE4d{3Jj4%qTpbOzab2~Fu1Bjy8Jz=Unmp~g|tV)Y;p03 zO2I%l6c~4f1fxKrc1W0nt+XTrih|nzMxpFv)ZM*Yk+|u^xFQ|U;vQ}ezdL>jE~8|q zCC>v91O2>K;sC#~xLjmZywFIjyO)W(yNf)}F9h(H=kM|c z%Kb4Zniy}KMZm9$|Fh>QD9 z4rtu-{bxb_s~z*dOcqqy9tMGcz@ktz5{8>BC`c3zwL^-6!L}%SNt^;u*dOWrMfY~M z$NC|?&`J)tOmW%Z3g~w>K!M*<3jSv-evas0I)QM{HV7gLku(9rWI&QKP_QrvECT}Z zi2s?e_^+q>Um44Z|38|@{Wkd9GJx~@qYbyb;8rW~zm}^%HTxyw|KaD)x%fXE0f+u) zkpD{Gf8_c{uK!Ab|H}BE>iS2n|4M=X%J`q^`u`>u`M+Oy&~CVYL4LT`neQ$6C%9K3 zVp|;zRlw!1Z+>fODz1ghL(|+F0HC1%b>IOqvsrMBBv>syHIfzlt5=A)>LLVN0RSQ@ zEmb8G$GNR6S6wzA_MY~jmO8mK>W_$AW_I3C86Fi*<->Qey>(9-R2K^=+_4!TNPbHS zW|57HEP1hdj z{Vcp}KAoLiy=cD-x}+filn)J}Ag|o&vltIlac9X4T^FD|DWQ>x&B5Uz`I|Hsx>=$6 zX{uU_2W5%AhV5sRH`t7%**gmc?;&n5NU&15H=FDNYgqxR${1O3L26t2Ji1rx zU9>rk&90>?oqreBEUSoa%BTIlj1s7|0EAAgN9OS=^$*$|)RP%1o|6t?b* zTI_x5!$|qrMIJd*ik5gCwo?ZOr2}2+(W$AHAVv$(?^IS#+P{SF7F2opG5uBG9Yl;dI)zw&jFXmllZ32ZHQcg-jJcs3qYN?* za)CiPCoMoT?PHZ))#fG`Japy8lW$EicOr(d3-hYaABBj~FwTSI?V*FrPTJaU5?=_4 zemyZABui^x;5)G(b|-tv zM{c&+pT)+3as0F9<$R4p)A#%KNd=hxkEndG&X+i^{bSu1xkX`2b*ztBeLMc zu?*Rn$Fxeo`5_{-kKQiOc_Z1cjbqr=twU0tYG)@k4v!wdY28AaS?IrRJsE`8G%lq{ zdecT&?OD~%OaMdMe7}x+W?@y}dVF`4dXnB@EBuxoK@0Zr6~gMoJVeSXuId*2&TU8B z#c2}9(wOEWq|H^9*o_}*qqwQ@AsiR(lFJ7m)($l96~&q5j*k*55AIA=Q4(yXBg?JW zlq49($Ry3$uUu&OE=x%7tIwiYFX$LDI;t5IwcZRnM4mQp#?}?i^5s_`GRozs2ldY}6UB#&LQ;M`o17NkqsQ>9@{3-qy8R2yzlR4jEgK-Sy|!7%nc8}(bw0nrjChtim~YX3RE4Dkd=}73G2TkB3aa8 Vj8me+;?_!lmYTk5g|bcf{{Zqm*T(<= literal 0 HcmV?d00001 From bdfae184e9291c240e6e3409a033d04ebfb66d20 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Tue, 1 Mar 2022 20:46:31 +0100 Subject: [PATCH 34/37] [config.mak] Omega 2.0.0 --- build/config.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/config.mak b/build/config.mak index ee15ea61c..502989a91 100644 --- a/build/config.mak +++ b/build/config.mak @@ -5,7 +5,7 @@ DEBUG ?= 0 HOME_DISPLAY_EXTERNALS ?= 1 EPSILON_VERSION ?= 15.5.0 -OMEGA_VERSION ?= 1.23.0 +OMEGA_VERSION ?= 2.0.0 # OMEGA_USERNAME ?= N/A OMEGA_STATE ?= public EPSILON_APPS ?= calculation rpn graph code statistics probability solver atomic sequence regression settings external From fc252672cc39440c27db850944bab9d44c4e0fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Tue, 15 Mar 2022 22:14:08 +0100 Subject: [PATCH 35/37] [Simulator] Fix macOS compilation --- ion/src/simulator/macos/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index a640a6571..cdc8015d6 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -15,6 +15,7 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ collect_registers.cpp \ haptics.cpp \ journal.cpp \ + store_script.cpp \ ) ifeq ($(EPSILON_TELEMETRY),1) From f1c24db90a7b7843834cfc7c5cadc9978fc0f619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Tue, 15 Mar 2022 22:41:51 +0100 Subject: [PATCH 36/37] [ion/external_flash] Fix SectorAtAddress assert() --- ion/src/device/bootloader/drivers/external_flash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/device/bootloader/drivers/external_flash.cpp b/ion/src/device/bootloader/drivers/external_flash.cpp index 1c7686cd4..612efc017 100644 --- a/ion/src/device/bootloader/drivers/external_flash.cpp +++ b/ion/src/device/bootloader/drivers/external_flash.cpp @@ -405,7 +405,7 @@ int SectorAtAddress(uint32_t address) { i = address >> NumberOfAddressBitsIn32KbyteBlock; if (i >= 1) { i = Config::NumberOf4KSectors + i - 1; - assert(i >= 0 && i <= Config::NumberOf32KSectors); + assert(i >= Config::NumberOf4KSectors && i <= Config::NumberOf4KSectors + Config::NumberOf32KSectors); return i; } i = address >> NumberOfAddressBitsIn4KbyteBlock; From 8c3378a72399b1e11fa1cb138e6be03aee5bf1a8 Mon Sep 17 00:00:00 2001 From: Maxime FRIESS Date: Wed, 16 Mar 2022 00:06:24 +0100 Subject: [PATCH 37/37] [ion/bootloader] Fixed wrong trampoline call. --- ion/src/device/bootloader/drivers/external_flash_tramp.cpp | 4 ++-- ion/src/device/bootloader/drivers/power.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp index 29a2bf03e..2116c5034 100644 --- a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp +++ b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp @@ -371,13 +371,13 @@ void MassErase() { void WriteMemory(uint8_t * destination, const uint8_t * source, size_t length) { asm("cpsid if"); - reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashWriteMemory))(destination, source, length); + (*reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashWriteMemory)))(destination, source, length); asm("cpsie if"); } void EraseSector(int i) { asm("cpsid if"); - reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashEraseSector))(i); + (*reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashEraseSector)))(i); asm("cpsie if"); } diff --git a/ion/src/device/bootloader/drivers/power.cpp b/ion/src/device/bootloader/drivers/power.cpp index f38f9e978..5680bb30f 100644 --- a/ion/src/device/bootloader/drivers/power.cpp +++ b/ion/src/device/bootloader/drivers/power.cpp @@ -79,7 +79,7 @@ void __attribute__((noinline)) internalFlashStandby() { } void enterLowPowerMode() { - reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::Suspend))(); + (*reinterpret_cast(Ion::Device::Trampoline::address(Ion::Device::Trampoline::Suspend)))(); } }