Skip to main content

airfrog_core/arm/
dp.rs

1// Copyright (C) 2025 Piers Finlayson <piers@piers.rocks>
2//
3// MIT License
4
5//! ARM Debug Port Registers
6
7use crate::arm::register::{DpRegister, ReadableRegister, RegisterDescriptor, WritableRegister};
8use crate::{register_data_r, register_data_rw, register_data_w};
9use alloc::{format, string::String};
10use core::fmt;
11
12/// IDCODE Register descriptor (read-only)
13pub struct IdCodeRegister;
14
15impl RegisterDescriptor for IdCodeRegister {
16    const ADDRESS: u8 = 0x00;
17    type Value = IdCode;
18}
19
20impl ReadableRegister for IdCodeRegister {}
21impl DpRegister for IdCodeRegister {}
22
23/// ARM Debug Port IDCODE register data
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
25pub struct IdCode(u32);
26
27impl IdCode {
28    pub const fn new(value: u32) -> Self {
29        IdCode(value)
30    }
31
32    pub fn data(&self) -> u32 {
33        self.0
34    }
35
36    /// Get revision field (bits 31:28)
37    pub fn revision(&self) -> u8 {
38        ((self.0 >> 28) & 0xF) as u8
39    }
40
41    /// Get part number (bits 27:20)
42    pub fn part_number(&self) -> u8 {
43        ((self.0 >> 20) & 0xFF) as u8
44    }
45
46    /// Get version (bits 15:12)
47    pub fn version(&self) -> u8 {
48        ((self.0 >> 12) & 0xF) as u8
49    }
50
51    /// Get MIN (bit 16)
52    pub fn min(&self) -> bool {
53        (self.0 & (1 << 16)) != 0
54    }
55
56    /// Get JEDEC desginer ID (bits 11:1)
57    pub fn designer_id(&self) -> u16 {
58        ((self.0 >> 1) & 0x7FF) as u16
59    }
60
61    /// Check if LSB is set (should always be 1 for valid IDCODE)
62    pub fn is_valid(&self) -> bool {
63        (self.0 & 1) == 1
64    }
65
66    /// Get RAO (bit 0)
67    pub fn rao(&self) -> bool {
68        (self.0 & 1) != 0
69    }
70
71    /// Get manufacturer name if known
72    pub fn designer_name(&self) -> &'static str {
73        match self.designer_id() {
74            0x23B => "ARM Ltd",
75            _ => "Unknown",
76        }
77    }
78
79    /// Get part description if known
80    pub fn part_description(&self) -> &'static str {
81        if self.designer_id() == 0x23B {
82            if self.part_number() == 0xBA {
83                match self.version() {
84                    0 => "ARM Debug Port v0",
85                    1 => "ARM Debug Port v1",
86                    2 => "ARM Debug Port v2",
87                    _ => "Unknown ARM Debug Port Version",
88                }
89            } else {
90                "unknown"
91            }
92        } else {
93            "unknown"
94        }
95    }
96
97    /// Check if this is an ARM Debug Port
98    pub fn is_arm_debug_port(&self) -> bool {
99        self.designer_id() == 0x23B && self.part_number() == 0xBA
100    }
101
102    pub const fn from_u32(value: u32) -> Self {
103        IdCode(value)
104    }
105}
106
107impl From<u32> for IdCode {
108    fn from(value: u32) -> Self {
109        Self::from_u32(value)
110    }
111}
112
113impl fmt::Display for IdCode {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        if f.alternate() {
116            if !self.is_valid() {
117                return write!(f, "Invalid IDCODE: 0x{:08X} (LSB not set)", self.0);
118            }
119
120            write!(f, "0x{:08X} {}", self.0, self.part_description())
121        } else {
122            write!(f, "0x{:08X}", self.0)
123        }
124    }
125}
126
127impl fmt::LowerHex for IdCode {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        write!(f, "0x{:08x}", self.0)
130    }
131}
132
133impl fmt::UpperHex for IdCode {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        write!(f, "0x{:08X}", self.0)
136    }
137}
138
139/// ABORT Register descriptor (write-only)
140pub struct AbortRegister;
141
142impl RegisterDescriptor for AbortRegister {
143    const ADDRESS: u8 = 0x00;
144    type Value = Abort;
145}
146
147impl WritableRegister for AbortRegister {}
148impl DpRegister for AbortRegister {}
149
150/// ARM Debug Port ABORT register data
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
152pub struct Abort(u32);
153
154// Standard register data impls
155register_data_w!(Abort);
156
157impl Abort {
158    const STKCMPCLR: u32 = 1 << 1;
159    const STKERRCLR: u32 = 1 << 2;
160    const WDERRCLR: u32 = 1 << 3;
161    const ORUNERRCLR: u32 = 1 << 4;
162
163    /// Set sticky compare clear flag
164    pub fn set_stkcmpclr(&mut self, enable: bool) {
165        if enable {
166            self.0 |= Self::STKCMPCLR;
167        } else {
168            self.0 &= !Self::STKCMPCLR;
169        }
170    }
171
172    /// Set sticky error clear flag
173    pub fn set_stkerrclr(&mut self, enable: bool) {
174        if enable {
175            self.0 |= Self::STKERRCLR;
176        } else {
177            self.0 &= !Self::STKERRCLR;
178        }
179    }
180
181    /// Set write data error clear flag
182    pub fn set_wderrclr(&mut self, enable: bool) {
183        if enable {
184            self.0 |= Self::WDERRCLR;
185        } else {
186            self.0 &= !Self::WDERRCLR;
187        }
188    }
189
190    /// Set overrun error clear flag
191    pub fn set_orunerrclr(&mut self, enable: bool) {
192        if enable {
193            self.0 |= Self::ORUNERRCLR;
194        } else {
195            self.0 &= !Self::ORUNERRCLR;
196        }
197    }
198}
199
200/// CTRL/STAT Register descriptor (read-write)
201pub struct CtrlStatRegister;
202
203impl RegisterDescriptor for CtrlStatRegister {
204    const ADDRESS: u8 = 0x04;
205    type Value = CtrlStat;
206}
207
208impl ReadableRegister for CtrlStatRegister {}
209impl WritableRegister for CtrlStatRegister {}
210impl DpRegister for CtrlStatRegister {}
211
212/// ARM Debug Port CTRL/STAT register data
213#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
214pub struct CtrlStat(u32);
215
216// Standard register data impls
217register_data_rw!(CtrlStat);
218
219impl CtrlStat {
220    // Field masks and shifts
221    const ORUNDETECT: u32 = 1 << 0;
222    const STICKYORUN: u32 = 1 << 1;
223
224    const TRNMODE_MASK: u32 = 0b11;
225    const TRNMODE_SHIFT: u32 = 2;
226
227    const STICKYCMP: u32 = 1 << 4;
228    const STICKYERR: u32 = 1 << 5;
229    const READOK: u32 = 1 << 6;
230    const WDATAERR: u32 = 1 << 7;
231
232    const MASKLANE_MASK: u32 = 0b1111;
233    const MASKLANE_SHIFT: u32 = 8;
234
235    const TRNCNT_MASK: u32 = 0b111111111111;
236    const TRNCNT_SHIFT: u32 = 12;
237
238    const CDBGRSTREQ: u32 = 1 << 26;
239    const CDBGRSTACK: u32 = 1 << 27;
240    const CDBGPWRUPREQ: u32 = 1 << 28;
241    const CDBGPWRUPACK: u32 = 1 << 29;
242    const CSYSPWRUPREQ: u32 = 1 << 30;
243    const CSYSPWRUPACK: u32 = 1 << 31;
244
245    // Transaction mode values
246    pub const TRNMODE_NORMAL: u32 = 0b00;
247    pub const TRNMODE_VERIFY: u32 = 0b01;
248    pub const TRNMODE_COMPARE: u32 = 0b10;
249
250    /// Get raw register value
251    pub fn value(&self) -> u32 {
252        self.0
253    }
254
255    /// Get overrun detection enable
256    pub fn orundetect(&self) -> bool {
257        self.0 & Self::ORUNDETECT != 0
258    }
259
260    /// Get sticky overrun flag
261    pub fn stickyorun(&self) -> bool {
262        self.0 & Self::STICKYORUN != 0
263    }
264
265    /// Get transaction mode
266    pub fn trnmode(&self) -> u32 {
267        (self.0 >> Self::TRNMODE_SHIFT) & Self::TRNMODE_MASK
268    }
269
270    /// Get sticky compare flag
271    pub fn stickycmp(&self) -> bool {
272        self.0 & Self::STICKYCMP != 0
273    }
274
275    /// Get sticky error flag
276    pub fn stickyerr(&self) -> bool {
277        self.0 & Self::STICKYERR != 0
278    }
279
280    /// Get read OK flag
281    pub fn readok(&self) -> bool {
282        self.0 & Self::READOK != 0
283    }
284
285    /// Get write data error flag
286    pub fn wdataerr(&self) -> bool {
287        self.0 & Self::WDATAERR != 0
288    }
289
290    /// Get mask lane value
291    pub fn masklane(&self) -> u32 {
292        (self.0 >> Self::MASKLANE_SHIFT) & Self::MASKLANE_MASK
293    }
294
295    /// Get transaction count
296    pub fn trncnt(&self) -> u32 {
297        (self.0 >> Self::TRNCNT_SHIFT) & Self::TRNCNT_MASK
298    }
299
300    /// Get debug reset request
301    pub fn cdbgrstreq(&self) -> bool {
302        self.0 & Self::CDBGRSTREQ != 0
303    }
304
305    /// Get debug reset acknowledge
306    pub fn cdbgrstack(&self) -> bool {
307        self.0 & Self::CDBGRSTACK != 0
308    }
309
310    /// Get debug power-up request
311    pub fn cdbgpwrupreq(&self) -> bool {
312        self.0 & Self::CDBGPWRUPREQ != 0
313    }
314
315    /// Get debug power-up acknowledge
316    pub fn cdbgpwrupack(&self) -> bool {
317        self.0 & Self::CDBGPWRUPACK != 0
318    }
319
320    /// Get system power-up request
321    pub fn csyspwrupreq(&self) -> bool {
322        self.0 & Self::CSYSPWRUPREQ != 0
323    }
324
325    /// Get system power-up acknowledge
326    pub fn csyspwrupack(&self) -> bool {
327        self.0 & Self::CSYSPWRUPACK != 0
328    }
329
330    pub fn has_errors(&self) -> bool {
331        self.stickyorun()
332            || self.stickycmp()
333            || self.stickyerr()
334            || self.wdataerr()
335            || self.stickyorun()
336    }
337
338    // Setters
339    /// Set overrun detection enable
340    pub fn set_orundetect(&mut self, enable: bool) {
341        if enable {
342            self.0 |= Self::ORUNDETECT;
343        } else {
344            self.0 &= !Self::ORUNDETECT;
345        }
346    }
347
348    /// Set sticky overrun flag
349    pub fn set_stickyorun(&mut self, enable: bool) {
350        if enable {
351            self.0 |= Self::STICKYORUN;
352        } else {
353            self.0 &= !Self::STICKYORUN;
354        }
355    }
356
357    /// Set transaction mode
358    pub fn set_trnmode(&mut self, mode: u32) {
359        self.0 = (self.0 & !(Self::TRNMODE_MASK << Self::TRNMODE_SHIFT))
360            | ((mode & Self::TRNMODE_MASK) << Self::TRNMODE_SHIFT);
361    }
362
363    /// Set sticky compare flag
364    pub fn set_stickycmp(&mut self, enable: bool) {
365        if enable {
366            self.0 |= Self::STICKYCMP;
367        } else {
368            self.0 &= !Self::STICKYCMP;
369        }
370    }
371
372    /// Set sticky error flag
373    pub fn set_stickyerr(&mut self, enable: bool) {
374        if enable {
375            self.0 |= Self::STICKYERR;
376        } else {
377            self.0 &= !Self::STICKYERR;
378        }
379    }
380
381    /// Set mask lane value
382    pub fn set_masklane(&mut self, mask: u32) {
383        self.0 = (self.0 & !(Self::MASKLANE_MASK << Self::MASKLANE_SHIFT))
384            | ((mask & Self::MASKLANE_MASK) << Self::MASKLANE_SHIFT);
385    }
386
387    /// Set transaction count
388    pub fn set_trncnt(&mut self, count: u32) {
389        self.0 = (self.0 & !(Self::TRNCNT_MASK << Self::TRNCNT_SHIFT))
390            | ((count & Self::TRNCNT_MASK) << Self::TRNCNT_SHIFT);
391    }
392
393    /// Set debug reset request
394    pub fn set_cdbgrstreq(&mut self, enable: bool) {
395        if enable {
396            self.0 |= Self::CDBGRSTREQ;
397        } else {
398            self.0 &= !Self::CDBGRSTREQ;
399        }
400    }
401
402    /// Set debug power-up request
403    pub fn set_cdbgpwrupreq(&mut self, enable: bool) {
404        if enable {
405            self.0 |= Self::CDBGPWRUPREQ;
406        } else {
407            self.0 &= !Self::CDBGPWRUPREQ;
408        }
409    }
410
411    /// Set system power-up request
412    pub fn set_csyspwrupreq(&mut self, enable: bool) {
413        if enable {
414            self.0 |= Self::CSYSPWRUPREQ;
415        } else {
416            self.0 &= !Self::CSYSPWRUPREQ;
417        }
418    }
419
420    // Formatters
421    /// Get error state description
422    pub fn error_states(&self) -> String {
423        let mut errors = [""; 5];
424        let mut count = 0;
425
426        if self.stickyorun() {
427            errors[count] = "STICKYORUN";
428            count += 1;
429        }
430        if self.stickycmp() {
431            errors[count] = "STICKYCMP";
432            count += 1;
433        }
434        if self.stickyerr() {
435            errors[count] = "STICKYERR";
436            count += 1;
437        }
438        if self.wdataerr() {
439            errors[count] = "WDATAERR";
440            count += 1;
441        }
442        if self.orundetect() {
443            errors[count] = "ORUNDETECT";
444            count += 1;
445        }
446
447        if count == 0 {
448            format!("No errors{}", if self.readok() { " (READOK)" } else { "" })
449        } else {
450            format!("Errors: {}", errors[..count].join(", "))
451        }
452    }
453
454    /// Get power state description
455    pub fn power_states(&self) -> String {
456        format!(
457            "Debug: {}/{}, System: {}/{}",
458            if self.cdbgpwrupreq() { "REQ" } else { "off" },
459            if self.cdbgpwrupack() { "ACK" } else { "nak" },
460            if self.csyspwrupreq() { "REQ" } else { "off" },
461            if self.csyspwrupack() { "ACK" } else { "nak" }
462        )
463    }
464
465    /// Get transaction state description
466    pub fn transaction_state(&self) -> String {
467        let mode = match self.trnmode() {
468            Self::TRNMODE_NORMAL => "Normal",
469            Self::TRNMODE_VERIFY => "Verify",
470            Self::TRNMODE_COMPARE => "Compare",
471            _ => "Reserved",
472        };
473        format!(
474            "Mode: {}, Count: {}, Mask: 0x{:x}",
475            mode,
476            self.trncnt(),
477            self.masklane()
478        )
479    }
480}
481
482/// SELECT Register descriptor (read-write)
483pub struct SelectRegister;
484
485impl RegisterDescriptor for SelectRegister {
486    const ADDRESS: u8 = 0x08;
487    type Value = Select;
488}
489
490impl ReadableRegister for SelectRegister {}
491impl WritableRegister for SelectRegister {}
492impl DpRegister for SelectRegister {}
493
494/// ARM Debug Port SELECT register data
495#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
496pub struct Select(u32);
497
498// Standard register data impls
499register_data_rw!(Select);
500
501impl Select {
502    // Field masks and shifts
503    const APSEL_MASK: u32 = 0xFF;
504    const APSEL_SHIFT: u32 = 24;
505
506    pub const DPBANKSEL_MASK: u32 = 0xF;
507    pub const DPBANKSEL_SHIFT: u32 = 0;
508
509    pub const APBANKSEL_MASK: u32 = 0xF;
510    pub const APBANKSEL_SHIFT: u32 = 4;
511
512    /// Get raw register value
513    pub fn value(&self) -> u32 {
514        self.0
515    }
516
517    /// Get access port select
518    pub fn apsel(&self) -> u32 {
519        (self.0 >> Self::APSEL_SHIFT) & Self::APSEL_MASK
520    }
521
522    /// Get DP bank select
523    pub fn dpbanksel(&self) -> u32 {
524        (self.0 >> Self::DPBANKSEL_SHIFT) & Self::DPBANKSEL_MASK
525    }
526
527    /// Get AP bank select
528    pub fn apbanksel(&self) -> u32 {
529        (self.0 >> Self::APBANKSEL_SHIFT) & Self::APBANKSEL_MASK
530    }
531
532    /// Set access port select
533    pub fn set_apsel(&mut self, apsel: u32) {
534        self.0 = (self.0 & !(Self::APSEL_MASK << Self::APSEL_SHIFT))
535            | ((apsel & Self::APSEL_MASK) << Self::APSEL_SHIFT);
536    }
537
538    /// Set DP bank select
539    pub fn set_dpbanksel(&mut self, banksel: u8) {
540        let banksel = banksel as u32;
541        self.0 = (self.0 & !(Self::DPBANKSEL_MASK << Self::DPBANKSEL_SHIFT))
542            | ((banksel & Self::DPBANKSEL_MASK) << Self::DPBANKSEL_SHIFT);
543    }
544
545    /// Set AP bank select
546    pub fn set_apbanksel(&mut self, banksel: u8) {
547        let banksel = banksel as u32;
548        self.0 = (self.0 & !(Self::APBANKSEL_MASK << Self::APBANKSEL_SHIFT))
549            | ((banksel & Self::APBANKSEL_MASK) << Self::APBANKSEL_SHIFT);
550    }
551
552    /// Set DP bank select from address
553    pub fn set_dpbanksel_from_addr(&mut self, addr: u8) {
554        let banksel = (addr >> 4) & 0xF;
555        self.set_dpbanksel(banksel);
556    }
557
558    /// Set AP bank select from address
559    pub fn set_apbanksel_from_addr(&mut self, addr: u8) {
560        let banksel = (addr >> 4) & 0xF;
561        self.set_apbanksel(banksel);
562    }
563
564    /// Get selection information string
565    pub fn selection_info(&self) -> String {
566        format!(
567            "AP: {}, DP Bank: {}, AP Bank: {}",
568            self.apsel(),
569            self.dpbanksel(),
570            self.apbanksel()
571        )
572    }
573}
574
575/// RDBUFF Register descriptor (read-only)
576pub struct RdBuffRegister;
577
578impl RegisterDescriptor for RdBuffRegister {
579    const ADDRESS: u8 = 0x0C;
580    type Value = RdBuff;
581}
582
583impl ReadableRegister for RdBuffRegister {}
584impl DpRegister for RdBuffRegister {}
585
586/// ARM Debug Port RDBUFF register data
587#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
588pub struct RdBuff(u32);
589
590// Standard register data impls
591register_data_r!(RdBuff);
592
593impl RdBuff {
594    /// Get the buffered data
595    pub fn data(&self) -> u32 {
596        self.0
597    }
598}
599
600/// TARGETSEL Register descriptor (write-only, DPv2)
601pub struct TargetSelRegister;
602
603impl RegisterDescriptor for TargetSelRegister {
604    const ADDRESS: u8 = 0x0C;
605    type Value = TargetSel;
606}
607
608impl WritableRegister for TargetSelRegister {}
609impl DpRegister for TargetSelRegister {}
610
611/// ARM Debug Port RDBUFF register data
612#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
613pub struct TargetSel(u32);
614
615register_data_w!(TargetSel);
616
617impl TargetSel {
618    pub const fn new(value: u32) -> Self {
619        TargetSel(value)
620    }
621
622    /// Get the buffered data
623    pub fn data(&self) -> u32 {
624        self.0
625    }
626}
627
628impl From<IdCode> for TargetSel {
629    fn from(id: IdCode) -> Self {
630        TargetSel(id.data())
631    }
632}
633
634// RP2040 Multi-Drop Targets
635pub const TARGET_SEL_RP2040_BASE: u32 = 0x01002927;
636pub const TARGET_SEL_RP2040_CORE0: TargetSel = TargetSel(TARGET_SEL_RP2040_BASE);
637pub const TARGET_SEL_RP2040_CORE1: TargetSel = TargetSel(0x1 << 28 | TARGET_SEL_RP2040_BASE);
638pub const TARGET_SEL_RP2040_RESCUE_DP: TargetSel = TargetSel(0xF << 28 | TARGET_SEL_RP2040_BASE);
639
640pub const TARGET_SEL_RP2040_ALL: [TargetSel; 3] = [
641    TARGET_SEL_RP2040_CORE0,
642    TARGET_SEL_RP2040_CORE1,
643    TARGET_SEL_RP2040_RESCUE_DP,
644];