blob: 3bc2a2b4de3552b9d8ce1cd475e3c393bf46be15 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include "pw_span/span.h"
#include "pw_status/status.h"
#include "pw_status/status_with_size.h"
namespace pw {
// TODO: These are general-purpose utility functions that should be moved
// elsewhere.
constexpr size_t AlignDown(size_t value, size_t alignment) {
return (value / alignment) * alignment;
}
constexpr size_t AlignUp(size_t value, size_t alignment) {
return (value + alignment - 1) / alignment * alignment;
}
} // namespace pw
namespace pw::kvs {
enum class PartitionPermission : bool {
kReadOnly,
kReadAndWrite,
};
class FlashMemory {
public:
// The flash address is in the range of: 0 to FlashSize.
typedef uint32_t Address;
constexpr FlashMemory(size_t sector_size,
size_t sector_count,
size_t alignment,
uint32_t start_address = 0,
uint32_t sector_start = 0,
std::byte erased_memory_content = std::byte{0xFF})
: sector_size_(sector_size),
flash_sector_count_(sector_count),
alignment_(alignment),
start_address_(start_address),
start_sector_(sector_start),
erased_memory_content_(erased_memory_content) {
// TODO: The smallest possible alignment is 1 B; 0 is invalid.
// DCHECK_NE(alignment_, 0);
}
virtual ~FlashMemory() = default;
virtual Status Enable() = 0;
virtual Status Disable() = 0;
virtual bool IsEnabled() const = 0;
virtual Status SelfTest() { return Status::UNIMPLEMENTED; }
// Erase num_sectors starting at a given address. Blocking call.
// Address should be on a sector boundary.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or sector count is invalid.
// UNKNOWN, on HAL error
virtual Status Erase(Address flash_address, size_t num_sectors) = 0;
// Reads bytes from flash into buffer. Blocking call.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// UNKNOWN, on HAL error
virtual StatusWithSize Read(Address address, span<std::byte> output) = 0;
StatusWithSize Read(Address address, void* buffer, size_t len) {
return Read(address, span(static_cast<std::byte*>(buffer), len));
}
// Writes bytes to flash. Blocking call.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// UNKNOWN, on HAL error
virtual StatusWithSize Write(Address destination_flash_address,
span<const std::byte> data) = 0;
StatusWithSize Write(Address destination_flash_address,
const void* data,
size_t len) {
return Write(destination_flash_address,
span(static_cast<const std::byte*>(data), len));
}
// Convert an Address to an MCU pointer, this can be used for memory
// mapped reads. Return NULL if the memory is not memory mapped.
virtual std::byte* FlashAddressToMcuAddress(Address) const { return nullptr; }
// start_sector() is useful for FlashMemory instances where the
// sector start is not 0. (ex.: cases where there are portions of flash
// that should be handled independently).
constexpr uint32_t start_sector() const { return start_sector_; }
constexpr size_t sector_size_bytes() const { return sector_size_; }
constexpr size_t sector_count() const { return flash_sector_count_; }
constexpr size_t alignment_bytes() const { return alignment_; }
constexpr size_t size_bytes() const {
return sector_size_ * flash_sector_count_;
}
// Address of the start of flash (the address of sector 0)
constexpr uint32_t start_address() const { return start_address_; }
constexpr std::byte erased_memory_content() const {
return erased_memory_content_;
}
private:
const uint32_t sector_size_;
const uint32_t flash_sector_count_;
const uint8_t alignment_;
const uint32_t start_address_;
const uint32_t start_sector_;
const std::byte erased_memory_content_;
};
class FlashPartition {
public:
// The flash address is in the range of: 0 to PartitionSize.
using Address = uint32_t;
constexpr FlashPartition(
FlashMemory* flash,
uint32_t start_sector_index,
uint32_t sector_count,
uint32_t alignment_bytes = 0, // Defaults to flash alignment
PartitionPermission permission = PartitionPermission::kReadAndWrite)
: flash_(*flash),
start_sector_index_(start_sector_index),
sector_count_(sector_count),
alignment_bytes_(alignment_bytes == 0 ? flash_.alignment_bytes()
: alignment_bytes),
permission_(permission) {}
virtual ~FlashPartition() = default;
// Performs any required partition or flash-level initialization.
virtual Status Init() { return Status::OK; }
// Erase num_sectors starting at a given address. Blocking call.
// Address should be on a sector boundary.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or sector count is invalid.
// PERMISSION_DENIED, if partition is read only.
// UNKNOWN, on HAL error
virtual Status Erase(Address address, size_t num_sectors);
Status Erase() { return Erase(0, this->sector_count()); }
// Reads bytes from flash into buffer. Blocking call.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// UNKNOWN, on HAL error
virtual StatusWithSize Read(Address address, span<std::byte> output);
StatusWithSize Read(Address address, size_t length, void* output) {
return Read(address, span(static_cast<std::byte*>(output), length));
}
// Writes bytes to flash. Blocking call.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// PERMISSION_DENIED, if partition is read only.
// UNKNOWN, on HAL error
virtual StatusWithSize Write(Address address, span<const std::byte> data);
// Returns the total number of bytes written, including any padding.
StatusWithSize WriteAligned(
Address start_address, std::initializer_list<span<const std::byte>> data);
// Check to see if chunk of flash memory is erased. Address and len need to
// be aligned with FlashMemory.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// UNKNOWN, on HAL error
// TODO: StatusWithBool
virtual Status IsRegionErased(Address source_flash_address,
size_t len,
bool* is_erased);
// Overridden by derived classes. The reported sector size is space available
// to users of FlashPartition. It accounts for space reserved in the sector
// for FlashPartition to store metadata.
virtual size_t sector_size_bytes() const {
return flash_.sector_size_bytes();
}
size_t size_bytes() const { return sector_count() * sector_size_bytes(); }
size_t alignment_bytes() const { return alignment_bytes_; }
size_t sector_count() const { return sector_count_; }
// Convert a FlashMemory::Address to an MCU pointer, this can be used for
// memory mapped reads. Return NULL if the memory is not memory mapped.
std::byte* PartitionAddressToMcuAddress(Address address) const {
return flash_.FlashAddressToMcuAddress(PartitionToFlashAddress(address));
}
// Converts an address from the partition address space to the flash address
// space. If the partition reserves additional space in the sector, the flash
// address space may not be contiguous, and this conversion accounts for that.
virtual FlashMemory::Address PartitionToFlashAddress(Address address) const {
return flash_.start_address() +
(start_sector_index_ - flash_.start_sector()) * sector_size_bytes() +
address;
}
bool writable() const {
return permission_ == PartitionPermission::kReadAndWrite;
}
uint32_t start_sector_index() const { return start_sector_index_; }
protected:
Status CheckBounds(Address address, size_t len) const;
FlashMemory& flash() const { return flash_; }
private:
FlashMemory& flash_;
const uint32_t start_sector_index_;
const uint32_t sector_count_;
const uint32_t alignment_bytes_;
const PartitionPermission permission_;
};
} // namespace pw::kvs