1use std::io::Cursor;
6use std::mem::size_of;
7use std::rc::Rc;
8use std::sync::LazyLock;
9use std::time::Duration;
10
11use anyhow::{Result, bail, ensure};
12use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
13use regex::Regex;
14use zerocopy::FromBytes;
15
16use crate::io::gpio::{
17 BitbangEntry, ClockNature, DacBangEntry, Edge, GpioBitbangOperation, GpioBitbanging,
18 GpioDacBangOperation, GpioError, GpioMonitoring, GpioPin, MonitoringEvent,
19 MonitoringReadResponse, MonitoringStartResponse, PinMode, PullMode,
20};
21use crate::transport::TransportError;
22use crate::transport::hyperdebug::{BulkInterface, Inner};
23
24pub struct HyperdebugGpioPin {
25 inner: Rc<Inner>,
26 pinname: String,
27}
28
29impl HyperdebugGpioPin {
30 pub fn open(inner: &Rc<Inner>, pinname: &str) -> Result<Self> {
31 let result = Self {
32 inner: Rc::clone(inner),
33 pinname: pinname.to_string(),
34 };
35 Ok(result)
36 }
37}
38
39impl GpioPin for HyperdebugGpioPin {
40 fn read(&self) -> Result<bool> {
42 let line = self
43 .inner
44 .cmd_one_line_output(&format!("gpioget {}", &self.pinname))?;
45 Ok(line.trim_start().starts_with('1'))
46 }
47
48 fn write(&self, value: bool) -> Result<()> {
50 self.inner
51 .cmd_no_output(&format!("gpioset {} {}", &self.pinname, u32::from(value)))
52 }
53
54 fn set_mode(&self, mode: PinMode) -> Result<()> {
55 self.inner.cmd_no_output(&format!(
56 "gpiomode {} {}",
57 &self.pinname,
58 match mode {
59 PinMode::Input => "input",
60 PinMode::OpenDrain => "opendrain",
61 PinMode::PushPull => "pushpull",
62 PinMode::AnalogInput => "adc",
63 PinMode::AnalogOutput => "dac",
64 PinMode::Alternate => "alternate",
65 }
66 ))
67 }
68
69 fn set_pull_mode(&self, mode: PullMode) -> Result<()> {
70 self.inner.cmd_no_output(&format!(
71 "gpiopullmode {} {}",
72 &self.pinname,
73 match mode {
74 PullMode::None => "none",
75 PullMode::PullUp => "up",
76 PullMode::PullDown => "down",
77 }
78 ))
79 }
80
81 fn analog_read(&self) -> Result<f32> {
82 let line = self
83 .inner
84 .cmd_one_line_output(&format!("adc {}", &self.pinname))
85 .map_err(|_| TransportError::CommunicationError("No output from adc".to_string()))?;
86 static ADC_REGEX: LazyLock<Regex> =
87 LazyLock::new(|| Regex::new("^ +([^ ])+ = ([0-9]+) mV").unwrap());
88 if let Some(captures) = ADC_REGEX.captures(&line) {
89 let milli_volts: u32 = captures.get(2).unwrap().as_str().parse()?;
90 Ok(milli_volts as f32 / 1000.0)
91 } else {
92 Err(TransportError::CommunicationError("Unrecognized adc output".to_string()).into())
93 }
94 }
95
96 fn analog_write(&self, volts: f32) -> Result<()> {
97 if !(0.0..=3.3).contains(&volts) {
98 return Err(GpioError::UnsupportedPinVoltage(volts).into());
99 }
100 let milli_volts = (volts * 1000.0) as u32;
101 self.inner.cmd_no_output(&format!(
102 "gpio analog-set {} {}",
103 &self.pinname, milli_volts,
104 ))
105 }
106
107 fn set(
108 &self,
109 mode: Option<PinMode>,
110 value: Option<bool>,
111 pull: Option<PullMode>,
112 volts: Option<f32>,
113 ) -> Result<()> {
114 if let Some(v) = volts
115 && !(0.0..=3.3).contains(&v)
116 {
117 return Err(GpioError::UnsupportedPinVoltage(v).into());
118 }
119 self.inner.cmd_no_output(&format!(
120 "gpio multiset {} {} {} {} {}",
121 &self.pinname,
122 match value {
123 Some(false) => "0",
124 Some(true) => "1",
125 None => "-",
126 },
127 match mode {
128 Some(PinMode::Input) => "input",
129 Some(PinMode::OpenDrain) => "opendrain",
130 Some(PinMode::PushPull) => "pushpull",
131 Some(PinMode::AnalogInput) => "adc",
132 Some(PinMode::AnalogOutput) => "dac",
133 Some(PinMode::Alternate) => "alternate",
134 None => "-",
135 },
136 match pull {
137 Some(PullMode::None) => "none",
138 Some(PullMode::PullUp) => "up",
139 Some(PullMode::PullDown) => "down",
140 None => "-",
141 },
142 if let Some(v) = volts {
143 format!("{}", (v * 1000.0) as u32)
144 } else {
145 "-".to_string()
146 },
147 ))
148 }
149
150 fn get_internal_pin_name(&self) -> Option<&str> {
151 Some(&self.pinname)
152 }
153}
154
155const USB_MAX_SIZE: usize = 64;
156
157#[derive(FromBytes, Debug)]
173#[repr(C)]
174struct RspGpioMonitoringHeader {
175 struct_size: u16,
179 status: u16,
181 start_levels: u16,
184 transcript_size: u16,
186 start_timestamp: u64,
189 end_timestamp: u64,
191}
192
193pub struct HyperdebugGpioMonitoring {
194 inner: Rc<Inner>,
195 cmsis_interface: Option<BulkInterface>,
196}
197
198impl HyperdebugGpioMonitoring {
199 const CMSIS_DAP_CUSTOM_COMMAND_GPIO: u8 = 0x83;
201
202 const GPIO_MONITORING_READ: u8 = 0x00;
204
205 const MON_SUCCESS: u16 = 0;
207 const MON_BUFFER_OVERRUN: u16 = 5;
208
209 pub fn open(inner: &Rc<Inner>, cmsis_interface: Option<BulkInterface>) -> Result<Self> {
210 Ok(Self {
211 inner: Rc::clone(inner),
212 cmsis_interface,
213 })
214 }
215}
216
217impl GpioMonitoring for HyperdebugGpioMonitoring {
218 fn get_clock_nature(&self) -> Result<ClockNature> {
219 Ok(ClockNature::Wallclock {
220 resolution: 1_000_000,
221 offset: None,
222 })
223 }
224
225 fn monitoring_start(&self, pins: &[&dyn GpioPin]) -> Result<MonitoringStartResponse> {
228 let mut pin_names = Vec::new();
229 for pin in pins {
230 pin_names.push(
231 pin.get_internal_pin_name()
232 .ok_or(TransportError::InvalidOperation)?,
233 );
234 }
235 static START_TIME_REGEX: LazyLock<Regex> =
236 LazyLock::new(|| Regex::new("^ +@([0-9]+)").unwrap());
237 static SIGNAL_REGEX: LazyLock<Regex> =
238 LazyLock::new(|| Regex::new("^ +([0-9]+) ([^ ])+ ([01])").unwrap());
239 let mut start_time: u64 = 0;
240 let mut signals = Vec::new();
241 let mut unexpected_output = false;
242 self.inner.execute_command(
243 &format!("gpio monitoring start {}", pin_names.join(" ")),
244 |line| {
245 if let Some(captures) = START_TIME_REGEX.captures(line) {
246 start_time = captures.get(1).unwrap().as_str().parse().unwrap();
247 } else if let Some(captures) = SIGNAL_REGEX.captures(line) {
248 signals.push(captures.get(3).unwrap().as_str() != "0");
249 } else {
250 unexpected_output = true;
251 log::error!("Unexpected HyperDebug output: {}\n", line);
252 };
253 },
254 )?;
255 if unexpected_output {
256 bail!(TransportError::CommunicationError(
257 "Unrecognized response".to_string()
258 ))
259 }
260 Ok(MonitoringStartResponse {
261 timestamp: start_time,
262 initial_levels: signals,
263 })
264 }
265
266 fn monitoring_read(
270 &self,
271 pins: &[&dyn GpioPin],
272 continue_monitoring: bool,
273 ) -> Result<MonitoringReadResponse> {
274 let mut pin_names = Vec::new();
275 for pin in pins {
276 pin_names.push(
277 pin.get_internal_pin_name()
278 .ok_or(TransportError::InvalidOperation)?,
279 );
280 }
281
282 if let Some(cmsis_interface) = self.cmsis_interface {
283 let mut pkt = Vec::<u8>::new();
287 pkt.write_u8(Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO)?;
288 pkt.write_u8(Self::GPIO_MONITORING_READ)?;
289 pkt.write_u8(pin_names.len().try_into()?)?;
290 for pin_name in &pin_names {
291 pkt.write_u8(pin_name.len().try_into()?)?;
292 pkt.extend_from_slice(pin_name.as_bytes());
293 }
294 self.inner
295 .usb_device
296 .borrow()
297 .write_bulk(cmsis_interface.out_endpoint, &pkt)?;
298
299 let mut databytes: Vec<u8> =
300 vec![0u8; 1 + size_of::<RspGpioMonitoringHeader>() + USB_MAX_SIZE];
301 let mut bytecount = 0;
302
303 while bytecount < 1 + size_of::<RspGpioMonitoringHeader>() {
304 let read_count = self.inner.usb_device.borrow().read_bulk(
305 cmsis_interface.in_endpoint,
306 &mut databytes[bytecount..][..USB_MAX_SIZE],
307 )?;
308 ensure!(
309 read_count > 0,
310 TransportError::CommunicationError("Truncated GPIO response".to_string())
311 );
312 bytecount += read_count;
313 }
314 ensure!(
315 databytes[0] == Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO,
316 TransportError::CommunicationError(
317 "Unrecognized CMSIS-DAP response to GPIO request".to_string()
318 )
319 );
320 let resp: RspGpioMonitoringHeader =
321 FromBytes::read_from_prefix(&databytes[1..]).unwrap().0;
322 ensure!(
323 resp.struct_size as usize >= size_of::<RspGpioMonitoringHeader>(),
324 TransportError::CommunicationError(
325 "Short CMSIS-DAP response to GPIO request".to_string()
326 )
327 );
328 let header_bytes = resp.struct_size as usize + 1;
329 databytes.resize(header_bytes + resp.transcript_size as usize, 0u8);
330
331 while bytecount < databytes.len() {
332 let c = self
333 .inner
334 .usb_device
335 .borrow()
336 .read_bulk(cmsis_interface.in_endpoint, &mut databytes[bytecount..])?;
337 bytecount += c;
338 }
339
340 match resp.status {
341 Self::MON_SUCCESS => (),
342 Self::MON_BUFFER_OVERRUN => bail!(TransportError::CommunicationError(
343 "HyperDebug GPIO monitoring buffer overrun".to_string()
344 )),
345 n => bail!(TransportError::CommunicationError(format!(
346 "Unexpected HyperDebug GPIO error: {}",
347 n
348 ))),
349 }
350
351 let signal_bits = 32 - (pin_names.len() as u32 - 1).leading_zeros();
355 let signal_mask = (1u64 << signal_bits) - 1;
356
357 let mut cur_time: u64 = resp.start_timestamp;
358 let mut cur_levels = resp.start_levels;
359 let mut events = Vec::new();
360 let mut idx = header_bytes;
361
362 while idx < databytes.len() {
365 let value = decode_leb128(&mut idx, &databytes)?;
366
367 cur_time += value >> signal_bits;
371 let signal_index = (value & signal_mask) as u8;
372 cur_levels ^= 1 << signal_index;
373 events.push(MonitoringEvent {
374 signal_index,
375 edge: if cur_levels & (1 << signal_index) == 0 {
376 Edge::Falling
377 } else {
378 Edge::Rising
379 },
380 timestamp: cur_time,
381 });
382 }
383
384 if !continue_monitoring {
385 self.inner
386 .cmd_no_output(&format!("gpio monitoring stop {}", pin_names.join(" ")))?;
387 }
388 return Ok(MonitoringReadResponse {
389 events,
390 timestamp: resp.end_timestamp,
391 });
392 }
393
394 static START_TIME_REGEX: LazyLock<Regex> =
395 LazyLock::new(|| Regex::new("^ +@([0-9]+)").unwrap());
396 static EDGE_REGEX: LazyLock<Regex> =
397 LazyLock::new(|| Regex::new("^ +([0-9]+) (-?[0-9]+) ([RF])").unwrap());
398 let mut reference_time: u64 = 0;
399 let mut events = Vec::new();
400 loop {
401 let mut more_data = false;
402 let mut buffer_overrun = false;
403 let mut unexpected_output = false;
404 self.inner.execute_command(
405 &format!("gpio monitoring read {}", pin_names.join(" ")),
406 |line| {
407 if let Some(captures) = START_TIME_REGEX.captures(line) {
408 reference_time = captures.get(1).unwrap().as_str().parse().unwrap();
409 } else if let Some(captures) = EDGE_REGEX.captures(line) {
410 events.push(MonitoringEvent {
411 signal_index: captures.get(1).unwrap().as_str().parse().unwrap(),
412 edge: if captures.get(3).unwrap().as_str() == "R" {
413 Edge::Rising
414 } else {
415 Edge::Falling
416 },
417 timestamp: (reference_time as i64
418 + captures.get(2).unwrap().as_str().parse::<i64>().unwrap())
419 as u64,
420 });
421 } else if line == "Warning: more data" {
422 more_data = true;
423 } else if line == "Error: Buffer overrun" {
424 buffer_overrun = true;
425 } else {
426 unexpected_output = true;
427 log::error!("Unexpected HyperDebug output: {}\n", line);
428 }
429 },
430 )?;
431 if unexpected_output {
432 bail!(TransportError::CommunicationError(
433 "Unrecognized response".to_string()
434 ))
435 }
436 if buffer_overrun {
437 bail!(TransportError::CommunicationError(
438 "HyperDebug GPIO monitoring buffer overrun".to_string()
439 ))
440 }
441 if !more_data {
442 break;
443 }
444 }
445 if !continue_monitoring {
446 self.inner
447 .cmd_no_output(&format!("gpio monitoring stop {}", pin_names.join(" ")))?;
448 }
449 Ok(MonitoringReadResponse {
450 events,
451 timestamp: reference_time,
452 })
453 }
454}
455
456fn decode_leb128(idx: &mut usize, databytes: &[u8]) -> Result<u64> {
459 let mut i = *idx;
460 let mut value = 0u64;
461 let mut shift = 0;
462 while i < databytes.len() {
463 let byte = databytes[i];
464 value |= ((byte & 0x7F) as u64) << shift;
465 shift += 7;
466 i += 1;
467 if (byte & 0x80) == 0 {
468 *idx = i;
469 return Ok(value);
470 }
471 if shift + 7 > 64 {
472 bail!(TransportError::CommunicationError(
474 "Corrupt data from HyperDebug GPIO monitoring".to_string(),
475 ));
476 }
477 }
478 bail!(TransportError::CommunicationError(
480 "Corrupt data from HyperDebug GPIO monitoring".to_string(),
481 ));
482}
483
484pub struct HyperdebugGpioBitbanging {
485 inner: Rc<Inner>,
486 cmsis_interface: BulkInterface,
487}
488
489impl HyperdebugGpioBitbanging {
490 pub fn open(inner: &Rc<Inner>, cmsis_interface: BulkInterface) -> Result<Self> {
491 inner
493 .usb_device
494 .borrow_mut()
495 .claim_interface(cmsis_interface.interface)?;
496 Ok(Self {
497 inner: Rc::clone(inner),
498 cmsis_interface,
499 })
500 }
501}
502
503struct DacEncoder {
504 factor: f32,
506}
507
508impl DacEncoder {
509 fn encode_dac_sample(&self, out: &mut Vec<u8>, voltage: f32) {
510 let count = voltage * self.factor;
511 let count: u16 = if count <= 0.0 {
512 0
513 } else if count >= 4095.0 {
514 4095
515 } else {
516 count as u16
517 };
518 out.push((count >> 8) as u8);
519 out.push((count & 0xFF) as u8);
520 }
521}
522
523static DAC_BANG_REGEX: LazyLock<Regex> =
524 LazyLock::new(|| Regex::new("^Calibration: ([0-9]+) ([0-9]+)").unwrap());
525
526impl GpioBitbanging for HyperdebugGpioBitbanging {
527 fn start<'a>(
528 &self,
529 pins: &[&dyn GpioPin],
530 clock_tick: Duration,
531 waveform: Box<[BitbangEntry<'a, 'a>]>,
532 ) -> Result<Box<dyn GpioBitbangOperation<'a, 'a> + 'a>> {
533 Ok(Box::new(HyperdebugGpioBitbangOperation::new(
534 Rc::clone(&self.inner),
535 self.cmsis_interface,
536 pins,
537 clock_tick,
538 waveform,
539 )?))
540 }
541
542 fn dac_start(
543 &self,
544 pins: &[&dyn GpioPin],
545 clock_tick: Duration,
546 waveform: Box<[DacBangEntry]>,
547 ) -> Result<Box<dyn GpioDacBangOperation>> {
548 Ok(Box::new(HyperdebugGpioDacBangOperation::new(
549 Rc::clone(&self.inner),
550 self.cmsis_interface,
551 pins,
552 clock_tick,
553 &waveform,
554 )?))
555 }
556}
557
558pub struct HyperdebugDataOperation {
561 inner: Rc<Inner>,
562 cmsis_interface: BulkInterface,
563 encoded_waveform: Vec<u8>,
564 free_bytes: usize,
565 out_ptr: usize,
566 in_ptr: usize,
567}
568
569impl HyperdebugDataOperation {
570 const CMSIS_DAP_CUSTOM_COMMAND_GPIO: u8 = 0x83;
572
573 const GPIO_BITBANG: u8 = 0x10;
575 const GPIO_BITBANG_STREAMING: u8 = 0x11;
576
577 const STATUS_BITBANG_IDLE: u8 = 0x00;
579 const STATUS_BITBANG_ONGOING: u8 = 0x01;
580 const STATUS_BITBANG_ERROR_WAVEFORM: u8 = 0x80;
581
582 fn new(
583 inner: Rc<Inner>,
584 cmsis_interface: BulkInterface,
585 encoded_waveform: Vec<u8>,
586 ) -> Result<HyperdebugDataOperation> {
587 let free_bytes: usize = {
590 let usb = inner.usb_device.borrow();
591 let mut pkt = Vec::<u8>::new();
592 pkt.write_u8(Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO)?;
593 pkt.write_u8(Self::GPIO_BITBANG)?;
594 pkt.write_u16::<LittleEndian>(0)?;
595 usb.write_bulk(cmsis_interface.out_endpoint, &pkt)?;
596
597 let mut databytes = [0u8; 64];
598
599 let c = usb.read_bulk(cmsis_interface.in_endpoint, &mut databytes)?;
600 let mut rdr = Cursor::new(&databytes[..c]);
601 ensure!(
602 rdr.read_u8()? == Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO,
603 TransportError::CommunicationError(
604 "Incorrect CMSIS-DAP header in response to GPIO request".to_string()
605 )
606 );
607 ensure!(
608 rdr.read_u8()? == Self::STATUS_BITBANG_IDLE,
609 TransportError::CommunicationError(
610 "HyperDebug not responding correctly".to_string()
611 )
612 );
613 let free_bytes = rdr.read_u16::<LittleEndian>()?;
614 free_bytes as usize
615 };
616 Ok(Self {
617 inner,
618 cmsis_interface,
619 encoded_waveform,
620 free_bytes,
621 out_ptr: 0,
622 in_ptr: 0,
623 })
624 }
625
626 fn query(&mut self) -> Result<bool> {
627 if self.in_ptr >= self.encoded_waveform.len() {
628 return Ok(true);
629 }
630
631 let usb = self.inner.usb_device.borrow();
632 let chunk_size = std::cmp::min(self.encoded_waveform.len() - self.out_ptr, self.free_bytes);
633
634 let mut pkt = Vec::<u8>::new();
635 pkt.write_u8(Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO)?;
636 if self.out_ptr + chunk_size < self.encoded_waveform.len() {
637 pkt.write_u8(Self::GPIO_BITBANG_STREAMING)?;
640 } else {
641 pkt.write_u8(Self::GPIO_BITBANG)?;
643 }
644 pkt.write_u16::<LittleEndian>(chunk_size as u16)?;
645 pkt.extend_from_slice(&self.encoded_waveform[self.out_ptr..self.out_ptr + chunk_size]);
646 usb.write_bulk(self.cmsis_interface.out_endpoint, &pkt)?;
647
648 let mut databytes = [0u8; 64];
649
650 let c = usb.read_bulk(self.cmsis_interface.in_endpoint, &mut databytes)?;
651 let mut rdr = Cursor::new(&databytes[..c]);
652 ensure!(
653 rdr.read_u8()? == Self::CMSIS_DAP_CUSTOM_COMMAND_GPIO,
654 TransportError::CommunicationError(
655 "Incorrect CMSIS-DAP header in response to GPIO request".to_string()
656 )
657 );
658 match rdr.read_u8()? {
659 Self::STATUS_BITBANG_ONGOING => (),
660 Self::STATUS_BITBANG_IDLE => bail!(TransportError::CommunicationError(
661 "GPIO request aborted".to_string()
662 )),
663 Self::STATUS_BITBANG_ERROR_WAVEFORM => bail!(TransportError::CommunicationError(
664 "HyperDebug reports encoding error".to_string()
665 )),
666 status => bail!(TransportError::CommunicationError(std::format!(
667 "Unrecognized status code: {}",
668 status
669 ))),
670 }
671
672 self.free_bytes = rdr.read_u16::<LittleEndian>()? as usize;
673 let response_size = rdr.read_u16::<LittleEndian>()? as usize;
674
675 let final_in_ptr = self.in_ptr + response_size;
676
677 let data_in_header = c - rdr.position() as usize;
679 self.encoded_waveform[self.in_ptr..self.in_ptr + data_in_header]
680 .copy_from_slice(&databytes[rdr.position() as usize..][..data_in_header]);
681 self.in_ptr += data_in_header;
682
683 while self.in_ptr < final_in_ptr {
684 self.in_ptr += usb.read_bulk(
685 self.cmsis_interface.in_endpoint,
686 &mut self.encoded_waveform[self.in_ptr..final_in_ptr],
687 )?;
688 }
689
690 self.out_ptr += chunk_size;
691
692 if self.in_ptr >= self.encoded_waveform.len() {
693 Ok(true)
694 } else {
695 Ok(false)
696 }
697 }
698}
699
700pub struct HyperdebugGpioBitbangOperation<'a> {
702 num_pins: usize,
703 waveform: Box<[BitbangEntry<'a, 'a>]>,
704 operation: HyperdebugDataOperation,
705}
706
707impl<'a> HyperdebugGpioBitbangOperation<'a> {
708 fn new(
709 inner: Rc<Inner>,
710 cmsis_interface: BulkInterface,
711 pins: &[&dyn GpioPin],
712 clock_tick: Duration,
713 waveform: Box<[BitbangEntry<'a, 'a>]>,
714 ) -> Result<Self> {
715 let encoded_waveform = encode_waveform(&waveform, pins.len())?;
718
719 let mut pin_names = Vec::new();
722 for pin in pins {
723 pin_names.push(
724 pin.get_internal_pin_name()
725 .ok_or(TransportError::InvalidOperation)?,
726 );
727 }
728 inner.cmd_no_output(&format!(
729 "gpio bit-bang {} {}",
730 clock_tick.as_nanos(),
731 pin_names.join(" ")
732 ))?;
733
734 Ok(Self {
743 num_pins: pins.len(),
744 waveform,
745 operation: HyperdebugDataOperation::new(inner, cmsis_interface, encoded_waveform)?,
746 })
747 }
748}
749
750impl<'a> GpioBitbangOperation<'a, 'a> for HyperdebugGpioBitbangOperation<'a> {
751 fn query(&mut self) -> Result<bool> {
752 if self.operation.query()? {
753 decode_waveform(
757 &mut self.waveform,
758 &self.operation.encoded_waveform,
759 self.num_pins,
760 )?;
761 Ok(true)
762 } else {
763 Ok(false)
764 }
765 }
766
767 fn get_result(self: Box<Self>) -> Result<Box<[BitbangEntry<'a, 'a>]>> {
768 Ok(self.waveform)
769 }
770}
771
772pub struct HyperdebugGpioDacBangOperation {
777 operation: HyperdebugDataOperation,
778}
779
780impl HyperdebugGpioDacBangOperation {
781 fn new(
782 inner: Rc<Inner>,
783 cmsis_interface: BulkInterface,
784 pins: &[&dyn GpioPin],
785 clock_tick: Duration,
786 waveform: &[DacBangEntry],
787 ) -> Result<Self> {
788 let mut pin_names = Vec::new();
791 for pin in pins {
792 pin_names.push(
793 pin.get_internal_pin_name()
794 .ok_or(TransportError::InvalidOperation)?,
795 );
796 }
797
798 let mut buf = String::new();
799 let captures = inner.cmd_one_line_output_match(
800 &format!(
801 "gpio dac-bang {} {}",
802 clock_tick.as_nanos(),
803 pin_names.join(" ")
804 ),
805 &DAC_BANG_REGEX,
806 &mut buf,
807 )?;
808 let multiplier: u32 = captures.get(1).unwrap().as_str().parse().unwrap();
809 let divisor: u32 = captures.get(2).unwrap().as_str().parse().unwrap();
810
811 let encoder = DacEncoder {
814 factor: 1000.0 * multiplier as f32 / divisor as f32,
815 };
816
817 let mut encoded_waveform: Vec<u8> = Vec::new();
818
819 let mut last: Vec<f32> = Vec::new();
820 last.resize(pins.len(), 0.0);
821 let mut delay = 0u32;
822 let mut linear = 0u32;
823 for a in waveform.iter() {
824 match a {
825 DacBangEntry::Write(d) => {
826 ensure!(
827 !d.is_empty() && d.len() % pins.len() == 0,
828 GpioError::InvalidDacBangData
829 );
830 ensure!(delay == 0 || linear == 0, GpioError::InvalidDacBangDelay);
831 if linear > 1 {
832 for sample_no in 1..linear {
838 for i in 0..pins.len() {
839 let val =
840 last[i] + (d[i] - last[i]) * sample_no as f32 / linear as f32;
841 encoder.encode_dac_sample(&mut encoded_waveform, val);
842 }
843 }
844 }
845 if delay > 1 {
846 let encoded_delay = delay - 1;
850 let mut shift = 0;
851 while (encoded_delay >> shift) != 0 {
852 encoded_waveform.push(0x80 | ((encoded_delay >> shift) & 0x7F) as u8);
853 shift += 7;
854 }
855 }
856 for voltage in *d {
857 encoder.encode_dac_sample(&mut encoded_waveform, *voltage);
858 }
859 for i in 0..pins.len() {
860 last[i] = d[d.len() - pins.len() + i];
861 }
862 delay = 0;
863 linear = 0;
864 }
865 DacBangEntry::WriteOwned(d) => {
866 ensure!(
867 !d.is_empty() && d.len() % pins.len() == 0,
868 GpioError::InvalidDacBangData
869 );
870 ensure!(delay == 0 || linear == 0, GpioError::InvalidDacBangDelay);
871 if linear > 1 {
872 for sample_no in 1..linear {
878 for i in 0..pins.len() {
879 let val =
880 last[i] + (d[i] - last[i]) * sample_no as f32 / linear as f32;
881 encoder.encode_dac_sample(&mut encoded_waveform, val);
882 }
883 }
884 }
885 if delay > 1 {
886 let encoded_delay = delay - 1;
890 let mut shift = 0;
891 while (encoded_delay >> shift) != 0 {
892 encoded_waveform.push(0x80 | ((encoded_delay >> shift) & 0x7F) as u8);
893 shift += 7;
894 }
895 }
896 for voltage in d.iter() {
897 encoder.encode_dac_sample(&mut encoded_waveform, *voltage);
898 }
899 for i in 0..pins.len() {
900 last[i] = d[d.len() - pins.len() + i];
901 }
902 delay = 0;
903 linear = 0;
904 }
905 DacBangEntry::Delay(0) => bail!(GpioError::InvalidDacBangDelay),
906 DacBangEntry::Delay(n @ 1..) => {
907 delay += *n;
908 }
909 DacBangEntry::Linear(0) => bail!(GpioError::InvalidDacBangDelay),
910 DacBangEntry::Linear(n @ 1..) => {
911 linear += *n;
912 }
913 }
914 }
915
916 ensure!(delay == 0, GpioError::InvalidBitbangDelay);
918 ensure!(linear == 0, GpioError::InvalidBitbangDelay);
919
920 Ok(Self {
921 operation: HyperdebugDataOperation::new(inner, cmsis_interface, encoded_waveform)?,
922 })
923 }
924}
925
926impl GpioDacBangOperation for HyperdebugGpioDacBangOperation {
927 fn query(&mut self) -> Result<bool> {
928 self.operation.query()
929 }
930}
931
932fn encode_waveform(waveform: &[BitbangEntry], num_pins: usize) -> Result<Vec<u8>> {
934 ensure!(
935 (1..=7).contains(&num_pins),
936 GpioError::UnsupportedNumberOfPins(num_pins)
937 );
938
939 let mut encoded_waveform = Vec::<u8>::new();
940
941 let mut delay = 0u32;
942 for entry in waveform {
943 match entry {
944 BitbangEntry::Write(wbuf) | BitbangEntry::Both(wbuf, _) => {
945 if delay > 1 {
946 let encoded_delay = delay - 1;
950 let mut shift = 0;
951 while (encoded_delay >> shift) != 0 {
952 encoded_waveform.push(0x80 | ((encoded_delay >> shift) & 0x7F) as u8);
953 shift += 7;
954 }
955 }
956 for byte in *wbuf {
959 ensure!(
960 (byte >> num_pins) == 0,
961 GpioError::InvalidBitbangData(num_pins)
962 );
963 }
964 encoded_waveform.extend_from_slice(wbuf);
965 delay = 0;
966 }
967 BitbangEntry::WriteOwned(wbuf) | BitbangEntry::BothOwned(wbuf) => {
968 if delay > 1 {
969 let encoded_delay = delay - 1;
973 let mut shift = 0;
974 while (encoded_delay >> shift) != 0 {
975 encoded_waveform.push(0x80 | ((encoded_delay >> shift) & 0x7F) as u8);
976 shift += 7;
977 }
978 }
979 for byte in wbuf.iter() {
982 ensure!(
983 (byte >> num_pins) == 0,
984 GpioError::InvalidBitbangData(num_pins)
985 );
986 }
987 encoded_waveform.extend_from_slice(wbuf);
988 delay = 0;
989 }
990 BitbangEntry::Delay(0) => bail!(GpioError::InvalidBitbangDelay),
991 BitbangEntry::Delay(n @ 1..) => {
992 delay += *n;
993 }
994 BitbangEntry::Await { mask, pattern } => {
995 ensure!(delay == 0, GpioError::InvalidBitbangDelay);
996 encoded_waveform.extend_from_slice(&[0x80, 0x80, *mask, *pattern]);
997 delay = 0;
998 }
999 }
1000 }
1001
1002 ensure!(delay == 0, GpioError::InvalidBitbangDelay);
1004
1005 Ok(encoded_waveform)
1006}
1007
1008fn decode_waveform(
1012 waveform: &mut [BitbangEntry],
1013 encoded_response: &[u8],
1014 num_pins: usize,
1015) -> Result<()> {
1016 ensure!(
1017 (1..=7).contains(&num_pins),
1018 GpioError::UnsupportedNumberOfPins(num_pins)
1019 );
1020
1021 let mut index = 0usize;
1022 for entry in waveform {
1023 match entry {
1024 BitbangEntry::Write(wbuf) => {
1025 index += wbuf.len();
1026 }
1027 BitbangEntry::WriteOwned(wbuf) => {
1028 index += wbuf.len();
1029 }
1030 BitbangEntry::Both(wbuf, rbuf) => {
1031 ensure!(
1032 rbuf.len() == wbuf.len(),
1033 GpioError::MismatchedDataLength(wbuf.len(), rbuf.len())
1034 );
1035 rbuf.copy_from_slice(&encoded_response[index..][..rbuf.len()]);
1036 index += wbuf.len();
1037 }
1038 BitbangEntry::BothOwned(rbuf) => {
1039 rbuf.copy_from_slice(&encoded_response[index..][..rbuf.len()]);
1040 index += rbuf.len();
1041 }
1042 BitbangEntry::Delay(_) => {
1043 while encoded_response[index] & 0x80 != 0 {
1044 index += 1;
1045 }
1046 }
1047 BitbangEntry::Await { .. } => {
1048 index += 4;
1049 }
1050 }
1051 }
1052
1053 assert!(index == encoded_response.len());
1054
1055 Ok(())
1056}
1057
1058#[cfg(test)]
1059mod tests {
1060 use super::*;
1061
1062 #[test]
1063 fn test_encode_waveforms() {
1064 let encoding = encode_waveform(
1065 &[
1066 BitbangEntry::Write(&[0, 1, 0, 1]),
1067 BitbangEntry::Delay(0x0101),
1068 BitbangEntry::Write(&[0, 1, 0, 1]),
1069 ],
1070 1,
1071 )
1072 .unwrap();
1073
1074 assert_eq!(
1075 encoding,
1076 [0x00, 0x01, 0x00, 0x01, 0x80, 0x82, 0x00, 0x01, 0x00, 0x01]
1077 );
1078 }
1079}