opentitanlib/test_utils/
load_bitstream.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 serde_annotate::Annotate;
8use std::path::{Path, PathBuf};
9use std::time::Duration;
10
11use crate::app::{StagedProgressBar, TransportWrapper};
12use crate::transport::common::fpga::{ClearBitstream, FpgaProgram};
13
14/// Load a bitstream into the FPGA.
15#[derive(Debug, Args)]
16pub struct LoadBitstream {
17    /// Whether to clear out any existing bitstream.
18    #[arg(long)]
19    pub clear_bitstream: bool,
20
21    /// The bitstream to load for the test.
22    #[arg(long)]
23    pub bitstream: Option<PathBuf>,
24
25    /// Duration of the reset pulse.
26    #[arg(long, value_parser = humantime::parse_duration, default_value = "50ms")]
27    pub rom_reset_pulse: Duration,
28
29    /// Duration of ROM detection timeout.
30    #[arg(long, value_parser = humantime::parse_duration, default_value = "2s")]
31    pub rom_timeout: Duration,
32}
33
34impl LoadBitstream {
35    pub fn init(&self, transport: &TransportWrapper) -> Result<Option<Box<dyn Annotate>>> {
36        // Clear out existing bitstream, if requested.
37        if self.clear_bitstream {
38            log::info!("Clearing bitstream.");
39            transport.dispatch(&ClearBitstream)?;
40        }
41        // Load the specified bitstream, if provided.
42        if let Some(bitstream) = &self.bitstream {
43            self.load(transport, bitstream)
44        } else {
45            Ok(None)
46        }
47    }
48
49    pub fn load(
50        &self,
51        transport: &TransportWrapper,
52        file: &Path,
53    ) -> Result<Option<Box<dyn Annotate>>> {
54        log::info!("Loading bitstream: {:?}", file);
55        let payload = std::fs::read(file)?;
56        let progress = StagedProgressBar::new();
57        let operation = FpgaProgram {
58            bitstream: payload,
59            rom_reset_pulse: self.rom_reset_pulse,
60            rom_timeout: self.rom_timeout,
61            progress: Box::new(progress),
62        };
63
64        if operation.should_skip(transport)? {
65            return Ok(None);
66        }
67        transport.dispatch(&operation)
68    }
69}