Skip to main content

wishbone_bridge/
lib.rs

1//! # Wishbone Bridges
2//!
3//! Wishbone is an internal bus that runs on-chip. It provides memory-based
4//! interconnect between various hardware modules. Wishbone buses frequently
5//! contain memories such as RAM and ROM, as well as memory-mapped peripherals.
6//!
7//! By accessing these memories remotely, a target device may be examined or
8//! tested by a host.
9//!
10//! Wishbone may be bridged from a target to a host using a variety of protocols.
11//! This library supports different protocols depending on what features are
12//! enabled. By default, all supported protocols are enabled.
13//!
14//! Creating a Wishbone `Bridge` object involves first creating a configuration
15//! struct that describes the connection mechanism, and then calling `.create()`
16//! on that struct to create the `Bridge`. For example, to create a USB Bridge
17//! using the USB PID `0x1234`, peek memory at address 0, and poke the value
18//! `0x12345678` into address `0x20000000`, you would use a `UsbBridge` like this:
19//!
20//! ```no_run
21//! use wishbone_bridge::UsbBridge;
22//! let bridge = UsbBridge::new().pid(0x1234).create().unwrap();
23//! println!("Memory at address 0: {:08x}", bridge.peek(0).unwrap());
24//! bridge.poke(0x2000_0000, 0x1234_5678).unwrap();
25//! ```
26//!
27//! Creating other bridges is done in a similar manner -- see their individual
28//! pages for more information.
29
30#[cfg(not(any(
31    feature = "pcie",
32    feature = "uart",
33    feature = "spi",
34    feature = "ethernet",
35    feature = "usb"
36)))]
37compile_error!("Must enable at least one bridge type: pcie, uart, spi, ethernet, or usb");
38
39pub(crate) mod bridges;
40
41#[doc(hidden)]
42#[cfg(feature = "ethernet")]
43pub use bridges::ethernet::EthernetBridgeInner;
44#[doc(hidden)]
45#[cfg(feature = "pcie")]
46pub use bridges::pcie::PCIeBridgeInner;
47#[doc(hidden)]
48#[cfg(feature = "spi")]
49pub use bridges::spi::SpiBridgeInner;
50#[doc(hidden)]
51#[cfg(feature = "uart")]
52pub use bridges::uart::UartBridgeInner;
53#[doc(hidden)]
54#[cfg(feature = "usb")]
55pub use bridges::usb::UsbBridgeInner;
56
57#[cfg(feature = "ethernet")]
58pub use bridges::ethernet::{EthernetBridge, EthernetBridgeProtocol};
59#[cfg(feature = "pcie")]
60pub use bridges::pcie::PCIeBridge;
61#[cfg(feature = "spi")]
62pub use bridges::spi::SpiBridge;
63#[cfg(feature = "uart")]
64pub use bridges::uart::UartBridge;
65#[cfg(feature = "usb")]
66pub use bridges::usb::UsbBridge;
67
68use log::debug;
69
70use std::io;
71use std::sync::{Arc, Mutex};
72
73#[doc(hidden)]
74#[derive(Clone)]
75/// A `BridgeConfig` describes the configuration of a bridge that has
76/// not yet been opened.
77pub enum BridgeConfig {
78    /// An unconfigured `BridgeConfig`. Attempts to use this will return
79    /// an `Err(NoBridgeSpecified)`, so this value exists so that `Default`
80    /// may be implemented.
81    None,
82
83    /// Describes a bridge that connects via Ethernet, either via UDP
84    /// (for direct hardware connections) or TCP (for connecting to
85    /// other Wishbone servers such as `litex_server` or `wishbone-tool`)
86    #[cfg(feature = "ethernet")]
87    EthernetBridge(EthernetBridge),
88
89    /// Describes a connection to a device via a PCIe bridge. Unlike most
90    /// other bridges, a PCIe bridge does not provide a complete view of
91    /// the memory space.
92    #[cfg(feature = "pcie")]
93    PCIeBridge(PCIeBridge),
94
95    /// Describes a connection to a device via SPI wires.
96    #[cfg(feature = "spi")]
97    SpiBridge(SpiBridge),
98
99    /// Describes a connection to a device via a serial or other UART port.
100    #[cfg(feature = "uart")]
101    UartBridge(UartBridge),
102
103    /// Describes a connection to a device via USB.
104    #[cfg(feature = "usb")]
105    UsbBridge(UsbBridge),
106}
107
108#[doc(hidden)]
109#[derive(Clone)]
110pub enum BridgeCore {
111    #[cfg(feature = "ethernet")]
112    EthernetBridge(EthernetBridgeInner),
113    #[cfg(feature = "pcie")]
114    PCIeBridge(PCIeBridgeInner),
115    #[cfg(feature = "spi")]
116    SpiBridge(SpiBridgeInner),
117    #[cfg(feature = "uart")]
118    UartBridge(UartBridgeInner),
119    #[cfg(feature = "usb")]
120    UsbBridge(UsbBridgeInner),
121}
122
123/// Bridges represent the actual connection to the device. You must create
124/// a Bridge by constructing a configuration from the relevant
125/// configuration type, and then calling `create()`.
126///
127/// For example, to create a USB bridge, use the `UsbBridge` object:
128///
129/// ```
130/// use wishbone_bridge::UsbBridge;
131/// let mut bridge_config = UsbBridge::new();
132/// let bridge = bridge_config.pid(0x1234).create().unwrap();
133/// ```
134#[derive(Clone)]
135pub struct Bridge {
136    /// Implementation-specific bridge core
137    core: BridgeCore,
138
139    /// Current offset for `Read` and `Write` operations
140    offset: usize,
141
142    /// A Mutex to enforce only a single operation at a time
143    mutex: Arc<Mutex<()>>,
144}
145
146/// Errors that are generated while creating or using the Wishbone Bridge.
147#[derive(Debug)]
148pub enum BridgeError {
149    /// No bridge was specified (i.e. it was None)
150    NoBridgeSpecified,
151
152    /// Expected one size, but got another
153    LengthError(usize, usize),
154
155    /// USB subsystem returned an error
156    #[cfg(feature = "usb")]
157    USBError(libusb_wishbone_tool::Error),
158
159    /// std::io error
160    IoError(io::Error),
161
162    /// Attempted to communicate with the bridge, but it wasn't connected
163    NotConnected,
164
165    /// The address or path was incorrect
166    InvalidAddress,
167
168    /// We got something weird back from the bridge
169    WrongResponse,
170
171    /// Requested protocol is not supported on this platform
172    #[allow(dead_code)]
173    ProtocolNotSupported,
174
175    /// We got nothing back from the bridge
176    #[allow(dead_code)]
177    Timeout,
178}
179
180impl ::std::fmt::Display for BridgeError {
181    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
182        use BridgeError::*;
183        match self {
184            LengthError(expected, actual) => {
185                write!(f, "expected {} bytes, but got {} instead", expected, actual)
186            }
187            #[cfg(feature = "usb")]
188            USBError(e) => write!(f, "libusb error {}", e.strerror()),
189            IoError(e) => write!(f, "io error {}", e),
190            NoBridgeSpecified => write!(f, "no bridge was specified"),
191            NotConnected => write!(f, "bridge not connected"),
192            WrongResponse => write!(f, "wrong response received"),
193            InvalidAddress => write!(f, "bad address or path"),
194            ProtocolNotSupported => write!(f, "protocol not supported on this platform"),
195            Timeout => write!(f, "connection timed out"),
196        }
197    }
198}
199
200#[cfg(feature = "usb")]
201impl std::convert::From<libusb_wishbone_tool::Error> for BridgeError {
202    fn from(e: libusb_wishbone_tool::Error) -> BridgeError {
203        BridgeError::USBError(e)
204    }
205}
206
207impl std::convert::From<io::Error> for BridgeError {
208    fn from(e: io::Error) -> BridgeError {
209        BridgeError::IoError(e)
210    }
211}
212
213impl Bridge {
214    /// Create a new Bridge with the specified configuration. The new bridge
215    /// starts out in a Disconnected state, but may be connecting in the background.
216    /// To ensure the bridge is connected, so you must call `connect()`.
217    pub(crate) fn new(bridge_cfg: BridgeConfig) -> Result<Bridge, BridgeError> {
218        let mutex = Arc::new(Mutex::new(()));
219        match &bridge_cfg {
220            BridgeConfig::None => Err(BridgeError::NoBridgeSpecified),
221            #[cfg(feature = "ethernet")]
222            BridgeConfig::EthernetBridge(bridge_cfg) => Ok(Bridge {
223                mutex,
224                core: BridgeCore::EthernetBridge(EthernetBridgeInner::new(bridge_cfg)?),
225                offset: 0,
226            }),
227            #[cfg(feature = "pcie")]
228            BridgeConfig::PCIeBridge(bridge_cfg) => Ok(Bridge {
229                mutex,
230                core: BridgeCore::PCIeBridge(PCIeBridgeInner::new(bridge_cfg)?),
231                offset: 0,
232            }),
233            #[cfg(feature = "spi")]
234            BridgeConfig::SpiBridge(bridge_cfg) => Ok(Bridge {
235                mutex,
236                core: BridgeCore::SpiBridge(SpiBridgeInner::new(bridge_cfg)?),
237                offset: 0,
238            }),
239            #[cfg(feature = "uart")]
240            BridgeConfig::UartBridge(bridge_cfg) => Ok(Bridge {
241                mutex,
242                core: BridgeCore::UartBridge(UartBridgeInner::new(bridge_cfg)?),
243                offset: 0,
244            }),
245            #[cfg(feature = "usb")]
246            BridgeConfig::UsbBridge(bridge_cfg) => Ok(Bridge {
247                mutex,
248                core: BridgeCore::UsbBridge(UsbBridgeInner::new(bridge_cfg)?),
249                offset: 0,
250            }),
251        }
252    }
253
254    /// Ensure the bridge is connected. Many bridges support performing connection
255    /// in the background, so calling `connect()` ensures that the bridge has been
256    /// established.
257    pub fn connect(&self) -> Result<(), BridgeError> {
258        let _mtx = self.mutex.lock().unwrap();
259        match &self.core {
260            #[cfg(feature = "ethernet")]
261            BridgeCore::EthernetBridge(b) => b.connect(),
262            #[cfg(feature = "pcie")]
263            BridgeCore::PCIeBridge(b) => b.connect(),
264            #[cfg(feature = "spi")]
265            BridgeCore::SpiBridge(b) => b.connect(),
266            #[cfg(feature = "uart")]
267            BridgeCore::UartBridge(b) => b.connect(),
268            #[cfg(feature = "usb")]
269            BridgeCore::UsbBridge(b) => b.connect(),
270        }
271    }
272
273    /// Read a single 32-bit value from the target device.
274    /// ```no_run
275    /// use wishbone_bridge::UsbBridge;
276    /// let mut bridge_config = UsbBridge::new();
277    /// let bridge = bridge_config.pid(0x5bf0).create().unwrap();
278    /// println!("The value at address 0 is: {:08x}", bridge.peek(0).unwrap());
279    /// ```
280    pub fn peek(&self, addr: u32) -> Result<u32, BridgeError> {
281        let _mtx = self.mutex.lock().unwrap();
282        loop {
283            let result = match &self.core {
284                #[cfg(feature = "ethernet")]
285                BridgeCore::EthernetBridge(b) => b.peek(addr),
286                #[cfg(feature = "pcie")]
287                BridgeCore::PCIeBridge(b) => b.peek(addr),
288                #[cfg(feature = "spi")]
289                BridgeCore::SpiBridge(b) => b.peek(addr),
290                #[cfg(feature = "uart")]
291                BridgeCore::UartBridge(b) => b.peek(addr),
292                #[cfg(feature = "usb")]
293                BridgeCore::UsbBridge(b) => b.peek(addr),
294            };
295            #[allow(unreachable_code)] // Only possible when no features are enabled (compile error)
296            if let Err(e) = result {
297                #[cfg(feature = "usb")]
298                if let BridgeError::USBError(libusb_wishbone_tool::Error::Pipe) = e {
299                    debug!("USB device disconnected, forcing early return");
300                    return Err(e);
301                }
302                debug!("Peek failed, trying again: {:?}", e);
303            } else {
304                return result;
305            }
306        }
307    }
308
309    /// Write a single 32-bit value into the specified address.
310    /// ```no_run
311    /// use wishbone_bridge::UsbBridge;
312    /// let mut bridge_config = UsbBridge::new();
313    /// let bridge = bridge_config.pid(0x5bf0).create().unwrap();
314    /// // Poke 0x12345678 into the target device at address 0
315    /// bridge.poke(0, 0x12345678).unwrap();
316    /// ```
317    pub fn poke(&self, addr: u32, value: u32) -> Result<(), BridgeError> {
318        let _mtx = self.mutex.lock().unwrap();
319        loop {
320            let result = match &self.core {
321                #[cfg(feature = "ethernet")]
322                BridgeCore::EthernetBridge(b) => b.poke(addr, value),
323                #[cfg(feature = "pcie")]
324                BridgeCore::PCIeBridge(b) => b.poke(addr, value),
325                #[cfg(feature = "spi")]
326                BridgeCore::SpiBridge(b) => b.poke(addr, value),
327                #[cfg(feature = "uart")]
328                BridgeCore::UartBridge(b) => b.poke(addr, value),
329                #[cfg(feature = "usb")]
330                BridgeCore::UsbBridge(b) => b.poke(addr, value),
331            };
332            #[allow(unreachable_code)] // Only possible when no features are enabled (compile error)
333            if let Err(e) = result {
334                match e {
335                    #[cfg(feature = "usb")]
336                    BridgeError::USBError(libusb_wishbone_tool::Error::Pipe) => {
337                        debug!("USB device disconnected (Windows), forcing early return");
338                        return Err(e);
339                    }
340                    #[cfg(feature = "usb")]
341                    BridgeError::USBError(libusb_wishbone_tool::Error::Io) => {
342                        debug!("USB device disconnected (Posix), forcing early return");
343                        return Err(e);
344                    }
345                    _ => {}
346                }
347                debug!("Poke failed, trying again: {:?}", e);
348            } else {
349                return result;
350            }
351        }
352    }
353
354    pub fn burst_read(&self, addr: u32, length: u32) -> Result<Vec<u8>, BridgeError> {
355        let _mtx = self.mutex.lock().unwrap();
356        loop {
357            let result = match &self.core {
358                #[cfg(feature = "ethernet")]
359                BridgeCore::EthernetBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
360                #[cfg(feature = "pcie")]
361                BridgeCore::PCIeBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
362                #[cfg(feature = "spi")]
363                BridgeCore::SpiBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
364                #[cfg(feature = "uart")]
365                BridgeCore::UartBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
366                #[cfg(feature = "usb")]
367                BridgeCore::UsbBridge(b) => b.burst_read(addr, length),
368            };
369            #[allow(unreachable_code)] // Only possible when no features are enabled (compile error)
370            if let Err(e) = result {
371                #[cfg(feature = "usb")]
372                if let BridgeError::USBError(libusb_wishbone_tool::Error::Pipe) = e {
373                    debug!("USB device disconnected, forcing early return");
374                    return Err(e);
375                }
376                debug!("Peek failed, trying again: {:?}", e);
377            } else {
378                return result;
379            }
380        }
381    }
382
383    pub fn burst_write(&self, addr: u32, data: &Vec<u8>) -> Result<(), BridgeError> {
384        let _mtx = self.mutex.lock().unwrap();
385        loop {
386            let result = match &self.core {
387                #[cfg(feature = "ethernet")]
388                BridgeCore::EthernetBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
389                #[cfg(feature = "pcie")]
390                BridgeCore::PCIeBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
391                #[cfg(feature = "spi")]
392                BridgeCore::SpiBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
393                #[cfg(feature = "uart")]
394                BridgeCore::UartBridge(_b) => return Err(BridgeError::ProtocolNotSupported),
395                #[cfg(feature = "usb")]
396                BridgeCore::UsbBridge(b) => b.burst_write(addr, data),
397            };
398            #[allow(unreachable_code)] // Only possible when no features are enabled (compile error)
399            if let Err(e) = result {
400                #[cfg(feature = "usb")]
401                if let BridgeError::USBError(libusb_wishbone_tool::Error::Pipe) = e {
402                    debug!("USB device disconnected, forcing early return");
403                    return Err(e);
404                }
405                debug!("Peek failed, trying again: {:?}", e);
406            } else {
407                return result;
408            }
409        }
410    }
411}
412
413impl std::io::Read for Bridge {
414    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
415        let _mtx = self.mutex.lock().unwrap();
416        let addr = self.offset as _;
417        use std::convert::TryInto;
418        use std::io::{Error, ErrorKind};
419
420        fn fill_array(src: &[u8], dest: &mut [u8]) -> usize {
421            let mut fill_bytes = 0;
422            for (s, d) in src.iter().zip(dest) {
423                *d = *s;
424                fill_bytes += 1;
425            }
426            fill_bytes
427        }
428
429        let copied = match &self.core {
430            #[cfg(feature = "ethernet")]
431            BridgeCore::EthernetBridge(b) => {
432                b.peek(addr).map(|v| fill_array(&v.to_le_bytes(), buf))
433            }
434            #[cfg(feature = "pcie")]
435            BridgeCore::PCIeBridge(b) => b.peek(addr).map(|v| fill_array(&v.to_le_bytes(), buf)),
436            #[cfg(feature = "spi")]
437            BridgeCore::SpiBridge(b) => b.peek(addr).map(|v| fill_array(&v.to_le_bytes(), buf)),
438            #[cfg(feature = "uart")]
439            BridgeCore::UartBridge(b) => b.peek(addr).map(|v| fill_array(&v.to_le_bytes(), buf)),
440            #[cfg(feature = "usb")]
441            BridgeCore::UsbBridge(b) => b
442                .burst_read(addr, buf.len().try_into().unwrap())
443                .map(|v| fill_array(&v, buf)),
444        }
445        .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
446        self.offset += copied;
447        Ok(copied)
448    }
449}
450
451impl std::io::Seek for Bridge {
452    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
453        use std::convert::TryInto;
454        use std::io::{Error, ErrorKind};
455        let new_offset = match pos {
456            std::io::SeekFrom::End(_) => Err(Error::new(
457                ErrorKind::AddrNotAvailable,
458                "cannot seek from end",
459            ))?,
460            std::io::SeekFrom::Current(add) => {
461                if add > 0 {
462                    self.offset + (add as usize)
463                } else {
464                    self.offset - (-add as usize)
465                }
466            }
467            std::io::SeekFrom::Start(offset) => offset as usize,
468        };
469        self.offset += new_offset;
470        Ok(self.offset.try_into().unwrap())
471    }
472}
473
474impl std::io::Write for Bridge {
475    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
476        use std::convert::TryInto;
477        use std::io::{Error, ErrorKind};
478        let _mtx = self.mutex.lock().unwrap();
479
480        fn slice_to_u32(buf: &[u8]) -> std::io::Result<u32> {
481            if buf.len() < 3 {
482                Err(Error::new(
483                    ErrorKind::InvalidData,
484                    "data not a multiple of 4 bytes",
485                ))?;
486            }
487            Ok(u32::from_le_bytes(buf[0..3].try_into().unwrap()))
488        }
489
490        let addr = self.offset as _;
491        let bytes_written = match &self.core {
492            #[cfg(feature = "ethernet")]
493            BridgeCore::EthernetBridge(_) => self.poke(addr, slice_to_u32(buf)?).map(|_| 4),
494            #[cfg(feature = "pcie")]
495            BridgeCore::PCIeBridge(_) => self.poke(addr, slice_to_u32(buf)?).map(|_| 4),
496            #[cfg(feature = "spi")]
497            BridgeCore::SpiBridge(_) => self.poke(addr, slice_to_u32(buf)?).map(|_| 4),
498            #[cfg(feature = "uart")]
499            BridgeCore::UartBridge(_) => self.poke(addr, slice_to_u32(buf)?).map(|_| 4),
500            #[cfg(feature = "usb")]
501            BridgeCore::UsbBridge(b) => b.burst_write(addr, buf).map(|_| buf.len()),
502        }
503        .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?;
504        self.offset += bytes_written;
505        Ok(bytes_written)
506    }
507
508    fn flush(&mut self) -> std::io::Result<()> {
509        Ok(())
510    }
511}