diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 7f8c483217..e7e98fb4a1 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -58,7 +58,7 @@ jobs: 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: numworks/setup-arm-toolchain@2022-12 - uses: actions/checkout@v2 with: submodules: 'recursive' @@ -95,47 +95,42 @@ jobs: with: name: epsilon-binpack-n0100.tgz path: binpack-n0100.tgz - n0110: + n0110-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: numworks/setup-arm-toolchain@2022-12 - uses: actions/checkout@v2 with: submodules: 'recursive' - - run: make -j2 epsilon.dfu - - run: make -j2 epsilon.onboarding.dfu - - run: make -j2 epsilon.onboarding.update.dfu - - run: make -j2 epsilon.onboarding.beta.dfu - - run: make -j2 flasher.light.dfu - - run: make -j2 flasher.verbose.dfu - - run: make -j2 bench.ram.dfu - - run: make -j2 bench.flash.dfu - - run: make -j2 binpack - - run: cp output/release/device/n0110/binpack-n0110-`git rev-parse HEAD | head -c 7`.tgz output/release/device/n0110/binpack-n0110.tgz + - run: make MODEL=n0110 -j2 bootloader.bin + - run: make MODEL=n0110-bootloader -j2 epsilon.A.bin epsilon.B.bin + - run: make MODEL=n0110-bootloader -j2 epsilon.onboarding.A.bin epsilon.onboarding.B.bin + - run: make MODEL=n0110-bootloader -j2 epsilon.onboarding.update.A.bin epsilon.onboarding.update.B.bin + - run: make MODEL=n0110-bootloader -j2 epsilon.onboarding.beta.A.bin epsilon.onboarding.beta.B.bin + - run: tar -zcf output/release/device/n0110-bootloader/binpack-n0110-bootloader.tgz output/release/device/n0110-bootloader/epsilon.onboarding.A.bin output/release/device/n0110-bootloader/epsilon.onboarding.B.bin output/release/device/n0110/bootloader.bin - uses: actions/upload-artifact@master with: - name: epsilon-binpack-n0110.tgz - path: output/release/device/n0110/binpack-n0110.tgz - bootloader: + name: epsilon-binpack-n0110-bootloader.tgz + path: output/release/device/n0110-bootloader/binpack-n0110-bootloader.tgz + n0115-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: numworks/setup-arm-toolchain@2022-12 - 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 - - 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 + - run: make MODEL=n0115 -j2 bootloader.bin + - run: make MODEL=n0115-bootloader -j2 epsilon.A.bin epsilon.B.bin + - run: make MODEL=n0115-bootloader -j2 epsilon.onboarding.A.bin epsilon.onboarding.B.bin + - run: make MODEL=n0115-bootloader -j2 epsilon.onboarding.update.A.bin epsilon.onboarding.update.B.bin + - run: make MODEL=n0115-bootloader -j2 epsilon.onboarding.beta.A.bin epsilon.onboarding.beta.B.bin + - run: tar -zcf output/release/device/n0115-bootloader/binpack-n0115-bootloader.tgz output/release/device/n0115-bootloader/epsilon.onboarding.A.bin output/release/device/n0115-bootloader/epsilon.onboarding.B.bin output/release/device/n0115/bootloader.bin - uses: actions/upload-artifact@master with: - name: epsilon-binpack-bootloader.tgz - path: output/release/device/bootloader/binpack-bootloader.tgz + name: epsilon-binpack-n0115-bootloader.tgz + path: output/release/device/n0115-bootloader/binpack-n0115-bootloader.tgz windows: runs-on: windows-latest defaults: @@ -157,7 +152,7 @@ jobs: web: runs-on: ubuntu-latest steps: - - uses: numworks/setup-emscripten@v1 + - uses: numworks/setup-emscripten@latest with: sdk: latest-upstream - uses: actions/checkout@v2 @@ -173,7 +168,7 @@ jobs: linux: runs-on: ubuntu-latest steps: - - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config + - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config libx11-dev libxext-dev - uses: actions/checkout@v2 with: submodules: 'recursive' diff --git a/Makefile b/Makefile index 67f626c9a7..3e58724f05 100644 --- a/Makefile +++ b/Makefile @@ -12,14 +12,14 @@ include build/toolchain.$(TOOLCHAIN).mak include build/variants.mak include build/helpers.mk -ifeq (${MODEL},n0110) - apps_list = ${EPSILON_APPS} -else - ifeq (${MODEL},bootloader) - apps_list = ${EPSILON_APPS} - else +ifeq (${MODEL},$(filter ${MODEL},n0110 n0110-bootloader n0115 n0115-bootloader)) + ifeq (${MODEL},) apps_list = $(foreach i, ${EPSILON_APPS}, $(if $(filter external, $(i)),,$(i))) + else + apps_list = ${EPSILON_APPS} endif +else + apps_list = $(foreach i, ${EPSILON_APPS}, $(if $(filter external, $(i)),,$(i))) endif ifdef FORCE_EXTERNAL diff --git a/apps/external/main_controller.cpp b/apps/external/main_controller.cpp index acf837441e..9327abf9be 100644 --- a/apps/external/main_controller.cpp +++ b/apps/external/main_controller.cpp @@ -131,7 +131,7 @@ void MainController::willDisplayCellForIndex(HighlightCell * cell, int index) { void MainController::viewWillAppear() { int count; #if defined(DEVICE) || defined(EXTERNAL_BUILTIN) - #if !defined(DEVICE_N0110) && !defined(EXTERNAL_BUILTIN) + #if !defined(DEVICE_N0110) && !defined(DEVICE_N0115) && !defined(EXTERNAL_BUILTIN) count = 2; #else if(numberOfFiles() > 0){ diff --git a/bootloader/boot.cpp b/bootloader/boot.cpp index 7b1fd6d615..ad0cf7eeba 100644 --- a/bootloader/boot.cpp +++ b/bootloader/boot.cpp @@ -39,29 +39,29 @@ __attribute__((noreturn)) void Boot::boot() { } else if (!Slot::A().kernelHeader()->isValid()) { // If slot A is invalid and B valid, boot B setMode(BootMode::SlotB); - Slot::B().boot("B"); + Slot::B().boot(); } else if (!Slot::B().kernelHeader()->isValid()) { // If slot B is invalid and A valid, boot A setMode(BootMode::SlotA); - Slot::A().boot("A"); + Slot::A().boot(); } else { const char* version = Bootloader::Slot::A().kernelHeader()->version(); bool isExam = Bootloader::ExamMode::SlotsExamMode::FetchSlotExamMode(version, "A") > 0; if (isExam) { - Bootloader::Slot::A().boot("A"); + Bootloader::Slot::A().boot(); } version = Bootloader::Slot::B().kernelHeader()->version(); isExam = Bootloader::ExamMode::SlotsExamMode::FetchSlotExamMode(version, "B") > 0; if (isExam) { - Bootloader::Slot::B().boot("B"); + Bootloader::Slot::B().boot(); } // Both valid, boot the selected one if (mode() == BootMode::SlotA) { - Slot::A().boot("A"); + Slot::A().boot(); } else if (mode() == BootMode::SlotB) { - Slot::B().boot("B"); + Slot::B().boot(); } } diff --git a/bootloader/jump_to_firmware.s b/bootloader/jump_to_firmware.s index 25a5217134..f0ed4ccad3 100644 --- a/bootloader/jump_to_firmware.s +++ b/bootloader/jump_to_firmware.s @@ -3,6 +3,7 @@ .section .text.jump_to_firmware .align 2 .thumb +.type jump_to_firmware, %function .global jump_to_firmware jump_to_firmware: msr msp, r0 diff --git a/bootloader/slot.cpp b/bootloader/slot.cpp index b3b44344d5..572689e5a8 100644 --- a/bootloader/slot.cpp +++ b/bootloader/slot.cpp @@ -27,17 +27,17 @@ const UserlandHeader* Slot::userland2Header() const { return m_userland2Header; } -[[ noreturn ]] void Slot::boot(const char* Slot) const { +[[ noreturn ]] void Slot::boot() const { //protection mechanism against bootloader update introduced in E20 //check if the slot use Userland2Header if (m_userland2Header->isValid()) { - if (Slot == "A") { + if (kernelHeader() == (void*)0x90000000) { //check if 0x90010000 = 0xFFFFFFFF if (*(uint32_t*)0x90010000 != 0xFFFFFFFF) { Ion::Device::ExternalFlash::EraseSector(9); } } - else if (Slot == "B") { + else if (kernelHeader() == (void*)0x90400000) { //check if 0x90410000 = 0xFFFFFFFF if (*(uint32_t*)0x90410000 != 0xFFFFFFFF) { Ion::Device::ExternalFlash::EraseSector(73); diff --git a/bootloader/slot.h b/bootloader/slot.h index 096ad7db86..dcb223eac8 100644 --- a/bootloader/slot.h +++ b/bootloader/slot.h @@ -20,7 +20,7 @@ class Slot { const KernelHeader* kernelHeader() const; const UserlandHeader* userlandHeader() const; const UserlandHeader* userland2Header() const; - [[ noreturn ]] void boot(const char* Slot) const; + [[ noreturn ]] void boot() const; static const Slot A(); static const Slot B(); diff --git a/bootloader/usb_desc.cpp b/bootloader/usb_desc.cpp index 43927c0cee..5d40dcd03f 100644 --- a/bootloader/usb_desc.cpp +++ b/bootloader/usb_desc.cpp @@ -10,6 +10,8 @@ const char* stringDescriptor(int slot) { return "@Flash/0x90400000/64*064Kg"; } else if (slot == 3) { return "@Flash/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg"; + } else { + return ""; } } diff --git a/build/defaults.mak b/build/defaults.mak index 11a7f9edd2..5290f738a4 100644 --- a/build/defaults.mak +++ b/build/defaults.mak @@ -59,9 +59,16 @@ ifeq ("$(PLATFORM)", "device") SFLAGS += -DPLATFORM_DEVICE ifeq ("$(MODEL)", "n0100") SFLAGS += -DDEVICE_N0100 - else + endif + ifeq ("$(MODEL)", "n0110") + SFLAGS += -DDEVICE_N0110 + endif + ifeq ("$(MODEL)", "bootloader") SFLAGS += -DDEVICE_N0110 endif + ifeq ("$(MODEL)", "n0115") + SFLAGS += -DDEVICE_N0115 + endif endif # Host detection diff --git a/build/device/elf2dfu.py b/build/device/elf2dfu.py index 6fc0523f0f..81b70693cf 100644 --- a/build/device/elf2dfu.py +++ b/build/device/elf2dfu.py @@ -12,7 +12,7 @@ # arm-none-eabi-objcopy -O binary -j .data file.elf file.bin def loadable_sections(elf_file, address_prefix = ""): - objdump_section_headers_pattern = re.compile("^\s+\d+\s+(\.[\w\.]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+("+address_prefix+"[0-9a-f]+)\s+([0-9a-f]+).*LOAD", flags=re.MULTILINE) + objdump_section_headers_pattern = re.compile("^\\s+\\d+\\s+(\\.[\\w\\.]+)\\s+([0-9a-f]+)\\s+([0-9a-f]+)\\s+("+address_prefix+"[0-9a-f]+)\\s+([0-9a-f]+).*LOAD", flags=re.MULTILINE) objdump_output = subprocess.check_output(["arm-none-eabi-objdump", "-h", "-w", elf_file]).decode('utf-8') sections = [] for (name, size, vma, lma, offset) in re.findall(objdump_section_headers_pattern, objdump_output): diff --git a/build/platform.device.bootloader.mak b/build/platform.device.n0110-bootloader.mak similarity index 62% rename from build/platform.device.bootloader.mak rename to build/platform.device.n0110-bootloader.mak index 256dd180d5..efa5e25693 100644 --- a/build/platform.device.bootloader.mak +++ b/build/platform.device.n0110-bootloader.mak @@ -1,3 +1,5 @@ TOOLCHAIN ?= arm-gcc-m7f ION_KEYBOARD_LAYOUT = layout_B3 PCB_LATEST = 343 # PCB version 3.43 + +ION_ADDITIONAL_DEVICE_SFLAGS = -Iion/src/device/n0110 diff --git a/build/platform.device.n0115-bootloader.mak b/build/platform.device.n0115-bootloader.mak new file mode 100644 index 0000000000..abb2ae7b54 --- /dev/null +++ b/build/platform.device.n0115-bootloader.mak @@ -0,0 +1,5 @@ +TOOLCHAIN ?= arm-gcc-m7f +ION_KEYBOARD_LAYOUT = layout_B3 +PCB_LATEST = 343 # PCB version 3.43 + +ION_ADDITIONAL_DEVICE_SFLAGS = -Iion/src/device/n0115 -Iion/src/device/n0110-bootloader -Iion/src/device/n0110 diff --git a/build/platform.device.n0115.mak b/build/platform.device.n0115.mak new file mode 100644 index 0000000000..c4fdd5e4a1 --- /dev/null +++ b/build/platform.device.n0115.mak @@ -0,0 +1,5 @@ +TOOLCHAIN ?= arm-gcc-m7f +ION_KEYBOARD_LAYOUT = layout_B3 +PCB_LATEST = 504 # PCB version 5.04 + +ION_ADDITIONAL_DEVICE_SFLAGS = -Iion/src/device/n0110 diff --git a/build/targets.device.bootloader.mak b/build/targets.device.n0110-bootloader.mak similarity index 90% rename from build/targets.device.bootloader.mak rename to build/targets.device.n0110-bootloader.mak index 48466b778c..734903fd9c 100644 --- a/build/targets.device.bootloader.mak +++ b/build/targets.device.n0110-bootloader.mak @@ -3,9 +3,9 @@ epsilon_flavors_bootloader = $(foreach floavor,$(epsilon_flavors),$(floavor).A $ 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).A.$$(EXE): LDSCRIPT = ion/src/device/n0110-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).B.$$(EXE): LDSCRIPT = ion/src/device/n0110-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 @@ -15,10 +15,10 @@ $$(BUILD_DIR)/epsilon.$(1).bin: $$(BUILD_DIR)/epsilon.$(1).A.bin $$(BUILD_DIR)/e 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.A.$(EXE): LDSCRIPT = ion/src/device/n0110-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 +$(BUILD_DIR)/epsilon.B.$(EXE): LDSCRIPT = ion/src/device/n0110-bootloader/bootloader.B.ld $(BUILD_DIR)/epsilon.bin: $(BUILD_DIR)/epsilon.A.bin $(BUILD_DIR)/epsilon.B.bin @echo "COMBINE $@" diff --git a/build/targets.device.n0115-bootloader.mak b/build/targets.device.n0115-bootloader.mak new file mode 100644 index 0000000000..e2daf6983e --- /dev/null +++ b/build/targets.device.n0115-bootloader.mak @@ -0,0 +1,55 @@ + +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/n0115-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/n0115-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)) +$(BUILD_DIR)/epsilon.A.$(EXE): LDSCRIPT = ion/src/device/n0115-bootloader/bootloader.A.ld + +$(BUILD_DIR)/epsilon.B.$(EXE): $(call flavored_object_for,$(epsilon_src)) +$(BUILD_DIR)/epsilon.B.$(EXE): LDSCRIPT = ion/src/device/n0115-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)))) + + +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 + @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: binpack +binpack: $(BUILD_DIR)/epsilon.onboarding.bin + rm -rf $(BUILD_DIR)/binpack + mkdir -p $(BUILD_DIR)/binpack + 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/* diff --git a/build/targets.device.n0115.mak b/build/targets.device.n0115.mak new file mode 100644 index 0000000000..a7715de25c --- /dev/null +++ b/build/targets.device.n0115.mak @@ -0,0 +1,38 @@ +HANDY_TARGETS += test.external_flash.write test.external_flash.read bootloader + +$(BUILD_DIR)/test.external_flash.%.$(EXE): LDSCRIPT = ion/test/device/n0115/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: 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/n0115/internal_flash.ld + +.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 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/escher/Makefile b/escher/Makefile index a8bb8491cc..20a8e5a58a 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -149,3 +149,6 @@ $(call object_for,$(1)): SFLAGS += $(foreach d,$(sort $(dir $(call object_for,$( escher_src += $(2) image_list += $(2) endef + +# This file needs the external icon to display in the home menu +$(eval $(call depends_on_image,escher/src/background_view.cpp,apps/external/external_icon.png)) diff --git a/ion/Makefile b/ion/Makefile index 8bcef599e7..7440dd2750 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 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))" +$(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/n0110-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/device/Makefile b/ion/src/device/Makefile index cc5594242d..e4c3dbf781 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/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\")))" +$(call object_for,ion/src/device/n0100/platform_info.cpp ion/src/device/n0110/platform_info.cpp ion/src/device/n0110-bootloader/platform_info.cpp): SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" ifeq ($(EPSILON_TELEMETRY),1) ion_src += ion/src/shared/telemetry_console.cpp @@ -14,7 +14,7 @@ ion_src += ion/src/shared/collect_registers.cpp IN_FACTORY ?= 0 -ION_DEVICE_SFLAGS = -Iion/src/device/$(MODEL) -Iion/src/device/shared -DPCB_LATEST=$(PCB_LATEST) -DIN_FACTORY=$(IN_FACTORY) +ION_DEVICE_SFLAGS = -Iion/src/device/$(MODEL) $(ION_ADDITIONAL_DEVICE_SFLAGS) -Iion/src/device/shared -DPCB_LATEST=$(PCB_LATEST) -DIN_FACTORY=$(IN_FACTORY) $(call object_for,$(ion_device_src) $(ion_device_flasher_src) $(ion_device_bench_src)): SFLAGS += $(ION_DEVICE_SFLAGS) diff --git a/ion/src/device/bootloader/Makefile b/ion/src/device/bootloader/Makefile deleted file mode 100644 index c5d7208f7a..0000000000 --- a/ion/src/device/bootloader/Makefile +++ /dev/null @@ -1,19 +0,0 @@ - -ion_device_src += $(addprefix ion/src/device/bootloader/drivers/, \ - board.cpp \ - cache.cpp \ - external_flash_tramp.cpp \ - led.cpp \ - power.cpp \ - reset.cpp \ - trampoline.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 \ -) diff --git a/ion/src/device/bootloader/drivers/cache.cpp b/ion/src/device/bootloader/drivers/cache.cpp deleted file mode 100644 index 0d16f5261e..0000000000 --- a/ion/src/device/bootloader/drivers/cache.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#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 deleted file mode 100644 index cc047743b3..0000000000 --- a/ion/src/device/bootloader/drivers/cache.h +++ /dev/null @@ -1,46 +0,0 @@ -#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 deleted file mode 100644 index 6f94838693..0000000000 --- a/ion/src/device/bootloader/drivers/config/backlight.h +++ /dev/null @@ -1,25 +0,0 @@ -#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/clocks.h b/ion/src/device/bootloader/drivers/config/clocks.h deleted file mode 100644 index 0cc551cb03..0000000000 --- a/ion/src/device/bootloader/drivers/config/clocks.h +++ /dev/null @@ -1,68 +0,0 @@ -#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 deleted file mode 100644 index 58a527201a..0000000000 --- a/ion/src/device/bootloader/drivers/config/console.h +++ /dev/null @@ -1,32 +0,0 @@ -#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 deleted file mode 100644 index c14f0d21bd..0000000000 --- a/ion/src/device/bootloader/drivers/config/display.h +++ /dev/null @@ -1,38 +0,0 @@ -#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 deleted file mode 100644 index e2a2e2abae..0000000000 --- a/ion/src/device/bootloader/drivers/config/exam_mode.h +++ /dev/null @@ -1,30 +0,0 @@ -#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/internal_flash.h b/ion/src/device/bootloader/drivers/config/internal_flash.h deleted file mode 100644 index 9fcbeed145..0000000000 --- a/ion/src/device/bootloader/drivers/config/internal_flash.h +++ /dev/null @@ -1,31 +0,0 @@ -#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 deleted file mode 100644 index 2c98f84a3e..0000000000 --- a/ion/src/device/bootloader/drivers/config/keyboard.h +++ /dev/null @@ -1,75 +0,0 @@ -#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 deleted file mode 100644 index 1fea36d4a8..0000000000 --- a/ion/src/device/bootloader/drivers/config/led.h +++ /dev/null @@ -1,28 +0,0 @@ -#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 deleted file mode 100644 index c5ad127c30..0000000000 --- a/ion/src/device/bootloader/drivers/config/serial_number.h +++ /dev/null @@ -1,18 +0,0 @@ -#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 deleted file mode 100644 index 1b9fcaa20a..0000000000 --- a/ion/src/device/bootloader/drivers/config/swd.h +++ /dev/null @@ -1,24 +0,0 @@ -#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 deleted file mode 100644 index fa4502000a..0000000000 --- a/ion/src/device/bootloader/drivers/config/timing.h +++ /dev/null @@ -1,19 +0,0 @@ -#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/external_flash.cpp b/ion/src/device/bootloader/drivers/external_flash.cpp deleted file mode 100644 index 612efc0179..0000000000 --- a/ion/src/device/bootloader/drivers/external_flash.cpp +++ /dev/null @@ -1,521 +0,0 @@ -#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 >= Config::NumberOf4KSectors && i <= Config::NumberOf4KSectors + 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; - } - 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. - * 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 deleted file mode 100644 index 676464827b..0000000000 --- a/ion/src/device/bootloader/drivers/led.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#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.h b/ion/src/device/bootloader/drivers/power.h deleted file mode 100644 index a3d142bc1c..0000000000 --- a/ion/src/device/bootloader/drivers/power.h +++ /dev/null @@ -1,16 +0,0 @@ -#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 deleted file mode 100644 index f8c30d7e6b..0000000000 --- a/ion/src/device/bootloader/drivers/reset.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#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 deleted file mode 100644 index d56cee4752..0000000000 --- a/ion/src/device/bootloader/drivers/usb.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#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/regs/config/cortex.h b/ion/src/device/bootloader/regs/config/cortex.h deleted file mode 100644 index faee2a9ced..0000000000 --- a/ion/src/device/bootloader/regs/config/cortex.h +++ /dev/null @@ -1,6 +0,0 @@ -#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 deleted file mode 100644 index faa0a263b0..0000000000 --- a/ion/src/device/bootloader/regs/config/crc.h +++ /dev/null @@ -1,6 +0,0 @@ -#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 deleted file mode 100644 index 770fcc37ad..0000000000 --- a/ion/src/device/bootloader/regs/config/flash.h +++ /dev/null @@ -1,6 +0,0 @@ -#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 deleted file mode 100644 index 85f7c0e9d1..0000000000 --- a/ion/src/device/bootloader/regs/config/pwr.h +++ /dev/null @@ -1,6 +0,0 @@ -#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 deleted file mode 100644 index 54db5f2116..0000000000 --- a/ion/src/device/bootloader/regs/config/rcc.h +++ /dev/null @@ -1,7 +0,0 @@ -#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 deleted file mode 100644 index 1165be7c97..0000000000 --- a/ion/src/device/bootloader/regs/config/syscfg.h +++ /dev/null @@ -1,6 +0,0 @@ -#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 deleted file mode 100644 index 5de196af22..0000000000 --- a/ion/src/device/bootloader/regs/config/usart.h +++ /dev/null @@ -1,12 +0,0 @@ -#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/n0110-bootloader/Makefile b/ion/src/device/n0110-bootloader/Makefile new file mode 100644 index 0000000000..426560186b --- /dev/null +++ b/ion/src/device/n0110-bootloader/Makefile @@ -0,0 +1,24 @@ +ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \ + cache.cpp \ + fccid.cpp \ + led.cpp \ + reset.cpp \ + usb.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110-bootloader/drivers/, \ + board.cpp \ + external_flash_tramp.cpp \ + power.cpp \ + trampoline.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110-bootloader/boot/, \ + rt0.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110-bootloader/, \ + platform_info.cpp \ +) + +LDSCRIPT ?= ion/src/device/n0110-bootloader/flash.ld diff --git a/ion/src/device/bootloader/boot/rt0.cpp b/ion/src/device/n0110-bootloader/boot/rt0.cpp similarity index 100% rename from ion/src/device/bootloader/boot/rt0.cpp rename to ion/src/device/n0110-bootloader/boot/rt0.cpp diff --git a/ion/src/device/bootloader/bootloader.A.ld b/ion/src/device/n0110-bootloader/bootloader.A.ld similarity index 94% rename from ion/src/device/bootloader/bootloader.A.ld rename to ion/src/device/n0110-bootloader/bootloader.A.ld index 3e1188476d..ac7d8f1eff 100644 --- a/ion/src/device/bootloader/bootloader.A.ld +++ b/ion/src/device/n0110-bootloader/bootloader.A.ld @@ -25,4 +25,4 @@ FIRST_FLASH_SECTOR_SIZE = 4K; SIGNED_PAYLOAD_LENGTH = 8; USERLAND_OFFSET = 64K; -INCLUDE ion/src/device/bootloader/bootloader_common.ld; +INCLUDE ion/src/device/n0110-bootloader/bootloader_common.ld; diff --git a/ion/src/device/bootloader/bootloader.B.ld b/ion/src/device/n0110-bootloader/bootloader.B.ld similarity index 94% rename from ion/src/device/bootloader/bootloader.B.ld rename to ion/src/device/n0110-bootloader/bootloader.B.ld index f89ebca2f2..31bcb017a2 100644 --- a/ion/src/device/bootloader/bootloader.B.ld +++ b/ion/src/device/n0110-bootloader/bootloader.B.ld @@ -25,4 +25,4 @@ FIRST_FLASH_SECTOR_SIZE = 4K; SIGNED_PAYLOAD_LENGTH = 8; USERLAND_OFFSET = 64K; -INCLUDE ion/src/device/bootloader/bootloader_common.ld; +INCLUDE ion/src/device/n0110-bootloader/bootloader_common.ld; diff --git a/ion/src/device/bootloader/bootloader_common.ld b/ion/src/device/n0110-bootloader/bootloader_common.ld similarity index 100% rename from ion/src/device/bootloader/bootloader_common.ld rename to ion/src/device/n0110-bootloader/bootloader_common.ld diff --git a/ion/src/device/bootloader/drivers/board.cpp b/ion/src/device/n0110-bootloader/drivers/board.cpp similarity index 99% rename from ion/src/device/bootloader/drivers/board.cpp rename to ion/src/device/n0110-bootloader/drivers/board.cpp index 03e47bc2cd..39aaca0fff 100644 --- a/ion/src/device/bootloader/drivers/board.cpp +++ b/ion/src/device/n0110-bootloader/drivers/board.cpp @@ -10,12 +10,6 @@ typedef void(*ISR)(void); extern ISR InitialisationVector[]; -// Public Ion methods - -const char * Ion::fccId() { - return "2ALWP-N0110"; -} - // Private Ion::Device methods namespace Ion { diff --git a/ion/src/device/bootloader/drivers/config/usb.h b/ion/src/device/n0110-bootloader/drivers/config/usb.h similarity index 100% rename from ion/src/device/bootloader/drivers/config/usb.h rename to ion/src/device/n0110-bootloader/drivers/config/usb.h diff --git a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp b/ion/src/device/n0110-bootloader/drivers/external_flash_tramp.cpp similarity index 100% rename from ion/src/device/bootloader/drivers/external_flash_tramp.cpp rename to ion/src/device/n0110-bootloader/drivers/external_flash_tramp.cpp diff --git a/ion/src/device/bootloader/drivers/power.cpp b/ion/src/device/n0110-bootloader/drivers/power.cpp similarity index 100% rename from ion/src/device/bootloader/drivers/power.cpp rename to ion/src/device/n0110-bootloader/drivers/power.cpp diff --git a/ion/src/device/bootloader/drivers/trampoline.cpp b/ion/src/device/n0110-bootloader/drivers/trampoline.cpp similarity index 100% rename from ion/src/device/bootloader/drivers/trampoline.cpp rename to ion/src/device/n0110-bootloader/drivers/trampoline.cpp diff --git a/ion/src/device/bootloader/drivers/trampoline.h b/ion/src/device/n0110-bootloader/drivers/trampoline.h similarity index 100% rename from ion/src/device/bootloader/drivers/trampoline.h rename to ion/src/device/n0110-bootloader/drivers/trampoline.h diff --git a/ion/src/device/bootloader/platform_info.cpp b/ion/src/device/n0110-bootloader/platform_info.cpp similarity index 100% rename from ion/src/device/bootloader/platform_info.cpp rename to ion/src/device/n0110-bootloader/platform_info.cpp diff --git a/ion/src/device/n0110/Makefile b/ion/src/device/n0110/Makefile index 11c3b224f9..3b80d6c412 100644 --- a/ion/src/device/n0110/Makefile +++ b/ion/src/device/n0110/Makefile @@ -2,6 +2,8 @@ ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \ board.cpp \ cache.cpp \ external_flash.cpp \ + external_flash_gpio.cpp \ + fccid.cpp \ led.cpp \ power.cpp \ reset.cpp \ diff --git a/ion/src/device/n0110/drivers/board.cpp b/ion/src/device/n0110/drivers/board.cpp index b78d9d2ddc..7caf00f460 100644 --- a/ion/src/device/n0110/drivers/board.cpp +++ b/ion/src/device/n0110/drivers/board.cpp @@ -10,12 +10,6 @@ typedef void(*ISR)(void); extern ISR InitialisationVector[]; -// Public Ion methods - -const char * Ion::fccId() { - return "2ALWP-N0110"; -} - // Private Ion::Device methods namespace Ion { diff --git a/ion/src/device/n0110/drivers/external_flash.cpp b/ion/src/device/n0110/drivers/external_flash.cpp index 612efc0179..cdc8b475f0 100644 --- a/ion/src/device/n0110/drivers/external_flash.cpp +++ b/ion/src/device/n0110/drivers/external_flash.cpp @@ -118,12 +118,13 @@ class OperatingModes { /* 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); +// We martk these as const and not constexpr so that the linker keeps the symbol name so that we can relocate themm in internal flash. +static const OperatingModes sOperatingModes100(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData, QUADSPI::CCR::OperatingMode::NoData); +static const OperatingModes sOperatingModes101(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData, QUADSPI::CCR::OperatingMode::Single); +static const OperatingModes sOperatingModes110(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::NoData); +static const OperatingModes sOperatingModes111(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single); +static const OperatingModes sOperatingModes114(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad); +static const OperatingModes sOperatingModes144(QUADSPI::CCR::OperatingMode::Single, QUADSPI::CCR::OperatingMode::Quad, QUADSPI::CCR::OperatingMode::Quad); static QUADSPI::CCR::OperatingMode sOperatingMode = QUADSPI::CCR::OperatingMode::Single; @@ -303,12 +304,6 @@ 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); @@ -345,6 +340,8 @@ static void initChip() { set_as_memory_mapped(); } +void initGPIO(); + void init() { if (Config::NumberOfSectors == 0) { return; @@ -354,14 +351,6 @@ void init() { 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 @@ -383,6 +372,8 @@ static void shutdownQSPI() { RCC.AHB3ENR()->setQSPIEN(false); // TODO: move in Device::shutdownClocks } +void shutdownGPIO(); + void shutdown() { if (Config::NumberOfSectors == 0) { return; diff --git a/ion/src/device/n0110/drivers/external_flash_gpio.cpp b/ion/src/device/n0110/drivers/external_flash_gpio.cpp new file mode 100644 index 0000000000..15df92f125 --- /dev/null +++ b/ion/src/device/n0110/drivers/external_flash_gpio.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +namespace Ion { +namespace Device { +namespace ExternalFlash { + +using namespace Regs; + +void initGPIO() { + for(const AFGPIOPin & p : Config::Pins) { + p.init(); + } +} + +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); + } +} + +} +} +} diff --git a/ion/src/device/n0110/drivers/fccid.cpp b/ion/src/device/n0110/drivers/fccid.cpp new file mode 100644 index 0000000000..56cbf29265 --- /dev/null +++ b/ion/src/device/n0110/drivers/fccid.cpp @@ -0,0 +1,5 @@ +#include + +const char * Ion::fccId() { + return "2ALWP-N0110"; +} diff --git a/ion/src/device/n0110/flash.ld b/ion/src/device/n0110/flash.ld index 9728e4ad38..bbcea3cd82 100644 --- a/ion/src/device/n0110/flash.ld +++ b/ion/src/device/n0110/flash.ld @@ -103,7 +103,7 @@ SECTIONS { *(.text._ZN3Ion6Device5Reset17internalFlashJumpEj) /* Optimization */ - */libgcc.a:(.text) + */libgcc.a:(.text .text*) } >INTERNAL_FLASH diff --git a/ion/src/device/n0110/internal_flash.ld b/ion/src/device/n0110/internal_flash.ld index 9eb035afc0..20afc054e3 100644 --- a/ion/src/device/n0110/internal_flash.ld +++ b/ion/src/device/n0110/internal_flash.ld @@ -13,7 +13,7 @@ FLASH_SECOND_SECTOR_OFFSET = 16K; FLASH_SECOND_SECTOR_SIZE = 16K; SECTIONS { - .isr_vector_table ORIGIN(INTERNAL_FLASH) : { + .isr_vector_table ORIGIN(INTERNAL_FLASH) (READONLY) : { /* 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 @@ -40,7 +40,7 @@ SECTIONS { *(.rodata.*) } >INTERNAL_FLASH - .exam_mode_buffer ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET : { + .exam_mode_buffer ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET (READONLY) : { _exam_mode_buffer_start = ORIGIN(INTERNAL_FLASH_AXIM) + FLASH_SECOND_SECTOR_OFFSET; KEEP(*(.exam_mode_buffer)) /* Note: We don't increment "." here, we set it. */ diff --git a/ion/src/device/n0115-bootloader/Makefile b/ion/src/device/n0115-bootloader/Makefile new file mode 100644 index 0000000000..9af0e78372 --- /dev/null +++ b/ion/src/device/n0115-bootloader/Makefile @@ -0,0 +1,27 @@ +ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \ + cache.cpp \ + led.cpp \ + reset.cpp \ + usb.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0115/drivers/, \ + fccid.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110-bootloader/drivers/, \ + board.cpp \ + external_flash_tramp.cpp \ + power.cpp \ + trampoline.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110-bootloader/boot/, \ + rt0.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110-bootloader/, \ + platform_info.cpp \ +) + +LDSCRIPT ?= ion/src/device/n0110-bootloader/flash.ld diff --git a/ion/src/device/n0115-bootloader/bootloader.A.ld b/ion/src/device/n0115-bootloader/bootloader.A.ld new file mode 100644 index 0000000000..90e617ee26 --- /dev/null +++ b/ion/src/device/n0115-bootloader/bootloader.A.ld @@ -0,0 +1,28 @@ +/* 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; +USERLAND_OFFSET = 64K; + +INCLUDE ion/src/device/n0115-bootloader/bootloader_common.ld; diff --git a/ion/src/device/n0115-bootloader/bootloader.B.ld b/ion/src/device/n0115-bootloader/bootloader.B.ld new file mode 100644 index 0000000000..2c3dbca3d3 --- /dev/null +++ b/ion/src/device/n0115-bootloader/bootloader.B.ld @@ -0,0 +1,28 @@ +/* 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; +USERLAND_OFFSET = 64K; + +INCLUDE ion/src/device/n0115-bootloader/bootloader_common.ld; diff --git a/ion/src/device/n0115-bootloader/bootloader_common.ld b/ion/src/device/n0115-bootloader/bootloader_common.ld new file mode 100644 index 0000000000..e8f0c9bbc3 --- /dev/null +++ b/ion/src/device/n0115-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/n0115/Makefile b/ion/src/device/n0115/Makefile new file mode 100644 index 0000000000..9726f25198 --- /dev/null +++ b/ion/src/device/n0115/Makefile @@ -0,0 +1,24 @@ +ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \ + board.cpp \ + cache.cpp \ + external_flash.cpp \ + led.cpp \ + power.cpp \ + reset.cpp \ + usb.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110/boot/, \ + rt0.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0110/, \ + platform_info.cpp \ +) + +ion_device_src += $(addprefix ion/src/device/n0115/drivers/, \ + external_flash_gpio.cpp \ + fccid.cpp \ +) + +LDSCRIPT ?= ion/src/device/n0115/flash.ld diff --git a/ion/src/device/bootloader/drivers/config/battery.h b/ion/src/device/n0115/drivers/config/battery.h similarity index 82% rename from ion/src/device/bootloader/drivers/config/battery.h rename to ion/src/device/n0115/drivers/config/battery.h index 07851149b3..a53183777a 100644 --- a/ion/src/device/bootloader/drivers/config/battery.h +++ b/ion/src/device/n0115/drivers/config/battery.h @@ -1,5 +1,5 @@ -#ifndef ION_DEVICE_N0110_CONFIG_BATTERY_H -#define ION_DEVICE_N0110_CONFIG_BATTERY_H +#ifndef ION_DEVICE_N0115_CONFIG_BATTERY_H +#define ION_DEVICE_N0115_CONFIG_BATTERY_H #include @@ -19,7 +19,7 @@ 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 ADCReferenceVoltage = 3.3f; constexpr float ADCDividerBridgeRatio = 2.0f; } diff --git a/ion/src/device/bootloader/drivers/config/external_flash.h b/ion/src/device/n0115/drivers/config/external_flash.h similarity index 92% rename from ion/src/device/bootloader/drivers/config/external_flash.h rename to ion/src/device/n0115/drivers/config/external_flash.h index f245dca200..07d84bb06c 100644 --- a/ion/src/device/bootloader/drivers/config/external_flash.h +++ b/ion/src/device/n0115/drivers/config/external_flash.h @@ -1,5 +1,5 @@ -#ifndef ION_DEVICE_N0110_CONFIG_EXTERNAL_FLASH_H -#define ION_DEVICE_N0110_CONFIG_EXTERNAL_FLASH_H +#ifndef ION_DEVICE_N0115_CONFIG_EXTERNAL_FLASH_H +#define ION_DEVICE_N0115_CONFIG_EXTERNAL_FLASH_H #include @@ -37,6 +37,8 @@ constexpr static AFGPIOPin Pins[] = { AFGPIOPin(GPIOE, 2, GPIO::AFR::AlternateFunction::AF9, GPIO::PUPDR::Pull::None, GPIO::OSPEEDR::OutputSpeed::Fast), }; +constexpr static GPIOPin PowerPin(GPIOA, 10); + } } } diff --git a/ion/src/device/n0115/drivers/external_flash_gpio.cpp b/ion/src/device/n0115/drivers/external_flash_gpio.cpp new file mode 100644 index 0000000000..f89f5c7eb7 --- /dev/null +++ b/ion/src/device/n0115/drivers/external_flash_gpio.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +namespace Ion { +namespace Device { +namespace ExternalFlash { + +using namespace Regs; + +void initGPIO() { + // Turn on the power to the external flash + Config::PowerPin.group().MODER()->setMode(Config::PowerPin.pin(), GPIO::MODER::Mode::Output); + Config::PowerPin.group().ODR()->set(Config::PowerPin.pin(), true); + + for(const AFGPIOPin & p : Config::Pins) { + p.init(); + } +} + +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); + } + + // Turn off the power to the external flash + Config::PowerPin.group().ODR()->set(Config::PowerPin.pin(), false); + Config::PowerPin.group().MODER()->setMode(Config::PowerPin.pin(), GPIO::MODER::Mode::Analog); + Config::PowerPin.group().PUPDR()->setPull(Config::PowerPin.pin(), GPIO::PUPDR::Pull::None); +} + +} +} +} diff --git a/ion/src/device/n0115/drivers/fccid.cpp b/ion/src/device/n0115/drivers/fccid.cpp new file mode 100644 index 0000000000..daf908c81e --- /dev/null +++ b/ion/src/device/n0115/drivers/fccid.cpp @@ -0,0 +1,5 @@ +#include + +const char * Ion::fccId() { + return "NA"; +} diff --git a/ion/src/device/n0115/flash.ld b/ion/src/device/n0115/flash.ld new file mode 100644 index 0000000000..bbcea3cd82 --- /dev/null +++ b/ion/src/device/n0115/flash.ld @@ -0,0 +1,218 @@ +/* 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 ">INTERNAL_FLASH" to ask for a given section to + * be stored in Flash. */ + +MEMORY { + INTERNAL_FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 64K + SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K + EXTERNAL_FLASH (rx) : ORIGIN = 0x90000000, LENGTH = 8M + /* + 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_EXTERNAL_FLASH_SECTOR_SIZE = 4K; + +SECTIONS { + .isr_vector_table ORIGIN(INTERNAL_FLASH) : { + /* When booting, the STM32F412 fetches the content of address 0x0, and + * extracts from it various key infos: the initial value of the PC register + * (program counter), the initial value of the stack pointer, and various + * 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. */ + KEEP(*(.isr_vector_table)) + } >INTERNAL_FLASH + + .header : { + KEEP(*(.header)) + } >INTERNAL_FLASH + + /* Internal flash memory */ + + /* We link 'jump_to_external_flash' in a separate section from text.internal. + * Indeed, it is the only internal flash symbol that refers the external + * flash. This way we can assert that there are no cross references from the + * internal flash to the external flash. */ + .text.internal_to_external : { + *(.text._ZL22jump_to_external_flashv*) + } >INTERNAL_FLASH + + /* Use boot routine and required dependencies */ + /* We're relying on symbols being in their own sub-section. On GCC, this is + * done with -fdata-sections -ffunction-sections */ + .text.internal : { + . = ALIGN(4); + *(.text.start) + *(.text.abort) + *(.text.isr_systick) + *(.text.__assert) + *(.text.memcpy) + *(.text.memset) + /* + *(.text.strlen) + *(.text.strncmp) + *(.text.strlcpy) + *(.text.strcmp) + *(.text.memmove) + */ + /* 'start' dependencies */ + *(.text._ZN3Ion6Device5Board4initEv) + *(.text._ZN3Ion6Device5Board7initFPUEv) + *(.text._ZN3Ion6Device5Board7initMPUEv) + *(.text._ZN3Ion6Device5Board10initClocksEv) + *(.text._ZNV3Ion6Device4Regs*) + *(.text._ZN3Ion6Device4Regs*) + *(.text.*3Ion6Device4Regs4GPIO5PUPDR*) + *(.text._ZNK3Ion6Device4Regs9AFGPIOPin*) + *(.text._ZN3Ion6Device13ExternalFlash*) + *(.text._ZN3Ion6Device5Cache*) + *(.text._ZN3Ion6Timing6usleepEj) + + /* 'abort' dependencies */ + *(.text._ZN3Ion6Device5Reset4coreEv) + + /* 'standby' dependencies '*/ + *(.text._ZN3Ion6Device5Power20internalFlashStandbyEv) + *(.text._ZN3Ion6Device5Power17enterLowPowerModeEv) + *(.text._ZN3Ion6Device5Board14shutdownClocksEb) + *(.text._ZN3Ion6Device5Reset16coreWhilePluggedEv) + + /* 'suspend' dependencies '*/ + *(.text._ZN3Ion6Device5Power20internalFlashSuspendEb) + + /* 'Reset::jump' dependencies '*/ + *(.text._ZN3Ion6Device5Reset17internalFlashJumpEj) + + /* Optimization */ + */libgcc.a:(.text .text*) + } >INTERNAL_FLASH + + + .rodata.internal : { + . = ALIGN(4); + *(.rodata._ZN3Ion6Device13ExternalFlash*) + /* 'start' dependencies */ + *(.rodata._ZN3Ion6Device4RegsL5GPIOAE) + *(.rodata._ZN3Ion6Device4RegsL5GPIOBE) + *(.rodata._ZN3Ion6Device5Board4initEv.str1.4) + } >INTERNAL_FLASH + + .exam_mode_buffer ORIGIN(EXTERNAL_FLASH) : { + _exam_mode_buffer_start = .; + KEEP(*(.exam_mode_buffer)) + /* Note: We don't increment "." here, we set it. */ + . = ORIGIN(EXTERNAL_FLASH) + FIRST_EXTERNAL_FLASH_SECTOR_SIZE; + _exam_mode_buffer_end = .; + } >EXTERNAL_FLASH + + /* External flash memory */ + .text.external : { + . = ALIGN(4); + *(.text) + *(.text.*) + } >EXTERNAL_FLASH + + .rodata.external : { + *(.rodata) + *(.rodata.*) + } >EXTERNAL_FLASH + + .init_array : { + . = ALIGN(4); + _init_array_start = .; + KEEP (*(.init_array*)) + _init_array_end = .; + } >INTERNAL_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> 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 + * 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*) + } +} + +NOCROSSREFS_TO(.text.external .text.internal); +NOCROSSREFS_TO(.rodata.external .text.internal); +NOCROSSREFS_TO(.text.external .rodata.internal); +NOCROSSREFS_TO(.rodata.external .rodata.internal); +NOCROSSREFS_TO(.text.external .isr_vector_table); +NOCROSSREFS_TO(.rodata.external .isr_vector_table); +NOCROSSREFS_TO(.text.external .header); +NOCROSSREFS_TO(.rodata.external .header); + +NOCROSSREFS_TO(.exam_mode_buffer .text.internal); +NOCROSSREFS_TO(.exam_mode_buffer .rodata.internal); +NOCROSSREFS_TO(.exam_mode_buffer .isr_vector_table); +NOCROSSREFS_TO(.exam_mode_buffer .header); diff --git a/ion/src/device/n0115/internal_flash.ld b/ion/src/device/n0115/internal_flash.ld new file mode 100644 index 0000000000..20afc054e3 --- /dev/null +++ b/ion/src/device/n0115/internal_flash.ld @@ -0,0 +1,126 @@ +/* Same as flash.ld but everything is linked in internal flash */ + +MEMORY { + INTERNAL_FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 64K + INTERNAL_FLASH_AXIM (rx) : ORIGIN = 0x08000000, LENGTH = 64K + SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K +} + +STACK_SIZE = 32K; +TRAMPOLINES_OFFSET = 0xE000; +CUSTOM_TRAMPOLINES_OFFSET = 64K - 64; +FLASH_SECOND_SECTOR_OFFSET = 16K; +FLASH_SECOND_SECTOR_SIZE = 16K; + +SECTIONS { + .isr_vector_table ORIGIN(INTERNAL_FLASH) (READONLY) : { + /* 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. */ + KEEP(*(.isr_vector_table)) + } >INTERNAL_FLASH + + .header : { + KEEP(*(.header)) + } >INTERNAL_FLASH + + .rodata : { + . = ALIGN(4); + *(.rodata) + *(.rodata.*) + } >INTERNAL_FLASH + + .exam_mode_buffer ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET (READONLY) : { + _exam_mode_buffer_start = ORIGIN(INTERNAL_FLASH_AXIM) + FLASH_SECOND_SECTOR_OFFSET; + 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 = ORIGIN(INTERNAL_FLASH_AXIM) + FLASH_SECOND_SECTOR_OFFSET + FLASH_SECOND_SECTOR_SIZE; + } >INTERNAL_FLASH + + .text : { + . = ALIGN(4); + *(.text) + *(.text.*) + } >INTERNAL_FLASH + + .init_array : { + . = ALIGN(4); + _init_array_start = .; + KEEP (*(.init_array*)) + _init_array_end = .; + } >INTERNAL_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> INTERNAL_FLASH + + .trampolines_table : { + . = ORIGIN(INTERNAL_FLASH) + TRAMPOLINES_OFFSET; + 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 + * 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 +} diff --git a/ion/src/device/shared/drivers/display.cpp b/ion/src/device/shared/drivers/display.cpp index 508242ddeb..62923a00b1 100644 --- a/ion/src/device/shared/drivers/display.cpp +++ b/ion/src/device/shared/drivers/display.cpp @@ -427,22 +427,68 @@ void initPanel() { send_command(Command::SleepOut); Timing::msleep(5); - send_command(Command::PixelFormatSet, 0x05); + uint32_t panelId = panelIdentifier(); + + send_command(Command::PixelFormatSet, 0x55); send_command(Command::TearingEffectLineOn, 0x00); - send_command(Command::FrameRateControl, 0x1E); // 40 Hz frame rate + + if (panelId == 0x4E4102 || panelId == 0x4E5401) { + // These panels need additionnal configuration + static const uint8_t porch_config[] = { 0x0C, 0x0C, 0x00, 0x33, 0x33 }; + send_long_command(Command::PorchSetting, 5, porch_config); + + send_command(Command::GateControl, 0x35); + send_command(Command::VCOMSetting, 0x3C); + send_command(Command::LMCControl, 0x2C); + send_command(Command::VDVVRHEnable, 0x1); + send_command(Command::VRHSet, 0x10); + send_command(Command::VDVSet, 0x20); + send_command(Command::PowerControl1, 0xA4, 0xA1); + } + + if (panelId == 0x4E4101 || panelId == 0x4E4105 || panelId == 0x4E4801) { + // These pannels accept a lower framerate + send_command(Command::FrameRateControl, 0x1E); // 40Hz + } else { + send_command(Command::FrameRateControl, 0x05); // 90Hz + } // Calibration - const uint8_t * gammaCalibration = nullptr; - uint32_t panelId = panelIdentifier(); - if (panelId == 0x4E4101 || panelId == 0x4E4801) { - /* Don't forget the "static" qualifier, otherwise this array can be deleted - * before reaching send_long_command. */ - static const uint8_t calibration[] = {0xA2, 0xA, 0x11, 0xA, 0xC, 0x1A, 0x34, 0x22, 0x4D, 0x28, 0x15, 0x13, 0x29, 0x2D}; - gammaCalibration = calibration; + const uint8_t * posGammaCalibration = nullptr; + const uint8_t * negGammaCalibration = nullptr; + + // TODO: Cleanup ? + switch(panelId) { + case 0x4E4101: + case 0x4E4105: + case 0x4E4801: + static const uint8_t calibration_4E4101[] = {0xa2, 0xa, 0x11, 0xa, 0xc, 0x1a, 0x34, 0x22, 0x4d, 0x28, 0x15, 0x13, 0x29, 0x2d}; + posGammaCalibration = calibration_4E4101; + negGammaCalibration = calibration_4E4101; + break; + case 0x4E4102: + static const uint8_t calibration_4E4102[] = {0xf0, 0x9, 0xf, 0x8, 0x7, 0x3, 0x28, 0x11, 0x3c, 0x17, 0x12, 0x11, 0x28, 0x2d}; + posGammaCalibration = calibration_4E4102; + negGammaCalibration = calibration_4E4102; + break; + case 0x4E4601: + static const uint8_t calibration_4E4601[] = {0xc4, 0xc, 0x13, 0xc, 0xe, 0x3c, 0x36, 0x44, 0x4f, 0x3a, 0x17, 0x15, 0x2b, 0x2f}; + posGammaCalibration = calibration_4E4601; + negGammaCalibration = calibration_4E4601; + break; + case 0x4E5401: + static const uint8_t pos_calibration_4E5401[] = {0xf0, 0xd, 0x12, 0x9, 0x8, 0x14, 0x2d, 0x22, 0x43, 0x38, 0x17, 0x19, 0x2c, 0x30}; + static const uint8_t neg_calibration_4E5401[] = {0xf0, 0x9, 0xd, 0xa, 0x9, 0x5, 0x29, 0x43, 0x40, 0x39, 0x18, 0x16, 0x2e, 0x2e}; + posGammaCalibration = pos_calibration_4E5401; + negGammaCalibration = neg_calibration_4E5401; + break; + default: + break; } - if (gammaCalibration != nullptr) { - send_long_command(Command::PositiveVoltageGammaControl, 14, gammaCalibration); - send_long_command(Command::NegativeVoltageGammaControl, 14, gammaCalibration); + + if (posGammaCalibration != nullptr && negGammaCalibration != nullptr) { + send_long_command(Command::PositiveVoltageGammaControl, 14, posGammaCalibration); + send_long_command(Command::NegativeVoltageGammaControl, 14, negGammaCalibration); } if (Config::DisplayInversion) { diff --git a/ion/src/device/shared/drivers/display.h b/ion/src/device/shared/drivers/display.h index 92271234a1..f36b2822bb 100644 --- a/ion/src/device/shared/drivers/display.h +++ b/ion/src/device/shared/drivers/display.h @@ -51,9 +51,17 @@ enum class Command : uint16_t { TearingEffectLineOn = 0x35, MemoryAccessControl = 0x36, PixelFormatSet = 0x3A, + PorchSetting = 0xB2, + VCOMSetting = 0xBB, + LMCControl = 0xC0, + VDVVRHEnable = 0xC2, + VRHSet = 0xC3, + VDVSet = 0xC4, FrameRateControl = 0xC6, + PowerControl1 = 0xD0, PositiveVoltageGammaControl = 0xE0, - NegativeVoltageGammaControl = 0xE1 + NegativeVoltageGammaControl = 0xE1, + GateControl = 0xE4 }; constexpr static int FSMCMemoryBank = 1; diff --git a/ion/src/device/shared/usb/Makefile b/ion/src/device/shared/usb/Makefile index 02e931b6a2..bf9bd3a3f6 100644 --- a/ion/src/device/shared/usb/Makefile +++ b/ion/src/device/shared/usb/Makefile @@ -41,11 +41,47 @@ ion_device_dfu_src += liba/src/memset.c ion_device_dfu_src += liba/src/memcpy.c ion_device_dfu_src += libaxx/src/cxxabi/pure_virtual.cpp ion_device_dfu_src += ion/src/device/shared/usb/boot.cpp -ion_device_dfu_src += ion/src/device/$(MODEL)/drivers/board.cpp -ion_device_dfu_src += ion/src/device/$(MODEL)/drivers/cache.cpp -ion_device_dfu_src += ion/src/device/$(MODEL)/drivers/external_flash.cpp -ion_device_dfu_src += ion/src/device/$(MODEL)/drivers/reset.cpp -ion_device_dfu_src += ion/src/device/$(MODEL)/drivers/usb.cpp + +ifeq ($(MODEL),n0100) + ion_device_dfu_src += ion/src/device/n0100/drivers/board.cpp + ion_device_dfu_src += ion/src/device/n0100/drivers/cache.cpp + ion_device_dfu_src += ion/src/device/n0100/drivers/external_flash.cpp + ion_device_dfu_src += ion/src/device/n0100/drivers/reset.cpp + ion_device_dfu_src += ion/src/device/n0100/drivers/usb.cpp +endif +ifeq ($(MODEL),n0110) + ion_device_dfu_src += ion/src/device/n0110/drivers/board.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/cache.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/external_flash.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/external_flash_gpio.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/reset.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/usb.cpp +endif +ifeq ($(MODEL),n0110-bootloader) + ion_device_dfu_src += ion/src/device/n0110-bootloader/drivers/board.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/cache.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/external_flash.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/external_flash_gpio.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/reset.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/usb.cpp +endif +ifeq ($(MODEL),n0115) + ion_device_dfu_src += ion/src/device/n0110/drivers/board.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/cache.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/external_flash.cpp + ion_device_dfu_src += ion/src/device/n0115/drivers/external_flash_gpio.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/reset.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/usb.cpp +endif +ifeq ($(MODEL),n0115-bootloader) + ion_device_dfu_src += ion/src/device/n0110-bootloader/drivers/board.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/cache.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/external_flash.cpp + ion_device_dfu_src += ion/src/device/n0115/drivers/external_flash_gpio.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/reset.cpp + ion_device_dfu_src += ion/src/device/n0110/drivers/usb.cpp +endif + ion_device_dfu_src += $(addprefix ion/src/device/shared/drivers/, \ backlight.cpp \ battery.cpp \ diff --git a/ion/src/device/shared/usb/dfu_relocated.cpp b/ion/src/device/shared/usb/dfu_relocated.cpp index 56f7283793..60ce747054 100644 --- a/ion/src/device/shared/usb/dfu_relocated.cpp +++ b/ion/src/device/shared/usb/dfu_relocated.cpp @@ -65,7 +65,7 @@ void DFU(bool exitWithKeyboard, int slot) { /* 5- Jump to DFU bootloader code. We made sure in the linker script that the * first function we want to call is at the beginning of the DFU code. */ - PollFunctionPointer dfu_bootloader_entry = reinterpret_cast(dfu_bootloader_ram_start); + PollFunctionPointer dfu_bootloader_entry = reinterpret_cast(((uint32_t)dfu_bootloader_ram_start) | 1); /* To have the right debug symbols for the reallocated code, break here and: * - Get the address of the new .text section diff --git a/ion/test/device/n0115/Makefile b/ion/test/device/n0115/Makefile new file mode 100644 index 0000000000..d379f0fb27 --- /dev/null +++ b/ion/test/device/n0115/Makefile @@ -0,0 +1,10 @@ +test_ion_external_flash_read_src += $(addprefix ion/test/$(PLATFORM)/$(MODEL)/, \ + external_flash_read.cpp \ +) + +test_ion_external_flash_write_src += $(addprefix ion/test/$(PLATFORM)/$(MODEL)/, \ + external_flash_write.cpp \ +) + +test_ion_external_flash_src += $(test_ion_external_flash_read_src) $(test_ion_external_flash_write_src) +$(call object_for,$(test_ion_external_flash_src)): SFLAGS += $(ION_DEVICE_SFLAGS) diff --git a/ion/test/device/n0115/external_flash_helper.h b/ion/test/device/n0115/external_flash_helper.h new file mode 100644 index 0000000000..11350ce9b9 --- /dev/null +++ b/ion/test/device/n0115/external_flash_helper.h @@ -0,0 +1,19 @@ +#include + +// Choose some not too uniform data to program and test the external flash memory with. + +inline uint8_t expected_value_at(uint8_t * ptr) { + uint32_t address = reinterpret_cast(ptr) - Ion::Device::ExternalFlash::Config::StartAddress; + return (address / 0x10000) + (address / 0x100) + address; + // Example: the value expected at the address 0x123456 is 0x12 + 0x34 + 0x56. +} + +inline uint16_t expected_value_at(uint16_t * ptr) { + uint8_t * ptr8 = reinterpret_cast(ptr); + return (static_cast(expected_value_at(ptr8+1)) << 8) | static_cast(expected_value_at(ptr8)); +} + +inline uint32_t expected_value_at(uint32_t * ptr) { + uint16_t * ptr16 = reinterpret_cast(ptr); + return (static_cast(expected_value_at(ptr16+1)) << 16) + static_cast(expected_value_at(ptr16)); +} diff --git a/ion/test/device/n0115/external_flash_read.cpp b/ion/test/device/n0115/external_flash_read.cpp new file mode 100644 index 0000000000..4a2417c6ec --- /dev/null +++ b/ion/test/device/n0115/external_flash_read.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include "external_flash_helper.h" + +template +static inline void check(volatile T * p, int repeat) { + for (int i = 0; i < repeat; i++) { + quiz_assert(*p == expected_value_at(const_cast(p))); + } +} + +template +void test(int accessType, int repeat) { + uint8_t * start = reinterpret_cast(Ion::Device::ExternalFlash::Config::StartAddress); + uint8_t * end = reinterpret_cast(Ion::Device::ExternalFlash::Config::StartAddress + Ion::Device::ExternalFlash::FlashAddressSpaceSize); + + // Forward sequential access + if (accessType == 0) { + for (uint8_t * p = start; p <= end-sizeof(T); p++) { + volatile T * q = reinterpret_cast(p); + check(q, repeat); + } + } + + // Backward sequential access + if (accessType == 1) { + for (uint8_t * p = end - sizeof(T); p >= start; p--) { + volatile T * q = reinterpret_cast(p); + check(q, repeat); + } + } + + // Random access + if (accessType == 2) { + T * endT = reinterpret_cast(Ion::Device::ExternalFlash::Config::StartAddress + Ion::Device::ExternalFlash::FlashAddressSpaceSize); + for (size_t i=0; i> (32 - Ion::Device::ExternalFlash::NumberOfAddressBitsInChip); + volatile T * q = reinterpret_cast(randomAddr + Ion::Device::ExternalFlash::Config::StartAddress); + if (q <= endT - 1) { + check(q, repeat); + } + } + } +} + +QUIZ_CASE(ion_extflash_read_byte_fwd) { + uint64_t startTime = quiz_stopwatch_start(); + test(0, 1); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_extflash_read_byte_bck) { + uint64_t startTime = quiz_stopwatch_start(); + test(1, 1); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_extflash_read_byte_rand) { + uint64_t startTime = quiz_stopwatch_start(); + test(2, 1); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_extflash_read_half_fwd) { + uint64_t startTime = quiz_stopwatch_start(); + test(0, 1); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_extflash_read_half_bck) { + uint64_t startTime = quiz_stopwatch_start(); + test(1, 1); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_extflash_read_half_rand) { + uint64_t startTime = quiz_stopwatch_start(); + test(2, 1); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_extflash_read_word_fwd) { + uint64_t startTime = quiz_stopwatch_start(); + test(0, 1); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_extflash_read_word_bck) { + uint64_t startTime = quiz_stopwatch_start(); + test(1, 1); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_extflash_read_word_rand) { + uint64_t startTime = quiz_stopwatch_start(); + test(2, 1); + quiz_stopwatch_print_lap(startTime); + Ion::Timing::msleep(3000); +} diff --git a/ion/test/device/n0115/external_flash_tests.ld b/ion/test/device/n0115/external_flash_tests.ld new file mode 100644 index 0000000000..61a8b97d36 --- /dev/null +++ b/ion/test/device/n0115/external_flash_tests.ld @@ -0,0 +1,113 @@ +/* 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 ">INTERNAL_FLASH" to ask for a given section to + * be stored in Flash. */ + +MEMORY { + INTERNAL_FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K + SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K +} + +STACK_SIZE = 32K; + +SECTIONS { + .isr_vector_table ORIGIN(INTERNAL_FLASH) : { + /* When booting, the STM32F412 fetches the content of address 0x0, and + * extracts from it various key infos: the initial value of the PC register + * (program counter), the initial value of the stack pointer, and various + * 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. */ + KEEP(*(.isr_vector_table)) + } >INTERNAL_FLASH + + .header : { + KEEP(*(.header)) + } >INTERNAL_FLASH + + .text.internal : { + . = ALIGN(4); + *(.text) + *(.text.*) + } >INTERNAL_FLASH + + .rodata.internal : { + . = ALIGN(4); + *(.rodata) + *(.rodata.*) + } >INTERNAL_FLASH + + .init_array : { + . = ALIGN(4); + _init_array_start = .; + KEEP (*(.init_array*)) + _init_array_end = .; + } >INTERNAL_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> 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 + * 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 +} + diff --git a/ion/test/device/n0115/external_flash_write.cpp b/ion/test/device/n0115/external_flash_write.cpp new file mode 100644 index 0000000000..a9097117af --- /dev/null +++ b/ion/test/device/n0115/external_flash_write.cpp @@ -0,0 +1,25 @@ +#include +#include +#include +#include "external_flash_helper.h" + +// Choose some not too uniform data to program the external flash memory with. + +QUIZ_CASE(ion_ext_flash_erase) { + uint64_t startTime = quiz_stopwatch_start(); + Ion::Device::ExternalFlash::MassErase(); + quiz_stopwatch_print_lap(startTime); +} + +QUIZ_CASE(ion_ext_flash_program) { + // Program separately each page of the flash memory + uint64_t startTime = quiz_stopwatch_start(); + for (int page = 0; page < (1<<15); page++) { + uint8_t buffer[256]; + for (int byte = 0; byte < 256; byte++) { + buffer[byte] = expected_value_at(reinterpret_cast(Ion::Device::ExternalFlash::Config::StartAddress + page * 256 + byte)); + } + Ion::Device::ExternalFlash::WriteMemory(reinterpret_cast(page * 256), buffer, 256); + } + quiz_stopwatch_print_lap(startTime); +} diff --git a/liba/Makefile b/liba/Makefile index 639bf196db..e1a14bd1ba 100644 --- a/liba/Makefile +++ b/liba/Makefile @@ -11,6 +11,7 @@ liba_src += $(addprefix liba/src/, \ fpclassify.c \ fpclassifyf.c \ ieee754.c \ + itoa.c \ malloc.c \ memcmp.c \ memcpy.c \ diff --git a/liba/include/stdlib.h b/liba/include/stdlib.h index 6c27b3d6e1..3d630aa70c 100644 --- a/liba/include/stdlib.h +++ b/liba/include/stdlib.h @@ -11,6 +11,8 @@ void * malloc(size_t size); void * realloc(void *ptr, size_t size); void * calloc(size_t count, size_t size); +char * itoa(int value, char *str, int base); + void abort(void) __attribute__((noreturn)); int abs(int n); diff --git a/liba/src/external/sqlite/mem5.c b/liba/src/external/sqlite/mem5.c index 1479ddd0d0..5d8ec53adc 100644 --- a/liba/src/external/sqlite/mem5.c +++ b/liba/src/external/sqlite/mem5.c @@ -561,13 +561,13 @@ void sqlite3Memsys5Dump(const char *zFilename){ */ const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){ static const sqlite3_mem_methods memsys5Methods = { - memsys5Malloc, - memsys5Free, - memsys5Realloc, - memsys5Size, - memsys5Roundup, - memsys5Init, - memsys5Shutdown, + (sqlite3_mem_methods) memsys5Malloc, + (sqlite3_mem_methods) memsys5Free, + (sqlite3_mem_methods) memsys5Realloc, + (sqlite3_mem_methods) memsys5Size, + (sqlite3_mem_methods) memsys5Roundup, + (sqlite3_mem_methods) memsys5Init, + (sqlite3_mem_methods) memsys5Shutdown, 0 }; return &memsys5Methods; diff --git a/liba/src/itoa.c b/liba/src/itoa.c new file mode 100644 index 0000000000..e45515594d --- /dev/null +++ b/liba/src/itoa.c @@ -0,0 +1,60 @@ +#include +#include + +// https://www.techiedelight.com/implement-itoa-function-in-c/ + +// Function to swap two numbers +void swap(char *x, char *y) { + char t = *x; *x = *y; *y = t; +} + +// Function to reverse `buffer[i…j]` +char* reverse(char *buffer, int i, int j) { + while (i < j) { + swap(&buffer[i++], &buffer[j--]); + } + + return buffer; +} + +// Iterative function to implement `itoa()` function in C +char* itoa(int value, char* buffer, int base) { + // invalid input + if (base < 2 || base > 32) { + return buffer; + } + + // consider the absolute value of the number + int n = abs(value); + + int i = 0; + while (n) { + int r = n % base; + + if (r >= 10) { + buffer[i++] = 65 + (r - 10); + } + else { + buffer[i++] = 48 + r; + } + + n = n / base; + } + + // if the number is 0 + if (i == 0) { + buffer[i++] = '0'; + } + + // If the base is 10 and the value is negative, the resulting string + // is preceded with a minus sign (-) + // With any other base, value is always considered unsigned + if (value < 0 && base == 10) { + buffer[i++] = '-'; + } + + buffer[i] = '\0'; // null terminate string + + // reverse the string and return it + return reverse(buffer, 0, i - 1); +} \ No newline at end of file diff --git a/libaxx/include/external/libcxx/include/__config b/libaxx/include/external/libcxx/include/__config index dce88939fd..96b903c79c 100644 --- a/libaxx/include/external/libcxx/include/__config +++ b/libaxx/include/external/libcxx/include/__config @@ -8,7 +8,9 @@ #define _LIBCPP_CONSTEXPR_AFTER_CXX11 #define _LIBCPP_CONSTEXPR_AFTER_CXX14 #define _LIBCPP_CONSTEXPR constexpr +#ifndef __has_feature #define __has_feature(__x) 0 +#endif #define _LIBCPP_NO_CFI #define _NOEXCEPT noexcept #define _NOEXCEPT_(x) noexcept(x)