opentitanlib/transport/common/
fpga.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;
6
7use crate::app::TransportWrapper;
8use crate::io::fpga_backdoor::{BackdoorParams, enter_backdoor_loader};
9use crate::io::jtag::JtagParams;
10use crate::transport::ProgressIndicator;
11use crate::util::usr_access::usr_access_get;
12
13/// Command for Transport::dispatch().
14pub struct FpgaProgram {
15    /// The bitstream content to load into the FPGA.
16    pub bitstream: Vec<u8>,
17    /// A progress function to provide user feedback.
18    /// Will be called with the address and length of each chunk sent to the target device.
19    pub progress: Box<dyn ProgressIndicator>,
20}
21
22impl FpgaProgram {
23    /// Strap into the bkdr_loader TAP and read the FPGA's `USR_ACCESS_TIMESTAMP` register.
24    /// If the FPGA has no bitstream loaded at all (e.g. right after power-up), the
25    /// `fpga_backdoor.tap` won't be present on the JTAG scan chain, so this fails outright
26    /// rather than returning a value.
27    fn read_fpga_usr_access(transport: &TransportWrapper, jtag_params: &JtagParams) -> Result<u32> {
28        enter_backdoor_loader(transport)?;
29        let backdoor_params = BackdoorParams {
30            jtag: jtag_params.clone(),
31        };
32        let mut backdoor = backdoor_params.create(transport)?.connect(false)?;
33        let usr_access = backdoor.read_usr_access_timestamp()?;
34        backdoor.set_done()?;
35        Ok(usr_access)
36    }
37
38    /// Check whether the FPGA is already configured with this bitstream, by strapping into the
39    /// bkdr_loader TAP and comparing its `USR_ACCESS_TIMESTAMP` register against the `USR_ACCESS`
40    /// value embedded in this bitstream file.
41    ///
42    /// Any failure to read that register (e.g. no bitstream loaded yet, so the scan chain
43    /// doesn't even contain the backdoor TAP) is treated as "not the right bitstream": we fall
44    /// back to reprogramming rather than propagating the error.
45    fn check_correct_version(
46        &self,
47        transport: &TransportWrapper,
48        jtag_params: &JtagParams,
49    ) -> Result<bool> {
50        let expected = usr_access_get(&self.bitstream)?;
51
52        let actual = match Self::read_fpga_usr_access(transport, jtag_params) {
53            Ok(actual) => actual,
54            Err(e) => {
55                log::debug!("Could not read USR_ACCESS_TIMESTAMP over the backdoor TAP: {e:#}");
56                log::info!("Assuming no (or an incompatible) bitstream is currently loaded.");
57                return Ok(false);
58            }
59        };
60
61        if actual == expected {
62            log::info!(
63                "Already running the correct bitstream (USR_ACCESS_TIMESTAMP=0x{actual:08x}). Skip loading bitstream."
64            );
65            return Ok(true);
66        }
67        log::info!(
68            "Bitstream USR_ACCESS_TIMESTAMP mismatch (running=0x{actual:08x}, expected=0x{expected:08x}); reprogramming."
69        );
70        Ok(false)
71    }
72
73    fn skip(&self) -> bool {
74        self.bitstream.starts_with(b"__skip__")
75    }
76
77    pub fn should_skip(
78        &self,
79        transport: &TransportWrapper,
80        jtag_params: &JtagParams,
81    ) -> Result<bool> {
82        if self.skip() {
83            log::info!("Skip loading the __skip__ bitstream.");
84            return Ok(true);
85        }
86        if self.check_correct_version(transport, jtag_params)? {
87            return Ok(true);
88        }
89        Ok(false)
90    }
91}