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/light.h" 22 #include "pw_kvs/alignment.h" 23 #include "pw_status/status.h" 24 #include "pw_status/status_with_size.h" 25 26 namespace pw { 27 namespace kvs { 28 29 enum class PartitionPermission : bool { 30 kReadOnly, 31 kReadAndWrite, 32 }; 33 34 class FlashMemory { 35 public: 36 // The flash address is in the range of: 0 to FlashSize. 37 typedef uint32_t Address; 38 39 // TODO(pwbug/246): This can be constexpr when tokenized asserts are fixed. 40 FlashMemory(size_t sector_size, 41 size_t sector_count, 42 size_t alignment, 43 uint32_t start_address = 0, 44 uint32_t sector_start = 0, 45 std::byte erased_memory_content = std::byte(0xFF)) sector_size_(sector_size)46 : sector_size_(sector_size), 47 flash_sector_count_(sector_count), 48 alignment_(alignment), 49 start_address_(start_address), 50 start_sector_(sector_start), 51 erased_memory_content_(erased_memory_content) { 52 PW_ASSERT(alignment_ != 0u); 53 } 54 55 virtual ~FlashMemory() = default; 56 57 virtual Status Enable() = 0; 58 59 virtual Status Disable() = 0; 60 61 virtual bool IsEnabled() const = 0; 62 SelfTest()63 virtual Status SelfTest() { return Status::Unimplemented(); } 64 65 // Erase num_sectors starting at a given address. Blocking call. 66 // Address should be on a sector boundary. Returns: 67 // 68 // OK - success 69 // DEADLINE_EXCEEDED - timeout 70 // INVALID_ARGUMENT - address is not sector-aligned 71 // OUT_OF_RANGE - erases past the end of the memory 72 virtual Status Erase(Address flash_address, size_t num_sectors) = 0; 73 74 // Reads bytes from flash into buffer. Blocking call. Returns: 75 // 76 // OK - success 77 // DEADLINE_EXCEEDED - timeout 78 // OUT_OF_RANGE - write does not fit in the flash memory 79 virtual StatusWithSize Read(Address address, std::span<std::byte> output) = 0; 80 Read(Address address,void * buffer,size_t len)81 StatusWithSize Read(Address address, void* buffer, size_t len) { 82 return Read(address, 83 std::span<std::byte>(static_cast<std::byte*>(buffer), len)); 84 } 85 86 // Writes bytes to flash. Blocking call. Returns: 87 // 88 // OK - success 89 // DEADLINE_EXCEEDED - timeout 90 // INVALID_ARGUMENT - address or data size are not aligned 91 // OUT_OF_RANGE - write does not fit in the memory 92 virtual StatusWithSize Write(Address destination_flash_address, 93 std::span<const std::byte> data) = 0; 94 Write(Address destination_flash_address,const void * data,size_t len)95 StatusWithSize Write(Address destination_flash_address, 96 const void* data, 97 size_t len) { 98 return Write( 99 destination_flash_address, 100 std::span<const std::byte>(static_cast<const std::byte*>(data), len)); 101 } 102 103 // Convert an Address to an MCU pointer, this can be used for memory 104 // mapped reads. Return NULL if the memory is not memory mapped. FlashAddressToMcuAddress(Address)105 virtual std::byte* FlashAddressToMcuAddress(Address) const { return nullptr; } 106 107 // start_sector() is useful for FlashMemory instances where the 108 // sector start is not 0. (ex.: cases where there are portions of flash 109 // that should be handled independently). start_sector()110 constexpr uint32_t start_sector() const { return start_sector_; } 111 sector_size_bytes()112 constexpr size_t sector_size_bytes() const { return sector_size_; } 113 sector_count()114 constexpr size_t sector_count() const { return flash_sector_count_; } 115 alignment_bytes()116 constexpr size_t alignment_bytes() const { return alignment_; } 117 size_bytes()118 constexpr size_t size_bytes() const { 119 return sector_size_ * flash_sector_count_; 120 } 121 122 // Address of the start of flash (the address of sector 0) start_address()123 constexpr uint32_t start_address() const { return start_address_; } 124 erased_memory_content()125 constexpr std::byte erased_memory_content() const { 126 return erased_memory_content_; 127 } 128 129 private: 130 const uint32_t sector_size_; 131 const uint32_t flash_sector_count_; 132 const uint32_t alignment_; 133 const uint32_t start_address_; 134 const uint32_t start_sector_; 135 const std::byte erased_memory_content_; 136 }; 137 138 class FlashPartition { 139 public: 140 // The flash address is in the range of: 0 to PartitionSize. 141 using Address = uint32_t; 142 143 // Implement Output for the Write method. 144 class Output final : public pw::Output { 145 public: Output(FlashPartition & flash,FlashPartition::Address address)146 constexpr Output(FlashPartition& flash, FlashPartition::Address address) 147 : flash_(flash), address_(address) {} 148 149 private: 150 StatusWithSize DoWrite(std::span<const std::byte> data) override; 151 152 FlashPartition& flash_; 153 FlashPartition::Address address_; 154 }; 155 156 // Implement Input for the Read method. 157 class Input final : public pw::Input { 158 public: Input(FlashPartition & flash,FlashPartition::Address address)159 constexpr Input(FlashPartition& flash, FlashPartition::Address address) 160 : flash_(flash), address_(address) {} 161 162 private: 163 StatusWithSize DoRead(std::span<std::byte> data) override; 164 165 FlashPartition& flash_; 166 FlashPartition::Address address_; 167 }; 168 169 FlashPartition( 170 FlashMemory* flash, 171 uint32_t start_sector_index, 172 uint32_t sector_count, 173 uint32_t alignment_bytes = 0, // Defaults to flash alignment 174 PartitionPermission permission = PartitionPermission::kReadAndWrite); 175 176 // Creates a FlashPartition that uses the entire flash with its alignment. FlashPartition(FlashMemory * flash)177 FlashPartition(FlashMemory* flash) 178 : FlashPartition( 179 flash, 0, flash->sector_count(), flash->alignment_bytes()) {} 180 181 FlashPartition(FlashPartition&&) = default; 182 FlashPartition(const FlashPartition&) = delete; 183 FlashPartition& operator=(const FlashPartition&) = delete; 184 185 virtual ~FlashPartition() = default; 186 187 // Performs any required partition or flash-level initialization. Init()188 virtual Status Init() { return OkStatus(); } 189 190 // Erase num_sectors starting at a given address. Blocking call. 191 // Address must be on a sector boundary. Returns: 192 // 193 // OK - success. 194 // TIMEOUT - on timeout. 195 // INVALID_ARGUMENT - address or sector count is invalid. 196 // PERMISSION_DENIED - partition is read only. 197 // UNKNOWN - HAL error 198 virtual Status Erase(Address address, size_t num_sectors); 199 Erase()200 Status Erase() { return Erase(0, this->sector_count()); } 201 202 // Reads bytes from flash into buffer. Blocking call. Returns: 203 // 204 // OK - success. 205 // TIMEOUT - on timeout. 206 // INVALID_ARGUMENT - address or length is invalid. 207 // UNKNOWN - HAL error 208 virtual StatusWithSize Read(Address address, std::span<std::byte> output); 209 Read(Address address,size_t length,void * output)210 StatusWithSize Read(Address address, size_t length, void* output) { 211 return Read(address, 212 std::span<std::byte>(static_cast<std::byte*>(output), length)); 213 } 214 215 // Writes bytes to flash. Address and data.size_bytes() must both be a 216 // multiple of alignment_bytes(). Blocking call. Returns: 217 // 218 // OK - success. 219 // TIMEOUT - on timeout. 220 // INVALID_ARGUMENT - address or length is invalid. 221 // PERMISSION_DENIED - partition is read only. 222 // UNKNOWN - HAL error 223 virtual StatusWithSize Write(Address address, 224 std::span<const std::byte> data); 225 226 // Check to see if chunk of flash partition is erased. Address and len need to 227 // be aligned with FlashMemory. Returns: 228 // 229 // OK - success. 230 // TIMEOUT - on timeout. 231 // INVALID_ARGUMENT - address or length is invalid. 232 // UNKNOWN - HAL error 233 // TODO: Result<bool> 234 virtual Status IsRegionErased(Address source_flash_address, 235 size_t length, 236 bool* is_erased); 237 238 // Check if the entire partition is erased. 239 // Returns: same as IsRegionErased(). IsErased(bool * is_erased)240 Status IsErased(bool* is_erased) { 241 return IsRegionErased(0, this->size_bytes(), is_erased); 242 } 243 244 // Checks to see if the data appears to be erased. No reads or writes occur; 245 // the FlashPartition simply compares the data to 246 // flash_.erased_memory_content(). 247 bool AppearsErased(std::span<const std::byte> data) const; 248 249 // Overridden by derived classes. The reported sector size is space available 250 // to users of FlashPartition. It accounts for space reserved in the sector 251 // for FlashPartition to store metadata. sector_size_bytes()252 virtual size_t sector_size_bytes() const { 253 return flash_.sector_size_bytes(); 254 } 255 size_bytes()256 size_t size_bytes() const { return sector_count() * sector_size_bytes(); } 257 258 // Alignment required for write address and write size. alignment_bytes()259 size_t alignment_bytes() const { return alignment_bytes_; } 260 sector_count()261 size_t sector_count() const { return sector_count_; } 262 263 // Convert a FlashMemory::Address to an MCU pointer, this can be used for 264 // memory mapped reads. Return NULL if the memory is not memory mapped. PartitionAddressToMcuAddress(Address address)265 std::byte* PartitionAddressToMcuAddress(Address address) const { 266 return flash_.FlashAddressToMcuAddress(PartitionToFlashAddress(address)); 267 } 268 269 // Converts an address from the partition address space to the flash address 270 // space. If the partition reserves additional space in the sector, the flash 271 // address space may not be contiguous, and this conversion accounts for that. PartitionToFlashAddress(Address address)272 virtual FlashMemory::Address PartitionToFlashAddress(Address address) const { 273 return flash_.start_address() + 274 (start_sector_index_ - flash_.start_sector()) * sector_size_bytes() + 275 address; 276 } 277 writable()278 bool writable() const { 279 return permission_ == PartitionPermission::kReadAndWrite; 280 } 281 erased_memory_content()282 constexpr std::byte erased_memory_content() const { 283 return flash_.erased_memory_content(); 284 } 285 start_sector_index()286 uint32_t start_sector_index() const { return start_sector_index_; } 287 288 protected: 289 Status CheckBounds(Address address, size_t len) const; 290 flash()291 FlashMemory& flash() const { return flash_; } 292 293 private: 294 FlashMemory& flash_; 295 const uint32_t start_sector_index_; 296 const uint32_t sector_count_; 297 const uint32_t alignment_bytes_; 298 const PartitionPermission permission_; 299 }; 300 301 } // namespace kvs 302 } // namespace pw 303