mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
Fixed conflicts
This commit is contained in:
@@ -5,11 +5,13 @@
|
||||
#include <poincare/init.h>
|
||||
#include <poincare/exception_checkpoint.h>
|
||||
#include <ion/backlight.h>
|
||||
#include <poincare/preferences.h>
|
||||
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
}
|
||||
|
||||
using namespace Poincare;
|
||||
using namespace Shared;
|
||||
|
||||
AppsContainer * AppsContainer::sharedAppsContainer() {
|
||||
@@ -199,7 +201,7 @@ bool AppsContainer::processEvent(Ion::Events::Event event) {
|
||||
// Warning: if the window is dirtied, you need to call window()->redraw()
|
||||
if (event == Ion::Events::USBPlug) {
|
||||
if (Ion::USB::isPlugged()) {
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate) {
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode()) {
|
||||
displayExamModePopUp(false);
|
||||
window()->redraw();
|
||||
} else {
|
||||
@@ -244,6 +246,9 @@ bool AppsContainer::switchTo(App::Snapshot * snapshot) {
|
||||
|
||||
void AppsContainer::run() {
|
||||
window()->setFrame(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height));
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode()) {
|
||||
activateExamMode();
|
||||
}
|
||||
refreshPreferences();
|
||||
|
||||
/* ExceptionCheckpoint stores the value of the stack pointer when setjump is
|
||||
@@ -319,7 +324,7 @@ void AppsContainer::shutdownDueToLowBattery() {
|
||||
}
|
||||
while (Ion::Battery::level() == Ion::Battery::Charge::EMPTY) {
|
||||
Ion::Backlight::setBrightness(0);
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Deactivate) {
|
||||
if (!GlobalPreferences::sharedGlobalPreferences()->examMode()) {
|
||||
/* Unless the LED is lit up for the exam mode, switch off the LED. IF the
|
||||
* low battery event happened during the Power-On Self-Test, a LED might
|
||||
* have stayed lit up. */
|
||||
@@ -352,6 +357,26 @@ void AppsContainer::redrawWindow(bool force) {
|
||||
m_window.redraw(force);
|
||||
}
|
||||
|
||||
void AppsContainer::activateExamMode() {
|
||||
reset();
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
switch ((int)preferences->colorOfLED()) {
|
||||
case 0:
|
||||
Ion::LED::setColor(KDColorWhite);
|
||||
break;
|
||||
case 1:
|
||||
Ion::LED::setColor(KDColorGreen);
|
||||
break;
|
||||
case 2:
|
||||
Ion::LED::setColor(KDColorBlue);
|
||||
break;
|
||||
case 3:
|
||||
Ion::LED::setColor(KDColorYellow);
|
||||
break;
|
||||
}
|
||||
Ion::LED::setBlinking(1000, 0.1f);
|
||||
}
|
||||
|
||||
void AppsContainer::examDeactivatingPopUpIsDismissed() {
|
||||
if (Ion::USB::isPlugged()) {
|
||||
Ion::USB::enable();
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
void setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus newStatus);
|
||||
OnBoarding::PopUpController * promptController();
|
||||
void redrawWindow(bool force = false);
|
||||
void activateExamMode();
|
||||
// Exam pop-up controller delegate
|
||||
void examDeactivatingPopUpIsDismissed() override;
|
||||
// Ion::StorageDelegate
|
||||
|
||||
@@ -92,7 +92,7 @@ ExpiringPointer<Calculation> CalculationStore::push(const char * text, Context *
|
||||
// Compute and serialize the outputs
|
||||
{
|
||||
Expression outputs[] = {Expression(), Expression()};
|
||||
PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate ? Preferences::sharedPreferences()->isExamSymbolic() : true); // Symbolic computation
|
||||
PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, GlobalPreferences::sharedGlobalPreferences()->examMode() ? Preferences::sharedPreferences()->isExamSymbolic() : true); // Symbolic computation
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!serializeExpression(outputs[i], nextSerializationLocation, &newCalculationsLocation)) {
|
||||
/* If the exat/approximate output does not fit in the store (event if the
|
||||
|
||||
@@ -16,10 +16,8 @@ ExamPopUpController::ExamPopUpController(ExamPopUpControllerDelegate * delegate)
|
||||
}
|
||||
|
||||
void ExamPopUpController::setActivatingExamMode(bool activatingExamMode) {
|
||||
if (m_isActivatingExamMode != activatingExamMode) {
|
||||
m_isActivatingExamMode = activatingExamMode;
|
||||
m_contentView.setMessages(activatingExamMode);
|
||||
}
|
||||
m_isActivatingExamMode = activatingExamMode;
|
||||
m_contentView.setMessages(activatingExamMode);
|
||||
}
|
||||
|
||||
View * ExamPopUpController::view() {
|
||||
@@ -55,27 +53,10 @@ ExamPopUpController::ContentView::ContentView(Responder * parentResponder) :
|
||||
}, parentResponder), KDFont::SmallFont),
|
||||
m_okButton(parentResponder, I18n::Message::Ok, Invocation([](void * context, void * sender) {
|
||||
ExamPopUpController * controller = (ExamPopUpController *)context;
|
||||
GlobalPreferences::ExamMode nextExamMode = controller->isActivatingExamMode() ? GlobalPreferences::ExamMode::Activate : GlobalPreferences::ExamMode::Deactivate;
|
||||
GlobalPreferences::sharedGlobalPreferences()->setExamMode(nextExamMode);
|
||||
Preferences * preferences = Preferences::sharedPreferences();
|
||||
GlobalPreferences::sharedGlobalPreferences()->setExamMode(controller->isActivatingExamMode());
|
||||
AppsContainer * container = AppsContainer::sharedAppsContainer();
|
||||
if (controller->isActivatingExamMode()) {
|
||||
container->reset();
|
||||
switch ((int)preferences->colorOfLED()) {
|
||||
case 0:
|
||||
Ion::LED::setColor(KDColorWhite);
|
||||
break;
|
||||
case 1:
|
||||
Ion::LED::setColor(KDColorGreen);
|
||||
break;
|
||||
case 2:
|
||||
Ion::LED::setColor(KDColorBlue);
|
||||
break;
|
||||
case 3:
|
||||
Ion::LED::setColor(KDColorYellow);
|
||||
break;
|
||||
}
|
||||
Ion::LED::setBlinking(1000, 0.1f);
|
||||
container->activateExamMode();
|
||||
} else {
|
||||
Ion::LED::setColor(KDColorBlack);
|
||||
Ion::LED::updateColorWithPlugAndCharge();
|
||||
|
||||
@@ -5,6 +5,22 @@ GlobalPreferences * GlobalPreferences::sharedGlobalPreferences() {
|
||||
return &globalPreferences;
|
||||
}
|
||||
|
||||
bool GlobalPreferences::examMode() const {
|
||||
if (m_examMode == ExamMode::Unknown) {
|
||||
m_examMode = (ExamMode)Ion::ExamMode::FetchExamMode();
|
||||
}
|
||||
assert((int)m_examMode == 0 || (int)m_examMode == 1);
|
||||
return (bool)m_examMode;
|
||||
}
|
||||
|
||||
void GlobalPreferences::setExamMode(bool activateExamMode) {
|
||||
if (((bool)examMode()) == activateExamMode) {
|
||||
return;
|
||||
}
|
||||
Ion::ExamMode::ToggleExamMode();
|
||||
m_examMode = (ExamMode)activateExamMode;
|
||||
}
|
||||
|
||||
void GlobalPreferences::setBrightnessLevel(int brightnessLevel) {
|
||||
if (m_brightnessLevel != brightnessLevel) {
|
||||
brightnessLevel = brightnessLevel < 0 ? 0 : brightnessLevel;
|
||||
|
||||
@@ -5,15 +5,11 @@
|
||||
|
||||
class GlobalPreferences {
|
||||
public:
|
||||
enum class ExamMode {
|
||||
Activate,
|
||||
Deactivate
|
||||
};
|
||||
static GlobalPreferences * sharedGlobalPreferences();
|
||||
I18n::Language language() const { return m_language; }
|
||||
void setLanguage(I18n::Language language) { m_language = language; }
|
||||
ExamMode examMode() const { return m_examMode; }
|
||||
void setExamMode(ExamMode examMode) { m_examMode = examMode; }
|
||||
bool examMode() const;
|
||||
void setExamMode(bool activateExamMode);
|
||||
bool showPopUp() const { return m_showPopUp; }
|
||||
void setShowPopUp(bool showPopUp) { m_showPopUp = showPopUp; }
|
||||
int brightnessLevel() const { return m_brightnessLevel; }
|
||||
@@ -22,11 +18,18 @@ public:
|
||||
private:
|
||||
GlobalPreferences() :
|
||||
m_language(I18n::Language::EN),
|
||||
m_examMode(ExamMode::Deactivate),
|
||||
m_examMode(ExamMode::Unknown),
|
||||
m_showPopUp(true),
|
||||
m_brightnessLevel(Ion::Backlight::MaxBrightness) {}
|
||||
I18n::Language m_language;
|
||||
ExamMode m_examMode;
|
||||
enum class ExamMode : uint8_t {
|
||||
Deactivate = 0,
|
||||
Activate = 1,
|
||||
Unknown = 2
|
||||
};
|
||||
static_assert((uint8_t)GlobalPreferences::ExamMode::Deactivate == 0, "GlobalPreferences::setExamMode and examMode() are not right");
|
||||
static_assert((uint8_t)GlobalPreferences::ExamMode::Activate == 1, "GlobalPreferences::setExamMode and examMode() are not right");
|
||||
mutable ExamMode m_examMode;
|
||||
bool m_showPopUp;
|
||||
int m_brightnessLevel;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "logo_controller.h"
|
||||
#include "power_on_self_test.h"
|
||||
#include <apps/apps_container.h>
|
||||
#include <apps/global_preferences.h>
|
||||
#include <ion/led.h>
|
||||
|
||||
namespace OnBoarding {
|
||||
@@ -45,6 +47,11 @@ void LogoController::viewWillAppear() {
|
||||
void LogoController::viewDidDisappear() {
|
||||
if (m_didPerformTests) {
|
||||
Ion::LED::setColor(m_previousLEDColor);
|
||||
/* TODO: instead of setting again the exam mode, put the previous led color
|
||||
* AND BLINKING.*/
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode()) {
|
||||
AppsContainer::sharedAppsContainer()->activateExamMode();
|
||||
}
|
||||
}
|
||||
ViewController::viewDidDisappear();
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ EditionLinear = "Linear "
|
||||
Edition2D = "Natürlich "
|
||||
ComplexFormat = "Komplex"
|
||||
ExamMode = "Testmodus"
|
||||
ActivateExamMode = "Start Testmodus"
|
||||
ExamModeActive = "Testmodus: aktiv"
|
||||
ActivateExamMode = "Starten Testmodus"
|
||||
ExamModeActive = "Wieder starten Testmodus"
|
||||
About = "Über"
|
||||
Degrees = "Grad "
|
||||
Gradians = "Gone "
|
||||
|
||||
@@ -8,7 +8,7 @@ Edition2D = "Natural "
|
||||
ComplexFormat = "Complex format"
|
||||
ExamMode = "Exam mode"
|
||||
ActivateExamMode = "Activate exam mode"
|
||||
ExamModeActive = "Exam mode: active"
|
||||
ExamModeActive = "Reactivate exam mode"
|
||||
About = "About"
|
||||
Degrees = "Degrees "
|
||||
Gradians = "Gradians "
|
||||
|
||||
@@ -8,7 +8,7 @@ Edition2D = "Natural "
|
||||
ComplexFormat = "Forma compleja"
|
||||
ExamMode = "Modo examen"
|
||||
ActivateExamMode = "Activar el modo examen"
|
||||
ExamModeActive = "Modo examen: activo"
|
||||
ExamModeActive = "Reactivar el modo examen"
|
||||
About = "Acerca"
|
||||
Degrees = "Grados "
|
||||
Gradians = "Gradianes "
|
||||
|
||||
@@ -8,7 +8,7 @@ Edition2D = "Naturelle "
|
||||
ComplexFormat = "Forme complexe"
|
||||
ExamMode = "Mode examen"
|
||||
ActivateExamMode = "Activer le mode examen"
|
||||
ExamModeActive = "Mode examen: actif"
|
||||
ExamModeActive = "Réactiver le mode examen"
|
||||
About = "À propos"
|
||||
Degrees = "Degrés "
|
||||
Gradians = "Grades "
|
||||
|
||||
@@ -6,9 +6,9 @@ EditionMode = "Formato escrita "
|
||||
EditionLinear = "Em linha "
|
||||
Edition2D = "Natural "
|
||||
ComplexFormat = "Complexos"
|
||||
ExamMode = "Modo de Exame"
|
||||
ActivateExamMode = "Inicio modo de exame"
|
||||
ExamModeActive = "Modo de exame : ativo"
|
||||
ExamMode = "Modo de exame"
|
||||
ActivateExamMode = "Ativar o modo de exame"
|
||||
ExamModeActive = "Reativar o modo de exame"
|
||||
About = "Acerca"
|
||||
Degrees = "Graus "
|
||||
Gradians = "Grados "
|
||||
|
||||
@@ -28,9 +28,6 @@ bool ExamModeController::handleEvent(Ion::Events::Event event) {
|
||||
I18n::Message childLabel = m_messageTreeModel->children(selectedRow())->label();
|
||||
if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) {
|
||||
if (childLabel == I18n::Message::ActivateExamMode || childLabel == I18n::Message::ExamModeActive) {
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate) {
|
||||
return false;
|
||||
}
|
||||
AppsContainer::sharedAppsContainer()->displayExamModePopUp(true);
|
||||
return true;
|
||||
}
|
||||
@@ -81,7 +78,7 @@ void ExamModeController::willDisplayCellForIndex(HighlightCell * cell, int index
|
||||
GenericSubController::willDisplayCellForIndex(cell, index);
|
||||
I18n::Message thisLabel = m_messageTreeModel->children(index)->label();
|
||||
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate && (thisLabel == I18n::Message::ActivateExamMode || thisLabel == I18n::Message::ExamModeActive)) {
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode() && (thisLabel == I18n::Message::ActivateExamMode || thisLabel == I18n::Message::ExamModeActive)) {
|
||||
MessageTableCell * myCell = (MessageTableCell *)cell;
|
||||
myCell->setMessage(I18n::Message::ExamModeActive);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ void TitleBarView::layoutSubviews() {
|
||||
m_preferenceView.setFrame(KDRect(Metric::TitleBarExternHorizontalMargin, 0, m_preferenceView.minimalSizeForOptimalDisplay().width(), bounds().height()));
|
||||
KDSize batterySize = m_batteryView.minimalSizeForOptimalDisplay();
|
||||
m_batteryView.setFrame(KDRect(bounds().width() - batterySize.width() - Metric::TitleBarExternHorizontalMargin, (bounds().height()- batterySize.height())/2, batterySize));
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode() == GlobalPreferences::ExamMode::Activate) {
|
||||
if (GlobalPreferences::sharedGlobalPreferences()->examMode()) {
|
||||
m_examModeIconView.setFrame(KDRect(k_examIconMargin, (bounds().height() - k_examIconHeight)/2, k_examIconWidth, k_examIconHeight));
|
||||
} else {
|
||||
m_examModeIconView.setFrame(KDRectZero);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <ion/console.h>
|
||||
#include <ion/display.h>
|
||||
#include <ion/events.h>
|
||||
#include <ion/exam_mode.h>
|
||||
#include <ion/keyboard.h>
|
||||
#include <ion/led.h>
|
||||
#include <ion/power.h>
|
||||
|
||||
13
ion/include/ion/exam_mode.h
Normal file
13
ion/include/ion/exam_mode.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef ION_EXAM_MODE_H
|
||||
#define ION_EXAM_MODE_H
|
||||
|
||||
namespace Ion {
|
||||
namespace ExamMode {
|
||||
|
||||
bool FetchExamMode();
|
||||
void ToggleExamMode();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -17,6 +17,7 @@ ion_src += $(addprefix ion/src/shared/, \
|
||||
dummy/battery.cpp \
|
||||
dummy/display.cpp \
|
||||
dummy/events_modifier.cpp \
|
||||
dummy/exam_mode.cpp \
|
||||
dummy/fcc_id.cpp \
|
||||
dummy/led.cpp \
|
||||
dummy/keyboard.cpp \
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#ifndef ION_DEVICE_N0100_CONFIG_FLASH_H
|
||||
#define ION_DEVICE_N0100_CONFIG_FLASH_H
|
||||
#ifndef ION_DEVICE_N0100_CONFIG_INTERNAL_FLASH_H
|
||||
#define ION_DEVICE_N0100_CONFIG_INTERNAL_FLASH_H
|
||||
|
||||
#include <regs/regs.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace Flash {
|
||||
namespace InternalFlash {
|
||||
namespace Config {
|
||||
|
||||
constexpr static uint32_t StartAddress = 0x08000000;
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <drivers/reset.h>
|
||||
#include "config/flash.h"
|
||||
#include "config/internal_flash.h"
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
@@ -10,7 +10,7 @@ void coreWhilePlugged() {
|
||||
* might be plugged in. Doing a full core reset would result in the device
|
||||
* entering the ST DFU bootloader. By performing a jump-reset, we mimic the
|
||||
* core reset without entering ST bootloader.*/
|
||||
jump(Flash::Config::StartAddress);
|
||||
jump(InternalFlash::Config::StartAddress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,14 +9,16 @@
|
||||
* This will let us use shortcuts such as ">FLASH" to ask for a given section to
|
||||
* be stored in Flash. */
|
||||
MEMORY {
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
|
||||
FLASH_FIRST_SECTOR (rx) : ORIGIN = 0x08000000, LENGTH = 16K
|
||||
FLASH_SECOND_SECTOR (rx) : ORIGIN = (0x08000000 + 16K), LENGTH = 16K
|
||||
FLASH_LAST_SECTORS (rx) : ORIGIN = (0x08000000 + 32K), LENGTH = (1024K - 32K)
|
||||
SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
STACK_SIZE = 32K;
|
||||
|
||||
SECTIONS {
|
||||
.isr_vector_table ORIGIN(FLASH) : {
|
||||
.isr_vector_table ORIGIN(FLASH_FIRST_SECTOR) : {
|
||||
/* 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
|
||||
@@ -31,30 +33,37 @@ SECTIONS {
|
||||
* convenient: using function pointers, we can easily point to the service
|
||||
* routine for each interrupt. */
|
||||
KEEP(*(.isr_vector_table))
|
||||
} >FLASH
|
||||
} >FLASH_FIRST_SECTOR
|
||||
|
||||
.header : {
|
||||
KEEP(*(.header))
|
||||
} >FLASH
|
||||
} >FLASH_FIRST_SECTOR
|
||||
|
||||
.exam_mode_persistence ORIGIN(FLASH_SECOND_SECTOR): {
|
||||
_exam_mode_persistence_start = .;
|
||||
/* Note: We don't increment "." here, we set it. */
|
||||
. = (ORIGIN(FLASH_SECOND_SECTOR) + LENGTH(FLASH_SECOND_SECTOR));
|
||||
_exam_mode_persistence_end = .;
|
||||
} >FLASH_SECOND_SECTOR
|
||||
|
||||
.text : {
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
} >FLASH
|
||||
} >FLASH_LAST_SECTORS
|
||||
|
||||
.init_array : {
|
||||
. = ALIGN(4);
|
||||
_init_array_start = .;
|
||||
KEEP (*(.init_array*))
|
||||
_init_array_end = .;
|
||||
} >FLASH
|
||||
} >FLASH_LAST_SECTORS
|
||||
|
||||
.rodata : {
|
||||
. = ALIGN(4);
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >FLASH
|
||||
} >FLASH_LAST_SECTORS
|
||||
|
||||
.data : {
|
||||
/* The data section is written to Flash but linked as if it were in RAM.
|
||||
@@ -75,7 +84,7 @@ SECTIONS {
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
_data_section_end_ram = .;
|
||||
} >SRAM AT> FLASH
|
||||
} >SRAM AT> FLASH_LAST_SECTORS
|
||||
|
||||
.bss : {
|
||||
/* The bss section contains data for all uninitialized variables
|
||||
|
||||
@@ -35,10 +35,36 @@ void initMPU() {
|
||||
|
||||
// 2. MPU settings
|
||||
// 2.1 Configure a MPU region for the FMC memory area
|
||||
// This is needed for interfacing with the LCD
|
||||
MPU.RNR()->setREGION(0x00);
|
||||
/* This is needed for interfacing with the LCD
|
||||
* We define the whole FMC memory bank 1 as strongly ordered, non-executable
|
||||
* and not accessible. We define the FMC command and data addresses as
|
||||
* writeable non-cachable, non-buffereable and non shareable. */
|
||||
int sector = 0;
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x60000000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32MB);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_256MB);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::NoAccess);
|
||||
MPU.RASR()->setXN(true);
|
||||
MPU.RASR()->setTEX(2);
|
||||
MPU.RASR()->setS(0);
|
||||
MPU.RASR()->setC(0);
|
||||
MPU.RASR()->setB(0);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x60000000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32B);
|
||||
MPU.RASR()->setXN(true);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
|
||||
MPU.RASR()->setTEX(2);
|
||||
MPU.RASR()->setS(0);
|
||||
MPU.RASR()->setC(0);
|
||||
MPU.RASR()->setB(0);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x60000000+0x20000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32B);
|
||||
MPU.RASR()->setXN(true);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
|
||||
MPU.RASR()->setTEX(2);
|
||||
@@ -56,7 +82,7 @@ void initMPU() {
|
||||
* strongly ordered, non-executable and not accessible. Plus, we define the
|
||||
* Quad-SPI region corresponding to the Expternal Chip as executable and
|
||||
* fully accessible (AN4861). */
|
||||
MPU.RNR()->setREGION(0x01);
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x90000000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_256MB);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::NoAccess);
|
||||
@@ -67,7 +93,7 @@ void initMPU() {
|
||||
MPU.RASR()->setB(0);
|
||||
MPU.RASR()->setENABLE(true);
|
||||
|
||||
MPU.RNR()->setREGION(0x02);
|
||||
MPU.RNR()->setREGION(sector++);
|
||||
MPU.RBAR()->setADDR(0x90000000);
|
||||
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_8MB);
|
||||
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#ifndef ION_DEVICE_N0110_CONFIG_FLASH_H
|
||||
#define ION_DEVICE_N0110_CONFIG_FLASH_H
|
||||
#ifndef ION_DEVICE_N0110_CONFIG_INTERNAL_FLASH_H
|
||||
#define ION_DEVICE_N0110_CONFIG_INTERNAL_FLASH_H
|
||||
|
||||
#include <regs/regs.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace Flash {
|
||||
namespace InternalFlash {
|
||||
namespace Config {
|
||||
|
||||
constexpr static uint32_t StartAddress = 0x08000000;
|
||||
@@ -12,7 +12,8 @@
|
||||
MEMORY {
|
||||
INTERNAL_FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 64K
|
||||
SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
EXTERNAL_FLASH (rx) : ORIGIN = 0x90000000, LENGTH = 8M
|
||||
EXTERNAL_FLASH_FIRST_SECTOR (rx) : ORIGIN = 0x90000000, LENGTH = 4K
|
||||
EXTERNAL_FLASH_NEXT_SECTORS (rx) : ORIGIN = (0x90000000 + 4K), LENGTH = (8M - 4K)
|
||||
/*
|
||||
ITCM (rwx) : ORIGIN = 0x00000000, LENGTH = 16K
|
||||
DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
|
||||
@@ -55,6 +56,13 @@ SECTIONS {
|
||||
*(.text._ZL22jump_to_external_flashv)
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.exam_mode_persistence ORIGIN(EXTERNAL_FLASH_FIRST_SECTOR): {
|
||||
_exam_mode_persistence_start = .;
|
||||
/* Note: We don't increment "." here, we set it. */
|
||||
. = (ORIGIN(EXTERNAL_FLASH_FIRST_SECTOR) + LENGTH(EXTERNAL_FLASH_FIRST_SECTOR));
|
||||
_exam_mode_persistence_end = .;
|
||||
} >EXTERNAL_FLASH_FIRST_SECTOR
|
||||
|
||||
/* Use boot routine and required dependencies */
|
||||
/* We're relying on symbols being in their own sub-section. On GCC, this is
|
||||
* done with -fdata-sections -ffunction-sections */
|
||||
@@ -120,12 +128,12 @@ SECTIONS {
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
} >EXTERNAL_FLASH
|
||||
} >EXTERNAL_FLASH_NEXT_SECTORS
|
||||
|
||||
.rodata.external : {
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >EXTERNAL_FLASH
|
||||
} >EXTERNAL_FLASH_NEXT_SECTORS
|
||||
|
||||
.init_array : {
|
||||
. = ALIGN(4);
|
||||
@@ -202,3 +210,8 @@ 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_persistence .text.internal);
|
||||
NOCROSSREFS_TO(.exam_mode_persistence .rodata.internal);
|
||||
NOCROSSREFS_TO(.exam_mode_persistence .isr_vector_table);
|
||||
NOCROSSREFS_TO(.exam_mode_persistence .header);
|
||||
|
||||
@@ -6,8 +6,10 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \
|
||||
crc32.cpp \
|
||||
display.cpp \
|
||||
events_keyboard_platform.cpp \
|
||||
exam_mode.cpp \
|
||||
external_flash.cpp \
|
||||
flash.cpp \
|
||||
internal_flash.cpp \
|
||||
keyboard.cpp \
|
||||
led.cpp \
|
||||
power.cpp\
|
||||
|
||||
74
ion/src/device/shared/drivers/exam_mode.cpp
Normal file
74
ion/src/device/shared/drivers/exam_mode.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <ion/exam_mode.h>
|
||||
#include "flash.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace ExamMode {
|
||||
|
||||
extern "C" {
|
||||
extern char _exam_mode_persistence_start;
|
||||
extern char _exam_mode_persistence_end;
|
||||
}
|
||||
|
||||
/* The exam mode is written in flash so that it is resilient to resets.
|
||||
* We erase the dedicated flash sector (all bits written to 1) and, upon
|
||||
* activating or deactivating the exam mode we write one bit to 0. To determine
|
||||
* if we are in exam mode, we count the number of leading 0 bits. If it is even,
|
||||
* the exam mode is deactivated, if it is odd, the exam mode is activated. */
|
||||
|
||||
/* significantExamModeAddress returns the first uint32_t * in the exam mode
|
||||
* flash sector that does not point to 0. If this flash sector has only 0s, it
|
||||
* is erased (to 1) and significantExamModeAddress returns the start of the
|
||||
* sector. */
|
||||
|
||||
uint32_t * SignificantExamModeAddress() {
|
||||
uint32_t * persitence_start = (uint32_t *)&_exam_mode_persistence_start;
|
||||
uint32_t * persitence_end = (uint32_t *)&_exam_mode_persistence_end;
|
||||
while (persitence_start < persitence_end && *persitence_start == 0x0) {
|
||||
// Skip even number of zero bits
|
||||
persitence_start++;
|
||||
}
|
||||
if (persitence_start == persitence_end) {
|
||||
assert(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_persistence_start) >= 0);
|
||||
Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_persistence_start));
|
||||
return (uint32_t *)&_exam_mode_persistence_start;
|
||||
}
|
||||
return persitence_start;
|
||||
}
|
||||
|
||||
size_t firstOneBit(int i, size_t size) {
|
||||
int minShift = 0;
|
||||
int maxShift = size;
|
||||
while (maxShift > minShift+1) {
|
||||
int shift = (minShift + maxShift)/2;
|
||||
int shifted = i >> shift;
|
||||
if (shifted == 0) {
|
||||
maxShift = shift;
|
||||
} else {
|
||||
minShift = shift;
|
||||
}
|
||||
}
|
||||
return maxShift;
|
||||
}
|
||||
|
||||
bool FetchExamMode() {
|
||||
uint32_t * readingAddress = SignificantExamModeAddress();
|
||||
size_t numberOfLeading0 = 32 - firstOneBit(*readingAddress, 32);
|
||||
return numberOfLeading0 % 2 == 1;
|
||||
}
|
||||
|
||||
void ToggleExamMode() {
|
||||
uint32_t * writingAddress = SignificantExamModeAddress();
|
||||
assert(*writingAddress != 0);
|
||||
// Compute the new value with one bit switched
|
||||
uint8_t numberOfLeadingZeroes = 32 - firstOneBit(*writingAddress, 32);
|
||||
/* When writing in flash, we can only switch a 1 to a 0. If we want to switch
|
||||
* the fifth bit in a byte, we can thus write "11110111". */
|
||||
uint32_t newValue = ~(1 << (31 - numberOfLeadingZeroes));
|
||||
|
||||
// Write the value in flash
|
||||
Ion::Device::Flash::WriteMemory((uint8_t *)writingAddress, (uint8_t *)&newValue, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -402,7 +402,7 @@ void MassErase() {
|
||||
set_as_memory_mapped();
|
||||
}
|
||||
|
||||
void EraseSector(int i) {
|
||||
void __attribute__((noinline)) EraseSector(int i) {
|
||||
assert(i >= 0 && i < Config::NumberOfSectors);
|
||||
unset_memory_mapped_mode();
|
||||
unlockFlash();
|
||||
@@ -413,7 +413,7 @@ void EraseSector(int i) {
|
||||
set_as_memory_mapped();
|
||||
}
|
||||
|
||||
void WriteMemory(uint8_t * destination, const uint8_t * source, size_t length) {
|
||||
void __attribute__((noinline)) WriteMemory(uint8_t * destination, const uint8_t * source, size_t length) {
|
||||
if (Config::NumberOfSectors == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,253 +1,53 @@
|
||||
#include "flash.h"
|
||||
#include <drivers/cache.h>
|
||||
#include <drivers/config/flash.h>
|
||||
#include "external_flash.h"
|
||||
#include "internal_flash.h"
|
||||
#include <drivers/config/internal_flash.h>
|
||||
#include <drivers/config/external_flash.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace Flash {
|
||||
|
||||
using namespace Regs;
|
||||
|
||||
static inline void wait() {
|
||||
/* Issue a DSB instruction to guarantee the completion of a previous access
|
||||
* to FLASH_CR register or data write operation. (RM0431) */
|
||||
Cache::dsb();
|
||||
// Wait for pending Flash operations to complete
|
||||
while (FLASH.SR()->getBSY()) {
|
||||
}
|
||||
}
|
||||
|
||||
static void open() {
|
||||
// Unlock the Flash configuration register if needed
|
||||
if (FLASH.CR()->getLOCK()) {
|
||||
FLASH.KEYR()->set(0x45670123);
|
||||
FLASH.KEYR()->set(0xCDEF89AB);
|
||||
}
|
||||
assert(FLASH.CR()->getLOCK() == false);
|
||||
|
||||
// Set the programming parallelism
|
||||
FLASH.CR()->setPSIZE(MemoryAccessWidth);
|
||||
}
|
||||
|
||||
static void close() {
|
||||
// Clear error flags
|
||||
class FLASH::SR sr(0);
|
||||
// Error flags are cleared by writing 1
|
||||
sr.setERSERR(true);
|
||||
sr.setPGPERR(true);
|
||||
sr.setPGAERR(true);
|
||||
sr.setWRPERR(true);
|
||||
sr.setEOP(true);
|
||||
FLASH.SR()->set(sr);
|
||||
|
||||
// Lock the Flash configuration register
|
||||
assert(!FLASH.CR()->getMER());
|
||||
assert(!FLASH.CR()->getSER());
|
||||
assert(!FLASH.CR()->getPG());
|
||||
FLASH.CR()->setLOCK(true);
|
||||
|
||||
// Purge Data and instruction cache
|
||||
#if REGS_FLASH_CONFIG_ART
|
||||
if (FLASH.ACR()->getARTEN()) {
|
||||
FLASH.ACR()->setARTEN(false);
|
||||
FLASH.ACR()->setARTRST(true);
|
||||
FLASH.ACR()->setARTRST(false);
|
||||
FLASH.ACR()->setARTEN(true);
|
||||
}
|
||||
#else
|
||||
if (FLASH.ACR()->getDCEN()) {
|
||||
FLASH.ACR()->setDCEN(false);
|
||||
FLASH.ACR()->setDCRST(true);
|
||||
FLASH.ACR()->setDCRST(false);
|
||||
FLASH.ACR()->setDCEN(true);
|
||||
}
|
||||
if (FLASH.ACR()->getICEN()) {
|
||||
FLASH.ACR()->setICEN(false);
|
||||
FLASH.ACR()->setICRST(true);
|
||||
FLASH.ACR()->setICRST(false);
|
||||
FLASH.ACR()->setICEN(true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Compile-time log2
|
||||
static inline constexpr size_t clog2(size_t input) {
|
||||
return (input == 1) ? 0 : clog2(input/2)+1;
|
||||
}
|
||||
|
||||
// Align a pointer to a given type's boundaries
|
||||
// Returns a value that is lower or equal to input
|
||||
template <typename T>
|
||||
static inline T * align(void * input) {
|
||||
size_t k = clog2(sizeof(T));
|
||||
return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(input) & ~((1<<k) - 1));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T eat(void * ptr) {
|
||||
T * pointer = *reinterpret_cast<T **>(ptr);
|
||||
T result = *pointer;
|
||||
*reinterpret_cast<T **>(ptr) = pointer+1;
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline ptrdiff_t byte_offset(void * p1, void * p2) {
|
||||
return reinterpret_cast<uint8_t *>(p2) - reinterpret_cast<uint8_t *>(p1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T min(T i, T j) {
|
||||
return (i<j) ? i : j;
|
||||
}
|
||||
|
||||
static void flash_memcpy(uint8_t * destination, uint8_t * source, size_t length) {
|
||||
/* RM0402 3.5.4
|
||||
* It is not allowed to program data to the Flash memory that would cross the
|
||||
* 128-bit row boundary. In such a case, the write operation is not performed
|
||||
* and a program alignment error flag (PGAERR) is set in the FLASH_SR
|
||||
* register.
|
||||
* The write access type (byte, half-word, word or double word) must
|
||||
* correspond to the type of parallelism chosen (x8, x16, x32 or x64). If not,
|
||||
* the write operation is not performed and a program parallelism error flag
|
||||
* (PGPERR) is set in the FLASH_SR register. */
|
||||
|
||||
static_assert(
|
||||
sizeof(MemoryAccessType) == 1 ||
|
||||
sizeof(MemoryAccessType) == 2 ||
|
||||
sizeof(MemoryAccessType) == 4 ||
|
||||
sizeof(MemoryAccessType) == 8,
|
||||
"Invalid MemoryAccessType");
|
||||
|
||||
/* So we may only perform memory writes with pointers of type MemoryAccessType
|
||||
* and we must make sure to never cross 128 bit boundaries. This second
|
||||
* requirement is satisfied iif the pointers are aligned on MemoryAccessType
|
||||
* boundaries.
|
||||
* Long story short: we want to perform writes to aligned(MemoryAccessType *).
|
||||
*/
|
||||
|
||||
/* Step 1 - Copy a header if needed
|
||||
* We start by copying a Header, whose size is MemoryAccessType, to bring us
|
||||
* back on aligned tracks.
|
||||
*
|
||||
* _AlignedDst _DESTINATION
|
||||
* | |
|
||||
* --+--------+--------+--------+--------+--------+--------+--
|
||||
* | || | | | || |
|
||||
*---+--------+--------+--------+--------+--------+--------+--
|
||||
* |<------------ Header ------------->|
|
||||
* |-- HeaderDelta ->|
|
||||
*/
|
||||
|
||||
MemoryAccessType * alignedDestination = align<MemoryAccessType>(destination);
|
||||
ptrdiff_t headerDelta = byte_offset(alignedDestination, destination);
|
||||
assert(headerDelta >= 0 && headerDelta < static_cast<ptrdiff_t>(sizeof(MemoryAccessType)));
|
||||
|
||||
if (headerDelta > 0) {
|
||||
// At this point, alignedDestination < destination
|
||||
// We'll then retrieve the current value at alignedDestination, fill it with
|
||||
// bytes from source, and write it back at alignedDestination.
|
||||
|
||||
// First, retrieve the current value at alignedDestination
|
||||
MemoryAccessType header = *alignedDestination;
|
||||
|
||||
// Then copy headerLength bytes from source and put them in the header
|
||||
uint8_t * headerStart = reinterpret_cast<uint8_t *>(&header);
|
||||
// Here's where source data shall start being copied in the header
|
||||
uint8_t * headerDataStart = headerStart + headerDelta;
|
||||
// And here's where it should end
|
||||
uint8_t * headerDataEnd = min<uint8_t *>(
|
||||
headerStart + sizeof(MemoryAccessType), // Either at the end of the header
|
||||
headerDataStart + length // or whenever src runs out of data
|
||||
);
|
||||
for (uint8_t * h = headerDataStart; h<headerDataEnd; h++) {
|
||||
*h = eat<uint8_t>(&source);
|
||||
}
|
||||
|
||||
// Then eventually write the header back into the aligned destination
|
||||
*alignedDestination++ = header;
|
||||
wait();
|
||||
}
|
||||
|
||||
/* Step 2 - Copy the bulk of the data
|
||||
* At this point, we can use aligned MemoryAccessType pointers. */
|
||||
|
||||
MemoryAccessType * lastAlignedDestination = align<MemoryAccessType>(destination + length);
|
||||
while (alignedDestination < lastAlignedDestination) {
|
||||
*alignedDestination++ = eat<MemoryAccessType>(&source);
|
||||
wait();
|
||||
}
|
||||
|
||||
/* Step 3 - Copy a footer if needed
|
||||
* Some unaligned data can be pending at the end. Let's take care of it like
|
||||
* we did for the header.
|
||||
*
|
||||
* _alignedDst _Destination+length
|
||||
* | |
|
||||
* --+--------+--------+--------+--------+--------+--------+--
|
||||
* | || | | | || |
|
||||
*---+--------+--------+--------+--------+--------+--------+--
|
||||
* |<------------ Footer ------------->|
|
||||
* |- footerLength ->|
|
||||
*/
|
||||
|
||||
ptrdiff_t footerLength = byte_offset(alignedDestination, destination + length);
|
||||
assert(footerLength < static_cast<ptrdiff_t>(sizeof(MemoryAccessType)));
|
||||
if (footerLength > 0) {
|
||||
assert(alignedDestination == lastAlignedDestination);
|
||||
|
||||
// First, retrieve the current value at alignedDestination
|
||||
MemoryAccessType footer = *alignedDestination;
|
||||
|
||||
/* Then copy footerLength bytes from source and put them at the beginning of
|
||||
* the footer */
|
||||
uint8_t * footerPointer = reinterpret_cast<uint8_t *>(&footer);
|
||||
for (ptrdiff_t i=0; i<footerLength; i++) {
|
||||
footerPointer[i] = eat<uint8_t>(&source);
|
||||
}
|
||||
|
||||
// Then eventually write the footer back into the aligned destination
|
||||
*alignedDestination = footer;
|
||||
wait();
|
||||
}
|
||||
int TotalNumberOfSectors() {
|
||||
return InternalFlash::Config::NumberOfSectors + ExternalFlash::Config::NumberOfSectors;
|
||||
}
|
||||
|
||||
int SectorAtAddress(uint32_t address) {
|
||||
for (int i = 0; i < Config::NumberOfSectors; i++) {
|
||||
if (address >= Config::SectorAddresses[i] && address < Config::SectorAddresses[i+1]) {
|
||||
return i;
|
||||
}
|
||||
if (address >= InternalFlash::Config::StartAddress
|
||||
&& address <= InternalFlash::Config::EndAddress)
|
||||
{
|
||||
return InternalFlash::SectorAtAddress(address);
|
||||
}
|
||||
if (address >= ExternalFlash::Config::StartAddress
|
||||
&& address <= ExternalFlash::Config::EndAddress)
|
||||
{
|
||||
return InternalFlash::Config::NumberOfSectors + ExternalFlash::SectorAtAddress(address - ExternalFlash::Config::StartAddress);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MassErase() {
|
||||
open();
|
||||
FLASH.CR()->setMER(true);
|
||||
FLASH.CR()->setSTRT(true);
|
||||
wait();
|
||||
FLASH.CR()->setMER(false);
|
||||
close();
|
||||
InternalFlash::MassErase();
|
||||
ExternalFlash::MassErase();
|
||||
}
|
||||
|
||||
void EraseSector(int i) {
|
||||
assert(i >= 0 && i < Config::NumberOfSectors);
|
||||
open();
|
||||
FLASH.CR()->setSNB(i);
|
||||
FLASH.CR()->setSER(true);
|
||||
FLASH.CR()->setSTRT(true);
|
||||
wait();
|
||||
FLASH.CR()->setSNB(0);
|
||||
FLASH.CR()->setSER(false);
|
||||
close();
|
||||
assert(i >= 0 && i < TotalNumberOfSectors());
|
||||
if (i < InternalFlash::Config::NumberOfSectors) {
|
||||
InternalFlash::EraseSector(i);
|
||||
} else {
|
||||
ExternalFlash::EraseSector(i - InternalFlash::Config::NumberOfSectors);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteMemory(uint8_t * destination, uint8_t * source, size_t length) {
|
||||
open();
|
||||
FLASH.CR()->setPG(true);
|
||||
flash_memcpy(destination, source, length);
|
||||
FLASH.CR()->setPG(false);
|
||||
close();
|
||||
assert(SectorAtAddress((uint32_t)destination) >= 0);
|
||||
if (SectorAtAddress((uint32_t)destination) < InternalFlash::Config::NumberOfSectors) {
|
||||
InternalFlash::WriteMemory(destination, source, length);
|
||||
} else {
|
||||
ExternalFlash::WriteMemory(destination - ExternalFlash::Config::StartAddress, source, length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,24 +2,19 @@
|
||||
#define ION_DEVICE_SHARED_FLASH_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <regs/regs.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace Flash {
|
||||
|
||||
void MassErase();
|
||||
|
||||
int TotalNumberOfSectors();
|
||||
int SectorAtAddress(uint32_t address);
|
||||
|
||||
void MassErase();
|
||||
void EraseSector(int i);
|
||||
|
||||
void WriteMemory(uint8_t * destination, uint8_t * source, size_t length);
|
||||
|
||||
/* The Device is powered by a 2.8V LDO. This allows us to perform writes to the
|
||||
* Flash 32 bits at once. */
|
||||
constexpr Regs::FLASH::CR::PSIZE MemoryAccessWidth = Regs::FLASH::CR::PSIZE::X32;
|
||||
typedef uint32_t MemoryAccessType;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
256
ion/src/device/shared/drivers/internal_flash.cpp
Normal file
256
ion/src/device/shared/drivers/internal_flash.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
#include "internal_flash.h"
|
||||
#include <drivers/cache.h>
|
||||
#include <drivers/config/internal_flash.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace InternalFlash {
|
||||
|
||||
using namespace Regs;
|
||||
|
||||
static inline void wait() {
|
||||
/* Issue a DSB instruction to guarantee the completion of a previous access
|
||||
* to FLASH_CR register or data write operation. (RM0431) */
|
||||
Cache::dsb();
|
||||
// Wait for pending Flash operations to complete
|
||||
while (FLASH.SR()->getBSY()) {
|
||||
}
|
||||
}
|
||||
|
||||
static void open() {
|
||||
// Unlock the Flash configuration register if needed
|
||||
if (FLASH.CR()->getLOCK()) {
|
||||
FLASH.KEYR()->set(0x45670123);
|
||||
FLASH.KEYR()->set(0xCDEF89AB);
|
||||
}
|
||||
assert(FLASH.CR()->getLOCK() == false);
|
||||
|
||||
// Set the programming parallelism
|
||||
FLASH.CR()->setPSIZE(MemoryAccessWidth);
|
||||
}
|
||||
|
||||
static void close() {
|
||||
// Clear error flags
|
||||
class FLASH::SR sr(0);
|
||||
// Error flags are cleared by writing 1
|
||||
sr.setERSERR(true);
|
||||
sr.setPGPERR(true);
|
||||
sr.setPGAERR(true);
|
||||
sr.setWRPERR(true);
|
||||
sr.setEOP(true);
|
||||
FLASH.SR()->set(sr);
|
||||
|
||||
// Lock the Flash configuration register
|
||||
assert(!FLASH.CR()->getMER());
|
||||
assert(!FLASH.CR()->getSER());
|
||||
assert(!FLASH.CR()->getPG());
|
||||
FLASH.CR()->setLOCK(true);
|
||||
|
||||
// Purge Data and instruction cache
|
||||
#if REGS_FLASH_CONFIG_ART
|
||||
if (FLASH.ACR()->getARTEN()) {
|
||||
FLASH.ACR()->setARTEN(false);
|
||||
FLASH.ACR()->setARTRST(true);
|
||||
FLASH.ACR()->setARTRST(false);
|
||||
FLASH.ACR()->setARTEN(true);
|
||||
}
|
||||
#else
|
||||
if (FLASH.ACR()->getDCEN()) {
|
||||
FLASH.ACR()->setDCEN(false);
|
||||
FLASH.ACR()->setDCRST(true);
|
||||
FLASH.ACR()->setDCRST(false);
|
||||
FLASH.ACR()->setDCEN(true);
|
||||
}
|
||||
if (FLASH.ACR()->getICEN()) {
|
||||
FLASH.ACR()->setICEN(false);
|
||||
FLASH.ACR()->setICRST(true);
|
||||
FLASH.ACR()->setICRST(false);
|
||||
FLASH.ACR()->setICEN(true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Compile-time log2
|
||||
static inline constexpr size_t clog2(size_t input) {
|
||||
return (input == 1) ? 0 : clog2(input/2)+1;
|
||||
}
|
||||
|
||||
// Align a pointer to a given type's boundaries
|
||||
// Returns a value that is lower or equal to input
|
||||
template <typename T>
|
||||
static inline T * align(void * input) {
|
||||
size_t k = clog2(sizeof(T));
|
||||
return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(input) & ~((1<<k) - 1));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T eat(void * ptr) {
|
||||
T * pointer = *reinterpret_cast<T **>(ptr);
|
||||
T result = *pointer;
|
||||
*reinterpret_cast<T **>(ptr) = pointer+1;
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline ptrdiff_t byte_offset(void * p1, void * p2) {
|
||||
return reinterpret_cast<uint8_t *>(p2) - reinterpret_cast<uint8_t *>(p1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T min(T i, T j) {
|
||||
return (i<j) ? i : j;
|
||||
}
|
||||
|
||||
static void flash_memcpy(uint8_t * destination, uint8_t * source, size_t length) {
|
||||
/* RM0402 3.5.4
|
||||
* It is not allowed to program data to the Flash memory that would cross the
|
||||
* 128-bit row boundary. In such a case, the write operation is not performed
|
||||
* and a program alignment error flag (PGAERR) is set in the FLASH_SR
|
||||
* register.
|
||||
* The write access type (byte, half-word, word or double word) must
|
||||
* correspond to the type of parallelism chosen (x8, x16, x32 or x64). If not,
|
||||
* the write operation is not performed and a program parallelism error flag
|
||||
* (PGPERR) is set in the FLASH_SR register. */
|
||||
|
||||
static_assert(
|
||||
sizeof(MemoryAccessType) == 1 ||
|
||||
sizeof(MemoryAccessType) == 2 ||
|
||||
sizeof(MemoryAccessType) == 4 ||
|
||||
sizeof(MemoryAccessType) == 8,
|
||||
"Invalid MemoryAccessType");
|
||||
|
||||
/* So we may only perform memory writes with pointers of type MemoryAccessType
|
||||
* and we must make sure to never cross 128 bit boundaries. This second
|
||||
* requirement is satisfied iif the pointers are aligned on MemoryAccessType
|
||||
* boundaries.
|
||||
* Long story short: we want to perform writes to aligned(MemoryAccessType *).
|
||||
*/
|
||||
|
||||
/* Step 1 - Copy a header if needed
|
||||
* We start by copying a Header, whose size is MemoryAccessType, to bring us
|
||||
* back on aligned tracks.
|
||||
*
|
||||
* _AlignedDst _DESTINATION
|
||||
* | |
|
||||
* --+--------+--------+--------+--------+--------+--------+--
|
||||
* | || | | | || |
|
||||
*---+--------+--------+--------+--------+--------+--------+--
|
||||
* |<------------ Header ------------->|
|
||||
* |-- HeaderDelta ->|
|
||||
*/
|
||||
|
||||
MemoryAccessType * alignedDestination = align<MemoryAccessType>(destination);
|
||||
ptrdiff_t headerDelta = byte_offset(alignedDestination, destination);
|
||||
assert(headerDelta >= 0 && headerDelta < static_cast<ptrdiff_t>(sizeof(MemoryAccessType)));
|
||||
|
||||
if (headerDelta > 0) {
|
||||
// At this point, alignedDestination < destination
|
||||
// We'll then retrieve the current value at alignedDestination, fill it with
|
||||
// bytes from source, and write it back at alignedDestination.
|
||||
|
||||
// First, retrieve the current value at alignedDestination
|
||||
MemoryAccessType header = *alignedDestination;
|
||||
|
||||
// Then copy headerLength bytes from source and put them in the header
|
||||
uint8_t * headerStart = reinterpret_cast<uint8_t *>(&header);
|
||||
// Here's where source data shall start being copied in the header
|
||||
uint8_t * headerDataStart = headerStart + headerDelta;
|
||||
// And here's where it should end
|
||||
uint8_t * headerDataEnd = min<uint8_t *>(
|
||||
headerStart + sizeof(MemoryAccessType), // Either at the end of the header
|
||||
headerDataStart + length // or whenever src runs out of data
|
||||
);
|
||||
for (uint8_t * h = headerDataStart; h<headerDataEnd; h++) {
|
||||
*h = eat<uint8_t>(&source);
|
||||
}
|
||||
|
||||
// Then eventually write the header back into the aligned destination
|
||||
*alignedDestination++ = header;
|
||||
wait();
|
||||
}
|
||||
|
||||
/* Step 2 - Copy the bulk of the data
|
||||
* At this point, we can use aligned MemoryAccessType pointers. */
|
||||
|
||||
MemoryAccessType * lastAlignedDestination = align<MemoryAccessType>(destination + length);
|
||||
while (alignedDestination < lastAlignedDestination) {
|
||||
*alignedDestination++ = eat<MemoryAccessType>(&source);
|
||||
wait();
|
||||
}
|
||||
|
||||
/* Step 3 - Copy a footer if needed
|
||||
* Some unaligned data can be pending at the end. Let's take care of it like
|
||||
* we did for the header.
|
||||
*
|
||||
* _alignedDst _Destination+length
|
||||
* | |
|
||||
* --+--------+--------+--------+--------+--------+--------+--
|
||||
* | || | | | || |
|
||||
*---+--------+--------+--------+--------+--------+--------+--
|
||||
* |<------------ Footer ------------->|
|
||||
* |- footerLength ->|
|
||||
*/
|
||||
|
||||
ptrdiff_t footerLength = byte_offset(alignedDestination, destination + length);
|
||||
assert(footerLength < static_cast<ptrdiff_t>(sizeof(MemoryAccessType)));
|
||||
if (footerLength > 0) {
|
||||
assert(alignedDestination == lastAlignedDestination);
|
||||
|
||||
// First, retrieve the current value at alignedDestination
|
||||
MemoryAccessType footer = *alignedDestination;
|
||||
|
||||
/* Then copy footerLength bytes from source and put them at the beginning of
|
||||
* the footer */
|
||||
uint8_t * footerPointer = reinterpret_cast<uint8_t *>(&footer);
|
||||
for (ptrdiff_t i=0; i<footerLength; i++) {
|
||||
footerPointer[i] = eat<uint8_t>(&source);
|
||||
}
|
||||
|
||||
// Then eventually write the footer back into the aligned destination
|
||||
*alignedDestination = footer;
|
||||
wait();
|
||||
}
|
||||
}
|
||||
|
||||
int SectorAtAddress(uint32_t address) {
|
||||
for (int i = 0; i < Config::NumberOfSectors; i++) {
|
||||
if (address >= Config::SectorAddresses[i] && address < Config::SectorAddresses[i+1]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void MassErase() {
|
||||
open();
|
||||
FLASH.CR()->setMER(true);
|
||||
FLASH.CR()->setSTRT(true);
|
||||
wait();
|
||||
FLASH.CR()->setMER(false);
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
void EraseSector(int i) {
|
||||
assert(i >= 0 && i < Config::NumberOfSectors);
|
||||
open();
|
||||
FLASH.CR()->setSNB(i);
|
||||
FLASH.CR()->setSER(true);
|
||||
FLASH.CR()->setSTRT(true);
|
||||
wait();
|
||||
FLASH.CR()->setSNB(0);
|
||||
FLASH.CR()->setSER(false);
|
||||
close();
|
||||
}
|
||||
|
||||
void WriteMemory(uint8_t * destination, uint8_t * source, size_t length) {
|
||||
open();
|
||||
FLASH.CR()->setPG(true);
|
||||
flash_memcpy(destination, source, length);
|
||||
FLASH.CR()->setPG(false);
|
||||
close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
27
ion/src/device/shared/drivers/internal_flash.h
Normal file
27
ion/src/device/shared/drivers/internal_flash.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef ION_DEVICE_SHARED_INTERNAL_FLASH_H
|
||||
#define ION_DEVICE_SHARED_INTERNAL_FLASH_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <regs/regs.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace InternalFlash {
|
||||
|
||||
void MassErase();
|
||||
|
||||
int SectorAtAddress(uint32_t address);
|
||||
void EraseSector(int i);
|
||||
|
||||
void WriteMemory(uint8_t * destination, uint8_t * source, size_t length);
|
||||
|
||||
/* The Device is powered by a 2.8V LDO. This allows us to perform writes to the
|
||||
* Flash 32 bits at once. */
|
||||
constexpr Regs::FLASH::CR::PSIZE MemoryAccessWidth = Regs::FLASH::CR::PSIZE::X32;
|
||||
typedef uint32_t MemoryAccessType;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -55,6 +55,7 @@ dfu_src += $(addprefix ion/src/device/shared/drivers/, \
|
||||
events_keyboard_platform.cpp \
|
||||
external_flash.cpp \
|
||||
flash.cpp \
|
||||
internal_flash.cpp \
|
||||
keyboard.cpp \
|
||||
led.cpp \
|
||||
power.cpp\
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
#include "dfu_interface.h"
|
||||
#include <drivers/config/external_flash.h>
|
||||
#include <string.h>
|
||||
#include <drivers/flash.h>
|
||||
#include <drivers/external_flash.h>
|
||||
#include <drivers/config/flash.h>
|
||||
#include <drivers/config/external_flash.h>
|
||||
#include <ion/timing.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace Device {
|
||||
namespace USB {
|
||||
|
||||
using namespace Ion::Device::Regs;
|
||||
|
||||
static inline uint32_t minUint32T(uint32_t x, uint32_t y) { return x < y ? x : y; }
|
||||
|
||||
void DFUInterface::StatusData::push(Channel * c) const {
|
||||
@@ -188,7 +182,7 @@ void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBuffe
|
||||
|
||||
if (transferBufferLength == 1) {
|
||||
// Mass erase
|
||||
m_erasePage = Flash::Config::NumberOfSectors + ExternalFlash::Config::NumberOfSectors;
|
||||
m_erasePage = Flash::TotalNumberOfSectors();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -200,11 +194,8 @@ void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBuffe
|
||||
+ (transferBuffer[3] << 16)
|
||||
+ (transferBuffer[4] << 24);
|
||||
|
||||
if (eraseAddress >= Flash::Config::StartAddress && eraseAddress <= Flash::Config::EndAddress) {
|
||||
m_erasePage = Flash::SectorAtAddress(eraseAddress);
|
||||
} else if (eraseAddress >= ExternalFlash::Config::StartAddress && eraseAddress <= ExternalFlash::Config::EndAddress) {
|
||||
m_erasePage = Flash::Config::NumberOfSectors + ExternalFlash::SectorAtAddress(eraseAddress - ExternalFlash::Config::StartAddress);
|
||||
} else {
|
||||
m_erasePage = Flash::SectorAtAddress(eraseAddress);
|
||||
if (m_erasePage < 0) {
|
||||
// Unrecognized sector
|
||||
m_state = State::dfuERROR;
|
||||
m_status = Status::errTARGET;
|
||||
@@ -218,13 +209,10 @@ void DFUInterface::eraseMemoryIfNeeded() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_erasePage == Flash::Config::NumberOfSectors + ExternalFlash::Config::NumberOfSectors) {
|
||||
if (m_erasePage == Flash::TotalNumberOfSectors()) {
|
||||
Flash::MassErase();
|
||||
ExternalFlash::MassErase();
|
||||
} else if (m_erasePage < Flash::Config::NumberOfSectors) {
|
||||
Flash::EraseSector(m_erasePage);
|
||||
} else {
|
||||
ExternalFlash::EraseSector(m_erasePage - Flash::Config::NumberOfSectors);
|
||||
Flash::EraseSector(m_erasePage);
|
||||
}
|
||||
|
||||
/* Put an out of range value in m_erasePage to indicate that no erase is
|
||||
@@ -235,15 +223,12 @@ void DFUInterface::eraseMemoryIfNeeded() {
|
||||
}
|
||||
|
||||
void DFUInterface::writeOnMemory() {
|
||||
if (m_writeAddress >= Flash::Config::StartAddress && m_writeAddress <= Flash::Config::EndAddress) {
|
||||
// Write to the Flash memory
|
||||
Flash::WriteMemory(reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBuffer, m_largeBufferLength);
|
||||
} else if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) {
|
||||
if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) {
|
||||
// Write on SRAM
|
||||
// FIXME We should check that we are not overriding the current instructions.
|
||||
memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength);
|
||||
} else if (m_writeAddress >= ExternalFlash::Config::StartAddress && m_writeAddress <= ExternalFlash::Config::EndAddress) {
|
||||
ExternalFlash::WriteMemory(reinterpret_cast<uint8_t *>(m_writeAddress) - ExternalFlash::Config::StartAddress, m_largeBuffer, m_largeBufferLength);
|
||||
} else if (Flash::SectorAtAddress(m_writeAddress) >= 0) {
|
||||
Flash::WriteMemory(reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBuffer, m_largeBufferLength);
|
||||
} else {
|
||||
// Invalid write address
|
||||
m_largeBufferLength = 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "device.h"
|
||||
#include <drivers/config/flash.h>
|
||||
#include <drivers/config/internal_flash.h>
|
||||
#include <drivers/reset.h>
|
||||
#include <regs/regs.h>
|
||||
|
||||
@@ -92,7 +92,7 @@ void Device::detach() {
|
||||
}
|
||||
|
||||
void Device::leave(uint32_t leaveAddress) {
|
||||
if (leaveAddress == Ion::Device::Flash::Config::StartAddress) {
|
||||
if (leaveAddress == Ion::Device::InternalFlash::Config::StartAddress) {
|
||||
Ion::Device::Reset::coreWhilePlugged();
|
||||
} else {
|
||||
Ion::Device::Reset::jump(leaveAddress);
|
||||
|
||||
14
ion/src/shared/dummy/exam_mode.cpp
Normal file
14
ion/src/shared/dummy/exam_mode.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <ion/exam_mode.h>
|
||||
|
||||
namespace Ion {
|
||||
namespace ExamMode {
|
||||
|
||||
bool FetchExamMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void ToggleExamMode() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ ion_src += $(addprefix ion/src/shared/, \
|
||||
dummy/backlight.cpp \
|
||||
dummy/battery.cpp \
|
||||
dummy/display.cpp \
|
||||
dummy/exam_mode.cpp \
|
||||
dummy/fcc_id.cpp \
|
||||
dummy/led.cpp \
|
||||
dummy/serial_number.cpp \
|
||||
|
||||
Reference in New Issue
Block a user