mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-28 18:20:14 +01:00
[ion/f730] Port external flash
This commit is contained in:
@@ -21,6 +21,7 @@ objs += $(addprefix ion/src/f730/, \
|
||||
device.o\
|
||||
display.o\
|
||||
events.o\
|
||||
external_flash.o\
|
||||
flash.o\
|
||||
keyboard.o\
|
||||
led.o\
|
||||
|
||||
@@ -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) {
|
||||
|
||||
314
ion/src/f730/external_flash.cpp
Normal file
314
ion/src/f730/external_flash.cpp
Normal file
@@ -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<uint8_t *>(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<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(&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<uint8_t *>(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<QUADSPI::CCR::Size>(numberOfAltBytes - 1));
|
||||
QUADSPI.ABR()->set(altBytes);
|
||||
}
|
||||
if (address != reinterpret_cast<uint8_t *>(FlashAddressSpaceSize) || functionalMode == QUADSPI::CCR::FunctionalMode::MemoryMapped) {
|
||||
ccr.setADMODE(operatingMode);
|
||||
ccr.setADSIZE(QUADSPI::CCR::Size::ThreeBytes);
|
||||
}
|
||||
ccr.setIMODE(operatingMode);
|
||||
ccr.setINSTRUCTION(static_cast<uint8_t>(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<uint8_t *>(FlashAddressSpaceSize)) {
|
||||
QUADSPI.AR()->set(reinterpret_cast<uint32_t>(address));
|
||||
}
|
||||
|
||||
if (functionalMode == QUADSPI::CCR::FunctionalMode::IndirectWrite) {
|
||||
for (size_t i=0; i<dataLength; i++) {
|
||||
QUADSPI.DR()->set(data[i]);
|
||||
}
|
||||
} else if (functionalMode == QUADSPI::CCR::FunctionalMode::IndirectRead) {
|
||||
for (size_t i=0; i<dataLength; i++) {
|
||||
data[i] = QUADSPI.DR()->get();
|
||||
}
|
||||
}
|
||||
|
||||
/* 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<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(&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<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(&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<uint8_t *>(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<uint32_t>(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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
81
ion/src/f730/external_flash.h
Normal file
81
ion/src/f730/external_flash.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef ION_DEVICE_EXTERNAL_FLASH_H
|
||||
#define ION_DEVICE_EXTERNAL_FLASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
@@ -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);
|
||||
|
||||
126
ion/src/f730/regs/quadspi.h
Normal file
126
ion/src/f730/regs/quadspi.h
Normal file
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "dfu_interface.h"
|
||||
#include <string.h>
|
||||
#include <ion/src/f730/flash.h>
|
||||
#include <ion/src/f730/external_flash.h>
|
||||
|
||||
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<uint8_t *>(m_writeAddress) - k_externalFlashStartAddress, m_largeBufferLength);
|
||||
} else {
|
||||
// Invalid write address
|
||||
m_largeBufferLength = 0;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user