A library for building FPGA projects using SpinalHDL, Rust (with Embassy), and FuseSoC.
Spiny has three main components:
1. SpinalHDL library
- VexRiscv CPU profiles for running embedded Rust
- Customizable peripherals with auto-generated Rust boilerplate (and documentation)
- Automated mapping and wiring for peripheral bus and interrupts
- Output system view description (SVD) file
- Generate linker script for SoC memory layout
2. FuseSoC generators
- Automatically generate peripheral access crate (PAC) for Rust from SVD file
- Include Rust firmware build in FuseSoC dependency tree
3. Hardware abstraction layer for Embassy (coming soon)
# Clone the repository
git clone https://github.com/craigjb/spiny.git
cd spiny
# Build firmware and simulate example. Run from spiny's root directory.
fusesoc run --target=sim blinky
# Build example for FPGA (Digilent Nexys A7-100T board)
fusesoc run --build --target=nexys_a7_100t blinkySee examples/blinky for detailed build instructions and expected behavior.
-
Scala Build Tool (sbt) - for SpinalHDL compilation
# Install via SDKMAN (recommended) curl -s "https://get.sdkman.io" | bash sdk install sbt
-
FuseSoC - build system and package management for digital hardware
pip3 install fusesoc
-
Rust & Cargo - for firmware development
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # For example SoC rustup target add riscv32i-unknown-none-elf
See: https://rustup.rs For supported RISC-V extensions and targets, see these Rust docs
-
Verilator - for RTL simulation
# Ubuntu/Debian sudo apt-get install verilator # macOS brew install verilator
-
Xilinx Vivado - for FPGA synthesis, place, route, and bitstream generation
- Download from: https://www.xilinx.com/support/download.html
To use Spiny's SpinalHDL library in your own project, add it as a dependency in your build.sbt:
lazy val spiny = RootProject(uri("https://github.com/craigjb/spiny.git"))
lazy val myProject = (project in file("."))
.dependsOn(spiny)
.settings(
name := "my-project",
scalaVersion := "2.12.18",
// ... other settings
)Then import Spiny components in your Scala code:
import spiny.soc._
import spiny.peripheral._To use the FuseSoC generators, add Spiny to your FuseSoC library:
fusesoc library add spiny https://github.com/craigjb/spiny.gitOr manually add to your fusesoc.conf:
[library.spiny]
sync-uri = https://github.com/craigjb/spiny.git
sync-type = gitHere's a minimal example showing how to create a SoC with a timer and a GPIO peripheral:
import spinal.core._
import spiny.soc._
import spiny.peripheral._
class MyDesign extends Component {
val io = new Bundle {
val SYS_CLK = in(Bool())
val CPU_RESET_N = in(Bool())
}
val sysClkDomain = ClockDomain(
clock = io.SYS_CLK,
reset = ResetCtrl.asyncAssertSyncDeassert(
input = !io.CPU_RESET_N,
clockDomain = ClockDomain(io.SYS_CLK)
),
frequency = FixedFrequency(100 MHz)
)
val soc = sysClkDomain on new SpinySoC(
cpuProfile = SpinyRv32iRustCpuProfile(),
ramSize = 4 kB,
firmwarePath = "firmware.bin"
) {
// Timer with RISC-V machine timer interrupt
val timer = new SpinyTimer(
timerWidth = 32,
prescaleWidth = 16,
numCompares = 1,
isMachineTimer = true
).setName("Timer")
// GPIO with output and input banks
val gpio = new SpinyGpio(
Seq(
SpinyGpioBankConfig(
width = 16,
direction = SpinyGpioDirection.Output,
name = "leds"
),
SpinyGpioBankConfig(
width = 16,
direction = SpinyGpioDirection.Input,
name = "switches"
)
)
).setName("Gpio")
// Export GPIO pins to top-level IO
gpio.getBankBits("leds").toIo().setName("LEDS")
gpio.getBankBits("switches").toIo().setName("SWITCHES")
// Build the SoC with peripherals
build(peripherals = Seq(timer, gpio))
}
}See the complete Blinky example for a full working SoC with firmware.
Spiny provides FuseSoC generators that can be used independently in any FuseSoC project, even without using this SpinalHDL library.
First, add Spiny to your FuseSoC libraries:
fusesoc library add spiny https://github.com/craigjb/spiny.gitOr manually add to your fusesoc.conf:
[library.spiny]
sync-uri = https://github.com/craigjb/spiny.git
sync-type = gitSee the FuseSoC package manager documentation for more details.
Generates Verilog from SpinalHDL by running sbt. Useful for any SpinalHDL-based project.
In your FuseSoC .core file:
generate:
my_rtl:
generator: spinalhdl
parameters:
sbt_dir: "./" # Path to directory containing build.sbt
output_path: "rtl/MyTop.v" # Where to write generated Verilog
main: my.package.TopLevelVerilog # Scala main object to run
file_type: verilogSource # FuseSoC file type for output
args: # Optional arguments to main
- "arg1"
- "arg2"
filesets:
rtl:
depend:
- craigjb:spiny:generatorsThe generator will:
- Run
sbt "runMain <main> <args...>" - Collect generated Verilog
- Make output available to FuseSoC build as the specified file type
Builds Rust firmware projects, and with cargo-binutils installed, handles binary conversion.
In your FuseSoC .core file:
generate:
firmware:
generator: cargo
parameters:
project_dir: "rust" # Directory containing Cargo.toml
args:
- "objcopy"
- "--release"
- "--"
- "-O"
- "binary"
- "target/release/firmware.bin"
filesets:
fw:
depend:
- craigjb:spiny:generatorsThe generator will run cargo <args...> with the arguments specified.
Generates a Rust peripheral access crate (PAC) from an SVD file using svd2rust.
In your FuseSoC .core file:
generate:
pac:
generator: rustpac
parameters:
crate_name: my-pac # Output PAC name
crate_version: 0.1.0 # Output PAC version
output_path: "target/rust/my-pac" # Where to create PAC
svd_path: "target/peripheral.svd" # Path to SVD input
linker_script_path: "target/memory.x" # Optional linker script output
filesets:
pac:
depend:
- craigjb:spiny:generatorsThe generator will:
- Run
svd2rust,form, andrustfmtto create PAC source files - Generate
Cargo.toml,build.rs, and optional linker script
| Peripheral | Description |
|---|---|
| SpinyTimer | Configurable timer with prescaler, multiple compare channels, overflow & compare interrupts, optional machine timer interrupt |
| SpinyGpio | Multi-bank GPIO with configurable direction (input, output, inout) per bank |
| SpinyEthernet | 100BASE-X Ethernet MAC with RMII interface and TX/RX FIFOs |
More coming! All peripherals implement the SpinyPeripheral trait for easy integration into SpinySoC.
See source code in spinal/spiny/peripheral/ for implementation details.
Simple example demonstrating timer interrupts and GPIO control. Features:
- Timer configured as RISC-V machine timer for interrupt-driven operation
- LED sequencing based on switch inputs
- Rust firmware using generated peripheral access crate (PAC)
- Simulation with Verilator and build for FPGA (Digilent Nexys A7 board)
See examples/blinky for complete documentation and build instructions.
spiny/
├── spinal/
│ └── spiny/
│ ├── soc/ # SoC infrastructure (CPU, memory, interconnect)
│ └── peripheral/ # Reusable peripherals (Timer, GPIO, etc.)
├── examples/
│ └── blinky/ # Example SoC with firmware
│ ├── spinal/ # Hardware description
│ ├── rust/ # Rust firmware
│ └── data/ # Constraints and settings
├── generators/ # FuseSoC generator scripts
└── build.sbt # Scala build configuration