forked from cesanta/mongoose
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmongoose.c
16014 lines (14350 loc) · 540 KB
/
mongoose.c
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
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2024 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see http://www.gnu.org/licenses/
//
// You are free to use this software under the terms of the GNU General
// Public License, 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.
//
// Alternatively, you can license this software under a commercial
// license, as set out in https://www.mongoose.ws/licensing/
//
// SPDX-License-Identifier: GPL-2.0-only or commercial
#include "mongoose.h"
#ifdef MG_ENABLE_LINES
#line 1 "src/base64.c"
#endif
static int mg_base64_encode_single(int c) {
if (c < 26) {
return c + 'A';
} else if (c < 52) {
return c - 26 + 'a';
} else if (c < 62) {
return c - 52 + '0';
} else {
return c == 62 ? '+' : '/';
}
}
static int mg_base64_decode_single(int c) {
if (c >= 'A' && c <= 'Z') {
return c - 'A';
} else if (c >= 'a' && c <= 'z') {
return c + 26 - 'a';
} else if (c >= '0' && c <= '9') {
return c + 52 - '0';
} else if (c == '+') {
return 62;
} else if (c == '/') {
return 63;
} else if (c == '=') {
return 64;
} else {
return -1;
}
}
size_t mg_base64_update(unsigned char ch, char *to, size_t n) {
unsigned long rem = (n & 3) % 3;
if (rem == 0) {
to[n] = (char) mg_base64_encode_single(ch >> 2);
to[++n] = (char) ((ch & 3) << 4);
} else if (rem == 1) {
to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 4));
to[++n] = (char) ((ch & 15) << 2);
} else {
to[n] = (char) mg_base64_encode_single(to[n] | (ch >> 6));
to[++n] = (char) mg_base64_encode_single(ch & 63);
n++;
}
return n;
}
size_t mg_base64_final(char *to, size_t n) {
size_t saved = n;
// printf("---[%.*s]\n", n, to);
if (n & 3) n = mg_base64_update(0, to, n);
if ((saved & 3) == 2) n--;
// printf(" %d[%.*s]\n", n, n, to);
while (n & 3) to[n++] = '=';
to[n] = '\0';
return n;
}
size_t mg_base64_encode(const unsigned char *p, size_t n, char *to, size_t dl) {
size_t i, len = 0;
if (dl > 0) to[0] = '\0';
if (dl < ((n / 3) + (n % 3 ? 1 : 0)) * 4 + 1) return 0;
for (i = 0; i < n; i++) len = mg_base64_update(p[i], to, len);
len = mg_base64_final(to, len);
return len;
}
size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t dl) {
const char *end = src == NULL ? NULL : src + n; // Cannot add to NULL
size_t len = 0;
if (dl < n / 4 * 3 + 1) goto fail;
while (src != NULL && src + 3 < end) {
int a = mg_base64_decode_single(src[0]),
b = mg_base64_decode_single(src[1]),
c = mg_base64_decode_single(src[2]),
d = mg_base64_decode_single(src[3]);
if (a == 64 || a < 0 || b == 64 || b < 0 || c < 0 || d < 0) {
goto fail;
}
dst[len++] = (char) ((a << 2) | (b >> 4));
if (src[2] != '=') {
dst[len++] = (char) ((b << 4) | (c >> 2));
if (src[3] != '=') dst[len++] = (char) ((c << 6) | d);
}
src += 4;
}
dst[len] = '\0';
return len;
fail:
if (dl > 0) dst[0] = '\0';
return 0;
}
#ifdef MG_ENABLE_LINES
#line 1 "src/device_ch32v307.c"
#endif
#if MG_DEVICE == MG_DEVICE_CH32V307
// RM: https://www.wch-ic.com/downloads/CH32FV2x_V3xRM_PDF.html
#define FLASH_BASE 0x40022000
#define FLASH_ACTLR (FLASH_BASE + 0)
#define FLASH_KEYR (FLASH_BASE + 4)
#define FLASH_OBKEYR (FLASH_BASE + 8)
#define FLASH_STATR (FLASH_BASE + 12)
#define FLASH_CTLR (FLASH_BASE + 16)
#define FLASH_ADDR (FLASH_BASE + 20)
#define FLASH_OBR (FLASH_BASE + 28)
#define FLASH_WPR (FLASH_BASE + 32)
void *mg_flash_start(void) {
return (void *) 0x08000000;
}
size_t mg_flash_size(void) {
return 480 * 1024; // First 320k is 0-wait
}
size_t mg_flash_sector_size(void) {
return 4096;
}
size_t mg_flash_write_align(void) {
return 4;
}
int mg_flash_bank(void) {
return 0;
}
void mg_device_reset(void) {
*((volatile uint32_t *) 0xbeef0000) |= 1U << 7; // NVIC_SystemReset()
}
static void flash_unlock(void) {
static bool unlocked;
if (unlocked == false) {
MG_REG(FLASH_KEYR) = 0x45670123;
MG_REG(FLASH_KEYR) = 0xcdef89ab;
unlocked = true;
}
}
static void flash_wait(void) {
while (MG_REG(FLASH_STATR) & MG_BIT(0)) (void) 0;
}
bool mg_flash_erase(void *addr) {
//MG_INFO(("%p", addr));
flash_unlock();
flash_wait();
MG_REG(FLASH_ADDR) = (uint32_t) addr;
MG_REG(FLASH_CTLR) |= MG_BIT(1) | MG_BIT(6); // PER | STRT;
flash_wait();
return true;
}
static bool is_page_boundary(const void *addr) {
uint32_t val = (uint32_t) addr;
return (val & (mg_flash_sector_size() - 1)) == 0;
}
bool mg_flash_write(void *addr, const void *buf, size_t len) {
//MG_INFO(("%p %p %lu", addr, buf, len));
//mg_hexdump(buf, len);
flash_unlock();
const uint16_t *src = (uint16_t *) buf, *end = &src[len / 2];
uint16_t *dst = (uint16_t *) addr;
MG_REG(FLASH_CTLR) |= MG_BIT(0); // Set PG
//MG_INFO(("CTLR: %#lx", MG_REG(FLASH_CTLR)));
while (src < end) {
if (is_page_boundary(dst)) mg_flash_erase(dst);
*dst++ = *src++;
flash_wait();
}
MG_REG(FLASH_CTLR) &= ~MG_BIT(0); // Clear PG
return true;
}
#endif
#ifdef MG_ENABLE_LINES
#line 1 "src/device_dummy.c"
#endif
#if MG_DEVICE == MG_DEVICE_NONE
void *mg_flash_start(void) {
return NULL;
}
size_t mg_flash_size(void) {
return 0;
}
size_t mg_flash_sector_size(void) {
return 0;
}
size_t mg_flash_write_align(void) {
return 0;
}
int mg_flash_bank(void) {
return 0;
}
bool mg_flash_erase(void *location) {
(void) location;
return false;
}
bool mg_flash_swap_bank(void) {
return true;
}
bool mg_flash_write(void *addr, const void *buf, size_t len) {
(void) addr, (void) buf, (void) len;
return false;
}
void mg_device_reset(void) {
}
#endif
#ifdef MG_ENABLE_LINES
#line 1 "src/device_flash.c"
#endif
#if MG_DEVICE == MG_DEVICE_STM32H7 || MG_DEVICE == MG_DEVICE_STM32H5 || \
MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060
// Flash can be written only if it is erased. Erased flash is 0xff (all bits 1)
// Writes must be mg_flash_write_align() - aligned. Thus if we want to save an
// object, we pad it at the end for alignment.
//
// Objects in the flash sector are stored sequentially:
// | 32-bit size | 32-bit KEY | ..data.. | ..pad.. | 32-bit size | ......
//
// In order to get to the next object, read its size, then align up.
// Traverse the list of saved objects
size_t mg_flash_next(char *p, char *end, uint32_t *key, size_t *size) {
size_t aligned_size = 0, align = mg_flash_write_align(), left = end - p;
uint32_t *p32 = (uint32_t *) p, min_size = sizeof(uint32_t) * 2;
if (p32[0] != 0xffffffff && left > MG_ROUND_UP(min_size, align)) {
if (size) *size = (size_t) p32[0];
if (key) *key = p32[1];
aligned_size = MG_ROUND_UP(p32[0] + sizeof(uint32_t) * 2, align);
if (left < aligned_size) aligned_size = 0; // Out of bounds, fail
}
return aligned_size;
}
// Return the last sector of Bank 2
static char *flash_last_sector(void) {
size_t ss = mg_flash_sector_size(), size = mg_flash_size();
char *base = (char *) mg_flash_start(), *last = base + size - ss;
if (mg_flash_bank() == 2) last -= size / 2;
return last;
}
// Find a saved object with a given key
bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
char *base = (char *) mg_flash_start(), *s = (char *) sector, *res = NULL;
size_t ss = mg_flash_sector_size(), ofs = 0, n, sz;
bool ok = false;
if (s == NULL) s = flash_last_sector();
if (s < base || s >= base + mg_flash_size()) {
MG_ERROR(("%p is outsize of flash", sector));
} else if (((s - base) % ss) != 0) {
MG_ERROR(("%p is not a sector boundary", sector));
} else {
uint32_t k, scanned = 0;
while ((n = mg_flash_next(s + ofs, s + ss, &k, &sz)) > 0) {
// MG_DEBUG((" > obj %lu, ofs %lu, key %x/%x", scanned, ofs, k, key));
// mg_hexdump(s + ofs, n);
if (k == key && sz == len) {
res = s + ofs + sizeof(uint32_t) * 2;
memcpy(buf, res, len); // Copy object
ok = true; // Keep scanning for the newer versions of it
}
ofs += n, scanned++;
}
MG_DEBUG(("Scanned %u objects, key %x is @ %p", scanned, key, res));
}
return ok;
}
// For all saved objects in the sector, delete old versions of objects
static void mg_flash_sector_cleanup(char *sector) {
// Buffer all saved objects into an IO buffer (backed by RAM)
// erase sector, and re-save them.
struct mg_iobuf io = {0, 0, 0, 2048};
size_t ss = mg_flash_sector_size();
size_t n, size, size2, ofs = 0, hs = sizeof(uint32_t) * 2;
uint32_t key;
// Traverse all objects
MG_DEBUG(("Cleaning up sector %p", sector));
while ((n = mg_flash_next(sector + ofs, sector + ss, &key, &size)) > 0) {
// Delete an old copy of this object in the cache
for (size_t o = 0; o < io.len; o += size2 + hs) {
uint32_t k = *(uint32_t *) (io.buf + o + sizeof(uint32_t));
size2 = *(uint32_t *) (io.buf + o);
if (k == key) {
mg_iobuf_del(&io, o, size2 + hs);
break;
}
}
// And add the new copy
mg_iobuf_add(&io, io.len, sector + ofs, size + hs);
ofs += n;
}
// All objects are cached in RAM now
if (mg_flash_erase(sector)) { // Erase sector. If successful,
for (ofs = 0; ofs < io.len; ofs += size + hs) { // Traverse cached objects
size = *(uint32_t *) (io.buf + ofs);
key = *(uint32_t *) (io.buf + ofs + sizeof(uint32_t));
mg_flash_save(sector, key, io.buf + ofs + hs, size); // Save to flash
}
}
mg_iobuf_free(&io);
}
// Save an object with a given key - append to the end of an object list
bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
char *base = (char *) mg_flash_start(), *s = (char *) sector;
size_t ss = mg_flash_sector_size(), ofs = 0, n;
bool ok = false;
if (s == NULL) s = flash_last_sector();
if (s < base || s >= base + mg_flash_size()) {
MG_ERROR(("%p is outsize of flash", sector));
} else if (((s - base) % ss) != 0) {
MG_ERROR(("%p is not a sector boundary", sector));
} else {
char ab[mg_flash_write_align()]; // Aligned write block
uint32_t hdr[2] = {(uint32_t) len, key};
size_t needed = sizeof(hdr) + len;
size_t needed_aligned = MG_ROUND_UP(needed, sizeof(ab));
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
// If there is not enough space left, cleanup sector and re-eval ofs
if (ofs + needed_aligned >= ss) {
mg_flash_sector_cleanup(s);
ofs = 0;
while ((n = mg_flash_next(s + ofs, s + ss, NULL, NULL)) > 0) ofs += n;
}
if (ofs + needed_aligned <= ss) {
// Enough space to save this object
if (sizeof(ab) < sizeof(hdr)) {
// Flash write granularity is 32 bit or less, write with no buffering
ok = mg_flash_write(s + ofs, hdr, sizeof(hdr));
if (ok) mg_flash_write(s + ofs + sizeof(hdr), buf, len);
} else {
// Flash granularity is sizeof(hdr) or more. We need to save in
// 3 chunks: initial block, bulk, rest. This is because we have
// two memory chunks to write: hdr and buf, on aligned boundaries.
n = sizeof(ab) - sizeof(hdr); // Initial chunk that we write
if (n > len) n = len; // is
memset(ab, 0xff, sizeof(ab)); // initialized to all-one
memcpy(ab, hdr, sizeof(hdr)); // contains the header (key + size)
memcpy(ab + sizeof(hdr), buf, n); // and an initial part of buf
MG_INFO(("saving initial block of %lu", sizeof(ab)));
ok = mg_flash_write(s + ofs, ab, sizeof(ab));
if (ok && len > n) {
size_t n2 = MG_ROUND_DOWN(len - n, sizeof(ab));
if (n2 > 0) {
MG_INFO(("saving bulk, %lu", n2));
ok = mg_flash_write(s + ofs + sizeof(ab), (char *) buf + n, n2);
}
if (ok && len > n) {
size_t n3 = len - n - n2;
if (n3 > sizeof(ab)) n3 = sizeof(ab);
memset(ab, 0xff, sizeof(ab));
memcpy(ab, (char *) buf + n + n2, n3);
MG_INFO(("saving rest, %lu", n3));
ok = mg_flash_write(s + ofs + sizeof(ab) + n2, ab, sizeof(ab));
}
}
}
MG_DEBUG(("Saved %lu/%lu bytes @ %p, key %x: %d", len, needed_aligned,
s + ofs, key, ok));
MG_DEBUG(("Sector space left: %lu bytes", ss - ofs - needed_aligned));
} else {
MG_ERROR(("Sector is full"));
}
}
return ok;
}
#else
bool mg_flash_save(void *sector, uint32_t key, const void *buf, size_t len) {
(void) sector, (void) key, (void) buf, (void) len;
return false;
}
bool mg_flash_load(void *sector, uint32_t key, void *buf, size_t len) {
(void) sector, (void) key, (void) buf, (void) len;
return false;
}
#endif
#ifdef MG_ENABLE_LINES
#line 1 "src/device_imxrt.c"
#endif
#if MG_DEVICE == MG_DEVICE_RT1020 || MG_DEVICE == MG_DEVICE_RT1060
struct mg_flexspi_lut_seq {
uint8_t seqNum;
uint8_t seqId;
uint16_t reserved;
};
struct mg_flexspi_mem_config {
uint32_t tag;
uint32_t version;
uint32_t reserved0;
uint8_t readSampleClkSrc;
uint8_t csHoldTime;
uint8_t csSetupTime;
uint8_t columnAddressWidth;
uint8_t deviceModeCfgEnable;
uint8_t deviceModeType;
uint16_t waitTimeCfgCommands;
struct mg_flexspi_lut_seq deviceModeSeq;
uint32_t deviceModeArg;
uint8_t configCmdEnable;
uint8_t configModeType[3];
struct mg_flexspi_lut_seq configCmdSeqs[3];
uint32_t reserved1;
uint32_t configCmdArgs[3];
uint32_t reserved2;
uint32_t controllerMiscOption;
uint8_t deviceType;
uint8_t sflashPadType;
uint8_t serialClkFreq;
uint8_t lutCustomSeqEnable;
uint32_t reserved3[2];
uint32_t sflashA1Size;
uint32_t sflashA2Size;
uint32_t sflashB1Size;
uint32_t sflashB2Size;
uint32_t csPadSettingOverride;
uint32_t sclkPadSettingOverride;
uint32_t dataPadSettingOverride;
uint32_t dqsPadSettingOverride;
uint32_t timeoutInMs;
uint32_t commandInterval;
uint16_t dataValidTime[2];
uint16_t busyOffset;
uint16_t busyBitPolarity;
uint32_t lookupTable[64];
struct mg_flexspi_lut_seq lutCustomSeq[12];
uint32_t reserved4[4];
};
struct mg_flexspi_nor_config {
struct mg_flexspi_mem_config memConfig;
uint32_t pageSize;
uint32_t sectorSize;
uint8_t ipcmdSerialClkFreq;
uint8_t isUniformBlockSize;
uint8_t reserved0[2];
uint8_t serialNorType;
uint8_t needExitNoCmdMode;
uint8_t halfClkForNonReadCmd;
uint8_t needRestoreNoCmdMode;
uint32_t blockSize;
uint32_t reserve2[11];
};
/* FLEXSPI memory config block related defintions */
#define MG_FLEXSPI_CFG_BLK_TAG (0x42464346UL) // ascii "FCFB" Big Endian
#define MG_FLEXSPI_CFG_BLK_VERSION (0x56010400UL) // V1.4.0
#define MG_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \
(MG_FLEXSPI_LUT_OPERAND0(op0) | MG_FLEXSPI_LUT_NUM_PADS0(pad0) | MG_FLEXSPI_LUT_OPCODE0(cmd0) | \
MG_FLEXSPI_LUT_OPERAND1(op1) | MG_FLEXSPI_LUT_NUM_PADS1(pad1) | MG_FLEXSPI_LUT_OPCODE1(cmd1))
#define MG_CMD_SDR 0x01
#define MG_CMD_DDR 0x21
#define MG_DUMMY_SDR 0x0C
#define MG_DUMMY_DDR 0x2C
#define MG_RADDR_SDR 0x02
#define MG_RADDR_DDR 0x22
#define MG_READ_SDR 0x09
#define MG_READ_DDR 0x29
#define MG_WRITE_SDR 0x08
#define MG_WRITE_DDR 0x28
#define MG_STOP 0
#define MG_FLEXSPI_1PAD 0
#define MG_FLEXSPI_2PAD 1
#define MG_FLEXSPI_4PAD 2
#define MG_FLEXSPI_8PAD 3
#define MG_FLEXSPI_QSPI_LUT \
{ \
[0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xEB, MG_RADDR_SDR, MG_FLEXSPI_4PAD, \
0x18), \
[1] = MG_FLEXSPI_LUT_SEQ(MG_DUMMY_SDR, MG_FLEXSPI_4PAD, 0x06, MG_READ_SDR, MG_FLEXSPI_4PAD, \
0x04), \
[4 * 1 + 0] = \
MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x05, MG_READ_SDR, MG_FLEXSPI_1PAD, 0x04), \
[4 * 3 + 0] = \
MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x06, MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
[4 * 5 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x20, MG_RADDR_SDR, \
MG_FLEXSPI_1PAD, 0x18), \
[4 * 8 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0xD8, MG_RADDR_SDR, \
MG_FLEXSPI_1PAD, 0x18), \
[4 * 9 + 0] = MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x02, MG_RADDR_SDR, \
MG_FLEXSPI_1PAD, 0x18), \
[4 * 9 + 1] = \
MG_FLEXSPI_LUT_SEQ(MG_WRITE_SDR, MG_FLEXSPI_1PAD, 0x04, MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
[4 * 11 + 0] = \
MG_FLEXSPI_LUT_SEQ(MG_CMD_SDR, MG_FLEXSPI_1PAD, 0x60, MG_STOP, MG_FLEXSPI_1PAD, 0x0), \
}
#define MG_FLEXSPI_LUT_OPERAND0(x) (((uint32_t) (((uint32_t) (x)))) & 0xFFU)
#define MG_FLEXSPI_LUT_NUM_PADS0(x) (((uint32_t) (((uint32_t) (x)) << 8U)) & 0x300U)
#define MG_FLEXSPI_LUT_OPCODE0(x) (((uint32_t) (((uint32_t) (x)) << 10U)) & 0xFC00U)
#define MG_FLEXSPI_LUT_OPERAND1(x) (((uint32_t) (((uint32_t) (x)) << 16U)) & 0xFF0000U)
#define MG_FLEXSPI_LUT_NUM_PADS1(x) (((uint32_t) (((uint32_t) (x)) << 24U)) & 0x3000000U)
#define MG_FLEXSPI_LUT_OPCODE1(x) (((uint32_t) (((uint32_t) (x)) << 26U)) & 0xFC000000U)
#define FLEXSPI_NOR_INSTANCE 0
#if MG_DEVICE == MG_DEVICE_RT1020
struct mg_flexspi_nor_driver_interface {
uint32_t version;
int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t dst_addr,
const uint32_t *src);
uint32_t reserved;
int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t start,
uint32_t lengthInBytes);
uint32_t reserved2;
int (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t *lutBase,
uint32_t seqNumber);
int (*xfer)(uint32_t instance, char *xfer);
void (*clear_cache)(uint32_t instance);
};
#elif MG_DEVICE == MG_DEVICE_RT1060
struct mg_flexspi_nor_driver_interface {
uint32_t version;
int (*init)(uint32_t instance, struct mg_flexspi_nor_config *config);
int (*program)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t dst_addr,
const uint32_t *src);
int (*erase_all)(uint32_t instance, struct mg_flexspi_nor_config *config);
int (*erase)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t start,
uint32_t lengthInBytes);
int (*read)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t *dst, uint32_t addr,
uint32_t lengthInBytes);
void (*clear_cache)(uint32_t instance);
int (*xfer)(uint32_t instance, char *xfer);
int (*update_lut)(uint32_t instance, uint32_t seqIndex, const uint32_t *lutBase,
uint32_t seqNumber);
int (*get_config)(uint32_t instance, struct mg_flexspi_nor_config *config, uint32_t *option);
};
#endif
#define flexspi_nor (*((struct mg_flexspi_nor_driver_interface**) \
(*(uint32_t*)0x0020001c + 16)))
static bool s_flash_irq_disabled;
MG_IRAM void *mg_flash_start(void) {
return (void *) 0x60000000;
}
MG_IRAM size_t mg_flash_size(void) {
return 8 * 1024 * 1024;
}
MG_IRAM size_t mg_flash_sector_size(void) {
return 4 * 1024; // 4k
}
MG_IRAM size_t mg_flash_write_align(void) {
return 256;
}
MG_IRAM int mg_flash_bank(void) {
return 0;
}
MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
char *base = (char *) mg_flash_start(), *end = base + mg_flash_size();
volatile char *p = (char *) dst;
return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0;
}
// Note: the get_config function below works both for RT1020 and 1060
#if MG_DEVICE == MG_DEVICE_RT1020
MG_IRAM static int flexspi_nor_get_config(struct mg_flexspi_nor_config *config) {
struct mg_flexspi_nor_config default_config = {
.memConfig = {.tag = MG_FLEXSPI_CFG_BLK_TAG,
.version = MG_FLEXSPI_CFG_BLK_VERSION,
.readSampleClkSrc = 1, // ReadSampleClk_LoopbackFromDqsPad
.csHoldTime = 3,
.csSetupTime = 3,
.controllerMiscOption = MG_BIT(4),
.deviceType = 1, // serial NOR
.sflashPadType = 4,
.serialClkFreq = 7, // 133MHz
.sflashA1Size = 8 * 1024 * 1024,
.lookupTable = MG_FLEXSPI_QSPI_LUT},
.pageSize = 256,
.sectorSize = 4 * 1024,
.ipcmdSerialClkFreq = 1,
.blockSize = 64 * 1024,
.isUniformBlockSize = false};
*config = default_config;
return 0;
}
#else
MG_IRAM static int flexspi_nor_get_config(struct mg_flexspi_nor_config *config) {
uint32_t options[] = {0xc0000000, 0x00};
MG_ARM_DISABLE_IRQ();
uint32_t status =
flexspi_nor->get_config(FLEXSPI_NOR_INSTANCE, config, options);
if (!s_flash_irq_disabled) {
MG_ARM_ENABLE_IRQ();
}
if (status) {
MG_ERROR(("Failed to extract flash configuration: status %u", status));
}
return status;
}
#endif
MG_IRAM bool mg_flash_erase(void *addr) {
struct mg_flexspi_nor_config config;
if (flexspi_nor_get_config(&config) != 0) {
return false;
}
if (flash_page_start(addr) == false) {
MG_ERROR(("%p is not on a sector boundary", addr));
return false;
}
void *dst = (void *)((char *) addr - (char *) mg_flash_start());
// Note: Interrupts must be disabled before any call to the ROM API on RT1020
// and 1060
MG_ARM_DISABLE_IRQ();
bool ok = (flexspi_nor->erase(FLEXSPI_NOR_INSTANCE, &config, (uint32_t) dst,
mg_flash_sector_size()) == 0);
if (!s_flash_irq_disabled) {
MG_ARM_ENABLE_IRQ(); // Reenable them after the call
}
MG_DEBUG(("Sector starting at %p erasure: %s", addr, ok ? "ok" : "fail"));
return ok;
}
MG_IRAM bool mg_flash_swap_bank(void) {
return true;
}
static inline void spin(volatile uint32_t count) {
while (count--) (void) 0;
}
static inline void flash_wait(void) {
while ((*((volatile uint32_t *)(0x402A8000 + 0xE0)) & MG_BIT(1)) == 0)
spin(1);
}
MG_IRAM static void *flash_code_location(void) {
return (void *) ((char *) mg_flash_start() + 0x2000);
}
MG_IRAM bool mg_flash_write(void *addr, const void *buf, size_t len) {
struct mg_flexspi_nor_config config;
if (flexspi_nor_get_config(&config) != 0) {
return false;
}
if ((len % mg_flash_write_align()) != 0) {
MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align()));
return false;
}
if ((char *) addr < (char *) mg_flash_start()) {
MG_ERROR(("Invalid flash write address: %p", addr));
return false;
}
uint32_t *dst = (uint32_t *) addr;
uint32_t *src = (uint32_t *) buf;
uint32_t *end = (uint32_t *) ((char *) buf + len);
bool ok = true;
// Note: If we overwrite the flash irq section of the image, we must also
// make sure interrupts are disabled and are not reenabled until we write
// this sector with another irq table.
if ((char *) addr == (char *) flash_code_location()) {
s_flash_irq_disabled = true;
MG_ARM_DISABLE_IRQ();
}
while (ok && src < end) {
if (flash_page_start(dst) && mg_flash_erase(dst) == false) {
break;
}
uint32_t status;
uint32_t dst_ofs = (uint32_t) dst - (uint32_t) mg_flash_start();
if ((char *) buf >= (char *) mg_flash_start()) {
// If we copy from FLASH to FLASH, then we first need to copy the source
// to RAM
size_t tmp_buf_size = mg_flash_write_align() / sizeof(uint32_t);
uint32_t tmp[tmp_buf_size];
for (size_t i = 0; i < tmp_buf_size; i++) {
flash_wait();
tmp[i] = src[i];
}
MG_ARM_DISABLE_IRQ();
status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config,
(uint32_t) dst_ofs, tmp);
} else {
MG_ARM_DISABLE_IRQ();
status = flexspi_nor->program(FLEXSPI_NOR_INSTANCE, &config,
(uint32_t) dst_ofs, src);
}
if (!s_flash_irq_disabled) {
MG_ARM_ENABLE_IRQ();
}
src = (uint32_t *) ((char *) src + mg_flash_write_align());
dst = (uint32_t *) ((char *) dst + mg_flash_write_align());
if (status != 0) {
ok = false;
}
}
MG_DEBUG(("Flash write %lu bytes @ %p: %s.", len, dst, ok ? "ok" : "fail"));
return ok;
}
MG_IRAM void mg_device_reset(void) {
MG_DEBUG(("Resetting device..."));
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
}
#endif
#ifdef MG_ENABLE_LINES
#line 1 "src/device_stm32h5.c"
#endif
#if MG_DEVICE == MG_DEVICE_STM32H5
#define FLASH_BASE 0x40022000 // Base address of the flash controller
#define FLASH_KEYR (FLASH_BASE + 0x4) // See RM0481 7.11
#define FLASH_OPTKEYR (FLASH_BASE + 0xc)
#define FLASH_OPTCR (FLASH_BASE + 0x1c)
#define FLASH_NSSR (FLASH_BASE + 0x20)
#define FLASH_NSCR (FLASH_BASE + 0x28)
#define FLASH_NSCCR (FLASH_BASE + 0x30)
#define FLASH_OPTSR_CUR (FLASH_BASE + 0x50)
#define FLASH_OPTSR_PRG (FLASH_BASE + 0x54)
void *mg_flash_start(void) {
return (void *) 0x08000000;
}
size_t mg_flash_size(void) {
return 2 * 1024 * 1024; // 2Mb
}
size_t mg_flash_sector_size(void) {
return 8 * 1024; // 8k
}
size_t mg_flash_write_align(void) {
return 16; // 128 bit
}
int mg_flash_bank(void) {
return MG_REG(FLASH_OPTCR) & MG_BIT(31) ? 2 : 1;
}
static void flash_unlock(void) {
static bool unlocked = false;
if (unlocked == false) {
MG_REG(FLASH_KEYR) = 0x45670123;
MG_REG(FLASH_KEYR) = 0Xcdef89ab;
MG_REG(FLASH_OPTKEYR) = 0x08192a3b;
MG_REG(FLASH_OPTKEYR) = 0x4c5d6e7f;
unlocked = true;
}
}
static int flash_page_start(volatile uint32_t *dst) {
char *base = (char *) mg_flash_start(), *end = base + mg_flash_size();
volatile char *p = (char *) dst;
return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0;
}
static bool flash_is_err(void) {
return MG_REG(FLASH_NSSR) & ((MG_BIT(8) - 1) << 17); // RM0481 7.11.9
}
static void flash_wait(void) {
while ((MG_REG(FLASH_NSSR) & MG_BIT(0)) &&
(MG_REG(FLASH_NSSR) & MG_BIT(16)) == 0) {
(void) 0;
}
}
static void flash_clear_err(void) {
flash_wait(); // Wait until ready
MG_REG(FLASH_NSCCR) = ((MG_BIT(9) - 1) << 16U); // Clear all errors
}
static bool flash_bank_is_swapped(void) {
return MG_REG(FLASH_OPTCR) & MG_BIT(31); // RM0481 7.11.8
}
bool mg_flash_erase(void *location) {
bool ok = false;
if (flash_page_start(location) == false) {
MG_ERROR(("%p is not on a sector boundary"));
} else {
uintptr_t diff = (char *) location - (char *) mg_flash_start();
uint32_t sector = diff / mg_flash_sector_size();
uint32_t saved_cr = MG_REG(FLASH_NSCR); // Save CR value
flash_unlock();
flash_clear_err();
MG_REG(FLASH_NSCR) = 0;
if ((sector < 128 && flash_bank_is_swapped()) ||
(sector > 127 && !flash_bank_is_swapped())) {
MG_REG(FLASH_NSCR) |= MG_BIT(31); // Set FLASH_CR_BKSEL
}
if (sector > 127) sector -= 128;
MG_REG(FLASH_NSCR) |= MG_BIT(2) | (sector << 6); // Erase | sector_num
MG_REG(FLASH_NSCR) |= MG_BIT(5); // Start erasing
flash_wait();
ok = !flash_is_err();
MG_DEBUG(("Erase sector %lu @ %p: %s. CR %#lx SR %#lx", sector, location,
ok ? "ok" : "fail", MG_REG(FLASH_NSCR), MG_REG(FLASH_NSSR)));
// mg_hexdump(location, 32);
MG_REG(FLASH_NSCR) = saved_cr; // Restore saved CR
}
return ok;
}
bool mg_flash_swap_bank(void) {
uint32_t desired = flash_bank_is_swapped() ? 0 : MG_BIT(31);
flash_unlock();
flash_clear_err();
// printf("OPTSR_PRG 1 %#lx\n", FLASH->OPTSR_PRG);
MG_SET_BITS(MG_REG(FLASH_OPTSR_PRG), MG_BIT(31), desired);
// printf("OPTSR_PRG 2 %#lx\n", FLASH->OPTSR_PRG);
MG_REG(FLASH_OPTCR) |= MG_BIT(1); // OPTSTART
while ((MG_REG(FLASH_OPTSR_CUR) & MG_BIT(31)) != desired) (void) 0;
return true;
}
bool mg_flash_write(void *addr, const void *buf, size_t len) {
if ((len % mg_flash_write_align()) != 0) {
MG_ERROR(("%lu is not aligned to %lu", len, mg_flash_write_align()));
return false;
}
uint32_t *dst = (uint32_t *) addr;
uint32_t *src = (uint32_t *) buf;
uint32_t *end = (uint32_t *) ((char *) buf + len);
bool ok = true;
flash_unlock();
flash_clear_err();
MG_ARM_DISABLE_IRQ();
// MG_DEBUG(("Starting flash write %lu bytes @ %p", len, addr));
MG_REG(FLASH_NSCR) = MG_BIT(1); // Set programming flag
while (ok && src < end) {
if (flash_page_start(dst) && mg_flash_erase(dst) == false) break;
*(volatile uint32_t *) dst++ = *src++;
flash_wait();
if (flash_is_err()) ok = false;
}
MG_ARM_ENABLE_IRQ();
MG_DEBUG(("Flash write %lu bytes @ %p: %s. CR %#lx SR %#lx", len, dst,
flash_is_err() ? "fail" : "ok", MG_REG(FLASH_NSCR),
MG_REG(FLASH_NSSR)));
MG_REG(FLASH_NSCR) = 0; // Clear flags
return ok;
}
void mg_device_reset(void) {
// SCB->AIRCR = ((0x5fa << SCB_AIRCR_VECTKEY_Pos)|SCB_AIRCR_SYSRESETREQ_Msk);
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004;
}
#endif
#ifdef MG_ENABLE_LINES
#line 1 "src/device_stm32h7.c"
#endif
#if MG_DEVICE == MG_DEVICE_STM32H7
#define FLASH_BASE1 0x52002000 // Base address for bank1
#define FLASH_BASE2 0x52002100 // Base address for bank2
#define FLASH_KEYR 0x04 // See RM0433 4.9.2
#define FLASH_OPTKEYR 0x08
#define FLASH_OPTCR 0x18
#define FLASH_SR 0x10
#define FLASH_CR 0x0c
#define FLASH_CCR 0x14
#define FLASH_OPTSR_CUR 0x1c
#define FLASH_OPTSR_PRG 0x20
#define FLASH_SIZE_REG 0x1ff1e880
MG_IRAM void *mg_flash_start(void) {
return (void *) 0x08000000;
}
MG_IRAM size_t mg_flash_size(void) {
return MG_REG(FLASH_SIZE_REG) * 1024;
}
MG_IRAM size_t mg_flash_sector_size(void) {
return 128 * 1024; // 128k
}
MG_IRAM size_t mg_flash_write_align(void) {
return 32; // 256 bit
}
MG_IRAM int mg_flash_bank(void) {
if (mg_flash_size() < 2 * 1024 * 1024) return 0; // No dual bank support
return MG_REG(FLASH_BASE1 + FLASH_OPTCR) & MG_BIT(31) ? 2 : 1;
}
MG_IRAM static void flash_unlock(void) {
static bool unlocked = false;
if (unlocked == false) {
MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0x45670123;
MG_REG(FLASH_BASE1 + FLASH_KEYR) = 0xcdef89ab;
if (mg_flash_bank() > 0) {
MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0x45670123;
MG_REG(FLASH_BASE2 + FLASH_KEYR) = 0xcdef89ab;
}
MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x08192a3b; // opt reg is "shared"
MG_REG(FLASH_BASE1 + FLASH_OPTKEYR) = 0x4c5d6e7f; // thus unlock once
unlocked = true;
}
}
MG_IRAM static bool flash_page_start(volatile uint32_t *dst) {
char *base = (char *) mg_flash_start(), *end = base + mg_flash_size();
volatile char *p = (char *) dst;
return p >= base && p < end && ((p - base) % mg_flash_sector_size()) == 0;
}
MG_IRAM static bool flash_is_err(uint32_t bank) {
return MG_REG(bank + FLASH_SR) & ((MG_BIT(11) - 1) << 17); // RM0433 4.9.5
}
MG_IRAM static void flash_wait(uint32_t bank) {
while (MG_REG(bank + FLASH_SR) & (MG_BIT(0) | MG_BIT(2))) (void) 0;
}
MG_IRAM static void flash_clear_err(uint32_t bank) {
flash_wait(bank); // Wait until ready
MG_REG(bank + FLASH_CCR) = ((MG_BIT(11) - 1) << 16U); // Clear all errors
}
MG_IRAM static bool flash_bank_is_swapped(uint32_t bank) {
return MG_REG(bank + FLASH_OPTCR) & MG_BIT(31); // RM0433 4.9.7
}
// Figure out flash bank based on the address
MG_IRAM static uint32_t flash_bank(void *addr) {
size_t ofs = (char *) addr - (char *) mg_flash_start();
if (mg_flash_bank() == 0) return FLASH_BASE1;
return ofs < mg_flash_size() / 2 ? FLASH_BASE1 : FLASH_BASE2;
}
MG_IRAM bool mg_flash_erase(void *addr) {
bool ok = false;
if (flash_page_start(addr) == false) {
MG_ERROR(("%p is not on a sector boundary", addr));
} else {
uintptr_t diff = (char *) addr - (char *) mg_flash_start();
uint32_t sector = diff / mg_flash_sector_size();
uint32_t bank = flash_bank(addr);
uint32_t saved_cr = MG_REG(bank + FLASH_CR); // Save CR value
flash_unlock();
if (sector > 7) sector -= 8;
flash_clear_err(bank);
MG_REG(bank + FLASH_CR) = MG_BIT(5); // 32-bit write parallelism
MG_REG(bank + FLASH_CR) |= (sector & 7U) << 8U; // Sector to erase
MG_REG(bank + FLASH_CR) |= MG_BIT(2); // Sector erase bit
MG_REG(bank + FLASH_CR) |= MG_BIT(7); // Start erasing