forked from blackmagic-debug/blackmagic
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathriscv_debug.c
More file actions
1340 lines (1234 loc) · 51.2 KB
/
Copy pathriscv_debug.c
File metadata and controls
1340 lines (1234 loc) · 51.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
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
/*
* This file is part of the Black Magic Debug project.
*
* Copyright (C) 2023-2024 1BitSquared <info@1bitsquared.com>
* Written by Rachel Mant <git@dragonmux.network>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "general.h"
#include "target_probe.h"
#include "target_internal.h"
#include "gdb_reg.h"
#include "riscv_debug.h"
#include "buffer_utils.h"
#include <assert.h>
/*
* Links to exact specifications used in this code are listed here for ease:
* riscv-debug-spec v0.11 as used by SiFive:
* https://static.dev.sifive.com/riscv-debug-spec-0.11nov12.pdf
* riscv-debug-spec v0.13.2 (release):
* https://raw.githubusercontent.com/riscv/riscv-debug-spec/v0.13-release/riscv-debug-release.pdf
* riscv-debug-spec v1.0 (stable):
* https://github.com/riscv/riscv-debug-spec/blob/master/riscv-debug-stable.pdf
*/
#define RV_DM_CONTROL 0x10U
#define RV_DM_STATUS 0x11U
#define RV_DM_NEXT_DM 0x1dU
#define RV_DM_PROGBUF_BASE 0x20U
#define RV_DM_CTRL_ACTIVE (1U << 0U)
#define RV_DM_CTRL_SYSTEM_RESET (1U << 1U)
#define RV_DM_CTRL_HARTSEL_MASK 0x03ffffc0U
#define RV_DM_CTRL_HARTSELLO_MASK 0x03ff0000U
#define RV_DM_CTRL_HARTSELHI_MASK 0x0000ffc0U
#define RV_DM_CTRL_HART_ACK_RESET (1U << 28U)
#define RV_DM_CTRL_HART_RESET (1U << 29U)
#define RV_DM_CTRL_RESUME_REQ (1U << 30U)
#define RV_DM_CTRL_HALT_REQ (1U << 31U)
#define RV_DM_CTRL_HARTSELLO_SHIFT 16U
#define RV_DM_CTRL_HARTSELHI_SHIFT 4U
#define RV_DM_STAT_ALL_HALTED (1U << 9U)
#define RV_DM_STAT_UNAVAILABLE (1U << 12U)
#define RV_DM_STAT_NON_EXISTENT (1U << 14U)
#define RV_DM_STAT_ALL_RESUME_ACK (1U << 17U)
#define RV_DM_STAT_ALL_RESET (1U << 19U)
#define RV_DM_ABST_STATUS_BUSY (1U << 12U)
#define RV_DM_ABST_STATUS_DATA_COUNT 0x0000000fU
#define RV_DM_ABST_STATUS_PROGBUFSIZE_MASK 0x1f000000U
#define RV_DM_ABST_STATUS_PROGBUFSIZE_SHIFT 24U
#define RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK 0x00000fe0U
#define RV_CSR_FORCE_MASK 0xc000U
#define RV_CSR_FORCE_32_BIT 0x4000U
#define RV_CSR_FORCE_64_BIT 0x8000U
#define RV_CSR_TYPE_MASK 0x3000U
#define RV_CSR_TYPE_GPR 0x1000U
#define RV_CSR_ADDR_MASK 0x0fffU
/* The following is a set of CSR address definitions */
/* misa -> The Hart's machine ISA register */
#define RV_ISA 0x301U
/* dcsr -> Debug Control/Status Register */
#define RV_DCSR 0x7b0U
/* mvendorid -> The JEP-106 code for the vendor implementing this Hart */
#define RV_VENDOR_ID 0xf11U
/* marchid -> The RISC-V International architecture ID code */
#define RV_ARCH_ID 0xf12U
/* mimplid -> Hart's processor implementation ID */
#define RV_IMPL_ID 0xf13U
/* mhartid -> machine ID of the Hart */
#define RV_HART_ID 0xf14U
/* tselect -> Trigger selection register */
#define RV_TRIG_SELECT 0x7a0U
/* tinfo -> selected trigger information register */
#define RV_TRIG_INFO 0x7a4U
/* tdata1 -> selected trigger configuration register 1 */
#define RV_TRIG_DATA_1 0x7a1U
/* tdata2 -> selected trigger configuration register 2 */
#define RV_TRIG_DATA_2 0x7a2U
/* GPR a0, aka x10 is used as a bounce buffer for our progbuf CSR I/O */
#define RV_GPR_A0 0x100aU
/*
* Instructions for reading and writing CSRs through a0
* CSRR -> CSR Read, abuses the CSRRS atomic read and set bits instruction
* CSRW -> CSR Write, abuses the CSRRW atomic read/write instruction
* In the case of CSRRS, if the rs1 register is x0, no write is performed.
* In the case of CSRRW, if the rd register is x0, no read is performed.
*/
#define RV_CSRR_A0 0x00002573U
#define RV_CSRW_A0 0x00051073U
#define RV_EBREAK 0x00100073U
#define RV_VENDOR_JEP106_CONT_MASK 0x7fffff80U
#define RV_VENDOR_JEP106_CODE_MASK 0x7fU
#define RV_DCSR_STEP 0x00000004U
#define RV_DCSR_CAUSE_MASK 0x000001c0U
#define RV_DCSR_STEPIE 0x00000800U
#define RV_DCSR_EBREAK_MACHINE 0x00008000U
#define RV_DCSR_STOP_TIME (1U << 9)
#define RV_DCSR_STOP_COUNT (1U << 10)
#define RV_GPRS_COUNT 32U
/* This enum defines the set of currently known and valid halt causes */
typedef enum riscv_halt_cause {
/* Halt was caused by an `ebreak` instruction executing */
RV_HALT_CAUSE_EBREAK = (1U << 6U),
/* Halt was caused by a breakpoint or watchpoint (set in the trigger module) */
RV_HALT_CAUSE_TRIGGER = (2U << 6U),
/* Halt was caused by debugger request (haltreq) */
RV_HALT_CAUSE_REQUEST = (3U << 6U),
/* Halt was caused by single-step execution */
RV_HALT_CAUSE_STEP = (4U << 6U),
/* Halt was caused by request out of reset (resethaltreq) */
RV_HALT_CAUSE_RESET = (5U << 6U),
} riscv_halt_cause_e;
// clang-format off
/* General-purpose register name strings */
static const char *const riscv_gpr_names[RV_GPRS_COUNT] = {
"zero", "ra", "sp", "gp",
"tp", "t0", "t1", "t2",
"fp", "s1", "a0", "a1",
"a2", "a3", "a4", "a5",
"a6", "a7", "s2", "s3",
"s4", "s5", "s6", "s7",
"s8", "s9", "s10", "s11",
"t3", "t4", "t5", "t6",
};
// clang-format on
typedef struct riscv_csr_descriptor {
const char *name;
const uint32_t csr_number; // fits in 16 bits actually (?)
} riscv_csr_descriptor_s;
static const riscv_csr_descriptor_s riscv_csrs[] = {
{"mstatus", RV_CSR_STATUS},
{"misa", RV_CSR_MISA},
{"mie", RV_CSR_MIE},
{"mtvec", RV_CSR_MTVEC},
{"mscratch", RV_CSR_MSCRATCH},
{"mepc", RV_CSR_MEPC},
{"mcause", RV_CSR_MCAUSE},
{"mtval", RV_CSR_MTVAL},
{"mip", RV_CSR_MIP},
};
// fpu registers straight from gdb https://patchwork.kernel.org/project/qemu-devel/patch/20181228220731.4753-1-jimw@sifive.com/
// actual order from gdb 13.2
// csr registers declared in the "normal" registers section, they may also need to be in the csr section
static const char *riscv_fpu_ctrl_regs[] = {
"flags",
"rm",
"csr",
};
// Regular FPU register suffixes
static const char *riscv_fpu_regs[] = {
"t0",
"t1",
"t2",
"t3",
"t4",
"t5",
"t6",
"t7",
"s0",
"s1",
"a0",
"a1",
"a2",
"a3",
"a4",
"a5",
"a6",
"a7",
"s2",
"s3",
"s4",
"s5",
"s6",
"s7",
"s8",
"s9",
"s10",
"s11",
"t8",
"t9",
"t10",
"t11",
};
/* General-purpose register types */
static const gdb_reg_type_e riscv_gpr_types[RV_GPRS_COUNT] = {
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_CODE_PTR,
GDB_TYPE_DATA_PTR,
GDB_TYPE_DATA_PTR,
GDB_TYPE_DATA_PTR,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_DATA_PTR,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
GDB_TYPE_UNSPECIFIED,
};
// clang-format off
static_assert(ARRAY_LENGTH(riscv_gpr_names) == ARRAY_LENGTH(riscv_gpr_types),
"GPR array length mismatch! GPR type array should have the same length as GPR name array."
);
// clang-format on
static void riscv_dm_init(riscv_dm_s *dbg_module);
static bool riscv_hart_init(riscv_hart_s *hart);
static void riscv_hart_free(void *priv);
static bool riscv_dmi_read(riscv_dmi_s *dmi, uint32_t address, uint32_t *value);
static bool riscv_dmi_write(riscv_dmi_s *dmi, uint32_t address, uint32_t value);
static void riscv_dm_ref(riscv_dm_s *dbg_module);
static void riscv_dm_unref(riscv_dm_s *dbg_module);
static riscv_debug_version_e riscv_dm_version(uint32_t status);
static uint32_t riscv_hart_discover_isa(riscv_hart_s *hart);
static void riscv_hart_discover_triggers(riscv_hart_s *hart);
static void riscv_hart_memory_access_type(target_s *target);
static const char *riscv_target_description(target_s *target);
static bool riscv_check_error(target_s *target);
static void riscv_halt_request(target_s *target);
static void riscv_halt_resume(target_s *target, bool step);
static target_halt_reason_e riscv_halt_poll(target_s *target, target_addr64_t *watch);
static void riscv_reset(target_s *target);
void riscv_dmi_init(riscv_dmi_s *const dmi)
{
/* If we don't currently know how to talk to this DMI, warn and fail */
if (dmi->version == RISCV_DEBUG_UNKNOWN)
return;
if (dmi->version == RISCV_DEBUG_0_11) {
DEBUG_INFO("RISC-V debug v0.11 not presently supported\n");
return;
}
/* Iterate through the possible DMs and probe implemented ones */
/* The first DM is always at base address 0 */
uint32_t base_addr = 0U;
do {
/* Read out the DM's status register */
uint32_t dm_status = 0;
if (!riscv_dmi_read(dmi, base_addr + RV_DM_STATUS, &dm_status)) {
/* If we fail to read the status register, abort */
break;
}
const riscv_debug_version_e dm_version = riscv_dm_version(dm_status);
/* If the DM is not unimplemented, allocate a structure for it and do further processing */
if (dm_version != RISCV_DEBUG_UNIMPL) {
riscv_dm_s *dbg_module = calloc(1, sizeof(*dbg_module));
if (!dbg_module) { /* calloc failed: heap exhaustion */
DEBUG_WARN("calloc: failed in %s\n", __func__);
return;
}
/* Setup and try to discover the DM's Harts */
dbg_module->dmi_bus = dmi;
dbg_module->base = base_addr;
dbg_module->version = dm_version;
riscv_dm_init(dbg_module);
/* If we failed to discover any Harts, free the structure */
if (!dbg_module->ref_count)
free(dbg_module);
}
/* Read out the address of the next DM */
if (!riscv_dmi_read(dmi, base_addr + RV_DM_NEXT_DM, &base_addr)) {
/* If this fails then abort further scanning */
DEBUG_INFO("Error while reading the next DM base address\n");
break;
}
/* A new base address of 0 indicates this is the last one on the chain and we should stop. */
} while (base_addr != 0U);
}
static void riscv_dm_init(riscv_dm_s *const dbg_module)
{
/* Attempt to activate the DM */
if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, RV_DM_CTRL_ACTIVE))
return;
/* Now find out how many hartsel bits are present */
uint32_t control = RV_DM_CTRL_ACTIVE | RV_DM_CTRL_HARTSEL_MASK;
if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, control) || !riscv_dm_read(dbg_module, RV_DM_CONTROL, &control))
return;
/* Extract the maximum number of harts present and iterate through the harts */
const uint32_t harts_max = ((control & RV_DM_CTRL_HARTSELLO_MASK) >> RV_DM_CTRL_HARTSELLO_SHIFT) |
((control & RV_DM_CTRL_HARTSELHI_MASK) << RV_DM_CTRL_HARTSELHI_SHIFT);
for (uint32_t hart_idx = 0; hart_idx <= harts_max; ++hart_idx) {
/* Select the hart */
control = ((hart_idx << RV_DM_CTRL_HARTSELLO_SHIFT) & RV_DM_CTRL_HARTSELLO_MASK) |
((hart_idx >> RV_DM_CTRL_HARTSELHI_SHIFT) & RV_DM_CTRL_HARTSELHI_MASK) | RV_DM_CTRL_ACTIVE;
uint32_t status = 0;
if (!riscv_dm_write(dbg_module, RV_DM_CONTROL, control) || !riscv_dm_read(dbg_module, RV_DM_STATUS, &status))
return;
/* If the hart doesn't exist, the spec says to terminate scan */
if (status & RV_DM_STAT_NON_EXISTENT)
break;
/* If the hart is not available, skip it */
if (status & RV_DM_STAT_UNAVAILABLE) {
DEBUG_INFO("Skipping hart %" PRIu32 " -> Unavailable\n", hart_idx);
continue;
}
riscv_hart_s *hart = calloc(1, sizeof(*hart));
if (!hart) { /* calloc failed: heap exhaustion */
DEBUG_WARN("calloc: failed in %s\n", __func__);
return;
}
/* Setup the hart structure and discover the target core */
hart->dbg_module = dbg_module;
hart->hart_idx = hart_idx;
hart->hartsel = control;
if (!riscv_hart_init(hart))
free(hart);
}
}
static uint8_t riscv_isa_address_width(const uint32_t isa)
{
switch ((isa & RV_ISA_MXL_MASK) >> RV_ISA_MXL_SHIFT) {
case RV_ISA_MXL_32:
return 32U;
case RV_ISA_MXL_64:
return 64U;
case RV_ISA_MXL_128:
return 128U;
default:
DEBUG_INFO("Unknown address width, defaulting to 32\n");
return 32U;
}
}
static void riscv_hart_read_ids(riscv_hart_s *const hart)
{
/* Read out the vendor ID */
riscv_csr_read(hart, RV_VENDOR_ID | RV_CSR_FORCE_32_BIT, &hart->vendorid);
/* Adjust the value to fit our view of JEP-106 codes */
hart->vendorid =
((hart->vendorid & RV_VENDOR_JEP106_CONT_MASK) << 1U) | (hart->vendorid & RV_VENDOR_JEP106_CODE_MASK);
/* Depending on the bus width, read out the other IDs suitably */
if (hart->access_width == 32U) {
riscv_csr_read(hart, RV_ARCH_ID, &hart->archid);
riscv_csr_read(hart, RV_IMPL_ID, &hart->implid);
riscv_csr_read(hart, RV_HART_ID, &hart->hartid);
} else if (hart->access_width == 64U) {
/* For now, on rv64, we just truncate these down after read */
uint64_t ident = 0;
riscv_csr_read(hart, RV_ARCH_ID, &ident);
hart->archid = ident & 0xffffffffU;
riscv_csr_read(hart, RV_IMPL_ID, &ident);
hart->implid = ident & 0xffffffffU;
riscv_csr_read(hart, RV_HART_ID, &ident);
hart->hartid = ident & 0xffffffffU;
}
/* rv128 is unimpl. */
}
static size_t riscv_snprint_isa_subset(
char *const string_buffer, const size_t buffer_size, const uint8_t access_width, const uint32_t extensions)
{
size_t offset = snprintf(string_buffer, buffer_size, "rv%" PRIu8, access_width);
const bool is_embedded = extensions & RV_ISA_EXT_EMBEDDED;
offset = write_char(string_buffer, buffer_size, offset, is_embedded ? 'e' : 'i');
const bool is_general_purpose_isa =
!is_embedded && (extensions & RV_ISA_EXT_GENERAL_PURPOSE) == RV_ISA_EXT_GENERAL_PURPOSE;
if (is_general_purpose_isa) {
offset = write_char(string_buffer, buffer_size, offset, 'g');
if (extensions & RV_ISA_EXT_QUAD_FLOAT)
offset = write_char(string_buffer, buffer_size, offset, 'q');
} else {
if (extensions & RV_ISA_EXT_MUL_DIV_INT)
offset = write_char(string_buffer, buffer_size, offset, 'm');
if (extensions & RV_ISA_EXT_ATOMIC)
offset = write_char(string_buffer, buffer_size, offset, 'a');
if (extensions & RV_ISA_EXT_QUAD_FLOAT)
offset = write_char(string_buffer, buffer_size, offset, 'q'); /* Implies d */
else if (extensions & RV_ISA_EXT_DOUBLE_FLOAT)
offset = write_char(string_buffer, buffer_size, offset, 'd'); /* Implies f */
else if (extensions & RV_ISA_EXT_SINGLE_FLOAT)
offset = write_char(string_buffer, buffer_size, offset, 'f');
}
if (extensions & RV_ISA_EXT_DECIMAL_FLOAT)
offset = write_char(string_buffer, buffer_size, offset, 'l');
if (extensions & RV_ISA_EXT_COMPRESSED)
offset = write_char(string_buffer, buffer_size, offset, 'c');
if (extensions & RV_ISA_EXT_BIT_MANIP)
offset = write_char(string_buffer, buffer_size, offset, 'b');
if (extensions & RV_ISA_EXT_DYNAMIC_LANG)
offset = write_char(string_buffer, buffer_size, offset, 'j');
if (extensions & RV_ISA_EXT_TRANSACT_MEM)
offset = write_char(string_buffer, buffer_size, offset, 't');
if (extensions & RV_ISA_EXT_PACKED_SIMD)
offset = write_char(string_buffer, buffer_size, offset, 'p');
if (extensions & RV_ISA_EXT_VECTOR)
offset = write_char(string_buffer, buffer_size, offset, 'v');
if (extensions & RV_ISA_EXT_USER_INTERRUPTS)
offset = write_char(string_buffer, buffer_size, offset, 'n');
/* null-terminate the string */
if (string_buffer && buffer_size > 0)
string_buffer[offset < buffer_size ? offset : buffer_size - 1U] = '\0';
return offset;
}
static bool riscv_hart_init(riscv_hart_s *const hart)
{
/* Allocate a new target */
target_s *target = target_new();
if (!target)
return false;
/* Grab a reference to the DMI and DM structures and do preliminary setup of the target structure */
riscv_dm_ref(hart->dbg_module);
target->driver = "RISC-V";
target->priv = hart;
target->priv_free = riscv_hart_free;
/* Request halt and read certain key registers */
riscv_halt_request(target);
uint32_t isa = riscv_hart_discover_isa(hart);
hart->address_width = riscv_isa_address_width(isa);
hart->extensions = isa & RV_ISA_EXTENSIONS_MASK;
/* Figure out if the target needs us to use sysbus or not for memory access */
riscv_hart_memory_access_type(target);
/* Then read out the ID registers */
riscv_hart_read_ids(hart);
/* Build the ISA subset string from the Hart */
riscv_snprint_isa_subset(hart->isa_name, sizeof(hart->isa_name), hart->access_width, hart->extensions);
target->core = hart->isa_name;
DEBUG_INFO("Hart %" PRIx32 ": %u-bit RISC-V (arch = %08" PRIx32 "), %s ISA (exts = %08" PRIx32
"), vendor = %" PRIx32 ", impl = %" PRIx32 "\n",
hart->hartid, hart->access_width, hart->archid, hart->isa_name, hart->extensions, hart->vendorid, hart->implid);
/* We don't support rv128, so tell the user and fast-quit on this target. */
if (hart->access_width == 128U) {
DEBUG_WARN("rv128 is unsupported, ignoring this hart\n");
return true;
}
/* If the hart implements mvendorid, this gives us the JEP-106, otherwise use the DTM designer code */
target->designer_code = hart->vendorid ? hart->vendorid : hart->dbg_module->dmi_bus->designer_code;
target->cpuid = hart->archid;
/* If the DMI bus is provided via ADI, grab the AP's partno for the target */
if (hart->dbg_module->dmi_bus->dev_index == 0xffU && hart->dbg_module->dmi_bus->idle_cycles == 0xffU)
target->part_id = ((riscv_dmi_ap_s *)hart->dbg_module->dmi_bus)->ap->partno;
/* Now we're in a safe environment, leasurely read out the triggers, etc. */
riscv_hart_discover_triggers(hart);
/* Setup core-agnostic target functions */
target->attach = riscv_attach;
target->detach = riscv_detach;
target->regs_description = riscv_target_description;
target->check_error = riscv_check_error;
target->halt_request = riscv_halt_request;
target->halt_resume = riscv_halt_resume;
target->halt_poll = riscv_halt_poll;
target->reset = riscv_reset;
if (hart->access_width == 32U) {
DEBUG_INFO("-> riscv32_probe\n");
if (!riscv32_probe(target))
DEBUG_INFO("Probing failed, please report unknown RISC-V 32 device\n");
}
if (hart->access_width == 64U) {
DEBUG_INFO("-> riscv64_probe\n");
if (!riscv64_probe(target))
DEBUG_INFO("Probing failed, please report unknown RISC-V 64 device\n");
}
riscv_halt_resume(target, false);
return true;
}
riscv_hart_s *riscv_hart_struct(target_s *const target)
{
return (riscv_hart_s *)target->priv;
}
static void riscv_hart_free(void *const priv)
{
riscv_dm_unref(((riscv_hart_s *)priv)->dbg_module);
free(priv);
}
static bool riscv_dmi_read(riscv_dmi_s *const dmi, const uint32_t address, uint32_t *const value)
{
const bool result = dmi->read(dmi, address, value);
if (result)
DEBUG_PROTO("%s: %08" PRIx32 " -> %08" PRIx32 "\n", __func__, address, *value);
return result;
}
static bool riscv_dmi_write(riscv_dmi_s *const dmi, const uint32_t address, const uint32_t value)
{
DEBUG_PROTO("%s: %08" PRIx32 " <- %08" PRIx32 "\n", __func__, address, value);
return dmi->write(dmi, address, value);
}
bool riscv_dm_read(riscv_dm_s *dbg_module, const uint8_t address, uint32_t *const value)
{
return riscv_dmi_read(dbg_module->dmi_bus, dbg_module->base + address, value);
}
bool riscv_dm_write(riscv_dm_s *dbg_module, const uint8_t address, const uint32_t value)
{
return riscv_dmi_write(dbg_module->dmi_bus, dbg_module->base + address, value);
}
static riscv_debug_version_e riscv_dm_version(const uint32_t status)
{
uint8_t version = status & RV_STATUS_VERSION_MASK;
switch (version) {
case 0:
return RISCV_DEBUG_UNIMPL;
case 1:
DEBUG_INFO("RISC-V debug v0.11 DM\n");
return RISCV_DEBUG_0_11;
case 2:
DEBUG_INFO("RISC-V debug v0.13 DM\n");
return RISCV_DEBUG_0_13;
case 3:
DEBUG_INFO("RISC-V debug v1.0 DM\n");
return RISCV_DEBUG_1_0;
default:
break;
}
DEBUG_INFO("Please report part with unknown RISC-V debug DM version %x\n", version);
return RISCV_DEBUG_UNKNOWN;
}
static inline void riscv_dmi_ref(riscv_dmi_s *const dmi)
{
++dmi->ref_count;
}
static inline void riscv_dmi_unref(riscv_dmi_s *const dmi)
{
--dmi->ref_count;
if (!dmi->ref_count) {
/* Check if the DMI bus is on an AP */
if (dmi->dev_index == 0xffU && dmi->idle_cycles == 0xffU)
adiv5_ap_unref(((riscv_dmi_ap_s *)dmi)->ap);
free(dmi);
}
}
static void riscv_dm_ref(riscv_dm_s *const dbg_module)
{
if (!dbg_module->ref_count)
riscv_dmi_ref(dbg_module->dmi_bus);
++dbg_module->ref_count;
}
static void riscv_dm_unref(riscv_dm_s *const dbg_module)
{
--dbg_module->ref_count;
if (!dbg_module->ref_count) {
riscv_dmi_unref(dbg_module->dmi_bus);
free(dbg_module);
}
}
static uint32_t riscv_hart_discover_isa(riscv_hart_s *const hart)
{
/* Read out the abstract command control/status register */
uint32_t data_registers = 0;
if (!riscv_dm_read(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, &data_registers))
return 0U;
/* Extract the program buffer size in DM registers */
hart->progbuf_size = (data_registers & RV_DM_ABST_STATUS_PROGBUFSIZE_MASK) >> RV_DM_ABST_STATUS_PROGBUFSIZE_SHIFT;
/* Now use the data count bits to divine an initial guess on the platform width */
data_registers &= RV_DM_ABST_STATUS_DATA_COUNT;
DEBUG_INFO("Hart has %" PRIu32 " data registers and %u progbuf registers\n", data_registers, hart->progbuf_size);
/* Check we have at least enough data registers for arg0 */
if (data_registers >= 4)
hart->access_width = 128U;
else if (data_registers >= 2)
hart->access_width = 64U;
else if (data_registers)
hart->access_width = 32U;
/* If the control/status register contains an invalid count, abort */
else
return 0;
do {
DEBUG_INFO("Attempting %u-bit read on misa\n", hart->access_width);
/* Try reading the register on the guessed width */
uint32_t isa_data[4] = {0U};
bool result = riscv_csr_read(hart, RV_ISA, isa_data);
if (result) {
if (hart->access_width == 128U)
return (isa_data[3] & 0xc0000000) | (isa_data[0] & 0x3fffffffU);
if (hart->access_width == 64U)
return (isa_data[1] & 0xc0000000) | (isa_data[0] & 0x3fffffffU);
return isa_data[0];
}
/* If that failed, then find out why and instead try the next narrower width */
if (hart->status != RISCV_HART_BUS_ERROR && hart->status != RISCV_HART_EXCEPTION &&
hart->status != RISCV_HART_NOT_SUPP)
return 0;
if (hart->access_width == 32U) {
hart->access_width = 0U;
return 0; /* We are unable to read the misa register */
}
if (hart->access_width == 64U)
hart->access_width = 32U;
if (hart->access_width == 128U)
hart->access_width = 64U;
} while (hart->access_width != 0U);
DEBUG_WARN("Unable to read misa register\n");
/* If the above loop failed, we're done.. */
return 0U;
}
static uint32_t riscv_hart_access_width(uint8_t access_width)
{
if (access_width == 128U)
return RV_REG_ACCESS_128_BIT;
if (access_width == 64U)
return RV_REG_ACCESS_64_BIT;
return RV_REG_ACCESS_32_BIT;
}
static uint32_t riscv_csr_access_width(const uint16_t reg)
{
const uint16_t access_width = reg & RV_CSR_FORCE_MASK;
if (access_width == RV_CSR_FORCE_32_BIT)
return 32U;
if (access_width == RV_CSR_FORCE_64_BIT)
return 64U;
return 128U;
}
bool riscv_command_wait_complete(riscv_hart_s *const hart)
{
uint32_t status = RV_DM_ABST_STATUS_BUSY;
while (status & RV_DM_ABST_STATUS_BUSY) {
if (!riscv_dm_read(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, &status))
return false;
}
/* Shift out and mask off the command status, then reset the status on the Hart */
hart->status = (status >> 8U) & RISCV_HART_OTHER;
if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_CTRLSTATUS, RISCV_HART_OTHER << 8U))
return false;
if (hart->status != RISCV_HART_NO_ERROR)
DEBUG_WARN("CSR access failed: %u\n", hart->status);
/* If the command failed, return the failure */
return hart->status == RISCV_HART_NO_ERROR;
}
static bool riscv_csr_read_data(riscv_hart_s *const hart, void *const data, const uint8_t access_width)
{
uint32_t *const value = (uint32_t *)data;
/* If we're doing a 128-bit read, grab the upper-most 2 uint32_t's */
if (access_width == 128U &&
!(riscv_dm_read(hart->dbg_module, RV_DM_DATA3, value + 3) &&
riscv_dm_read(hart->dbg_module, RV_DM_DATA2, value + 2)))
return false;
/* If we're doing at least a 64-bit read, grab the next uint32_t */
if (access_width >= 64U && !riscv_dm_read(hart->dbg_module, RV_DM_DATA1, value + 1))
return false;
/* Finally grab the last and lowest uint32_t */
return riscv_dm_read(hart->dbg_module, RV_DM_DATA0, value);
}
static bool riscv_csr_progbuf_read(riscv_hart_s *const hart, const uint16_t reg, void *const data)
{
/* Set up the program buffer to read out the target CSR */
if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + 0U, RV_CSRR_A0 | ((reg & RV_CSR_ADDR_MASK) << 20U)))
return false;
/* If there's more than one progbuf register, set the second to an ebreak */
if (hart->progbuf_size > 1U && !riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + 1U, RV_EBREAK))
return false;
/* Execute the program buffer we've set up, reading a0 out to keep it safe */
bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND,
RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | RV_ABST_POSTEXEC |
riscv_hart_access_width(hart->access_width) | RV_GPR_A0);
/* Wait for both the register read and progbuf execution to complete */
result &= riscv_command_wait_complete(hart);
/* Extract the data read out by the GPR read */
uint32_t a0_value[3];
result &= riscv_csr_read_data(hart, a0_value, hart->access_width);
/* Now try to read out the requested data from a0 at the requested size */
result &= riscv_csr_read(hart, RV_GPR_A0 | (reg & RV_CSR_FORCE_MASK), data);
/* Whatever happened, now put back a0 from before */
return riscv_csr_write(hart, RV_GPR_A0, a0_value) && result;
}
bool riscv_csr_read(riscv_hart_s *const hart, const uint16_t reg, void *const data)
{
const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width;
DEBUG_TARGET("Reading %u-bit CSR %03x\n", access_width, reg & ~RV_CSR_FORCE_MASK);
/* If the read must be completed using the progbuf mechanism, switch to doing that */
if ((hart->flags & RV_HART_FLAG_DATA_GPR_ONLY) && (reg & RV_CSR_TYPE_MASK) != RV_CSR_TYPE_GPR)
return riscv_csr_progbuf_read(hart, reg, data);
/* Set up the register read and wait for it to complete */
if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND,
RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_READ | RV_REG_XFER | riscv_hart_access_width(access_width) |
(reg & ~RV_CSR_FORCE_MASK)) ||
!riscv_command_wait_complete(hart)) {
/*
* Figure out why the read failed - if it's because the command requested is unsupported,
* then restart and use the program buffer mechanism instead, marking the hart as supporting
* only GPR access using abstract commands. NB: we have no recourse if no progbuf regs are implemented.
*/
if (hart->status == RISCV_HART_NOT_SUPP && (reg & RV_CSR_TYPE_MASK) != RV_CSR_TYPE_GPR &&
hart->progbuf_size > 0U) {
hart->flags |= RV_HART_FLAG_DATA_GPR_ONLY;
return riscv_csr_read(hart, reg, data);
}
return false;
}
return riscv_csr_read_data(hart, data, access_width);
}
static bool riscv_csr_write_data(riscv_hart_s *const hart, const void *const data, const uint8_t access_width)
{
const uint32_t *const value = (const uint32_t *)data;
/* Regardless of width, we have to write data0 */
if (!riscv_dm_write(hart->dbg_module, RV_DM_DATA0, value[0]))
return false;
/* If we're doing at least a 64-bit wide access, set up data1 */
if (access_width >= 64U && !riscv_dm_write(hart->dbg_module, RV_DM_DATA1, value[1]))
return false;
/* For a 128-bit access, set up data2 and data3 too */
if (access_width == 128 &&
!(riscv_dm_write(hart->dbg_module, RV_DM_DATA2, value[2]) &&
riscv_dm_write(hart->dbg_module, RV_DM_DATA3, value[3])))
return false;
return true;
}
static bool riscv_csr_progbuf_write(riscv_hart_s *const hart, const uint16_t reg, const void *const data)
{
/* Read out a0 to keep it safe as the actions below clobber it */
uint32_t a0_value[3];
if (!riscv_csr_read(hart, RV_GPR_A0, a0_value))
return false;
/* Set up the program buffer to write to the target CSR */
if (!riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + 0U, RV_CSRW_A0 | ((reg & RV_CSR_ADDR_MASK) << 20U)))
return false;
/* If there's more than one progbuf register, set the second to an ebreak */
if (hart->progbuf_size > 1U && !riscv_dm_write(hart->dbg_module, RV_DM_PROGBUF_BASE + 1U, RV_EBREAK))
return false;
/* Figure out what access width should be used for the data phase of this */
const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width;
/* Set up the data for the write */
if (!riscv_csr_write_data(hart, data, access_width))
return false;
/* Execute the program buffer we've set up, writing the data into a0 first */
bool result = riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND,
RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | RV_ABST_POSTEXEC |
riscv_hart_access_width(access_width) | RV_GPR_A0);
/* Wait for both the register write and progbuf execution to complete */
result &= riscv_command_wait_complete(hart);
/* Whatever happened, now put back a0 from before */
return riscv_csr_write(hart, RV_GPR_A0, a0_value) && result;
}
bool riscv_csr_write(riscv_hart_s *const hart, const uint16_t reg, const void *const data)
{
const uint8_t access_width = (reg & RV_CSR_FORCE_MASK) ? riscv_csr_access_width(reg) : hart->access_width;
DEBUG_TARGET("Writing %u-bit CSR %03x\n", access_width, reg & ~RV_CSR_FORCE_MASK);
/* If the write must be completed using the progbuf mechanism, switch to doing that */
if ((hart->flags & RV_HART_FLAG_DATA_GPR_ONLY) && (reg & RV_CSR_TYPE_MASK) != RV_CSR_TYPE_GPR)
return riscv_csr_progbuf_write(hart, reg, data);
/* Set up the data registers based on the Hart native access size */
if (!riscv_csr_write_data(hart, data, access_width))
return false;
/* Configure and run the write */
if (!riscv_dm_write(hart->dbg_module, RV_DM_ABST_COMMAND,
RV_DM_ABST_CMD_ACCESS_REG | RV_ABST_WRITE | RV_REG_XFER | riscv_hart_access_width(access_width) |
(reg & ~RV_CSR_FORCE_MASK)))
return false;
if (!riscv_command_wait_complete(hart)) {
/*
* Figure out why the write failed - if it's because the command requested is unsupported,
* then restart and use the program buffer mechanism instead, marking the hart as supporting
* only GPR access using abstract commands. NB: we have no recourse if no progbuf regs are implemented.
*/
if (hart->status == RISCV_HART_NOT_SUPP && (reg & RV_CSR_TYPE_MASK) != RV_CSR_TYPE_GPR &&
hart->progbuf_size > 0U) {
hart->flags |= RV_HART_FLAG_DATA_GPR_ONLY;
return riscv_csr_write(hart, reg, data);
}
return false;
}
return true;
}
uint8_t riscv_mem_access_width(const riscv_hart_s *const hart, const target_addr_t address, const size_t length)
{
/* Grab the Hart's most maxmimally aligned possible write width */
uint8_t access_width = riscv_hart_access_width(hart->address_width) >> RV_ABST_MEM_ACCESS_SHIFT;
/* Convert the hart access width to a mask - for example, for 32-bit harts, this gives (1U << 2U) - 1U = 3U */
uint8_t align_mask = (1U << access_width) - 1U;
/* Mask out the bottom bits of both the address and length - anything that determines the alignment */
const uint8_t addr_bits = address & align_mask;
const uint8_t len_bits = length & align_mask;
/* bitwise-OR together the result so, for example, an odd address 2-byte read results in a pattern like 0bxx11 */
const uint8_t align = addr_bits | len_bits;
/* Loop down through the possible access widths till we find one suitably aligned */
for (; access_width; --access_width) {
if ((align & align_mask) == 0)
return access_width;
align_mask >>= 1U;
}
return access_width;
}
static void riscv_hart_discover_triggers(riscv_hart_s *const hart)
{
/* Discover how many breakpoints this hart supports */
hart->triggers = UINT32_MAX;
if (!riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &hart->triggers) ||
!riscv_csr_read(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &hart->triggers)) {
hart->triggers = 0;
return;
}
/*
* The value we read back will always be one less than the actual number supported
* as it represents the last valid index, rather than the last valid breakpoint.
*/
++hart->triggers;
DEBUG_INFO("Hart has %" PRIu32 " trigger slots available\n", hart->triggers);
/* If the hardware supports more slots than we do, cap it. */
if (hart->triggers > RV_TRIGGERS_MAX)
hart->triggers = RV_TRIGGERS_MAX;
/* Next, go through each one and map what it supports out into the trigger_uses slots */
for (uint32_t trigger = 0; trigger < hart->triggers; ++trigger) {
/* Select the trigger */
riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &trigger);
/* Try reading the trigger info */
uint32_t info = 0;
/*
* If the read succeeds but info is still 0, assume we're talking to something like a WCH device
* which'll do this despite not actually implementing the tinfo register. Handle it the same as
* the read explicitly failing.
*/
if (!riscv_csr_read(hart, RV_TRIG_INFO | RV_CSR_FORCE_32_BIT, &info) || !info) {
/*
* If that fails, it's probably because the tinfo register isn't implemented, so read
* the tdata1 register instead and extract the type from the MSb and build the info bitset from that
*/
if (hart->access_width == 32U) {
uint32_t data = 0;
riscv_csr_read(hart, RV_TRIG_DATA_1, &data);
/* The last 4 bits contain the trigger info */
info = data >> 28U;
} else {
uint64_t data = 0;
riscv_csr_read(hart, RV_TRIG_DATA_1, &data);
/* The last 4 bits contain the trigger info */
info = data >> 60U;
}
/* Info now needs converting from a value from 0 to 15 to having the correct bit set */
info = 1U << info;
}
/* If the 0th bit is set, this means the trigger is unsupported. Clear it to make testing easy */
info &= RV_TRIGGER_SUPPORT_MASK;
/* Now info's bottom 16 bits contain the supported trigger modes, so write this info to the slot in the hart */
hart->trigger_uses[trigger] = info;
DEBUG_TARGET("Hart trigger slot %" PRIu32 " modes: %04" PRIx32 "\n", trigger, info);
}
}
static void riscv_hart_memory_access_type(target_s *const target)
{
riscv_hart_s *const hart = riscv_hart_struct(target);
hart->flags &= (uint8_t)~RV_HART_FLAG_MEMORY_SYSBUS;
uint32_t sysbus_status;
/*
* Try reading the system bus access control and status register.
* Check if the value read back is non-zero for the sbasize field
*/
if (!riscv_dm_read(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, &sysbus_status) ||
!(sysbus_status & RV_DM_SYSBUS_STATUS_ADDR_WIDTH_MASK))
return;
/* If all the checks passed, we now have a valid system bus so can proceed with using it for memory access */
hart->flags = RV_HART_FLAG_MEMORY_SYSBUS | (sysbus_status & RV_HART_FLAG_ACCESS_WIDTH_MASK);
/* System Bus also means the target can have memory read without halting */
target->target_options |= TOPT_NON_HALTING_MEM_IO;
/* Make sure the system bus is not in any kind of error state */
(void)riscv_dm_write(hart->dbg_module, RV_DM_SYSBUS_CTRLSTATUS, 0x00407000U);
}
riscv_match_size_e riscv_breakwatch_match_size(const size_t size)
{
switch (size) {
case 8U:
return RV_MATCH_SIZE_8_BIT;
case 16U:
return RV_MATCH_SIZE_16_BIT;
case 32U:
return RV_MATCH_SIZE_32_BIT;
case 48U:
return RV_MATCH_SIZE_48_BIT;
case 64U:
return RV_MATCH_SIZE_64_BIT;
case 80U:
return RV_MATCH_SIZE_80_BIT;
case 96U:
return RV_MATCH_SIZE_96_BIT;
case 112U:
return RV_MATCH_SIZE_112_BIT;
case 128U:
return RV_MATCH_SIZE_128_BIT;
default:
break;
}
return 0U;
}
bool riscv_config_trigger(riscv_hart_s *const hart, const uint32_t trigger, const riscv_trigger_state_e mode,
const void *const config, const void *const address)
{
/*
* Select the trigger and write the new configuration to it provided by config.
* tdata1 (RV_TRIG_DATA_1) becomes mcontrol (match control) for this -
* see §5.2.9 pg53 of the RISC-V debug spec v0.13.2 for more details.
*/
const bool result = riscv_csr_write(hart, RV_TRIG_SELECT | RV_CSR_FORCE_32_BIT, &trigger) &&
riscv_csr_write(hart, RV_TRIG_DATA_1, config) && riscv_csr_write(hart, RV_TRIG_DATA_2, address);