• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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