blob: b8a109d68d57cc7f965b766a4dff2c84322be7dc [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 directories::ProjectDirs;
use log::LevelFilter;
use serde_annotate::Annotate;
use std::env::ArgsOs;
use std::ffi::OsString;
use std::io::ErrorKind;
use std::iter::{IntoIterator, Iterator};
use std::path::PathBuf;
use structopt::StructOpt;
use super::bootstrap::Bootstrap;
use super::load_bitstream::LoadBitstream;
use crate::app::TransportWrapper;
use crate::backend;
// use opentitanlib::io::uart::UartParams;
#[derive(Debug, StructOpt)]
pub struct InitializeTest {
#[structopt(
long,
default_value = "config",
help = "Filename of a default flagsfile. Relative to $XDG_CONFIG_HOME/opentitantool."
)]
pub rcfile: PathBuf,
#[structopt(long, default_value = "off")]
pub logging: LevelFilter,
#[structopt(flatten)]
pub backend_opts: backend::BackendOpts,
// TODO: Bootstrap::options already has a uart_params (and a spi_params).
// This probably needs some refactoring.
//#[structopt(flatten)]
//pub uart_params: UartParams,
#[structopt(flatten)]
pub load_bitstream: LoadBitstream,
#[structopt(flatten)]
pub bootstrap: Bootstrap,
}
impl InitializeTest {
pub fn init_logging(&self) {
let level = self.logging;
if level != LevelFilter::Off {
env_logger::Builder::from_default_env()
.filter(None, level)
.init();
}
}
// Given some existing option configuration, maybe re-evaluate command
// line options by reading an `rcfile`.
pub fn parse_command_line(&self, mut args: ArgsOs) -> Result<Vec<OsString>> {
// Initialize the logger if the user requested the non-defualt option.
self.init_logging();
if self.rcfile.as_os_str().is_empty() {
// No rcfile to parse.
return Ok(Vec::new());
}
// Construct the rcfile path based on the user's config directory
// (ie: $HOME/.config/opentitantool/<filename>).
let rcfile = if let Some(base) = ProjectDirs::from("org", "opentitan", "opentitantool") {
base.config_dir().join(&self.rcfile)
} else {
self.rcfile.clone()
};
// argument[0] is the executable name.
let mut arguments = vec![args.next().unwrap()];
// Read in the rcfile and extend the argument list.
match std::fs::read_to_string(&rcfile) {
Ok(content) => {
for line in content.split('\n') {
// Strip basic comments as shellwords won't handle comments.
let (line, _) = line.split_once('#').unwrap_or((line, ""));
arguments.extend(shellwords::split(line)?.iter().map(OsString::from));
}
Ok(())
}
Err(e) if e.kind() == ErrorKind::NotFound => {
log::warn!("Could not read {:?}. Ignoring.", rcfile);
Ok(())
}
Err(e) => Err(anyhow::Error::new(e).context(format!("Reading file {:?}", rcfile))),
}?;
// Extend the argument list with all remaining command line arguments.
arguments.extend(args.into_iter());
Ok(arguments)
}
// Print the result of a command.
// If there is an error and `RUST_BACKTRACE=1`, print a backtrace.
pub fn print_result(operation: &str, result: Result<Option<Box<dyn Annotate>>>) -> Result<()> {
match result {
Ok(Some(value)) => {
log::info!("{}: success.", operation);
println!(
"\"{}\": {}",
operation,
serde_json::to_string_pretty(&value)?
);
Ok(())
}
Ok(None) => {
log::info!("{}: success.", operation);
println!("\"{}\": true", operation);
Ok(())
}
Err(e) => {
log::info!("{}: {:?}.", operation, e);
println!("\"{}\": false", operation);
Err(e)
}
}
}
pub fn init_target(&self) -> Result<TransportWrapper> {
// Create the transport interface.
let transport = backend::create(&self.backend_opts)?;
// Set up the default pin configurations as specified in the transport's config file.
transport.apply_default_configuration()?;
// Create the UART first to initialize the desired parameters.
let _uart = self.bootstrap.options.uart_params.create(&transport)?;
// Load a bitstream.
Self::print_result("load_bitstream", self.load_bitstream.init(&transport))?;
// Bootstrap an rv32 test program.
Self::print_result("bootstrap", self.bootstrap.init(&transport))?;
Ok(transport)
}
}