diff --git a/ion/src/f730/Makefile b/ion/src/f730/Makefile index 4c5ffee55..19c5e33da 100644 --- a/ion/src/f730/Makefile +++ b/ion/src/f730/Makefile @@ -21,6 +21,7 @@ objs += $(addprefix ion/src/f730/, \ device.o\ display.o\ events.o\ + external_flash.o\ flash.o\ keyboard.o\ led.o\ diff --git a/ion/src/f730/device.cpp b/ion/src/f730/device.cpp index 145b3e68f..2dc37989c 100644 --- a/ion/src/f730/device.cpp +++ b/ion/src/f730/device.cpp @@ -15,6 +15,7 @@ extern "C" { #include "usb.h" #include "bench/bench.h" #include "base64.h" +#include "external_flash.h" #define USE_SD_CARD 0 @@ -210,6 +211,7 @@ void initPeripherals() { Console::Device::init(); SWD::Device::init(); initSysTick(); + ExternalFlash::Device::init(); } void shutdownPeripherals(bool keepLEDAwake) { diff --git a/ion/src/f730/external_flash.cpp b/ion/src/f730/external_flash.cpp new file mode 100644 index 000000000..5de68b64c --- /dev/null +++ b/ion/src/f730/external_flash.cpp @@ -0,0 +1,314 @@ +#include "external_flash.h" + +namespace Ion { +namespace ExternalFlash { +namespace Device { + +/* 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 --> | regsiter --> 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. */ + +class ExternalFlashStatusRegister { +public: + class StatusRegister1 : Register8 { + public: + using Register8::Register8; + REGS_BOOL_FIELD_R(BUSY, 0); + }; + class StatusRegister2 : Register8 { + public: + using Register8::Register8; + REGS_BOOL_FIELD_W(QE, 1); + }; +}; + +static constexpr QUADSPI::CCR::OperatingMode DefaultOperatingMode = QUADSPI::CCR::OperatingMode::Quad; +static constexpr int ClockFrequencyDivisor = 2; +static constexpr int ChipSelectHighTime = (ClockFrequencyDivisor == 1) ? 3 : (ClockFrequencyDivisor == 2) ? 2 : 1; + +static void send_command_full(QUADSPI::CCR::FunctionalMode functionalMode, QUADSPI::CCR::OperatingMode operatingMode, 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, QUADSPI::CCR::OperatingMode operatingMode = DefaultOperatingMode) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectWrite, + operatingMode, + c, + reinterpret_cast(FlashAddressSpaceSize), + 0, 0, + 0, + nullptr, 0 + ); +} + +static inline void send_write_command(Command c, uint8_t * address, uint8_t * data, size_t dataLength, QUADSPI::CCR::OperatingMode operatingMode = DefaultOperatingMode) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectWrite, + operatingMode, + c, + address, + 0, 0, + 0, + data, dataLength + ); +} + +static inline void send_read_command(Command c, uint8_t * address, uint8_t * data, size_t dataLength, QUADSPI::CCR::OperatingMode operatingMode = DefaultOperatingMode) { + send_command_full( + QUADSPI::CCR::FunctionalMode::IndirectRead, + operatingMode, + c, + address, + 0, 0, + 0, + data, dataLength + ); +} + +static inline void wait(QUADSPI::CCR::OperatingMode operatingMode = DefaultOperatingMode) { + ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0); + do { + send_read_command(Command::ReadStatusRegister, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&statusRegister1), sizeof(statusRegister1), operatingMode); + } 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.) */ + constexpr int FastReadDummyCycles = (DefaultOperatingMode == QUADSPI::CCR::OperatingMode::Single) ? 8 : (ClockFrequencyDivisor > 1) ? 4 : 6; + send_command_full( + QUADSPI::CCR::FunctionalMode::MemoryMapped, + DefaultOperatingMode, + Command::FastReadQuadIO, + reinterpret_cast(FlashAddressSpaceSize), + 0xA0, 1, + 2, //FIXME + 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, + DefaultOperatingMode, + Command::FastReadQuadIO, + 0, + ~(0xA0), 1, + 2, //FIXME + &dummyData, 1 + ); +} + +void send_command_full(QUADSPI::CCR::FunctionalMode functionalMode, QUADSPI::CCR::OperatingMode operatingMode, Command c, uint8_t * address, uint32_t altBytes, size_t numberOfAltBytes, uint8_t dummyCycles, uint8_t * data, size_t dataLength) { + class QUADSPI::CCR ccr(0); + ccr.setFMODE(functionalMode); + if (data != nullptr || functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + ccr.setDMODE(operatingMode); + } + if (functionalMode != QUADSPI::CCR::FunctionalMode::MemoryMapped) { + QUADSPI.DLR()->set((dataLength > 0) ? dataLength-1 : 0); + } + ccr.setDCYC(dummyCycles); + if (numberOfAltBytes > 0) { + ccr.setABMODE(operatingMode); + ccr.setABSIZE(static_cast(numberOfAltBytes - 1)); + QUADSPI.ABR()->set(altBytes); + } + if (address != reinterpret_cast(FlashAddressSpaceSize) || functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) { + ccr.setADMODE(operatingMode); + ccr.setADSIZE(QUADSPI::CCR::Size::ThreeBytes); + } + ccr.setIMODE(operatingMode); + 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." */ + if (functionalMode != QUADSPI::CCR::FunctionalMode::MemoryMapped) { + while (QUADSPI.SR()->getBUSY()) { + } + } +} + +void init() { + initGPIO(); + initQSPI(); + initChip(); +} + +void initGPIO() { + for(const GPIOPin & g : QSPIPins) { + g.group().OSPEEDR()->setOutputSpeed(g.pin(), GPIO::OSPEEDR::OutputSpeed::High); + g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::AlternateFunction); + g.group().AFR()->setAlternateFunction(g.pin(), (g.pin() == 6 ? GPIO::AFR::AlternateFunction::AF10 : GPIO::AFR::AlternateFunction::AF9)); + } +} + +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); + dcr.setCSHT(ChipSelectHighTime - 1); + dcr.setCKMODE(true); + QUADSPI.DCR()->set(dcr); + class QUADSPI::CR cr(0); + cr.setPRESCALER(ClockFrequencyDivisor - 1); + cr.setEN(true); + QUADSPI.CR()->set(cr); +} + +void initChip() { + /* The chip initially expects commands in SPI mode. We need to use SPI to tell + * it to switch to QPI. */ + if (DefaultOperatingMode == QUADSPI::CCR::OperatingMode::Quad) { + send_command(Command::WriteEnable, QUADSPI::CCR::OperatingMode::Single); + ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0); + statusRegister2.setQE(true); + wait(QUADSPI::CCR::OperatingMode::Single); + send_write_command(Command::WriteStatusRegister2, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&statusRegister2), sizeof(statusRegister2), QUADSPI::CCR::OperatingMode::Single); + wait(QUADSPI::CCR::OperatingMode::Single); + send_command(Command::EnableQPI, QUADSPI::CCR::OperatingMode::Single); + wait(); + if (ClockFrequencyDivisor == 1) { + class ReadParameters : Register8 { + public: + /* Parameters sent along with SetReadParameters instruction in order + * to configure the number of dummy cycles for the QPI Read instructions. */ + using Register8::Register8; + REGS_BOOL_FIELD_W(P5, 1); + }; + ReadParameters readParameters(0); + readParameters.setP5(true); + send_write_command(Command::SetReadParameters, reinterpret_cast(FlashAddressSpaceSize), reinterpret_cast(&readParameters), sizeof(readParameters)); + } + } + set_as_memory_mapped(); +} + +int SectorAtAddress(uint32_t address) { + int i = address >> NumberOfAddressBitsIn64KbyteBlock; + if (i >= NumberOfSectors) { + return -1; + } + return i; +} + +void MassErase() { + unset_memory_mapped_mode(); + send_command(Command::WriteEnable); + wait(); + send_command(Command::ChipErase); + wait(); + set_as_memory_mapped(); +} + +void EraseSector(int i) { + assert(i >= 0 && i < NumberOfSectors); + unset_memory_mapped_mode(); + send_command(Command::WriteEnable); + wait(); + send_write_command(Command::Erase64KbyteBlock, reinterpret_cast(i << NumberOfAddressBitsIn64KbyteBlock), nullptr, 0); + wait(); + set_as_memory_mapped(); +} + +void WriteMemory(uint8_t * source, uint8_t * destination, size_t length) { + 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; + constexpr Command pageProgram = (DefaultOperatingMode == QUADSPI::CCR::OperatingMode::Single) ? Command::PageProgram : Command::QuadPageProgram; + 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(); + send_write_command(pageProgram, destination, source, lengthThatFitsInPage); + length -= lengthThatFitsInPage; + destination += lengthThatFitsInPage; + source += lengthThatFitsInPage; + lengthThatFitsInPage = PageSize; + wait(); + } + set_as_memory_mapped(); +} + +} +} +} diff --git a/ion/src/f730/external_flash.h b/ion/src/f730/external_flash.h new file mode 100644 index 000000000..bde2678f8 --- /dev/null +++ b/ion/src/f730/external_flash.h @@ -0,0 +1,81 @@ +#ifndef ION_DEVICE_EXTERNAL_FLASH_H +#define ION_DEVICE_EXTERNAL_FLASH_H + +#include +#include +#include "regs/regs.h" + +// Quad-SPI on STM32 microcontroller +// https://www.st.com/resource/en/application_note/dm00227538.pdf + +// Adesto Technologies AT25SF641 +// https://www.adestotech.com/wp-content/uploads/AT25SF641_111.pdf + +/* External Flash Memory map Address space + * 8MiB chip 0x000000 - 0x7FFFFF + * 2^7 64KiB blocks 0x..0000 - 0x..FFFF + * 2^7 * 2 32KiB blocks 0x..0000 - 0x..7FFF or 0x..8000 - 0x..FFFF + * 2^7 * 2 * 2^3 4KiB blocks 0x...000 - 0x...FFF + * 2^7 * 2 * 2^3 * 2^4 256B pages 0x....00 - 0x....FF */ + +namespace Ion { +namespace ExternalFlash { +namespace Device { + +/* Pin | Role | Mode | Function + * -----+----------------------+-----------------------+----------------- + * PB2 | QUADSPI CLK | Alternate Function 9 | QUADSPI_CLK + * PB6 | QUADSPI BK1_NCS | Alternate Function 10 | QUADSPI_BK1_NCS + * PC8 | QUADSPI BK1_IO2/WP | Alternate Function 9 | QUADSPI_BK1_IO2 + * PC9 | QUADSPI BK1_IO0/SO | Alternate Function 9 | QUADSPI_BK1_IO0 + * PD12 | QUADSPI BK1_IO1/SI | Alternate Function 9 | QUADSPI_BK1_IO1 + * PD13 | QUADSPI BK1_IO3/HOLD | Alternate Function 9 | QUADSPI_BK1_IO3 + */ + +void init(); +void shutdown(); + +void initGPIO(); +void initQSPI(); +void initChip(); + +void MassErase(); + +constexpr int NumberOfSectors = 128; +int SectorAtAddress(uint32_t address); +void EraseSector(int i); + +void WriteMemory(uint8_t * source, uint8_t * destination, size_t length); + +enum class Command : uint8_t { + ReadStatusRegister = 0x05, + WriteStatusRegister2 = 0x31, + WriteEnable = 0x06, + ReadData = 0x03, + FastRead = 0x0B, + FastReadQuadIO = 0xEB, + // Program previously erased memory areas as being "0" + PageProgram = 0x02, + QuadPageProgram = 0x33, + EnableQPI = 0x38, + // Erase the whole chip or a 64-Kbyte block as being "1" + ChipErase = 0xC7, + Erase64KbyteBlock = 0xD8, + SetReadParameters = 0xC0 +}; + +constexpr static uint32_t QSPIBaseAddress = 0x90000000; +constexpr static uint8_t NumberOfAddressBitsInChip = 23; +constexpr static uint8_t NumberOfAddressBitsIn64KbyteBlock = 16; +constexpr static uint32_t FlashAddressSpaceSize = 1 << NumberOfAddressBitsInChip; + +constexpr static GPIOPin QSPIPins[] = { + GPIOPin(GPIOB, 2), GPIOPin(GPIOB, 6), GPIOPin(GPIOC, 9), GPIOPin(GPIOD,12), + GPIOPin(GPIOC, 8), GPIOPin(GPIOD,13) +}; + +} +} +} + +#endif diff --git a/ion/src/f730/regs/gpio.h b/ion/src/f730/regs/gpio.h index 2371ac0eb..bf7300295 100644 --- a/ion/src/f730/regs/gpio.h +++ b/ion/src/f730/regs/gpio.h @@ -27,6 +27,19 @@ public: void setType(int index, Type type) volatile { setBitRange(index, index, (uint32_t)type); } }; + class OSPEEDR : Register32 { + // Datasheet, page 120, table 58 + public: + enum class OutputSpeed { + Low = 0, + Medium = 1, + Fast = 2, + High = 3 + }; + OutputSpeed getOutputSpeed(int index) { return (OutputSpeed)getBitRange(2*index+1, 2*index); } + void setOutputSpeed(int index, OutputSpeed speed) volatile { setBitRange(2*index+1, 2*index, (uint32_t)speed); } + }; + class PUPDR : public Register32 { public: enum class Pull { @@ -72,6 +85,7 @@ public: constexpr operator int() const { return m_index; } REGS_REGISTER_AT(MODER, 0x00); REGS_REGISTER_AT(OTYPER, 0x04); + REGS_REGISTER_AT(OSPEEDR, 0x08); REGS_REGISTER_AT(PUPDR, 0x0C); REGS_REGISTER_AT(IDR, 0x10); REGS_REGISTER_AT(ODR, 0x14); diff --git a/ion/src/f730/regs/quadspi.h b/ion/src/f730/regs/quadspi.h new file mode 100644 index 000000000..18d6c81f0 --- /dev/null +++ b/ion/src/f730/regs/quadspi.h @@ -0,0 +1,126 @@ +#ifndef REGS_QUADSPI_H +#define REGS_QUADSPI_H + +#include "register.h" + +// Quad-SPI register map on STM32 microcontroller +// See section 12 of the STM32F412 reference manual + +class QUADSPI { +public: + class CR : public Register32 { // Control register + public: + using Register32::Register32; + REGS_BOOL_FIELD(EN, 0); + REGS_BOOL_FIELD(TCEN, 3); // Lower-power timeout counter enable in memory-mapped mode + REGS_BOOL_FIELD(SSHIFT, 4); + REGS_FIELD(PRESCALER, uint8_t, 31, 24); + }; + + class DCR : public Register32 { // Device configuration register + public: + using Register32::Register32; + REGS_BOOL_FIELD(CKMODE, 0); // MODE 0 or 3 + REGS_FIELD(CSHT, uint8_t, 10, 8); // Chip select minimum high time + REGS_FIELD(FSIZE, uint8_t, 20, 16); + }; + + class SR : Register32 { // Status register + public: + REGS_BOOL_FIELD(TEF, 0); // Transfer error flag + REGS_BOOL_FIELD(TCF, 1); // Transfer complete flag + REGS_BOOL_FIELD(BUSY, 5); + }; + + class FCR : Register32 { // Flag clear register + }; + + class DLR : public Register32 { // Data length register + // In indirect and status-polling modes + // Number of bytes to be transferred = DLR + 1 + }; + + class CCR : public Register32 { // Communication configuration register + public: + using Register32::Register32; + enum class Size : uint8_t { + OneByte = 0, + TwoBytes = 1, + ThreeBytes = 2, + FourBytes = 3 + }; + enum class OperatingMode : uint8_t { + NoData = 0, + Single = 1, + Dual = 2, + Quad = 3 + }; + enum class FunctionalMode : uint8_t { + IndirectWrite = 0, + IndirectRead = 1, + StatusPolling = 2, + MemoryMapped = 3 + }; + REGS_FIELD(INSTRUCTION, uint8_t, 7, 0); + REGS_FIELD(IMODE, OperatingMode, 9, 8); + REGS_FIELD(ADMODE, OperatingMode, 11, 10); + REGS_FIELD(ADSIZE, Size, 13, 12); + REGS_FIELD(ABMODE, OperatingMode, 15, 14); + REGS_FIELD(ABSIZE, Size, 17, 16); + REGS_FIELD(DCYC, uint8_t, 22, 18); + REGS_FIELD(DMODE, OperatingMode, 25, 24); + REGS_FIELD(FMODE, FunctionalMode, 27, 26); + REGS_BOOL_FIELD(SIOO, 28); + }; + + class AR : public Register32 { // Address register + }; + + class ABR : public Register32 { // Alternate bytes register + }; + + class DR : public Register8 { // Data register + /* In indirect (read or write) mode, any data must be written to or read from + * the data register, which supports byte, halfword and word access + * (aligned to the bottom of the register). + * N bytes read/written remove/add N bytes from/to the FIFO. + */ + }; + + class PSMKR : Register32 { // Polling status mask register + }; + + class PSMAR : Register32 { // Polling status match register + }; + + class PIR : Register32 { // Polling interval register + }; + + class LPTR : public Register16 { // Low-power timeout register + // Period before putting the Flash memory in lower-power state (in memory-mapped mode) + }; + + constexpr QUADSPI() {}; + + REGS_REGISTER_AT(CR, 0x00); + REGS_REGISTER_AT(DCR, 0x04); + REGS_REGISTER_AT(SR, 0x08); + REGS_REGISTER_AT(FCR, 0x0C); + REGS_REGISTER_AT(DLR, 0x10); + REGS_REGISTER_AT(CCR, 0x14); + REGS_REGISTER_AT(AR, 0x18); + REGS_REGISTER_AT(ABR, 0x1C); + REGS_REGISTER_AT(DR, 0x20); + REGS_REGISTER_AT(PSMKR, 0x24); + REGS_REGISTER_AT(PSMAR, 0x28); + REGS_REGISTER_AT(PIR, 0x2C); + REGS_REGISTER_AT(LPTR, 0x30); +private: + constexpr uint32_t Base() const { + return 0xA0001000; + } +}; + +constexpr QUADSPI QUADSPI; + +#endif diff --git a/ion/src/f730/regs/regs.h b/ion/src/f730/regs/regs.h index ed5b48bdb..cfa644dd7 100644 --- a/ion/src/f730/regs/regs.h +++ b/ion/src/f730/regs/regs.h @@ -16,6 +16,7 @@ #include "rcc.h" #include "rng.h" #include "otg.h" +#include "quadspi.h" #include "sdio.h" #include "spi.h" #include "syscfg.h" diff --git a/ion/src/f730/usb/Makefile b/ion/src/f730/usb/Makefile index 7d38b05d6..d32478413 100644 --- a/ion/src/f730/usb/Makefile +++ b/ion/src/f730/usb/Makefile @@ -50,6 +50,7 @@ dfu_objs += ion/src/f730/device.o dfu_objs += ion/src/f730/usb.o dfu_objs += ion/src/f730/base64.o dfu_objs += ion/src/f730/flash.o +dfu_objs += ion/src/device/external_flash.o dfu_objs += ion/src/f730/timing.o ion/src/f730/usb/dfu.elf: LDSCRIPT = ion/src/f730/usb/dfu.ld diff --git a/ion/src/f730/usb/calculator.h b/ion/src/f730/usb/calculator.h index d3383e419..81b8c9b2a 100644 --- a/ion/src/f730/usb/calculator.h +++ b/ion/src/f730/usb/calculator.h @@ -93,7 +93,7 @@ public: m_manufacturerStringDescriptor("NumWorks"), m_productStringDescriptor("NumWorks Calculator"), m_serialNumberStringDescriptor(serialNumber), - m_interfaceStringDescriptor("@Flash/0x08000000/04*016Kg,01*064Kg,07*128Kg"), + m_interfaceStringDescriptor("@Flash/0x08000000/04*016Kg,01*064Kg,07*128Kg/0x90000000/64*064Kg,64*064Kg"), //m_interfaceStringDescriptor("@SRAM/0x20000000/01*256Ke"), /* Switch to this descriptor to use dfu-util to write in the SRAM. * FIXME Should be an alternate Interface. */ diff --git a/ion/src/f730/usb/dfu_interface.cpp b/ion/src/f730/usb/dfu_interface.cpp index 395576cc6..b6e6fde24 100644 --- a/ion/src/f730/usb/dfu_interface.cpp +++ b/ion/src/f730/usb/dfu_interface.cpp @@ -1,6 +1,7 @@ #include "dfu_interface.h" #include #include +#include namespace Ion { namespace USB { @@ -173,7 +174,7 @@ void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBuffe if (transferBufferLength == 1) { // Mass erase - m_erasePage = Flash::Device::NumberOfSectors; + m_erasePage = Flash::Device::NumberOfSectors + ExternalFlash::Device::NumberOfSectors; return; } @@ -185,9 +186,11 @@ void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBuffe + (transferBuffer[3] << 16) + (transferBuffer[4] << 24); - m_erasePage = Flash::Device::SectorAtAddress(eraseAddress); - - if (m_erasePage < 0) { + if (eraseAddress >= k_flashStartAddress && eraseAddress <= k_flashEndAddress) { + m_erasePage = Flash::Device::SectorAtAddress(eraseAddress); + } else if (eraseAddress >= k_externalFlashStartAddress && eraseAddress <= k_externalFlashEndAddress) { + m_erasePage = Flash::Device::NumberOfSectors + ExternalFlash::Device::SectorAtAddress(eraseAddress - k_externalFlashStartAddress); + } else { // Unrecognized sector m_state = State::dfuERROR; m_status = Status::errTARGET; @@ -201,10 +204,13 @@ void DFUInterface::eraseMemoryIfNeeded() { return; } - if (m_erasePage == Ion::Flash::Device::NumberOfSectors) { + if (m_erasePage == Flash::Device::NumberOfSectors + ExternalFlash::Device::NumberOfSectors) { Flash::Device::MassErase(); - } else { + ExternalFlash::Device::MassErase(); + } else if (m_erasePage < Flash::Device::NumberOfSectors) { Flash::Device::EraseSector(m_erasePage); + } else { + ExternalFlash::Device::EraseSector(m_erasePage - Flash::Device::NumberOfSectors); } /* Put an out of range value in m_erasePage to indicate that no erase is @@ -222,6 +228,8 @@ void DFUInterface::writeOnMemory() { // 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 >= k_externalFlashStartAddress && m_writeAddress <= k_externalFlashEndAddress) { + ExternalFlash::Device::WriteMemory(m_largeBuffer, reinterpret_cast(m_writeAddress) - k_externalFlashStartAddress, m_largeBufferLength); } else { // Invalid write address m_largeBufferLength = 0; diff --git a/ion/src/f730/usb/dfu_interface.h b/ion/src/f730/usb/dfu_interface.h index 6284ce951..7fce4bf40 100644 --- a/ion/src/f730/usb/dfu_interface.h +++ b/ion/src/f730/usb/dfu_interface.h @@ -130,6 +130,8 @@ private: constexpr static uint32_t k_flashEndAddress = 0x08100000; constexpr static uint32_t k_sramStartAddress = 0x20000000; constexpr static uint32_t k_sramEndAddress = 0x20040000; + constexpr static uint32_t k_externalFlashStartAddress = 0x90000000; + constexpr static uint32_t k_externalFlashEndAddress = 0x90800000; // Download and upload bool processDownloadRequest(uint16_t wLength, uint16_t * transferBufferLength);