1#![doc = include_str!("../README.md")]
4
5use std::{fmt, time::Duration, convert::{From, TryFrom}, path::Path, fs::File, io::Read};
6use num_enum::TryFromPrimitive;
7use indicatif::{ProgressBar, ProgressStyle};
8use jtagdap::jtag::{IDCODE, JTAGTAP, JTAGChain, Error as JTAGError};
9use jtagdap::bitvec::{self, bytes_to_bits, bits_to_bytes, Error as BitvecError};
10
11pub use jtagdap;
12
13#[derive(thiserror::Error, Debug)]
14pub enum Error {
15 #[error("Device status register in incorrect state.")]
16 BadStatus,
17 #[error("Cannot access flash memory unless the device is the only TAP in the JTAG chain.")]
18 NotOnlyTAP,
19 #[error(
20 "Bitstream file contains an IDCODE 0x{bitstream:08X} incompatible \
21 with the detected device IDCODE 0x{jtag:08X}."
22 )]
23 IncompatibleIdcode { bitstream: u32, jtag: u32 },
24 #[error("Could not remove VERIFY_IDCODE because parsing the bitstream failed")]
25 RemoveIdcodeNoMetadata,
26 #[error("SPI Flash error")]
27 SPIFlash(#[from] spi_flash::Error),
28 #[error("JTAG error")]
29 JTAG(#[from] JTAGError),
30 #[error("Bitvec error")]
31 Bitvec(#[from] BitvecError),
32 #[error("I/O error")]
33 IO(#[from] std::io::Error),
34 #[error(transparent)]
35 Other(#[from] anyhow::Error),
36}
37
38pub type Result<T> = std::result::Result<T, Error>;
39
40#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromPrimitive)]
46#[allow(non_camel_case_types)]
47#[repr(u32)]
48pub enum X7IDCODE {
49 X7S6 = 0x03622093,
50 X7S15 = 0x03620093,
51 X7S25 = 0x037C4093,
52 X7S50 = 0x0362F093,
53 X7S75 = 0x037C8093,
54 X7S100 = 0x037c7093,
55 X7A12T = 0x037c3093,
56 X7A15T = 0x0362E093,
57 X7A25T = 0x037C2093,
58 X7A35T = 0x0362D093,
59 X7A50T = 0x0362C093,
60 X7A75T = 0x03632093,
61 X7A100T = 0x03631093,
62 X7A200T = 0x03636093,
63 X7K70T = 0x03647093,
64 X7K160T = 0x0364C093,
65 X7K325T = 0x03651093,
66 X7K355T = 0x03747093,
67 X7K410T = 0x03656093,
68 X7K420T = 0x03752093,
69 X7K480T = 0x03751093,
70 X7V575T = 0x03671093,
71 X7VX330T = 0x03667093,
72 X7VX415T = 0x03682093,
73 X7VX485T = 0x03687093,
74 X7VX550T = 0x03692093,
75 X7VX690T = 0x03691093,
76 X7VX980T = 0x03696093,
77 X7VX1140T = 0x036D5093,
78 X7VH580T = 0x036D9093,
79 X7VH870T = 0x036DB093,
80 X7Z007S = 0x03723093,
81 X7Z012S = 0x0373c093,
82 X7Z014S = 0x03728093,
83 X7Z010 = 0x03722093,
84 X7Z015 = 0x0373b093,
85 X7Z020 = 0x03727093,
86 X7Z030 = 0x0372c093,
87 X7Z035 = 0x03732093,
88 X7Z045 = 0x03731093,
89 X7Z100 = 0x03736093,
90}
91
92impl From<X7IDCODE> for IDCODE {
93 fn from(id: X7IDCODE) -> IDCODE {
94 IDCODE(id as u32)
95 }
96}
97
98impl From<&X7IDCODE> for IDCODE {
99 fn from(id: &X7IDCODE) -> IDCODE {
100 IDCODE(*id as u32)
101 }
102}
103
104impl X7IDCODE {
105 pub fn try_from_idcode(idcode: IDCODE) -> Option<Self> {
106 Self::try_from(idcode.0 & 0x0FFF_FFFF).ok()
107 }
108
109 pub fn try_from_u32(idcode: u32) -> Option<Self> {
110 Self::try_from_idcode(IDCODE(idcode))
111 }
112
113 pub fn try_from_name(name: &str) -> Option<Self> {
114 match name.to_ascii_uppercase().as_str() {
115 "X7S6" => Some(X7IDCODE::X7S6),
116 "X7S15" => Some(X7IDCODE::X7S15),
117 "X7S25" => Some(X7IDCODE::X7S25),
118 "X7S50" => Some(X7IDCODE::X7S50),
119 "X7S75" => Some(X7IDCODE::X7S75),
120 "X7S100" => Some(X7IDCODE::X7S100),
121 "X7A12T" => Some(X7IDCODE::X7A12T),
122 "X7A15T" => Some(X7IDCODE::X7A15T),
123 "X7A25T" => Some(X7IDCODE::X7A25T),
124 "X7A35T" => Some(X7IDCODE::X7A35T),
125 "X7A50T" => Some(X7IDCODE::X7A50T),
126 "X7A75T" => Some(X7IDCODE::X7A75T),
127 "X7A100T" => Some(X7IDCODE::X7A100T),
128 "X7A200T" => Some(X7IDCODE::X7A200T),
129 "X7K70T" => Some(X7IDCODE::X7K70T),
130 "X7K160T" => Some(X7IDCODE::X7K160T),
131 "X7K325T" => Some(X7IDCODE::X7K325T),
132 "X7K355T" => Some(X7IDCODE::X7K355T),
133 "X7K410T" => Some(X7IDCODE::X7K410T),
134 "X7K420T" => Some(X7IDCODE::X7K420T),
135 "X7K480T" => Some(X7IDCODE::X7K480T),
136 "X7V575T" => Some(X7IDCODE::X7V575T),
137 "X7VX330T" => Some(X7IDCODE::X7VX330T),
138 "X7VX415T" => Some(X7IDCODE::X7VX415T),
139 "X7VX485T" => Some(X7IDCODE::X7VX485T),
140 "X7VX550T" => Some(X7IDCODE::X7VX550T),
141 "X7VX690T" => Some(X7IDCODE::X7VX690T),
142 "X7VX980T" => Some(X7IDCODE::X7VX980T),
143 "X7VX1140T" => Some(X7IDCODE::X7VX1140T),
144 "X7VH580T" => Some(X7IDCODE::X7VH580T),
145 "X7VH870T" => Some(X7IDCODE::X7VH870T),
146 "X7Z007S" => Some(X7IDCODE::X7Z007S),
147 "X7Z012S" => Some(X7IDCODE::X7Z012S),
148 "X7Z014S" => Some(X7IDCODE::X7Z014S),
149 "X7Z010" => Some(X7IDCODE::X7Z010),
150 "X7Z015" => Some(X7IDCODE::X7Z015),
151 "X7Z020" => Some(X7IDCODE::X7Z020),
152 "X7Z030" => Some(X7IDCODE::X7Z030),
153 "X7Z035" => Some(X7IDCODE::X7Z035),
154 "X7Z045" => Some(X7IDCODE::X7Z045),
155 "X7Z100" => Some(X7IDCODE::X7Z100),
156 _ => None,
157 }
158 }
159
160 pub fn name(&self) -> &'static str {
161 match self {
162 X7IDCODE::X7S6 => "X7S6",
163 X7IDCODE::X7S15 => "X7S15",
164 X7IDCODE::X7S25 => "X7S25",
165 X7IDCODE::X7S50 => "X7S50",
166 X7IDCODE::X7S75 => "X7S75",
167 X7IDCODE::X7S100 => "X7S100",
168 X7IDCODE::X7A12T => "X7A12T",
169 X7IDCODE::X7A15T => "X7A15T",
170 X7IDCODE::X7A25T => "X7A25T",
171 X7IDCODE::X7A35T => "X7A35T",
172 X7IDCODE::X7A50T => "X7A50T",
173 X7IDCODE::X7A75T => "X7A75T",
174 X7IDCODE::X7A100T => "X7A100T",
175 X7IDCODE::X7A200T => "X7A200T",
176 X7IDCODE::X7K70T => "X7K70T",
177 X7IDCODE::X7K160T => "X7K160T",
178 X7IDCODE::X7K325T => "X7K325T",
179 X7IDCODE::X7K355T => "X7K355T",
180 X7IDCODE::X7K410T => "X7K410T",
181 X7IDCODE::X7K420T => "X7K420T",
182 X7IDCODE::X7K480T => "X7K480T",
183 X7IDCODE::X7V575T => "X7V575T",
184 X7IDCODE::X7VX330T => "X7VX330T",
185 X7IDCODE::X7VX415T => "X7VX415T",
186 X7IDCODE::X7VX485T => "X7VX485T",
187 X7IDCODE::X7VX550T => "X7VX550T",
188 X7IDCODE::X7VX690T => "X7VX690T",
189 X7IDCODE::X7VX980T => "X7VX980T",
190 X7IDCODE::X7VX1140T => "X7VX1140T",
191 X7IDCODE::X7VH580T => "X7VH580T",
192 X7IDCODE::X7VH870T => "X7VH870T",
193 X7IDCODE::X7Z007S => "X7Z007S",
194 X7IDCODE::X7Z012S => "X7Z012S",
195 X7IDCODE::X7Z014S => "X7Z014S",
196 X7IDCODE::X7Z010 => "X7Z010",
197 X7IDCODE::X7Z015 => "X7Z015",
198 X7IDCODE::X7Z020 => "X7Z020",
199 X7IDCODE::X7Z030 => "X7Z030",
200 X7IDCODE::X7Z035 => "X7Z035",
201 X7IDCODE::X7Z045 => "X7Z045",
202 X7IDCODE::X7Z100 => "X7Z100",
203 }
204 }
205
206 pub fn compatible(&self, other: X7IDCODE) -> bool {
209 *self == other
210 }
211
212 pub fn config_bits_per_frame(&self) -> (usize, usize, usize) {
216 (0, 0, 0)
217 }
218}
219
220pub fn check_tap_idx(chain: &JTAGChain, index: usize) -> Option<X7IDCODE> {
221 match chain.idcodes().iter().nth(index) {
222 Some(Some(idcode)) => X7IDCODE::try_from_idcode(*idcode),
223 _ => None,
224 }
225}
226
227pub fn auto_tap_idx(chain: &JTAGChain) -> Option<(usize, X7IDCODE)> {
229 let x7_idxs: Vec<(usize, X7IDCODE)> = chain
230 .idcodes()
231 .iter()
232 .enumerate()
233 .filter_map(|(idx, id)| id.map(|id| (idx, id)))
234 .filter_map(|(idx, id)| X7IDCODE::try_from_idcode(id).map(|id| (idx, id)))
235 .collect();
236 let len = x7_idxs.len();
237 if len == 0 {
238 log::info!("No 7-series device found in JTAG chain");
239 None
240 } else if len > 1 {
241 log::info!("Multiple 7-series devices found in JTAG chain, specify one using --tap");
242 None
243 } else {
244 let (index, idcode) = x7_idxs.first().unwrap();
245 log::debug!("Automatically selecting device at TAP {}", index);
246 Some((*index, *idcode))
247 }
248}
249
250#[derive(Copy, Clone, Debug)]
252#[allow(unused, non_camel_case_types, clippy::upper_case_acronyms)]
253#[repr(u8)]
254enum Command {
255 EXTEST = 0b100110,
256 EXTEST_PULSE = 0b111100,
257 EXTEST_TRAIN = 0b1111101,
258 SAMPLE = 0b000001,
259 USER1 = 0b000010,
260 USER2 = 0b000011,
261 USER3 = 0b100010,
262 USER4 = 0b100011,
263 CFG_OUT = 0b000100,
264 CFG_IN = 0b000101,
265 USERCODE = 0b001000,
266 IDCODE = 0b001001,
267 HIGHZ_IO = 0b001010,
268 JPROGRAM = 0b001011,
269 JSTART = 0b001100,
270 JSHUTDOWN = 0b001101,
271 XADC_DRP = 0b110111,
272 ISC_ENABLE = 0b010000,
273 ISC_PROGRAM = 0b010001,
274 XSC_PROGRAM_KEY = 0b010010,
275 XSC_DNA = 0b010111,
276 FUSE_DNA = 0b110010,
277 ISC_NOOP = 0b010100,
278 ISC_DISABLE = 0b010110,
279 BYPASS = 0b111111,
280}
281
282impl Command {
283 pub fn bits(&self) -> Vec<bool> {
284 jtagdap::bitvec::bytes_to_bits(&[*self as u8], 6).unwrap()
285 }
286}
287
288#[derive(Copy, Clone)]
290pub struct Status(u32);
291
292impl Status {
293 pub fn new(word: u32) -> Self {
294 Self(word)
295 }
296
297 pub fn startup_state(&self) -> u8 { ((self.0 >> 18) & 0b111) as u8 }
298 pub fn xadc_overtemp(&self) -> bool { self.bit(17) }
299 pub fn dec_error(&self) -> bool { self.bit(16) }
300 pub fn id_error(&self) -> bool { self.bit(15) }
301 pub fn done(&self) -> bool { self.bit(14) }
302 pub fn release_done(&self) -> bool { self.bit(13) }
303 pub fn init_b(&self) -> bool { self.bit(12) }
304 pub fn init_complete(&self) -> bool { self.bit(11) }
305 pub fn mode(&self) -> u8 { ((self.0 >> 8) & 0b111) as u8 }
306 pub fn ghigh_b(&self) -> bool { self.bit(7) }
307 pub fn gwe(&self) -> bool { self.bit(6) }
308 pub fn gts_cfg_b(&self) -> bool { self.bit(5) }
309 pub fn eos(&self) -> bool { self.bit(4) }
310 pub fn dci_match(&self) -> bool { self.bit(3) }
311 pub fn mmcm_lock(&self) -> bool { self.bit(2) }
312 pub fn part_secured(&self) -> bool { self.bit(1) }
313 pub fn crc_error(&self) -> bool { self.bit(0) }
314
315 fn bit(&self, offset: usize) -> bool {
316 (self.0 >> offset) & 1 == 1
317 }
318}
319
320impl fmt::Debug for Status {
321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322 f.write_fmt(format_args!(
323 "Status: {:08X}
324 Startup state: 0b{:03b}
325 XADC overtemp: {}
326 Decrypt error: {}
327 ID error: {}
328 DONE: {}
329 Release DONE: {}
330 INIT_B: {}
331 INIT complete: {}
332 Mode: 0b{:03b}
333 GHIGH_B: {}
334 Global write enable: {}
335 Global tri-state: {}
336 End of startup: {}
337 DCI match: {}
338 MMCM lock: {}
339 Secured: {}
340 CRC error: {}",
341 self.0, self.startup_state(), self.xadc_overtemp(), self.dec_error(), self.id_error(),
342 self.done(), self.release_done(), self.init_b(), self.init_complete(), self.mode(),
343 self.ghigh_b(), self.gwe(), self.gts_cfg_b(), self.eos(), self.dci_match(),
344 self.mmcm_lock(), self.part_secured(), self.crc_error()))
345 }
346}
347
348pub struct X7 {
349 tap: JTAGTAP,
350 idcode: X7IDCODE,
351}
352
353impl X7 {
354 pub fn new(tap: JTAGTAP, idcode: X7IDCODE) -> Self {
355 X7 { tap, idcode }
356 }
357
358 pub fn idcode(&self) -> X7IDCODE {
359 self.idcode
360 }
361
362 pub fn dna(&mut self) -> Result<Vec<u8>> {
364 self.command(Command::FUSE_DNA)?;
365 let data = self.tap.read_dr(64)?;
366 let dna = bits_to_bytes(&data);
367 log::info!("Read DNA: {:02X?}", dna);
368 Ok(dna)
369 }
370
371 pub fn status(&mut self) -> Result<Status> {
373 self.tap.test_logic_reset()?;
374 self.tap.run_test_idle(5)?;
375 self.command(Command::CFG_IN)?;
376 let mut bits = Vec::new();
377 bitvec::append_u32(&mut bits, 0xaa99_5566u32.reverse_bits());
378 bitvec::append_u32(&mut bits, 0x2000_0000u32.reverse_bits());
379 bitvec::append_u32(&mut bits, 0x2800_e001u32.reverse_bits());
380 bitvec::append_u32(&mut bits, 0x2000_0000u32.reverse_bits());
381 bitvec::append_u32(&mut bits, 0x2000_0000u32.reverse_bits());
382 self.tap.write_dr(&bits)?;
383 self.command(Command::CFG_OUT)?;
384 let status = bits_to_bytes(&self.tap.read_dr(32)?);
385 let status = u32::from_le_bytes([status[0], status[1], status[2], status[3]]);
386 let status = Status::new(status.reverse_bits());
387 log::debug!("{:?}", status);
388 self.tap.test_logic_reset()?;
389 Ok(status)
390 }
391
392 pub fn program(&mut self, data: &[u8]) -> Result<()> {
396 self.program_cb(data, |_| {})
397 }
398
399 pub fn program_progress(&mut self, data: &[u8]) -> Result<()> {
403 const DATA_PROGRESS_TPL: &str =
404 " {msg} [{bar:40.cyan/black}] {bytes}/{total_bytes} ({bytes_per_sec}; {eta_precise})";
405 const DATA_FINISHED_TPL: &str =
406 " {msg} [{bar:40.green/black}] {bytes}/{total_bytes} ({bytes_per_sec}; {eta_precise})";
407 const DATA_PROGRESS_CHARS: &str = "ββΈβ";
408 let pb = ProgressBar::new(data.len() as u64).with_style(
409 ProgressStyle::with_template(DATA_PROGRESS_TPL)
410 .unwrap()
411 .progress_chars(DATA_PROGRESS_CHARS));
412 pb.set_message("Programming");
413 pb.set_position(0);
414
415 self.program_cb(data, |n| pb.set_position(n as u64))?;
416
417 pb.set_style(ProgressStyle::with_template(DATA_FINISHED_TPL)
418 .unwrap()
419 .progress_chars(DATA_PROGRESS_CHARS)
420 );
421
422 pb.finish();
423 Ok(())
424 }
425
426 pub fn program_cb<F: Fn(usize)>(&mut self, data: &[u8], cb: F) -> Result<()> {
430 self.check_ready_to_program()?;
432 self.tap.test_logic_reset()?;
433 self.command(Command::JPROGRAM)?;
434 self.tap.run_test_idle(1)?;
435 std::thread::sleep(Duration::from_millis(20));
436
437 self.tap.test_logic_reset()?;
439 self.command(Command::CFG_IN)?;
440
441 let data: Vec<u8> = data.iter().map(|x| x.reverse_bits()).collect();
445 let bits = bytes_to_bits(&data, data.len() * 8)?;
446
447 self.tap.write_dr_cb(&bits, |n| cb(n / 8))?;
449
450 self.tap.run_test_idle(1)?;
452
453 self.command(Command::JSTART)?;
455 self.tap.run_test_idle(2000)?;
456 self.tap.test_logic_reset()?;
457
458 self.check_programmed_ok()?;
460 self.tap.test_logic_reset()?;
461
462 Ok(())
463 }
464
465 pub fn jprogram(&mut self) -> Result<()> {
466 self.command(Command::JPROGRAM)?;
467 self.tap.run_test_idle(2000)?;
468 self.tap.test_logic_reset()?;
469 Ok(())
470 }
471
472 fn check_ready_to_program(&mut self) -> Result<()> {
473 log::debug!("Checking status before programming...");
474 let status = self.status()?;
475 if !status.init_complete() {
476 log::error!("FPGA init not complete");
477 return Err(Error::BadStatus);
478 }
479 if !status.init_b() {
480 log::error!("FPGA INIT_B still low");
481 return Err(Error::BadStatus);
482 }
483 Ok(())
484 }
485
486 fn check_programmed_ok(&mut self) -> Result<()> {
487 log::debug!("Checking status after programming...");
488 let status = self.status()?;
489 if !status.init_complete() {
490 log::error!("Init not complete");
491 return Err(Error::BadStatus);
492 }
493 if !status.init_b() {
494 log::error!("INIT_B still low");
495 return Err(Error::BadStatus);
496 }
497 if !status.done() {
498 log::error!("DONE still low");
499 return Err(Error::BadStatus);
500 }
501 if !status.release_done() {
502 log::error!("DONE not released");
503 return Err(Error::BadStatus);
504 }
505 if status.dec_error() {
506 log::error!("Decrypt error");
507 return Err(Error::BadStatus);
508 }
509 if status.id_error() {
510 log::error!("ID error");
511 return Err(Error::BadStatus);
512 }
513 if status.crc_error() {
514 log::error!("CRC error");
515 return Err(Error::BadStatus);
516 }
517 Ok(())
518 }
519
520 fn command(&mut self, command: Command) -> Result<()> {
522 log::trace!("Loading command {:?}", command);
523 Ok(self.tap.write_ir(&command.bits())?)
524 }
525}
526
527pub struct Bitstream {
528 data: Vec<u8>,
529}
530
531impl Bitstream {
532 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
534 let mut file = File::open(path)?;
535 Self::from_file(&mut file)
536 }
537
538 pub fn from_file(file: &mut File) -> Result<Self> {
540 let mut data = if let Ok(metadata) = file.metadata() {
541 Vec::with_capacity(metadata.len() as usize)
542 } else {
543 Vec::new()
544 };
545 file.read_to_end(&mut data)?;
546 Ok(Self::new(data))
547 }
548
549 pub fn from_data(data: &[u8]) -> Self {
551 Self::new(data.to_owned())
552 }
553
554 pub fn new(data: Vec<u8>) -> Self {
556 Self { data }
557 }
558
559 pub fn data(&self) -> &[u8] {
561 &self.data[..]
562 }
563}