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
15 #include <span>
16
17 #include "gtest/gtest.h"
18 #include "pw_kvs/flash_memory.h"
19 #include "pw_kvs/flash_test_partition.h"
20 #include "pw_kvs_private/config.h"
21 #include "pw_log/log.h"
22
23 namespace pw::kvs::PartitionTest {
24 namespace {
25
26 #ifndef PW_FLASH_TEST_ITERATIONS
27 #define PW_FLASH_TEST_ITERATIONS 2
28 #endif // PW_FLASH_TEST_ITERATIONS
29
30 constexpr size_t kTestIterations = PW_FLASH_TEST_ITERATIONS;
31
32 size_t error_count = 0;
33
WriteData(FlashPartition & partition,uint8_t fill_byte)34 void WriteData(FlashPartition& partition, uint8_t fill_byte) {
35 uint8_t test_data[kMaxFlashAlignment];
36 memset(test_data, fill_byte, sizeof(test_data));
37
38 const size_t alignment = partition.alignment_bytes();
39
40 ASSERT_EQ(OkStatus(), partition.Erase(0, partition.sector_count()));
41
42 const size_t chunks_per_sector = partition.sector_size_bytes() / alignment;
43
44 // Fill partition sector by sector. Fill the sector with an integer number
45 // of alignment-size chunks. If the sector is not evenly divisible by
46 // alignment-size, the remainder is not written.
47 for (size_t sector_index = 0; sector_index < partition.sector_count();
48 sector_index++) {
49 FlashPartition::Address address =
50 sector_index * partition.sector_size_bytes();
51
52 for (size_t chunk_index = 0; chunk_index < chunks_per_sector;
53 chunk_index++) {
54 StatusWithSize status =
55 partition.Write(address, as_bytes(std::span(test_data, alignment)));
56 ASSERT_EQ(OkStatus(), status.status());
57 ASSERT_EQ(alignment, status.size());
58 address += alignment;
59 }
60 }
61
62 // Check the fill result. Use expect so the test doesn't bail on error.
63 // Count the errors and print if any errors are found.
64 for (size_t sector_index = 0; sector_index < partition.sector_count();
65 sector_index++) {
66 FlashPartition::Address address =
67 sector_index * partition.sector_size_bytes();
68
69 for (size_t chunk_index = 0; chunk_index < chunks_per_sector;
70 chunk_index++) {
71 memset(test_data, 0, sizeof(test_data));
72 StatusWithSize status = partition.Read(address, alignment, test_data);
73
74 EXPECT_EQ(OkStatus(), status.status());
75 EXPECT_EQ(alignment, status.size());
76 if (!status.ok() || (alignment != status.size())) {
77 error_count++;
78 PW_LOG_DEBUG(" Read Error [%s], %u of %u",
79 status.status().str(),
80 unsigned(status.size()),
81 unsigned(alignment));
82 continue;
83 }
84
85 for (size_t i = 0; i < alignment; i++) {
86 if (test_data[i] != fill_byte) {
87 error_count++;
88 PW_LOG_DEBUG(
89 " Error %u, Read compare @ address %x, got 0x%02x, "
90 "expected 0x%02x",
91 unsigned(error_count),
92 unsigned(address + i),
93 unsigned(test_data[i]),
94 unsigned(fill_byte));
95 }
96 }
97
98 address += alignment;
99 }
100 }
101
102 EXPECT_EQ(error_count, 0U);
103 if (error_count != 0) {
104 PW_LOG_ERROR("Partition test, fill '%c', %u errors found",
105 fill_byte,
106 unsigned(error_count));
107 }
108 }
109
TEST(FlashPartitionTest,FillTest)110 TEST(FlashPartitionTest, FillTest) {
111 FlashPartition& test_partition = FlashTestPartition();
112
113 ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes());
114
115 for (size_t i = 0; i < kTestIterations; i++) {
116 PW_LOG_DEBUG("FillTest iteration %u, write '0'", unsigned(i));
117 WriteData(test_partition, 0);
118 PW_LOG_DEBUG("FillTest iteration %u, write '0xff'", unsigned(i));
119 WriteData(test_partition, 0xff);
120 PW_LOG_DEBUG("FillTest iteration %u, write '0x55'", unsigned(i));
121 WriteData(test_partition, 0x55);
122 PW_LOG_DEBUG("FillTest iteration %u, write '0xa3'", unsigned(i));
123 WriteData(test_partition, 0xa3);
124 PW_LOG_DEBUG("Completed iterations %u, Total errors %u",
125 unsigned(i),
126 unsigned(error_count));
127 }
128 }
129
TEST(FlashPartitionTest,EraseTest)130 TEST(FlashPartitionTest, EraseTest) {
131 FlashPartition& test_partition = FlashTestPartition();
132
133 static const uint8_t fill_byte = 0x55;
134 uint8_t test_data[kMaxFlashAlignment];
135 memset(test_data, fill_byte, sizeof(test_data));
136
137 ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes());
138
139 const size_t block_size =
140 std::min(sizeof(test_data), test_partition.sector_size_bytes());
141 auto data_span = std::span(test_data, block_size);
142
143 ASSERT_EQ(OkStatus(), test_partition.Erase(0, test_partition.sector_count()));
144
145 // Write to the first page of each sector.
146 for (size_t sector_index = 0; sector_index < test_partition.sector_count();
147 sector_index++) {
148 FlashPartition::Address address =
149 sector_index * test_partition.sector_size_bytes();
150
151 StatusWithSize status = test_partition.Write(address, as_bytes(data_span));
152 ASSERT_EQ(OkStatus(), status.status());
153 ASSERT_EQ(block_size, status.size());
154 }
155
156 // Preset the flag to make sure the check actually sets it.
157 bool is_erased = true;
158 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
159 ASSERT_EQ(false, is_erased);
160
161 ASSERT_EQ(OkStatus(), test_partition.Erase());
162
163 // Preset the flag to make sure the check actually sets it.
164 is_erased = false;
165 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
166 ASSERT_EQ(true, is_erased);
167
168 // Read the first page of each sector and make sure it has been erased.
169 for (size_t sector_index = 0; sector_index < test_partition.sector_count();
170 sector_index++) {
171 FlashPartition::Address address =
172 sector_index * test_partition.sector_size_bytes();
173
174 StatusWithSize status =
175 test_partition.Read(address, data_span.size_bytes(), data_span.data());
176 EXPECT_EQ(OkStatus(), status.status());
177 EXPECT_EQ(data_span.size_bytes(), status.size());
178
179 EXPECT_EQ(true, test_partition.AppearsErased(as_bytes(data_span)));
180 }
181 }
182
TEST(FlashPartitionTest,AlignmentCheck)183 TEST(FlashPartitionTest, AlignmentCheck) {
184 FlashPartition& test_partition = FlashTestPartition();
185 const size_t alignment = test_partition.alignment_bytes();
186 const size_t sector_size_bytes = test_partition.sector_size_bytes();
187
188 EXPECT_LE(alignment, kMaxFlashAlignment);
189 EXPECT_GT(alignment, 0u);
190 EXPECT_EQ(kMaxFlashAlignment % alignment, 0U);
191 EXPECT_LE(kMaxFlashAlignment, sector_size_bytes);
192 EXPECT_LE(sector_size_bytes % kMaxFlashAlignment, 0U);
193 }
194
195 #define TESTING_CHECK_FAILURES_IS_SUPPORTED 0
196 #if TESTING_CHECK_FAILURES_IS_SUPPORTED
197 // TODO: Ensure that this test triggers an assert.
TEST(FlashPartitionTest,BadWriteAddressAlignment)198 TEST(FlashPartitionTest, BadWriteAddressAlignment) {
199 FlashPartition& test_partition = FlashTestPartition();
200
201 // Can't get bad alignment with alignment of 1.
202 if (test_partition.alignment_bytes() == 1) {
203 return;
204 }
205
206 std::array<std::byte, kMaxFlashAlignment> source_data;
207 test_partition.Write(1, source_data);
208 }
209
210 // TODO: Ensure that this test triggers an assert.
TEST(FlashPartitionTest,BadWriteSizeAlignment)211 TEST(FlashPartitionTest, BadWriteSizeAlignment) {
212 FlashPartition& test_partition = FlashTestPartition();
213
214 // Can't get bad alignment with alignment of 1.
215 if (test_partition.alignment_bytes() == 1) {
216 return;
217 }
218
219 std::array<std::byte, 1> source_data;
220 test_partition.Write(0, source_data);
221 }
222
223 // TODO: Ensure that this test triggers an assert.
TEST(FlashPartitionTest,BadEraseAddressAlignment)224 TEST(FlashPartitionTest, BadEraseAddressAlignment) {
225 FlashPartition& test_partition = FlashTestPartition();
226
227 // Can't get bad alignment with sector size of 1.
228 if (test_partition.sector_size_bytes() == 1) {
229 return;
230 }
231
232 // Try Erase at address 1 for 1 sector.
233 test_partition.Erase(1, 1);
234 }
235
236 #endif // TESTING_CHECK_FAILURES_IS_SUPPORTED
237
TEST(FlashPartitionTest,IsErased)238 TEST(FlashPartitionTest, IsErased) {
239 FlashPartition& test_partition = FlashTestPartition();
240 const size_t alignment = test_partition.alignment_bytes();
241
242 // Make sure the partition is big enough to do this test.
243 ASSERT_GE(test_partition.size_bytes(), 3 * kMaxFlashAlignment);
244
245 ASSERT_EQ(OkStatus(), test_partition.Erase());
246
247 bool is_erased = true;
248 ASSERT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
249 ASSERT_EQ(true, is_erased);
250
251 static const uint8_t fill_byte = 0x55;
252 uint8_t test_data[kMaxFlashAlignment];
253 memset(test_data, fill_byte, sizeof(test_data));
254 auto data_span = std::span(test_data);
255
256 // Write the chunk with fill byte.
257 StatusWithSize status = test_partition.Write(alignment, as_bytes(data_span));
258 ASSERT_EQ(OkStatus(), status.status());
259 ASSERT_EQ(data_span.size_bytes(), status.size());
260
261 EXPECT_EQ(OkStatus(), test_partition.IsErased(&is_erased));
262 EXPECT_EQ(false, is_erased);
263
264 // Check the chunk that was written.
265 EXPECT_EQ(OkStatus(),
266 test_partition.IsRegionErased(
267 alignment, data_span.size_bytes(), &is_erased));
268 EXPECT_EQ(false, is_erased);
269
270 // Check a region that starts erased but later has been written.
271 EXPECT_EQ(OkStatus(),
272 test_partition.IsRegionErased(0, 2 * alignment, &is_erased));
273 EXPECT_EQ(false, is_erased);
274
275 // Check erased for a region smaller than kMaxFlashAlignment. This has been a
276 // bug in the past.
277 EXPECT_EQ(OkStatus(),
278 test_partition.IsRegionErased(0, alignment, &is_erased));
279 EXPECT_EQ(true, is_erased);
280 }
281
282 } // namespace
283 } // namespace pw::kvs::PartitionTest
284