use std::{cell::OnceCell, path::Path, sync::Arc};
use bitvec::vec::BitVec;
use crate::{
Error,
jtag::{
Backend, JtagInterface, JtagState,
Register::{Data, Instruction},
},
scanner::PinScanner,
types::TapInfo,
};
pub struct PinScannerBuilder {
interface: Box<dyn JtagInterface>,
idcodes: OnceCell<Vec<u32>>,
tap_infos: Vec<TapInfo>,
extest_keep_present: bool,
}
impl PinScannerBuilder {
pub fn new<T: Backend>(cable: T) -> Result<Self, Error> {
let interface = cable.into_interface()?;
Ok(Self {
interface,
idcodes: OnceCell::new(),
tap_infos: Vec::new(),
extest_keep_present: false,
})
}
pub fn scan_tap_info(&mut self, path: &Path) -> Result<(), Error> {
if !path.exists() || !path.is_dir() {
return Err("Invalid path".into());
}
let mut bsdls = Vec::new();
let mut bypassed = Vec::new();
for entry in path.read_dir().map_err(|e| e.to_string())? {
if let Ok(entry) = entry
&& let Ok(file_type) = entry.file_type()
&& file_type.is_file()
&& let Some(ext) = entry.path().extension()
{
match ext.to_string_lossy().to_lowercase().as_str() {
"bsd" | "bsdl" => match bsdl::Entity::parse(&entry.path(), None) {
Ok(bsdl) => bsdls.push((bsdl, entry.path())),
Err(e) => println!("Error parsing {}: {e}", entry.file_name().display()),
},
"bypass" => {
let path = entry.path();
let stem = path.file_stem().unwrap_or_default().to_string_lossy();
let mut stem_iter = stem.split('_');
let (Some(idcode), Some(ir_len)) = (stem_iter.next(), stem_iter.next())
else {
println!("Error parsing file name {}", entry.file_name().display());
continue;
};
let (Ok(idcode), Ok(ir_len)) =
(u32::from_str_radix(idcode, 16), ir_len.parse())
else {
println!("Error parsing file name {}", entry.file_name().display());
continue;
};
bypassed.push((idcode, ir_len));
}
_ => (),
}
}
}
let mut tap_infos = Vec::new();
for idcode in self.get_id_codes()? {
if let Some((_, path)) = bsdls
.iter()
.find(|(i, _)| i.idcode_register()[0] == jtag_idcode::IDCode::new(idcode))
{
tap_infos.push(TapInfo::WithBsdl(Arc::new(
bsdl::Entity::parse(path, None).unwrap(),
)));
} else if let Some(&(idcode, ir_len)) = bypassed.iter().find(|(i, _)| *i == idcode) {
tap_infos.push(TapInfo::AlwaysBypass { idcode, ir_len });
} else {
return Err(format!("BSDL or BYPASS file of {idcode:08x} not found").into());
};
}
self.tap_infos = tap_infos;
Ok(())
}
pub fn add_tap_info(&mut self, info: TapInfo) -> Result<(), Error> {
let idcodes = self.get_id_codes()?;
if self.tap_infos.len() >= idcodes.len() {
return Err("already having enough `TapInfo`".into());
}
if !info.matches_idcode(idcodes[self.tap_infos.len()]) {
return Err("IDCODE mismatch".into());
}
self.tap_infos.push(info);
Ok(())
}
pub fn extest_keep_present(&mut self, new_val: bool) {
self.extest_keep_present = new_val;
}
pub fn build(mut self) -> Result<PinScanner, Error> {
if self.tap_infos.len() != self.get_id_codes()?.len() {
return Err("not having enough `TapInfo`".into());
}
let ir_len = self.interface.measure_ir_len()?;
let info_ir_len: usize = self.tap_infos.iter().map(|info| info.ir_len()).sum();
if info_ir_len != ir_len {
return Err(format!(
"IR length mismatch, current: {}, from given info: {}",
ir_len, info_ir_len,
)
.into());
}
PinScanner::build(
self.interface,
self.tap_infos,
ir_len,
self.extest_keep_present,
)
}
pub fn get_id_codes(&mut self) -> Result<Vec<u32>, Error> {
if let Some(idcodes) = self.idcodes.get() {
return Ok(idcodes.clone());
}
let dev_cnt = self.count_devices()?;
self.interface.mode_reset()?; let dr_len = self.interface.measure_dr_len()?;
if dr_len != dev_cnt * 32 {
return Err(format!("Found device without IDCODE register, dr_len: {dr_len}").into());
}
self.interface.mode_reset()?;
self.interface.switch_mode(JtagState::CaptureDR, false)?;
let mut raw_idcodes = BitVec::repeat(false, 32 * dev_cnt);
self.interface.read_bits(Data, &mut raw_idcodes)?;
self.interface.mode_reset()?;
let mut idcodes = Vec::new();
for raw_idcode in raw_idcodes.as_raw_slice().chunks(4) {
let idcode = (raw_idcode[0] as u32)
| ((raw_idcode[1] as u32) << 8)
| ((raw_idcode[2] as u32) << 16)
| ((raw_idcode[3] as u32) << 24);
idcodes.push(idcode);
}
idcodes.reverse();
let _ = self.idcodes.set(idcodes.clone());
Ok(idcodes)
}
fn count_devices(&mut self) -> Result<usize, Error> {
self.interface.mode_reset()?;
self.interface.write_repeated(Instruction, 1, 1024)?;
self.interface.switch_mode(JtagState::Idle, true)?;
self.interface.measure_dr_len()
}
}