From 7e141a7d754189d81176cafe34510c1c54a75a63 Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Thu, 12 Dec 2024 18:40:44 +0000 Subject: [PATCH 1/4] [rust] Add new dependencies to opentitanlib The new dependencies will be used to implement a new transport that will use a ftdi chip to bootstrap a opentitan chip. Signed-off-by: Douglas Reis --- third_party/rust/Cargo.lock | 63 +++++++++++++++++++++++++++++++++++++ third_party/rust/Cargo.toml | 3 ++ 2 files changed, 66 insertions(+) diff --git a/third_party/rust/Cargo.lock b/third_party/rust/Cargo.lock index 8888900be4301..1b786fc13e15a 100644 --- a/third_party/rust/Cargo.lock +++ b/third_party/rust/Cargo.lock @@ -296,10 +296,13 @@ dependencies = [ "ecdsa", "elf", "elliptic-curve", + "embedded-hal 1.0.0", "env_logger", "erased-serde", "foreign-types", "ftdi", + "ftdi-embedded-hal", + "ftdi-mpsse", "heck", "hex", "humantime", @@ -732,6 +735,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -855,6 +884,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ftdi-embedded-hal" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e328c2738c104e2649586bda4baabf376825db50014eb0d41bee74fb6064deb" +dependencies = [ + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-nb", + "ftdi-mpsse", + "nb 1.1.0", +] + [[package]] name = "ftdi-mpsse" version = "0.1.1" @@ -1400,6 +1442,21 @@ dependencies = [ "mio", ] +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -2713,6 +2770,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" diff --git a/third_party/rust/Cargo.toml b/third_party/rust/Cargo.toml index d324a51c141e6..f4fd8ad5656a2 100644 --- a/third_party/rust/Cargo.toml +++ b/third_party/rust/Cargo.toml @@ -35,6 +35,9 @@ erased-serde = "0.3.12" # too recent version because it break things. foreign-types = "0.3.2" ftdi = "0.1.3" +ftdi-embedded-hal = "0.22.0" +ftdi-mpsse = "0.1.1" +embedded-hal = "1.0.0" heck = "0.4.1" hex = { version = "0.4.3", features = ["serde"] } humantime = "2.1.0" From 42c8ab04fcf7e8cdc3a92f39afbe099a6c155ae3 Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Fri, 13 Dec 2024 15:26:07 +0000 Subject: [PATCH 2/4] [opentitanlib] Add ftdi transport for voyager board Signed-off-by: Douglas Reis --- sw/host/opentitanlib/BUILD | 6 + sw/host/opentitanlib/src/app/config/mod.rs | 1 + .../app/config/opentitan_ftdi_voyager.json | 87 +++++++++++++ sw/host/opentitanlib/src/backend/ftdi.rs | 14 ++ sw/host/opentitanlib/src/backend/mod.rs | 6 + .../opentitanlib/src/transport/ftdi/chip.rs | 24 ++++ .../opentitanlib/src/transport/ftdi/mod.rs | 120 ++++++++++++++++++ sw/host/opentitanlib/src/transport/mod.rs | 1 + 8 files changed, 259 insertions(+) create mode 100644 sw/host/opentitanlib/src/app/config/opentitan_ftdi_voyager.json create mode 100644 sw/host/opentitanlib/src/backend/ftdi.rs create mode 100644 sw/host/opentitanlib/src/transport/ftdi/chip.rs create mode 100644 sw/host/opentitanlib/src/transport/ftdi/mod.rs diff --git a/sw/host/opentitanlib/BUILD b/sw/host/opentitanlib/BUILD index 8583e428df238..c3477f5f99863 100644 --- a/sw/host/opentitanlib/BUILD +++ b/sw/host/opentitanlib/BUILD @@ -65,6 +65,7 @@ rust_library( "src/app/mod.rs", "src/app/spi.rs", "src/backend/chip_whisperer.rs", + "src/backend/ftdi.rs", "src/backend/hyperdebug.rs", "src/backend/mod.rs", "src/backend/proxy.rs", @@ -182,6 +183,8 @@ rust_library( "src/transport/dediprog/mod.rs", "src/transport/dediprog/spi.rs", "src/transport/errors.rs", + "src/transport/ftdi/chip.rs", + "src/transport/ftdi/mod.rs", "src/transport/hyperdebug/c2d2.rs", "src/transport/hyperdebug/dfu.rs", "src/transport/hyperdebug/gpio.rs", @@ -300,9 +303,12 @@ rust_library( "@crate_index//:deser-hjson", "@crate_index//:directories", "@crate_index//:ecdsa", + "@crate_index//:embedded-hal", "@crate_index//:env_logger", "@crate_index//:erased-serde", "@crate_index//:ftdi", + "@crate_index//:ftdi-embedded-hal", + "@crate_index//:ftdi-mpsse", "@crate_index//:hex", "@crate_index//:humantime", "@crate_index//:humantime-serde", diff --git a/sw/host/opentitanlib/src/app/config/mod.rs b/sw/host/opentitanlib/src/app/config/mod.rs index 43c4cba56566f..d3fdf422654f8 100644 --- a/sw/host/opentitanlib/src/app/config/mod.rs +++ b/sw/host/opentitanlib/src/app/config/mod.rs @@ -67,6 +67,7 @@ static BUILTINS: Lazy> = Lazy::new(|| { "/__builtin__/ti50emulator.json" => include_str!("ti50emulator.json"), "/__builtin__/opentitan_cw310.json" => include_str!("opentitan_cw310.json"), "/__builtin__/opentitan_cw340.json" => include_str!("opentitan_cw340.json"), + "/__builtin__/opentitan_ftdi_voyager.json" => include_str!("opentitan_ftdi_voyager.json"), "/__builtin__/opentitan.json" => include_str!("opentitan.json"), "/__builtin__/hyperdebug.json" => include_str!("hyperdebug.json"), "/__builtin__/hyperdebug_chipwhisperer.json" => include_str!("hyperdebug_chipwhisperer.json"), diff --git a/sw/host/opentitanlib/src/app/config/opentitan_ftdi_voyager.json b/sw/host/opentitanlib/src/app/config/opentitan_ftdi_voyager.json new file mode 100644 index 0000000000000..38a079c575921 --- /dev/null +++ b/sw/host/opentitanlib/src/app/config/opentitan_ftdi_voyager.json @@ -0,0 +1,87 @@ +{ + "includes": ["/__builtin__/opentitan.json"], + "interface": "ftdi", + "pins": [ + { + "name": "RESET", + "mode": "PushPull", + "invert": false, + "alias_of": "adbus5", + "pull_mode": "None" + }, + { + "name": "TRST", + "mode": "PushPull", + "level": false, + "alias_of": "adbus4", + "pull_mode": "None" + }, + { + "name": "IOC0", + "alias_of": "bdbus4" + }, + { + "name": "IOC1", + "alias_of": "bdbus5" + }, + { + "name": "IOC2", + "alias_of": "bdbus6" + }, + { + "name": "MUX_FTDI", + "mode": "PushPull", + "alias_of": "bdbus7" + }, + { + "name": "IOC8", + "alias_of": "adbus6" + }, + { + "name": "IOC5", + "alias_of": "adbus7" + }, + { + "name": "SPI_DEV_CSB", + "alias_of": "bdbus3" + } + ], + "spi": [ + { + "name": "BOOTSTRAP" + } + ], + "uarts": [ + { + "name": "console", + "alias_of": "0" + }, + { + "name": "dut", + "alias_of": "3" + } + ], + "strappings": [ + { + "name": "ROM_BOOTSTRAP", + "pins": [ + { + "name": "SW_STRAP0", + "level": true + }, + { + "name": "SW_STRAP1", + "level": true + }, + { + "name": "SW_STRAP2", + "level": true + }, + { + "name": "MUX_FTDI", + "level": true + } + ] + } + ] +} diff --git a/sw/host/opentitanlib/src/backend/ftdi.rs b/sw/host/opentitanlib/src/backend/ftdi.rs new file mode 100644 index 0000000000000..8327ef546b24c --- /dev/null +++ b/sw/host/opentitanlib/src/backend/ftdi.rs @@ -0,0 +1,14 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; + +use crate::backend::BackendOpts; +use crate::transport::ftdi::chip::Chip; +use crate::transport::ftdi::Ftdi; +use crate::transport::Transport; + +pub fn create(_args: &BackendOpts) -> Result> { + Ok(Box::new(Ftdi::::new()?)) +} diff --git a/sw/host/opentitanlib/src/backend/mod.rs b/sw/host/opentitanlib/src/backend/mod.rs index d6f1dcf8e4eff..ba90380916dd7 100644 --- a/sw/host/opentitanlib/src/backend/mod.rs +++ b/sw/host/opentitanlib/src/backend/mod.rs @@ -11,6 +11,7 @@ use crate::app::config::process_config_file; use crate::app::{TransportWrapper, TransportWrapperBuilder}; use crate::transport::chip_whisperer::board::{Cw310, Cw340}; use crate::transport::dediprog::Dediprog; +use crate::transport::ftdi::chip::Ft4232hq; use crate::transport::hyperdebug::{ C2d2Flavor, ChipWhispererFlavor, ServoMicroFlavor, StandardFlavor, Ti50Flavor, }; @@ -18,6 +19,7 @@ use crate::transport::{EmptyTransport, Transport}; use crate::util::parse_int::ParseInt; mod chip_whisperer; +mod ftdi; mod hyperdebug; mod proxy; mod ti50emulator; @@ -131,6 +133,10 @@ pub fn create(args: &BackendOpts) -> Result { chip_whisperer::create::(args)?, Some(Path::new("/__builtin__/opentitan_cw340.json")), ), + "ftdi" => ( + ftdi::create::(args)?, + Some(Path::new("/__builtin__/opentitan_ftdi_voyager.json")), + ), "dediprog" => { let dediprog: Box = Box::new(Dediprog::new( args.usb_vid, diff --git a/sw/host/opentitanlib/src/transport/ftdi/chip.rs b/sw/host/opentitanlib/src/transport/ftdi/chip.rs new file mode 100644 index 0000000000000..24530653f6280 --- /dev/null +++ b/sw/host/opentitanlib/src/transport/ftdi/chip.rs @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +pub trait Chip { + const VENDOR_ID: u16 = 0x0403; + const PRODUCT_ID: u16; + + const UART_BAUD: u32 = 115200; + + const INTERFACES: &'static [ftdi::Interface]; +} + +pub struct Ft4232hq {} + +impl Chip for Ft4232hq { + const PRODUCT_ID: u16 = 0x6011; + const INTERFACES: &'static [ftdi::Interface] = &[ + ftdi::Interface::A, + ftdi::Interface::B, + // ftdi::Interface::C, + ftdi::Interface::D, + ]; +} diff --git a/sw/host/opentitanlib/src/transport/ftdi/mod.rs b/sw/host/opentitanlib/src/transport/ftdi/mod.rs new file mode 100644 index 0000000000000..5097d34ce2626 --- /dev/null +++ b/sw/host/opentitanlib/src/transport/ftdi/mod.rs @@ -0,0 +1,120 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use serde_annotate::Annotate; +use std::any::Any; +use std::cell::RefCell; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::rc::Rc; + +use crate::io::gpio::{GpioPin, PinMode}; +use crate::io::spi::Target; +use crate::io::uart::Uart; +use crate::io::uart::UartError; +use crate::transport::common::uart::SerialPortUart; +use crate::transport::{ + Capabilities, Capability, Transport, TransportError, TransportInterfaceType, +}; +use crate::util::parse_int::ParseInt; +use serialport::SerialPortType; + +use chip::Chip; +use ftdi_embedded_hal as ftdi_hal; + +pub mod chip; + +#[derive(Default)] +struct Inner { + spi: Option>, + gpio: HashMap>, + uart: HashMap>, +} + +pub struct Ftdi { + pub(crate) ftdi_interfaces: Rc>>, + inner: RefCell, + phantom: std::marker::PhantomData, +} + +impl Ftdi { + pub fn new() -> anyhow::Result { + let mut ftdi_interfaces = HashMap::new(); + for interface in C::INTERFACES { + let device = ftdi::find_by_vid_pid(C::VENDOR_ID, C::PRODUCT_ID) + .interface(*interface) + .open()?; + ftdi_interfaces.insert(*interface, ftdi_hal::FtHal::init_freq(device, 8_000_000)?); + } + + let ftdi_dev = Ftdi { + ftdi_interfaces: Rc::new(ftdi_interfaces), + inner: RefCell::default(), + phantom: std::marker::PhantomData, + }; + Ok(ftdi_dev) + } + + fn open_uart(&self, instance: u32) -> Result { + let mut ports = serialport::available_ports() + .map_err(|e| UartError::EnumerationError(e.to_string()))?; + + ports.retain(|port| { + if let SerialPortType::UsbPort(info) = &port.port_type { + if info.vid == C::VENDOR_ID && info.pid == C::PRODUCT_ID { + return true; + } + } + false + }); + + let port = ports.get(instance as usize).ok_or_else(|| { + TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string()) + })?; + + SerialPortUart::open(&port.port_name, C::UART_BAUD) + } +} + +impl Transport for Ftdi { + fn capabilities(&self) -> Result { + Ok(Capabilities::new( + Capability::SPI | Capability::GPIO | Capability::UART | Capability::UART_NONBLOCKING, + )) + } + + fn uart(&self, instance: &str) -> Result> { + let mut inner = self.inner.borrow_mut(); + let instance = u32::from_str(instance).ok().ok_or_else(|| { + TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string()) + })?; + let uart = match inner.uart.entry(instance) { + Entry::Vacant(v) => { + let u = v.insert(Rc::new(self.open_uart(instance)?)); + Rc::clone(u) + } + Entry::Occupied(o) => Rc::clone(o.get()), + }; + Ok(uart) + } + + fn gpio_pin(&self, pinname: &str) -> Result> { + Err(TransportError::UnsupportedOperation.into()) + } + + fn spi(&self, _instance: &str) -> Result> { + Err(TransportError::UnsupportedOperation.into()) + } + + fn dispatch(&self, _action: &dyn Any) -> Result>> { + Err(TransportError::UnsupportedOperation.into()) + } +} + +/// Command for Transport::dispatch(). +pub struct SetPll {} + +/// Command for Transport::dispatch(). Resets the Chip whisperer board's SAM3X chip. +pub struct ResetSam3x {} diff --git a/sw/host/opentitanlib/src/transport/mod.rs b/sw/host/opentitanlib/src/transport/mod.rs index db0162ae8b4b9..60abb239d3372 100644 --- a/sw/host/opentitanlib/src/transport/mod.rs +++ b/sw/host/opentitanlib/src/transport/mod.rs @@ -22,6 +22,7 @@ use crate::io::uart::Uart; pub mod chip_whisperer; pub mod common; pub mod dediprog; +pub mod ftdi; pub mod hyperdebug; pub mod ioexpander; pub mod proxy; From dcd643ba13ec84c32e84ec58a528fff0952cfbe6 Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Fri, 13 Dec 2024 15:30:26 +0000 Subject: [PATCH 3/4] [opentitanlib] Add gpio implementation for ftdi transport Signed-off-by: Douglas Reis --- sw/host/opentitanlib/BUILD | 1 + .../opentitanlib/src/transport/ftdi/gpio.rs | 140 ++++++++++++++++++ .../opentitanlib/src/transport/ftdi/mod.rs | 13 +- 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 sw/host/opentitanlib/src/transport/ftdi/gpio.rs diff --git a/sw/host/opentitanlib/BUILD b/sw/host/opentitanlib/BUILD index c3477f5f99863..d6be64331e48a 100644 --- a/sw/host/opentitanlib/BUILD +++ b/sw/host/opentitanlib/BUILD @@ -184,6 +184,7 @@ rust_library( "src/transport/dediprog/spi.rs", "src/transport/errors.rs", "src/transport/ftdi/chip.rs", + "src/transport/ftdi/gpio.rs", "src/transport/ftdi/mod.rs", "src/transport/hyperdebug/c2d2.rs", "src/transport/hyperdebug/dfu.rs", diff --git a/sw/host/opentitanlib/src/transport/ftdi/gpio.rs b/sw/host/opentitanlib/src/transport/ftdi/gpio.rs new file mode 100644 index 0000000000000..d3b7b5546b278 --- /dev/null +++ b/sw/host/opentitanlib/src/transport/ftdi/gpio.rs @@ -0,0 +1,140 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +use embedded_hal::digital::{InputPin, OutputPin}; +use ftdi_embedded_hal as ftdi_hal; + +use crate::io::gpio::{GpioError, GpioPin, PinMode, PullMode}; +use crate::transport::ftdi::Chip; + +#[derive(Default)] +enum PinType { + Output(ftdi_hal::OutputPin), + Input(ftdi_hal::InputPin), + #[default] + None, +} + +pub struct Pin { + pin: RefCell, + ftdi: Rc>>, + pinname: String, +} + +impl Pin { + pub fn open( + ftdi: &Rc>>, + pinname: String, + ) -> Result { + Ok(Self { + pin: RefCell::new(PinType::None), + ftdi: Rc::clone(ftdi), + pinname, + }) + } + + fn map_outpin(&self) -> Result> { + let pinname = self.pinname.to_lowercase(); + let interface = match &pinname[0..1] { + "a" => ftdi::Interface::A, + "b" => ftdi::Interface::B, + "c" => ftdi::Interface::C, + "d" => ftdi::Interface::D, + &_ => return Err(GpioError::InvalidPinName(pinname).into()), + }; + let hal = self + .ftdi + .get(&interface) + .ok_or_else(|| GpioError::InvalidPinName(pinname.clone()))?; + + let pin = match &pinname[1..] { + "dbus0" => hal.ad0(), + "dbus1" => hal.ad1(), + "dbus2" => hal.ad2(), + "dbus3" => hal.ad3(), + "dbus4" => hal.ad4(), + "dbus5" => hal.ad5(), + "dbus6" => hal.ad6(), + "dbus7" => hal.ad7(), + _ => return Err(GpioError::InvalidPinName(self.pinname.to_owned()).into()), + }?; + Ok(pin) + } + + fn map_inpin(&self) -> Result> { + let pinname = self.pinname.to_lowercase(); + let interface = match &pinname[0..1] { + "a" => ftdi::Interface::A, + "b" => ftdi::Interface::B, + "c" => ftdi::Interface::C, + "d" => ftdi::Interface::D, + &_ => return Err(GpioError::InvalidPinName(pinname).into()), + }; + let hal = self + .ftdi + .get(&interface) + .ok_or_else(|| GpioError::InvalidPinName(pinname.clone()))?; + + let pin = match &pinname[1..] { + "dbus0" => hal.adi0(), + "dbus1" => hal.adi1(), + "dbus2" => hal.adi2(), + "dbus3" => hal.adi3(), + "dbus4" => hal.adi4(), + "dbus5" => hal.adi5(), + "dbus6" => hal.adi6(), + "dbus7" => hal.adi7(), + _ => return Err(GpioError::InvalidPinName(self.pinname.to_owned()).into()), + }?; + Ok(pin) + } +} + +impl GpioPin for Pin { + fn read(&self) -> Result { + let mut pin = self.pin.borrow_mut(); + match *pin { + PinType::Input(ref mut pin) => Ok(pin.is_high()?), + _ => Err(GpioError::InvalidPinMode(0).into()), + } + } + + fn write(&self, value: bool) -> Result<()> { + let mut pin = self.pin.borrow_mut(); + if let PinType::Output(ref mut pin) = *pin { + if value { + pin.set_high()?; + } else { + pin.set_low()?; + } + return Ok(()); + }; + Err(GpioError::InvalidPinMode(0).into()) + } + + fn set_mode(&self, mode: PinMode) -> Result<()> { + let mut pin = self.pin.borrow_mut(); + *pin = match (&*pin, mode) { + (PinType::None, PinMode::Input) => PinType::Input(self.map_inpin()?), + (PinType::None, PinMode::PushPull | PinMode::OpenDrain) => { + PinType::Output(self.map_outpin()?) + } + _ => return Ok(()), + }; + Ok(()) + } + + fn set_pull_mode(&self, _mode: PullMode) -> Result<()> { + Ok(()) + } + + fn get_internal_pin_name(&self) -> Option<&str> { + Some(&self.pinname) + } +} diff --git a/sw/host/opentitanlib/src/transport/ftdi/mod.rs b/sw/host/opentitanlib/src/transport/ftdi/mod.rs index 5097d34ce2626..3b881f80a7cba 100644 --- a/sw/host/opentitanlib/src/transport/ftdi/mod.rs +++ b/sw/host/opentitanlib/src/transport/ftdi/mod.rs @@ -25,6 +25,7 @@ use chip::Chip; use ftdi_embedded_hal as ftdi_hal; pub mod chip; +pub mod gpio; #[derive(Default)] struct Inner { @@ -101,7 +102,17 @@ impl Transport for Ftdi { } fn gpio_pin(&self, pinname: &str) -> Result> { - Err(TransportError::UnsupportedOperation.into()) + let mut inner = self.inner.borrow_mut(); + Ok(match inner.gpio.entry(pinname.to_string()) { + Entry::Vacant(v) => { + let u = v.insert(Rc::new(gpio::Pin::open::( + &self.ftdi_interfaces, + pinname.to_string(), + )?)); + Rc::clone(u) + } + Entry::Occupied(o) => Rc::clone(o.get()), + }) } fn spi(&self, _instance: &str) -> Result> { From 46f5fc67bbee8a92efef9ed38ead4eaa074fd91a Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Fri, 13 Dec 2024 15:31:45 +0000 Subject: [PATCH 4/4] [opentitanlib] Add spi implementation for ftdi transport Signed-off-by: Douglas Reis --- sw/host/opentitanlib/BUILD | 1 + .../opentitanlib/src/transport/ftdi/mod.rs | 9 +- .../opentitanlib/src/transport/ftdi/spi.rs | 136 ++++++++++++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 sw/host/opentitanlib/src/transport/ftdi/spi.rs diff --git a/sw/host/opentitanlib/BUILD b/sw/host/opentitanlib/BUILD index d6be64331e48a..d3626feb4477b 100644 --- a/sw/host/opentitanlib/BUILD +++ b/sw/host/opentitanlib/BUILD @@ -186,6 +186,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 3b881f80a7cba..91025b796dbd7 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,13 @@ impl Transport for Ftdi { } fn spi(&self, _instance: &str) -> Result> { - Err(TransportError::UnsupportedOperation.into()) + let spi_cs = gpio::Pin::open::(&self.ftdi_interfaces, "bdbus3".to_string())?; + spi_cs.set_mode(PinMode::PushPull)?; + let mut inner = self.inner.borrow_mut(); + if inner.spi.is_none() { + inner.spi = Some(Rc::new(spi::Spi::open(&self.ftdi_interfaces, spi_cs)?)); + } + 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..caa0db7aef7ea --- /dev/null +++ b/sw/host/opentitanlib/src/transport/ftdi/spi.rs @@ -0,0 +1,136 @@ +// 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: &Rc>>, + 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 { + 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()) + } +}