wishbone-tool 0.7.3

A library and command line program to control a Wishbone bus of an embedded device
Documentation

wishbone-tool - All-in-one Wishbone Binary and Library

wishbone-tool is useful for interacting with the internal Wishbone bridge on a device.

Some of the things you can use wishbone-tool for:

  • Peeking and poking memory, similar to using devmem2
  • Testing memory and bridge link quality
  • Exposing a Wishbone bridge to Ethernet
  • Attaching a GDB server to a softcore

Currently-supported Wishbone bridges include:

  • USB - For use with Valentyusb such as on Fomu
  • Serial - Generic UART, nominally running at 115200 (but can be changed with --baud)
  • SPI - Using 2-, 3-, or 4-wire SPI from spibone
  • Ethernet - Both TCP (e.g. a remote copy of wishbone-tool) or UDP (via Etherbone)
  • PCI Express - Using a PCIe softcore with the CSR register bank exposed

Binaries

Precompiled versions of wishbone-tool can be found in the Releases section.

Building

To build wishbone-tool:

  1. Install Rust and Cargo. The easiest way to do this is to go to https://rustup.rs/ and follow the instructions.
  2. Enter the wishbone-tool directory.
  3. Run cargo build or cargo build --release

The wishbone-tool binary will be located under target/debug/ or target/release/.

Usage

By default, wishbone-tool will communicate via USB, attempting to open a device with PID 0x5bf0. It will also run the peek/poke server by default.

USB Bridge

Simply run wishbone-tool [ADDRESS] to peek at a particular address. To specify a particular vendor ID, pass --vid [ID], for example --vid 0xb0f1. To read from an area of memory (such as 0x10000000), run:

$ # Read from address 0x10000000 via USB
$ wishbone-tool 0x10000000
INFO [wishbone_tool::usb_bridge] waiting for target device
INFO [wishbone_tool::usb_bridge] opened USB device device 019 on bus 001
Value at 00000000: 6f80106f
$

To write a value to memory, add an additional parameter:

$ wishbone-tool 0x10000000 0x12345678
INFO [wishbone_tool::usb_bridge] opened USB device device 019 on bus 001
$ wishbone-tool 0x10000000
INFO [wishbone_tool::usb_bridge] opened USB device device 019 on bus 001
Value at 00000000: 12345678
$

Serial Bridge

You can connect to a serial port by specifying the --serial argument:

$ wishbone-tool --serial COM4: 0x00000000
Value at 00000000: ffffffff
$ wishbone-tool --serial /dev/ttyUSB0 0x00000000
Value at 00000000: ffffffff

Ensure that you have write permission to the serial port. On some Linux systems you may need to add your user to the dialout group.

Ethernet Bridge

To connect to an Ethernet device, pass the --ethernet-host parameter:

$ wishbone-tool --ethernet 192.168.100.50 0x00000000
Value at 00000000: ffffffff

To connect to a different port, add --ethernet-port PORT_NUMBER. Finally, if you would like to connect to another copy of wishbone-tool or to a copy of lxserver, add --ethernet-tcp to switch the connection from Etherbone to TCP.

PCIe Bridge

If your device is connected via PCI Express, you can specify a PCIe BAR with --pcie-bar FILE_PATH. This will be a device under /sys/bus.

Note that when running in PCIe mode, only a small portion of the memory space is exposed. This means that you may need to specify --register-offset OFFSET, because e.g. address 0 in the PCIe BAR may actually correspond to address 0xe0000000, and wishbone-tool needs to know how to perform the translation.

SPI Bridge

If you specify --spi-pins, wishbone-tool will communicate with the target device via SPI. This is currently only supported on Raspberry Pi. Specify the physical Broadcom Pin numbers. Consult Pinout.xyz for more details. For example, assume you want to connect COPI,CPIO,CLK, and CS_N to pins 3,5,7, and 12 on the Raspberry Pi header. If you consult that website, you'll see pin 3 is BCM2, pin 5 is BCM3, pin 7 is BCM4, and pin 12 is BCM18. Therefore, the argument you would provide to wishbone-tool is --spi-pins 2,3,4,18

Crossover UART

If your bridge is over a UART, then that means your UART is already in use, and isn't available for use as a console. Or if you're connecting via some other medium and you only have a single cable connecting the two devices. LiteX supports creating a "crossover" UART that wishbone-tool can interact with and present a local terminal on.

To add a UART bridge and a crossover UART to your design, instantiate the main SoC object with uart_name="crossover" and add a separate Wishbone bridge.

   class MySoC(SoCCore):
      def __init__(self, platform, sys_clk_freq):
         SoCCore.__init__(self, platform, sys_clk_freq, uart_name="crossover")

         # Add a bridge with the real UART pins
         self.submodules.uart_bridge = UARTWishboneBridge(
               platform.request("serial"),
               sys_clk_freq,
               baudrate=115200)
         self.add_wb_master(self.uart_bridge.wishbone)

Then, to interact with the terminal, run wishbone-tool and provide it with the csr.csv file from your build, and add the -s terminal flag:

$ wishbone-tool -s terminal --csr-csv build/csr.csv

Note that you can run multiple wishbone-tool servers at the same time. For example, to run the gdb server as well, run:

$ wishbone-tool -s gdb -s terminal --csr-csv build/csr.csv

To exit the session, press Ctrl-C.

GDB Server

If your softcore has a Vexriscv CPU in it, you can enable debug mode and use wishbone-tool to act as a gdbserver.

Command line Auto-Completion

You can generate auto-completion for wishbone-tool with the -c option. For example, to generate auto-completion for bash, run:

$ wishbone-tool -c bash > wishbone-tool.bash
$ . wishbone-tool.bash
$

Auto-completion is available for zsh, bash, fish, powershell, and elvish.

wishbone-tool as a Library

You can also use wishbone-tool as a library from within your own program.

For example, there is a kind of device that has a USB bridge with a small random number generator at address 0xf001_7000. This device has a simple API:

  1. Write 1 to 0xf001_7000 to enable the device
  2. When 0xf001_7008 is 1 there is data available
  3. Read the data from 0xf001_7004
  4. Goto 2

We can turn this into a command that reads from this RNG and prints to stdout:

use std::io::{self, Write};
use wishbone_tool::{Bridge, BridgeError, BridgeKind, Config, UsbBridgeConfig};

fn main() -> Result<(), BridgeError> {
    let stdout = io::stdout();
    let mut handle = stdout.lock();

    // Create a configuration object with a USB bridge that
    // connects to a device with the product ID of 0x5bf0.
    let mut cfg = Config::default();
    cfg.bridge_kind = BridgeKind::UsbBridge(UsbBridgeConfig {
        pid: Some(0x5bf0),
        ..Default::default()
    });

    // Create the USB bridge and connect to it.
    let bridge = Bridge::new(&cfg)?;
    bridge.connect()?;

    // Enable the oscillator. Note that this address may change,
    // so consult the `csr.csv` for your device.
    bridge.poke(0xf001_7000, 1)?;

    loop {
        // Wait until the `Ready` flag is `1`
        while bridge.peek(0xf001_7008)? & 1 == 0 {}

        // Read the random word and write it to stdout
        handle
            .write_all(&bridge.peek(0xf001_7004)?.to_le_bytes())
            .unwrap();
    }
}