1use std::ops::{Deref, DerefMut};
6use std::time::Duration;
7
8use anyhow::{Context, Result, bail, ensure};
9use thiserror::Error;
10
11use super::openocd::OpenOcd;
12use crate::test_utils::poll::poll_until;
13
14pub mod consts {
16 pub const DTMCS: u32 = 0x10;
18 pub const DMI: u32 = 0x11;
19
20 pub const DTMCS_VERSION_SHIFT: u32 = 0;
21 pub const DTMCS_ABITS_SHIFT: u32 = 4;
22 pub const DTMCS_IDLE_SHIFT: u32 = 12;
23 pub const DTMCS_DMIRESET_SHIFT: u32 = 16;
24
25 pub const DTMCS_VERSION_MASK: u32 = 0xf << DTMCS_VERSION_SHIFT;
26 pub const DTMCS_ABITS_MASK: u32 = 0x3f << DTMCS_ABITS_SHIFT;
27 pub const DTMCS_IDLE_MASK: u32 = 0x7 << DTMCS_IDLE_SHIFT;
28 pub const DTMCS_DMIRESET_MASK: u32 = 1 << DTMCS_DMIRESET_SHIFT;
29
30 pub const DTMCS_VERSION_0_13: u32 = 1;
31
32 pub const DMI_ADDRESS_SHIFT: u32 = 34;
33 pub const DMI_DATA_SHIFT: u32 = 2;
34
35 pub const DMI_OP_READ: u64 = 0x1;
36 pub const DMI_OP_WRITE: u64 = 0x2;
37
38 pub const DATA0: u32 = 0x04;
40 pub const DATA1: u32 = 0x05;
41 pub const DMCONTROL: u32 = 0x10;
42 pub const DMSTATUS: u32 = 0x11;
43 pub const HARTINFO: u32 = 0x12;
44 pub const ABSTRACTCS: u32 = 0x16;
45
46 pub const DMSTATUS_ANYHALTED_MASK: u32 = 1 << 8;
47 pub const DMSTATUS_ANYRUNNING_MASK: u32 = 1 << 10;
48 pub const DMSTATUS_ANYUNAVAIL_MASK: u32 = 1 << 12;
49 pub const DMSTATUS_ANYNONEXISTENT_MASK: u32 = 1 << 14;
50 pub const DMSTATUS_ANYRESUMEACK_MASK: u32 = 1 << 16;
51 pub const DMSTATUS_ANYHAVERESET_MASK: u32 = 1 << 18;
52 pub const DMSTATUS_ALLHAVERESET_MASK: u32 = 1 << 19;
53
54 pub const DMCONTROL_HASEL_SHIFT: u32 = 26;
55 pub const DMCONTROL_HARTSELHI_SHIFT: u32 = 6;
56 pub const DMCONTROL_HARTSELLO_SHIFT: u32 = 16;
57
58 pub const DMCONTROL_DMACTIVE_MASK: u32 = 1 << 0;
59 pub const DMCONTROL_NDMRESET_MASK: u32 = 1 << 1;
60 pub const DMCONTROL_ACKHAVERESET_MASK: u32 = 1 << 28;
61 pub const DMCONTROL_RESUMEREQ_MASK: u32 = 1 << 30;
62 pub const DMCONTROL_HALTREQ_MASK: u32 = 1 << 31;
63
64 pub const ABSTRACTCS_CMDERR_MASK: u32 = (1 << 11) - (1 << 8);
65 pub const ABSTRACTCS_BUSY_MASK: u32 = 1 << 12;
66
67 pub const ABSTRACTCS_CMDERR_SHIFT: u32 = 8;
68
69 pub const ABSTRACTCS_CMDERR_NONE: u32 = 0;
70}
71
72use consts::*;
73
74pub trait Dmi {
76 fn dmi_read(&mut self, addr: u32) -> Result<u32>;
78
79 fn dmi_write(&mut self, addr: u32, data: u32) -> Result<()>;
81
82 fn batched_dmi_writes(&mut self, writes: &[(u32, u32)]) -> Result<()> {
85 writes
86 .iter()
87 .try_for_each(|&(addr, data)| self.dmi_write(addr, data))
88 }
89
90 fn batched_dmi_reads(&mut self, addrs: &[u32]) -> Result<Vec<u32>> {
94 addrs.iter().map(|&addr| self.dmi_read(addr)).collect()
95 }
96}
97
98impl<T: Dmi> Dmi for &mut T {
99 fn dmi_read(&mut self, addr: u32) -> Result<u32> {
100 T::dmi_read(self, addr)
101 }
102
103 fn dmi_write(&mut self, addr: u32, data: u32) -> Result<()> {
104 T::dmi_write(self, addr, data)
105 }
106
107 fn batched_dmi_writes(&mut self, writes: &[(u32, u32)]) -> Result<()> {
108 T::batched_dmi_writes(self, writes)
109 }
110
111 fn batched_dmi_reads(&mut self, addrs: &[u32]) -> Result<Vec<u32>> {
112 T::batched_dmi_reads(self, addrs)
113 }
114}
115
116pub struct OpenOcdDmi {
118 openocd: OpenOcd,
119 tap: String,
120 abits: u32,
121 extra_idle: u32,
127}
128
129const DEFAULT_EXTRA_IDLE: u32 = 4;
131
132impl OpenOcdDmi {
133 pub fn new(mut openocd: OpenOcd, tap: &str) -> Result<Self> {
139 let target_names = openocd.execute("target names")?;
140 ensure!(
141 target_names.is_empty(),
142 "Target must not be setup when accessing DMI directly"
143 );
144
145 openocd.irscan(tap, DTMCS)?;
146 let res = openocd.drscan(tap, 32, DTMCS_DMIRESET_MASK)?;
147 let version = (res & DTMCS_VERSION_MASK) >> DTMCS_VERSION_SHIFT;
148 let abits = (res & DTMCS_ABITS_MASK) >> DTMCS_ABITS_SHIFT;
149 let idle = (res & DTMCS_IDLE_MASK) >> DTMCS_IDLE_SHIFT;
150
151 ensure!(
152 version == DTMCS_VERSION_0_13,
153 "DTMCS indicates version other than 0.13"
154 );
155
156 let extra_idle = match std::env::var("OT_DMI_EXTRA_IDLE") {
157 Ok(val) => val.parse().context("invalid OT_DMI_EXTRA_IDLE")?,
158 Err(_) => DEFAULT_EXTRA_IDLE,
159 };
160 log::info!(
161 "DTMCS.idle = {idle} (using {extra_idle} as extra_idle, i.e. {} extra runtest \
162 cycle(s) per scan; set OT_DMI_EXTRA_IDLE to override)",
163 extra_idle.saturating_sub(1)
164 );
165
166 openocd.irscan(tap, DMI)?;
167 Ok(Self {
168 openocd,
169 tap: tap.to_owned(),
170 abits,
171 extra_idle,
172 })
173 }
174
175 fn drscan_bits(&self) -> u32 {
176 self.abits + DMI_ADDRESS_SHIFT
177 }
178
179 fn wait_idle_cmd(&self) -> String {
184 if self.extra_idle > 1 {
185 format!("runtest {}", self.extra_idle - 1)
186 } else {
187 String::new()
188 }
189 }
190
191 fn wait_idle(&mut self) -> Result<()> {
194 let cmd = self.wait_idle_cmd();
195 if !cmd.is_empty() {
196 self.openocd.execute(&cmd)?;
197 }
198 Ok(())
199 }
200
201 fn dmi_op(&mut self, op: u64) -> Result<u64> {
202 let res = self.openocd.drscan(&self.tap, self.drscan_bits(), op)?;
203
204 ensure!(res == 0, "Unexpected DMI initial response {res:#x}");
206
207 self.wait_idle()?;
209
210 let res = self.openocd.drscan(&self.tap, self.drscan_bits(), 0)?;
212 ensure!(res & 3 == 0, "DMI operation failed with {res:#x}");
213
214 ensure!(
216 res >> DMI_ADDRESS_SHIFT == op >> DMI_ADDRESS_SHIFT,
217 "DMI operation address mismatch {res:#x}"
218 );
219
220 Ok(res)
221 }
222}
223
224impl Dmi for OpenOcdDmi {
225 fn dmi_read(&mut self, addr: u32) -> Result<u32> {
226 let output = (self.dmi_op((addr as u64) << DMI_ADDRESS_SHIFT | DMI_OP_READ)?
227 >> DMI_DATA_SHIFT) as u32;
228 log::debug!("DMI read {:#x} -> {:#x}", addr, output);
229 Ok(output)
230 }
231
232 fn dmi_write(&mut self, addr: u32, value: u32) -> Result<()> {
233 self.dmi_op(
234 (addr as u64) << DMI_ADDRESS_SHIFT | (value as u64) << DMI_DATA_SHIFT | DMI_OP_WRITE,
235 )?;
236 log::debug!("DMI write {:#x} <- {:#x}", addr, value);
237 Ok(())
238 }
239
240 fn batched_dmi_writes(&mut self, writes: &[(u32, u32)]) -> Result<()> {
241 if writes.is_empty() {
242 return Ok(());
243 }
244
245 log::debug!(
246 "DMI {} batched writes: {}",
247 writes.len(),
248 writes
249 .iter()
250 .map(|&(addr, value)| format!("{:#x} <- {:#x}", addr, value))
251 .collect::<Vec<_>>()
252 .join(", ")
253 );
254
255 const CHUNK_SIZE: usize = 16384;
257
258 for chunk in writes.chunks(CHUNK_SIZE) {
259 let mut cmd = chunk
265 .iter()
266 .map(|&(addr, value)| {
267 let data = (addr as u64) << DMI_ADDRESS_SHIFT
268 | (value as u64) << DMI_DATA_SHIFT
269 | DMI_OP_WRITE;
270 let scan = self.openocd.drscan_cmd(&self.tap, self.drscan_bits(), data);
271 let idle = self.wait_idle_cmd();
272 if idle.is_empty() {
273 scan
274 } else {
275 format!("{scan}\n{idle}")
276 }
277 })
278 .collect::<Vec<_>>()
279 .join("\n");
280 cmd.push_str(
281 format!(
282 "\n{}",
283 self.openocd.drscan_cmd(&self.tap, self.drscan_bits(), 0)
284 )
285 .as_str(),
286 );
287 let result = self.openocd.execute(&cmd)?;
288
289 let res = u64::from_str_radix(result.trim(), 16)
293 .with_context(|| format!("unexpected DMI batched write response '{result}'"))?;
294 ensure!(
295 res & 3 == 0,
296 "DMI batched write failed with sticky status {res:#x}; at least one write was \
297 dropped (is the JTAG clock too fast for the DMI to keep up?)"
298 );
299 }
300 Ok(())
301 }
302
303 fn batched_dmi_reads(&mut self, addrs: &[u32]) -> Result<Vec<u32>> {
304 const CHUNK_SIZE: usize = 16384;
309
310 let mut values = Vec::with_capacity(addrs.len());
311 for chunk in addrs.chunks(CHUNK_SIZE) {
312 let mut cmd = String::from("set _r {}");
313 for &addr in chunk {
314 let op = (addr as u64) << DMI_ADDRESS_SHIFT | DMI_OP_READ;
315 cmd.push_str(&format!(
316 "\nlappend _r [{}]",
317 self.openocd.drscan_cmd(&self.tap, self.drscan_bits(), op)
318 ));
319 let idle = self.wait_idle_cmd();
320 if !idle.is_empty() {
321 cmd.push_str(&format!("\n{idle}"));
322 }
323 }
324 cmd.push_str(&format!(
326 "\nlappend _r [{}]\nset _r",
327 self.openocd.drscan_cmd(&self.tap, self.drscan_bits(), 0)
328 ));
329
330 let result = self.openocd.execute(&cmd)?;
331 let entries = result
332 .split_whitespace()
333 .map(|s| {
334 u64::from_str_radix(s, 16)
335 .with_context(|| format!("unexpected DMI batched read response '{s}'"))
336 })
337 .collect::<Result<Vec<u64>>>()?;
338 ensure!(
339 entries.len() == chunk.len() + 1,
340 "DMI batched read returned {} entries, expected {}",
341 entries.len(),
342 chunk.len() + 1
343 );
344
345 for (idx, &res) in entries.iter().enumerate() {
349 ensure!(
350 res & 3 == 0,
351 "DMI batched read failed with sticky status {res:#x} at entry {idx} \
352 (is the JTAG clock too fast for the DMI to keep up?)"
353 );
354 if idx > 0 {
355 let addr = chunk[idx - 1];
356 ensure!(
357 res >> DMI_ADDRESS_SHIFT == addr as u64,
358 "DMI batched read address mismatch: expected {addr:#x}, response {res:#x}"
359 );
360 values.push((res >> DMI_DATA_SHIFT) as u32);
361 }
362 }
363 }
364
365 Ok(values)
366 }
367}
368
369#[derive(Debug, Error)]
370pub enum DmiError {
371 #[error("Hart does not exist")]
372 Nonexistent,
373 #[error("Hart is not currently available")]
374 Unavailable,
375 #[error("Timeout waiting for hart to halt")]
376 WaitTimeout,
377}
378
379pub struct DmiDebugger<D> {
381 dmi: D,
382 hartsel_mask: Option<u32>,
383}
384
385impl<D> Deref for DmiDebugger<D> {
386 type Target = D;
387
388 fn deref(&self) -> &Self::Target {
389 &self.dmi
390 }
391}
392
393impl<D> DerefMut for DmiDebugger<D> {
394 fn deref_mut(&mut self) -> &mut Self::Target {
395 &mut self.dmi
396 }
397}
398
399impl<D: Dmi> DmiDebugger<D> {
400 pub fn new(dmi: D) -> Self {
401 Self {
402 dmi,
403 hartsel_mask: None,
404 }
405 }
406
407 pub fn hartsel_mask(&mut self) -> Result<u32> {
409 if self.hartsel_mask.is_none() {
410 let dm_control = 0 << DMCONTROL_HASEL_SHIFT
412 | 0x3ff << DMCONTROL_HARTSELLO_SHIFT
413 | 0x3ff << DMCONTROL_HARTSELHI_SHIFT
414 | DMCONTROL_DMACTIVE_MASK;
415 self.dmi.dmi_write(DMCONTROL, dm_control)?;
416
417 let dm_control = self.dmi.dmi_read(DMCONTROL)?;
420 let hart_select = (dm_control >> DMCONTROL_HARTSELLO_SHIFT) & 0x3ff
421 | ((dm_control >> DMCONTROL_HARTSELHI_SHIFT) & 0x3ff) << 10;
422
423 self.hartsel_mask = Some(hart_select);
424 }
425
426 Ok(self.hartsel_mask.unwrap())
427 }
428
429 pub fn select_hart(&mut self, hartid: u32) -> Result<DmiHart<'_, D>> {
431 if hartid >= (1 << 20) {
433 bail!("Invalid hartid: {hartid}");
434 }
435
436 if hartid != 0 {
438 let mask = self.hartsel_mask()?;
439 if (hartid & mask) != hartid {
440 bail!(DmiError::Nonexistent);
441 }
442 }
443
444 let hart_select = 0 << DMCONTROL_HASEL_SHIFT
445 | (hartid & 0x3ff) << DMCONTROL_HARTSELLO_SHIFT
446 | (hartid >> 10) << DMCONTROL_HARTSELHI_SHIFT
447 | DMCONTROL_DMACTIVE_MASK;
448 self.dmi.dmi_write(DMCONTROL, hart_select)?;
449
450 let mut hart = DmiHart {
451 debugger: self,
452 hart_select,
453 };
454
455 let dmstatus = hart.dmstatus()?;
456 if dmstatus & DMSTATUS_ANYNONEXISTENT_MASK != 0 {
457 bail!(DmiError::Nonexistent);
458 }
459 if dmstatus & DMSTATUS_ANYUNAVAIL_MASK != 0 {
460 bail!(DmiError::Unavailable);
461 }
462
463 Ok(hart)
464 }
465
466 pub fn data(&mut self, idx: u32) -> Result<u32> {
468 ensure!(idx < 12, "data register index out of range {:#x}", idx);
469 self.dmi_read(DATA0 + idx)
470 }
471
472 pub fn set_data(&mut self, idx: u32, data: u32) -> Result<()> {
474 ensure!(idx < 12, "data register index out of range {:#x}", idx);
475 self.dmi_write(DATA0 + idx, data)
476 }
477}
478
479pub struct DmiHart<'a, D> {
481 debugger: &'a mut DmiDebugger<D>,
482
483 hart_select: u32,
485}
486
487impl<D> Deref for DmiHart<'_, D> {
488 type Target = DmiDebugger<D>;
489
490 fn deref(&self) -> &Self::Target {
491 self.debugger
492 }
493}
494
495impl<D> DerefMut for DmiHart<'_, D> {
496 fn deref_mut(&mut self) -> &mut Self::Target {
497 self.debugger
498 }
499}
500
501pub struct HartState {
506 pub running: bool,
507 pub halted: bool,
508}
509
510impl<D: Dmi> DmiHart<'_, D> {
511 pub fn dmstatus(&mut self) -> Result<u32> {
513 let dmstatus = self.debugger.dmi_read(DMSTATUS)?;
514
515 if (dmstatus ^ (dmstatus >> 1))
518 & (DMSTATUS_ANYHALTED_MASK
519 | DMSTATUS_ANYRUNNING_MASK
520 | DMSTATUS_ANYUNAVAIL_MASK
521 | DMSTATUS_ANYNONEXISTENT_MASK
522 | DMSTATUS_ANYRESUMEACK_MASK
523 | DMSTATUS_ANYHAVERESET_MASK)
524 != 0
525 {
526 bail!(
527 "Invalid dmstatus {:#x}: any and all bits mismatch",
528 dmstatus
529 );
530 }
531
532 Ok(dmstatus)
533 }
534
535 pub fn set_dmcontrol(&mut self, value: u32) -> Result<()> {
537 self.debugger.dmi_write(DMCONTROL, value | self.hart_select)
538 }
539
540 pub fn hartinfo(&mut self) -> Result<u32> {
542 self.debugger.dmi_read(HARTINFO)
543 }
544
545 pub fn state(&mut self) -> Result<HartState> {
547 let dmstatus = self.dmstatus()?;
548 let running = dmstatus & DMSTATUS_ANYRUNNING_MASK != 0;
549 let halted = dmstatus & DMSTATUS_ANYHALTED_MASK != 0;
550 assert!(!(running && halted));
551 Ok(HartState { running, halted })
552 }
553
554 pub fn set_halt_request(&mut self, active: bool) -> Result<()> {
556 self.set_dmcontrol(if active { DMCONTROL_HALTREQ_MASK } else { 0 })
557 }
558
559 pub fn wait_halt(&mut self) -> Result<()> {
561 poll_until(Duration::from_secs(1), Duration::from_millis(50), || {
564 Ok(self.state()?.halted)
565 })
566 }
567
568 pub fn set_resume_request(&mut self, active: bool) -> Result<()> {
570 self.set_dmcontrol(if active { DMCONTROL_RESUMEREQ_MASK } else { 0 })
571 }
572
573 pub fn wait_resume(&mut self) -> Result<()> {
575 poll_until(Duration::from_secs(1), Duration::from_secs(1), || {
578 Ok(self.state()?.running)
579 })
580 }
581}