spi 0.1.0

Emulator-focused SPI host controller implementation
Documentation
  • Coverage
  • 89.47%
    17 out of 19 items documented0 out of 15 items with examples
  • Size
  • Source code size: 15.15 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.72 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 13s Average build duration of successful builds.
  • all releases: 13s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • katkosmos/spi-rs
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • katk0smos

spi

An emulator-focused SPI host controller implementation in Rust.

This crate provides a cycle-accurate SPI host controller (SpiHost) and a trait (SpiDevice) for attaching peripheral devices. It is designed for use in hardware emulators where you need to emulate SPI communication between a CPU and one or more peripherals.

Features

  • All four SPI modes (CPOL/CPHA combinations)
  • MSB-first and LSB-first transfer endianness
  • Up to 8 simultaneously attached devices with chip-select per device
  • System clock and external clock inputs
  • Fast transfer mode (FRX) for back-to-back transfers
  • Interrupt support (transmission complete)
  • Tristate MOSI support
  • Software reset via control register
  • SpiDevice blanket impls for Box<dyn SpiDevice>, RefCell<T>, and Rc<RefCell<T>>

Usage

Add to your Cargo.toml:

[dependencies]
spi = "0.1"

Implementing a device

use spi::SpiDevice;

struct MyFlash {
    // ...
}

impl SpiDevice for MyFlash {
    fn select(&mut self, sel: bool) {
        // called when chip select changes
    }

    fn clock(&mut self, mosi: bool) -> bool {
        // called once per clock edge with the MOSI bit
        // return the MISO bit
        false
    }
}

Wiring up the host controller

use spi::SpiHost;

let mut host = SpiHost::new();

// Attach a device; save the returned ID to remove it later
let id = host.add_device(Box::new(MyFlash { /* ... */ })).unwrap();

// In your CPU read/write handlers (4 registers, address bits [1:0]):
//   reg 0 — data (read: RX byte, write: TX byte)
//   reg 1 — control/status
//   reg 2/3 — device select bitmask (one bit per device)
host.write(1, 0b10000000); // software reset
host.write(2, 0b00000001); // select device 0
host.write(0, 0xAB);       // begin transfer of 0xAB

// Drive the system clock from your emulator's clock source
host.sys_clock(true);
host.sys_clock(false);
// ... repeat until transfer completes (STS_TC set in status register)

let rx = host.read(0); // read received byte

// Optionally check for interrupt
if host.int() {
    // handle interrupt
}

Register map

Addr (bits [1:0]) Read Write
0 RX data byte TX data byte (starts transfer)
1 Status register Control register
2–3 Device select bitmask Device select bitmask

Control register bits (write to reg 1):

Bit Name Description
7 SR Software reset (self-clearing)
6 END Endianness: 1 = MSB-first, 0 = LSB-first
5 IER Interrupt enable
4 FRX Fast transfer: auto-reload TX from RX on data read
3 TMO Tristate MOSI (output 0 regardless of data)
2 ECE External clock enable
1 CPOL Clock polarity
0 CPHA Clock phase

Status register bits (read from reg 1):

Bit Name Description
7 TC Transmission complete
6 BSY Transfer in progress
5–0 Mirror of control register bits 5–0

Clock modes

All four standard SPI modes are supported via CPOL and CPHA:

Mode CPOL CPHA Clock idle Sample edge
0 0 0 Low Rising
1 0 1 Low Falling
2 1 0 High Falling
3 1 1 High Rising

License

Licensed under your choice of MIT or Apache-2.0.