1 // Copyright 2020 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 #pragma once 15 16 #include <cstddef> 17 #include <cstdint> 18 #include <initializer_list> 19 #include <span> 20 21 #include "pw_assert/assert.h" 22 #include "pw_kvs/alignment.h" 23 #include "pw_polyfill/standard.h" 24 #include "pw_status/status.h" 25 #include "pw_status/status_with_size.h" 26 27 #if PW_CXX_STANDARD_IS_SUPPORTED(17) // Requires C++17 for pw::Result 28 #include "pw_stream/seek.h" 29 #include "pw_stream/stream.h" 30 #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) 31 32 namespace pw { 33 namespace kvs { 34 35 enum class PartitionPermission : bool { 36 kReadOnly, 37 kReadAndWrite, 38 }; 39 40 class FlashMemory { 41 public: 42 // The flash address is in the range of: 0 to FlashSize. 43 typedef uint32_t Address; 44 45 // TODO(pwbug/246): This can be constexpr when tokenized asserts are fixed. 46 FlashMemory(size_t sector_size, 47 size_t sector_count, 48 size_t alignment, 49 uint32_t start_address = 0, 50 uint32_t sector_start = 0, 51 std::byte erased_memory_content = std::byte(0xFF)) sector_size_(sector_size)52 : sector_size_(sector_size), 53 flash_sector_count_(sector_count), 54 alignment_(alignment), 55 start_address_(start_address), 56 start_sector_(sector_start), 57 erased_memory_content_(erased_memory_content) { 58 PW_ASSERT(alignment_ != 0u); 59 } 60 61 virtual ~FlashMemory() = default; 62 63 virtual Status Enable() = 0; 64 65 virtual Status Disable() = 0; 66 67 virtual bool IsEnabled() const = 0; 68 SelfTest()69 virtual Status SelfTest() { return Status::Unimplemented(); } 70 71 // Erase num_sectors starting at a given address. Blocking call. 72 // Address should be on a sector boundary. Returns: 73 // 74 // OK - success 75 // DEADLINE_EXCEEDED - timeout 76 // INVALID_ARGUMENT - address is not sector-aligned 77 // OUT_OF_RANGE - erases past the end of the memory 78 virtual Status Erase(Address flash_address, size_t num_sectors) = 0; 79 80 // Reads bytes from flash into buffer. Blocking call. Returns: 81 // 82 // OK - success 83 // DEADLINE_EXCEEDED - timeout 84 // OUT_OF_RANGE - write does not fit in the flash memory 85 virtual StatusWithSize Read(Address address, std::span<std::byte> output) = 0; 86 Read(Address address,void * buffer,size_t len)87 StatusWithSize Read(Address address, void* buffer, size_t len) { 88 return Read(address, 89 std::span<std::byte>(static_cast<std::byte*>(buffer), len)); 90 } 91 92 // Writes bytes to flash. Blocking call. Returns: 93 // 94 // OK - success 95 // DEADLINE_EXCEEDED - timeout 96 // INVALID_ARGUMENT - address or data size are not aligned 97 // OUT_OF_RANGE - write does not fit in the memory 98 virtual StatusWithSize Write(Address destination_flash_address, 99 std::span<const std::byte> data) = 0; 100 Write(Address destination_flash_address,const void * data,size_t len)101 StatusWithSize Write(Address destination_flash_address, 102 const void* data, 103 size_t len) { 104 return Write( 105 destination_flash_address, 106 std::span<const std::byte>(static_cast<const std::byte*>(data), len)); 107 } 108 109 // Convert an Address to an MCU pointer, this can be used for memory 110 // mapped reads. Return NULL if the memory is not memory mapped. FlashAddressToMcuAddress(Address)111 virtual std::byte* FlashAddressToMcuAddress(Address) const { return nullptr; } 112 113 // start_sector() is useful for FlashMemory instances where the 114 // sector start is not 0. (ex.: cases where there are portions of flash 115 // that should be handled independently). start_sector()116 constexpr uint32_t start_sector() const { return start_sector_; } 117 sector_size_bytes()118 constexpr size_t sector_size_bytes() const { return sector_size_; } 119 sector_count()120 constexpr size_t sector_count() const { return flash_sector_count_; } 121 alignment_bytes()122 constexpr size_t alignment_bytes() const { return alignment_; } 123 size_bytes()124 constexpr size_t size_bytes() const { 125 return sector_size_ * flash_sector_count_; 126 } 127 128 // Address of the start of flash (the address of sector 0) start_address()129 constexpr uint32_t start_address() const { return start_address_; } 130 erased_memory_content()131 constexpr std::byte erased_memory_content() const { 132 return erased_memory_content_; 133 } 134 135 private: 136 const uint32_t sector_size_; 137 const uint32_t flash_sector_count_; 138 const uint32_t alignment_; 139 const uint32_t start_address_; 140 const uint32_t start_sector_; 141 const std::byte erased_memory_content_; 142 }; 143 144 class FlashPartition { 145 public: 146 // The flash address is in the range of: 0 to PartitionSize. 147 using Address = uint32_t; 148 149 #if PW_CXX_STANDARD_IS_SUPPORTED(17) // Requires C++17 for pw::Result 150 class Writer final : public stream::NonSeekableWriter { 151 public: Writer(kvs::FlashPartition & partition)152 constexpr Writer(kvs::FlashPartition& partition) 153 : partition_(partition), position_(0) {} 154 155 private: 156 Status DoWrite(ConstByteSpan data) override; 157 DoTell()158 size_t DoTell() const override { return position_; } 159 ConservativeLimit(LimitType type)160 size_t ConservativeLimit(LimitType type) const override { 161 return type == LimitType::kWrite ? partition_.size_bytes() - position_ 162 : 0; 163 } 164 165 FlashPartition& partition_; 166 size_t position_; 167 }; 168 169 class Reader final : public stream::SeekableReader { 170 public: Reader(kvs::FlashPartition & partition)171 constexpr Reader(kvs::FlashPartition& partition) 172 : partition_(partition), position_(0) {} 173 174 Reader(const Reader&) = delete; 175 Reader& operator=(const Reader&) = delete; 176 177 private: 178 StatusWithSize DoRead(ByteSpan data) override; 179 DoTell()180 size_t DoTell() const override { return position_; } 181 DoSeek(ptrdiff_t offset,Whence origin)182 Status DoSeek(ptrdiff_t offset, Whence origin) override { 183 return CalculateSeek(offset, origin, partition_.size_bytes(), position_); 184 } 185 ConservativeLimit(LimitType type)186 size_t ConservativeLimit(LimitType type) const override { 187 return type == LimitType::kRead ? partition_.size_bytes() - position_ : 0; 188 } 189 190 FlashPartition& partition_; 191 size_t position_; 192 }; 193 #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) 194 195 // Implement Output for the Write method. 196 class Output final : public pw::Output { 197 public: Output(FlashPartition & flash,FlashPartition::Address address)198 constexpr Output(FlashPartition& flash, FlashPartition::Address address) 199 : flash_(flash), address_(address) {} 200 201 private: 202 StatusWithSize DoWrite(std::span<const std::byte> data) override; 203 204 FlashPartition& flash_; 205 FlashPartition::Address address_; 206 }; 207 208 // Implement Input for the Read method. 209 class Input final : public pw::Input { 210 public: Input(FlashPartition & flash,FlashPartition::Address address)211 constexpr Input(FlashPartition& flash, FlashPartition::Address address) 212 : flash_(flash), address_(address) {} 213 214 private: 215 StatusWithSize DoRead(std::span<std::byte> data) override; 216 217 FlashPartition& flash_; 218 FlashPartition::Address address_; 219 }; 220 221 FlashPartition( 222 FlashMemory* flash, 223 uint32_t start_sector_index, 224 uint32_t sector_count, 225 uint32_t alignment_bytes = 0, // Defaults to flash alignment 226 PartitionPermission permission = PartitionPermission::kReadAndWrite); 227 228 // Creates a FlashPartition that uses the entire flash with its alignment. FlashPartition(FlashMemory * flash)229 FlashPartition(FlashMemory* flash) 230 : FlashPartition( 231 flash, 0, flash->sector_count(), flash->alignment_bytes()) {} 232 233 FlashPartition(FlashPartition&&) = default; 234 FlashPartition(const FlashPartition&) = delete; 235 FlashPartition& operator=(const FlashPartition&) = delete; 236 237 virtual ~FlashPartition() = default; 238 239 // Performs any required partition or flash-level initialization. Init()240 virtual Status Init() { return OkStatus(); } 241 242 // Erase num_sectors starting at a given address. Blocking call. 243 // Address must be on a sector boundary. Returns: 244 // 245 // OK - success. 246 // TIMEOUT - on timeout. 247 // INVALID_ARGUMENT - address or sector count is invalid. 248 // PERMISSION_DENIED - partition is read only. 249 // UNKNOWN - HAL error 250 virtual Status Erase(Address address, size_t num_sectors); 251 Erase()252 Status Erase() { return Erase(0, this->sector_count()); } 253 254 // Reads bytes from flash into buffer. Blocking call. Returns: 255 // 256 // OK - success. 257 // TIMEOUT - on timeout. 258 // INVALID_ARGUMENT - address or length is invalid. 259 // UNKNOWN - HAL error 260 virtual StatusWithSize Read(Address address, std::span<std::byte> output); 261 Read(Address address,size_t length,void * output)262 StatusWithSize Read(Address address, size_t length, void* output) { 263 return Read(address, 264 std::span<std::byte>(static_cast<std::byte*>(output), length)); 265 } 266 267 // Writes bytes to flash. Address and data.size_bytes() must both be a 268 // multiple of alignment_bytes(). Blocking call. Returns: 269 // 270 // OK - success. 271 // TIMEOUT - on timeout. 272 // INVALID_ARGUMENT - address or length is invalid. 273 // PERMISSION_DENIED - partition is read only. 274 // UNKNOWN - HAL error 275 virtual StatusWithSize Write(Address address, 276 std::span<const std::byte> data); 277 278 // Check to see if chunk of flash partition is erased. Address and len need to 279 // be aligned with FlashMemory. Returns: 280 // 281 // OK - success. 282 // TIMEOUT - on timeout. 283 // INVALID_ARGUMENT - address or length is invalid. 284 // UNKNOWN - HAL error 285 // TODO: Result<bool> 286 virtual Status IsRegionErased(Address source_flash_address, 287 size_t length, 288 bool* is_erased); 289 290 // Check if the entire partition is erased. 291 // Returns: same as IsRegionErased(). IsErased(bool * is_erased)292 Status IsErased(bool* is_erased) { 293 return IsRegionErased(0, this->size_bytes(), is_erased); 294 } 295 296 // Checks to see if the data appears to be erased. No reads or writes occur; 297 // the FlashPartition simply compares the data to 298 // flash_.erased_memory_content(). 299 bool AppearsErased(std::span<const std::byte> data) const; 300 301 // Overridden by derived classes. The reported sector size is space available 302 // to users of FlashPartition. It accounts for space reserved in the sector 303 // for FlashPartition to store metadata. sector_size_bytes()304 virtual size_t sector_size_bytes() const { 305 return flash_.sector_size_bytes(); 306 } 307 size_bytes()308 size_t size_bytes() const { return sector_count() * sector_size_bytes(); } 309 310 // Alignment required for write address and write size. alignment_bytes()311 size_t alignment_bytes() const { return alignment_bytes_; } 312 sector_count()313 size_t sector_count() const { return sector_count_; } 314 315 // Convert a FlashMemory::Address to an MCU pointer, this can be used for 316 // memory mapped reads. Return NULL if the memory is not memory mapped. PartitionAddressToMcuAddress(Address address)317 std::byte* PartitionAddressToMcuAddress(Address address) const { 318 return flash_.FlashAddressToMcuAddress(PartitionToFlashAddress(address)); 319 } 320 321 // Converts an address from the partition address space to the flash address 322 // space. If the partition reserves additional space in the sector, the flash 323 // address space may not be contiguous, and this conversion accounts for that. PartitionToFlashAddress(Address address)324 virtual FlashMemory::Address PartitionToFlashAddress(Address address) const { 325 return flash_.start_address() + 326 (start_sector_index_ - flash_.start_sector()) * sector_size_bytes() + 327 address; 328 } 329 writable()330 bool writable() const { 331 return permission_ == PartitionPermission::kReadAndWrite; 332 } 333 erased_memory_content()334 constexpr std::byte erased_memory_content() const { 335 return flash_.erased_memory_content(); 336 } 337 start_sector_index()338 uint32_t start_sector_index() const { return start_sector_index_; } 339 340 protected: 341 Status CheckBounds(Address address, size_t len) const; 342 flash()343 FlashMemory& flash() const { return flash_; } 344 345 private: 346 FlashMemory& flash_; 347 const uint32_t start_sector_index_; 348 const uint32_t sector_count_; 349 const uint32_t alignment_bytes_; 350 const PartitionPermission permission_; 351 }; 352 353 } // namespace kvs 354 } // namespace pw 355