blob: 760f8149f97e748490657303602a9cafd326b2ca [file] [log] [blame]
David Rogersca592962020-07-01 09:21:54 -07001// 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
David Rogersca592962020-07-01 09:21:54 -070015#include <span>
16
17#include "gtest/gtest.h"
18#include "pw_kvs/flash_memory.h"
David Rogers6a262b42020-07-09 03:27:41 -070019#include "pw_kvs/flash_test_partition.h"
David Rogersca592962020-07-01 09:21:54 -070020#include "pw_kvs_private/config.h"
21#include "pw_log/log.h"
22
23namespace pw::kvs::PartitionTest {
David Rogers6a262b42020-07-09 03:27:41 -070024namespace {
25
26#ifndef PW_FLASH_TEST_ITERATIONS
27#define PW_FLASH_TEST_ITERATIONS 2
28#endif // PW_FLASH_TEST_ITERATIONS
29
30constexpr size_t kTestIterations = PW_FLASH_TEST_ITERATIONS;
David Rogersca592962020-07-01 09:21:54 -070031
David Rogers6a6dae62020-07-10 01:13:38 -070032size_t error_count = 0;
David Rogersca592962020-07-01 09:21:54 -070033
David Rogersca592962020-07-01 09:21:54 -070034void WriteData(FlashPartition& partition, uint8_t fill_byte) {
David Rogers6a6dae62020-07-10 01:13:38 -070035 uint8_t test_data[kMaxFlashAlignment];
David Rogersca592962020-07-01 09:21:54 -070036 memset(test_data, fill_byte, sizeof(test_data));
37
David Rogers6a6dae62020-07-10 01:13:38 -070038 const size_t alignment = partition.alignment_bytes();
David Rogersca592962020-07-01 09:21:54 -070039
David Rogers6a6dae62020-07-10 01:13:38 -070040 ASSERT_EQ(Status::OK, partition.Erase(0, partition.sector_count()));
David Rogersca592962020-07-01 09:21:54 -070041
David Rogers6a6dae62020-07-10 01:13:38 -070042 const size_t chunks_per_sector = partition.sector_size_bytes() / alignment;
David Rogersca592962020-07-01 09:21:54 -070043
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++) {
David Rogers6a6dae62020-07-10 01:13:38 -070054 StatusWithSize status =
55 partition.Write(address, as_bytes(std::span(test_data, alignment)));
David Rogersca592962020-07-01 09:21:54 -070056 ASSERT_EQ(Status::OK, status.status());
David Rogers6a6dae62020-07-10 01:13:38 -070057 ASSERT_EQ(alignment, status.size());
58 address += alignment;
David Rogersca592962020-07-01 09:21:54 -070059 }
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.
David Rogersca592962020-07-01 09:21:54 -070064 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));
David Rogers6a6dae62020-07-10 01:13:38 -070072 StatusWithSize status = partition.Read(address, alignment, test_data);
David Rogersca592962020-07-01 09:21:54 -070073
74 EXPECT_EQ(Status::OK, status.status());
David Rogers6a6dae62020-07-10 01:13:38 -070075 EXPECT_EQ(alignment, status.size());
76 if (!status.ok() || (alignment != status.size())) {
David Rogersca592962020-07-01 09:21:54 -070077 error_count++;
David Rogers6a6dae62020-07-10 01:13:38 -070078 PW_LOG_DEBUG(" Read Error [%s], %u of %u",
79 status.status().str(),
80 unsigned(status.size()),
81 unsigned(alignment));
David Rogersca592962020-07-01 09:21:54 -070082 continue;
83 }
84
David Rogers6a6dae62020-07-10 01:13:38 -070085 for (size_t i = 0; i < alignment; i++) {
David Rogersca592962020-07-01 09:21:54 -070086 if (test_data[i] != fill_byte) {
87 error_count++;
David Rogers6a6dae62020-07-10 01:13:38 -070088 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));
David Rogersca592962020-07-01 09:21:54 -070095 }
96 }
97
David Rogers6a6dae62020-07-10 01:13:38 -070098 address += alignment;
David Rogersca592962020-07-01 09:21:54 -070099 }
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
David Rogers6a262b42020-07-09 03:27:41 -0700110TEST(FlashPartitionTest, FillTest) {
111 FlashPartition& test_partition = FlashTestPartition();
David Rogersca592962020-07-01 09:21:54 -0700112
David Rogers6a6dae62020-07-10 01:13:38 -0700113 ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes());
David Rogersca592962020-07-01 09:21:54 -0700114
David Rogers6a262b42020-07-09 03:27:41 -0700115 for (size_t i = 0; i < kTestIterations; i++) {
David Rogers6a6dae62020-07-10 01:13:38 -0700116 PW_LOG_DEBUG("FillTest iteration %u, write '0'", unsigned(i));
David Rogers6a262b42020-07-09 03:27:41 -0700117 WriteData(test_partition, 0);
David Rogers6a6dae62020-07-10 01:13:38 -0700118 PW_LOG_DEBUG("FillTest iteration %u, write '0xff'", unsigned(i));
David Rogers6a262b42020-07-09 03:27:41 -0700119 WriteData(test_partition, 0xff);
David Rogers6a6dae62020-07-10 01:13:38 -0700120 PW_LOG_DEBUG("FillTest iteration %u, write '0x55'", unsigned(i));
David Rogers6a262b42020-07-09 03:27:41 -0700121 WriteData(test_partition, 0x55);
David Rogers6a6dae62020-07-10 01:13:38 -0700122 PW_LOG_DEBUG("FillTest iteration %u, write '0xa3'", unsigned(i));
David Rogers6a262b42020-07-09 03:27:41 -0700123 WriteData(test_partition, 0xa3);
David Rogers6a6dae62020-07-10 01:13:38 -0700124 PW_LOG_DEBUG("Completed iterations %u, Total errors %u",
125 unsigned(i),
126 unsigned(error_count));
David Rogersca592962020-07-01 09:21:54 -0700127 }
128}
129
David Rogers6a262b42020-07-09 03:27:41 -0700130TEST(FlashPartitionTest, EraseTest) {
131 FlashPartition& test_partition = FlashTestPartition();
132
David Rogersca592962020-07-01 09:21:54 -0700133 static const uint8_t fill_byte = 0x55;
David Rogers6a6dae62020-07-10 01:13:38 -0700134 uint8_t test_data[kMaxFlashAlignment];
David Rogersca592962020-07-01 09:21:54 -0700135 memset(test_data, fill_byte, sizeof(test_data));
136
David Rogers6a6dae62020-07-10 01:13:38 -0700137 ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes());
David Rogersca592962020-07-01 09:21:54 -0700138
139 const size_t block_size =
David Rogers6a262b42020-07-09 03:27:41 -0700140 std::min(sizeof(test_data), test_partition.sector_size_bytes());
David Rogersca592962020-07-01 09:21:54 -0700141 auto data_span = std::span(test_data, block_size);
142
David Rogers6a262b42020-07-09 03:27:41 -0700143 ASSERT_EQ(Status::OK, test_partition.Erase(0, test_partition.sector_count()));
David Rogersca592962020-07-01 09:21:54 -0700144
145 // Write to the first page of each sector.
David Rogers6a262b42020-07-09 03:27:41 -0700146 for (size_t sector_index = 0; sector_index < test_partition.sector_count();
David Rogersca592962020-07-01 09:21:54 -0700147 sector_index++) {
148 FlashPartition::Address address =
David Rogers6a262b42020-07-09 03:27:41 -0700149 sector_index * test_partition.sector_size_bytes();
David Rogersca592962020-07-01 09:21:54 -0700150
David Rogers6a262b42020-07-09 03:27:41 -0700151 StatusWithSize status = test_partition.Write(address, as_bytes(data_span));
David Rogersca592962020-07-01 09:21:54 -0700152 ASSERT_EQ(Status::OK, status.status());
153 ASSERT_EQ(block_size, status.size());
154 }
155
David Rogersa5661ef2020-07-09 15:15:12 -0700156 // Preset the flag to make sure the check actually sets it.
157 bool is_erased = true;
David Rogers6a6dae62020-07-10 01:13:38 -0700158 ASSERT_EQ(Status::OK, test_partition.IsErased(&is_erased));
David Rogersa5661ef2020-07-09 15:15:12 -0700159 ASSERT_EQ(false, is_erased);
160
David Rogers6a262b42020-07-09 03:27:41 -0700161 ASSERT_EQ(Status::OK, test_partition.Erase());
David Rogersca592962020-07-01 09:21:54 -0700162
David Rogersa5661ef2020-07-09 15:15:12 -0700163 // Preset the flag to make sure the check actually sets it.
164 is_erased = false;
David Rogers6a6dae62020-07-10 01:13:38 -0700165 ASSERT_EQ(Status::OK, test_partition.IsErased(&is_erased));
David Rogersca592962020-07-01 09:21:54 -0700166 ASSERT_EQ(true, is_erased);
167
168 // Read the first page of each sector and make sure it has been erased.
David Rogers6a262b42020-07-09 03:27:41 -0700169 for (size_t sector_index = 0; sector_index < test_partition.sector_count();
David Rogersca592962020-07-01 09:21:54 -0700170 sector_index++) {
171 FlashPartition::Address address =
David Rogers6a262b42020-07-09 03:27:41 -0700172 sector_index * test_partition.sector_size_bytes();
David Rogersca592962020-07-01 09:21:54 -0700173
174 StatusWithSize status =
David Rogers6a262b42020-07-09 03:27:41 -0700175 test_partition.Read(address, data_span.size_bytes(), data_span.data());
David Rogers6a6dae62020-07-10 01:13:38 -0700176 EXPECT_EQ(Status::OK, status.status());
177 EXPECT_EQ(data_span.size_bytes(), status.size());
David Rogersca592962020-07-01 09:21:54 -0700178
David Rogers6a6dae62020-07-10 01:13:38 -0700179 EXPECT_EQ(true, test_partition.AppearsErased(as_bytes(data_span)));
David Rogersca592962020-07-01 09:21:54 -0700180 }
181}
182
David Rogers6a6dae62020-07-10 01:13:38 -0700183TEST(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_EQ(kMaxFlashAlignment % alignment, 0U);
190 EXPECT_LE(kMaxFlashAlignment, sector_size_bytes);
191 EXPECT_LE(sector_size_bytes % kMaxFlashAlignment, 0U);
192}
193
194TEST(FlashPartitionTest, IsErased) {
195 FlashPartition& test_partition = FlashTestPartition();
196 const size_t alignment = test_partition.alignment_bytes();
197
198 // Make sure the partition is big enough to do this test.
199 ASSERT_GE(test_partition.size_bytes(), 3 * kMaxFlashAlignment);
200
201 ASSERT_EQ(Status::OK, test_partition.Erase());
202
203 bool is_erased = true;
204 ASSERT_EQ(Status::OK, test_partition.IsErased(&is_erased));
205 ASSERT_EQ(true, is_erased);
206
207 static const uint8_t fill_byte = 0x55;
208 uint8_t test_data[kMaxFlashAlignment];
209 memset(test_data, fill_byte, sizeof(test_data));
210 auto data_span = std::span(test_data);
211
212 // Write the chunk with fill byte.
213 StatusWithSize status = test_partition.Write(alignment, as_bytes(data_span));
214 ASSERT_EQ(Status::OK, status.status());
215 ASSERT_EQ(data_span.size_bytes(), status.size());
216
217 EXPECT_EQ(Status::OK, test_partition.IsErased(&is_erased));
218 EXPECT_EQ(false, is_erased);
219
220 // Check the chunk that was written.
221 EXPECT_EQ(Status::OK,
222 test_partition.IsRegionErased(
223 alignment, data_span.size_bytes(), &is_erased));
224 EXPECT_EQ(false, is_erased);
225
226 // Check a region that starts erased but later has been written.
227 EXPECT_EQ(Status::OK,
228 test_partition.IsRegionErased(0, 2 * alignment, &is_erased));
229 EXPECT_EQ(false, is_erased);
230
231 // Check erased for a region smaller than kMaxFlashAlignment. This has been a
232 // bug in the past.
233 EXPECT_EQ(Status::OK,
234 test_partition.IsRegionErased(0, alignment, &is_erased));
235 EXPECT_EQ(true, is_erased);
236}
237
David Rogers6a262b42020-07-09 03:27:41 -0700238} // namespace
David Rogersca592962020-07-01 09:21:54 -0700239} // namespace pw::kvs::PartitionTest