1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use anyhow::Result;
use clap::Args;
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::Iterator;
use std::path::PathBuf;
use std::str::FromStr;
use super::bootstrap::Bootstrap;
use super::load_bitstream::LoadBitstream;
use crate::app::TransportWrapper;
use crate::backend;
use crate::io::jtag::JtagParams;
// use opentitanlib::io::uart::UartParams;
#[derive(Debug, Args)]
pub struct InitializeTest {
/// Filename of a default flagsfile. Relative to $XDG_CONFIG_HOME/opentitantool.
#[arg(long, value_parser = PathBuf::from_str, default_value = "config")]
pub rcfile: PathBuf,
#[arg(long, default_value = "off")]
pub logging: LevelFilter,
#[command(flatten)]
pub backend_opts: backend::BackendOpts,
// TODO: Bootstrap::options already has a uart_params (and a spi_params).
// This probably needs some refactoring.
//#[command(flatten)]
//pub uart_params: UartParams,
#[command(flatten)]
pub load_bitstream: LoadBitstream,
#[command(flatten)]
pub bootstrap: Bootstrap,
#[command(flatten)]
pub jtag_params: JtagParams,
}
impl InitializeTest {
pub fn init_logging(&self) {
let level = self.logging;
// The tests might use OpenOCD which uses util::printer so we will get
// more useful logging if we log the target instead of the module path
if level != LevelFilter::Off {
env_logger::Builder::from_default_env()
.format_target(true)
.format_module_path(false)
.format_timestamp_millis()
.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);
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(None)?;
// 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)
}
}