This repository was archived by the owner on Mar 7, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 845
Expand file tree
/
Copy pathstm32l0.c
More file actions
871 lines (764 loc) · 35.6 KB
/
stm32l0.c
File metadata and controls
871 lines (764 loc) · 35.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2014,2015 Marc Singer <elf@woollysoft.com>
* Copyright (C) 2022-2025 1BitSquared <info@1bitsquared.com>
* Written by Marc Singer <elf@woollysoft.com>
* Modified by Rachel Mant <git@dragonmux.network>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* This file implements STM32L0 and STM32L1 target specific functions for detecting
* the device, providing the XML memory map and Flash memory programming.
*
* References:
* RM0377 - Ultra-low-power STM32L0x1 advanced Arm®-based 32-bit MCUs, Rev. 10
* - https://www.st.com/resource/en/reference_manual/rm0377-ultralowpower-stm32l0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
* RM0038 - STM32L100xx, STM32L151xx, STM32L152xx and STM32L162xx advanced Arm®-based 32-bit MCUs, Rev. 17
* - https://www.st.com/resource/en/reference_manual/rm0038-stm32l100xx-stm32l151xx-stm32l152xx-and-stm32l162xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
*
* Note:
* This implementation has a few known defficiencies and quirks, these are:
* - Error handling -> We should probably clear Flash controller statusu register errors
* immediately after detecting them. If we don't then we must always wait for the controller
* to complete the previous operatioon before starting the next.
* - Minor inconsistencies between the STM32L0 and STM32L1 Flash controllers that should be handled
* - On the STM32L1, the Flash controller PECR can only be changed when the controller is
* idle, while on the STM32L0 it may be updated while an operation is in progress
*/
#include "general.h"
#include "target.h"
#include "target_internal.h"
#include "cortexm.h"
#include "stm32_common.h"
#define STM32Lx_FLASH_BANK_BASE 0x08000000U
#define STM32L0_FLASH_BANK_SIZE 0x00010000U
#define STM32L0_FLASH_PAGE_SIZE 0x00000080U
#define STM32L1_FLASH_PAGE_SIZE 0x00000100U
#define STM32Lx_EEPROM_BASE 0x08080000U
#define STM32Lx_SRAM_BASE 0x20000000U
#define STM32L0_SRAM_SIZE 0x00005000U
#define STM32L1_SRAM_SIZE 0x00014000U
#define STM32Lx_FLASH_PECR(flash_base) ((flash_base) + 0x04U)
#define STM32Lx_FLASH_PEKEYR(flash_base) ((flash_base) + 0x0cU)
#define STM32Lx_FLASH_PRGKEYR(flash_base) ((flash_base) + 0x10U)
#define STM32Lx_FLASH_OPTKEYR(flash_base) ((flash_base) + 0x14U)
#define STM32Lx_FLASH_SR(flash_base) ((flash_base) + 0x18U)
#define STM32Lx_FLASH_OPTR(flash_base) ((flash_base) + 0x1cU)
#define STM32L0_FLASH_BASE UINT32_C(0x40022000)
#define STM32L0_FLASH_OPT_SIZE 12U
#define STM32L0_FLASH_EEPROM_CAT1_SIZE 512U // 512B
#define STM32L0_FLASH_EEPROM_CAT2_SIZE 1024U // 1KiB
#define STM32L0_FLASH_EEPROM_CAT3_SIZE 2048U // 2KiB
#define STM32L0_FLASH_EEPROM_CAT5_SIZE 6144U // 6KiB
#define STM32L1_FLASH_BASE UINT32_C(0x40023c00)
#define STM32L1_FLASH_OPT_SIZE 32U
#define STM32L1_FLASH_EEPROM_SIZE 16384U // 16KiB
#define STM32Lx_FLASH_OPT_BASE UINT32_C(0x1ff80000)
#define STM32Lx_FLASH_EEPROM_BASE UINT32_C(0x08080000)
#define STM32Lx_FLASH_PEKEY1 UINT32_C(0x89abcdef)
#define STM32Lx_FLASH_PEKEY2 UINT32_C(0x02030405)
#define STM32Lx_FLASH_PRGKEY1 UINT32_C(0x8c9daebf)
#define STM32Lx_FLASH_PRGKEY2 UINT32_C(0x13141516)
#define STM32Lx_FLASH_OPTKEY1 UINT32_C(0xfbead9c8)
#define STM32Lx_FLASH_OPTKEY2 UINT32_C(0x24252627)
#define STM32Lx_FLASH_PECR_OBL_LAUNCH (1U << 18U)
#define STM32Lx_FLASH_PECR_ERRIE (1U << 17U)
#define STM32Lx_FLASH_PECR_EOPIE (1U << 16U)
#define STM32Lx_FLASH_PECR_FPRG (1U << 10U)
#define STM32Lx_FLASH_PECR_ERASE (1U << 9U)
#define STM32Lx_FLASH_PECR_FIX (1U << 8U) /* FTDW */
#define STM32Lx_FLASH_PECR_DATA (1U << 4U)
#define STM32Lx_FLASH_PECR_PROG (1U << 3U)
#define STM32Lx_FLASH_PECR_OPTLOCK (1U << 2U)
#define STM32Lx_FLASH_PECR_PRGLOCK (1U << 1U)
#define STM32Lx_FLASH_PECR_PELOCK (1U << 0U)
#define STM32Lx_FLASH_SR_NOTZEROERR (1U << 16U)
#define STM32Lx_FLASH_SR_SIZERR (1U << 10U)
#define STM32Lx_FLASH_SR_PGAERR (1U << 9U)
#define STM32Lx_FLASH_SR_WRPERR (1U << 8U)
#define STM32Lx_FLASH_SR_EOP (1U << 1U)
#define STM32Lx_FLASH_SR_BSY (1U << 0U)
#define STM32Lx_FLASH_SR_ERR_MASK \
(STM32Lx_FLASH_SR_WRPERR | STM32Lx_FLASH_SR_PGAERR | STM32Lx_FLASH_SR_SIZERR | STM32Lx_FLASH_SR_NOTZEROERR)
#define STM32L0_FLASH_OPTR_BOOT1 (1U << 31U)
#define STM32Lx_FLASH_OPTR_WDG_SW (1U << 20U)
#define STM32L0_FLASH_OPTR_WPRMOD (1U << 8U)
#define STM32Lx_FLASH_OPTR_RDPROT_SHIFT 0U
#define STM32Lx_FLASH_OPTR_RDPROT_MASK 0xffU
#define STM32Lx_FLASH_OPTR_RDPROT_0 0xaaU
#define STM32Lx_FLASH_OPTR_RDPROT_2 0xccU
#define STM32L1_FLASH_OPTR_nBFB2 (1U << 23U)
#define STM32L1_FLASH_OPTR_nRST_STDBY (1U << 22U)
#define STM32L1_FLASH_OPTR_nRST_STOP (1U << 21U)
#define STM32L1_FLASH_OPTR_BOR_LEV_SHIFT 16U
#define STM32L1_FLASH_OPTR_BOR_LEV_MASK 0xfU
#define STM32L1_FLASH_OPTR_SPRMOD (1U << 8U)
#define STM32L0_DBGMCU_BASE UINT32_C(0x40015800)
#define STM32L0_DBGMCU_IDCODE (STM32L0_DBGMCU_BASE + 0x000U)
#define STM32L0_DBGMCU_CONFIG (STM32L0_DBGMCU_BASE + 0x004U)
#define STM32L0_DBGMCU_APB1FREEZE (STM32L0_DBGMCU_BASE + 0x008U)
#define STM32L0_UID_BASE 0x1ff80050U
#define STM32L0_UID_FLASH_SIZE 0x1ff8007cU
/*
* NB: The L1 has two different UID and Flash size register base addresses!
* The L1xxxB ones are for Category 1 & 2 devices only. The L1xxxx ones
* are for Category 3, 4, 5 and 6 as the devices have two different memory maps
* that depend on the category code.
*/
#define STM32L1_DBGMCU_BASE UINT32_C(0xe0042000)
#define STM32L1_DBGMCU_IDCODE (STM32L1_DBGMCU_BASE + 0x000U)
#define STM32L1_DBGMCU_CONFIG (STM32L1_DBGMCU_BASE + 0x004U)
#define STM32L1_DBGMCU_APB1FREEZE (STM32L1_DBGMCU_BASE + 0x008U)
#define STM32L1xxxB_UID_BASE 0x1ff80050U
#define STM32L1xxxB_UID_FLASH_SIZE 0x1ff8004cU
#define STM32L1xxxx_UID_BASE 0x1ff800d0U
#define STM32L1xxxx_UID_FLASH_SIZE 0x1ff800ccU
#define STM32Lx_DBGMCU_CONFIG_DBG_SLEEP (1U << 0U)
#define STM32Lx_DBGMCU_CONFIG_DBG_STOP (1U << 1U)
#define STM32Lx_DBGMCU_CONFIG_DBG_STANDBY (1U << 2U)
#define STM32Lx_DBGMCU_APB1FREEZE_WWDG (1U << 11U)
#define STM32Lx_DBGMCU_APB1FREEZE_IWDG (1U << 12U)
/* Taken from DBGMCU_IDCODE in §27.4.1 in RM0377 rev 10, pg820 */
#define ID_STM32L01x 0x457U /* Category 1 */
#define ID_STM32L03x 0x425U /* Category 2 */
#define ID_STM32L05x 0x417U /* Category 3 */
#define ID_STM32L07x 0x447U /* Category 5 */
/* Taken from DBGMCU_IDCODE in §30.6.1 in RM0038 rev 17, pg861 */
#define ID_STM32L1xxxB 0x416U /* Category 1 */
#define ID_STM32L1xxxBxA 0x429U /* Category 2 */
#define ID_STM32L1xxxC 0x427U /* Category 3 */
#define ID_STM32L1xxxD 0x436U /* Category 3/4 */
#define ID_STM32L1xxxE 0x437U /* Category 5/6 */
static bool stm32lx_cmd_option(target_s *target, int argc, const char **argv);
static bool stm32lx_cmd_eeprom(target_s *target, int argc, const char **argv);
static const command_s stm32lx_cmd_list[] = {
{"option", stm32lx_cmd_option, "Manipulate option bytes"},
{"eeprom", stm32lx_cmd_eeprom, "Manipulate EEPROM (FLASH data) memory"},
{NULL, NULL, NULL},
};
static bool stm32l0_attach(target_s *target);
static bool stm32l1_attach(target_s *target);
static void stm32l0_detach(target_s *target);
static void stm32l1_detach(target_s *target);
static bool stm32lx_flash_erase(target_flash_s *flash, target_addr_t addr, size_t length);
static bool stm32lx_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length);
static bool stm32lx_eeprom_erase(target_flash_s *flash, target_addr_t addr, size_t length);
static bool stm32lx_eeprom_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t length);
typedef struct stm32l_priv {
target_addr32_t uid_taddr;
char stm32l_variant[21];
} stm32l_priv_s;
static bool stm32lx_is_stm32l1(const target_s *const target)
{
return target->part_id != ID_STM32L01x && target->part_id != ID_STM32L03x && target->part_id != ID_STM32L05x &&
target->part_id != ID_STM32L07x;
}
static uint32_t stm32lx_nvm_eeprom_size(const target_s *const target)
{
switch (target->part_id) {
case 0x457U: /* STM32L0xx Cat1 */
return STM32L0_FLASH_EEPROM_CAT1_SIZE;
case 0x425U: /* STM32L0xx Cat2 */
return STM32L0_FLASH_EEPROM_CAT2_SIZE;
case 0x417U: /* STM32L0xx Cat3 */
return STM32L0_FLASH_EEPROM_CAT3_SIZE;
case 0x447U: /* STM32L0xx Cat5 */
return STM32L0_FLASH_EEPROM_CAT5_SIZE;
default: /* STM32L1xx */
return STM32L1_FLASH_EEPROM_SIZE;
}
}
static target_addr32_t stm32lx_flash_base(const target_s *const target)
{
if (stm32lx_is_stm32l1(target))
return STM32L1_FLASH_BASE;
return STM32L0_FLASH_BASE;
}
static uint32_t stm32lx_nvm_option_size(const target_s *const target)
{
if (stm32lx_is_stm32l1(target))
return STM32L1_FLASH_OPT_SIZE;
return STM32L0_FLASH_OPT_SIZE;
}
static void stm32l_add_flash(target_s *const target, const uint32_t addr, const size_t length, const size_t erasesize)
{
target_flash_s *flash = calloc(1, sizeof(*flash));
if (!flash) { /* calloc failed: heap exhaustion */
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return;
}
flash->start = addr;
flash->length = length;
flash->blocksize = erasesize;
flash->erase = stm32lx_flash_erase;
flash->write = stm32lx_flash_write;
flash->writesize = erasesize >> 1U;
target_add_flash(target, flash);
}
static void stm32l_add_eeprom(target_s *const target, const uint32_t addr, const size_t length)
{
target_flash_s *flash = calloc(1, sizeof(*flash));
if (!flash) { /* calloc failed: heap exhaustion */
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return;
}
flash->start = addr;
flash->length = length;
flash->blocksize = 4;
flash->erase = stm32lx_eeprom_erase;
flash->write = stm32lx_eeprom_write;
target_add_flash(target, flash);
}
static void stm32l0_configure_dbgmcu(target_s *const target)
{
/* Enable debugging during all low power modes */
target_mem32_write32(target, STM32L0_DBGMCU_CONFIG,
STM32Lx_DBGMCU_CONFIG_DBG_SLEEP | STM32Lx_DBGMCU_CONFIG_DBG_STANDBY | STM32Lx_DBGMCU_CONFIG_DBG_STOP);
/* And make sure the WDTs stay synchronised to the run state of the processor */
target_mem32_write32(
target, STM32L0_DBGMCU_APB1FREEZE, STM32Lx_DBGMCU_APB1FREEZE_WWDG | STM32Lx_DBGMCU_APB1FREEZE_IWDG);
}
bool stm32l0_probe(target_s *const target)
{
/* Try to identify the part, make sure it's a STM32L0 */
if (target->part_id != ID_STM32L01x && target->part_id != ID_STM32L03x && target->part_id != ID_STM32L05x &&
target->part_id != ID_STM32L07x)
return false;
/* Now we have a stable debug environment, make sure the WDTs + WFI and WFE instructions can't cause problems */
stm32l0_configure_dbgmcu(target);
target->driver = "STM32L0";
target->attach = stm32l0_attach;
target->detach = stm32l0_detach;
target_add_commands(target, stm32lx_cmd_list, target->driver);
/* Having identified that it's a STM32L0 of some sort, read out how much Flash it has */
const uint32_t flash_size = target_mem32_read16(target, STM32L0_UID_FLASH_SIZE) * 1024U;
/* There's no good way to tell how much RAM a part has, so use a one-size map */
target_add_ram32(target, STM32Lx_SRAM_BASE, STM32L0_SRAM_SIZE);
/* Now fill in the Flash map based on the part category */
switch (target->part_id) {
case ID_STM32L01x:
case ID_STM32L03x:
case ID_STM32L05x:
/* Category 1, 2 and 3 only have one bank */
stm32l_add_flash(target, STM32Lx_FLASH_BANK_BASE, flash_size, STM32L0_FLASH_PAGE_SIZE);
break;
case ID_STM32L07x: {
/* Category 5 parts have 2 banks, split 50:50 on the total size of the Flash */
const size_t bank_size = flash_size >> 1U;
const target_addr32_t bank2_base = STM32Lx_FLASH_BANK_BASE + bank_size;
stm32l_add_flash(target, STM32Lx_FLASH_BANK_BASE, bank_size, STM32L0_FLASH_PAGE_SIZE);
stm32l_add_flash(target, bank2_base, bank_size, STM32L0_FLASH_PAGE_SIZE);
break;
default:
break;
}
}
/* There's also no good way to know how much EEPROM the part has, so define a one-size map for that too */
stm32l_add_eeprom(target, STM32Lx_EEPROM_BASE, 0x1800);
return true;
}
static bool stm32l1_configure_dbgmcu(target_s *const target)
{
/* If we're in the probe phase */
if (target->target_storage == NULL) {
/* Allocate and save private storage */
stm32l_priv_s *const priv_storage = calloc(1, sizeof(*priv_storage));
if (!priv_storage) { /* calloc failed: heap exhaustion */
DEBUG_ERROR("calloc: failed in %s\n", __func__);
return false;
}
target->target_storage = priv_storage;
target->attach = stm32l1_attach;
target->detach = stm32l1_detach;
}
/* Now we have a stable debug environment, make sure the WDTs can't bonk the processor out from under us */
target_mem32_write32(target, STM32L1_DBGMCU_APB1FREEZE,
target_mem32_read32(target, STM32L1_DBGMCU_APB1FREEZE) | STM32Lx_DBGMCU_APB1FREEZE_WWDG |
STM32Lx_DBGMCU_APB1FREEZE_IWDG);
/* Then Reconfigure the config register to prevent WFI/WFE from cutting debug access */
target_mem32_write32(target, STM32L1_DBGMCU_CONFIG,
target_mem32_read32(target, STM32L1_DBGMCU_CONFIG) | STM32Lx_DBGMCU_CONFIG_DBG_SLEEP |
STM32Lx_DBGMCU_CONFIG_DBG_STANDBY | STM32Lx_DBGMCU_CONFIG_DBG_STOP);
return true;
}
bool stm32l1_probe(target_s *const target)
{
/* Try to identify the part, make sure it's a STM32L1 */
const adiv5_access_port_s *const ap = cortex_ap(target);
/* Use the partno from the AP always to handle the difference between JTAG and SWD */
if (ap->partno != ID_STM32L1xxxB && ap->partno != ID_STM32L1xxxBxA && ap->partno != ID_STM32L1xxxC &&
ap->partno != ID_STM32L1xxxD && ap->partno != ID_STM32L1xxxE)
return false;
target->part_id = target_mem32_read32(target, STM32L1_DBGMCU_IDCODE) & 0xfffU;
/* Now we have a stable debug environment, make sure the WDTs + WFI and WFE instructions can't cause problems */
stm32l1_configure_dbgmcu(target);
target->driver = "STM32L1";
target_add_commands(target, stm32lx_cmd_list, target->driver);
/* There's no good way to tell how much RAM a part has, so use a one-size map */
target_add_ram32(target, STM32Lx_SRAM_BASE, STM32L1_SRAM_SIZE);
/* Having identified that it's a STM32L1 of some sort, dispatch Flash map setup based on the category */
target_addr32_t flash_size_taddr = 0U;
stm32l_priv_s *const priv = (stm32l_priv_s *)target->target_storage;
switch (target->part_id) {
case ID_STM32L1xxxB:
case ID_STM32L1xxxBxA:
flash_size_taddr = STM32L1xxxB_UID_FLASH_SIZE;
priv->uid_taddr = STM32L1xxxB_UID_BASE;
break;
case ID_STM32L1xxxC:
case ID_STM32L1xxxD:
case ID_STM32L1xxxE:
flash_size_taddr = STM32L1xxxx_UID_FLASH_SIZE;
priv->uid_taddr = STM32L1xxxx_UID_BASE;
break;
default:
break;
}
/* Read out the appropriate Flash size register value */
uint32_t flash_size = target_mem32_read16(target, flash_size_taddr);
/* Having read out the Flash size register, deal with two special cases before converting to an actual Flash size */
if (target->part_id == ID_STM32L1xxxBxA)
/* Only the lowest byte is valid on category 2 parts */
flash_size &= 0xffU;
else if (target->part_id == ID_STM32L1xxxD)
/* Cat 3/4 parts have values of 0 or 1, convert to actual Flash sizes for these parts (384KiB or 256KiB) */
flash_size = flash_size == 0U ? 384U : 256U;
/* Finally, now all that's done.. convert the Flash size value to bytes */
flash_size *= 1024U;
/* Dispatch again on the category to complete Flash map setup */
switch (target->part_id) {
case ID_STM32L1xxxB:
case ID_STM32L1xxxBxA:
case ID_STM32L1xxxC:
case ID_STM32L1xxxD: {
/*
* Category 1, 2, and 3 only have one bank. This bank is split into up-to 64 4KiB sectors of 256 byte pages.
* Sectors are the write protection primitive, pages are the erase size primitive. The manual displays these
* as split with 1KiB of 256 byte pages, 3KiB of 1KiB pages, up to 124KiB of 4KiB pages, and then finally
* the rest of the Flash as 64KiB pages. However this is inaccurate.
* Category 4 has 2 banks but the first bank is laid out exactly the same as the first 3 categories.
* Category 4's second bank starts at the 192KiB mark and looks like it extends with a 128KiB page and a
* 64KiB page for another 192KiB for 384KiB of Flash. This bank, however, works the same as the first.
* This is documented in §3.2, tables 8, 9, and 10 on pg53 of RM0038, rev 17
*/
const bool category4 = flash_size == 0x00060000U;
/*
* Determine bank 1's size. Category 4 parts have their 384KiB of Flash split evenly between the two
* banks, while the others all have their entire Flash on the first bank only.
*/
const uint32_t bank_size = category4 ? flash_size >> 1U : flash_size;
stm32l_add_flash(target, STM32Lx_FLASH_BANK_BASE, bank_size, STM32L1_FLASH_PAGE_SIZE);
/* Now deal with the second bank on Category 4 parts */
if (category4)
stm32l_add_flash(target, STM32Lx_FLASH_BANK_BASE + 0x00030000U, bank_size, STM32L1_FLASH_PAGE_SIZE);
break;
}
case ID_STM32L1xxxE: {
/*
* Category 5 has 2 banks, documented in §3.2, table 11 on pg56 of RM0038, rev 17.
* These banks are split up into sectors and pages the same as any other for the L1 series.
* The manual displays this as first bank being split into 1KiB of 256 byte pages, 3KiB of 1KiB pages,
* 124KiB of 4KiB pages, and a 128KiB page for 256KiB. It then shows the second bank is split into two
* 128KiB pages for a second 256KiB. However this is inaccurate.
* This gives a total of 512KiB of Flash, which is the only way to tell these parts apart from category 6.
*
* Category 6 has 2 banks as well, documented in §3.2, table 12 on pg58 of RM0038, rev 17.
* The manual displays this as the first bank starting the same as a Category 5 device, right up until 128KiB
* in, after which it shows the bank being concluded by a single 64KiB page for 192KiB. Bank 2 is then shown as
* 1 128KiB page and one 64KiB page for another 192KiB, giving a total of 384KiB of Flash same as Category 4
* parts. While the total amount is accurate, this is an inaccurate representation. These too use
* the same sectors and pages arrangements as the other L1 parts, however the bank split location for
* the Category 5 and 6 parts is the same 256KiB mark, causing the Category 6 parts to have a small hole
* between the two banks, unlike Category 4 where the banks are contiguous.
*/
const uint32_t bank_size = flash_size >> 1U;
stm32l_add_flash(target, STM32Lx_FLASH_BANK_BASE, bank_size, STM32L1_FLASH_PAGE_SIZE);
stm32l_add_flash(target, STM32Lx_FLASH_BANK_BASE + 0x00040000U, bank_size, STM32L1_FLASH_PAGE_SIZE);
break;
}
default:
break;
}
return true;
}
static bool stm32l0_attach(target_s *const target)
{
/*
* Try to attach to the part, and then ensure that the WDTs + WFI and WFE
* instructions can't cause problems (this is duplicated as it's undone by detach.)
*/
if (!cortexm_attach(target))
return false;
stm32l0_configure_dbgmcu(target);
return true;
}
static void stm32l0_detach(target_s *const target)
{
/* Reverse all changes to STM32L0_DBGMCU_CONFIG */
target_mem32_write32(target, STM32L0_DBGMCU_CONFIG, 0U);
/* Now defer to the normal Cortex-M detach routine to complete the detach */
cortexm_detach(target);
}
static bool stm32l1_attach(target_s *const target)
{
/*
* Try to attach to the part, and then ensure that the WDTs + WFI and WFE
* instructions can't cause problems (this is duplicated as it's undone by detach.)
*/
return cortexm_attach(target) && stm32l1_configure_dbgmcu(target);
}
static void stm32l1_detach(target_s *const target)
{
/* Reverse all changes to the DBGMCU control and freeze registers */
target_mem32_write32(target, STM32L1_DBGMCU_APB1FREEZE,
target_mem32_read32(target, STM32L1_DBGMCU_APB1FREEZE) &
~(STM32Lx_DBGMCU_APB1FREEZE_WWDG | STM32Lx_DBGMCU_APB1FREEZE_IWDG));
target_mem32_write32(target, STM32L1_DBGMCU_CONFIG,
target_mem32_read32(target, STM32L1_DBGMCU_CONFIG) &
~(STM32Lx_DBGMCU_CONFIG_DBG_SLEEP | STM32Lx_DBGMCU_CONFIG_DBG_STANDBY | STM32Lx_DBGMCU_CONFIG_DBG_STOP));
/* Now defer to the normal Cortex-M detach routine to complete the detach */
cortexm_detach(target);
}
/* Lock the FLASH control registers preventing writes or erases. */
static void stm32lx_nvm_lock(target_s *const target, const target_addr32_t flash_base)
{
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), STM32Lx_FLASH_PECR_PELOCK);
}
/*
* Unlock the FLASH control registers for modifying program or data flash.
* Returns true if the unlock succeeds.
*/
static bool stm32lx_nvm_prog_data_unlock(target_s *const target, const target_addr32_t flash_base)
{
/* Always lock first because that's the only way to know that the unlock can succeed on the STM32L0's. */
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), STM32Lx_FLASH_PECR_PELOCK);
target_mem32_write32(target, STM32Lx_FLASH_PEKEYR(flash_base), STM32Lx_FLASH_PEKEY1);
target_mem32_write32(target, STM32Lx_FLASH_PEKEYR(flash_base), STM32Lx_FLASH_PEKEY2);
target_mem32_write32(target, STM32Lx_FLASH_PRGKEYR(flash_base), STM32Lx_FLASH_PRGKEY1);
target_mem32_write32(target, STM32Lx_FLASH_PRGKEYR(flash_base), STM32Lx_FLASH_PRGKEY2);
return !(target_mem32_read32(target, STM32Lx_FLASH_PECR(flash_base)) & STM32Lx_FLASH_PECR_PRGLOCK);
}
/*
* Unlock the FLASH control registers for modifying option bytes.
* Returns true if the unlock succeeds.
*/
static bool stm32lx_nvm_opt_unlock(target_s *const target, const target_addr32_t flash_base)
{
/* Always lock first because that's the only way to know that the unlock can succeed on the STM32L0's. */
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), STM32Lx_FLASH_PECR_PELOCK);
target_mem32_write32(target, STM32Lx_FLASH_PEKEYR(flash_base), STM32Lx_FLASH_PEKEY1);
target_mem32_write32(target, STM32Lx_FLASH_PEKEYR(flash_base), STM32Lx_FLASH_PEKEY2);
target_mem32_write32(target, STM32Lx_FLASH_OPTKEYR(flash_base), STM32Lx_FLASH_OPTKEY1);
target_mem32_write32(target, STM32Lx_FLASH_OPTKEYR(flash_base), STM32Lx_FLASH_OPTKEY2);
return !(target_mem32_read32(target, STM32Lx_FLASH_PECR(flash_base)) & STM32Lx_FLASH_PECR_OPTLOCK);
}
static bool stm32lx_nvm_busy_wait(
target_s *const target, const target_addr32_t flash_base, platform_timeout_s *const timeout)
{
while (target_mem32_read32(target, STM32Lx_FLASH_SR(flash_base)) & STM32Lx_FLASH_SR_BSY) {
if (target_check_error(target))
return false;
if (timeout)
target_print_progress(timeout);
}
const uint32_t status = target_mem32_read32(target, STM32Lx_FLASH_SR(flash_base));
return !target_check_error(target) && !(status & STM32Lx_FLASH_SR_ERR_MASK);
}
/*
* Erase a region of program flash using operations through the debug interface.
* This is slower than stubbed versions (see NOTES).
* The flash array is erased for all pages from addr to addr + length inclusive.
* The FLASH register base is automatically determined based on the target.
*/
static bool stm32lx_flash_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length)
{
target_s *const target = flash->t;
const target_addr32_t flash_base = stm32lx_flash_base(target);
const bool full_erase = addr == flash->start && length == flash->length;
if (!stm32lx_nvm_prog_data_unlock(target, flash_base))
return false;
/* Flash page erase instruction */
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), STM32Lx_FLASH_PECR_ERASE | STM32Lx_FLASH_PECR_PROG);
const uint32_t pecr = target_mem32_read32(target, STM32Lx_FLASH_PECR(flash_base)) &
(STM32Lx_FLASH_PECR_PROG | STM32Lx_FLASH_PECR_ERASE);
if (pecr != (STM32Lx_FLASH_PECR_PROG | STM32Lx_FLASH_PECR_ERASE))
return false;
/*
* Clear errors.
* Note that this only works when we wait for the FLASH block to complete the last operation.
*/
target_mem32_write32(target, STM32Lx_FLASH_SR(flash_base), STM32Lx_FLASH_SR_ERR_MASK);
platform_timeout_s timeout;
platform_timeout_set(&timeout, 500);
for (size_t offset = 0; offset < length; offset += flash->blocksize) {
/* Trigger the erase by writing the first uint32_t of the page to 0 */
target_mem32_write32(target, addr + offset, 0U);
if (full_erase)
target_print_progress(&timeout);
}
/* Disable further programming by locking PECR */
stm32lx_nvm_lock(target, flash_base);
/* Wait for completion or an error */
return stm32lx_nvm_busy_wait(target, flash_base, full_erase ? &timeout : NULL);
}
/* Write to program flash using operations through the debug interface. */
static bool stm32lx_flash_write(
target_flash_s *const flash, const target_addr_t dest, const void *const src, const size_t length)
{
target_s *const target = flash->t;
const target_addr32_t flash_base = stm32lx_flash_base(target);
if (!stm32lx_nvm_prog_data_unlock(target, flash_base))
return false;
/* Wait for BSY to clear because we cannot write the PECR until the previous operation completes */
if (!stm32lx_nvm_busy_wait(target, flash_base, NULL))
return false;
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), STM32Lx_FLASH_PECR_PROG | STM32Lx_FLASH_PECR_FPRG);
target_mem32_write(target, dest, src, length);
/* Disable further programming by locking PECR */
stm32lx_nvm_lock(target, flash_base);
/* Wait for completion or an error */
return stm32lx_nvm_busy_wait(target, flash_base, NULL);
}
/*
* Erase a region of data flash using operations through the debug interface.
* The flash is erased for all pages from addr to addr + length, inclusive, on a word boundary.
* The FLASH register base is automatically determined based on the target.
*/
static bool stm32lx_eeprom_erase(target_flash_s *const flash, const target_addr_t addr, const size_t length)
{
target_s *const target = flash->t;
const target_addr32_t flash_base = stm32lx_flash_base(target);
if (!stm32lx_nvm_prog_data_unlock(target, flash_base))
return false;
/* Flash data erase instruction */
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), STM32Lx_FLASH_PECR_ERASE | STM32Lx_FLASH_PECR_DATA);
const uint32_t pecr = target_mem32_read32(target, STM32Lx_FLASH_PECR(flash_base)) &
(STM32Lx_FLASH_PECR_ERASE | STM32Lx_FLASH_PECR_DATA);
if (pecr != (STM32Lx_FLASH_PECR_ERASE | STM32Lx_FLASH_PECR_DATA))
return false;
const uint32_t aligned_addr = addr & ~3U;
for (size_t offset = 0; offset < length; offset += flash->blocksize)
/* Trigger the erase by writing the first uint32_t of the page to 0 */
target_mem32_write32(target, aligned_addr + offset, 0U);
/* Disable further programming by locking PECR */
stm32lx_nvm_lock(target, flash_base);
/* Wait for completion or an error */
return stm32lx_nvm_busy_wait(target, flash_base, NULL);
}
/*
* Write to data flash using operations through the debug interface.
* The FLASH register base is automatically determined based on the target.
* Unaligned destination writes are supported (though unaligned sources are not).
*/
static bool stm32lx_eeprom_write(
target_flash_s *const flash, const target_addr_t dest, const void *const src, const size_t length)
{
target_s *const target = flash->t;
const target_addr32_t flash_base = stm32lx_flash_base(target);
const bool is_stm32l1 = stm32lx_is_stm32l1(target);
if (!stm32lx_nvm_prog_data_unlock(target, flash_base))
return false;
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), is_stm32l1 ? 0 : STM32Lx_FLASH_PECR_DATA);
/* Sling data to the target one uint32_t at a time */
const uint32_t *const data = (const uint32_t *)src;
for (size_t offset = 0; offset < length; offset += 4U) {
/* XXX: Why is this not able to use target_mem_write()? */
if (target_mem32_write32(target, dest + offset, data[offset]))
return false;
}
/* Disable further programming by locking PECR */
stm32lx_nvm_lock(target, flash_base);
/* Wait for completion or an error */
return stm32lx_nvm_busy_wait(target, flash_base, NULL);
}
/*
* Write one option word.
* The address is the physical address of the word and the value is a complete word value.
* The caller is responsible for making sure that the value satisfies the proper
* format where the upper 16 bits are the 1s complement of the lower 16 bits.
* The function returns when the operation is complete.
* The return value is true if the write succeeded.
*/
static bool stm32lx_option_write(target_s *const target, const uint32_t address, const uint32_t value)
{
const target_addr32_t flash_base = stm32lx_flash_base(target);
/* Erase and program option in one go. */
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), STM32Lx_FLASH_PECR_FIX);
target_mem32_write32(target, address, value);
/* Wait for completion or an error */
return stm32lx_nvm_busy_wait(target, flash_base, NULL);
}
/*
* Write one eeprom value.
* This version is more flexible than that bulk version used for writing data from the executable file.
* The address is the physical address of the word and the value is a complete word value.
* The function returns when the operation is complete.
* The return value is true if the write succeeded.
* FWIW, byte writing isn't supported because the ADIv5 layer doesn't support byte-level operations.
*/
static bool stm32lx_eeprom_write_one(
target_s *const target, const uint32_t address, const size_t block_size, const uint32_t value)
{
const target_addr32_t flash_base = stm32lx_flash_base(target);
const bool is_stm32l1 = stm32lx_is_stm32l1(target);
/* Clear errors. */
target_mem32_write32(target, STM32Lx_FLASH_SR(flash_base), STM32Lx_FLASH_SR_ERR_MASK);
/* Erase and program option in one go. */
target_mem32_write32(
target, STM32Lx_FLASH_PECR(flash_base), (is_stm32l1 ? 0 : STM32Lx_FLASH_PECR_DATA) | STM32Lx_FLASH_PECR_FIX);
if (block_size == 4)
target_mem32_write32(target, address, value);
else if (block_size == 2)
target_mem32_write16(target, address, value);
else if (block_size == 1)
target_mem32_write8(target, address, value);
else
return false;
/* Wait for completion or an error */
return stm32lx_nvm_busy_wait(target, flash_base, NULL);
}
static size_t stm32lx_prot_level(const uint32_t options)
{
const uint32_t read_protection = (options >> STM32Lx_FLASH_OPTR_RDPROT_SHIFT) & STM32Lx_FLASH_OPTR_RDPROT_MASK;
if (read_protection == STM32Lx_FLASH_OPTR_RDPROT_0)
return 0;
if (read_protection == STM32Lx_FLASH_OPTR_RDPROT_2)
return 2;
return 1;
}
static bool stm32lx_cmd_option(target_s *const target, const int argc, const char **const argv)
{
const target_addr32_t flash_base = stm32lx_flash_base(target);
const size_t opt_size = stm32lx_nvm_option_size(target);
if (!stm32lx_nvm_opt_unlock(target, flash_base)) {
tc_printf(target, "unable to unlock FLASH option bytes\n");
return true;
}
if (argc < 2)
goto usage;
const size_t command_len = strlen(argv[1]);
if (argc == 2 && strncasecmp(argv[1], "obl_launch", command_len) == 0)
target_mem32_write32(target, STM32Lx_FLASH_PECR(flash_base), STM32Lx_FLASH_PECR_OBL_LAUNCH);
else if (argc == 4) {
const bool raw_write = strncasecmp(argv[1], "raw", command_len) == 0;
if (!raw_write && strncasecmp(argv[1], "write", command_len) != 0)
goto usage;
const uint32_t addr = strtoul(argv[2], NULL, 0);
uint32_t val = strtoul(argv[3], NULL, 0);
if (!raw_write)
val = (val & 0xffffU) | ((~val & 0xffffU) << 16U);
tc_printf(target, "%s %08" PRIx32 " <- %08" PRIx32 "\n", argv[1], addr, val);
if (addr >= STM32Lx_FLASH_OPT_BASE && addr < STM32Lx_FLASH_OPT_BASE + opt_size && (addr & 3U) == 0) {
if (!stm32lx_option_write(target, addr, val))
tc_printf(target, "option write failed\n");
} else
goto usage;
}
/* Report the current option values */
for (size_t i = 0; i < opt_size; i += 4U) {
const uint32_t addr = STM32Lx_FLASH_OPT_BASE + i;
const uint32_t val = target_mem32_read32(target, addr);
tc_printf(target, "0x%08" PRIx32 ": 0x%04" PRIu32 " 0x%04" PRIu32 " %s\n", addr, val & 0xffffU,
(val >> 16U) & 0xffffU, (val & 0xffffU) == ((~val >> 16U) & 0xffffU) ? "OK" : "ERR");
}
const uint32_t options = target_mem32_read32(target, STM32Lx_FLASH_OPTR(flash_base));
const size_t read_protection = stm32lx_prot_level(options);
if (stm32lx_is_stm32l1(target)) {
tc_printf(target,
"OPTR: 0x%08" PRIx32 ", RDPRT %" PRIu32 ", SPRMD %u, BOR %" PRIu32 ", WDG_SW %u"
", nRST_STP %u, nRST_STBY %u, nBFB2 %u\n",
options, (uint32_t)read_protection, (options & STM32L1_FLASH_OPTR_SPRMOD) ? 1U : 0U,
(options >> STM32L1_FLASH_OPTR_BOR_LEV_SHIFT) & STM32L1_FLASH_OPTR_BOR_LEV_MASK,
(options & STM32Lx_FLASH_OPTR_WDG_SW) ? 1U : 0U, (options & STM32L1_FLASH_OPTR_nRST_STOP) ? 1U : 0U,
(options & STM32L1_FLASH_OPTR_nRST_STDBY) ? 1U : 0U, (options & STM32L1_FLASH_OPTR_nBFB2) ? 1U : 0U);
} else {
tc_printf(target, "OPTR: 0x%08" PRIx32 ", RDPROT %" PRIu32 ", WPRMOD %u, WDG_SW %u, BOOT1 %u\n", options,
(uint32_t)read_protection, (options & STM32L0_FLASH_OPTR_WPRMOD) ? 1U : 0U,
(options & STM32Lx_FLASH_OPTR_WDG_SW) ? 1U : 0U, (options & STM32L0_FLASH_OPTR_BOOT1) ? 1U : 0U);
}
goto done;
usage:
tc_printf(target, "usage: monitor option [ARGS]\n");
tc_printf(target, " show - Show options in FLASH and as loaded\n");
tc_printf(target, " obl_launch - Reload options from FLASH\n");
tc_printf(target, " write <addr> <value16> - Set option half-word; complement computed\n");
tc_printf(target, " raw <addr> <value32> - Set option word\n");
tc_printf(target, "The value of <addr> must be 32-bit aligned and from 0x%08" PRIx32 " to +0x%" PRIx32 "\n",
STM32Lx_FLASH_OPT_BASE, STM32Lx_FLASH_OPT_BASE + (uint32_t)(opt_size - 4U));
done:
stm32lx_nvm_lock(target, flash_base);
return true;
}
static const char *stm32lx_block_size_str(const size_t block_size)
{
if (block_size == 4U)
return "word";
if (block_size == 2U)
return "halfword";
if (block_size == 1U)
return "byte";
return "";
}
static bool stm32lx_cmd_eeprom(target_s *const target, const int argc, const char **const argv)
{
const target_addr32_t flash_base = stm32lx_flash_base(target);
if (!stm32lx_nvm_prog_data_unlock(target, flash_base)) {
tc_printf(target, "unable to unlock EEPROM\n");
return true;
}
if (argc == 4) {
uint32_t addr = strtoul(argv[2], NULL, 0);
uint32_t val = strtoul(argv[3], NULL, 0);
if (addr < STM32Lx_FLASH_EEPROM_BASE || addr >= STM32Lx_FLASH_EEPROM_BASE + stm32lx_nvm_eeprom_size(target))
goto usage;
const size_t command_len = strlen(argv[1]);
size_t block_size = 0U;
if (!strncasecmp(argv[1], "byte", command_len)) {
val &= 0xffU;
block_size = 1U;
} else if (!strncasecmp(argv[1], "halfword", command_len)) {
val &= 0xffffU;
block_size = 2U;
if (addr & 1U) {
tc_printf(target, "Refusing to do unaligned write\n");
goto usage;
}
} else if (!strncasecmp(argv[1], "word", command_len)) {
block_size = 4U;
if (addr & 3U) {
tc_printf(target, "Refusing to do unaligned write\n");
goto usage;
}
} else
goto usage;
tc_printf(
target, "writing %s 0x%08" PRIx32 " with 0x%" PRIx32 "\n", stm32lx_block_size_str(block_size), addr, val);
if (!stm32lx_eeprom_write_one(target, addr, block_size, val))
tc_printf(target, "eeprom write failed\n");
} else
goto usage;
goto done;
usage:
tc_printf(target, "usage: monitor eeprom [ARGS]\n");
tc_printf(target, " byte <addr> <value8> - Write a byte\n");
tc_printf(target, " halfword <addr> <value16> - Write a half-word\n");
tc_printf(target, " word <addr> <value32> - Write a word\n");
tc_printf(target, "The value of <addr> must in the interval [0x%08" PRIx32 ", 0x%" PRIx32 ")\n",
STM32Lx_FLASH_EEPROM_BASE, STM32Lx_FLASH_EEPROM_BASE + stm32lx_nvm_eeprom_size(target));
done:
stm32lx_nvm_lock(target, flash_base);
return true;
}