H753_CPP_W25Q_SPI_Flash_Read_Write_01 1.0
STM32H753 W25Q SPI flash (Adafruit 5632/5633/5634) read/write tutorial
Loading...
Searching...
No Matches
w25q_validation.cpp
1/*
2 * w25q_validation.cpp
3 *
4 * Created on: Aug 30, 2026
5 * Author: johng
6 */
7
8#include "w25q_validation.hpp"
9
10#include <stdio.h>
11
13 resetReport();
14}
15
16void W25QValidation::resetReport() {
17 _report.status = W25Q_VALIDATION_STATUS_PASS;
18 _report.failedTest = W25Q_VALIDATION_TEST_NONE;
19 _report.failedPhase = W25Q_VALIDATION_PHASE_IDLE;
20 _report.failedOffset = 0;
21 _report.expectedValue = 0;
22 _report.actualValue = 0;
23 _report.testsRun = 0;
24}
25
26void W25QValidation::recordFailure(w25qValidationTestId_t testId, w25qValidationPhase_t phase,
27 uint32_t offset, uint8_t expectedValue, uint8_t actualValue) {
28 _report.status = W25Q_VALIDATION_STATUS_FAIL;
29 _report.failedTest = testId;
30 _report.failedPhase = phase;
31 _report.failedOffset = offset;
32 _report.expectedValue = expectedValue;
33 _report.actualValue = actualValue;
34}
35
36const char *W25QValidation::getTestName(w25qValidationTestId_t testId) {
37 switch (testId) {
38 case W25Q_VALIDATION_TEST_ERASED_STATE:
39 return "Erased-state test";
40 case W25Q_VALIDATION_TEST_SMALL_RANGE:
41 return "Small range read/write test";
42 case W25Q_VALIDATION_TEST_PATTERN:
43 return "Pattern data test";
44 case W25Q_VALIDATION_TEST_PAGE_BOUNDARY:
45 return "Page-boundary write test";
46 case W25Q_VALIDATION_TEST_NONE:
47 default:
48 return "None";
49 }
50}
51
52const char *W25QValidation::getPhaseName(w25qValidationPhase_t phase) {
53 switch (phase) {
54 case W25Q_VALIDATION_PHASE_ERASE:
55 return "erase";
56 case W25Q_VALIDATION_PHASE_FILL:
57 return "fill";
58 case W25Q_VALIDATION_PHASE_VERIFY:
59 return "verify";
60 case W25Q_VALIDATION_PHASE_IDLE:
61 default:
62 return "idle";
63 }
64}
65
66void W25QValidation::printFailure() const {
67 printf(" FAIL\r\n");
68 printf(" Test : %s\r\n", getTestName(_report.failedTest));
69 printf(" Phase : %s\r\n", getPhaseName(_report.failedPhase));
70 printf(" Offset : 0x%06lX\r\n", (unsigned long)_report.failedOffset);
71 printf(" Expected: 0x%02X\r\n", _report.expectedValue);
72 printf(" Actual : 0x%02X\r\n", _report.actualValue);
73}
74
75bool W25QValidation::runErasedStateTest() {
76 _report.testsRun++;
77 _report.failedTest = W25Q_VALIDATION_TEST_ERASED_STATE;
78 _report.failedPhase = W25Q_VALIDATION_PHASE_ERASE;
79
80 printf("[TEST 1] %s\r\n", getTestName(W25Q_VALIDATION_TEST_ERASED_STATE));
81
82 bool erased = _flash.eraseSector(W25Q_VALIDATION_BASE_ADDR);
83 if (!erased) {
84 recordFailure(W25Q_VALIDATION_TEST_ERASED_STATE, W25Q_VALIDATION_PHASE_ERASE,
85 W25Q_VALIDATION_BASE_ADDR, 0xFF, 0x00);
86 printFailure();
87 return false;
88 }
89
90 _report.failedPhase = W25Q_VALIDATION_PHASE_VERIFY;
91
92 uint8_t buf[W25Q_VALIDATION_SMALL_RANGE_SIZE];
93 bool readOk = _flash.readBuffer(W25Q_VALIDATION_BASE_ADDR, buf, sizeof(buf));
94 if (!readOk) {
95 recordFailure(W25Q_VALIDATION_TEST_ERASED_STATE, W25Q_VALIDATION_PHASE_VERIFY,
96 W25Q_VALIDATION_BASE_ADDR, 0xFF, 0x00);
97 printFailure();
98 return false;
99 }
100
101 for (uint32_t offset = 0; offset < sizeof(buf); offset++) {
102 if (buf[offset] != 0xFF) {
103 recordFailure(W25Q_VALIDATION_TEST_ERASED_STATE, W25Q_VALIDATION_PHASE_VERIFY,
104 W25Q_VALIDATION_BASE_ADDR + offset, 0xFF, buf[offset]);
105 printFailure();
106 return false;
107 }
108 }
109
110 printf(" PASS\r\n");
111 return true;
112}
113
114bool W25QValidation::runSmallRangeTest() {
115 _report.testsRun++;
116 _report.failedTest = W25Q_VALIDATION_TEST_SMALL_RANGE;
117 _report.failedPhase = W25Q_VALIDATION_PHASE_ERASE;
118
119 printf("[TEST 2] %s\r\n", getTestName(W25Q_VALIDATION_TEST_SMALL_RANGE));
120
121 bool erased = _flash.eraseSector(W25Q_VALIDATION_BASE_ADDR);
122 if (!erased) {
123 recordFailure(W25Q_VALIDATION_TEST_SMALL_RANGE, W25Q_VALIDATION_PHASE_ERASE,
124 W25Q_VALIDATION_BASE_ADDR, 0, 0);
125 printFailure();
126 return false;
127 }
128
129 uint8_t writeBuf[W25Q_VALIDATION_SMALL_RANGE_SIZE];
130 for (uint32_t offset = 0; offset < sizeof(writeBuf); offset++) {
131 writeBuf[offset] = (uint8_t)(offset & 0xFFu);
132 }
133
134 _report.failedPhase = W25Q_VALIDATION_PHASE_FILL;
135 bool written = _flash.writeBuffer(W25Q_VALIDATION_BASE_ADDR, writeBuf, sizeof(writeBuf));
136 if (!written) {
137 recordFailure(W25Q_VALIDATION_TEST_SMALL_RANGE, W25Q_VALIDATION_PHASE_FILL,
138 W25Q_VALIDATION_BASE_ADDR, 0, 0);
139 printFailure();
140 return false;
141 }
142
143 _report.failedPhase = W25Q_VALIDATION_PHASE_VERIFY;
144 uint8_t readBuf[W25Q_VALIDATION_SMALL_RANGE_SIZE];
145 bool readOk = _flash.readBuffer(W25Q_VALIDATION_BASE_ADDR, readBuf, sizeof(readBuf));
146 if (!readOk) {
147 recordFailure(W25Q_VALIDATION_TEST_SMALL_RANGE, W25Q_VALIDATION_PHASE_VERIFY,
148 W25Q_VALIDATION_BASE_ADDR, 0, 0);
149 printFailure();
150 return false;
151 }
152
153 for (uint32_t offset = 0; offset < sizeof(readBuf); offset++) {
154 uint8_t expectedValue = (uint8_t)(offset & 0xFFu);
155 if (readBuf[offset] != expectedValue) {
156 recordFailure(W25Q_VALIDATION_TEST_SMALL_RANGE, W25Q_VALIDATION_PHASE_VERIFY,
157 W25Q_VALIDATION_BASE_ADDR + offset, expectedValue, readBuf[offset]);
158 printFailure();
159 return false;
160 }
161 }
162
163 printf(" PASS\r\n");
164 return true;
165}
166
167bool W25QValidation::runPatternTest() {
168 static const uint8_t patterns[] = {
169 0x00u, 0xFFu, 0x55u, 0xAAu,
170 0x01u, 0x02u, 0x04u, 0x08u, 0x10u, 0x20u, 0x40u, 0x80u,
171 0xFEu, 0xFDu, 0xFBu, 0xF7u, 0xEFu, 0xDFu, 0xBFu, 0x7Fu
172 };
173 uint32_t patternCount = (uint32_t)(sizeof(patterns) / sizeof(patterns[0]));
174
175 _report.testsRun++;
176 _report.failedTest = W25Q_VALIDATION_TEST_PATTERN;
177
178 printf("[TEST 3] %s\r\n", getTestName(W25Q_VALIDATION_TEST_PATTERN));
179
180 for (uint32_t patternIndex = 0; patternIndex < patternCount; patternIndex++) {
181 uint8_t expectedValue = patterns[patternIndex];
182
183 // Each pattern re-erases first: Page Program can only clear bits
184 // (1 -> 0), so writing e.g. 0xFF after 0x00 would silently fail to
185 // set any bits back without an erase in between.
186 _report.failedPhase = W25Q_VALIDATION_PHASE_ERASE;
187 bool erased = _flash.eraseSector(W25Q_VALIDATION_BASE_ADDR);
188 if (!erased) {
189 recordFailure(W25Q_VALIDATION_TEST_PATTERN, W25Q_VALIDATION_PHASE_ERASE,
190 W25Q_VALIDATION_BASE_ADDR, expectedValue, 0);
191 printFailure();
192 return false;
193 }
194
195 uint8_t writeBuf[W25Q_VALIDATION_PATTERN_TEST_SIZE];
196 for (uint32_t i = 0; i < sizeof(writeBuf); i++) {
197 writeBuf[i] = expectedValue;
198 }
199
200 _report.failedPhase = W25Q_VALIDATION_PHASE_FILL;
201 bool written = _flash.writeBuffer(W25Q_VALIDATION_BASE_ADDR, writeBuf, sizeof(writeBuf));
202 if (!written) {
203 recordFailure(W25Q_VALIDATION_TEST_PATTERN, W25Q_VALIDATION_PHASE_FILL,
204 W25Q_VALIDATION_BASE_ADDR, expectedValue, 0);
205 printFailure();
206 return false;
207 }
208
209 _report.failedPhase = W25Q_VALIDATION_PHASE_VERIFY;
210 uint8_t readBuf[W25Q_VALIDATION_PATTERN_TEST_SIZE];
211 bool readOk = _flash.readBuffer(W25Q_VALIDATION_BASE_ADDR, readBuf, sizeof(readBuf));
212 if (!readOk) {
213 recordFailure(W25Q_VALIDATION_TEST_PATTERN, W25Q_VALIDATION_PHASE_VERIFY,
214 W25Q_VALIDATION_BASE_ADDR, expectedValue, 0);
215 printFailure();
216 return false;
217 }
218
219 for (uint32_t offset = 0; offset < sizeof(readBuf); offset++) {
220 if (readBuf[offset] != expectedValue) {
221 recordFailure(W25Q_VALIDATION_TEST_PATTERN, W25Q_VALIDATION_PHASE_VERIFY,
222 W25Q_VALIDATION_BASE_ADDR + offset, expectedValue, readBuf[offset]);
223 printFailure();
224 return false;
225 }
226 }
227
228 printf(" [%2lu/%2lu] Pattern 0x%02X PASS\r\n",
229 (unsigned long)(patternIndex + 1), (unsigned long)patternCount, expectedValue);
230 }
231
232 printf(" PASS\r\n");
233 return true;
234}
235
236bool W25QValidation::runPageBoundaryTest() {
237 _report.testsRun++;
238 _report.failedTest = W25Q_VALIDATION_TEST_PAGE_BOUNDARY;
239 _report.failedPhase = W25Q_VALIDATION_PHASE_ERASE;
240
241 printf("[TEST 4] %s\r\n", getTestName(W25Q_VALIDATION_TEST_PAGE_BOUNDARY));
242
243 bool erased = _flash.eraseSector(W25Q_VALIDATION_BASE_ADDR);
244 if (!erased) {
245 recordFailure(W25Q_VALIDATION_TEST_PAGE_BOUNDARY, W25Q_VALIDATION_PHASE_ERASE,
246 W25Q_VALIDATION_BASE_ADDR, 0, 0);
247 printFailure();
248 return false;
249 }
250
251 // Start 128 bytes before a page boundary so the write spans two pages --
252 // exercises writeBuffer()'s internal per-page splitting logic.
253 uint32_t startAddr = W25Q_VALIDATION_BASE_ADDR + (W25Q_PAGE_SIZE - 128u);
254
255 uint8_t writeBuf[W25Q_VALIDATION_BOUNDARY_TEST_SIZE];
256 for (uint32_t i = 0; i < sizeof(writeBuf); i++) {
257 writeBuf[i] = (uint8_t)(i & 0xFFu);
258 }
259
260 _report.failedPhase = W25Q_VALIDATION_PHASE_FILL;
261 bool written = _flash.writeBuffer(startAddr, writeBuf, sizeof(writeBuf));
262 if (!written) {
263 recordFailure(W25Q_VALIDATION_TEST_PAGE_BOUNDARY, W25Q_VALIDATION_PHASE_FILL,
264 startAddr, 0, 0);
265 printFailure();
266 return false;
267 }
268
269 _report.failedPhase = W25Q_VALIDATION_PHASE_VERIFY;
270 uint8_t readBuf[W25Q_VALIDATION_BOUNDARY_TEST_SIZE];
271 bool readOk = _flash.readBuffer(startAddr, readBuf, sizeof(readBuf));
272 if (!readOk) {
273 recordFailure(W25Q_VALIDATION_TEST_PAGE_BOUNDARY, W25Q_VALIDATION_PHASE_VERIFY,
274 startAddr, 0, 0);
275 printFailure();
276 return false;
277 }
278
279 for (uint32_t offset = 0; offset < sizeof(readBuf); offset++) {
280 uint8_t expectedValue = (uint8_t)(offset & 0xFFu);
281 if (readBuf[offset] != expectedValue) {
282 recordFailure(W25Q_VALIDATION_TEST_PAGE_BOUNDARY, W25Q_VALIDATION_PHASE_VERIFY,
283 startAddr + offset, expectedValue, readBuf[offset]);
284 printFailure();
285 return false;
286 }
287 }
288
289 printf(" PASS\r\n");
290 return true;
291}
292
294 resetReport();
295
296 printf("W25Q validation start\r\n\r\n");
297 printf("WARNING: this suite is destructive -- it overwrites the first sector\r\n");
298 printf("(%lu bytes) starting at address 0x%06lX.\r\n\r\n",
299 (unsigned long)W25Q_SECTOR_SIZE, (unsigned long)W25Q_VALIDATION_BASE_ADDR);
300
301 bool ok = runErasedStateTest();
302
303 if (ok) {
304 printf("\r\n");
305 ok = runSmallRangeTest();
306 }
307
308 if (ok) {
309 printf("\r\n");
310 ok = runPatternTest();
311 }
312
313 if (ok) {
314 printf("\r\n");
315 ok = runPageBoundaryTest();
316 }
317
318 if (ok) {
319 printf("\r\nW25Q validation complete: PASS\r\n");
320 } else {
321 printf("\r\nW25Q validation complete: FAIL\r\n");
322 }
323
324 return &_report;
325}
326
328 return &_report;
329}
STM32 HAL SPI1 driver for a Winbond W25Q-series flash chip (Adafruit 5632/5633/5634),...
bool eraseSector(uint32_t addr)
Erases the single 4KB sector containing the given address.
bool readBuffer(uint32_t addr, uint8_t *buf, uint32_t len)
Reads len bytes starting at addr into buf.
bool writeBuffer(uint32_t addr, const uint8_t *data, uint32_t len)
Writes len bytes starting at addr, paging internally at 256-byte boundaries as the chip's Page Progra...
const w25qValidationReport_t * GetReport() const
Gets the most recent validation report without rerunning tests.
const w25qValidationReport_t * RunAll()
Runs every validation test in sequence, stopping at the first failure, printing progress and results ...
W25QValidation(W25QFlashSPI &flash)
Constructs the validation suite against an already-initialized driver.
Detailed validation report, persistent for the life of the W25QValidation instance.
uint32_t failedOffset
Byte offset (from W25Q_VALIDATION_BASE_ADDR) where the failure was detected.
uint8_t actualValue
Value actually read back at failedOffset.
uint8_t expectedValue
Value expected to be read back at failedOffset.
w25qValidationTestId_t failedTest
Which test failed, or NONE if status is PASS.
w25qValidationStatus_t status
Overall PASS/FAIL outcome of the last RunAll().
uint32_t testsRun
Count of tests that started running, whether they passed or failed.
w25qValidationPhase_t failedPhase
Which phase of failedTest failed (erase/fill/verify).