• 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 
20 #include "pw_assert/assert.h"
21 #include "pw_kvs/alignment.h"
22 #include "pw_polyfill/standard.h"
23 #include "pw_span/span.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(b/235149326): 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, 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, span<std::byte>(static_cast<std::byte*>(buffer), len));
89   }
90 
91   // Writes bytes to flash. Blocking call. Returns:
92   //
93   // OK - success
94   // DEADLINE_EXCEEDED - timeout
95   // INVALID_ARGUMENT - address or data size are not aligned
96   // OUT_OF_RANGE - write does not fit in the memory
97   virtual StatusWithSize Write(Address destination_flash_address,
98                                span<const std::byte> data) = 0;
99 
Write(Address destination_flash_address,const void * data,size_t len)100   StatusWithSize Write(Address destination_flash_address,
101                        const void* data,
102                        size_t len) {
103     return Write(
104         destination_flash_address,
105         span<const std::byte>(static_cast<const std::byte*>(data), len));
106   }
107 
108   // Convert an Address to an MCU pointer, this can be used for memory
109   // mapped reads. Return NULL if the memory is not memory mapped.
FlashAddressToMcuAddress(Address)110   virtual std::byte* FlashAddressToMcuAddress(Address) const { return nullptr; }
111 
112   // start_sector() is useful for FlashMemory instances where the
113   // sector start is not 0. (ex.: cases where there are portions of flash
114   // that should be handled independently).
start_sector()115   constexpr uint32_t start_sector() const { return start_sector_; }
116 
sector_size_bytes()117   constexpr size_t sector_size_bytes() const { return sector_size_; }
118 
sector_count()119   constexpr size_t sector_count() const { return flash_sector_count_; }
120 
alignment_bytes()121   constexpr size_t alignment_bytes() const { return alignment_; }
122 
size_bytes()123   constexpr size_t size_bytes() const {
124     return sector_size_ * flash_sector_count_;
125   }
126 
127   // Address of the start of flash (the address of sector 0)
start_address()128   constexpr uint32_t start_address() const { return start_address_; }
129 
erased_memory_content()130   constexpr std::byte erased_memory_content() const {
131     return erased_memory_content_;
132   }
133 
134  private:
135   const uint32_t sector_size_;
136   const uint32_t flash_sector_count_;
137   const uint32_t alignment_;
138   const uint32_t start_address_;
139   const uint32_t start_sector_;
140   const std::byte erased_memory_content_;
141 };
142 
143 class FlashPartition {
144  public:
145   // The flash address is in the range of: 0 to PartitionSize.
146   using Address = uint32_t;
147 
148 #if PW_CXX_STANDARD_IS_SUPPORTED(17)  // Requires C++17 for pw::Result
149   class Writer final : public stream::NonSeekableWriter {
150    public:
Writer(kvs::FlashPartition & partition)151     constexpr Writer(kvs::FlashPartition& partition)
152         : partition_(partition), position_(0) {}
153 
154    private:
155     Status DoWrite(ConstByteSpan data) override;
156 
DoTell()157     size_t DoTell() override { return position_; }
158 
ConservativeLimit(LimitType type)159     size_t ConservativeLimit(LimitType type) const override {
160       return type == LimitType::kWrite ? partition_.size_bytes() - position_
161                                        : 0;
162     }
163 
164     FlashPartition& partition_;
165     size_t position_;
166   };
167 
168   class Reader final : public stream::SeekableReader {
169    public:
Reader(kvs::FlashPartition & partition)170     constexpr Reader(kvs::FlashPartition& partition)
171         : partition_(partition), position_(0) {}
172 
173     Reader(const Reader&) = delete;
174     Reader& operator=(const Reader&) = delete;
175 
176    private:
177     StatusWithSize DoRead(ByteSpan data) override;
178 
DoTell()179     size_t DoTell() override { return position_; }
180 
DoSeek(ptrdiff_t offset,Whence origin)181     Status DoSeek(ptrdiff_t offset, Whence origin) override {
182       return CalculateSeek(offset, origin, partition_.size_bytes(), position_);
183     }
184 
ConservativeLimit(LimitType type)185     size_t ConservativeLimit(LimitType type) const override {
186       return type == LimitType::kRead ? partition_.size_bytes() - position_ : 0;
187     }
188 
189     FlashPartition& partition_;
190     size_t position_;
191   };
192 #endif  // PW_CXX_STANDARD_IS_SUPPORTED(17)
193 
194   // Implement Output for the Write method.
195   class Output final : public pw::Output {
196    public:
Output(FlashPartition & flash,FlashPartition::Address address)197     constexpr Output(FlashPartition& flash, FlashPartition::Address address)
198         : flash_(flash), address_(address) {}
199 
200    private:
201     StatusWithSize DoWrite(span<const std::byte> data) override;
202 
203     FlashPartition& flash_;
204     FlashPartition::Address address_;
205   };
206 
207   // Implement Input for the Read method.
208   class Input final : public pw::Input {
209    public:
Input(FlashPartition & flash,FlashPartition::Address address)210     constexpr Input(FlashPartition& flash, FlashPartition::Address address)
211         : flash_(flash), address_(address) {}
212 
213    private:
214     StatusWithSize DoRead(span<std::byte> data) override;
215 
216     FlashPartition& flash_;
217     FlashPartition::Address address_;
218   };
219 
220   FlashPartition(
221       FlashMemory* flash,
222       uint32_t flash_start_sector_index,
223       uint32_t flash_sector_count,
224       uint32_t alignment_bytes = 0,  // Defaults to flash alignment
225       PartitionPermission permission = PartitionPermission::kReadAndWrite);
226 
227   // Creates a FlashPartition that uses the entire flash with its alignment.
FlashPartition(FlashMemory * flash)228   FlashPartition(FlashMemory* flash)
229       : FlashPartition(
230             flash, 0, flash->sector_count(), flash->alignment_bytes()) {}
231 
232   FlashPartition(FlashPartition&&) = default;
233   FlashPartition(const FlashPartition&) = delete;
234   FlashPartition& operator=(const FlashPartition&) = delete;
235 
236   virtual ~FlashPartition() = default;
237 
238   // Performs any required partition or flash-level initialization.
Init()239   virtual Status Init() { return OkStatus(); }
240 
241   // Erase num_sectors starting at a given address. Blocking call.
242   // Address must be on a sector boundary. Returns:
243   //
244   // OK - success.
245   // TIMEOUT - on timeout.
246   // INVALID_ARGUMENT - address or sector count is invalid.
247   // PERMISSION_DENIED - partition is read only.
248   // UNKNOWN - HAL error
249   virtual Status Erase(Address address, size_t num_sectors);
250 
Erase()251   Status Erase() { return Erase(0, this->sector_count()); }
252 
253   // Reads bytes from flash into buffer. Blocking call. Returns:
254   //
255   // OK - success.
256   // TIMEOUT - on timeout.
257   // INVALID_ARGUMENT - address or length is invalid.
258   // UNKNOWN - HAL error
259   virtual StatusWithSize Read(Address address, span<std::byte> output);
260 
Read(Address address,size_t length,void * output)261   StatusWithSize Read(Address address, size_t length, void* output) {
262     return Read(address,
263                 span<std::byte>(static_cast<std::byte*>(output), length));
264   }
265 
266   // Writes bytes to flash. Address and data.size_bytes() must both be a
267   // multiple of alignment_bytes(). Blocking call. Returns:
268   //
269   // OK - success.
270   // TIMEOUT - on timeout.
271   // INVALID_ARGUMENT - address or length is invalid.
272   // PERMISSION_DENIED - partition is read only.
273   // UNKNOWN - HAL error
274   virtual StatusWithSize Write(Address address, span<const std::byte> data);
275 
276   // Check to see if chunk of flash partition is erased. Address and len need to
277   // be aligned with FlashMemory. Returns:
278   //
279   // OK - success.
280   // TIMEOUT - on timeout.
281   // INVALID_ARGUMENT - address or length is invalid.
282   // UNKNOWN - HAL error
283   // TODO(hepler): Result<bool>
284   virtual Status IsRegionErased(Address source_flash_address,
285                                 size_t length,
286                                 bool* is_erased);
287 
288   // Check if the entire partition is erased.
289   // Returns: same as IsRegionErased().
IsErased(bool * is_erased)290   Status IsErased(bool* is_erased) {
291     return IsRegionErased(0, this->size_bytes(), is_erased);
292   }
293 
294   // Checks to see if the data appears to be erased. No reads or writes occur;
295   // the FlashPartition simply compares the data to
296   // flash_.erased_memory_content().
297   bool AppearsErased(span<const std::byte> data) const;
298 
299   // Optionally overridden by derived classes. The reported sector size is space
300   // available to users of FlashPartition. This reported size can be smaller or
301   // larger than the sector size of the backing FlashMemory.
302   //
303   // Possible reasons for size to be different from the backing FlashMemory
304   // could be due to space reserved in the sector for FlashPartition to store
305   // metadata or due to logical FlashPartition sectors that combine several
306   // FlashMemory sectors.
sector_size_bytes()307   virtual size_t sector_size_bytes() const {
308     return flash_.sector_size_bytes();
309   }
310 
311   // Optionally overridden by derived classes. The reported sector count is
312   // sectors available to users of FlashPartition. This reported count can be
313   // same or smaller than the given flash_sector_count of the backing
314   // FlashMemory for the same region of flash.
315   //
316   // Possible reasons for count to be different from the backing FlashMemory
317   // could be due to space reserved in the FlashPartition to store metadata or
318   // due to logical FlashPartition sectors that combine several FlashMemory
319   // sectors.
sector_count()320   virtual size_t sector_count() const { return flash_sector_count_; }
321 
size_bytes()322   size_t size_bytes() const { return sector_count() * sector_size_bytes(); }
323 
324   // Alignment required for write address and write size.
alignment_bytes()325   size_t alignment_bytes() const { return alignment_bytes_; }
326 
327   // Convert a FlashMemory::Address to an MCU pointer, this can be used for
328   // memory mapped reads. Return NULL if the memory is not memory mapped.
PartitionAddressToMcuAddress(Address address)329   std::byte* PartitionAddressToMcuAddress(Address address) const {
330     return flash_.FlashAddressToMcuAddress(PartitionToFlashAddress(address));
331   }
332 
333   // Converts an address from the partition address space to the flash address
334   // space. If the partition reserves additional space in the sector, the flash
335   // address space may not be contiguous, and this conversion accounts for that.
PartitionToFlashAddress(Address address)336   virtual FlashMemory::Address PartitionToFlashAddress(Address address) const {
337     return flash_.start_address() +
338            (flash_start_sector_index_ - flash_.start_sector()) *
339                flash_.sector_size_bytes() +
340            address;
341   }
342 
writable()343   bool writable() const {
344     return permission_ == PartitionPermission::kReadAndWrite;
345   }
346 
erased_memory_content()347   constexpr std::byte erased_memory_content() const {
348     return flash_.erased_memory_content();
349   }
350 
start_sector_index()351   uint32_t start_sector_index() const { return flash_start_sector_index_; }
352 
353  protected:
354   Status CheckBounds(Address address, size_t len) const;
355 
flash()356   FlashMemory& flash() const { return flash_; }
357 
358   FlashMemory& flash_;
359   const uint32_t flash_sector_count_;
360 
361  private:
362   const uint32_t flash_start_sector_index_;
363   const uint32_t alignment_bytes_;
364   const PartitionPermission permission_;
365 };
366 
367 }  // namespace kvs
368 }  // namespace pw
369