Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions hil/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ probe-rs = "0.27.0"
rand = "0.8"
reqwest = { workspace = true, default-features = false, features = ["rustls-tls"] }
secrecy.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
tempfile = "3"
thiserror.workspace = true
Expand Down
34 changes: 34 additions & 0 deletions hil/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,37 @@ ors-os artifacts from S3 and pass that as an env var. See [here][aws cli config]
info.

[aws cli config]: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html

## Debug board configuration

With debug board v1.1 comes an embedded EEPROM that can be programmed to set a configuration
to the FTDI chip, such as the serial number.
This serial number enables selection among a few different ones connected to the host.

Use the `ftdi` command to read & write the config:

```sh
# write
orb-hil ftdi write ftdi_config.json

# read
orb-hil ftdi read [--file ftdi_config.json]
```

Here is an example of an FTDI configuration (please set a working serial, like the id of the orb it's connected to):

```json
{
"vendor_id": 1027,
"product_id": 24593,
"serial_number_enable": true,
"max_current_ma": 500,
"self_powered": false,
"remote_wakeup": false,
"pull_down_enable": true,
"manufacturer": "FTDI",
"manufacturer_id": "FT",
"description": "FT4232H",
"serial_number": "YOUR_SERIAL_HERE"
}
```
Comment thread
fouge marked this conversation as resolved.
25 changes: 19 additions & 6 deletions hil/src/boot.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::time::Duration;

use crate::ftdi::{FtdiGpio, FtdiId, OutputState};
use crate::ftdi::{FtdiChannel, FtdiGpio, FtdiId, OutputState};
use color_eyre::{eyre::WrapErr as _, Result};
use tracing::{debug, info};

pub const BUTTON_PIN: crate::ftdi::Pin = FtdiGpio::CTS_PIN;
pub const RECOVERY_PIN: crate::ftdi::Pin = FtdiGpio::RTS_PIN;
pub const BUTTON_PIN: crate::ftdi::Pin = FtdiGpio::DTR_PIN;
pub const RECOVERY_PIN: crate::ftdi::Pin = FtdiGpio::CTS_PIN;
pub const NVIDIA_VENDOR_ID: u16 = 0x0955;
pub const NVIDIA_USB_ETHERNET: u16 = 0x7035;

Expand All @@ -20,15 +20,28 @@ pub async fn is_recovery_mode_detected() -> Result<bool> {
Ok(num_nvidia_devices > 0)
}

/// If `device` is `None`, will get the first available device.
/// The default channel used for button/recovery control on the HIL.
pub const DEFAULT_CHANNEL: FtdiChannel = FtdiChannel::C;

/// If `device` is `None`, will get the first available device, or fall back to
/// the default channel (FT4232H C) if multiple devices are found.
#[tracing::instrument]
pub async fn reboot(recovery: bool, device: Option<&FtdiId>) -> Result<()> {
fn make_ftdi(device: Option<FtdiId>) -> Result<FtdiGpio> {
let builder = FtdiGpio::builder();
let builder = match &device {
Some(FtdiId::Description(desc)) => builder.with_description(desc),
Some(FtdiId::SerialNumber(serial)) => builder.with_serial_number(serial),
None => builder.with_default_device(),
Some(FtdiId::FtdiSerial(serial)) => builder.with_ftdi_serial(serial),
None => match builder.with_default_device() {
Ok(b) => return b.configure().wrap_err("failed to configure ftdi"),
Err(_) => {
// Fall back to default channel when multiple devices exist
return FtdiGpio::builder()
.with_description(DEFAULT_CHANNEL.description_suffix())
.and_then(|b| b.configure())
.wrap_err("failed to create ftdi device with default channel");
}
},
Comment thread
fouge marked this conversation as resolved.
};
builder
.and_then(|b| b.configure())
Expand Down
97 changes: 86 additions & 11 deletions hil/src/commands/button_ctrl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,112 @@ use clap::Parser;
use color_eyre::{eyre::WrapErr as _, Result};
use humantime::parse_duration;
use std::time::Duration;
use tracing::info;
use tracing::{debug, info};

use crate::boot::BUTTON_PIN;
use crate::ftdi::{FtdiGpio, OutputState};
use crate::boot::{BUTTON_PIN, DEFAULT_CHANNEL};
use crate::ftdi::{FtdiChannel, FtdiGpio, OutputState};

/// Control the orb button over the debug board
#[derive(Debug, Parser)]
pub struct ButtonCtrl {
///Button press duration (e.g., "1s", "500ms")
/// Button press duration (e.g., "1s", "500ms")
#[arg(long, default_value = "1s", value_parser = parse_duration)]
press_duration: Duration,

/// The USB serial number of the FTDI chip (e.g., "FT7ABC12").
/// Will use the default channel (C) for this chip.
#[arg(long, conflicts_with_all = ["ftdi_serial", "desc"])]
usb_serial: Option<String>,

/// The FTDI serial number including channel (e.g., "FT7ABC12C").
/// This is the USB serial + channel letter (A/B/C/D).
#[arg(long, conflicts_with_all = ["usb_serial", "desc"])]
ftdi_serial: Option<String>,

/// The FTDI description (e.g., "FT4232H C").
#[arg(long, conflicts_with_all = ["usb_serial", "ftdi_serial"])]
desc: Option<String>,

/// The channel to use when --usb-serial is provided (A, B, C, or D).
/// Defaults to C.
#[arg(long, default_value = "C", requires = "usb_serial")]
channel: FtdiChannel,
}

impl ButtonCtrl {
pub async fn run(self) -> Result<()> {
fn make_ftdi() -> Result<FtdiGpio> {
FtdiGpio::builder()
.with_default_device()
.and_then(|b| b.configure())
.wrap_err("failed to create ftdi device")
}
let usb_serial = self.usb_serial.clone();
let ftdi_serial = self.ftdi_serial.clone();
let desc = self.desc.clone();
let channel = self.channel;

let make_ftdi = move || -> Result<FtdiGpio> {
let builder = FtdiGpio::builder();
match (usb_serial.as_ref(), ftdi_serial.as_ref(), desc.as_ref()) {
(Some(usb_serial), None, None) => {
debug!(
"using FTDI device with USB serial: {usb_serial}, channel: {:?}",
channel
);
builder
.with_usb_serial(usb_serial, channel)
.and_then(|b| b.configure())
.wrap_err("failed to create ftdi device with USB serial")
}
(None, Some(ftdi_serial), None) => {
debug!("using FTDI device with FTDI serial: {ftdi_serial}");
builder
.with_ftdi_serial(ftdi_serial)
.and_then(|b| b.configure())
.wrap_err("failed to create ftdi device with FTDI serial")
}
(None, None, Some(desc)) => {
debug!("using FTDI device with description: {desc}");
builder
.with_description(desc)
.and_then(|b| b.configure())
.wrap_err("failed to create ftdi device with description")
}
(None, None, None) => {
// Try default device first, fall back to default channel description
match builder.with_default_device() {
Ok(b) => {
b.configure().wrap_err("failed to configure ftdi device")
}
Err(e) => {
debug!("default device selection failed: {e}");
let desc_suffix = DEFAULT_CHANNEL.description_suffix();
debug!(
"attempting to find device with description '{desc_suffix}'"
);
FtdiGpio::builder()
.with_description(desc_suffix)
.and_then(|b| b.configure())
.wrap_err_with(|| {
format!(
"failed to open FTDI device with description \
'{desc_suffix}'"
)
})
}
}
}
_ => unreachable!(),
}
};

info!(
"Holding button for {} seconds",
self.press_duration.as_secs_f32()
);
let press_duration = self.press_duration;
tokio::task::spawn_blocking(move || -> Result<(), color_eyre::Report> {
let mut ftdi = make_ftdi()?;
ftdi.set_pin(BUTTON_PIN, OutputState::Low)?;
std::thread::sleep(self.press_duration);
std::thread::sleep(press_duration);
ftdi.set_pin(BUTTON_PIN, OutputState::High)?;
ftdi.destroy().wrap_err("failed to destroy ftdi")?;

Ok(())
})
.await
Expand Down
Loading
Loading