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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
// 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::ValueEnum;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use thiserror::Error;

use crate::impl_serializable_error;
use crate::transport::TransportError;

/// Errors related to the GPIO interface.
#[derive(Debug, Error, Serialize, Deserialize)]
pub enum GpioError {
    #[error("Invalid pin name {0}")]
    InvalidPinName(String),
    #[error("Invalid pin number {0}")]
    InvalidPinNumber(u8),
    /// The current mode of the pin (input) does not support the requested operation (set
    /// level).
    #[error("Invalid mode for pin {0}")]
    InvalidPinMode(u8),
    /// The hardware does not support the requested mode (open drain, pull down input, etc.)
    #[error("Unsupported mode {0} requested")]
    UnsupportedPinMode(PinMode),
    /// The hardware does not support the requested mode (open drain, pull down input, etc.)
    #[error("Unsupported pull mode {0} requested")]
    UnsupportedPullMode(PullMode),
    #[error("Conflicting pin configurations for pin {0}: host:{1}, target:{2}")]
    PinModeConflict(String, String, String),
    #[error("Conflicting pin logic values for pin {0}: host:{1}, target:{2}")]
    PinValueConflict(String, String, String),
    #[error("Undefined pin logic value for pin {0}")]
    PinValueUndefined(String),
    #[error("Unsupported voltage {0}V requested")]
    UnsupportedPinVoltage(f32),
    #[error("Unsupported number of pins: {0}")]
    UnsupportedNumberOfPins(usize),
    #[error("Mismatched bitbang data length: {0} != {1}")]
    MismatchedDataLength(usize, usize),
    #[error("Bitbang data beyond the {0} least significant bits")]
    InvalidBitbangData(usize),
    #[error("Bitbang delay of zero, immediately preceding `await()`, or at end of sequence, not permitted")]
    InvalidBitbangDelay,
    #[error("Dac-bang samples not a multiple of number of pins")]
    InvalidDacBangData,
    #[error("Dac-bang delay of zero or immediately adjacent to linear(), or at end of sequence, not permitted")]
    InvalidDacBangDelay,
    #[error("Generic error: {0}")]
    Generic(String),
}
impl_serializable_error!(GpioError);

/// Mode of I/O pins.
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, ValueEnum)]
#[value(rename_all = "verbatim")]
pub enum PinMode {
    Input,
    PushPull,
    OpenDrain,
    AnalogInput,
    AnalogOutput,
    Alternate, // Pin used for UART/SPI/I2C or something else
}

impl std::fmt::Display for PinMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        std::fmt::Debug::fmt(self, f)
    }
}

/// Mode of weak pull (relevant in Input and OpenDrain modes).
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, ValueEnum)]
#[value(rename_all = "verbatim")]
pub enum PullMode {
    None,
    PullUp,
    PullDown,
}

impl std::fmt::Display for PullMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        std::fmt::Debug::fmt(self, f)
    }
}

/// A trait which represents a single GPIO pin.
pub trait GpioPin {
    /// Reads the value of the GPIO pin.
    fn read(&self) -> Result<bool>;

    /// Sets the value of the GPIO pin to `value`.
    fn write(&self, value: bool) -> Result<()>;

    /// Sets the mode of the GPIO pin as input, output, or open drain I/O.
    fn set_mode(&self, mode: PinMode) -> Result<()>;

    /// Sets the weak pull resistors of the GPIO pin.
    fn set_pull_mode(&self, mode: PullMode) -> Result<()>;

    /// Reads the analog value of the GPIO pin in Volts. `AnalogInput` mode disables digital
    /// circuitry for better results, but this method may also work in other modes.
    fn analog_read(&self) -> Result<f32> {
        Err(TransportError::UnsupportedOperation.into())
    }

    /// Sets the analog value of the GPIO pin to `value` Volts, must be in `AnalogOutput` mode.
    fn analog_write(&self, _volts: f32) -> Result<()> {
        Err(TransportError::UnsupportedOperation.into())
    }

    /// Simultaneously sets mode, value, and weak pull, some transports may guarantee atomicity.
    fn set(
        &self,
        mode: Option<PinMode>,
        value: Option<bool>,
        pull: Option<PullMode>,
        analog_value: Option<f32>,
    ) -> Result<()> {
        // Transports must override this function for truly atomic behavior.  Default
        // implementation below applies each setting separately.
        if let Some(mode) = mode {
            self.set_mode(mode)?;
        }
        if let Some(pull) = pull {
            self.set_pull_mode(pull)?;
        }
        if let Some(value) = value {
            self.write(value)?;
        }
        if let Some(analog_value) = analog_value {
            self.analog_write(analog_value)?;
        }
        Ok(())
    }

    /// Not meant for API clients, this method returns the pin name as it is known to the
    /// transport (which may have been through one or more alias mappings from the name provided
    /// by the API client.)  This method is used by implementations of `GpioMonitoring`.
    fn get_internal_pin_name(&self) -> Option<&str> {
        None
    }
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Edge {
    Rising,
    Falling,
}

#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum ClockNature {
    /// Unix time can be computed as (t + offset) / resolution, where t is a 64-bit timestamp
    /// value from `MonitoringEvent`.
    Wallclock {
        /// If resolution is microseconds, `resolution` will be 1_000_000.
        resolution: u64,
        /// Offset relative to Unix epoch, measured according to above resolution.
        offset: Option<u64>,
    },
    /// The 64-bit timestamp values could be emulator clock counts, or some other measure that
    /// increases monotonically, but not necessarily uniformly in relation to wall clock time.
    Unspecified,
}

/// Represents an edge detected on the GPIO pin.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct MonitoringEvent {
    /// Identification of the signal that had an event, in the form of an index into the array
    /// originally passed to `monitoring_read()`.
    pub signal_index: u8,
    /// Rising or falling edge
    pub edge: Edge,
    /// Timestamp of the edge, resolution and epoch is transport-specific, more information in
    /// `ClockNature`.
    pub timestamp: u64,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MonitoringStartResponse {
    /// Transport timestamp at the time monitoring started.
    pub timestamp: u64,
    /// Initial logic level for each of the given pins.
    pub initial_levels: Vec<bool>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MonitoringReadResponse {
    /// List of events having occurred since the start or the last read.
    pub events: Vec<MonitoringEvent>,
    /// All events at or before this timestamp are guaranteed to be included.
    pub timestamp: u64,
}

/// A trait implemented by transports which support advanced edge-detection on GPIO pins.  This
/// trait allows monitoring a set of pins, and getting a stream of "events" (rising and falling
/// edges with timestamps) for any change among the set.
pub trait GpioMonitoring {
    fn get_clock_nature(&self) -> Result<ClockNature>;

    /// Set up edge trigger detection on the given set of pins, transport will buffer the list
    /// internally, return the initial level of each of the given pins.
    fn monitoring_start(&self, pins: &[&dyn GpioPin]) -> Result<MonitoringStartResponse>;

    /// Retrieve list of events detected thus far, optionally stopping the possibly expensive edge
    /// detection.  Buffer overrun will be reported as an `Err`, and result in the stopping of the
    /// edge detection irrespective of the parameter value.
    fn monitoring_read(
        &self,
        pins: &[&dyn GpioPin],
        continue_monitoring: bool,
    ) -> Result<MonitoringReadResponse>;
}

/// Represents one entry in the specification of a bitbanging waveform.  Pins must have been
/// configured as either `PushPull`, `OpenDrain` or `Input` prior to requesting a bitbang
/// operation.
///
/// `Write` and `Both` are somewhat equivalent to their namesakes in `spi::Transfer`, that is
/// `Write` requests using the given data for generating waveforms on any of the pins configured
/// as output, while disregarding the actual levels of the pins.  `Both` requests generating a
/// waveform, and also sampling every pin at each clock tick.
pub enum BitbangEntry<'rd, 'wr> {
    /// Represents a sequence of pin values.  Bit 0 in each byte controls the first `GpioPin` in
    /// the slice given to `GpioBitbanging::run()`, bit 1 controls the second `GpioPin`, and so
    /// on.  Each byte is applied to the pins in sequence, with a particular delay between each
    /// given by the `clock_tick` argument to `run()`.
    Write(&'wr [u8]),
    /// Same as `Write`, but this `BitbangEntry` owns the data.
    WriteOwned(Box<[u8]>),
    /// Represents a sequence of pin values as above, but additionally captures the value of any
    /// pins in `Input` or `OpenDrain` mode.  At each clock tick, the input levels are sampled
    /// just before the given output levels are applied, meaning that the data coming back will be
    /// offset by one sample.
    Both(&'wr [u8], &'rd mut [u8]),
    /// Same as `Both`, but this `BitbangEntry` owns the data, which will be overwritten.
    BothOwned(Box<[u8]>),
    /// Represents a delay of the given number of clock ticks in which the output levels are held
    /// as indicated by the last byte of the preceding `Write`/`Both` entry.
    ///
    /// A delay of zero is invalid.  A delay of one tick is equivalent to not specifying any
    /// `Delay` between two `Write` blocks, which is also equivalent to concatenating the two into
    /// a single `Write` block.
    Delay(u32),
    /// Similar to `Delay`, but waits until `(pin_values ^ pattern) & mask` equals zero, that is,
    /// until the set of pins indicated by ones in `mask` all have the the value indicated in
    /// `pattern`.
    Await { mask: u8, pattern: u8 },
}

pub enum DacBangEntry<'wr> {
    /// Represents a sequence of volt values.  If `N` pin is being driven, then samples are
    /// interleaved such that at first, the first `N` values are applied, one to each of the pins,
    /// at next tick the next `N` values are applied, and so on, with a particular delay between
    /// each tick given by the `clock_tick` argument to `run()`.
    Write(&'wr [f32]),
    /// Same as `Write`, but this `DacBangEntry` owns the data.
    WriteOwned(Box<[f32]>),
    /// Represents a delay of the given number of clock ticks in which the output levels are held
    /// as indicated by the last values of the preceding `Write` entry.
    ///
    /// A delay of zero is invalid.  A delay of one tick is equivalent to not specifying any
    /// `Delay` between two `Write` blocks, which is also equivalent to concatenating the two into
    /// a single `Write` block.
    Delay(u32),
    /// Represents a time span of the given number of clock ticks during which the voltage
    /// linearly transitions from the previous to the subsequent value.
    ///
    /// A value of zero is invalid.  A value of one tick is equivalent to not specifying any delay
    /// between two `Write` blocks.  A value of two will result in a single intermediate sample
    /// halfway between the two voltages.  In general, a value of N will result in N-1
    /// intermediate samples being inserted at this point.
    Linear(u32),
}

/// A trait implemented by transports which support synchronous bit-banging on GPIO pins, similar
/// to FTDI devices.  This trait allows generation of arbitrary waveforms on a set of pins, and
/// optionally getting back samples from same or other pins, taken at precise times.
pub trait GpioBitbanging {
    /// Apply the given sequence of values to the given set of GPIO pins, by each tick of a clock
    /// with the given period.  This function does not change the mode of any pins, they must
    /// already be put into `PushPull`, `OpenDrain` or `Input` mode as appropriate.  (In the
    /// latter case, the specified waveform data does not matter, as the pin is not driving, and
    /// would be included in the set of pins only in order to have it sampled at each clock tick.)
    /// Returns a `GpioBitbangOperation` which must be continuously polled, to know when the
    /// waveform is complete.
    fn start<'a>(
        &self,
        pins: &[&dyn GpioPin],
        clock_tick: Duration,
        waveform: Box<[BitbangEntry<'a, 'a>]>,
    ) -> Result<Box<dyn GpioBitbangOperation<'a, 'a> + 'a>>;

    /// Convenience method which starts the bitbanging operation, and blocks until it is complete.
    fn run<'a>(
        &self,
        pins: &[&dyn GpioPin],
        clock_tick: Duration,
        waveform: Box<[BitbangEntry<'a, 'a>]>,
    ) -> Result<Box<[BitbangEntry<'a, 'a>]>> {
        let mut bitbang_op = self.start(pins, clock_tick, waveform)?;
        while !bitbang_op.query()? {}
        bitbang_op.get_result()
    }

    /// Apply given sequence of voltage values to the given set of pins assumed to already be in
    /// AnalogOutput mode.
    fn dac_start(
        &self,
        pins: &[&dyn GpioPin],
        clock_tick: Duration,
        waveform: Box<[DacBangEntry]>,
    ) -> Result<Box<dyn GpioDacBangOperation>>;

    /// Convenience method which starts the DAC-banging operation, and blocks until it is
    /// complete.
    fn dac_run(
        &self,
        pins: &[&dyn GpioPin],
        clock_tick: Duration,
        waveform: Box<[DacBangEntry]>,
    ) -> Result<()> {
        let mut dacbang_op = self.dac_start(pins, clock_tick, waveform)?;
        while !dacbang_op.query()? {}
        Ok(())
    }
}

/// Object representing an ongoing operation of generating a prescribed waveform on a number of
/// pins.  Must be frequently polled via the `query()` function, in order to guerantee completion
/// of the operation.
pub trait GpioBitbangOperation<'rd, 'wr> {
    /// Returns `true` when done, `false` means to call it again.
    fn query(&mut self) -> Result<bool>;

    /// After `query()` has returned `true`, this method can be called to get back the array of
    /// `BitbangEntry` originally given, this is particularly useful if there are `WriteOwned` or
    /// `BothOwned` among the entries of the array.
    fn get_result(self: Box<Self>) -> Result<Box<[BitbangEntry<'rd, 'wr>]>>;
}

pub trait GpioDacBangOperation {
    /// Returns `true` when done, `false` means to call it again.
    fn query(&mut self) -> Result<bool>;
}