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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// 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 bitflags::bitflags;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::collections::HashMap;
use std::path::PathBuf;
use std::rc::Rc;

use crate::bootstrap::BootstrapOptions;
use crate::io::emu::Emulator;
use crate::io::gpio::{GpioBitbanging, GpioMonitoring, GpioPin};
use crate::io::i2c::Bus;
use crate::io::jtag::{JtagChain, JtagParams};
use crate::io::nonblocking_help::{NoNonblockingHelp, NonblockingHelp};
use crate::io::spi::Target;
use crate::io::uart::Uart;

pub mod chip_whisperer;
pub mod common;
pub mod dediprog;
pub mod hyperdebug;
pub mod ioexpander;
pub mod proxy;
pub mod ti50emulator;
pub mod ultradebug;
pub mod verilator;

// Export custom error types
mod errors;
pub use errors::{TransportError, TransportInterfaceType};

bitflags! {
    /// A bitmap of capabilities which may be provided by a transport.
    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
    #[serde(transparent)]
    pub struct Capability: u32 {
        const NONE = 0x00;
        const UART = 0x01 << 0;
        const SPI = 0x01 << 1;
        const GPIO = 0x01 << 2;
        const I2C = 0x01 << 3;
        const PROXY = 0x01 << 4;
        const EMULATOR = 0x01 << 5;
        const GPIO_MONITORING = 0x01 << 6; // Logic analyzer functionality
        const JTAG = 0x01 << 7;
        const UART_NONBLOCKING = 0x01 << 8;
        const SPI_DUAL = 0x01 << 9;
        const SPI_QUAD = 0x01 << 10;
        const GPIO_BITBANGING = 0x01 << 11;
    }
}

/// A struct which represents what features a particular Transport instance supports.
#[derive(Serialize, Deserialize)]
pub struct Capabilities {
    capabilities: Capability,
}

impl Capabilities {
    /// Create a new Capabilities object representing a provider of
    /// capabilities specified by `cap`.
    fn new(cap: Capability) -> Self {
        Self { capabilities: cap }
    }

    pub fn add(&self, extra: Capability) -> Self {
        Self {
            capabilities: self.capabilities | extra,
        }
    }

    /// Request the capabilities specified by `cap`.
    pub fn request(&self, needed: Capability) -> NeededCapabilities {
        NeededCapabilities {
            capabilities: self.capabilities,
            needed,
        }
    }
}

/// A struct which can check that needed capability requirements are met.
pub struct NeededCapabilities {
    capabilities: Capability,
    needed: Capability,
}

impl NeededCapabilities {
    /// Checks that the requested capabilities are provided.
    pub fn ok(&self) -> Result<()> {
        if self.capabilities & self.needed != self.needed {
            Err(TransportError::MissingCapabilities(self.needed, self.capabilities).into())
        } else {
            Ok(())
        }
    }
}

/// A transport object is a factory for the low-level interfaces provided
/// by a given communications backend.
pub trait Transport {
    /// Returns a `Capabilities` object to check the capabilities of this
    /// transport object.
    fn capabilities(&self) -> Result<Capabilities>;

    /// Resets the transport to power-on condition.  That is, pin/uart/spi configuration reverts
    /// to default, ongoing operations are cancelled, etc.
    fn apply_default_configuration(&self) -> Result<()> {
        Ok(())
    }

    /// Returns a [`JtagChain`] implementation.
    fn jtag(&self, _opts: &JtagParams) -> Result<Box<dyn JtagChain + '_>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::Jtag).into())
    }
    /// Returns a SPI [`Target`] implementation.
    fn spi(&self, _instance: &str) -> Result<Rc<dyn Target>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::Spi).into())
    }
    /// Returns a I2C [`Bus`] implementation.
    fn i2c(&self, _instance: &str) -> Result<Rc<dyn Bus>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::I2c).into())
    }
    /// Returns a [`Uart`] implementation.
    fn uart(&self, _instance: &str) -> Result<Rc<dyn Uart>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::Uart).into())
    }
    /// Returns a [`GpioPin`] implementation.
    fn gpio_pin(&self, _instance: &str) -> Result<Rc<dyn GpioPin>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::Gpio).into())
    }
    /// Returns a [`GpioMonitoring`] implementation, for logic analyzer functionality.
    fn gpio_monitoring(&self) -> Result<Rc<dyn GpioMonitoring>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::GpioMonitoring).into())
    }
    /// Returns a [`GpioBitbanging`] implementation, for timed and synchronized manipulation of
    /// multiple GPIO pins.
    fn gpio_bitbanging(&self) -> Result<Rc<dyn GpioBitbanging>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::GpioBitbanging).into())
    }
    /// Returns a [`Emulator`] implementation.
    fn emulator(&self) -> Result<Rc<dyn Emulator>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::Emulator).into())
    }

    /// Methods available only on Proxy implementation.
    fn proxy_ops(&self) -> Result<Rc<dyn ProxyOps>> {
        Err(TransportError::InvalidInterface(TransportInterfaceType::ProxyOps).into())
    }

    /// Invoke non-standard functionality of some Transport implementations.
    fn dispatch(&self, _action: &dyn Any) -> Result<Option<Box<dyn serde_annotate::Annotate>>> {
        Err(TransportError::UnsupportedOperation.into())
    }

    /// As long as the returned `MaintainConnection` object is kept by the caller, this driver may
    /// assume that no other `opentitantool` processes attempt to access the same debugger device.
    /// This allows for optimzations such as keeping USB handles open across function invocations.
    fn maintain_connection(&self) -> Result<Rc<dyn MaintainConnection>> {
        // For implementations that have not implemented any optimizations, return a no-op object.
        Ok(Rc::new(()))
    }

    /// Before nonblocking operations can be used on `Uart` or other traits, this
    /// `NonblockingHelp` object must be invoked, in order to get the `Transport` implementation a
    /// chance to register its internal event sources with the main event loop.
    fn nonblocking_help(&self) -> Result<Rc<dyn NonblockingHelp>> {
        Ok(Rc::new(NoNonblockingHelp))
    }
}

/// As long as this object is kept alive, the `Transport` driver may assume that no other
/// `opentitantool` processes attempt to access the same debugger device.  This allows for
/// optimzations such as keeping USB handles open across function invocations.
pub trait MaintainConnection {}

/// No-op implmentation of the trait, for use by `Transport` implementations that do not do
/// any optimizations to maintain connection between method calls.
impl MaintainConnection for () {}

/// Methods available only on the Proxy implementation of the Transport trait.
pub trait ProxyOps {
    /// Returns a string->string map containing user-defined aspects "provided" by the testbed
    /// setup.  For instance, whether a SPI flash chip is fitted in the socket, or whether pullup
    /// resistors are suitable for high-speed I2C.  Most of the time, this information will not
    /// come from the actual transport layer, but from the TransportWrapper above it.
    fn provides_map(&self) -> Result<HashMap<String, String>>;

    fn bootstrap(&self, options: &BootstrapOptions, payload: &[u8]) -> Result<()>;
    fn apply_pin_strapping(&self, strapping_name: &str) -> Result<()>;
    fn remove_pin_strapping(&self, strapping_name: &str) -> Result<()>;

    /// Applies the default transport init configuration expect with the specify strap applied.
    fn apply_default_configuration_with_strap(&self, strapping_name: &str) -> Result<()>;
}

/// Used by Transport implementations dealing with emulated OpenTitan
/// chips, allowing e.g. more efficient direct means of programming
/// emulated flash storage.  (As opposed to running an actual
/// bootloater on the emulated target, which would receive data via
/// SPI to be flashed.)
pub struct Bootstrap {
    pub image_path: PathBuf,
}

/// Some transports allow dynamically changing which pins are used for JTAG.
pub struct SetJtagPins {
    pub tclk: Option<Rc<dyn GpioPin>>,
    pub tms: Option<Rc<dyn GpioPin>>,
    pub tdi: Option<Rc<dyn GpioPin>>,
    pub tdo: Option<Rc<dyn GpioPin>>,
    pub trst: Option<Rc<dyn GpioPin>>,
}

pub trait ProgressIndicator {
    // Begins a new stage, indicating "size" of this stage in bytes.  `name` can be the empty
    // string, for instance if the operation has only a single stage.
    fn new_stage(&self, name: &str, total: usize);
    // Indicates how far towards the previously declared `total`.  Operation will be shown as
    // complete, once the parameter value to this method equals `total`.
    fn progress(&self, absolute: usize);
}

/// Command for Transport::dispatch().
pub struct UpdateFirmware<'a> {
    /// The firmware to load into the HyperDebug device, None means load an "official" newest
    /// release of the firmware for the particular debugger device, assuming that the `Transport`
    /// trait implementation knows how to download such.
    pub firmware: Option<Vec<u8>>,
    /// A progress function to provide user feedback, see details of the `Progress` struct.
    pub progress: Box<dyn ProgressIndicator + 'a>,
    /// Should updating be attempted, even if the current firmware version matches that of the
    /// image to be updated to.
    pub force: bool,
}

/// An `EmptyTransport` provides no communications backend.
pub struct EmptyTransport;

impl Transport for EmptyTransport {
    fn capabilities(&self) -> Result<Capabilities> {
        Ok(Capabilities::new(Capability::NONE))
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;

    #[test]
    fn test_capabilities_met() -> anyhow::Result<()> {
        let cap = Capabilities::new(Capability::UART | Capability::SPI);
        assert!(cap.request(Capability::UART).ok().is_ok());
        Ok(())
    }

    #[test]
    fn test_capabilities_not_met() -> anyhow::Result<()> {
        let cap = Capabilities::new(Capability::UART | Capability::SPI);
        assert!(cap.request(Capability::GPIO).ok().is_err());
        Ok(())
    }
}