opentitanlib/transport/chip_whisperer/
mod.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::{Result, ensure};
6use serde_annotate::Annotate;
7use serialport::SerialPortType;
8use std::any::Any;
9use std::cell::RefCell;
10use std::collections::HashMap;
11use std::collections::hash_map::Entry;
12use std::rc::Rc;
13
14use crate::io::gpio::GpioPin;
15use crate::io::spi::Target;
16use crate::io::uart::{Uart, UartError};
17use crate::transport::common::fpga::{ClearBitstream, FpgaProgram};
18use crate::transport::common::uart::{SerialPortUart, SoftwareFlowControl};
19use crate::transport::{
20    Capabilities, Capability, Transport, TransportError, TransportInterfaceType,
21};
22use crate::util::parse_int::ParseInt;
23use board::Board;
24
25pub mod board;
26pub mod gpio;
27pub mod spi;
28pub mod usb;
29
30#[derive(Default)]
31struct Inner {
32    spi: Option<Rc<dyn Target>>,
33    gpio: HashMap<String, Rc<dyn GpioPin>>,
34    uart: HashMap<u32, Rc<dyn Uart>>,
35}
36
37pub struct ChipWhisperer<B: Board> {
38    pub(crate) device: Rc<RefCell<usb::Backend<B>>>,
39    uart_override: Vec<String>,
40    inner: RefCell<Inner>,
41}
42
43impl<B: Board> ChipWhisperer<B> {
44    pub fn new(
45        usb_vid: Option<u16>,
46        usb_pid: Option<u16>,
47        usb_serial: Option<&str>,
48        uart_override: &[&str],
49    ) -> anyhow::Result<Self> {
50        let board = ChipWhisperer {
51            device: Rc::new(RefCell::new(usb::Backend::new(
52                usb_vid, usb_pid, usb_serial,
53            )?)),
54            uart_override: uart_override.iter().map(|s| s.to_string()).collect(),
55            inner: RefCell::default(),
56        };
57        Ok(board)
58    }
59
60    fn open_uart(&self, instance: u32) -> Result<SoftwareFlowControl<SerialPortUart>> {
61        if self.uart_override.is_empty() {
62            let usb = self.device.borrow();
63            let serial_number = usb.get_serial_number();
64
65            let mut ports = serialport::available_ports()
66                .map_err(|e| UartError::EnumerationError(e.to_string()))?;
67            ports.retain(|port| {
68                if let SerialPortType::UsbPort(info) = &port.port_type
69                    && info.serial_number.as_deref() == Some(serial_number)
70                {
71                    return true;
72                }
73                false
74            });
75            // The CW board seems to have the last port connected as OpenTitan UART 0.
76            // Reverse the sort order so the last port will be instance 0.
77            ports.sort_by(|a, b| b.port_name.cmp(&a.port_name));
78
79            let port = ports.get(instance as usize).ok_or_else(|| {
80                TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string())
81            })?;
82            Ok(SoftwareFlowControl::new(SerialPortUart::open(
83                &port.port_name,
84                B::UART_BAUD,
85            )?))
86        } else {
87            let instance = instance as usize;
88            ensure!(
89                instance < self.uart_override.len(),
90                TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string())
91            );
92            Ok(SoftwareFlowControl::new(SerialPortUart::open(
93                &self.uart_override[instance],
94                B::UART_BAUD,
95            )?))
96        }
97    }
98}
99
100impl<B: Board + 'static> Transport for ChipWhisperer<B> {
101    fn capabilities(&self) -> Result<Capabilities> {
102        Ok(Capabilities::new(
103            Capability::SPI | Capability::GPIO | Capability::UART | Capability::UART_NONBLOCKING,
104        ))
105    }
106
107    fn uart(&self, instance: &str) -> Result<Rc<dyn Uart>> {
108        let mut inner = self.inner.borrow_mut();
109        let instance = u32::from_str(instance).ok().ok_or_else(|| {
110            TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string())
111        })?;
112        let uart = match inner.uart.entry(instance) {
113            Entry::Vacant(v) => {
114                let u = v.insert(Rc::new(self.open_uart(instance)?));
115                Rc::clone(u)
116            }
117            Entry::Occupied(o) => Rc::clone(o.get()),
118        };
119        Ok(uart)
120    }
121
122    fn gpio_pin(&self, pinname: &str) -> Result<Rc<dyn GpioPin>> {
123        let mut inner = self.inner.borrow_mut();
124        Ok(match inner.gpio.entry(pinname.to_string()) {
125            Entry::Vacant(v) => {
126                let u = v.insert(Rc::new(gpio::Pin::open(
127                    Rc::clone(&self.device),
128                    pinname.to_string(),
129                )?));
130                Rc::clone(u)
131            }
132            Entry::Occupied(o) => Rc::clone(o.get()),
133        })
134    }
135
136    fn spi(&self, instance: &str) -> Result<Rc<dyn Target>> {
137        ensure!(
138            instance == "0",
139            TransportError::InvalidInstance(TransportInterfaceType::Spi, instance.to_string())
140        );
141        let mut inner = self.inner.borrow_mut();
142        if inner.spi.is_none() {
143            inner.spi = Some(Rc::new(spi::Spi::open(Rc::clone(&self.device))?));
144        }
145        Ok(Rc::clone(inner.spi.as_ref().unwrap()))
146    }
147
148    fn dispatch(&self, action: &dyn Any) -> Result<Option<Box<dyn Annotate>>> {
149        if let Some(fpga_program) = action.downcast_ref::<FpgaProgram>() {
150            // Program the FPGA bitstream.
151            log::info!("Programming the FPGA bitstream.");
152            let usb = self.device.borrow();
153            usb.spi1_enable(false)?;
154            usb.fpga_program(&fpga_program.bitstream, fpga_program.progress.as_ref())?;
155            Ok(None)
156        } else if action.downcast_ref::<ResetSam3x>().is_some() {
157            self.device.borrow().reset_sam3x()?;
158            Ok(None)
159        } else if action.downcast_ref::<SetPll>().is_some() {
160            const TARGET_FREQ: u32 = 100_000_000;
161            let usb = self.device.borrow();
162            usb.pll_enable(true)?;
163            usb.pll_out_freq_set(1, TARGET_FREQ)?;
164            usb.pll_out_freq_set(2, TARGET_FREQ)?;
165            usb.pll_out_enable(0, false)?;
166            usb.pll_out_enable(1, true)?;
167            usb.pll_out_enable(2, false)?;
168            usb.pll_write_defaults()?;
169            Ok(None)
170        } else if action.downcast_ref::<ClearBitstream>().is_some() {
171            let usb = self.device.borrow();
172            usb.spi1_enable(false)?;
173            usb.clear_bitstream()?;
174            Ok(None)
175        } else if action.downcast_ref::<GetSam3xFwVersion>().is_some() {
176            let usb = self.device.borrow();
177            Ok(Some(Box::new(usb.get_firmware_version()?)))
178        } else {
179            Err(TransportError::UnsupportedOperation.into())
180        }
181    }
182}
183
184/// Command for Transport::dispatch().
185pub struct SetPll {}
186
187/// Command for Transport::dispatch(). Resets the Chip whisperer board's SAM3X chip.
188pub struct ResetSam3x {}
189
190/// Command for Transport::dispatch(). Returns the SAM3X firmware version.
191pub struct GetSam3xFwVersion {}