opentitanlib/test_utils/
init.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::Result;
6use clap::Args;
7use directories::ProjectDirs;
8use log::LevelFilter;
9use std::env::ArgsOs;
10use std::ffi::OsString;
11use std::io::ErrorKind;
12use std::iter::Iterator;
13use std::path::PathBuf;
14use std::str::FromStr;
15
16use super::bootstrap::Bootstrap;
17use super::fpga_backdoor::LoadMemories;
18use super::load_bitstream::LoadBitstream;
19use crate::app::TransportWrapper;
20use crate::backend;
21use crate::io::jtag::JtagParams;
22// use opentitanlib::io::uart::UartParams;
23
24#[derive(Debug, Args)]
25pub struct InitializeTest {
26    /// Filename of a default flagsfile.  Relative to $XDG_CONFIG_HOME/opentitantool.
27    #[arg(long, value_parser = PathBuf::from_str, default_value = "config")]
28    pub rcfile: PathBuf,
29
30    #[arg(long, default_value = "off")]
31    pub logging: LevelFilter,
32
33    /// De-assert reset signal before executing commands.
34    #[arg(short, long)]
35    drop_reset: bool,
36
37    #[command(flatten)]
38    pub backend_opts: backend::BackendOpts,
39
40    // TODO: Bootstrap::options already has a uart_params (and a spi_params).
41    // This probably needs some refactoring.
42    //#[command(flatten)]
43    //pub uart_params: UartParams,
44    #[command(flatten)]
45    pub load_bitstream: LoadBitstream,
46
47    #[command(flatten)]
48    pub load_memories: LoadMemories,
49
50    #[command(flatten)]
51    pub bootstrap: Bootstrap,
52
53    #[command(flatten)]
54    pub jtag_params: JtagParams,
55}
56
57impl InitializeTest {
58    pub fn init_logging(&self) {
59        let level = self.logging;
60        // The tests might use OpenOCD which uses util::printer so we will get
61        // more useful logging if we log the target instead of the module path
62        if level != LevelFilter::Off {
63            env_logger::Builder::from_default_env()
64                .format_target(true)
65                .format_module_path(false)
66                .format_timestamp_millis()
67                .filter(None, level)
68                .init();
69        }
70    }
71
72    // Given some existing option configuration, maybe re-evaluate command
73    // line options by reading an `rcfile`.
74    pub fn parse_command_line(&self, mut args: ArgsOs) -> Result<Vec<OsString>> {
75        // Initialize the logger if the user requested the non-defualt option.
76        self.init_logging();
77        if self.rcfile.as_os_str().is_empty() {
78            // No rcfile to parse.
79            return Ok(Vec::new());
80        }
81
82        // Construct the rcfile path based on the user's config directory
83        // (ie: $HOME/.config/opentitantool/<filename>).
84        let rcfile = if let Some(base) = ProjectDirs::from("org", "opentitan", "opentitantool") {
85            base.config_dir().join(&self.rcfile)
86        } else {
87            self.rcfile.clone()
88        };
89
90        // argument[0] is the executable name.
91        let mut arguments = vec![args.next().unwrap()];
92
93        // Read in the rcfile and extend the argument list.
94        match std::fs::read_to_string(&rcfile) {
95            Ok(content) => {
96                for line in content.split('\n') {
97                    // Strip basic comments as shellwords won't handle comments.
98                    let (line, _) = line.split_once('#').unwrap_or((line, ""));
99                    arguments.extend(shellwords::split(line)?.iter().map(OsString::from));
100                }
101                Ok(())
102            }
103            Err(e) if e.kind() == ErrorKind::NotFound => {
104                log::warn!("Could not read {:?}. Ignoring.", rcfile);
105                Ok(())
106            }
107            Err(e) => Err(anyhow::Error::new(e).context(format!("Reading file {:?}", rcfile))),
108        }?;
109
110        // Extend the argument list with all remaining command line arguments.
111        arguments.extend(args);
112        Ok(arguments)
113    }
114
115    // Print the result of a command.
116    // If there is an error and `RUST_BACKTRACE=1`, print a backtrace.
117    pub fn print_result(
118        operation: &str,
119        result: Result<Option<Box<dyn erased_serde::Serialize>>>,
120    ) -> Result<()> {
121        match result {
122            Ok(Some(value)) => {
123                log::info!("{}: success.", operation);
124                println!(
125                    "\"{}\": {}",
126                    operation,
127                    serde_json::to_string_pretty(&value)?
128                );
129                Ok(())
130            }
131            Ok(None) => {
132                log::info!("{}: success.", operation);
133                println!("\"{}\": true", operation);
134                Ok(())
135            }
136            Err(e) => {
137                log::info!("{}: {:?}.", operation, e);
138                println!("\"{}\": false", operation);
139                Err(e)
140            }
141        }
142    }
143
144    pub fn init_target(&self) -> Result<TransportWrapper> {
145        // Create the transport interface.
146        let transport = backend::create(&self.backend_opts)?;
147
148        // Set up the default pin configurations as specified in the transport's config file.
149        transport.apply_default_configuration(None)?;
150
151        if self.drop_reset {
152            transport.pin_strapping("RESET")?.remove()?;
153        }
154
155        // Create the UART first to initialize the desired parameters.
156        let _uart = self.bootstrap.options.uart_params.create(&transport)?;
157
158        // Load a bitstream.
159        Self::print_result(
160            "load_bitstream",
161            self.load_bitstream.init(&transport).map(|_| None),
162        )?;
163
164        // Program any memories (e.g. ROM, OTP).
165        Self::print_result(
166            "load_memories",
167            self.load_memories
168                .init(&transport, &self.jtag_params)
169                .map(|_| None),
170        )?;
171
172        // Bootstrap an rv32 test program.
173        Self::print_result("bootstrap", self.bootstrap.init(&transport))?;
174        Ok(transport)
175    }
176}