| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| use anyhow::{ensure, Result}; |
| use serde_annotate::Annotate; |
| use serialport::SerialPortType; |
| 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; |
| use crate::io::spi::Target; |
| use crate::io::uart::{Uart, UartError}; |
| use crate::transport::common::fpga::{ClearBitstream, FpgaProgram}; |
| use crate::transport::common::uart::SerialPortUart; |
| use crate::transport::{ |
| Capabilities, Capability, Transport, TransportError, TransportInterfaceType, |
| }; |
| use crate::util::parse_int::ParseInt; |
| |
| pub mod gpio; |
| pub mod spi; |
| pub mod usb; |
| |
| #[derive(Default)] |
| struct Inner { |
| spi: Option<Rc<dyn Target>>, |
| gpio: HashMap<String, Rc<dyn GpioPin>>, |
| uart: HashMap<u32, Rc<dyn Uart>>, |
| } |
| |
| pub struct CW310 { |
| pub(crate) device: Rc<RefCell<usb::Backend>>, |
| uart_override: Vec<String>, |
| inner: RefCell<Inner>, |
| } |
| |
| impl CW310 { |
| // Pins needed for SPI on the CW310 board. |
| const PIN_CLK: &'static str = "USB_SPI_SCK"; |
| const PIN_SDI: &'static str = "USB_SPI_COPI"; |
| const PIN_SDO: &'static str = "USB_SPI_CIPO"; |
| const PIN_CS: &'static str = "USB_SPI_CS"; |
| // Pins needed for reset & bootstrap on the CW310 board. |
| const PIN_TRST: &'static str = "USB_A13"; |
| const PIN_SRST: &'static str = "USB_A14"; |
| const PIN_SW_STRAP0: &'static str = "USB_A15"; |
| const PIN_SW_STRAP1: &'static str = "USB_A16"; |
| const PIN_SW_STRAP2: &'static str = "USB_A17"; |
| const PIN_TAP_STRAP0: &'static str = "USB_A18"; |
| const PIN_TAP_STRAP1: &'static str = "USB_A19"; |
| |
| pub fn new( |
| usb_vid: Option<u16>, |
| usb_pid: Option<u16>, |
| usb_serial: Option<&str>, |
| uart_override: &[&str], |
| ) -> anyhow::Result<Self> { |
| let board = CW310 { |
| device: Rc::new(RefCell::new(usb::Backend::new( |
| usb_vid, usb_pid, usb_serial, |
| )?)), |
| uart_override: uart_override.iter().map(|s| s.to_string()).collect(), |
| inner: RefCell::default(), |
| }; |
| board.init_pin_directions()?; |
| board.init_pin_values()?; |
| Ok(board) |
| } |
| |
| // Initialize the IO direction of some basic pins on the board. |
| fn init_pin_directions(&self) -> anyhow::Result<()> { |
| let device = self.device.borrow(); |
| device.pin_set_output(Self::PIN_TRST, true)?; |
| device.pin_set_output(Self::PIN_SRST, true)?; |
| device.pin_set_output(Self::PIN_TAP_STRAP0, true)?; |
| device.pin_set_output(Self::PIN_TAP_STRAP1, true)?; |
| device.pin_set_output(Self::PIN_SW_STRAP0, true)?; |
| device.pin_set_output(Self::PIN_SW_STRAP1, true)?; |
| device.pin_set_output(Self::PIN_SW_STRAP2, true)?; |
| Ok(()) |
| } |
| |
| // Initialize the values of the output pins on the board. |
| fn init_pin_values(&self) -> anyhow::Result<()> { |
| let device = self.device.borrow(); |
| device.pin_set_state(Self::PIN_TRST, true)?; |
| device.pin_set_state(Self::PIN_SRST, true)?; |
| device.pin_set_state(Self::PIN_TAP_STRAP0, false)?; |
| device.pin_set_state(Self::PIN_TAP_STRAP1, true)?; |
| device.pin_set_state(Self::PIN_SW_STRAP0, false)?; |
| device.pin_set_state(Self::PIN_SW_STRAP1, false)?; |
| device.pin_set_state(Self::PIN_SW_STRAP2, false)?; |
| Ok(()) |
| } |
| |
| fn open_uart(&self, instance: u32) -> Result<SerialPortUart> { |
| if self.uart_override.is_empty() { |
| let usb = self.device.borrow(); |
| let serial_number = usb.get_serial_number(); |
| |
| 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.serial_number.as_deref() == Some(serial_number) { |
| return true; |
| } |
| } |
| false |
| }); |
| // The CW board seems to have the last port connected as OpenTitan UART 0. |
| // Reverse the sort order so the last port will be instance 0. |
| ports.sort_by(|a, b| b.port_name.cmp(&a.port_name)); |
| |
| let port = ports.get(instance as usize).ok_or_else(|| { |
| TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string()) |
| })?; |
| SerialPortUart::open(&port.port_name) |
| } else { |
| let instance = instance as usize; |
| ensure!( |
| instance < self.uart_override.len(), |
| TransportError::InvalidInstance(TransportInterfaceType::Uart, instance.to_string()) |
| ); |
| SerialPortUart::open(&self.uart_override[instance]) |
| } |
| } |
| } |
| |
| impl Transport for CW310 { |
| fn capabilities(&self) -> Result<Capabilities> { |
| Ok(Capabilities::new( |
| Capability::SPI | Capability::GPIO | Capability::UART, |
| )) |
| } |
| |
| fn uart(&self, instance: &str) -> Result<Rc<dyn Uart>> { |
| 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<Rc<dyn GpioPin>> { |
| 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::CW310GpioPin::open( |
| Rc::clone(&self.device), |
| pinname.to_string(), |
| )?)); |
| Rc::clone(u) |
| } |
| Entry::Occupied(o) => Rc::clone(o.get()), |
| }) |
| } |
| |
| fn spi(&self, instance: &str) -> Result<Rc<dyn Target>> { |
| ensure!( |
| instance == "0", |
| TransportError::InvalidInstance(TransportInterfaceType::Spi, instance.to_string()) |
| ); |
| let mut inner = self.inner.borrow_mut(); |
| if inner.spi.is_none() { |
| inner.spi = Some(Rc::new(spi::CW310Spi::open(Rc::clone(&self.device))?)); |
| } |
| Ok(Rc::clone(inner.spi.as_ref().unwrap())) |
| } |
| |
| fn dispatch(&self, action: &dyn Any) -> Result<Option<Box<dyn Annotate>>> { |
| if let Some(fpga_program) = action.downcast_ref::<FpgaProgram>() { |
| // Open the console UART. We do this first so we get the receiver |
| // started and the uart buffering data for us. |
| let uart = self.uart("0")?; |
| let reset_pin = self.gpio_pin(Self::PIN_SRST)?; |
| if fpga_program.skip() { |
| log::info!("Skip loading the __skip__ bitstream."); |
| return Ok(None); |
| } |
| if fpga_program.check_correct_version(&*uart, &*reset_pin)? { |
| return Ok(None); |
| } |
| |
| // Program the FPGA bitstream. |
| log::info!("Programming the FPGA bitstream."); |
| let usb = self.device.borrow(); |
| usb.spi1_enable(false)?; |
| usb.fpga_program( |
| &fpga_program.bitstream, |
| fpga_program.progress.as_ref().map(Box::as_ref), |
| )?; |
| Ok(None) |
| } else if action.downcast_ref::<ResetSam3x>().is_some() { |
| self.device.borrow().reset_sam3x()?; |
| Ok(None) |
| } else if action.downcast_ref::<SetPll>().is_some() { |
| const TARGET_FREQ: u32 = 100_000_000; |
| let usb = self.device.borrow(); |
| usb.pll_enable(true)?; |
| usb.pll_out_freq_set(1, TARGET_FREQ)?; |
| usb.pll_out_freq_set(2, TARGET_FREQ)?; |
| usb.pll_out_enable(0, false)?; |
| usb.pll_out_enable(1, true)?; |
| usb.pll_out_enable(2, false)?; |
| usb.pll_write_defaults()?; |
| Ok(None) |
| } else if action.downcast_ref::<ClearBitstream>().is_some() { |
| let usb = self.device.borrow(); |
| usb.spi1_enable(false)?; |
| usb.clear_bitstream()?; |
| Ok(None) |
| } else { |
| Err(TransportError::UnsupportedOperation.into()) |
| } |
| } |
| } |
| |
| /// Command for Transport::dispatch(). |
| pub struct SetPll {} |
| |
| /// Command for Transport::dispatch(). Resets the CW310's SAM3X chip. |
| pub struct ResetSam3x {} |