David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 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 | |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 15 | #include <span> |
| 16 | |
| 17 | #include "gtest/gtest.h" |
| 18 | #include "pw_kvs/flash_memory.h" |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 19 | #include "pw_kvs/flash_test_partition.h" |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 20 | #include "pw_kvs_private/config.h" |
| 21 | #include "pw_log/log.h" |
| 22 | |
| 23 | namespace pw::kvs::PartitionTest { |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 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; |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 31 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 32 | size_t error_count = 0; |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 33 | |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 34 | void WriteData(FlashPartition& partition, uint8_t fill_byte) { |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 35 | uint8_t test_data[kMaxFlashAlignment]; |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 36 | memset(test_data, fill_byte, sizeof(test_data)); |
| 37 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 38 | const size_t alignment = partition.alignment_bytes(); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 39 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 40 | ASSERT_EQ(Status::OK, partition.Erase(0, partition.sector_count())); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 41 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 42 | const size_t chunks_per_sector = partition.sector_size_bytes() / alignment; |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 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++) { |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 54 | StatusWithSize status = |
| 55 | partition.Write(address, as_bytes(std::span(test_data, alignment))); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 56 | ASSERT_EQ(Status::OK, status.status()); |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 57 | ASSERT_EQ(alignment, status.size()); |
| 58 | address += alignment; |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 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. |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 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)); |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 72 | StatusWithSize status = partition.Read(address, alignment, test_data); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 73 | |
| 74 | EXPECT_EQ(Status::OK, status.status()); |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 75 | EXPECT_EQ(alignment, status.size()); |
| 76 | if (!status.ok() || (alignment != status.size())) { |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 77 | error_count++; |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 78 | PW_LOG_DEBUG(" Read Error [%s], %u of %u", |
| 79 | status.status().str(), |
| 80 | unsigned(status.size()), |
| 81 | unsigned(alignment)); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 82 | continue; |
| 83 | } |
| 84 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 85 | for (size_t i = 0; i < alignment; i++) { |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 86 | if (test_data[i] != fill_byte) { |
| 87 | error_count++; |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 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)); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 95 | } |
| 96 | } |
| 97 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 98 | address += alignment; |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 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 | |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 110 | TEST(FlashPartitionTest, FillTest) { |
| 111 | FlashPartition& test_partition = FlashTestPartition(); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 112 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 113 | ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes()); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 114 | |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 115 | for (size_t i = 0; i < kTestIterations; i++) { |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 116 | PW_LOG_DEBUG("FillTest iteration %u, write '0'", unsigned(i)); |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 117 | WriteData(test_partition, 0); |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 118 | PW_LOG_DEBUG("FillTest iteration %u, write '0xff'", unsigned(i)); |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 119 | WriteData(test_partition, 0xff); |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 120 | PW_LOG_DEBUG("FillTest iteration %u, write '0x55'", unsigned(i)); |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 121 | WriteData(test_partition, 0x55); |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 122 | PW_LOG_DEBUG("FillTest iteration %u, write '0xa3'", unsigned(i)); |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 123 | WriteData(test_partition, 0xa3); |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 124 | PW_LOG_DEBUG("Completed iterations %u, Total errors %u", |
| 125 | unsigned(i), |
| 126 | unsigned(error_count)); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 130 | TEST(FlashPartitionTest, EraseTest) { |
| 131 | FlashPartition& test_partition = FlashTestPartition(); |
| 132 | |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 133 | static const uint8_t fill_byte = 0x55; |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 134 | uint8_t test_data[kMaxFlashAlignment]; |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 135 | memset(test_data, fill_byte, sizeof(test_data)); |
| 136 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 137 | ASSERT_GE(kMaxFlashAlignment, test_partition.alignment_bytes()); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 138 | |
| 139 | const size_t block_size = |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 140 | std::min(sizeof(test_data), test_partition.sector_size_bytes()); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 141 | auto data_span = std::span(test_data, block_size); |
| 142 | |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 143 | ASSERT_EQ(Status::OK, test_partition.Erase(0, test_partition.sector_count())); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 144 | |
| 145 | // Write to the first page of each sector. |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 146 | for (size_t sector_index = 0; sector_index < test_partition.sector_count(); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 147 | sector_index++) { |
| 148 | FlashPartition::Address address = |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 149 | sector_index * test_partition.sector_size_bytes(); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 150 | |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 151 | StatusWithSize status = test_partition.Write(address, as_bytes(data_span)); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 152 | ASSERT_EQ(Status::OK, status.status()); |
| 153 | ASSERT_EQ(block_size, status.size()); |
| 154 | } |
| 155 | |
David Rogers | a5661ef | 2020-07-09 15:15:12 -0700 | [diff] [blame] | 156 | // Preset the flag to make sure the check actually sets it. |
| 157 | bool is_erased = true; |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 158 | ASSERT_EQ(Status::OK, test_partition.IsErased(&is_erased)); |
David Rogers | a5661ef | 2020-07-09 15:15:12 -0700 | [diff] [blame] | 159 | ASSERT_EQ(false, is_erased); |
| 160 | |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 161 | ASSERT_EQ(Status::OK, test_partition.Erase()); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 162 | |
David Rogers | a5661ef | 2020-07-09 15:15:12 -0700 | [diff] [blame] | 163 | // Preset the flag to make sure the check actually sets it. |
| 164 | is_erased = false; |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 165 | ASSERT_EQ(Status::OK, test_partition.IsErased(&is_erased)); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 166 | ASSERT_EQ(true, is_erased); |
| 167 | |
| 168 | // Read the first page of each sector and make sure it has been erased. |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 169 | for (size_t sector_index = 0; sector_index < test_partition.sector_count(); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 170 | sector_index++) { |
| 171 | FlashPartition::Address address = |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 172 | sector_index * test_partition.sector_size_bytes(); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 173 | |
| 174 | StatusWithSize status = |
David Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 175 | test_partition.Read(address, data_span.size_bytes(), data_span.data()); |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 176 | EXPECT_EQ(Status::OK, status.status()); |
| 177 | EXPECT_EQ(data_span.size_bytes(), status.size()); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 178 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 179 | EXPECT_EQ(true, test_partition.AppearsErased(as_bytes(data_span))); |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 180 | } |
| 181 | } |
| 182 | |
David Rogers | 6a6dae6 | 2020-07-10 01:13:38 -0700 | [diff] [blame^] | 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_EQ(kMaxFlashAlignment % alignment, 0U); |
| 190 | EXPECT_LE(kMaxFlashAlignment, sector_size_bytes); |
| 191 | EXPECT_LE(sector_size_bytes % kMaxFlashAlignment, 0U); |
| 192 | } |
| 193 | |
| 194 | TEST(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 Rogers | 6a262b4 | 2020-07-09 03:27:41 -0700 | [diff] [blame] | 238 | } // namespace |
David Rogers | ca59296 | 2020-07-01 09:21:54 -0700 | [diff] [blame] | 239 | } // namespace pw::kvs::PartitionTest |