blob: 92a5670bab280b0c89e12d2273e319873ad44ddd [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use anyhow::Result;
use nix::unistd::isatty;
use raw_tty::TtyModeGuard;
use serde_annotate::Annotate;
use std::any::Any;
use std::borrow::Borrow;
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::rc::Rc;
use std::time::Duration;
use structopt::StructOpt;
use opentitanlib::app::command::CommandDispatch;
use opentitanlib::app::TransportWrapper;
use opentitanlib::io::gpio::{ClockNature, Edge, GpioPin, PinMode, PullMode};
use opentitanlib::transport::Capability;
use opentitanlib::util::file;
use opentitanlib::util::voltage::Voltage;
#[derive(Debug, StructOpt)]
/// Reads a GPIO pin.
pub struct GpioRead {
#[structopt(name = "PIN", help = "The GPIO pin to read")]
pub pin: String,
}
#[derive(serde::Serialize)]
pub struct GpioReadResult {
pub pin: String,
pub value: bool,
}
impl CommandDispatch for GpioRead {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
let gpio_pin = transport.gpio_pin(&self.pin)?;
let value = gpio_pin.read()?;
Ok(Some(Box::new(GpioReadResult {
pin: self.pin.clone(),
value,
})))
}
}
#[derive(Debug, StructOpt)]
/// Writes a GPIO pin.
pub struct GpioWrite {
#[structopt(name = "PIN", help = "The GPIO pin to write")]
pub pin: String,
#[structopt(
name = "VALUE",
parse(try_from_str),
help = "The value to write to the pin"
)]
pub value: bool,
}
impl CommandDispatch for GpioWrite {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
let gpio_pin = transport.gpio_pin(&self.pin)?;
gpio_pin.write(self.value)?;
Ok(None)
}
}
#[derive(Debug, StructOpt)]
/// Set the I/O mode of a GPIO pin (Input/OpenDrain/PushPull).
pub struct GpioSetMode {
#[structopt(name = "PIN", help = "The GPIO pin to modify")]
pub pin: String,
#[structopt(
name = "MODE",
possible_values = &PinMode::variants(),
case_insensitive=true,
help = "The I/O mode of the pin"
)]
pub mode: PinMode,
}
impl CommandDispatch for GpioSetMode {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
let gpio_pin = transport.gpio_pin(&self.pin)?;
gpio_pin.set_mode(self.mode)?;
Ok(None)
}
}
#[derive(Debug, StructOpt)]
/// Set the I/O weak pull mode of a GPIO pin (PullUp/PullDown/None).
pub struct GpioSetPullMode {
#[structopt(name = "PIN", help = "The GPIO pin to modify")]
pub pin: String,
#[structopt(
name = "PULLMODE",
possible_values = &PullMode::variants(),
case_insensitive=true,
help = "The weak pull mode of the pin"
)]
pub pull_mode: PullMode,
}
impl CommandDispatch for GpioSetPullMode {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
let gpio_pin = transport.gpio_pin(&self.pin)?;
gpio_pin.set_pull_mode(self.pull_mode)?;
Ok(None)
}
}
#[derive(Debug, StructOpt)]
/// Simultaneously set mode, pull and output value of a GPIO pin.
pub struct GpioSet {
#[structopt(name = "PIN", help = "The GPIO pin to modify")]
pub pin: String,
#[structopt(long, case_insensitive = true, help = "The I/O mode of the pin")]
pub mode: Option<PinMode>,
#[structopt(
long,
parse(try_from_str),
help = "The value to write to the pin, has effect only in PushPull and OpenDrain modes"
)]
pub value: Option<bool>,
#[structopt(long, case_insensitive = true, help = "The weak pull mode of the pin")]
pub pull: Option<PullMode>,
#[structopt(
long,
parse(try_from_str),
help = "The analog value to write to the pin in volts, has effect only in AnalogOutput mode"
)]
pub voltage: Option<Voltage>,
}
impl CommandDispatch for GpioSet {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
let gpio_pin = transport.gpio_pin(&self.pin)?;
gpio_pin.set(
self.mode,
self.value,
self.pull,
self.voltage.map(|v| v.as_volts() as f32),
)?;
Ok(None)
}
}
#[derive(Debug, StructOpt)]
/// Reads a GPIO pin.
pub struct GpioAnalogRead {
#[structopt(name = "PIN", help = "The GPIO pin to read")]
pub pin: String,
}
#[derive(serde::Serialize)]
pub struct GpioAnalogReadResult {
pub pin: String,
pub volts: f32,
}
impl CommandDispatch for GpioAnalogRead {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
let gpio_pin = transport.gpio_pin(&self.pin)?;
let volts = gpio_pin.analog_read()?;
Ok(Some(Box::new(GpioAnalogReadResult {
pin: self.pin.clone(),
volts,
})))
}
}
#[derive(Debug, StructOpt)]
/// Writes an analog voltage to a GPIO pin.
pub struct GpioAnalogWrite {
#[structopt(name = "PIN", help = "The GPIO pin to write")]
pub pin: String,
#[structopt(
name = "VOLTS",
parse(try_from_str),
help = "The analog value to write to the pin in volts, has effect only in AnalogOutput mode"
)]
pub volts: f32,
}
impl CommandDispatch for GpioAnalogWrite {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
let gpio_pin = transport.gpio_pin(&self.pin)?;
gpio_pin.analog_write(self.volts)?;
Ok(None)
}
}
#[derive(Debug, StructOpt)]
/// Apply a configuration-named pin strapping
pub struct GpioApplyStrapping {
#[structopt(name = "NAME", help = "The pin strapping to apply")]
pub name: String,
}
impl CommandDispatch for GpioApplyStrapping {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
transport.apply_pin_strapping(&self.name)?;
Ok(None)
}
}
#[derive(Debug, StructOpt, CommandDispatch)]
pub enum GpioMonitoringCommand {
Start(GpioMonitoringStart),
Read(GpioMonitoringRead),
Vcd(GpioMonitoringVcd),
}
#[derive(Debug, StructOpt)]
/// Begin logic-analyzer style monitoring of a set of pins.
pub struct GpioMonitoringStart {
#[structopt(
name = "PINS",
help = "The list of GPIO pins to monitor (space separated)"
)]
pub pins: Vec<String>,
}
#[derive(serde::Serialize)]
pub struct InitialLevel {
pub signal_name: String,
pub value: bool,
}
#[derive(serde::Serialize)]
pub struct GpioMonitoringStartResult {
pub clock_nature: ClockNature,
pub timestamp: u64,
pub initial_levels: Vec<InitialLevel>,
}
impl CommandDispatch for GpioMonitoringStart {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport
.capabilities()?
.request(Capability::GPIO | Capability::GPIO_MONITORING)
.ok()?;
let gpio_monitoring = transport.gpio_monitoring()?;
let gpio_pins = transport.gpio_pins(&self.pins)?;
let clock_nature = gpio_monitoring.get_clock_nature()?;
let resp = gpio_monitoring.monitoring_start(
&gpio_pins
.iter()
.map(Rc::borrow)
.collect::<Vec<&dyn GpioPin>>(),
)?;
Ok(Some(Box::new(GpioMonitoringStartResult {
clock_nature,
initial_levels: resp
.initial_levels
.into_iter()
.enumerate()
.map(|(i, l)| InitialLevel {
signal_name: self.pins[i].clone(),
value: l,
})
.collect(),
timestamp: resp.timestamp,
})))
}
}
#[derive(Debug, StructOpt)]
/// Retrieve logic-analyzer style monitoring events detected so far, on a set of pins. Optionally
/// continue monitoring, in which case `monitoring read` must be called again later.
pub struct GpioMonitoringRead {
#[structopt(
name = "PINS",
help = "The list of GPIO pins being monitored (space separated)"
)]
pub pins: Vec<String>,
#[structopt(long, case_insensitive = true)]
pub continue_monitoring: bool,
}
#[derive(serde::Serialize)]
pub struct MonitoringEvent {
pub signal_name: String,
pub edge: Edge,
/// Timestamp of the edge, resolution and epoch is transport-specific.
pub timestamp: u64,
}
#[derive(serde::Serialize)]
pub struct GpioMonitoringReadResult {
/// List of events that has happened since last queried.
pub events: Vec<MonitoringEvent>,
/// Timestamp of the reading, all events happening before this time are guaranteed to be
/// included in the list above.
pub timestamp: u64,
}
impl CommandDispatch for GpioMonitoringRead {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport
.capabilities()?
.request(Capability::GPIO | Capability::GPIO_MONITORING)
.ok()?;
let gpio_monitoring = transport.gpio_monitoring()?;
let gpio_pins = transport.gpio_pins(&self.pins)?;
let resp = gpio_monitoring.monitoring_read(
&gpio_pins
.iter()
.map(Rc::borrow)
.collect::<Vec<&dyn GpioPin>>(),
self.continue_monitoring,
)?;
Ok(Some(Box::new(GpioMonitoringReadResult {
events: resp
.events
.into_iter()
.map(|e| MonitoringEvent {
signal_name: self.pins[e.signal_index as usize].clone(),
edge: e.edge,
timestamp: e.timestamp,
})
.collect(),
timestamp: resp.timestamp,
})))
}
}
#[derive(Debug, StructOpt)]
/// Whereas `monitoring start` and `monitoring read` are meant for use by scripts, this
/// `monitoring vcd` is intended for manual use by an operator. It will stay in the foreground,
/// collecting events until the user presses Ctrl-C. A transcript will be written in the industry
/// standard VCD format, which can be loaded into e.g. Pulseview (or probably also Saleae
/// software), to get a logic analyzer view of what transpired.
pub struct GpioMonitoringVcd {
#[structopt(
name = "PINS",
help = "The list of GPIO pins to monitor (space separated)"
)]
pub pins: Vec<String>,
#[structopt(short, long, help = "Output file")]
outfile: String,
#[structopt(short, long, help = "Do not print start end exit messages.")]
quiet: bool,
}
impl CommandDispatch for GpioMonitoringVcd {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport
.capabilities()?
.request(Capability::GPIO | Capability::GPIO_MONITORING)
.ok()?;
let gpio_monitoring = transport.gpio_monitoring()?;
let gpio_pins = transport.gpio_pins(&self.pins)?;
let mut file = File::create(&self.outfile)?;
if !self.quiet {
eprintln!("Dumping events to {}", &self.outfile);
eprint!("[CTRL+C] to exit ");
}
// Putting the terminal input into raw mode is the only way we can catch Ctrl-C. (This is
// not ideal, it would have been better to catch the SIGINT signal, but the mio_signals
// package is not compatible with the way that our rusb library uses background threads.)
// The tty guard will restore the console settings when it goes out of scope.
let mut stdin = std::io::stdin();
let _stdin_guard = if isatty(stdin.as_raw_fd())? {
let mut guard = TtyModeGuard::new(stdin.as_raw_fd())?;
guard.set_raw_mode()?;
Some(guard)
} else {
None
};
// Inform the transport that we want to monitor a set of pins, and write a file header
// with the names of each of the monitored signals.
let clock_nature = gpio_monitoring.get_clock_nature()?;
let initial = gpio_monitoring.monitoring_start(
&gpio_pins
.iter()
.map(Rc::borrow)
.collect::<Vec<&dyn GpioPin>>(),
)?;
writeln!(&mut file, "$version")?;
let properties = super::version::get_volatile_status();
writeln!(
&mut file,
" opentitantool {} {} {}",
properties.get("BUILD_GIT_VERSION").unwrap(),
properties.get("BUILD_SCM_STATUS").unwrap(),
properties.get("BUILD_TIMESTAMP").unwrap().parse::<i64>()?
)?;
writeln!(&mut file, "$end")?;
match clock_nature {
ClockNature::Wallclock { resolution, .. } => {
writeln!(
&mut file,
"$timescale {}ps $end",
1000000000000u64 / resolution
)?;
}
ClockNature::Unspecified => (),
}
writeln!(&mut file, "$scope module logic $end")?;
for (n, pin) in self.pins.iter().enumerate() {
writeln!(&mut file, "$var wire 1 '{} {} $end", n, pin)?;
}
writeln!(&mut file, "$upscope $end")?;
writeln!(&mut file, "$enddefinitions $end")?;
writeln!(&mut file, "#{}", initial.timestamp)?;
for (n, v) in initial.initial_levels.iter().enumerate() {
writeln!(&mut file, "{}'{}", if *v { 1 } else { 0 }, n)?;
}
// Now loop indefinitely, retrieving events from the internal queue of the transport and
// printing them to the output file.
let mut loop_count: usize = 0;
'event_loop: loop {
let resp = gpio_monitoring.monitoring_read(
&gpio_pins
.iter()
.map(Rc::borrow)
.collect::<Vec<&dyn GpioPin>>(),
true,
)?;
for event in &resp.events {
writeln!(&mut file, "#{}", event.timestamp)?;
writeln!(
&mut file,
"{}'{}",
match event.edge {
Edge::Rising => 1,
Edge::Falling => 0,
},
event.signal_index
)?;
}
eprint!("\u{8}{}", ['/', '-', '\\', '|'][loop_count & 3usize]);
let delay = if resp.events.is_empty() {
Duration::from_millis(10)
} else {
Duration::from_millis(0)
};
if file::wait_fd_read_timeout(stdin.as_raw_fd(), delay).is_ok() {
let mut buf = [0u8; 1];
let len = stdin.read(&mut buf)?;
if len == 1 && buf[0] == 3 {
// CtrlC
break 'event_loop;
}
}
loop_count += 1;
}
// Make one final reading to fetch any events that may have happened just before user
// requested to end monitoring.
let resp = gpio_monitoring.monitoring_read(
&gpio_pins
.iter()
.map(Rc::borrow)
.collect::<Vec<&dyn GpioPin>>(),
false,
)?;
for event in &resp.events {
writeln!(&mut file, "#{}", event.timestamp)?;
writeln!(
&mut file,
"{}'{}",
match event.edge {
Edge::Rising => 1,
Edge::Falling => 0,
},
event.signal_index
)?;
}
// Output timestamp of final reading (all signals remained stable from the last edge until
// this time.)
writeln!(&mut file, "#{}", resp.timestamp)?;
eprintln!("\r");
Ok(None)
}
}
#[derive(Debug, StructOpt)]
/// Remove a configuration-named pin strapping
pub struct GpioRemoveStrapping {
#[structopt(name = "NAME", help = "The pin strapping to release")]
pub name: String,
}
impl CommandDispatch for GpioRemoveStrapping {
fn run(
&self,
_context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
transport.capabilities()?.request(Capability::GPIO).ok()?;
transport.remove_pin_strapping(&self.name)?;
Ok(None)
}
}
#[derive(Debug, StructOpt)]
pub struct GpioMonitoring {
#[structopt(subcommand)]
command: GpioMonitoringCommand,
}
impl CommandDispatch for GpioMonitoring {
fn run(
&self,
context: &dyn Any,
transport: &TransportWrapper,
) -> Result<Option<Box<dyn Annotate>>> {
self.command.run(context, transport)
}
}
/// Commands for manipulating GPIO pins.
#[derive(Debug, StructOpt, CommandDispatch)]
pub enum GpioCommand {
Apply(GpioApplyStrapping),
Remove(GpioRemoveStrapping),
Read(GpioRead),
Write(GpioWrite),
SetMode(GpioSetMode),
SetPullMode(GpioSetPullMode),
Set(GpioSet),
AnalogRead(GpioAnalogRead),
AnalogWrite(GpioAnalogWrite),
Monitoring(GpioMonitoring),
}