forked from pyocd/pyOCD
/
cortex_m.py
1402 lines (1174 loc) · 54.2 KB
/
cortex_m.py
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
# pyOCD debugger
# Copyright (c) 2006-2020 Arm Limited
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from time import (time, sleep)
from xml.etree.ElementTree import (Element, SubElement, tostring)
from ..core.target import Target
from ..core import exceptions
from ..utility import (cmdline, conversion, timeout)
from ..utility.notification import Notification
from .component import CoreSightCoreComponent
from .fpb import FPB
from .dwt import DWT
from .core_ids import (CORE_TYPE_NAME, CoreArchitecture)
from ..debug.breakpoints.manager import BreakpointManager
from ..debug.breakpoints.software import SoftwareBreakpointProvider
LOG = logging.getLogger(__name__)
## @brief Map from register name to DCRSR register index.
#
# The CONTROL, FAULTMASK, BASEPRI, and PRIMASK registers are special in that they share the
# same DCRSR register index and are returned as a single value. In this dict, these registers
# have negative values to signal to the register read/write functions that special handling
# is necessary. The values are the byte number containing the register value, plus 1 and then
# negated. So -1 means a mask of 0xff, -2 is 0xff00, and so on. The actual DCRSR register index
# for these combined registers has the key of 'cfbp'.
#
# XPSR is always read in its entirety via the debug interface, but we also provide
# aliases for the submasks APSR, IPSR and EPSR. These are encoded as 0x10000 plus 3 lower bits
# indicating which parts of the PSR we're interested in - encoded the same way as MRS's SYSm.
# (Note that 'XPSR' continues to denote the raw 32 bits of the register, as per previous versions,
# not the union of the three APSR+IPSR+EPSR masks which don't cover the entire register).
#
# The double-precision floating point registers (D0-D15) are composed of two single-precision
# floating point registers (S0-S31). The value for double-precision registers in this dict is
# the negated value of the first associated single-precision register.
CORE_REGISTER = {
'r0': 0,
'r1': 1,
'r2': 2,
'r3': 3,
'r4': 4,
'r5': 5,
'r6': 6,
'r7': 7,
'r8': 8,
'r9': 9,
'r10': 10,
'r11': 11,
'r12': 12,
'sp': 13,
'r13': 13,
'lr': 14,
'r14': 14,
'pc': 15,
'r15': 15,
'xpsr': 16,
'apsr': 0x10000,
'iapsr': 0x10001,
'eapsr': 0x10002,
'ipsr': 0x10005,
'epsr': 0x10006,
'iepsr': 0x10007,
'msp': 17,
'psp': 18,
'cfbp': 20,
'control':-4,
'faultmask':-3,
'basepri':-2,
'primask':-1,
'fpscr': 33,
's0': 0x40,
's1': 0x41,
's2': 0x42,
's3': 0x43,
's4': 0x44,
's5': 0x45,
's6': 0x46,
's7': 0x47,
's8': 0x48,
's9': 0x49,
's10': 0x4a,
's11': 0x4b,
's12': 0x4c,
's13': 0x4d,
's14': 0x4e,
's15': 0x4f,
's16': 0x50,
's17': 0x51,
's18': 0x52,
's19': 0x53,
's20': 0x54,
's21': 0x55,
's22': 0x56,
's23': 0x57,
's24': 0x58,
's25': 0x59,
's26': 0x5a,
's27': 0x5b,
's28': 0x5c,
's29': 0x5d,
's30': 0x5e,
's31': 0x5f,
'd0': -0x40,
'd1': -0x42,
'd2': -0x44,
'd3': -0x46,
'd4': -0x48,
'd5': -0x4a,
'd6': -0x4c,
'd7': -0x4e,
'd8': -0x50,
'd9': -0x52,
'd10': -0x54,
'd11': -0x56,
'd12': -0x58,
'd13': -0x5a,
'd14': -0x5c,
'd15': -0x5e,
}
def register_name_to_index(reg):
if isinstance(reg, str):
try:
reg = CORE_REGISTER[reg.lower()]
except KeyError:
raise KeyError('cannot find %s core register' % reg)
return reg
def is_float_register(index):
return is_single_float_register(index) or is_double_float_register(index)
def is_single_float_register(index):
"""! @brief Returns true for registers holding single-precision float values"""
return 0x40 <= index <= 0x5f
def is_double_float_register(index):
"""! Returns true for registers holding double-precision float values"""
return -0x40 >= index > -0x60
def is_fpu_register(index):
return index == 33 or is_single_float_register(index) or is_double_float_register(index)
def is_cfbp_subregister(index):
return -4 <= index <= -1
def is_psr_subregister(index):
return 0x10000 <= index <= 0x10007
def sysm_to_psr_mask(sysm):
"""! Generate a PSR mask based on bottom 3 bits of a MRS SYSm value"""
mask = 0
if (sysm & 1) != 0:
mask |= CortexM.IPSR_MASK
if (sysm & 2) != 0:
mask |= CortexM.EPSR_MASK
if (sysm & 4) == 0:
mask |= CortexM.APSR_MASK
return mask
class CortexM(Target, CoreSightCoreComponent):
"""! @brief CoreSight component for a v6-M or v7-M Cortex-M core.
This class has basic functions to access a Cortex-M core:
- init
- read/write memory
- read/write core registers
- set/remove hardware breakpoints
"""
# Program Status Register
APSR_MASK = 0xF80F0000
EPSR_MASK = 0x0700FC00
IPSR_MASK = 0x000001FF
# Thumb bit in XPSR.
XPSR_THUMB = 0x01000000
# Control Register
CONTROL_FPCA = (1 << 2)
CONTROL_SPSEL = (1 << 1)
CONTROL_nPRIV = (1 << 0)
# Debug Fault Status Register
DFSR = 0xE000ED30
DFSR_EXTERNAL = (1 << 4)
DFSR_VCATCH = (1 << 3)
DFSR_DWTTRAP = (1 << 2)
DFSR_BKPT = (1 << 1)
DFSR_HALTED = (1 << 0)
# Debug Exception and Monitor Control Register
DEMCR = 0xE000EDFC
# DWTENA in armv6 architecture reference manual
DEMCR_TRCENA = (1 << 24)
DEMCR_VC_SFERR = (1 << 11)
DEMCR_VC_HARDERR = (1 << 10)
DEMCR_VC_INTERR = (1 << 9)
DEMCR_VC_BUSERR = (1 << 8)
DEMCR_VC_STATERR = (1 << 7)
DEMCR_VC_CHKERR = (1 << 6)
DEMCR_VC_NOCPERR = (1 << 5)
DEMCR_VC_MMERR = (1 << 4)
DEMCR_VC_CORERESET = (1 << 0)
# CPUID Register
CPUID = 0xE000ED00
# CPUID masks
CPUID_IMPLEMENTER_MASK = 0xff000000
CPUID_IMPLEMENTER_POS = 24
CPUID_VARIANT_MASK = 0x00f00000
CPUID_VARIANT_POS = 20
CPUID_ARCHITECTURE_MASK = 0x000f0000
CPUID_ARCHITECTURE_POS = 16
CPUID_PARTNO_MASK = 0x0000fff0
CPUID_PARTNO_POS = 4
CPUID_REVISION_MASK = 0x0000000f
CPUID_REVISION_POS = 0
CPUID_IMPLEMENTER_ARM = 0x41
ARMv6M = 0xC
ARMv7M = 0xF
# Debug Core Register Selector Register
DCRSR = 0xE000EDF4
DCRSR_REGWnR = (1 << 16)
DCRSR_REGSEL = 0x1F
# Debug Halting Control and Status Register
DHCSR = 0xE000EDF0
C_DEBUGEN = (1 << 0)
C_HALT = (1 << 1)
C_STEP = (1 << 2)
C_MASKINTS = (1 << 3)
C_SNAPSTALL = (1 << 5)
S_REGRDY = (1 << 16)
S_HALT = (1 << 17)
S_SLEEP = (1 << 18)
S_LOCKUP = (1 << 19)
S_RETIRE_ST = (1 << 24)
S_RESET_ST = (1 << 25)
# Debug Core Register Data Register
DCRDR = 0xE000EDF8
# Coprocessor Access Control Register
CPACR = 0xE000ED88
CPACR_CP10_CP11_MASK = (3 << 20) | (3 << 22)
# Interrupt Control and State Register
ICSR = 0xE000ED04
ICSR_PENDSVCLR = (1 << 27)
ICSR_PENDSTCLR = (1 << 25)
VTOR = 0xE000ED08
SCR = 0xE000ED10
SHPR1 = 0xE000ED18
SHPR2 = 0xE000ED1C
SHPR3 = 0xE000ED20
SHCSR = 0xE000ED24
FPCCR = 0xE000EF34
FPCAR = 0xE000EF38
FPDSCR = 0xE000EF3C
ICTR = 0xE000E004
NVIC_AIRCR = (0xE000ED0C)
NVIC_AIRCR_VECTKEY = (0x5FA << 16)
NVIC_AIRCR_VECTRESET = (1 << 0)
NVIC_AIRCR_VECTCLRACTIVE = (1 << 1)
NVIC_AIRCR_SYSRESETREQ = (1 << 2)
NVIC_AIRCR_PRIGROUP_MASK = 0x700
NVIC_AIRCR_PRIGROUP_SHIFT = 8
NVIC_ICER0 = 0xE000E180 # NVIC Clear-Enable Register 0
NVIC_ICPR0 = 0xE000E280 # NVIC Clear-Pending Register 0
NVIC_IPR0 = 0xE000E400 # NVIC Interrupt Priority Register 0
SYSTICK_CSR = 0xE000E010
DBGKEY = (0xA05F << 16)
# Media and FP Feature Register 0
MVFR0 = 0xE000EF40
MVFR0_DOUBLE_PRECISION_MASK = 0x00000f00
MVFR0_DOUBLE_PRECISION_SHIFT = 8
# Media and FP Feature Register 2
MVFR2 = 0xE000EF48
MVFR2_VFP_MISC_MASK = 0x000000f0
MVFR2_VFP_MISC_SHIFT = 4
class RegisterInfo(object):
def __init__(self, name, bitsize, reg_type, reg_group):
self.name = name
self.reg_num = CORE_REGISTER[name]
self.bitsize = bitsize
self.gdb_xml_attrib = {}
self.gdb_xml_attrib['name'] = str(name)
self.gdb_xml_attrib['bitsize'] = str(bitsize)
self.gdb_xml_attrib['type'] = str(reg_type)
self.gdb_xml_attrib['group'] = str(reg_group)
regs_general = [
# Name bitsize type group
RegisterInfo('r0', 32, 'int', 'general'),
RegisterInfo('r1', 32, 'int', 'general'),
RegisterInfo('r2', 32, 'int', 'general'),
RegisterInfo('r3', 32, 'int', 'general'),
RegisterInfo('r4', 32, 'int', 'general'),
RegisterInfo('r5', 32, 'int', 'general'),
RegisterInfo('r6', 32, 'int', 'general'),
RegisterInfo('r7', 32, 'int', 'general'),
RegisterInfo('r8', 32, 'int', 'general'),
RegisterInfo('r9', 32, 'int', 'general'),
RegisterInfo('r10', 32, 'int', 'general'),
RegisterInfo('r11', 32, 'int', 'general'),
RegisterInfo('r12', 32, 'int', 'general'),
RegisterInfo('sp', 32, 'data_ptr', 'general'),
RegisterInfo('lr', 32, 'int', 'general'),
RegisterInfo('pc', 32, 'code_ptr', 'general'),
RegisterInfo('msp', 32, 'data_ptr', 'system'),
RegisterInfo('psp', 32, 'data_ptr', 'system'),
RegisterInfo('primask', 32, 'int', 'system'),
]
regs_xpsr_control_plain = [
RegisterInfo('xpsr', 32, 'int', 'general'),
RegisterInfo('control', 32, 'int', 'system'),
]
regs_xpsr_control_fields = [
RegisterInfo('xpsr', 32, 'xpsr', 'general'),
RegisterInfo('control', 32, 'control', 'system'),
]
regs_system_armv7_only = [
# Name bitsize type group
RegisterInfo('basepri', 32, 'int', 'system'),
RegisterInfo('faultmask', 32, 'int', 'system'),
]
regs_float = [
# Name bitsize type group
RegisterInfo('fpscr', 32, 'int', 'float'),
RegisterInfo('d0' , 64, 'ieee_double', 'float'),
RegisterInfo('d1' , 64, 'ieee_double', 'float'),
RegisterInfo('d2' , 64, 'ieee_double', 'float'),
RegisterInfo('d3' , 64, 'ieee_double', 'float'),
RegisterInfo('d4' , 64, 'ieee_double', 'float'),
RegisterInfo('d5' , 64, 'ieee_double', 'float'),
RegisterInfo('d6' , 64, 'ieee_double', 'float'),
RegisterInfo('d7' , 64, 'ieee_double', 'float'),
RegisterInfo('d8' , 64, 'ieee_double', 'float'),
RegisterInfo('d9' , 64, 'ieee_double', 'float'),
RegisterInfo('d10', 64, 'ieee_double', 'float'),
RegisterInfo('d11', 64, 'ieee_double', 'float'),
RegisterInfo('d12', 64, 'ieee_double', 'float'),
RegisterInfo('d13', 64, 'ieee_double', 'float'),
RegisterInfo('d14', 64, 'ieee_double', 'float'),
RegisterInfo('d15', 64, 'ieee_double', 'float'),
]
@classmethod
def factory(cls, ap, cmpid, address):
# Create a new core instance.
root = ap.dp.target
core = cls(root.session, ap, root.memory_map, root._new_core_num, cmpid, address)
# Associate this core with the AP.
if ap.core is not None:
raise exceptions.TargetError("AP#%d has multiple cores associated with it" % ap.ap_num)
ap.core = core
# Add the new core to the root target.
root.add_core(core)
root._new_core_num += 1
return core
def __init__(self, session, ap, memoryMap=None, core_num=0, cmpid=None, address=None):
Target.__init__(self, session, memoryMap)
CoreSightCoreComponent.__init__(self, ap, cmpid, address)
self._architecture = None
self.core_type = 0
self.has_fpu = False
self.core_number = core_num
self._run_token = 0
self._target_context = None
self._elf = None
self.target_xml = None
self._supports_vectreset = False
self._reset_catch_delegate_result = False
self._reset_catch_saved_demcr = 0
self.fpb = None
self.dwt = None
# Default to software reset using the default software reset method.
self._default_reset_type = Target.ResetType.SW
# Select default sw reset type based on whether multicore debug is enabled and which core
# this is.
self._default_software_reset_type = Target.ResetType.SW_SYSRESETREQ \
if (not self.session.options.get('enable_multicore_debug')) or (self.core_number == 0) \
else Target.ResetType.SW_VECTRESET
# Set up breakpoints manager.
self.sw_bp = SoftwareBreakpointProvider(self)
self.bp_manager = BreakpointManager(self)
self.bp_manager.add_provider(self.sw_bp)
def add_child(self, cmp):
"""! @brief Connect related CoreSight components."""
super(CortexM, self).add_child(cmp)
if isinstance(cmp, FPB):
self.fpb = cmp
self.bp_manager.add_provider(cmp)
elif isinstance(cmp, DWT):
self.dwt = cmp
@property
def architecture(self):
"""! @brief @ref pyocd.coresight.core_ids.CoreArchitecture "CoreArchitecture" for this core."""
return self._architecture
@property
def elf(self):
return self._elf
@elf.setter
def elf(self, elffile):
self._elf = elffile
@property
def default_reset_type(self):
return self._default_reset_type
@default_reset_type.setter
def default_reset_type(self, reset_type):
assert isinstance(reset_type, Target.ResetType)
self._default_reset_type = reset_type
@property
def default_software_reset_type(self):
return self._default_software_reset_type
@default_software_reset_type.setter
def default_software_reset_type(self, reset_type):
"""! @brief Modify the default software reset method.
@param self
@param reset_type Must be one of the software reset types: Target.ResetType.SW_SYSRESETREQ,
Target.ResetType.SW_VECTRESET, or Target.ResetType.SW_EMULATED.
"""
assert isinstance(reset_type, Target.ResetType)
assert reset_type in (Target.ResetType.SW_SYSRESETREQ, Target.ResetType.SW_VECTRESET,
Target.ResetType.SW_EMULATED)
self._default_software_reset_type = reset_type
@property
def supported_security_states(self):
"""! @brief Tuple of security states supported by the processor.
@return Tuple of @ref pyocd.core.target.Target.SecurityState "Target.SecurityState". For
v6-M and v7-M cores, the return value only contains SecurityState.NONSECURE.
"""
return (Target.SecurityState.NONSECURE,)
def init(self):
"""! @brief Cortex M initialization.
The bus must be accessible when this method is called.
"""
if not self.call_delegate('will_start_debug_core', core=self):
self._read_core_type()
self._check_for_fpu()
self.build_target_xml()
self.sw_bp.init()
self.call_delegate('did_start_debug_core', core=self)
def disconnect(self, resume=True):
if not self.call_delegate('will_stop_debug_core', core=self):
# Remove breakpoints and watchpoints.
self.bp_manager.remove_all_breakpoints()
if self.dwt is not None:
self.dwt.remove_all_watchpoints()
# Disable other debug blocks.
self.write32(CortexM.DEMCR, 0)
# Disable core debug.
if resume:
self.resume()
self.write32(CortexM.DHCSR, CortexM.DBGKEY | 0x0000)
self.call_delegate('did_stop_debug_core', core=self)
def build_target_xml(self):
"""! @brief Build register_list and targetXML"""
self.register_list = []
xml_root = Element('target')
xml_regs_general = SubElement(xml_root, "feature", name="org.gnu.gdb.arm.m-profile")
def append_regs(regs, xml_element):
for reg in regs:
self.register_list.append(reg)
SubElement(xml_element, 'reg', **reg.gdb_xml_attrib)
# Add general purpose registers.
append_regs(self.regs_general, xml_regs_general)
# Depending on the xpsr_control_fields setting, the XPSR and CONTROL registers are
# added as either plain int regs or structured registers with fields defined in the XML.
if self.session.options.get('xpsr_control_fields'):
# Define XPSR and CONTROL register fields.
control = SubElement(xml_regs_general, 'flags', id="control", size="4")
SubElement(control, "field", name="nPRIV", start="0", end="0", type="bool")
SubElement(control, "field", name="SPSEL", start="1", end="1", type="bool")
if self.has_fpu:
SubElement(control, "field", name="FPCS", start="2", end="2", type="bool")
apsr = SubElement(xml_regs_general, 'flags', id="apsr", size="4")
SubElement(apsr, "field", name="N", start="31", end="31", type="bool")
SubElement(apsr, "field", name="Z", start="30", end="30", type="bool")
SubElement(apsr, "field", name="C", start="29", end="29", type="bool")
SubElement(apsr, "field", name="V", start="28", end="28", type="bool")
SubElement(apsr, "field", name="Q", start="27", end="27", type="bool")
ipsr = SubElement(xml_regs_general, 'struct', id="ipsr", size="4")
SubElement(ipsr, "field", name="EXC", start="0", end="8", type="int")
xpsr = SubElement(xml_regs_general, 'union', id="xpsr")
SubElement(xpsr, "field", name="xpsr", type="uint32")
SubElement(xpsr, "field", name="apsr", type="apsr")
SubElement(xpsr, "field", name="ipsr", type="ipsr")
append_regs(self.regs_xpsr_control_fields, xml_regs_general)
else:
# Add XPSR and CONTROL as plain int registers.
append_regs(self.regs_xpsr_control_plain, xml_regs_general)
# Check if target has ARMv7 registers
if self.architecture == CoreArchitecture.ARMv7M:
append_regs(self.regs_system_armv7_only, xml_regs_general)
# Check if target has FPU registers
if self.has_fpu:
# GDB understands the double/single separation so we don't need
# to separately pass the single regs, just the double
xml_regs_fpu = SubElement(xml_root, "feature", name="org.gnu.gdb.arm.vfp")
append_regs(self.regs_float, xml_regs_fpu)
self.target_xml = b'<?xml version="1.0"?><!DOCTYPE feature SYSTEM "gdb-target.dtd">' + tostring(xml_root)
def _read_core_type(self):
"""! @brief Read the CPUID register and determine core type and architecture."""
# Read CPUID register
cpuid = self.read32(CortexM.CPUID)
implementer = (cpuid & CortexM.CPUID_IMPLEMENTER_MASK) >> CortexM.CPUID_IMPLEMENTER_POS
if implementer != CortexM.CPUID_IMPLEMENTER_ARM:
LOG.warning("CPU implementer is not ARM!")
arch = (cpuid & CortexM.CPUID_ARCHITECTURE_MASK) >> CortexM.CPUID_ARCHITECTURE_POS
self.core_type = (cpuid & CortexM.CPUID_PARTNO_MASK) >> CortexM.CPUID_PARTNO_POS
self.cpu_revision = (cpuid & CortexM.CPUID_VARIANT_MASK) >> CortexM.CPUID_VARIANT_POS
self.cpu_patch = (cpuid & CortexM.CPUID_REVISION_MASK) >> CortexM.CPUID_REVISION_POS
# Only v7-M supports VECTRESET.
if arch == CortexM.ARMv7M:
self._architecture = CoreArchitecture.ARMv7M
self._supports_vectreset = True
else:
self._architecture = CoreArchitecture.ARMv6M
if self.core_type in CORE_TYPE_NAME:
LOG.info("CPU core #%d is %s r%dp%d", self.core_number, CORE_TYPE_NAME[self.core_type], self.cpu_revision, self.cpu_patch)
else:
LOG.warning("CPU core #%d type is unrecognized", self.core_number)
def _check_for_fpu(self):
"""! @brief Determine if a core has an FPU.
The core architecture must have been identified prior to calling this function.
"""
# FPU is not supported in these architectures.
if self.architecture in (CoreArchitecture.ARMv6M, CoreArchitecture.ARMv8M_BASE):
self.has_fpu = False
return
originalCpacr = self.read32(CortexM.CPACR)
cpacr = originalCpacr | CortexM.CPACR_CP10_CP11_MASK
self.write32(CortexM.CPACR, cpacr)
cpacr = self.read32(CortexM.CPACR)
self.has_fpu = (cpacr & CortexM.CPACR_CP10_CP11_MASK) != 0
# Restore previous value.
self.write32(CortexM.CPACR, originalCpacr)
if self.has_fpu:
# Now check whether double-precision is supported.
# (Minimal tests to distinguish current permitted ARMv7-M and
# ARMv8-M FPU types; used for printing only).
mvfr0 = self.read32(CortexM.MVFR0)
dp_val = (mvfr0 & CortexM.MVFR0_DOUBLE_PRECISION_MASK) >> CortexM.MVFR0_DOUBLE_PRECISION_SHIFT
mvfr2 = self.read32(CortexM.MVFR2)
vfp_misc_val = (mvfr2 & CortexM.MVFR2_VFP_MISC_MASK) >> CortexM.MVFR2_VFP_MISC_SHIFT
if dp_val >= 2:
fpu_type = "FPv5"
elif vfp_misc_val >= 4:
fpu_type = "FPv5-SP"
else:
fpu_type = "FPv4-SP"
LOG.info("FPU present: " + fpu_type)
def write_memory(self, addr, value, transfer_size=32):
"""! @brief Write a single memory location.
By default the transfer size is a word."""
self.ap.write_memory(addr, value, transfer_size)
def read_memory(self, addr, transfer_size=32, now=True):
"""! @brief Read a memory location.
By default, a word will be read."""
result = self.ap.read_memory(addr, transfer_size, now)
# Read callback returned for async reads.
def read_memory_cb():
return self.bp_manager.filter_memory(addr, transfer_size, result())
if now:
return self.bp_manager.filter_memory(addr, transfer_size, result)
else:
return read_memory_cb
def read_memory_block8(self, addr, size):
"""! @brief Read a block of unaligned bytes in memory.
@return an array of byte values
"""
data = self.ap.read_memory_block8(addr, size)
return self.bp_manager.filter_memory_unaligned_8(addr, size, data)
def write_memory_block8(self, addr, data):
"""! @brief Write a block of unaligned bytes in memory."""
self.ap.write_memory_block8(addr, data)
def write_memory_block32(self, addr, data):
"""! @brief Write an aligned block of 32-bit words."""
self.ap.write_memory_block32(addr, data)
def read_memory_block32(self, addr, size):
"""! @brief Read an aligned block of 32-bit words."""
data = self.ap.read_memory_block32(addr, size)
return self.bp_manager.filter_memory_aligned_32(addr, size, data)
def halt(self):
"""! @brief Halt the core
"""
LOG.debug("halting core %d", self.core_number)
self.session.notify(Target.Event.PRE_HALT, self, Target.HaltReason.USER)
self.write_memory(CortexM.DHCSR, CortexM.DBGKEY | CortexM.C_DEBUGEN | CortexM.C_HALT)
self.flush()
self.session.notify(Target.Event.POST_HALT, self, Target.HaltReason.USER)
def step(self, disable_interrupts=True, start=0, end=0):
"""! @brief Perform an instruction level step.
This function preserves the previous interrupt mask state.
"""
# Was 'if self.get_state() != TARGET_HALTED:'
# but now value of dhcsr is saved
dhcsr = self.read_memory(CortexM.DHCSR)
if not (dhcsr & (CortexM.C_STEP | CortexM.C_HALT)):
LOG.error('cannot step: target not halted')
return
LOG.debug("step core %d", self.core_number)
self.session.notify(Target.Event.PRE_RUN, self, Target.RunType.STEP)
self.clear_debug_cause_bits()
# Save previous interrupt mask state
interrupts_masked = (CortexM.C_MASKINTS & dhcsr) != 0
# Mask interrupts - C_HALT must be set when changing to C_MASKINTS
if not interrupts_masked and disable_interrupts:
self.write_memory(CortexM.DHCSR, CortexM.DBGKEY | CortexM.C_DEBUGEN | CortexM.C_HALT | CortexM.C_MASKINTS)
# Single step using current C_MASKINTS setting
while True:
if disable_interrupts or interrupts_masked:
self.write_memory(CortexM.DHCSR, CortexM.DBGKEY | CortexM.C_DEBUGEN | CortexM.C_MASKINTS | CortexM.C_STEP)
else:
self.write_memory(CortexM.DHCSR, CortexM.DBGKEY | CortexM.C_DEBUGEN | CortexM.C_STEP)
# Wait for halt to auto set (This should be done before the first read)
while not self.read_memory(CortexM.DHCSR) & CortexM.C_HALT:
pass
# Range is empty, 'range step' will degenerate to 'step'
if start == end:
break
# Read program counter and compare to [start, end)
program_counter = self.read_core_register(CORE_REGISTER['pc'])
if program_counter < start or end <= program_counter:
break
# Check other stop reasons
if self.read_memory(CortexM.DFSR) & (CortexM.DFSR_DWTTRAP | CortexM.DFSR_BKPT):
break
# Restore interrupt mask state
if not interrupts_masked and disable_interrupts:
# Unmask interrupts - C_HALT must be set when changing to C_MASKINTS
self.write_memory(CortexM.DHCSR, CortexM.DBGKEY | CortexM.C_DEBUGEN | CortexM.C_HALT)
self.flush()
self._run_token += 1
self.session.notify(Target.Event.POST_RUN, self, Target.RunType.STEP)
def clear_debug_cause_bits(self):
self.write_memory(CortexM.DFSR, CortexM.DFSR_VCATCH | CortexM.DFSR_DWTTRAP | CortexM.DFSR_BKPT | CortexM.DFSR_HALTED)
def _perform_emulated_reset(self):
"""! @brief Emulate a software reset by writing registers.
All core registers are written to reset values. This includes setting the initial PC and SP
to values read from the vector table, which is assumed to be located at the based of the
boot memory region.
If the memory map does not provide a boot region, then the current value of the VTOR register
is reused, as it should at least point to a valid vector table.
The current value of DEMCR.VC_CORERESET determines whether the core will be resumed or
left halted.
Note that this reset method will not set DHCSR.S_RESET_ST or DFSR.VCATCH.
"""
# Halt the core before making changes.
self.halt()
bootMemory = self.memory_map.get_boot_memory()
if bootMemory is None:
# Reuse current VTOR value if we don't know the boot memory region.
vectorBase = self.read32(self.VTOR)
else:
vectorBase = bootMemory.start
# Read initial SP and PC.
initialSp = self.read32(vectorBase)
initialPc = self.read32(vectorBase + 4)
# Init core registers.
regList = ['r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12',
'psp', 'msp', 'lr', 'pc', 'xpsr', 'cfbp']
valueList = [0] * 13 + \
[
0, # PSP
initialSp, # MSP
0xffffffff, # LR
initialPc, # PC
0x01000000, # XPSR
0, # CFBP
]
if self.has_fpu:
regList += [('s%d' % n) for n in range(32)] + ['fpscr']
valueList += [0] * 33
self.write_core_registers_raw(regList, valueList)
# "Reset" SCS registers.
data = [
(self.ICSR_PENDSVCLR | self.ICSR_PENDSTCLR), # ICSR
vectorBase, # VTOR
(self.NVIC_AIRCR_VECTKEY | self.NVIC_AIRCR_VECTCLRACTIVE), # AIRCR
0, # SCR
0, # CCR
0, # SHPR1
0, # SHPR2
0, # SHPR3
0, # SHCSR
0, # CFSR
]
self.write_memory_block32(self.ICSR, data)
self.write32(self.CPACR, 0)
if self.has_fpu:
data = [
0, # FPCCR
0, # FPCAR
0, # FPDSCR
]
self.write_memory_block32(self.FPCCR, data)
# "Reset" SysTick.
self.write_memory_block32(self.SYSTICK_CSR, [0] * 3)
# "Reset" NVIC registers.
numregs = (self.read32(self.ICTR) & 0xf) + 1
self.write_memory_block32(self.NVIC_ICER0, [0xffffffff] * numregs)
self.write_memory_block32(self.NVIC_ICPR0, [0xffffffff] * numregs)
self.write_memory_block32(self.NVIC_IPR0, [0xffffffff] * (numregs * 8))
def _get_actual_reset_type(self, reset_type):
"""! @brief Determine the reset type to use given defaults and passed in type."""
# Default to reset_type session option if reset_type parameter is None. If the session
# option isn't set, then use the core's default reset type.
if reset_type is None:
if self.session.options.get('reset_type') is None:
reset_type = self.default_reset_type
else:
try:
# Convert session option value to enum.
resetOption = self.session.options.get('reset_type')
reset_type = cmdline.convert_reset_type(resetOption)
# The converted option will be None if the option value is 'default'.
if reset_type is None:
reset_type = self.default_reset_type
except ValueError:
reset_type = self.default_reset_type
else:
assert isinstance(reset_type, Target.ResetType)
# If the reset type is just SW, then use our default software reset type.
if reset_type is Target.ResetType.SW:
reset_type = self.default_software_reset_type
# Fall back to emulated sw reset if the vectreset is specified and the core doesn't support it.
if (reset_type is Target.ResetType.SW_VECTRESET) and (not self._supports_vectreset):
reset_type = Target.ResetType.SW_EMULATED
return reset_type
def _perform_reset(self, reset_type):
"""! @brief Perform a reset of the specified type."""
assert isinstance(reset_type, Target.ResetType)
if reset_type is Target.ResetType.HW:
self.session.probe.reset()
elif reset_type is Target.ResetType.SW_EMULATED:
self._perform_emulated_reset()
else:
if reset_type is Target.ResetType.SW_SYSRESETREQ:
mask = CortexM.NVIC_AIRCR_SYSRESETREQ
elif reset_type is Target.ResetType.SW_VECTRESET:
mask = CortexM.NVIC_AIRCR_VECTRESET
else:
raise exceptions.InternalError("unhandled reset type")
try:
self.write_memory(CortexM.NVIC_AIRCR, CortexM.NVIC_AIRCR_VECTKEY | mask)
# Without a flush a transfer error can occur
self.flush()
except exceptions.TransferError:
self.flush()
def reset(self, reset_type=None):
"""! @brief Reset the core.
The reset method is selectable via the reset_type parameter as well as the reset_type
session option. If the reset_type parameter is not specified or None, then the reset_type
option will be used. If the option is not set, or if it is set to a value of 'default', the
the core's default_reset_type property value is used. So, the session option overrides the
core's default, while the parameter overrides everything.
Note that only v7-M cores support the `VECTRESET` software reset method. If this method
is chosen but the core doesn't support it, the the reset method will fall back to an
emulated software reset.
After a call to this function, the core is running.
"""
self.session.notify(Target.Event.PRE_RESET, self)
reset_type = self._get_actual_reset_type(reset_type)
LOG.debug("reset, core %d, type=%s", self.core_number, reset_type.name)
self._run_token += 1
# Give the delegate a chance to overide reset. If the delegate returns True, then it
# handled the reset on its own.
if not self.call_delegate('will_reset', core=self, reset_type=reset_type):
self._perform_reset(reset_type)
self.call_delegate('did_reset', core=self, reset_type=reset_type)
# Now wait for the system to come out of reset. Keep reading the DHCSR until
# we get a good response with S_RESET_ST cleared, or we time out.
with timeout.Timeout(2.0) as t_o:
while t_o.check():
try:
dhcsr = self.read32(CortexM.DHCSR)
if (dhcsr & CortexM.S_RESET_ST) == 0:
break
except exceptions.TransferError:
self.flush()
sleep(0.01)
self.session.notify(Target.Event.POST_RESET, self)
def set_reset_catch(self, reset_type=None):
"""! @brief Prepare to halt core on reset."""
LOG.debug("set reset catch, core %d", self.core_number)
self._reset_catch_delegate_result = self.call_delegate('set_reset_catch', core=self, reset_type=reset_type)
# Default behaviour if the delegate didn't handle it.
if not self._reset_catch_delegate_result:
# Halt the target.
self.halt()
# Save CortexM.DEMCR.
self._reset_catch_saved_demcr = self.read_memory(CortexM.DEMCR)
# Enable reset vector catch if needed.
if (self._reset_catch_saved_demcr & CortexM.DEMCR_VC_CORERESET) == 0:
self.write_memory(CortexM.DEMCR, self._reset_catch_saved_demcr | CortexM.DEMCR_VC_CORERESET)
def clear_reset_catch(self, reset_type=None):
"""! @brief Disable halt on reset."""
LOG.debug("clear reset catch, core %d", self.core_number)
self.call_delegate('clear_reset_catch', core=self, reset_type=reset_type)
if not self._reset_catch_delegate_result:
# restore vector catch setting
self.write_memory(CortexM.DEMCR, self._reset_catch_saved_demcr)
def reset_and_halt(self, reset_type=None):
"""! @brief Perform a reset and stop the core on the reset handler."""
# Set up reset catch.
self.set_reset_catch(reset_type)
# Perform the reset.
self.reset(reset_type)
# wait until the unit resets
with timeout.Timeout(2.0) as t_o:
while t_o.check():
if self.get_state() not in (Target.State.RESET, Target.State.RUNNING):
break
sleep(0.01)
# Make sure the thumb bit is set in XPSR in case the reset handler
# points to an invalid address.
xpsr = self.read_core_register('xpsr')
if xpsr & self.XPSR_THUMB == 0:
self.write_core_register('xpsr', xpsr | self.XPSR_THUMB)
# Restore to original state.
self.clear_reset_catch(reset_type)
def get_state(self):
dhcsr = self.read_memory(CortexM.DHCSR)
if dhcsr & CortexM.S_RESET_ST:
# Reset is a special case because the bit is sticky and really means
# "core was reset since last read of DHCSR". We have to re-read the
# DHCSR, check if S_RESET_ST is still set and make sure no instructions
# were executed by checking S_RETIRE_ST.
newDhcsr = self.read_memory(CortexM.DHCSR)
if (newDhcsr & CortexM.S_RESET_ST) and not (newDhcsr & CortexM.S_RETIRE_ST):
return Target.State.RESET
if dhcsr & CortexM.S_LOCKUP:
return Target.State.LOCKUP
elif dhcsr & CortexM.S_SLEEP:
return Target.State.SLEEPING
elif dhcsr & CortexM.S_HALT:
return Target.State.HALTED
else:
return Target.State.RUNNING