opentitanlib/transport/dediprog/
mod.rs1#![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, 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 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
319pub 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 fn write(&self, _value: bool) -> Result<()> {
339 bail!(TransportError::UnsupportedOperation)
340 }
341
342 fn set_mode(&self, mode: PinMode) -> Result<()> {
344 match mode {
345 PinMode::AnalogOutput => Ok(()),
346 _ => bail!(GpioError::UnsupportedPinMode(mode)),
347 }
348 }
349
350 fn set_pull_mode(&self, mode: PullMode) -> Result<()> {
352 match mode {
353 PullMode::None => Ok(()),
354 _ => bail!(GpioError::UnsupportedPullMode(mode)),
355 }
356 }
357
358 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 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());