Add bitstream loading to hyperdebug
The hyperdebug board is not capable of loading a bitstream into the
CW310 board. To accomplish bitstream loading, we add a `CW310Flavor`
to the hyperdebug backend. When using the CW310Flavor, we assume
that both the Hyperdebug and CW310 USB interfaces are attached to the
host. The hyperdebug `load-bitstream` operation is delegated to the
CW310 connection. All other IO (uarts, spi, etc) use the hyperdebug
connection.
Signed-off-by: Chris Frantz <cfrantz@google.com>
diff --git a/sw/host/opentitanlib/src/backend/mod.rs b/sw/host/opentitanlib/src/backend/mod.rs
index 3b2c2c5..2bf1811 100644
--- a/sw/host/opentitanlib/src/backend/mod.rs
+++ b/sw/host/opentitanlib/src/backend/mod.rs
@@ -10,7 +10,7 @@
use crate::app::config::process_config_file;
use crate::app::{TransportWrapper, TransportWrapperBuilder};
use crate::transport::hyperdebug::c2d2::C2d2Flavor;
-use crate::transport::hyperdebug::StandardFlavor;
+use crate::transport::hyperdebug::CW310Flavor;
use crate::transport::{EmptyTransport, Transport};
use crate::util::parse_int::ParseInt;
@@ -76,7 +76,7 @@
Some(Path::new("/__builtin__/opentitan_ultradebug.json")),
),
"hyperdebug" => (
- hyperdebug::create::<StandardFlavor>(args)?,
+ hyperdebug::create::<CW310Flavor>(args)?,
Some(Path::new("/__builtin__/hyperdebug_cw310.json")),
),
"hyperdebug_dfu" => (hyperdebug::create_dfu(args)?, None),
diff --git a/sw/host/opentitanlib/src/transport/cw310/mod.rs b/sw/host/opentitanlib/src/transport/cw310/mod.rs
index 1f87b6e..7b3981b 100644
--- a/sw/host/opentitanlib/src/transport/cw310/mod.rs
+++ b/sw/host/opentitanlib/src/transport/cw310/mod.rs
@@ -33,7 +33,7 @@
}
pub struct CW310 {
- device: Rc<RefCell<usb::Backend>>,
+ pub(crate) device: Rc<RefCell<usb::Backend>>,
uart_override: Vec<String>,
inner: RefCell<Inner>,
}
diff --git a/sw/host/opentitanlib/src/transport/hyperdebug/mod.rs b/sw/host/opentitanlib/src/transport/hyperdebug/mod.rs
index a0adf2f..524ecc2 100644
--- a/sw/host/opentitanlib/src/transport/hyperdebug/mod.rs
+++ b/sw/host/opentitanlib/src/transport/hyperdebug/mod.rs
@@ -25,7 +25,9 @@
use crate::io::i2c::Bus;
use crate::io::spi::Target;
use crate::io::uart::Uart;
+use crate::transport::common::fpga::{ClearBitstream, FpgaProgram};
use crate::transport::common::uart::{flock_serial, SerialPortExclusiveLock, SerialPortUart};
+use crate::transport::cw310::CW310;
use crate::transport::{
Capabilities, Capability, Transport, TransportError, TransportInterfaceType, UpdateFirmware,
};
@@ -56,6 +58,12 @@
fn gpio_pin(inner: &Rc<Inner>, pinname: &str) -> Result<Rc<dyn GpioPin>>;
fn get_default_usb_vid() -> u16;
fn get_default_usb_pid() -> u16;
+ fn load_bitstream(_transport: &impl Transport, _fpga_program: &FpgaProgram) -> Result<()> {
+ Err(TransportError::UnsupportedOperation.into())
+ }
+ fn clear_bitstream(_clear: &ClearBitstream) -> Result<()> {
+ Err(TransportError::UnsupportedOperation.into())
+ }
}
pub const VID_GOOGLE: u16 = 0x18d1;
@@ -578,13 +586,18 @@
&update_firmware_action.firmware,
&update_firmware_action.progress,
)
+ } else if let Some(fpga_program) = action.downcast_ref::<FpgaProgram>() {
+ T::load_bitstream(self, fpga_program).map(|_| None)
+ } else if let Some(clear) = action.downcast_ref::<ClearBitstream>() {
+ T::clear_bitstream(clear).map(|_| None)
} else {
Err(TransportError::UnsupportedOperation.into())
}
}
}
-pub struct StandardFlavor {}
+/// A `StandardFlavor` is a plain Hyperdebug board.
+pub struct StandardFlavor;
impl Flavor for StandardFlavor {
fn gpio_pin(inner: &Rc<Inner>, pinname: &str) -> Result<Rc<dyn GpioPin>> {
@@ -598,6 +611,61 @@
}
}
+/// A `CW310Flavor` is a Hyperdebug attached to a CW310 board. Furthermore,
+/// both the Hyperdebug and CW310 USB interfaces are attached to the host.
+/// Hyperdebug is used for all IO with the CW310 board except for bitstream
+/// programming.
+pub struct CW310Flavor;
+
+impl Flavor for CW310Flavor {
+ fn gpio_pin(inner: &Rc<Inner>, pinname: &str) -> Result<Rc<dyn GpioPin>> {
+ StandardFlavor::gpio_pin(inner, pinname)
+ }
+ fn get_default_usb_vid() -> u16 {
+ StandardFlavor::get_default_usb_vid()
+ }
+ fn get_default_usb_pid() -> u16 {
+ StandardFlavor::get_default_usb_pid()
+ }
+ fn load_bitstream(transport: &impl Transport, fpga_program: &FpgaProgram) -> Result<()> {
+ if fpga_program.skip() {
+ log::info!("Skip loading the __skip__ bitstream.");
+ return Ok(());
+ }
+
+ // First, try to establish a connection to the native CW310 interface
+ // which we will use for bitstream loading.
+ let cw310 = CW310::new(None, None, None, &[])?;
+
+ // The transport does not provide name resolution for the IO interface
+ // names, so: console=UART2 and RESET=CN10_29 on the Hyp+CW310.
+ // Open the console UART. We do this first so we get the receiver
+ // started and the uart buffering data for us.
+ let uart = transport.uart("UART2")?;
+ let reset_pin = transport.gpio_pin("CN10_29")?;
+ if fpga_program.check_correct_version(&*uart, &*reset_pin)? {
+ return Ok(());
+ }
+
+ // Program the FPGA bitstream.
+ log::info!("Programming the FPGA bitstream.");
+ let usb = cw310.device.borrow();
+ usb.spi1_enable(false)?;
+ usb.fpga_program(
+ &fpga_program.bitstream,
+ fpga_program.progress.as_ref().map(Box::as_ref),
+ )?;
+ Ok(())
+ }
+ fn clear_bitstream(_clear: &ClearBitstream) -> Result<()> {
+ let cw310 = CW310::new(None, None, None, &[])?;
+ let usb = cw310.device.borrow();
+ usb.spi1_enable(false)?;
+ usb.clear_bitstream()?;
+ Ok(())
+ }
+}
+
lazy_static! {
pub static ref SPI_REGEX: Regex = Regex::new("^ +([0-9]+) ([^ ]+) ([0-9]+)").unwrap();
}