Skip to main content

jtag_scanner/
scanner.rs

1use bitvec::{order::Lsb0, vec::BitVec};
2
3use crate::{
4    AccessMask, Error,
5    ErrorKind::{BrokenChain, WrongPinType},
6    PinIdIter,
7    jtag::{
8        JtagInterface, JtagState,
9        Register::{Data, Instruction},
10    },
11    types::{PinId, PinState, TapCmd, TapInfo, TapState},
12};
13
14/// Basic structure for doing boundary scanning. Expects a fixed chain of devices.
15pub struct PinScanner {
16    sm: Box<dyn JtagInterface>,
17    tap_infos: Vec<TapInfo>,
18    tap_cmds: Vec<TapCmd>,
19    dr_in: BitVec<u8, Lsb0>,
20    dr_out: BitVec<u8, Lsb0>,
21    extest_keep_present: bool,
22}
23
24impl PinScanner {
25    pub(crate) fn build(
26        mut sm: Box<dyn JtagInterface>,
27        tap_infos: Vec<TapInfo>,
28        ir_len: usize,
29        extest_keep_present: bool,
30    ) -> Result<Self, Error> {
31        // make sure all TAPs are in bypass mode
32        sm.write_repeated(Instruction, 1, ir_len + 1)?;
33        sm.switch_mode(JtagState::Idle, true)?;
34        let cnt_taps = tap_infos.len();
35        Ok(Self {
36            sm,
37            tap_infos,
38            tap_cmds: vec![TapCmd::Bypass; cnt_taps],
39            dr_in: BitVec::repeat(false, cnt_taps),
40            dr_out: BitVec::repeat(false, cnt_taps),
41            extest_keep_present,
42        })
43    }
44
45    /// Count of TAPs on the chain.
46    pub fn tap_count(&self) -> usize {
47        self.tap_infos.len()
48    }
49
50    /// Information for TAPs on the chain.
51    pub fn tap_infos(&self) -> &[TapInfo] {
52        &self.tap_infos
53    }
54
55    /// [TapInfo] of a TAP; the index is of [Self::tap_infos].
56    pub fn get_tap_info(&self, i: usize) -> Result<TapInfo, Error> {
57        ChainView::get_tap_info(self, i)
58    }
59
60    /// Current state of a TAP; the index is of [Self::tap_infos].
61    pub fn get_tap_state(&self, i: usize) -> Result<TapState, Error> {
62        ChainView::get_tap_state(self, i)
63    }
64
65    /// Switches the state of a TAP with index `i`; the index can be determined by
66    /// finding in [Self::tap_infos].
67    pub fn set_tap_state(&mut self, i: usize, new_state: TapState) -> Result<(), Error> {
68        // NOTE: `TapCmd` is not always `TapState`, because `TapCmd` can be a transition
69        // value; the transition should always be done here, not elsewhere.
70
71        let state = self.get_tap_state(i)?;
72        if state == new_state {
73            return Ok(());
74        }
75        if state == TapState::Bypass && new_state == TapState::Extest {
76            // do it, so the `match` below don't need to handle Bypass -> Extest
77            self.set_tap_state(i, TapState::Sample)?;
78        }
79
80        match (self.get_tap_state(i)?, new_state) {
81            (TapState::Bypass, TapState::Sample) => {
82                // check IDCODEs again
83                self.command(i, TapCmd::Idcode)?;
84                let raw_idcodes = self.split_dr_data(&self.dr_in);
85                let raw_idcode = raw_idcodes[i].as_raw_slice();
86                let idcode = (raw_idcode[0] as u32)
87                    | ((raw_idcode[1] as u32) << 8)
88                    | ((raw_idcode[2] as u32) << 16)
89                    | ((raw_idcode[3] as u32) << 24);
90                if !self.tap_infos[i].matches_idcode(idcode) {
91                    self.command(i, TapCmd::Bypass)?;
92                    return Err("IDCODE check failed".into());
93                }
94                if !self.tap_cmds.contains(&TapCmd::Extest) {
95                    // check DR len
96                    self.command(i, TapCmd::Sample)?;
97                    if self.sm.measure_dr_len()? != self.dr_in.len() {
98                        self.command(i, TapCmd::Bypass)?;
99                        return Err("IDCODE check failed".into());
100                    }
101                }
102            }
103            (TapState::Sample, TapState::Extest) => {
104                // do PRELOAD first
105                self.command(i, TapCmd::Preload)?;
106                let mut splited_dr_out = self.split_dr_data(&self.dr_out);
107                let preload_values = if self.extest_keep_present {
108                    // try to preserve previous output and output enable status
109                    &self.split_dr_data(&self.dr_in)[i]
110                } else {
111                    &self.tap_infos[i].safe_bsr_value()
112                };
113                splited_dr_out[i].copy_from_bitslice(preload_values);
114                self.dr_out = self.merge_dr_data(&mut splited_dr_out);
115                self.update()?;
116            }
117            _ => (),
118        }
119        self.command(i, new_state.into())?;
120        Ok(())
121    }
122
123    pub(crate) fn is_extesting(&self) -> bool {
124        self.tap_cmds.contains(&TapCmd::Extest)
125    }
126
127    /// Iterates through all [PinId]s for a TAP in the chain.
128    /// `i_tap` can be determined by finding in [Self::tap_infos].
129    pub fn pin_ids_for_tap(&self, i_tap: usize) -> Result<PinIdIter, Error> {
130        let TapInfo::WithBsdl(bsdl) = self.get_tap_info(i_tap)? else {
131            return Err("not loaded BSDL of this TAP".into());
132        };
133        Ok(PinIdIter::new(i_tap, bsdl))
134    }
135
136    /// Gets a pin identifier by its port name in the BSDL file.
137    /// `i_tap` can be determined by finding in [Self::tap_infos].
138    ///
139    /// For a pin in a bus port, `pin_name` should be "PORT_NAME(i)".
140    pub fn get_pin_id(&self, i_tap: usize, pin_name: &str) -> Result<PinId, Error> {
141        let TapInfo::WithBsdl(bsdl) = self.get_tap_info(i_tap)? else {
142            return Err("not loaded BSDL of this TAP".into());
143        };
144        PinId::from_name(i_tap, bsdl, pin_name)
145    }
146
147    /// Gets the pin state at the timepoint before the last [Self::update] call
148    /// applied newly set pin states.
149    pub fn get_pin_state(&self, pin_id: &PinId) -> Result<PinState, Error> {
150        BsrView::new(self, None).get(pin_id)
151    }
152
153    /// Sets the new pin state that will be applyed on the next [Self::update] call.
154    /// Only for setting pins with output cell in EXTEST mode.
155    ///
156    /// If `new_state` is `Some(state)`, `state` will be set in the output cell,
157    /// and `true` will be set in the output enable cell if the pin is tristated.
158    ///
159    /// If `new_state` is `None`, `false` will be set in the output enable cell
160    /// if the pin is tristated, otherwise an error is returned.
161    pub fn set_pin_state(&mut self, pin_id: &PinId, new_state: Option<bool>) -> Result<(), Error> {
162        BsrEdit::new(self, None).set(pin_id, new_state)
163    }
164
165    /// Applies [Self::set_pin_state] calls since last [Self::update] and refreshes
166    /// the capture buffer for later [Self::get_pin_state] calls with the values
167    /// before setting new pin states.
168    pub fn update(&mut self) -> Result<(), Error> {
169        self.sm.switch_mode(JtagState::CaptureDR, false)?;
170        self.sm
171            .read_write_bits(Data, &mut self.dr_in, &self.dr_out)?;
172        self.check_dr_in()?;
173        if self.is_extesting() {
174            // flush it (which passes through UpdateDR), otherwise it may be
175            // executed in the next cycle.
176            self.sm.switch_mode(JtagState::Idle, true)?;
177        }
178        Ok(())
179    }
180
181    /// Applies [Self::set_pin_state] calls and then does [Self::update], so that
182    /// later [Self::get_pin_state] calls will be influenced by the newly set values.
183    /// This makes no sense if no TAP is in EXTEST mode.
184    pub fn write_update(&mut self) -> Result<(), Error> {
185        if !self.is_extesting() {
186            return self.update();
187        }
188
189        // apply written bits. NOTE: this writes twice as much data before flushing
190        // compared to `Self::update`, so it is more likely to trigger some bug in
191        // the cable driver, while it is faster than flushing here without such bug.
192        // Such bug needs to be fixed in the driver because a workaround here is not
193        // enough (it may be triggered anyway if the BSR chain is long enough).
194        self.sm.write_bits(Data, &self.dr_out, false)?;
195        self.sm.switch_mode(JtagState::Idle, false)?;
196
197        // flush and capture DR again after updating DR.
198        self.sm
199            .read_write_bits(Data, &mut self.dr_in, &self.dr_out)?;
200        self.check_dr_in()?;
201        Ok(())
202    }
203
204    /// Switches all TAPs to BYPASS state.
205    pub fn release(&mut self) -> Result<(), Error> {
206        self.sm.mode_reset()?;
207        let ir_len: usize = self.tap_infos().iter().map(|t| t.ir_len()).sum();
208        self.sm.write_repeated(Instruction, 1, ir_len + 1)?;
209        self.sm.switch_mode(JtagState::UpdateIR, true)?;
210        self.sm.switch_mode(JtagState::Idle, true)?;
211        self.tap_cmds = vec![TapCmd::Bypass; self.tap_count()];
212        self.dr_in.truncate(self.tap_count());
213        self.dr_out.truncate(self.tap_count());
214        Ok(())
215    }
216
217    /// Switches the IR command for the TAP of index `i`.
218    ///
219    /// XXX: currently `dr_in` and `dr_out` are simply being resized here
220    /// when switching to a DR of different length, extended with 0s if needed.
221    fn command(&mut self, i: usize, tap_cmd: TapCmd) -> Result<(), Error> {
222        let old_dr_len = self.tap_infos[i].dr_len(self.tap_cmds[i]);
223        let new_dr_len = self.tap_infos[i].dr_len(tap_cmd);
224        if old_dr_len != new_dr_len {
225            let mut splited_dr_in = self.split_dr_data(&self.dr_in);
226            splited_dr_in[i].resize(new_dr_len, false);
227            self.dr_in = self.merge_dr_data(&mut splited_dr_in);
228            let mut splited_dr_out = self.split_dr_data(&self.dr_out);
229            splited_dr_out[i].resize(new_dr_len, false);
230            self.dr_out = self.merge_dr_data(&mut splited_dr_out);
231        }
232        self.tap_cmds[i] = tap_cmd;
233        let ir_data = {
234            let mut ir_data = BitVec::new();
235            // `i` is the index starting from TDI, the order of pushing opcodes
236            // needs to be reversed so that it starts from TDO.
237            for (i, &cmd) in self.tap_cmds.iter().enumerate().rev() {
238                self.tap_infos[i].push_opcode(cmd, &mut ir_data);
239            }
240            ir_data
241        };
242        let mut ir_readback = BitVec::new();
243        self.sm
244            .read_write_bits(Instruction, &mut ir_readback, &ir_data)?;
245        self.sm.switch_mode(JtagState::Idle, true)?;
246        if ir_readback.all() {
247            return Error::result(BrokenChain, "TDO holds 1");
248        } else if ir_readback.not_any() {
249            return Error::result(BrokenChain, "TDO holds 0");
250        }
251        self.update()?;
252        Ok(())
253    }
254
255    // Checks the chain if all ones or all zeros are detected in captured DR.
256    fn check_dr_in(&mut self) -> Result<(), Error> {
257        if self.dr_in.all() || self.dr_in.not_any() {
258            let test_pattern = {
259                let mut pattern = BitVec::with_capacity(self.dr_in.len());
260                for _ in 0..self.dr_in.len() / 2 {
261                    pattern.push(true);
262                    pattern.push(false);
263                }
264                if pattern.len() < self.dr_in.len() {
265                    pattern.push(false);
266                }
267                pattern
268            };
269            self.sm.write_bits(Data, &test_pattern, false)?;
270            // No UpdateDR before writing back `dr_out`.
271            let mut readback = BitVec::new();
272            self.sm.read_write_bits(Data, &mut readback, &self.dr_out)?;
273            if readback == test_pattern {
274                return Ok(());
275            } else {
276                let msg = if readback.all() {
277                    "TDO holds 1"
278                } else if readback.not_any() {
279                    "TDO holds 0"
280                } else {
281                    &format!("disturbing, {readback}, expected: {test_pattern}")
282                };
283                return Error::result(BrokenChain, msg);
284            }
285        }
286        Ok(())
287    }
288
289    fn split_dr_data(&self, dr: &BitVec<u8, Lsb0>) -> Vec<BitVec<u8, Lsb0>> {
290        let mut result = Vec::new();
291        let mut slice_rem = &dr[..];
292        // `i` counts from TDI, reverse it to make the loop start from TDO.
293        for (i, &cmd) in self.tap_cmds.iter().enumerate().rev() {
294            let dr_len = self.tap_infos()[i].dr_len(cmd);
295            let splited = &slice_rem[..dr_len];
296            slice_rem = &slice_rem[dr_len..];
297            let mut splited = BitVec::from_bitslice(splited);
298            splited.force_align();
299            result.push(splited);
300        }
301        // after this, the `result` is from TDI to TDO.
302        result.reverse();
303        result
304    }
305
306    fn merge_dr_data(&self, splited_dr: &mut [BitVec<u8, Lsb0>]) -> BitVec<u8, Lsb0> {
307        let mut result = BitVec::new();
308        // reverse the order in `splited_dr`, so the loop starts from TDO.
309        for dr in splited_dr.iter_mut().rev() {
310            dr.force_align();
311            result.extend_from_bitslice(dr);
312        }
313        result
314    }
315}
316
317impl PinScanner {
318    /// Executes queued read + write operations for `count` times.
319    ///
320    /// This can be used to speed up a group of transfers if each item
321    /// in the group does not depend on the result of the previous item.
322    ///
323    /// `writer` and `reader` will both be called for `count` times.
324    /// The cycle counter counts towards `count` starting from 0.
325    /// On startup, the current BSR output buffer is copied as the
326    /// buffer for `writer`.
327    ///
328    /// At the beginning of each cycle, `writer` is called with the output
329    /// buffer and the cycle counter value; any change to the buffer made by
330    /// `writer` will be captured and queued for writing to the device, and
331    /// preserved for the next `writer` call. When it is actually executed,
332    /// the captured states before this write is passed to the corresponding
333    /// call to `reader`, along with the same cycle counter value.
334    ///
335    /// Returns successfully when the last write is finished; before returning,
336    /// the output buffer (possibly modified by the last `writer` call) is
337    /// applied back to `self`, and [Self::update] is called.
338    ///
339    /// ```rust,ignore
340    /// let pins = [
341    ///     "PB0", "PB1", "PB5", "PB6", "PB7", "PB8", "PB9", "PB10", "PB11", "PB12", "PB13", "PB14",
342    ///     "PB15", "PC13", "PC14", "PC15",
343    /// ];
344    /// let pins: Vec<_> = pins
345    ///     .iter()
346    ///     .map(|n| scanner.get_pin_id(i_c6t6, *n).unwrap())
347    ///     .collect();
348    ///
349    /// scanner.set_tap_state(i_c6t6, TapState::Extest)?;
350    /// scanner.batch(
351    ///     65536,
352    ///     |i, edit| {
353    ///         // sets value `i` to the 16-bit "bus".
354    ///         for (i_pin, pin) in pins.iter().enumerate() {
355    ///             let val = ((i >> i_pin) & 1) == 1;
356    ///             edit.set(pin, Some(val))?;
357    ///         }
358    ///         Ok(())
359    ///     },
360    ///     |i, view| {
361    ///         let i_prev = if i > 0 {
362    ///             i - 1
363    ///         } else {
364    ///             return Ok(());
365    ///         };
366    ///         // `view` provides pin states before the `i`th edit is applied,
367    ///         // which equals the values set in `i - 1`th edit.
368    ///         let mut val = 0;
369    ///         for (i_pin, pin) in pins.iter().enumerate() {
370    ///             if view.get(pin)?.input().unwrap() {
371    ///                 val |= 1 << i_pin;
372    ///             }
373    ///         }
374    ///         assert_eq!(val, i_prev);
375    ///         Ok(())
376    ///     },
377    /// )?;
378    /// ```
379    pub fn batch(
380        &mut self,
381        count: usize,
382        writer: impl FnMut(usize, &mut BsrEdit) -> Result<(), Error>,
383        reader: impl FnMut(usize, &BsrView) -> Result<(), Error>,
384    ) -> Result<(), Error> {
385        self.batch_with_mask(count, writer, reader, None)
386    }
387
388    pub(crate) fn batch_with_mask(
389        &mut self,
390        count: usize,
391        mut writer: impl FnMut(usize, &mut BsrEdit) -> Result<(), Error>,
392        mut reader: impl FnMut(usize, &BsrView) -> Result<(), Error>,
393        access_mask: Option<&AccessMask>,
394    ) -> Result<(), Error> {
395        self.write_update()?;
396        let mut dr_out = self.dr_out.clone();
397        let mut bsr_edit = BsrEdit {
398            tap_infos: &self.tap_infos,
399            tap_cmds: &self.tap_cmds,
400            dr_out: &mut dr_out,
401            access_mask,
402        };
403        let res = self.sm.batch(
404            count,
405            &mut |i, write_buf| {
406                writer(i, &mut bsr_edit)?;
407                write_buf.resize(bsr_edit.dr_out.len(), false);
408                write_buf.copy_from_bitslice(bsr_edit.dr_out);
409                Ok(())
410            },
411            &mut |i, read_buf| {
412                let bsr_view = BsrView {
413                    tap_infos: &self.tap_infos,
414                    tap_cmds: &self.tap_cmds,
415                    dr_in: read_buf,
416                    access_mask,
417                };
418                reader(i, &bsr_view)
419            },
420        );
421        self.dr_out.copy_from_bitslice(&dr_out);
422        self.update()?;
423        res
424    }
425}
426
427impl Drop for PinScanner {
428    fn drop(&mut self) {
429        let _ = self
430            .release()
431            .inspect_err(|e| eprintln!("failed to set taps to bypass mode: {e}"));
432    }
433}
434
435/// Read-only BSR buffer. Used in the batch operation API.
436pub struct BsrView<'a, 'm> {
437    tap_infos: &'a [TapInfo],
438    tap_cmds: &'a [TapCmd],
439    dr_in: &'a BitVec<u8, Lsb0>,
440    access_mask: Option<&'m AccessMask>,
441}
442
443/// Write-only BSR buffer. Used in the batch operation API.
444pub struct BsrEdit<'a, 'm> {
445    tap_infos: &'a [TapInfo],
446    tap_cmds: &'a [TapCmd],
447    dr_out: &'a mut BitVec<u8, Lsb0>,
448    access_mask: Option<&'m AccessMask>,
449}
450
451impl<'a, 'm> BsrView<'a, 'm> {
452    pub(crate) fn new(scanner: &'a PinScanner, access_mask: Option<&'m AccessMask>) -> Self {
453        Self {
454            tap_infos: scanner.tap_infos(),
455            tap_cmds: scanner.tap_cmds(),
456            dr_in: &scanner.dr_in,
457            access_mask,
458        }
459    }
460
461    /// Gets the pin state at the timepoint when the BSR is captured.
462    pub fn get(&self, pin_id: &PinId) -> Result<PinState, Error> {
463        self.verify_pin_id(pin_id, false)?;
464        let tap_state = self.get_tap_state(pin_id.tap_index())?;
465        if tap_state == TapState::Bypass {
466            return Err("the chip that owns the pin is bypassed".into());
467        }
468        let get_val = |cell: Option<usize>| {
469            cell.map(|i| self.index_in_dr(pin_id.tap_index(), i))
470                .map(|i| self.dr_in[i])
471        };
472        Ok(PinState {
473            input: get_val(pin_id.cell_i()),
474            output: get_val(pin_id.cell_o()),
475            output_enable: get_val(pin_id.cell_oe()).map(|v| v != pin_id.cell_oe_disabler()),
476        })
477    }
478}
479
480impl<'a, 'm> BsrEdit<'a, 'm> {
481    pub(crate) fn new(scanner: &'a mut PinScanner, access_mask: Option<&'m AccessMask>) -> Self {
482        Self {
483            tap_infos: &scanner.tap_infos,
484            tap_cmds: &scanner.tap_cmds,
485            dr_out: &mut scanner.dr_out,
486            access_mask,
487        }
488    }
489
490    /// Sets the new pin state that will be applied when this queued item is finished.
491    /// Only for setting pins with output cell in EXTEST mode.
492    ///
493    /// If `new_state` is `Some(state)`, `state` will be set in the output cell,
494    /// and `true` will be set in the output enable cell if the pin is tristated.
495    ///
496    /// If `new_state` is `None`, `false` will be set in the output enable cell
497    /// if the pin is tristated, otherwise an error is returned.
498    pub fn set(&mut self, pin_id: &PinId, new_state: Option<bool>) -> Result<(), Error> {
499        self.verify_pin_id(pin_id, true)?;
500        if self.get_tap_state(pin_id.tap_index())? != TapState::Extest {
501            return Err("the chip that owns the pin is not in EXTEST mode".into());
502        }
503        if let Some(new_state) = new_state {
504            let Some(i) = pin_id
505                .cell_o()
506                .map(|i| self.index_in_dr(pin_id.tap_index(), i))
507            else {
508                return Error::result(WrongPinType, "not output pin");
509            };
510            // println!("setting dr[{}] to {:?}", i, new_state);
511            *self.dr_out.get_mut(i).unwrap() = new_state;
512        }
513        if let Some(i) = pin_id
514            .cell_oe()
515            .map(|i| self.index_in_dr(pin_id.tap_index(), i))
516        {
517            // disable OE if new_state is None; enable otherwise
518            let new_state_oe = if pin_id.cell_oe_disabler() {
519                new_state.is_none()
520            } else {
521                new_state.is_some()
522            };
523            // println!("setting dr[{}] to {:?}", i, new_state_oe);
524            *self.dr_out.get_mut(i).unwrap() = new_state_oe
525        } else if new_state.is_none() {
526            return Error::result(WrongPinType, "not tristated pin");
527        }
528        Ok(())
529    }
530}
531
532pub(crate) trait ChainView {
533    /// Information for TAPs on the chain.
534    fn tap_infos(&self) -> &[TapInfo];
535
536    /// States of TAPs on the chain.
537    fn tap_cmds(&self) -> &[TapCmd];
538
539    /// Checker for pins allowed to use, `None` if all allowed.
540    fn access_mask(&self) -> Option<&AccessMask>;
541
542    /// Count of TAPs on the chain.
543    fn tap_count(&self) -> usize {
544        self.tap_infos().len()
545    }
546
547    /// [TapInfo] of a TAP; the index is of [Self::tap_infos].
548    fn get_tap_info(&self, i: usize) -> Result<TapInfo, Error> {
549        if i >= self.tap_count() {
550            return Err("given TAP index exceeds TAP count".into());
551        }
552        Ok(self.tap_infos()[i].clone())
553    }
554
555    /// Current state of a TAP; the index is of [Self::tap_infos].
556    fn get_tap_state(&self, i: usize) -> Result<TapState, Error> {
557        if i >= self.tap_count() {
558            return Err("given TAP index exceeds TAP count".into());
559        }
560        Ok(match self.tap_cmds()[i] {
561            TapCmd::Bypass => TapState::Bypass,
562            TapCmd::Sample => TapState::Sample,
563            TapCmd::Extest => TapState::Extest,
564            _ => unreachable!(),
565        })
566    }
567
568    /// Checks if a given `PinId` belongs to the current `PinScanner` instance,
569    /// and writing on the pin is allowed.
570    fn verify_pin_id(&self, pin_id: &PinId, for_write: bool) -> Result<(), Error> {
571        if for_write && !pin_id.is_output() {
572            return Error::result(WrongPinType, "not output pin");
573        }
574        let tap_info = self.get_tap_info(pin_id.tap_index())?;
575        if tap_info.sessioned_tap_id() != pin_id.sessioned_tap_id() {
576            return Err("bad pin ID".into());
577        }
578        if let Some(mask) = self.access_mask() {
579            mask.check_access(pin_id, for_write)?;
580        }
581        Ok(())
582    }
583
584    /// Converts cell index in a DR to the index in the DR chain of TAPs.
585    /// NOTE: `i_tap` and `i_cell` is unchecked here.
586    fn index_in_dr(&self, i_tap: usize, i_cell: usize) -> usize {
587        // reverse it so it starts from TDO.
588        self.tap_infos()
589            .iter()
590            .enumerate()
591            .rev()
592            .take(self.tap_count() - i_tap - 1)
593            .map(|(i, tap)| tap.dr_len(self.tap_cmds()[i]))
594            .sum::<usize>()
595            + i_cell
596    }
597}
598
599impl<'a, 'm> ChainView for BsrView<'a, 'm> {
600    fn tap_infos(&self) -> &[TapInfo] {
601        self.tap_infos
602    }
603    fn tap_cmds(&self) -> &[TapCmd] {
604        self.tap_cmds
605    }
606    fn access_mask(&self) -> Option<&AccessMask> {
607        self.access_mask
608    }
609}
610
611impl<'a, 'm> ChainView for BsrEdit<'a, 'm> {
612    fn tap_infos(&self) -> &[TapInfo] {
613        self.tap_infos
614    }
615    fn tap_cmds(&self) -> &[TapCmd] {
616        self.tap_cmds
617    }
618    fn access_mask(&self) -> Option<&AccessMask> {
619        self.access_mask
620    }
621}
622
623impl ChainView for PinScanner {
624    fn tap_infos(&self) -> &[TapInfo] {
625        &self.tap_infos
626    }
627    fn tap_cmds(&self) -> &[TapCmd] {
628        &self.tap_cmds
629    }
630    fn access_mask(&self) -> Option<&AccessMask> {
631        None
632    }
633}