-
Notifications
You must be signed in to change notification settings - Fork 4
/
bootloader.c
1042 lines (971 loc) · 51.3 KB
/
bootloader.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
// CH32V UF2 bootloader, size-optimized (target: <= 4096 bytes)
#include <stdint.h>
typedef struct USBD_descriptor {
uint32_t addr_tx;
uint32_t count_tx;
uint32_t addr_rx;
uint32_t count_rx;
} USBD_descriptor;
// USBD memory allocation:
// WARNING THIS IS A 16-bit MEMORY LMAO
// In other words, the memory contains 16-bit values spaced out into 32-bit words:
// xx yy 00 00 | zz ww 00 00 | aa bb 00 00 | ..
// This significantly complicates memory copying.
// Also, accessing past the end of USBD RAM will lock up the entire core
// to the point where debugging stops working. This can be triggered by an "x"
// command in GDB (so be careful).
// There are indeed 512 bytes of total USBD RAM, but they span 1024 bytes of
// address space (because the 00 00 doesn't count).
// (It feels like WCH grafted an old core for not-32-bit processors into
// these RISC-V parts...)
// +0x00
// descriptors (two sets of two endpoints)
// +0x20
// EP 0 OUT (8 bytes)
// +0x30
// EP 0 IN (8 bytes)
// +0x40
// EP 1 OUT (64 bytes)
// +0xc0
// EP 1 IN (64 bytes)
// +0x140
// ram for checking download completion
// +0x1d0
// ram for stashing variables
// +0x200
// <256-byte flash page buffer>
// In addition to the expected USB buffers, this RAM is used to store program state.
// This allows the *entire* SRAM to be used when downloading to SRAM.
// Note that every buffer that needs to be sent over USB must be 16-bit aligned
// (in code flash).
// This is assumed by copying code (which will copy in 16-bit chunks).
// Device descriptor
const uint8_t USB_DEV_DESC[18] __attribute__((aligned(2))) = {
18, // bLength
1, // bDescriptorType
0x00, 0x02, // bcdUSB
0, // bDeviceClass
0, // bDeviceSubClass
0, // bDeviceProtocol
8, // bMaxPacketSize0
0x55, 0xf0, // idVendor
0x00, 0x00, // idProduct
0x00, 0x00, // bcdDevice
1, // iManufacturer
2, // iProduct
3, // iSerialNumber
1, // bNumConfigurations
};
const uint8_t USB_CONF_DESC[0x20] __attribute__((aligned(2))) = {
// Configuration descriptor
9, // bLength
2, // bDescriptorType
0x20, 0x00, // wTotalLength
1, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0x80, // bmAttributes
250, // bMaxPower
// Interface descriptor
9, // bLength
4, // bDescriptorType
0, // bInterfaceNumber
0, // bAlternateSetting
2, // bNumEndpoints
0x08, // bInterfaceClass
0x05, // bInterfaceSubClass
0x50, // bInterfaceProtocol
0, // iInterface
// EP1 IN endpoint descriptor
7, // bLength
5, // bDescriptorType
0x81, // bEndpointAddress
2, // bmAttributes
0x40, 0x00, // wMaxPacketSize
0, // bInterval
// EP1 OUT endpoint descriptor
7, // bLength
5, // bDescriptorType
0x01, // bEndpointAddress
2, // bmAttributes
0x40, 0x00, // wMaxPacketSize
0, // bInterval
};
// Must be UTF-16, and the first character is a *manually-calculated* total length
// (combined with a 0x03 string descriptor type)
// Non-latin is allowed, but LANGID is hardcoded to be 0x0409 (English (United States))
// further down in the code.
const uint16_t USB_MANUF[13] = u"\u031aArcaneNibble";
const uint16_t USB_PRODUCT[15] = u"\u031eCH32V UF2 Boot";
// Used for outputting serial number from electronic signature bytes
const uint8_t HEXLUT[16] = "0123456789ABCDEF";
// SCSI INQUIRY standard response
const uint8_t INQUIRY_RESPONSE[36] __attribute__((aligned(2))) = {
0x00, // direct access block device
0x80, // RMB removable media
// (if you don't set the RMB flag, macOS will show an orange drive
// instead of a grey drive, contrary to expectations of flash drives)
0x04, // SPC-2
0x02, // fixed, response data format
0x1F, // additional length
0x00, // xxx mostly obsolete junk
0x00,
0x00,
// Vendor
'A', 'r', 'c', 'a', 'n', 'e', 'N', 'b',
// Product
'C', 'H', '3', '2', 'V', ' ', 'U', 'F', '2', ' ', 'B', 'o', 'o', 't', ' ', ' ',
// Revision
' ', ' ', ' ', ' ',
};
// FAT16 boot sector
// Most of these parameters *cannot* be changed,
// as synthesize_block assumes a particular layout
const uint8_t BOOT_SECTOR[0x3e] __attribute__((aligned(2))) = {
0xeb, 0x3c, 0x90, // jump (seems to be required for some OSes to auto-mount)
'A', 'r', 'c', 'a', 'n', 'e', 'N', 'b', // oem name
0x00, 0x02, // 512 bytes/sector
0x01, // 1 sector/cluster
0x01, 0x00, // 1 reserved sector
0x01, // 1 fat
0x10, 0x00, // 16 root dir entries
0x00, 0x40, // num sectors
0xf8, // media descriptor
0x40, 0x00, // 16384 sectors/clusters -> 64 FAT sectors
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // useless stuff
0x80, // "drive number"
0x00, // reserved
0x29, // magic
0xde, 0xad, 0xbe, 0xef, // serial number
'C', 'H', '3', '2', 'V', ' ', 'U', 'F', '2', ' ', ' ', // volume label
'F', 'A', 'T', '1', '6', ' ', ' ', ' ', // fs type
// note that the trailing 55 aa is generated in code
// (seemingly also required for auto-mount on some OSes)
};
// Change here to change UF2 data files
const uint8_t INFO_UF2[70] __attribute__((aligned(2))) = "UF2 Bootloader v0.0.0\nModel: CH32V Generic\nBoard-ID: CH32Vxxx-Generic\n";
const uint8_t INDEX_HTM[119] __attribute__((aligned(2))) = "<!doctype html>\n<html><body><script>location.replace(\"https://github.com/ArcaneNibble/wch-uf2\")</script></body></html>\n";
// Change here for different chips
#define THIS_CHIP_FLASH_MAX_SZ_BYTES (224 * 1024)
#define THIS_CHIP_RAM_MAX_SZ_BYTES (20 * 1024)
#define BOOTLOADER_RESERVED_SZ_BYTES (4 * 1024)
#define FAMILY_ID 0x699b62ec
// FAT16 root directory entries
// Most of these parameters *cannot* be changed,
// as synthesize_block assumes a particular layout
const uint8_t ROOT_DIR[32 * 3] __attribute__((aligned(2))) = {
'C', 'H', '3', '2', 'V', ' ', 'U', 'F', '2', ' ', ' ', // name
0x08, // attributes (volume label)
0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timestamps
0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // timestamps
0x00, 0x00, // start cluster
0x00, 0x00, 0x00, 0x00, // file size
'I', 'N', 'F', 'O', '_', 'U', 'F', '2', 'T', 'X', 'T', // name
0x01, // attributes (RO)
0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timestamps
0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // timestamps
0x02, 0x00, // start cluster
sizeof(INFO_UF2), sizeof(INFO_UF2) >> 8, sizeof(INFO_UF2) >> 16, sizeof(INFO_UF2) >> 24,
'I', 'N', 'D', 'E', 'X', ' ', ' ', ' ', 'H', 'T', 'M', // name
0x01, // attributes (RO)
0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timestamps
0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // timestamps
0x03, 0x00, // start cluster
sizeof(INDEX_HTM), sizeof(INDEX_HTM) >> 8, sizeof(INDEX_HTM) >> 16, sizeof(INDEX_HTM) >> 24,
};
#define ESIG_UNIID(x) (*(volatile uint8_t*)(0x1FFFF7E8 + (x)))
// XXX manual claims 96 bits but only 64 bits seem to actually be programmed?
// Note that all of this stuff is declared *extern*
// A significant amount of code size is being saved because the gp register
// is pointed at 0x40006000, right at the beginning of USBD RAM
// (and within reach of the USBD registers).
// The linker is then being instructed to perform gp-relative linker relaxation.
// Actual addresses for these variables are in the linker.lds file.
extern volatile USBD_descriptor USB_DESCS[2];
extern volatile uint32_t USB_EP0_OUT[4];
extern volatile uint32_t USB_EP0_IN[4];
extern volatile uint32_t USB_EP1_OUT[32];
extern volatile uint32_t USB_EP1_IN[32];
// These are the state variables shoved into USBD RAM
// (in the area not being used by buffers)
extern uint32_t SCSI_XFER_CUR_LBA;
extern uint32_t SCSI_XFER_BLK_LEFT;
extern uint32_t BLOCKNUM_LO;
extern uint32_t TOTBLOCKS_LO;
extern uint32_t CSWTAG_LO;
extern uint32_t CSWTAG_HI;
extern uint32_t ADDRESS_LO;
extern uint32_t ADDRESS_HI;
extern uint32_t ACTIVE_CONFIG;
extern uint32_t CTRL_XFER_STATE;
extern uint32_t CTRL_XFER_STATE_X;
extern uint32_t CTRL_XFER_DESC_SZ;
extern uint32_t USB_SECTOR_STASH[128];
// Files larger than this won't cause an auto-reboot
// (there is not enough USBD RAM to mark which blocks have been received)
// The last bit (MSB) of the last halfword is used to mark
// "has received the first valid UF2 block which contains a valid block count"
#define AUTO_BOOT_BITMAP_NUM_HWORDS 36
#define MAX_AUTO_BOOT_BLOCKS (AUTO_BOOT_BITMAP_NUM_HWORDS * 16 - 1)
extern uint32_t UF2_GOT_BLOCKS[AUTO_BOOT_BITMAP_NUM_HWORDS];
// Constants for USBD registers
#define USB_EPTYPE_BULK 0b00
#define USB_EPTYPE_CONTROL 0b01
#define USB_STAT_DISABLED 0b00
#define USB_STAT_STALL 0b01
#define USB_STAT_NAK 0b10
#define USB_STAT_ACK 0b11
// Other registers we need
#define R16_BKP_DATAR10 (*(volatile uint32_t*)0x40006C28)
#define R32_PWR_CTLR (*(volatile uint32_t*)0x40007000)
#define R32_RCC_CTLR (*(volatile uint32_t*)0x40021000)
#define R32_RCC_CFGR0 (*(volatile uint32_t*)0x40021004)
#define R32_RCC_APB2PCENR (*(volatile uint32_t*)0x40021018)
#define R32_RCC_APB1PCENR (*(volatile uint32_t*)0x4002101C)
#define R32_RCC_RSTSCKR (*(volatile uint32_t*)0x40021024)
#define R32_GPIOA_CFGLR (*(volatile uint32_t*)0x40010800)
#define R32_GPIOA_CFGHR (*(volatile uint32_t*)0x40010804)
#define R32_GPIOA_BSHR (*(volatile uint32_t*)0x40010810)
// USBD registers are within range to perform gp relaxation
// XXX wrong access size, but this doesn't seem to matter
extern volatile uint32_t R16_USBD_EPR[16];
extern volatile uint16_t R16_USBD_CNTR;
extern volatile uint16_t R16_USBD_ISTR;
extern volatile uint16_t R16_USBD_DADDR;
#define R32_FLASH_KEYR (*(volatile uint32_t*)0x40022004)
#define R32_FLASH_STATR (*(volatile uint32_t*)0x4002200C)
#define R32_FLASH_CTLR (*(volatile uint32_t*)0x40022010)
#define R32_FLASH_ADDR (*(volatile uint32_t*)0x40022014)
#define R32_FLASH_MODEKEYR (*(volatile uint32_t*)0x40022024)
#define R32_EXTEN_CTR (*(volatile uint32_t*)0x40023800)
#define STK_CTLR (*(volatile uint32_t*)0xE000F000)
#define STK_SR (*(volatile uint32_t*)0xE000F004)
#define STK_CMPLR (*(volatile uint32_t*)0xE000F010)
#define PFIC_CFGR (*(volatile uint32_t*)0xE000E048)
// Everything in this code is controlled by various state machines
// USB device stack control transfer state handling
// state_x[7:0] = addr
#define STATE_SET_ADDR 0x00
// state_x[15:8] = byte pos
// state_x[7:0] = bytes left
#define STATE_GET_DESC 0x01
#define STATE_GET_STR_SERIAL 0x02
#define STATE_CTRL_SIMPLE_IN 0x03
// USB MSC state handling
// msc_state contains both the state *and* request sense:
// state[31:24] = additional sense code
// state[23:20] = sense key
// state[7:0] = state
#define STATE_WANT_CBW 0x00
#define STATE_SENT_CSW 0x01
#define STATE_SENT_CSW_REBOOT 0x02
#define STATE_SENT_DATA_IN 0x03
// state[10:8] = sector fragment
#define STATE_SEND_MORE_READ 0x04
// state[12] = uf2 good so far
// state[10:8] = sector fragment
#define STATE_WAITING_FOR_WRITE 0x05
// Because this code is running completely RAM-less
// (other than explicit usage of USBD RAM),
// there is no stack, and thus no space to spill registers.
// WARNING: This means that only one level of
// non-inline subroutine calls are possible.
// This is checked by manually searching the assembly listing
// for access to the sp register.
// Just to be entirely sure, we've marked some functions as
// __attribute__((always_inline)). Other functions are
// explicitly *outlined* in order to save code size.
// This is not entirely stable and relies on hand-checking
// the generated assembly.
__attribute__((always_inline)) static inline void set_ep_mode(uint32_t epidx, uint32_t epaddr, uint32_t eptype, uint32_t stat_rx, uint32_t stat_tx, uint32_t xtra, int clear_dtog) {
// The way the shifts are written has been tweaked
// to generate smaller code than some possible alternatives.
// "xtra" is used solely to set bit8 (EP_KIND, STATUS_OUT).
// This is an optimization present in the core to simplify
// CONTROL IN transactions, i.e.:
// 1. SETUP (H->D)
// 2. IN (D->H)
// 3. ZLP OUT (H->D) for status signaling
// The bit optimizes step 3 s.t. it is not necessary to
// check that a ZLP specifically was received.
uint32_t val = R16_USBD_EPR[epidx];
uint32_t cur_stats;
if (clear_dtog)
cur_stats = val & 0x7070;
else
cur_stats = val & 0x3030;
uint32_t want_stats = (stat_rx << 12) | (stat_tx) << 4;
R16_USBD_EPR[epidx] = epaddr | (eptype << 9) | xtra | (cur_stats ^ want_stats);
}
// Outlining these functions saves code size
static void set_ep0_ack_in() {
set_ep_mode(0, 0, USB_EPTYPE_CONTROL, USB_STAT_STALL, USB_STAT_ACK, 0, 0);
}
static void set_ep0_stall() {
set_ep_mode(0, 0, USB_EPTYPE_CONTROL, USB_STAT_STALL, USB_STAT_STALL, 0, 0);
}
static void set_ep1_ack_in() {
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_STALL, USB_STAT_ACK, 0, 0);
}
static void set_ep1_ack_out() {
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_ACK, USB_STAT_STALL, 0, 0);
}
__attribute__((always_inline)) static inline uint32_t min(uint32_t a, uint32_t b) {
if (a <= b)
return a;
return b;
}
const uint8_t MODE_SENSE_6[4] __attribute__((aligned(2))) = {
0x03, 0x00, 0x00, 0x00,
};
const uint8_t MODE_SENSE_10[8] __attribute__((aligned(2))) = {
0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const uint8_t READ_FORMAT_CAPACITY[12] __attribute__((aligned(2))) = {
0x00, 0x00, 0x00,
0x08,
0x00, 0x00, 0x40, 0x00, // 8 MiB
0x02, // formatted media
0x00, 0x02, 0x00, // 512 bytes / sector
};
const uint8_t READ_CAPACITY[8] __attribute__((aligned(2))) = {
0x00, 0x00, 0x3f, 0xff, // 8 MiB
0x00, 0x00, 0x02, 0x00,
};
static void ep1_send_hardcoded_response(const uint16_t *data, uint32_t len) {
for (uint32_t i = 0; i < (len + 1) / 2; i++)
USB_EP1_IN[i] = data[i];
USB_DESCS[1].count_tx = len;
set_ep1_ack_in();
}
static void synthesize_block(uint32_t block, uint32_t piece) {
if (block == 0 || block == 65 || block == 66 || block == 67) {
const uint16_t *sector_ptr;
uint32_t sector_sz_16bits;
if (block == 0) {
sector_ptr = (uint16_t*)BOOT_SECTOR;
sector_sz_16bits = (sizeof(BOOT_SECTOR) + 1) / 2;
} else if (block == 65) {
sector_ptr = (uint16_t*)ROOT_DIR;
sector_sz_16bits = (sizeof(ROOT_DIR) + 1) / 2;
} else if (block == 66) {
sector_ptr = (uint16_t*)INFO_UF2;
sector_sz_16bits = (sizeof(INFO_UF2) + 1) / 2;
} else if (block == 67) {
sector_ptr = (uint16_t*)INDEX_HTM;
sector_sz_16bits = (sizeof(INDEX_HTM) + 1) / 2;
}
uint32_t cur_offset_16bits = piece * 32;
int usbofs, secofs;
for (usbofs = 0, secofs = cur_offset_16bits; usbofs < 32 && secofs < sector_sz_16bits; usbofs++, secofs++)
USB_EP1_IN[usbofs] = sector_ptr[secofs];
for (; usbofs < 32; usbofs++)
USB_EP1_IN[usbofs] = 0;
if (block == 0 && piece == 7)
USB_EP1_IN[31] = 0xaa55;
} else if (block == 1 && piece == 0) {
// special FAT entries
USB_EP1_IN[0] = 0xfff8;
USB_EP1_IN[1] = 0xffff;
// one cluster each for the UF2 files
USB_EP1_IN[2] = 0xfff8;
USB_EP1_IN[3] = 0xfff8;
for (int i = 4; i < 32; i++)
USB_EP1_IN[i] = 0;
} else {
for (int i = 0; i < 32; i++)
USB_EP1_IN[i] = 0;
}
USB_DESCS[1].count_tx = 64;
set_ep1_ack_in();
}
static void make_msc_csw(uint32_t dCSWTag, uint32_t error) {
USB_EP1_IN[0] = 0x5355;
USB_EP1_IN[1] = 0x5342;
USB_EP1_IN[2] = dCSWTag;
USB_EP1_IN[3] = dCSWTag >> 16;
USB_EP1_IN[4] = 0; // xxx this isn't very right
USB_EP1_IN[5] = 0;
USB_EP1_IN[6] = error;
USB_DESCS[1].count_tx = 13;
set_ep1_ack_in();
}
__attribute__((naked)) int main(void) {
// Make sure this stuff is enabled
// (not every startup.S code path activates them)
R32_RCC_APB1PCENR |= (1 << 27) | (1 << 28);
R32_PWR_CTLR |= 1 << 8;
R16_BKP_DATAR10 = 0;
// PLL setup: system clock 96 MHz
R32_EXTEN_CTR |= (1 << 4); // sneaky div2
R32_RCC_CFGR0 = (R32_RCC_CFGR0 & ~0xff0000) | (0b01101000 << 16);
R32_RCC_CTLR |= (1 << 24);
while (!(R32_RCC_CTLR & (1 << 25))) {}
R32_RCC_CFGR0 = (R32_RCC_CFGR0 & ~0b11) | 0b10;
while ((R32_RCC_CFGR0 & 0b1100) != 0b1000) {}
// As recommended in the manual, output low on these pins before enabling USB
R32_RCC_APB2PCENR |= (1 << 2);
R32_GPIOA_CFGHR = (R32_GPIOA_CFGHR & ~(0xff << 12)) | (0b00100010 << 12);
R32_GPIOA_BSHR = (1 << 27) | (1 << 28);
// Enable USB
R32_RCC_APB1PCENR |= (1 << 23);
R16_USBD_CNTR = 1;
USB_DESCS[0].addr_tx = 0x30 / 2;
USB_DESCS[0].addr_rx = 0x20 / 2;
USB_DESCS[0].count_rx = (8 << 10);
USB_DESCS[1].addr_tx = 0xc0 / 2;
USB_DESCS[1].addr_rx = 0x40 / 2;
USB_DESCS[1].count_rx = (2 << 10) | (1 << 15);
// XXX because the table is at offset 0 don't bother writing this
// Attach USB
R16_USBD_CNTR = 0;
R32_EXTEN_CTR |= (1 << 1);
CTRL_XFER_STATE = 0;
ACTIVE_CONFIG = 0;
const uint8_t *outputting_desc = 0;
CTRL_XFER_DESC_SZ = 0;
ADDRESS_HI = 0;
uint32_t msc_state = STATE_WANT_CBW;
UF2_GOT_BLOCKS[AUTO_BOOT_BITMAP_NUM_HWORDS - 1] = 0;
// FIXME: we've tried activating USB interrupts and using WFI/WFE
// and haven't managed to get it to work properly with a
// "still mostly polling model"
// (i.e. WFE, then read interrupt status registers, then loop)
// We haven't successfully investigated why.
// We also haven't managed to write the code s.t.
// variables (outputting_desc and msc_state) are properly stored
// in registers when using a separate "IRQ handler" function
while (1) {
uint32_t usb_int_status = R16_USBD_ISTR;
if (usb_int_status & (1 << 10)) {
// RESET
// set all to STALL, SETUP will come in nonetheless
set_ep0_stall();
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_DISABLED, USB_STAT_DISABLED, 0, 0);
R16_USBD_DADDR = 0x80;
R16_USBD_CNTR = 0;
} else if (usb_int_status & (1 << 11)) {
// suspend
// FIXME: note that this polling code makes the core exceed
// suspend current limits
// (the "Suspend Current Limit Changes ECN" says that 2.5 mA is allowed, but
// the CH32V203 datasheet says that supply current in run mode is somewhere
// between 3.7 mA to 7.1 mA at a F_{HCLK} of 96 MHz. Ports are usually not
// sensitive to this, it merely breaks the rules and wastes battery life.)
R16_USBD_CNTR |= 1 << 3;
R16_USBD_CNTR |= 1 << 2;
} else if (usb_int_status & (1 << 12)) {
// wakeup
R16_USBD_CNTR = 0;
} else if (usb_int_status & (1 << 15)) {
uint32_t epidx = usb_int_status & 0xf;
uint32_t ep_status = R16_USBD_EPR[epidx];
if ((ep_status & (1 << 15)) && (epidx == 0)) {
if (ep_status & (1 << 11)) {
// setup, STAT_RX and STAT_TX both now NAK
uint16_t bRequest_bmRequestType = USB_EP0_OUT[0];
uint32_t wLength = USB_EP0_OUT[3];
if (bRequest_bmRequestType == 0x0080 || bRequest_bmRequestType == 0x0081) {
// GET_STATUS
USB_EP0_IN[0] = 0;
USB_DESCS[0].count_tx = min(2, wLength);
CTRL_XFER_STATE = STATE_CTRL_SIMPLE_IN;
set_ep0_ack_in();
} else if (bRequest_bmRequestType == 0x0102) {
// CLEAR_FEATURE
// XXX how is this supposed to work?
// Not implementing this will cause subtle breakage
// when an unsupported SCSI command is sent.
uint32_t wIndex = USB_EP0_OUT[2];
if (wIndex == 0x81) {
uint32_t dCSWTag = CSWTAG_LO | (CSWTAG_HI << 16);
make_msc_csw(dCSWTag, 1);
msc_state = (msc_state & 0xffffff00) | STATE_SENT_CSW;
set_ep0_ack_in();
} else if (wIndex == 0x01) {
set_ep1_ack_out();
msc_state = (msc_state & 0xffffff00) | STATE_WANT_CBW;
set_ep0_ack_in();
} else {
set_ep0_stall();
}
} else if (bRequest_bmRequestType == 0x0500) {
// SET_ADDRESS
CTRL_XFER_STATE_X = USB_EP0_OUT[1];
CTRL_XFER_STATE = STATE_SET_ADDR;
USB_DESCS[0].count_tx = 0;
set_ep0_ack_in();
} else if (bRequest_bmRequestType == 0x0680) {
// GET_DESCRIPTOR
uint32_t wValue = USB_EP0_OUT[1];
if (wValue == 0x0100 || wValue == 0x0200 || wValue == 0x0301 || wValue == 0x0302) {
if (wValue == 0x0100) {
outputting_desc = USB_DEV_DESC;
CTRL_XFER_DESC_SZ = sizeof(USB_DEV_DESC);
} else if (wValue == 0x0200) {
outputting_desc = USB_CONF_DESC;
CTRL_XFER_DESC_SZ = sizeof(USB_CONF_DESC);
} else if (wValue == 0x0301) {
outputting_desc = (uint8_t*)USB_MANUF;
CTRL_XFER_DESC_SZ = sizeof(USB_MANUF);
} else if (wValue == 0x0302) {
outputting_desc = (uint8_t*)USB_PRODUCT;
CTRL_XFER_DESC_SZ = sizeof(USB_PRODUCT);
}
for (int i = 0; i < 8; i+=2)
USB_EP0_IN[i / 2] = *((uint16_t*)(outputting_desc + i));
if (wLength < 8) {
USB_DESCS[0].count_tx = wLength;
CTRL_XFER_STATE_X = 0;
CTRL_XFER_STATE = STATE_GET_DESC;
} else {
USB_DESCS[0].count_tx = 8;
CTRL_XFER_STATE_X = (8 << 8) | (wLength - 8);
CTRL_XFER_STATE = STATE_GET_DESC;
}
set_ep0_ack_in();
} else if (wValue == 0x0300) {
// string LANGIDs
USB_EP0_IN[0] = 0x0304;
USB_EP0_IN[1] = 0x0409;
USB_DESCS[0].count_tx = min(4, wLength);
CTRL_XFER_STATE = STATE_CTRL_SIMPLE_IN;
set_ep0_ack_in();
} else if (wValue == 0x0303) {
// Note: having a serial number is mandatory for USB MSC
// (and is generally nice to have)
USB_EP0_IN[0] = 0x0300 | (24 * 2 + 2);
USB_EP0_IN[1] = HEXLUT[ESIG_UNIID(0) >> 4];
USB_EP0_IN[2] = HEXLUT[ESIG_UNIID(0) & 0xf];
USB_EP0_IN[3] = HEXLUT[ESIG_UNIID(1) >> 4];
if (wLength < 8) {
USB_DESCS[0].count_tx = wLength;
CTRL_XFER_STATE_X = 0;
CTRL_XFER_STATE = STATE_GET_STR_SERIAL;
} else {
USB_DESCS[0].count_tx = 8;
CTRL_XFER_STATE_X = (8 << 8) | (wLength - 8);
CTRL_XFER_STATE = STATE_GET_STR_SERIAL;
}
set_ep0_ack_in();
} else {
// bad descriptor
set_ep0_stall();
}
} else if (bRequest_bmRequestType == 0x0880) {
// GET_CONFIGURATION
USB_EP0_IN[0] = ACTIVE_CONFIG;
USB_DESCS[0].count_tx = min(1, wLength);
CTRL_XFER_STATE = STATE_CTRL_SIMPLE_IN;
set_ep0_ack_in();
} else if (bRequest_bmRequestType == 0x0900) {
// SET_CONFIGURATION
uint32_t wValue = USB_EP0_OUT[1];
if (wValue == 0 || wValue == 1) {
ACTIVE_CONFIG = wValue;
if (wValue) {
// activate, allow OUT
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_ACK, USB_STAT_STALL, 0, 1);
msc_state = STATE_WANT_CBW;
} else {
// deactivate
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_DISABLED, USB_STAT_DISABLED, 0, 0);
}
USB_DESCS[0].count_tx = 0;
set_ep0_ack_in();
} else {
set_ep0_stall();
}
} else if (bRequest_bmRequestType == 0x0A81) {
// GET_INTERFACE
USB_EP0_IN[0] = 0;
USB_DESCS[0].count_tx = min(1, wLength);
CTRL_XFER_STATE = STATE_CTRL_SIMPLE_IN;
set_ep0_ack_in();
} else {
// unknown
set_ep0_stall();
}
} else {
// back to stall for everything, expect SETUP
set_ep0_stall();
}
} else if ((ep_status & (1 << 7)) && (epidx == 0)) {
switch (CTRL_XFER_STATE) {
case STATE_SET_ADDR:
R16_USBD_DADDR = 0x80 | (CTRL_XFER_STATE_X & 0x7f);
// back to stall for everything, expect SETUP
set_ep0_stall();
break;
case STATE_GET_DESC:
case STATE_GET_STR_SERIAL:
{
uint32_t x = CTRL_XFER_STATE_X;
uint32_t byte_pos = (x >> 8) & 0xff;
uint32_t bytes_left = x & 0xff;
if (bytes_left == 0) {
// ACK for OUT ZLP, STALL for IN
set_ep_mode(0, 0, USB_EPTYPE_CONTROL, USB_STAT_ACK, USB_STAT_STALL, 1 << 8, 0);
} else {
uint32_t bytes = bytes_left;
if (bytes > 8) bytes = 8;
if (CTRL_XFER_STATE == STATE_GET_DESC) {
if (byte_pos + bytes > CTRL_XFER_DESC_SZ)
bytes = CTRL_XFER_DESC_SZ - byte_pos;
for (int i = 0; i < bytes; i+=2)
USB_EP0_IN[i / 2] = *((uint16_t*)(outputting_desc + byte_pos + i));
} else {
uint32_t serial_no_pos = (byte_pos - 2) / 2;
uint32_t ascii_bytes = (bytes + 1) / 2;
if (serial_no_pos + ascii_bytes > 24) {
ascii_bytes = 24 - serial_no_pos;
// xxx this should be fine, reads 8 byte chunks
bytes = ascii_bytes * 2;
}
for (int i = 0; i < ascii_bytes; i++)
USB_EP0_IN[i] = HEXLUT[(ESIG_UNIID((serial_no_pos + i) / 2) >> ((1 - ((serial_no_pos + i) % 2)) * 4)) & 0xf];
}
if (bytes < 8) {
USB_DESCS[0].count_tx = bytes;
CTRL_XFER_STATE_X = 0;
} else {
USB_DESCS[0].count_tx = 8;
CTRL_XFER_STATE_X = ((byte_pos + 8) << 8) | (bytes_left - 8);
}
set_ep0_ack_in();
}
}
break;
case STATE_CTRL_SIMPLE_IN:
// ACK for OUT ZLP, STALL for IN
set_ep_mode(0, 0, USB_EPTYPE_CONTROL, USB_STAT_ACK, USB_STAT_STALL, 1 << 8, 0);
break;
}
} else if ((ep_status & (1 << 15)) && (epidx == 1)) {
// ep 1 out
switch (msc_state & 0xff) {
case STATE_WANT_CBW:
if ((USB_DESCS[1].count_rx & 0x3f) != 0x1f || USB_EP1_OUT[0] != 0x5355 || USB_EP1_OUT[1] != 0x4342) {
set_ep1_ack_out();
} else {
CSWTAG_LO = USB_EP1_OUT[2];
CSWTAG_HI = USB_EP1_OUT[3];
uint32_t dCSWTag = CSWTAG_LO | (CSWTAG_HI << 16);
uint32_t dCBWDataTransferLength = USB_EP1_OUT[4] | (USB_EP1_OUT[5] << 16);
uint32_t operation_code = USB_EP1_OUT[7] >> 8;
switch (operation_code) {
case 0x00:
// test unit ready
make_msc_csw(dCSWTag, 0);
msc_state = STATE_SENT_CSW;
break;
case 0x03:
// request sense
USB_EP1_IN[0] = 0x0070;
USB_EP1_IN[1] = (msc_state >> 20) & 0xf;
USB_EP1_IN[2] = 0;
USB_EP1_IN[3] = 0;
USB_EP1_IN[4] = 0;
USB_EP1_IN[5] = 0;
USB_EP1_IN[6] = (msc_state >> 24) & 0xff;
USB_EP1_IN[7] = 0;
USB_EP1_IN[8] = 0;
USB_DESCS[1].count_tx = 18;
set_ep1_ack_in();
msc_state = STATE_SENT_DATA_IN;
break;
case 0x12:
// inquiry
if (USB_EP1_OUT[8] == 0) {
ep1_send_hardcoded_response((uint16_t*)INQUIRY_RESPONSE, sizeof(INQUIRY_RESPONSE));
msc_state = STATE_SENT_DATA_IN;
break;
}
msc_state = STATE_SENT_CSW | (5 << 20) | (0x24 << 24);
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_STALL, USB_STAT_STALL, 0, 0);
break;
case 0x1a:
// mode sense (6)
ep1_send_hardcoded_response((uint16_t*)MODE_SENSE_6, sizeof(MODE_SENSE_6));
msc_state = STATE_SENT_DATA_IN;
break;
case 0x1b:
// start/stop unit
uint32_t param = USB_EP1_OUT[9] >> 8;
make_msc_csw(dCSWTag, 0);
// this flag is used by "eject" in Windows Explorer
// (*not* safely remove hardware though,
// and also not "drag to trash" in macOS)
if (param == 0x02)
msc_state = STATE_SENT_CSW_REBOOT;
else
msc_state = STATE_SENT_CSW;
break;
case 0x23:
// read format capacity
// without this command, Windows won't detect the drive
// (but Linux and macOS will)
ep1_send_hardcoded_response((uint16_t*)READ_FORMAT_CAPACITY, sizeof(READ_FORMAT_CAPACITY));
msc_state = STATE_SENT_DATA_IN;
break;
case 0x5a:
// mode sense (10)
ep1_send_hardcoded_response((uint16_t*)MODE_SENSE_10, sizeof(MODE_SENSE_10));
msc_state = STATE_SENT_DATA_IN;
break;
case 0x25:
// READ CAPACITY (10)
// xxx don't bother checking the silly fields
ep1_send_hardcoded_response((uint16_t*)READ_CAPACITY, sizeof(READ_CAPACITY));
msc_state = STATE_SENT_DATA_IN;
break;
case 0x28:
case 0x2a:
{
// READ (10) / WRITE (10)
// xxx also don't bother checking the flags
// @ 16: flags lba3
// @ 18: lba2 lba1
// @ 20: lba0 group
// @ 22: len1 len0
uint32_t lba = (USB_EP1_OUT[8] << 16) & 0xff000000;
uint32_t tmp = USB_EP1_OUT[9];
lba |= (tmp & 0xff) << 24;
lba |= (tmp & 0xff00);
tmp = USB_EP1_OUT[10];
lba |= (tmp & 0xff);
tmp = USB_EP1_OUT[11];
uint32_t blocks = (tmp >> 8) | ((tmp & 0xff) << 8);
// The following two checks are out of paranoia
// Hosts don't seem to send this crap
if (blocks > 0x4000 || lba >= 0x4000 || (blocks + lba) > 0x4000) {
msc_state = STATE_SENT_CSW | (5 << 20) | (0x24 << 24);
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_STALL, USB_STAT_STALL, 0, 0);
break;
}
if (blocks == 0) {
make_msc_csw(dCSWTag, 0);
msc_state = STATE_SENT_CSW;
break;
}
SCSI_XFER_BLK_LEFT = blocks;
if (operation_code == 0x28) {
// READ
SCSI_XFER_CUR_LBA = lba;
synthesize_block(lba, 0);
msc_state = STATE_SEND_MORE_READ;
} else {
// WRITE
// Note: bug that took forever to track down: need to set up IN
// for *NAK* and *not* STALL.
// Hosts seem to do the BULK IN asynchronously with sending all of the
// data on the BULK OUT pipe (which takes multiple USB packets to send).
// If the BULK IN (asynchronously) returns STALL,
// the host will assume the command failed rather than "not done yet".
// This will trigger mysterious bugs:
// * Linux will fail using dd oflag=direct or with too much data written,
// but you will win the race condition for small transfers.
// * Windows will take a long time to detect and will only work
// a fraction of the time (USBPcap shows a large number of device resets)
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_ACK, USB_STAT_NAK, 0, 0);
msc_state = STATE_WAITING_FOR_WRITE;
}
break;
}
default:
// illegal command
msc_state = STATE_SENT_CSW | (5 << 20);
// Not checking the transfer length here will trigger mysterious bugs,
// especially on Windows. Windows will get upset when trying to read
// the *SCSI* serial number page using INQUIRY.
// Quirks that happen when trying to do data transfer and control plane ops
// via the same pipe...
if (dCBWDataTransferLength == 0) {
make_msc_csw(dCSWTag, 1);
} else {
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_STALL, USB_STAT_STALL, 0, 0);
}
break;
}
}
break;
case STATE_WAITING_FOR_WRITE:
uint32_t piece = (msc_state >> 8) & 0b111;
if (piece == 0) {
if ((USB_EP1_OUT[0] == 0x4655) &&
(USB_EP1_OUT[1] == 0x0A32) &&
(USB_EP1_OUT[2] == 0x5157) &&
(USB_EP1_OUT[3] == 0x9E5D)) {
// UF2 magic okay
uint32_t flags_lo = USB_EP1_OUT[4];
uint32_t address_lo = USB_EP1_OUT[6];
uint32_t address_hi = USB_EP1_OUT[7];
uint32_t bytes_lo = USB_EP1_OUT[8];
uint32_t bytes_hi = USB_EP1_OUT[9];
uint32_t familyid = USB_EP1_OUT[14] | (USB_EP1_OUT[15] << 16);
uint32_t blocknum_hi = USB_EP1_OUT[11];
uint32_t totblocks_hi = USB_EP1_OUT[13];
if (bytes_lo == 256 && bytes_hi == 0 && (address_lo & 0xff) == 0 && blocknum_hi == 0 && totblocks_hi == 0) {
if (flags_lo & (0x2000) && familyid == FAMILY_ID) {
// uf2 good so far!
// *preliminary* bounds check
if ((!(flags_lo & 1) && (address_hi >> 8) == 0x08) || ((flags_lo & 1) && (address_hi >> 8) == 0x20)) {
ADDRESS_LO = address_lo;
ADDRESS_HI = address_hi;
BLOCKNUM_LO = USB_EP1_OUT[10];
TOTBLOCKS_LO = USB_EP1_OUT[12];
for (int i = 0; i < 16; i++)
USB_SECTOR_STASH[i] = USB_EP1_OUT[16 + i];
msc_state += 0x1000;
}
}
}
}
} else if (piece >= 1 && piece <= 4) {
for (int i = 0; i < (piece != 4 ? 32 : 16); i++)
USB_SECTOR_STASH[16 + (piece - 1) * 32 + i] = USB_EP1_OUT[i];
}
if (piece != 7) {
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_ACK, USB_STAT_NAK, 0, 0);
msc_state += 0x100;
} else {
// full sector is done
if (msc_state & 0x1000) {
if (USB_EP1_OUT[30] == 0x6F30 && USB_EP1_OUT[31] == 0x0AB1) {
// uf2 all magics are good!
uint32_t address = ADDRESS_LO | (ADDRESS_HI << 16);
uint32_t blocknum = BLOCKNUM_LO;
uint32_t totblocks = TOTBLOCKS_LO;
if (UF2_GOT_BLOCKS[AUTO_BOOT_BITMAP_NUM_HWORDS - 1] & 0x8000) {
// not first uf2 block
UF2_GOT_BLOCKS[blocknum / 16] |= 1 << (blocknum % 16);
} else {
// first uf2 block, or too big
if (totblocks <= MAX_AUTO_BOOT_BLOCKS) {
for (int i = 0; i < AUTO_BOOT_BITMAP_NUM_HWORDS; i++)
UF2_GOT_BLOCKS[i] = 0;
for (int i = totblocks; i <= MAX_AUTO_BOOT_BLOCKS; i++) {
// deliberate off-by-1 to set high bit
UF2_GOT_BLOCKS[i / 16] |= 1 << (i % 16);
}
UF2_GOT_BLOCKS[blocknum / 16] |= 1 << (blocknum % 16);
}
}
if (address >= 0x08000000 + BOOTLOADER_RESERVED_SZ_BYTES &&
address <= 0x08000000 + THIS_CHIP_FLASH_MAX_SZ_BYTES - 256) {
R32_FLASH_KEYR = 0x45670123;
R32_FLASH_KEYR = 0xCDEF89AB;
R32_FLASH_MODEKEYR = 0x45670123;
R32_FLASH_MODEKEYR = 0xCDEF89AB;
R32_FLASH_CTLR = 1 << 17;
R32_FLASH_ADDR = address;
R32_FLASH_CTLR = (1 << 17) | (1 << 6);
while (R32_FLASH_STATR & 1) {}
R32_FLASH_CTLR = 1 << 16;
// Yes, we can program flash while running from it!
// (as long as we are in the "zero-wait" area which we are)
for (int i = 0; i < 64; i++) {
volatile uint32_t *addr = (volatile uint32_t *)(address + i * 4);
uint32_t val = USB_SECTOR_STASH[i * 2] | (USB_SECTOR_STASH[i * 2 + 1] << 16);
*addr = val;
while (R32_FLASH_STATR & 2) {}
}
R32_FLASH_CTLR = (1 << 16) | (1 << 21);
while (R32_FLASH_STATR & 1) {}
R32_FLASH_CTLR = (1 << 15) | (1 << 7);
}
if (address >= 0x20000000 && address <= 0x20000000 + THIS_CHIP_RAM_MAX_SZ_BYTES - 256) {
for (int i = 0; i < 64; i++) {
volatile uint32_t *addr = (volatile uint32_t *)(address + i * 4);
uint32_t val = USB_SECTOR_STASH[i * 2] | (USB_SECTOR_STASH[i * 2 + 1] << 16);
*addr = val;
}
}
}
}
if (SCSI_XFER_BLK_LEFT == 1) {
uint32_t dCSWTag = CSWTAG_LO | (CSWTAG_HI << 16);
make_msc_csw(dCSWTag, 0);
uint32_t all_blocks = 0xffff;
for (int i = 0; i < AUTO_BOOT_BITMAP_NUM_HWORDS; i++)
all_blocks &= UF2_GOT_BLOCKS[i];
if (all_blocks == 0xffff)
msc_state = STATE_SENT_CSW_REBOOT;
else
msc_state = STATE_SENT_CSW;
} else {
SCSI_XFER_BLK_LEFT--;
set_ep_mode(1, 1, USB_EPTYPE_BULK, USB_STAT_ACK, USB_STAT_NAK, 0, 0);
msc_state = STATE_WAITING_FOR_WRITE;
}
}
break;
}
} else if ((ep_status & (1 << 7)) && (epidx == 1)) {
// ep 1 in
uint32_t dCSWTag = CSWTAG_LO | (CSWTAG_HI << 16);
switch (msc_state & 0xff) {
case STATE_SENT_CSW:
set_ep1_ack_out();
msc_state = (msc_state & 0xffffff00) | STATE_WANT_CBW;
break;
case STATE_SENT_CSW_REBOOT:
// Microsoft's bootloader claims we need to do this
// (but we didn't personally test it)
STK_CMPLR = (50 /* ms */ * 12000 /* assume 96 MHz system clock, div8 */);
STK_CTLR = 0b111000;
STK_CTLR = 0b111001;
while (!(STK_SR & 1)) {}
STK_CTLR = 0;
STK_SR = 0;
R16_USBD_CNTR = 0b11;
R32_EXTEN_CTR &= ~(1 << 1);
if (ADDRESS_HI >> 8 == 0x20) {