27
27
#define SECTOR_MASK 0xFFFFF000
28
28
#define MEMORY_LIMIT (SPI_MEM_MEMORY_SIZE - 1)
29
29
#define SR_WIP 0x01
30
+ #define SR_PROTECT_BITS_MASK 0x3C
31
+ #define SR_PROTECT_BITS 0xC // protect the first 2 blocks - 128KB
32
+ #define CR1_TB_BIT_BOTTOM 0x8
33
+ #define CR1_TB_BIT_MASK 0x8
30
34
#define CMD_READ 0x03
31
35
#define CMD_WREN 0x06
36
+ #define CMD_WRSR 0x01
32
37
#define CMD_SE 0x20
33
38
#define CMD_PP 0x02
34
39
#define CMD_RDSR 0x05
40
+ #define CMD_RDCR 0x15
35
41
#define CMD_CE 0x60
36
42
43
+ // Drives the chip select pin low
37
44
static void _spi_mem_cs_low (void )
38
45
{
39
46
#ifndef TESTING
40
47
gpio_set_pin_level (PIN_MEM_CS , 0 );
41
48
#endif
42
49
}
43
50
51
+ // Drives the chip select pin high
44
52
static void _spi_mem_cs_high (void )
45
53
{
46
54
#ifndef TESTING
47
55
gpio_set_pin_level (PIN_MEM_CS , 1 );
48
56
#endif
49
57
}
50
58
59
+ // Reads the status register
51
60
static uint8_t _spi_mem_read_sr (void )
52
61
{
53
62
uint8_t buffer [2 ] = {0 };
@@ -58,19 +67,19 @@ static uint8_t _spi_mem_read_sr(void)
58
67
return buffer [1 ];
59
68
}
60
69
61
- static void _spi_mem_read (uint32_t address , size_t size , uint8_t * buffer )
70
+ // Reads the configuration register
71
+ static void _spi_mem_read_cr (uint8_t * data_out )
62
72
{
63
- buffer [0 ] = CMD_READ ;
64
- buffer [1 ] = (address >> 16 ) & 0xFF ;
65
- buffer [2 ] = (address >> 8 ) & 0xFF ;
66
- buffer [3 ] = address & 0xFF ;
67
- memset (& buffer [4 ], 0x00 , size );
68
-
73
+ uint8_t buffer [3 ] = {0 };
74
+ buffer [0 ] = CMD_RDCR ;
69
75
_spi_mem_cs_low ();
70
- SPI_MEM_exchange_block (buffer , size + 4 );
76
+ SPI_MEM_exchange_block (buffer , 3 );
71
77
_spi_mem_cs_high ();
78
+
79
+ memcpy (data_out , & buffer [1 ], 2 );
72
80
}
73
81
82
+ // Waits until the WIP bits goes low
74
83
static void _spi_mem_wait (void )
75
84
{
76
85
uint8_t status ;
@@ -79,20 +88,50 @@ static void _spi_mem_wait(void)
79
88
} while (status & SR_WIP );
80
89
}
81
90
82
- void spi_mem_full_erase (void )
91
+ // Set write enable bit
92
+ static void _spi_mem_write_enable (void )
93
+ {
94
+ uint8_t cmd = CMD_WREN ;
95
+ _spi_mem_cs_low ();
96
+ SPI_MEM_exchange_block (& cmd , 1 );
97
+ _spi_mem_cs_high ();
98
+ }
99
+
100
+ // Write the status and configuration registers
101
+ static void _spi_mem_write_sr (uint8_t * data_in )
83
102
{
84
- uint8_t buffer [2 ];
103
+ _spi_mem_write_enable ();
104
+ uint8_t buffer [4 ] = {0 };
105
+ buffer [0 ] = CMD_WRSR ;
106
+ memcpy (& buffer [1 ], data_in , 3 );
107
+ _spi_mem_cs_low ();
108
+ SPI_MEM_exchange_block (buffer , 4 );
109
+ _spi_mem_cs_high ();
110
+ _spi_mem_wait ();
111
+ }
112
+
113
+ // Reads `size` bytes starting from `address` and writes the data into `buffer`
114
+ static void _spi_mem_read (uint32_t address , size_t size , uint8_t * buffer )
115
+ {
116
+ buffer [0 ] = CMD_READ ;
117
+ buffer [1 ] = (address >> 16 ) & 0xFF ;
118
+ buffer [2 ] = (address >> 8 ) & 0xFF ;
119
+ buffer [3 ] = address & 0xFF ;
120
+ memset (& buffer [4 ], 0x00 , size );
85
121
86
- // --- Enable Write ---
87
- buffer [0 ] = CMD_WREN ;
88
122
_spi_mem_cs_low ();
89
- SPI_MEM_exchange_block (buffer , 1 );
123
+ SPI_MEM_exchange_block (buffer , size + 4 );
90
124
_spi_mem_cs_high ();
125
+ }
126
+
127
+ void spi_mem_full_erase (void )
128
+ {
129
+ _spi_mem_write_enable ();
91
130
92
131
// --- Chip Erase ---
93
- buffer [ 0 ] = CMD_CE ;
132
+ uint8_t cmd = CMD_CE ;
94
133
_spi_mem_cs_low ();
95
- SPI_MEM_exchange_block (buffer , 1 );
134
+ SPI_MEM_exchange_block (& cmd , 1 );
96
135
_spi_mem_cs_high ();
97
136
98
137
_spi_mem_wait ();
@@ -105,14 +144,10 @@ bool spi_mem_sector_erase(uint32_t sector_addr)
105
144
return false;
106
145
}
107
146
108
- uint8_t buffer [SPI_MEM_PAGE_SIZE + 4 ];
109
- // --- Enable Write ---
110
- buffer [0 ] = CMD_WREN ;
111
- _spi_mem_cs_low ();
112
- SPI_MEM_exchange_block (buffer , 1 );
113
- _spi_mem_cs_high ();
147
+ _spi_mem_write_enable ();
114
148
115
149
// --- Sector Erase (write 4 bytes) ---
150
+ uint8_t buffer [SPI_MEM_PAGE_SIZE + 4 ];
116
151
buffer [0 ] = CMD_SE ;
117
152
buffer [1 ] = (sector_addr >> 16 ) & 0xFF ;
118
153
buffer [2 ] = (sector_addr >> 8 ) & 0xFF ;
@@ -163,21 +198,18 @@ uint8_t* spi_mem_read(uint32_t address, size_t size)
163
198
return buffer ;
164
199
}
165
200
201
+ // Writes SPI_MEM_PAGE_SIZE bytes from `input` at `page_addr`
166
202
static bool _spi_mem_page_write (uint32_t page_addr , const uint8_t * input )
167
203
{
168
204
if (page_addr % SPI_MEM_PAGE_SIZE != 0 ) {
169
205
util_log ("Invalid page write address %p" , (void * )(uintptr_t )page_addr );
170
206
return false;
171
207
}
172
208
173
- uint8_t buffer [SPI_MEM_PAGE_SIZE + 4 ];
174
- // --- Enable Write ---
175
- buffer [0 ] = CMD_WREN ;
176
- _spi_mem_cs_low ();
177
- SPI_MEM_exchange_block (buffer , 1 );
178
- _spi_mem_cs_high ();
209
+ _spi_mem_write_enable ();
179
210
180
211
// --- Page Program (write 4 bytes) ---
212
+ uint8_t buffer [SPI_MEM_PAGE_SIZE + 4 ];
181
213
buffer [0 ] = CMD_PP ;
182
214
buffer [1 ] = (page_addr >> 16 ) & 0xFF ;
183
215
buffer [2 ] = (page_addr >> 8 ) & 0xFF ;
@@ -258,6 +290,47 @@ int32_t spi_mem_smart_erase(void)
258
290
return erased_sectors ;
259
291
}
260
292
293
+ // Writes the `protection` bits into the status register and sets the
294
+ // Top/Bottom bit to bottom in the configuration register.
295
+ static void _spi_mem_set_protection (uint8_t protection )
296
+ {
297
+ uint8_t reg [3 ];
298
+ reg [0 ] = _spi_mem_read_sr ();
299
+ _spi_mem_read_cr (& reg [1 ]);
300
+
301
+ // clean and update status register with protection bits
302
+ reg [0 ] &= ~SR_PROTECT_BITS_MASK ;
303
+ reg [0 ] |= protection & SR_PROTECT_BITS_MASK ;
304
+
305
+ // set the top/bottom protection bit.
306
+ // This is an OTP bit,so the write will have an effect
307
+ // only the first time.
308
+ reg [1 ] = reg [1 ] | CR1_TB_BIT_BOTTOM ;
309
+
310
+ _spi_mem_write_sr (reg );
311
+ }
312
+
313
+ void spi_mem_protected_area_lock (void )
314
+ {
315
+ _spi_mem_set_protection (SR_PROTECT_BITS );
316
+ }
317
+
318
+ void spi_mem_protected_area_unlock (void )
319
+ {
320
+ _spi_mem_set_protection (0x0 );
321
+ }
322
+
323
+ bool spi_mem_protected_area_write (uint32_t address , const uint8_t * input , size_t size )
324
+ {
325
+ bool result ;
326
+ uint8_t protection = _spi_mem_read_sr () & SR_PROTECT_BITS_MASK ;
327
+ _spi_mem_set_protection (0x0 );
328
+ result = spi_mem_write (address , input , size );
329
+ _spi_mem_set_protection (protection );
330
+
331
+ return result ;
332
+ }
333
+
261
334
// This is an utility function, useful to test the code, but not to be merged.
262
335
void spi_mem_test (void )
263
336
{
@@ -268,11 +341,148 @@ void spi_mem_test(void)
268
341
// --- Setup test buffers ---
269
342
uint8_t write_data [SPI_MEM_PAGE_SIZE ];
270
343
uint8_t read_data [SPI_MEM_PAGE_SIZE ];
344
+ uint32_t sector_addr = 0x00010000 ; // block 01
271
345
272
346
for (size_t i = 0 ; i < SPI_MEM_PAGE_SIZE ; i ++ ) {
273
347
write_data [i ] = (uint8_t )i ;
274
348
}
275
349
350
+ // === Test 0: Lock/Unlock Protection Behavior ===
351
+ util_log ("Test 0: Lock/Unlock Protection Verification" );
352
+
353
+ // Backup original SR and CR
354
+ uint8_t sr_before = _spi_mem_read_sr ();
355
+ uint8_t cr_before [2 ];
356
+ _spi_mem_read_cr (cr_before );
357
+
358
+ // unlock memory if it was already locked
359
+ if ((sr_before & SR_PROTECT_BITS_MASK ) != 0 ) {
360
+ _spi_mem_set_protection (0 );
361
+ sr_before = _spi_mem_read_sr ();
362
+ }
363
+
364
+ // 0.1 - Lock memory
365
+ spi_mem_protected_area_lock ();
366
+ uint8_t sr_locked = _spi_mem_read_sr ();
367
+ uint8_t cr_locked [2 ];
368
+ _spi_mem_read_cr (cr_locked );
369
+
370
+ // Verify that the protection bits were set
371
+ if ((sr_locked & SR_PROTECT_BITS_MASK ) != SR_PROTECT_BITS ) {
372
+ util_log ("Test 0.1: Lock bits not set correctly" );
373
+ success = false;
374
+ }
375
+
376
+ // Verify that the remaining part of the SR register didn't change
377
+ if ((sr_locked & ~SR_PROTECT_BITS_MASK ) != (sr_before & ~SR_PROTECT_BITS_MASK )) {
378
+ util_log ("Test 0.1: Status register corrupted" );
379
+ success = false;
380
+ }
381
+
382
+ // Verify that the TB bit was set
383
+ if ((cr_locked [0 ] & CR1_TB_BIT_MASK ) != CR1_TB_BIT_BOTTOM ) {
384
+ util_log ("Test 0.1: Lock bits not set correctly" );
385
+ success = false;
386
+ }
387
+
388
+ // Verify that the remaining part of the CR register didn't change
389
+ bool cr1_corrupted = (cr_before [0 ] & ~CR1_TB_BIT_MASK ) != (cr_locked [0 ] & ~CR1_TB_BIT_MASK );
390
+ if (cr1_corrupted || (cr_locked [1 ] != cr_before [1 ])) {
391
+ util_log ("Test 0.1: CR register changed unexpectedly during lock" );
392
+ success = false;
393
+ }
394
+
395
+ // 0.2 - Verify that writing locked memory doesn't change the data
396
+ uint8_t * read_page = spi_mem_read (sector_addr , SPI_MEM_PAGE_SIZE );
397
+ if (!read_page ) {
398
+ success = false;
399
+ }
400
+ if (!spi_mem_write (sector_addr , write_data , SPI_MEM_PAGE_SIZE )) {
401
+ success = false;
402
+ }
403
+ uint8_t * read_page2 = spi_mem_read (sector_addr , SPI_MEM_PAGE_SIZE );
404
+ if (!read_page2 ) {
405
+ success = false;
406
+ }
407
+ if (memcmp (read_page , read_page2 , SPI_MEM_PAGE_SIZE ) != 0 ) {
408
+ util_log ("Test 0.2: Write to protected area incorrectly allowed" );
409
+ success = false;
410
+ }
411
+ free (read_page2 );
412
+
413
+ // 0.3 - Verify that we can still write unprotected sectors
414
+ uint32_t unprotected_sector = 0x001D0000 ; // block 29
415
+ uint8_t * read_unprotected_page = spi_mem_read (unprotected_sector , SPI_MEM_PAGE_SIZE );
416
+ if (!read_page ) {
417
+ success = false;
418
+ }
419
+ if (!spi_mem_write (unprotected_sector , write_data , SPI_MEM_PAGE_SIZE )) {
420
+ success = false;
421
+ }
422
+ read_page2 = spi_mem_read (unprotected_sector , SPI_MEM_PAGE_SIZE );
423
+ if (!read_page2 ) {
424
+ success = false;
425
+ }
426
+ if (memcmp (read_unprotected_page , read_page2 , SPI_MEM_PAGE_SIZE ) == 0 ) {
427
+ util_log ("Test 0.3: Write to unprotected area failed" );
428
+ success = false;
429
+ }
430
+ free (read_page2 );
431
+ free (read_unprotected_page );
432
+
433
+ // 0.4 - Verify that writing locked memory with write_locked changes the data
434
+ if (!spi_mem_protected_area_write (sector_addr , write_data , SPI_MEM_PAGE_SIZE )) {
435
+ success = false;
436
+ }
437
+ read_page2 = spi_mem_read (sector_addr , SPI_MEM_PAGE_SIZE );
438
+ if (!read_page2 ) {
439
+ success = false;
440
+ }
441
+ if (memcmp (read_page , read_page2 , SPI_MEM_PAGE_SIZE ) == 0 ) {
442
+ util_log ("Test 0.4: Write to protected area failed" );
443
+ success = false;
444
+ }
445
+ free (read_page2 );
446
+
447
+ // 0.5 - Verify that protected area is still locked after write
448
+ sr_locked = _spi_mem_read_sr ();
449
+ if ((sr_locked & SR_PROTECT_BITS_MASK ) != SR_PROTECT_BITS ) {
450
+ util_log ("Test 0.5: Lock bits not set correctly" );
451
+ success = false;
452
+ }
453
+ // 0.6 - Unlock the memory and write back the initial data, with normal write
454
+ spi_mem_protected_area_unlock ();
455
+ if (!spi_mem_write (sector_addr , read_page , SPI_MEM_PAGE_SIZE )) {
456
+ success = false;
457
+ }
458
+ read_page2 = spi_mem_read (sector_addr , SPI_MEM_PAGE_SIZE );
459
+ if (!read_page2 ) {
460
+ success = false;
461
+ }
462
+ if (memcmp (read_page , read_page2 , SPI_MEM_PAGE_SIZE ) != 0 ) {
463
+ util_log ("Test 0.6: Write to unprotected area failed" );
464
+ success = false;
465
+ }
466
+ free (read_page2 );
467
+ free (read_page );
468
+
469
+ // 0.7 - Verify SR are restored or unchanged in unexpected ways
470
+ uint8_t sr_final = _spi_mem_read_sr ();
471
+ uint8_t cr_final [2 ];
472
+ _spi_mem_read_cr (cr_final );
473
+
474
+ if (sr_final != sr_before ) {
475
+ util_log ("Test 0.7: Status register corrupted during unlock" );
476
+ success = false;
477
+ }
478
+
479
+ // Verify that the remaining part of the CR register didn't change
480
+ cr1_corrupted = (cr_before [0 ] & ~CR1_TB_BIT_MASK ) != (cr_final [0 ] & ~CR1_TB_BIT_MASK );
481
+ if (cr1_corrupted || (cr_locked [1 ] != cr_before [1 ])) {
482
+ util_log ("Test 0.7: CR register changed unexpectedly during unlock" );
483
+ success = false;
484
+ }
485
+
276
486
// === Test 1: Valid page write/read at address 0 ===
277
487
uint32_t addr_start = 0x00000000 ;
278
488
if (!spi_mem_sector_erase (addr_start )) {
@@ -324,7 +534,6 @@ void spi_mem_test(void)
324
534
}
325
535
326
536
// === Test 3: Full sector write and read ===
327
- uint32_t sector_addr = 0x00020000 ;
328
537
uint8_t sector_write_data [SPI_MEM_SECTOR_SIZE ];
329
538
uint8_t * sector_read_data ;
330
539
@@ -628,6 +837,12 @@ void spi_mem_test(void)
628
837
util_log ("Test 10.6: Zero-size read correctly rejected" );
629
838
}
630
839
840
+ // Finally clean up the memory
841
+ if (!spi_mem_smart_erase ()) {
842
+ util_log ("Final smart erased failed" );
843
+ success = false;
844
+ }
845
+
631
846
util_log ("==== spi_mem_test %s ====" , success ? "PASSED ✅" : "FAILED ❌" );
632
847
screen_sprintf_debug (10000 , "Test result: %s" , success ? "OK" : "FAILED" );
633
848
}
0 commit comments