diff --git a/sw/host/opentitanlib/BUILD b/sw/host/opentitanlib/BUILD index a70dab0def813..6294d38ceb3e9 100644 --- a/sw/host/opentitanlib/BUILD +++ b/sw/host/opentitanlib/BUILD @@ -185,6 +185,7 @@ rust_library( "src/transport/ftdi/chip.rs", "src/transport/ftdi/gpio.rs", "src/transport/ftdi/mod.rs", + "src/transport/ftdi/spi.rs", "src/transport/hyperdebug/c2d2.rs", "src/transport/hyperdebug/dfu.rs", "src/transport/hyperdebug/gpio.rs", diff --git a/sw/host/opentitanlib/src/transport/ftdi/mod.rs b/sw/host/opentitanlib/src/transport/ftdi/mod.rs index 9b06eca70d3a5..bf45e33904e03 100644 --- a/sw/host/opentitanlib/src/transport/ftdi/mod.rs +++ b/sw/host/opentitanlib/src/transport/ftdi/mod.rs @@ -26,6 +26,7 @@ use ftdi_embedded_hal as ftdi_hal; pub mod chip; pub mod gpio; +pub mod spi; #[derive(Default)] struct Inner { @@ -116,7 +117,14 @@ impl Transport for Ftdi { } fn spi(&self, _instance: &str) -> Result> { - Err(TransportError::UnsupportedOperation.into()) + let mut inner = self.inner.borrow_mut(); + if inner.spi.is_none() { + inner.spi = Some(Rc::new(spi::Spi::open( + &self.ftdi_interfaces, + gpio::Pin::open::(&self.ftdi_interfaces, "bdbus3".to_string())?, + )?)); + } + Ok(Rc::clone(inner.spi.as_ref().unwrap())) } fn dispatch(&self, _action: &dyn Any) -> Result>> { diff --git a/sw/host/opentitanlib/src/transport/ftdi/spi.rs b/sw/host/opentitanlib/src/transport/ftdi/spi.rs new file mode 100644 index 0000000000000..7c44fee42ea8f --- /dev/null +++ b/sw/host/opentitanlib/src/transport/ftdi/spi.rs @@ -0,0 +1,137 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use crate::io::spi::Transfer; +use anyhow::Result; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +use embedded_hal::spi::SpiBus; + +use ftdi_embedded_hal as ftdi_hal; + +use crate::io::gpio; +use crate::io::spi::AssertChipSelect; +use crate::io::spi::MaxSizes; +use crate::io::spi::SpiError; +use crate::io::spi::TransferMode; +use crate::transport::Target; +use crate::transport::TransportError; + +pub struct Spi { + spi: Rc>>, + cs: T, +} + +impl Spi { + pub fn open( + ftdi_interfaces: &HashMap>, + cs: T, + ) -> Result { + let hal = ftdi_interfaces + .get(&ftdi::Interface::B) + .ok_or_else(|| SpiError::InvalidOption("spi interface".to_owned()))?; + let spi = hal.spi()?; + Ok(Spi { + spi: Rc::new(RefCell::new(spi)), + cs, + }) + } + + // Perform a SPI transaction. + fn spi_transaction(&self, transaction: &mut [Transfer]) -> Result<()> { + for transfer in transaction.iter_mut() { + match transfer { + Transfer::Read(buf) => self.spi.borrow_mut().read(buf)?, + Transfer::Write(buf) => self.spi.borrow_mut().write(buf)?, + Transfer::Both(wbuf, rbuf) => self.spi.borrow_mut().transfer(rbuf, wbuf)?, + Transfer::TpmPoll => (), + Transfer::GscReady => (), + } + } + Ok(()) + } +} + +impl Target for Spi { + fn get_transfer_mode(&self) -> Result { + Ok(TransferMode::Mode0) + } + fn set_transfer_mode(&self, mode: TransferMode) -> Result<()> { + log::warn!( + "set_transfer_mode to {:?}, but only Mode0 is supported on this device", + mode + ); + Ok(()) + } + + fn get_bits_per_word(&self) -> Result { + Ok(8) + } + fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()> { + match bits_per_word { + 8 => Ok(()), + _ => Err(SpiError::InvalidWordSize(bits_per_word).into()), + } + } + + fn get_max_speed(&self) -> Result { + // FIXME: what is the speed of the SAM3U SPI interface on the Chip Whisperer board? + Ok(6_000_000) + } + fn set_max_speed(&self, frequency: u32) -> Result<()> { + log::warn!( + "set_max_speed to {:?}, but this device doesn't support changing speeds.", + frequency + ); + Ok(()) + } + + fn supports_bidirectional_transfer(&self) -> Result { + Ok(true) + } + + /// Indicates whether `Transfer::TpmPoll` is supported. + fn supports_tpm_poll(&self) -> Result { + Ok(false) + } + + fn set_pins( + &self, + _serial_clock: Option<&Rc>, + _host_out_device_in: Option<&Rc>, + _host_in_device_out: Option<&Rc>, + _chip_select: Option<&Rc>, + _gsc_ready: Option<&Rc>, + ) -> Result<()> { + Err(TransportError::UnsupportedOperation.into()) + } + + fn get_max_transfer_count(&self) -> Result { + // Arbitrary value: number of `Transfers` that can be in a single transaction. + Ok(42) + } + + fn get_max_transfer_sizes(&self) -> Result { + Ok(MaxSizes { + read: 1024, + write: 1024, + }) + } + + fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()> { + // Assert CS# (drive low). + self.cs.write(false)?; + // Translate SPI Read/Write Transactions into Chip Whisperer spi operations. + let result = self.spi_transaction(transaction); + // Release CS# (allow to float high). + self.cs.write(true)?; + result + } + + fn assert_cs(self: Rc) -> Result { + Err(TransportError::UnsupportedOperation.into()) + } +}