probe_rs/vendor/nordicsemi/
mod.rs

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
//! Nordic Semiconductor vendor support.

use std::collections::{hash_map::Entry, HashMap};

use probe_rs_target::{
    chip_detection::{NordicConfigIdDetection, NordicFicrDetection},
    Chip,
};

use crate::{
    architecture::arm::{
        memory::ArmMemoryInterface, ArmChipInfo, ArmProbeInterface, FullyQualifiedApAddress,
    },
    config::{registry, DebugSequence},
    vendor::{
        nordicsemi::sequences::{nrf52::Nrf52, nrf53::Nrf5340, nrf91::Nrf9160},
        Vendor,
    },
    Error,
};

pub mod sequences;

/// Nordic Semiconductor
#[derive(docsplay::Display)]
pub struct NordicSemi;

impl Vendor for NordicSemi {
    fn try_create_debug_sequence(&self, chip: &Chip) -> Option<DebugSequence> {
        let sequence = if chip.name.starts_with("nRF5340") {
            DebugSequence::Arm(Nrf5340::create())
        } else if chip.name.starts_with("nRF52") {
            DebugSequence::Arm(Nrf52::create())
        } else if chip.name.starts_with("nRF9160") {
            DebugSequence::Arm(Nrf9160::create())
        } else {
            return None;
        };

        Some(sequence)
    }

    fn try_detect_arm_chip(
        &self,
        probe: &mut dyn ArmProbeInterface,
        chip_info: ArmChipInfo,
    ) -> Result<Option<String>, Error> {
        if chip_info.manufacturer.get() != Some("Nordic VLSI ASA") {
            return Ok(None);
        }

        // FIXME: This is a bit shaky but good enough for now.
        let access_port = &FullyQualifiedApAddress::v1_with_default_dp(0);
        let mut memory_interface = probe.memory_interface(access_port)?;

        // Cache to avoid reading the same register multiple times
        let mut register_values: HashMap<u32, u32> = HashMap::new();

        let families = registry::families_ref();
        for family in families.iter() {
            for info in family.chip_detection.iter() {
                let target = if let Some(spec) = info.as_nordic_ficr() {
                    ficr_info_detect(&mut register_values, memory_interface.as_mut(), spec)
                } else if let Some(spec) = info.as_nordic_configid() {
                    configid_detect(&mut register_values, memory_interface.as_mut(), spec)
                } else {
                    // Family does not have a Nordic specific detection method
                    continue;
                };

                if target.is_some() {
                    // We have a match
                    return Ok(target);
                }
            }
        }

        Ok(None)
    }
}

fn ficr_info_detect(
    register_values: &mut HashMap<u32, u32>,
    memory_interface: &mut dyn ArmMemoryInterface,
    spec: &NordicFicrDetection,
) -> Option<String> {
    // Read the PART register, if not already read
    if let Some(part) = read_register_cached(register_values, memory_interface, spec.part_address) {
        if part != spec.part {
            return None;
        }

        // Read the VARIANT register, if not already read
        if let Some(variant) =
            read_register_cached(register_values, memory_interface, spec.variant_address)
        {
            return spec.variants.get(&variant).cloned();
        }
    }

    None
}

fn configid_detect(
    register_values: &mut HashMap<u32, u32>,
    memory_interface: &mut dyn ArmMemoryInterface,
    spec: &NordicConfigIdDetection,
) -> Option<String> {
    // Read the CONFIGID register, if not already read
    if let Some(configid) =
        read_register_cached(register_values, memory_interface, spec.configid_address)
    {
        let hwid = configid & 0xFFFF;

        // Match the HWID
        return spec.hwid.get(&hwid).cloned();
    }

    None
}

fn read_register_cached(
    register_values: &mut HashMap<u32, u32>,
    memory_interface: &mut dyn ArmMemoryInterface,
    address: u32,
) -> Option<u32> {
    match register_values.entry(address) {
        Entry::Occupied(value) => Some(*value.get()),
        Entry::Vacant(e) => {
            if let Ok(value) = memory_interface.read_word_32(address as u64) {
                e.insert(value);
                Some(value)
            } else {
                None
            }
        }
    }
}