Skip to main content

jtag_scanner/
builder.rs

1use std::{cell::OnceCell, path::Path, sync::Arc};
2
3use bitvec::vec::BitVec;
4
5use crate::{
6    Error,
7    jtag::{
8        Backend, JtagInterface, JtagState,
9        Register::{Data, Instruction},
10    },
11    scanner::PinScanner,
12    types::TapInfo,
13};
14
15/// The only way to build [PinScanner]; detects IDCODEs of devices on the chain,
16/// and scan for BSDL files.
17pub struct PinScannerBuilder {
18    interface: Box<dyn JtagInterface>,
19    idcodes: OnceCell<Vec<u32>>,
20    tap_infos: Vec<TapInfo>,
21    extest_keep_present: bool,
22}
23
24impl PinScannerBuilder {
25    /// All configuration for the `sm` needs to be done before wrapping
26    /// it with the builder.
27    pub fn new<T: Backend>(cable: T) -> Result<Self, Error> {
28        let interface = cable.into_interface()?;
29        Ok(Self {
30            interface,
31            idcodes: OnceCell::new(),
32            tap_infos: Vec::new(),
33            extest_keep_present: false,
34        })
35    }
36
37    /// Scans for BSDL files under `path` nonrecursively and tries to build [TapInfo]s
38    /// for all TAPs on the chain. Overrides infos added by [Self::add_tap_info].
39    ///
40    /// Supports forced bypass of some TAPs, which needs empty bypass files with naming
41    /// `<id_code_hex>_<ir_len>.bypass`.
42    pub fn scan_tap_info(&mut self, path: &Path) -> Result<(), Error> {
43        if !path.exists() || !path.is_dir() {
44            return Err("Invalid path".into());
45        }
46        let mut bsdls = Vec::new();
47        let mut bypassed = Vec::new();
48        for entry in path.read_dir().map_err(|e| e.to_string())? {
49            if let Ok(entry) = entry
50                && let Ok(file_type) = entry.file_type()
51                && file_type.is_file()
52                && let Some(ext) = entry.path().extension()
53            {
54                match ext.to_string_lossy().to_lowercase().as_str() {
55                    "bsd" | "bsdl" => match bsdl::Entity::parse(&entry.path(), None) {
56                        Ok(bsdl) => bsdls.push((bsdl, entry.path())),
57                        Err(e) => println!("Error parsing {}: {e}", entry.file_name().display()),
58                    },
59                    "bypass" => {
60                        let path = entry.path();
61                        let stem = path.file_stem().unwrap_or_default().to_string_lossy();
62                        let mut stem_iter = stem.split('_');
63                        let (Some(idcode), Some(ir_len)) = (stem_iter.next(), stem_iter.next())
64                        else {
65                            println!("Error parsing file name {}", entry.file_name().display());
66                            continue;
67                        };
68                        let (Ok(idcode), Ok(ir_len)) =
69                            (u32::from_str_radix(idcode, 16), ir_len.parse())
70                        else {
71                            println!("Error parsing file name {}", entry.file_name().display());
72                            continue;
73                        };
74                        bypassed.push((idcode, ir_len));
75                    }
76                    _ => (),
77                }
78            }
79        }
80
81        let mut tap_infos = Vec::new();
82        for idcode in self.get_id_codes()? {
83            if let Some((_, path)) = bsdls
84                .iter()
85                .find(|(i, _)| i.idcode_register()[0] == jtag_idcode::IDCode::new(idcode))
86            {
87                // NOTE: this makes sure that every `TapInfo::WithBsdl` in the chain
88                // has an `Arc` of a different address, used for identifying `PinId`.
89                // At the time of writing this code, `bsdl::Entity` is not clonable.
90                tap_infos.push(TapInfo::WithBsdl(Arc::new(
91                    bsdl::Entity::parse(path, None).unwrap(),
92                )));
93            } else if let Some(&(idcode, ir_len)) = bypassed.iter().find(|(i, _)| *i == idcode) {
94                tap_infos.push(TapInfo::AlwaysBypass { idcode, ir_len });
95            } else {
96                return Err(format!("BSDL or BYPASS file of {idcode:08x} not found").into());
97            };
98        }
99
100        self.tap_infos = tap_infos;
101        Ok(())
102    }
103
104    /// Adds `TapInfo` manually for the next TAP.
105    /// Call this for `builder.get_id_codes().len()` times.
106    pub fn add_tap_info(&mut self, info: TapInfo) -> Result<(), Error> {
107        let idcodes = self.get_id_codes()?;
108        if self.tap_infos.len() >= idcodes.len() {
109            return Err("already having enough `TapInfo`".into());
110        }
111        if !info.matches_idcode(idcodes[self.tap_infos.len()]) {
112            return Err("IDCODE mismatch".into());
113        }
114        self.tap_infos.push(info);
115        Ok(())
116    }
117
118    /// `true` indicates that output and output enable values from
119    /// the internal logic be kept when switching to EXTEST mode;
120    ///
121    /// `false` indicates that safe values defined by BSDL should be
122    /// loaded when switching to EXTEST mode.
123    ///
124    /// `false` by default.
125    pub fn extest_keep_present(&mut self, new_val: bool) {
126        self.extest_keep_present = new_val;
127    }
128
129    /// Builds the [PinScanner] if there are enough [TapInfo]s.
130    pub fn build(mut self) -> Result<PinScanner, Error> {
131        if self.tap_infos.len() != self.get_id_codes()?.len() {
132            return Err("not having enough `TapInfo`".into());
133        }
134
135        // verify IR length
136        let ir_len = self.interface.measure_ir_len()?;
137        let info_ir_len: usize = self.tap_infos.iter().map(|info| info.ir_len()).sum();
138        if info_ir_len != ir_len {
139            return Err(format!(
140                "IR length mismatch, current: {}, from given info: {}",
141                ir_len, info_ir_len,
142            )
143            .into());
144        }
145
146        PinScanner::build(
147            self.interface,
148            self.tap_infos,
149            ir_len,
150            self.extest_keep_present,
151        )
152    }
153
154    /// Gets IDCODEs of all TAPs on the chain, from TDI to TDO.
155    pub fn get_id_codes(&mut self) -> Result<Vec<u32>, Error> {
156        if let Some(idcodes) = self.idcodes.get() {
157            return Ok(idcodes.clone());
158        }
159
160        let dev_cnt = self.count_devices()?;
161
162        self.interface.mode_reset()?; // test logic reset
163        let dr_len = self.interface.measure_dr_len()?;
164        if dr_len != dev_cnt * 32 {
165            return Err(format!("Found device without IDCODE register, dr_len: {dr_len}").into());
166        }
167
168        self.interface.mode_reset()?; // test logic reset
169
170        self.interface.switch_mode(JtagState::CaptureDR, false)?;
171        // reads IDCODEs starting from the last device on the chain
172        let mut raw_idcodes = BitVec::repeat(false, 32 * dev_cnt);
173        self.interface.read_bits(Data, &mut raw_idcodes)?;
174        self.interface.mode_reset()?; // test logic reset
175
176        let mut idcodes = Vec::new();
177        for raw_idcode in raw_idcodes.as_raw_slice().chunks(4) {
178            let idcode = (raw_idcode[0] as u32)
179                | ((raw_idcode[1] as u32) << 8)
180                | ((raw_idcode[2] as u32) << 16)
181                | ((raw_idcode[3] as u32) << 24);
182            idcodes.push(idcode);
183        }
184        idcodes.reverse();
185
186        let _ = self.idcodes.set(idcodes.clone());
187        Ok(idcodes)
188    }
189
190    fn count_devices(&mut self) -> Result<usize, Error> {
191        self.interface.mode_reset()?; // test logic reset
192
193        // make sure all TAPs are in bypass mode
194        self.interface.write_repeated(Instruction, 1, 1024)?;
195        self.interface.switch_mode(JtagState::Idle, true)?;
196
197        self.interface.measure_dr_len()
198    }
199}