Skip to content

Commit

Permalink
[driver] Add BdSpiStackFlash contiguous memory
Browse files Browse the repository at this point in the history
  • Loading branch information
rasmuskleist committed Aug 13, 2023
1 parent ebe4114 commit a1ad2a0
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/modm/driver/storage/block_device.lb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ Microchip SST26VF064B 64MBit flash chip in SOIJ-8, WDFN-8 or SOIC-16.
env.copy("block_device_spiflash_impl.hpp")
# -----------------------------------------------------------------------------

class BlockDeviceSpiStackFlash(Module):
def init(self, module):
module.name = "spi.stack.flash"
module.description = "SpiStack homogeneous flash memory"

def prepare(self, module, options):
module.depends(":architecture:block.device")
return True

def build(self, env):
env.outbasepath = "modm/src/modm/driver/storage"
env.copy("block_device_spistack_flash.hpp")
env.copy("block_device_spistack_flash_impl.hpp")
# -----------------------------------------------------------------------------

def init(module):
module.name = ":driver:block.device"
module.description = "Block Devices"
Expand All @@ -80,6 +95,7 @@ def prepare(module, options):
module.add_submodule(BlockDeviceHeap())
module.add_submodule(BlockDeviceMirror())
module.add_submodule(BlockDeviceSpiFlash())
module.add_submodule(BlockDeviceSpiStackFlash())
return True

def build(env):
Expand Down
125 changes: 125 additions & 0 deletions src/modm/driver/storage/block_device_spistack_flash.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// coding: utf-8
/*
* Copyright (c) 2018, Raphael Lehmann
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#ifndef MODM_BLOCK_DEVICE_SPISTACK_FLASH_HPP
#define MODM_BLOCK_DEVICE_SPISTACK_FLASH_HPP

#include <algorithm>
#include <cstdlib>

#include <modm/architecture/interface/block_device.hpp>
#include <modm/processing/resumable.hpp>

namespace modm
{

/**
* \brief SpiStack homogenoues memory
*
* The `read()`, `erase()`,`program()` and `write()` methodes wait for
* the chip to finish writing to the flash.
*
* \tparam SpiBlockDevice Base SPI block device of the homogenous stack
*
* \ingroup modm_driver_block_device_spi_stack_flash
* \author Rasmus Kleist Hørlyck Sørensen
*/
template <typename SpiBlockDevice, uint8_t DieCount>
class BdSpiStackFlash : public modm::BlockDevice, protected NestedResumable<3>
{
public:
/// Initializes the storage hardware
modm::ResumableResult<bool>
initialize();

/// Deinitializes the storage hardware
modm::ResumableResult<bool>
deinitialize();

/** Read data from one or more blocks
*
* @param buffer Buffer to read data into
* @param address Address to begin reading from
* @param size Size to read in bytes (multiple of read block size)
* @return True on success
*/
modm::ResumableResult<bool>
read(uint8_t* buffer, bd_address_t address, bd_size_t size);

/** Program blocks with data
*
* Any block has to be erased prior to being programmed
*
* @param buffer Buffer of data to write to blocks
* @param address Address of first block to begin writing to
* @param size Size to write in bytes (multiple of read block size)
* @return True on success
*/
modm::ResumableResult<bool>
program(const uint8_t* buffer, bd_address_t address, bd_size_t size);

/** Erase blocks
*
* The state of an erased block is undefined until it has been programmed
*
* @param address Address of block to begin erasing
* @param size Size to erase in bytes (multiple of read block size)
* @return True on success
*/
modm::ResumableResult<bool>
erase(bd_address_t address, bd_size_t size);

/** Writes data to one or more blocks after erasing them
*
* The blocks are erased prior to being programmed
*
* @param buffer Buffer of data to write to blocks
* @param address Address of first block to begin writing to
* @param size Size to write in bytes (multiple of read block size)
* @return True on success
*/
modm::ResumableResult<bool>
write(const uint8_t* buffer, bd_address_t address, bd_size_t size);

public:
/** Check if device is busy
*
* @return True if any die in the stack is busy.
*/
modm::ResumableResult<bool>
isBusy();

/** This function can be used in another resumable function
* to wait until the flash operation is finished.
*/
modm::ResumableResult<void>
waitWhileBusy();

public:
static constexpr bd_size_t BlockSizeRead = SpiBlockDevice::BlockSizeRead;
static constexpr bd_size_t BlockSizeWrite = SpiBlockDevice::BlockSizeWrite;
static constexpr bd_size_t BlockSizeErase = SpiBlockDevice::BlockSizeErase;
static constexpr bd_size_t DieSize = SpiBlockDevice::DeviceSize;
static constexpr bd_size_t DeviceSize = DieCount * DieSize;

private:
std::ldiv_t dv;
uint32_t index;
uint8_t currentDie;
SpiBlockDevice spiBlockDevice;
};

} // namespace modm

#include "block_device_spistack_flash_impl.hpp"

#endif // MODM_BLOCK_DEVICE_SPISTACK_FLASH_HPP
180 changes: 180 additions & 0 deletions src/modm/driver/storage/block_device_spistack_flash_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// coding: utf-8
/*
* Copyright (c) 2023, Rasmus Kleist Hørlyck Sørensen
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#ifndef MODM_BLOCK_DEVICE_SPISTACK_FLASH_HPP
#error "Don't include this file directly, use 'block_device_spistack_flash.hpp' instead!"
#endif

// ----------------------------------------------------------------------------

template <typename SpiBlockDevice, uint8_t DieCount>
modm::ResumableResult<bool>
modm::BdSpiStackFlash<SpiBlockDevice, DieCount>::initialize()
{
RF_BEGIN();

if (RF_CALL(spiBlockDevice.initialize())) {
RF_CALL(spiBlockDevice.selectDie(currentDie = 0x00));
RF_RETURN(true);
}

RF_END_RETURN(false);
}

// ----------------------------------------------------------------------------

template <typename SpiBlockDevice, uint8_t DieCount>
modm::ResumableResult<bool>
modm::BdSpiStackFlash<SpiBlockDevice, DieCount>::deinitialize()
{
RF_BEGIN();
RF_END_RETURN_CALL(spiBlockDevice.deinitialize());
}

// ----------------------------------------------------------------------------

template <typename SpiBlockDevice, uint8_t DieCount>
modm::ResumableResult<bool>
modm::BdSpiStackFlash<SpiBlockDevice, DieCount>::read(uint8_t* buffer, bd_address_t address, bd_size_t size)
{
RF_BEGIN();

if((size == 0) || (size % BlockSizeRead != 0) || (address + size > DeviceSize)) {
RF_RETURN(false);
}

index = 0;
while (index < size) {
dv = std::ldiv(index + address, DieSize); // dv.quot = die #ID, dv.rem = die address
if (currentDie != dv.quot) {
RF_CALL(spiBlockDevice.selectDie(currentDie = dv.quot));
}
if (RF_CALL(spiBlockDevice.read(&buffer[index], dv.rem, std::min(size - index, DieSize - dv.rem)))) {
index += DieSize - dv.rem; // size - index <= DieSize - dv.rem only on last iteration!
} else {
RF_RETURN(false);
}
}

RF_END_RETURN(true);
}

// ----------------------------------------------------------------------------

template <typename SpiBlockDevice, uint8_t DieCount>
modm::ResumableResult<bool>
modm::BdSpiStackFlash<SpiBlockDevice, DieCount>::program(const uint8_t* buffer, bd_address_t address, bd_size_t size)
{
RF_BEGIN();

if((size == 0) || (size % BlockSizeWrite != 0) || (address + size > DeviceSize)) {
RF_RETURN(false);
}

index = 0;
while (index < size) {
dv = std::ldiv(index + address, DieSize); // dv.quot = die #ID, dv.rem = die address
if (currentDie != dv.quot) {
RF_CALL(spiBlockDevice.selectDie(currentDie = dv.quot));
}
if (RF_CALL(spiBlockDevice.program(&buffer[index], dv.rem, std::min(size - index, DieSize - dv.rem)))) {
index += DieSize - dv.rem; // size - index <= DieSize - dv.rem only on last iteration!
} else {
RF_RETURN(false);
}
}

RF_END_RETURN(true);
}

// ----------------------------------------------------------------------------

template <typename SpiBlockDevice, uint8_t DieCount>
modm::ResumableResult<bool>
modm::BdSpiStackFlash<SpiBlockDevice, DieCount>::erase(bd_address_t address, bd_size_t size)
{
RF_BEGIN();

if((size == 0) || (size % BlockSizeErase != 0) || (address + size > DeviceSize)) {
RF_RETURN(false);
}

index = 0;
while (index < size) {
dv = std::ldiv(index + address, DieSize); // dv.quot = die #ID, dv.rem = die address
if (currentDie != dv.quot) {
RF_CALL(spiBlockDevice.selectDie(currentDie = dv.quot));
}
if (RF_CALL(spiBlockDevice.erase(dv.rem, std::min(size - index, DieSize - dv.rem)))) {
index += DieSize - dv.rem; // size - index <= DieSize - dv.rem only on last iteration!
} else {
RF_RETURN(false);
}
}

RF_END_RETURN(true);
}

// ----------------------------------------------------------------------------

template <typename SpiBlockDevice, uint8_t DieCount>
modm::ResumableResult<bool>
modm::BdSpiStackFlash<SpiBlockDevice, DieCount>::write(const uint8_t* buffer, bd_address_t address, bd_size_t size)
{
RF_BEGIN();

if((size == 0) || (size % BlockSizeErase != 0) || (size % BlockSizeWrite != 0) || (address + size > DeviceSize)) {
RF_RETURN(false);
}

if(!RF_CALL(this->erase(address, size))) {
RF_RETURN(false);
}

if(!RF_CALL(this->program(buffer, address, size))) {
RF_RETURN(false);
}

RF_END_RETURN(true);
}

// ----------------------------------------------------------------------------

template <typename SpiBlockDevice, uint8_t DieCount>
modm::ResumableResult<bool>
modm::BdSpiStackFlash<SpiBlockDevice, DieCount>::isBusy()
{
RF_BEGIN();

currentDie = DieCount;
while (currentDie > 0) {
RF_CALL(spiBlockDevice.selectDie(--currentDie));
if (RF_CALL(spiBlockDevice.isBusy())) {
RF_RETURN(true);
}
}

RF_END_RETURN(false);
}

// ----------------------------------------------------------------------------

template <typename SpiBlockDevice, uint8_t DieCount>
modm::ResumableResult<void>
modm::BdSpiStackFlash<SpiBlockDevice, DieCount>::waitWhileBusy()
{
RF_BEGIN();
while(RF_CALL(isBusy())) {
RF_YIELD();
}
RF_END();
}

0 comments on commit a1ad2a0

Please sign in to comment.