Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry-pick to earlgrey_es_sival: [opentitantool] Better SPI error messages from HyperDebug #21368

Merged
merged 1 commit into from
Feb 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 59 additions & 8 deletions sw/host/opentitanlib/src/transport/hyperdebug/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rusb::{Direction, Recipient, RequestType};
use std::cell::Cell;
use std::mem::size_of;
use std::rc::Rc;
use std::time::Duration;
use zerocopy::{AsBytes, FromBytes, FromZeroes};

use crate::io::eeprom;
Expand All @@ -27,6 +28,12 @@ pub struct HyperdebugSpiTarget {
cs_asserted_count: Cell<u32>,
}

/// HyperDebug will wait up to one second for EEPROM chip to report "ready" after a write
/// operation, sending a response if it gives up. Make sure that we leave a little time for USB
/// latency, so that we will receive the "giving up" response, rather than timeout ourselves in
/// opentitanlib.
const TRANSFER_START_TIMEOUT: Duration = Duration::from_millis(1100);

const USB_SPI_PKT_ID_CMD_GET_USB_SPI_CONFIG: u16 = 0;
const USB_SPI_PKT_ID_RSP_USB_SPI_CONFIG: u16 = 1;
const USB_SPI_PKT_ID_CMD_TRANSFER_START: u16 = 2;
Expand Down Expand Up @@ -71,6 +78,39 @@ const EEPROM_FLAGS_POLL_BUSY: u32 = 0x20000000;
const EEPROM_FLAGS_DOUBLE_BUFFER: u32 = 0x40000000;
const EEPROM_FLAGS_WRITE: u32 = 0x80000000;

const STATUS_SUCCESS: u16 = 0x0000;
const STATUS_TIMEOUT: u16 = 0x0001;
const STATUS_BUSY: u16 = 0x0002;
const STATUS_WRITE_COUNT_INVALID: u16 = 0x0003;
const STATUS_READ_COUNT_INVALID: u16 = 0x0004;
const STATUS_DISABLED: u16 = 0x0005;
const STATUS_RX_BAD_DATA_INDEX: u16 = 0x0006;
const STATUS_RX_DATA_OVERFLOW: u16 = 0x0007;
const STATUS_RX_UNEXPECTED_PACKET: u16 = 0x0008;
const STATUS_UNSUPPORTED_FULL_DUPLEX: u16 = 0x0009;
const STATUS_UNSUPPORTED_FLASH_MODE: u16 = 0x000A;
const STATUS_STREAMING_FIRST_SUCCESS: u16 = 0x000B;

fn status_code_description(status_code: u16) -> String {
match status_code {
STATUS_SUCCESS => "success",
STATUS_TIMEOUT => "timeout",
STATUS_BUSY => "busy",
STATUS_WRITE_COUNT_INVALID => "protocol corruption (WRITE_COUNT_INVALID)",
STATUS_READ_COUNT_INVALID => "protocol corruption (READ_COUNT_INVALID)",
STATUS_DISABLED => "port not enabled",
STATUS_RX_BAD_DATA_INDEX => "protocol corruption (RX_BAD_DATA_INDEX)",
STATUS_RX_DATA_OVERFLOW => "protocol corruption (RX_DATA_OVERFLOW)",
STATUS_RX_UNEXPECTED_PACKET => "protocol corruption (RX_UNEXPECTED_PACKET)",
STATUS_UNSUPPORTED_FULL_DUPLEX => "full duplex not supported",
STATUS_UNSUPPORTED_FLASH_MODE => "requested flash mode not supported",
_ => {
return format!("unknown error {:04x}", status_code);
}
}
.to_string()
}

#[derive(AsBytes, FromBytes, FromZeroes, Debug, Default)]
#[repr(C)]
struct RspUsbSpiConfig {
Expand Down Expand Up @@ -300,7 +340,7 @@ impl HyperdebugSpiTarget {
/// Receive data for a single SPI operation, using one or more USB packets.
fn receive(&self, rbuf: &mut [u8]) -> Result<()> {
let mut resp = RspTransferStart::new();
let bytecount = self.usb_read_bulk(resp.as_bytes_mut())?;
let bytecount = self.usb_read_bulk_timeout(resp.as_bytes_mut(), TRANSFER_START_TIMEOUT)?;
ensure!(
bytecount >= 4,
TransportError::CommunicationError("Short reponse to TRANSFER_START".to_string())
Expand All @@ -313,8 +353,11 @@ impl HyperdebugSpiTarget {
))
);
ensure!(
resp.status_code == 0,
TransportError::CommunicationError(format!("SPI error ({})", resp.status_code))
resp.status_code == STATUS_SUCCESS,
TransportError::CommunicationError(format!(
"SPI error: {}",
status_code_description(resp.status_code)
))
);
let databytes = bytecount - 4;
rbuf[0..databytes].clone_from_slice(&resp.data[0..databytes]);
Expand Down Expand Up @@ -363,10 +406,10 @@ impl HyperdebugSpiTarget {
))
);
ensure!(
resp.status_code == 0x0B,
resp.status_code == STATUS_STREAMING_FIRST_SUCCESS,
TransportError::CommunicationError(format!(
"SPI error ({}), expected streaming response",
resp.status_code
"SPI error: {}, expected streaming response",
status_code_description(resp.status_code)
))
);
Ok(())
Expand Down Expand Up @@ -573,8 +616,8 @@ impl HyperdebugSpiTarget {
TransportError::CommunicationError("Unrecognized reponse to CHIP_SELECT".to_string())
);
ensure!(
resp.status_code == 0,
TransportError::CommunicationError("SPI error".to_string())
resp.status_code == STATUS_SUCCESS,
TransportError::CommunicationError(format!("SPI error: {}", resp.status_code))
);
Ok(())
}
Expand All @@ -595,6 +638,14 @@ impl HyperdebugSpiTarget {
.borrow()
.read_bulk(self.interface.in_endpoint, buf)
}

/// Receive one USB packet, with particular timeout.
fn usb_read_bulk_timeout(&self, buf: &mut [u8], timeout: Duration) -> Result<usize> {
self.inner
.usb_device
.borrow()
.read_bulk_timeout(self.interface.in_endpoint, buf, timeout)
}
}

impl Target for HyperdebugSpiTarget {
Expand Down
Loading