opentitanlib/transport/dediprog/
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#![allow(dead_code)]
5#![allow(unused_imports)]
6
7use std::any::Any;
8use std::cell::RefCell;
9use std::collections::HashMap;
10use std::collections::hash_map::Entry;
11use std::rc::Rc;
12use std::sync::LazyLock;
13
14use anyhow::{Result, bail, ensure};
15use regex::Regex;
16use serde_annotate::Annotate;
17use serialport::SerialPortType;
18
19use crate::io::gpio::{GpioError, GpioPin, PinMode, PullMode};
20use crate::io::spi::Target;
21use crate::io::uart::{Uart, UartError};
22use crate::transport::common::fpga::{ClearBitstream, FpgaProgram};
23use crate::transport::common::uart::SerialPortUart;
24use crate::transport::{
25    Capabilities, Capability, Transport, TransportError, TransportInterfaceType,
26};
27use crate::util::parse_int::ParseInt;
28use crate::util::usb::UsbBackend;
29
30pub mod gpio;
31pub mod spi;
32
33pub struct Inner {
34    device: UsbBackend,
35    spi: Option<Rc<dyn Target>>,
36    gpio: HashMap<String, Rc<dyn GpioPin>>,
37    gpio_levels: u16,
38    spi_clock: ClockSpeed,
39    voltage: Voltage,
40    in_endpoint: u8,
41    out_endpoint: u8,
42}
43
44impl Inner {
45    fn new(device: UsbBackend, in_endpoint: u8, out_endpoint: u8) -> Self {
46        Self {
47            device,
48            spi: None,
49            gpio: HashMap::new(),
50            gpio_levels: 0xFFFF, // Low level turns on LEDs.
51            spi_clock: ClockSpeed::Clk375Khz,
52            voltage: Voltage::V0,
53            in_endpoint,
54            out_endpoint,
55        }
56    }
57
58    fn set_gpio_levels(&self) -> Result<()> {
59        self.device.write_control(
60            rusb::request_type(
61                rusb::Direction::Out,
62                rusb::RequestType::Vendor,
63                rusb::Recipient::Endpoint,
64            ),
65            Command::SetIoLed as u8,
66            self.gpio_levels,
67            0,
68            &[],
69        )?;
70        Ok(())
71    }
72
73    fn set_voltage(&self) -> Result<()> {
74        self.device.write_control(
75            rusb::request_type(
76                rusb::Direction::Out,
77                rusb::RequestType::Vendor,
78                rusb::Recipient::Endpoint,
79            ),
80            Command::SetVcc as u8,
81            self.voltage as u16,
82            0,
83            &[],
84        )?;
85        Ok(())
86    }
87}
88
89pub struct Dediprog {
90    inner: Rc<RefCell<Inner>>,
91}
92
93#[derive(Copy, Clone)]
94enum Command {
95    Transceive = 0x01,
96    SetIoLed = 0x07,
97    ReadProgInfo = 0x08,
98    SetVcc = 0x09,
99    SetVoltage = 0x0B,
100    Read = 0x20,
101    Write = 0x30,
102    SetSpiClk = 0x61,
103}
104
105#[derive(Copy, Clone)]
106enum ClockSpeed {
107    Clk24Mhz = 0,
108    Clk12Mhz = 2,
109    Clk8Mhz = 1,
110    Clk3Mhz = 3,
111    Clk2p18Mhz = 4,
112    Clk1p5Mhz = 5,
113    Clk750Khz = 6,
114    Clk375Khz = 7,
115}
116
117#[derive(Copy, Clone)]
118enum Voltage {
119    V0 = 0x00,
120    V1p8 = 0x12,
121    V2p5 = 0x11,
122    V3p5 = 0x10,
123}
124
125impl Dediprog {
126    const VID_ST_MICROELECTRONICS: u16 = 0x0483;
127    const PID_DEDIPROG_SF100: u16 = 0xDADA;
128
129    pub fn new(
130        usb_vid: Option<u16>,
131        usb_pid: Option<u16>,
132        usb_serial: Option<&str>,
133    ) -> anyhow::Result<Self> {
134        let mut device = UsbBackend::new(
135            usb_vid.unwrap_or(Self::VID_ST_MICROELECTRONICS),
136            usb_pid.unwrap_or(Self::PID_DEDIPROG_SF100),
137            usb_serial,
138        )?;
139
140        device.set_active_configuration(1)?;
141
142        let config_desc = device.active_config_descriptor()?;
143        // Iterate through each USB interface, discovering endpoints.
144        let mut in_endpoint: Option<u8> = None;
145        let mut out_endpoint: Option<u8> = None;
146        for interface in config_desc.interfaces() {
147            for interface_desc in interface.descriptors() {
148                for endpoint_desc in interface_desc.endpoint_descriptors() {
149                    if endpoint_desc.transfer_type() != rusb::TransferType::Bulk {
150                        continue;
151                    }
152                    match endpoint_desc.direction() {
153                        rusb::Direction::In => {
154                            ensure!(
155                                in_endpoint.is_none(),
156                                TransportError::CommunicationError(
157                                    "Multiple IN endpoints".to_string()
158                                )
159                            );
160                            in_endpoint.replace(endpoint_desc.address());
161                        }
162                        rusb::Direction::Out => {
163                            ensure!(
164                                out_endpoint.is_none(),
165                                TransportError::CommunicationError(
166                                    "Multiple OUT endpoints".to_string()
167                                )
168                            );
169                            out_endpoint.replace(endpoint_desc.address());
170                        }
171                    }
172                }
173            }
174        }
175        let (Some(in_endpoint), Some(out_endpoint)) = (in_endpoint, out_endpoint) else {
176            return Err(TransportError::UsbOpenError(
177                "Dediprog did not respond correctly".to_string(),
178            )
179            .into());
180        };
181
182        device.claim_interface(0)?;
183
184        let protocol_version = match Self::get_protocol_version(&device) {
185            Ok(protocol_version) => protocol_version,
186            Err(_) => {
187                let mut init_byte = [0u8];
188
189                device.read_control(
190                    rusb::request_type(
191                        rusb::Direction::In,
192                        rusb::RequestType::Vendor,
193                        rusb::Recipient::Other,
194                    ),
195                    Command::SetVoltage as u8,
196                    0,
197                    0,
198                    &mut init_byte,
199                )?;
200
201                if init_byte[0] != 0x6F {
202                    return Err(TransportError::UsbOpenError(
203                        "Dediprog did not respond correctly".to_string(),
204                    )
205                    .into());
206                }
207                Self::get_protocol_version(&device)?
208            }
209        };
210        if protocol_version < 2 {
211            return Err(TransportError::UsbOpenError(format!(
212                "Unsupportred Dediprog protocol version: {}",
213                protocol_version
214            ))
215            .into());
216        }
217
218        let inner = Inner::new(device, in_endpoint, out_endpoint);
219        inner.set_gpio_levels()?;
220        inner.set_voltage()?;
221        let board = Self {
222            inner: Rc::new(RefCell::new(inner)),
223        };
224        Ok(board)
225    }
226
227    fn get_protocol_version(device: &UsbBackend) -> Result<u32> {
228        let mut device_id_bytes = [0u8; 16];
229        device.read_control(
230            rusb::request_type(
231                rusb::Direction::In,
232                rusb::RequestType::Vendor,
233                rusb::Recipient::Endpoint,
234            ),
235            Command::ReadProgInfo as u8,
236            0,
237            0,
238            &mut device_id_bytes,
239        )?;
240        let device_id_str = std::str::from_utf8(&device_id_bytes)?;
241        let Some(captures) = DEDIPROG_VERSION_REGEX.captures(device_id_str) else {
242            return Err(TransportError::UsbOpenError(format!(
243                "Unrecognized Dediprog version: {}",
244                &device_id_str
245            ))
246            .into());
247        };
248        let product = captures.get(1).unwrap().as_str();
249        let version: [u32; 3] = [
250            captures.get(2).unwrap().as_str().parse()?,
251            captures.get(3).unwrap().as_str().parse()?,
252            captures.get(4).unwrap().as_str().parse()?,
253        ];
254
255        let protocol_version = if product == "SF100" || product == "SF200" {
256            if version < [5, 5, 0] { 1 } else { 2 }
257        } else if product == "SF600" {
258            if version < [6, 9, 0] {
259                1
260            } else if version < [7, 2, 21] {
261                2
262            } else {
263                3
264            }
265        } else {
266            return Err(TransportError::UsbOpenError(format!(
267                "Unrecognized Dediprog version: {}",
268                &device_id_str
269            ))
270            .into());
271        };
272        log::info!(
273            "DediProg: {}, version: {}.{}.{}, protocol V{}",
274            product,
275            version[0],
276            version[1],
277            version[2],
278            protocol_version
279        );
280        Ok(protocol_version)
281    }
282}
283
284impl Transport for Dediprog {
285    fn capabilities(&self) -> Result<Capabilities> {
286        Ok(Capabilities::new(Capability::SPI | Capability::GPIO))
287    }
288
289    fn gpio_pin(&self, pinname: &str) -> Result<Rc<dyn GpioPin>> {
290        if pinname == "VCC" {
291            return Ok(Rc::new(VoltagePin::open(&self.inner)?));
292        }
293        let mut inner = self.inner.borrow_mut();
294        Ok(match inner.gpio.entry(pinname.to_string()) {
295            Entry::Vacant(v) => {
296                let u = v.insert(Rc::new(gpio::DediprogPin::open(
297                    Rc::clone(&self.inner),
298                    pinname,
299                )?));
300                Rc::clone(u)
301            }
302            Entry::Occupied(o) => Rc::clone(o.get()),
303        })
304    }
305
306    fn spi(&self, instance: &str) -> Result<Rc<dyn Target>> {
307        ensure!(
308            instance == "0",
309            TransportError::InvalidInstance(TransportInterfaceType::Spi, instance.to_string())
310        );
311        if self.inner.borrow().spi.is_none() {
312            self.inner.borrow_mut().spi =
313                Some(Rc::new(spi::DediprogSpi::open(Rc::clone(&self.inner))?));
314        }
315        Ok(Rc::clone(self.inner.borrow().spi.as_ref().unwrap()))
316    }
317}
318
319/// The setting of Dediprog programming voltage is exposed as a "pin" in DAC mode.
320pub struct VoltagePin {
321    inner: Rc<RefCell<Inner>>,
322}
323
324impl VoltagePin {
325    pub fn open(inner: &Rc<RefCell<Inner>>) -> Result<Self> {
326        Ok(Self {
327            inner: Rc::clone(inner),
328        })
329    }
330}
331
332impl GpioPin for VoltagePin {
333    fn read(&self) -> Result<bool> {
334        bail!(TransportError::UnsupportedOperation)
335    }
336
337    /// Sets the value of the GPIO reset pin by means of the special h1_reset command.
338    fn write(&self, _value: bool) -> Result<()> {
339        bail!(TransportError::UnsupportedOperation)
340    }
341
342    /// Sets the mode of the GPIO pin as input, output, or open drain I/O.
343    fn set_mode(&self, mode: PinMode) -> Result<()> {
344        match mode {
345            PinMode::AnalogOutput => Ok(()),
346            _ => bail!(GpioError::UnsupportedPinMode(mode)),
347        }
348    }
349
350    /// Sets the weak pull resistors of the GPIO pin.
351    fn set_pull_mode(&self, mode: PullMode) -> Result<()> {
352        match mode {
353            PullMode::None => Ok(()),
354            _ => bail!(GpioError::UnsupportedPullMode(mode)),
355        }
356    }
357
358    /// Reads the Dediprog voltage in Volts.
359    fn analog_read(&self) -> Result<f32> {
360        Ok(match self.inner.borrow().voltage {
361            Voltage::V0 => 0.0,
362            Voltage::V1p8 => 1.8,
363            Voltage::V2p5 => 2.5,
364            Voltage::V3p5 => 3.5,
365        })
366    }
367
368    /// Sets the Dediprog voltage to `value` Volts.
369    fn analog_write(&self, volts: f32) -> Result<()> {
370        let mut inner = self.inner.borrow_mut();
371        inner.voltage = if volts <= 0.3 {
372            Voltage::V0
373        } else if (1.6..=2.0).contains(&volts) {
374            Voltage::V1p8
375        } else if (2.3..=2.7).contains(&volts) {
376            Voltage::V2p5
377        } else if (3.0..=3.6).contains(&volts) {
378            Voltage::V3p5
379        } else {
380            bail!(GpioError::UnsupportedPinVoltage(volts))
381        };
382        inner.set_voltage()
383    }
384}
385
386static DEDIPROG_VERSION_REGEX: LazyLock<Regex> =
387    LazyLock::new(|| Regex::new("^([^ ]+) +V:([0-9]+)\\.([0-9]+)\\.([0-9]+)").unwrap());