Table of Contents
Spade
Spade is a new hardware description language which makes hardware description easier and less error-prone. It does this by taking lessons from software programming languages, and adding language level support for common hardware constructs, all without compromising low level control over what hardware gets generated.
Key Features
Language Level Pipelines
Pipelines are a first class construct in Spade, making re-timing and
re-pipelining trivial. The reg
separates code into stages, meaning you never
have to manually define pipeline registers, and that behavior is separated
from physical implementation.
When you need to update your design to meet timing, you can simply add or move
reg
statements. The compiler is
smart enough to figure out when these changes affect the timing of instantiating
modules and tells you where to update your code to keep the original behavior.
pipeline(4) X(clk: clk, a: int<32>, b: int<32>) -> int<64> {
let product = a * b;
reg * 3;
let result = f(a, product);
reg;
result
}
For more complex pipelines with feedback, such as a processor data path, re-timing becomes less trivial, but being able to reason about signals in stages instead of individual registers is still very helpful.
Types and Enums
Spade has a powerful type system with structs, arrays, tuples and sum types
called enums
.
A strong type system makes interoperability with external or internal modules
easier, and means you can refactor your code with confidence. The compiler will
tell you where your changes affect the behavior of your code.
Enums are of particular importance: unlike the enums of C and Verilog, they can have associated payload.
You can model a value which may or may not be available with the Option
enum:
enum Option<T> {
Some(val: T),
None
}
Or why not model the instruction set of a CPU?
enum Insn {
Set { dreg: int<5>, val: int<32> },
Add { dreg: int<5>, lhs: int<5>, rhs: int<5> },
Sub { dreg: int<5>, lhs: int<5>, rhs: int<5> },
Jump { target: int<32> }
}
The compiler ensures that fields can only be accessed if the value is of the
correct type. For example, if the instruction is a Jump
, there is no way to
access a dreg
field.
Pattern Matching
Enums work really well with pattern matching which allows you to check conditions and easily bind sub-values to variables.
You can easily build an ALU:
fn alu(insn: Insn, rs1: int<32>, rs2: int<32>) -> int<32> {
let adder_result = match insn {
Insn::Add(_, _, _) => rs1 + rs2,
Insn::Sub(_, _, _) => rs1 - rs2,
Insn::Set(_, val) => sext(val),
Insn::Jump(_) => 0,
};
trunc(adder_result)
}
Or select the first of two values, and 0 if none are present:
let result = match (a, b) {
(Some (x), _) => x,
(_, Some(x)) => x,
_ => 0
}
The compiler makes sure that all cases are covered. For example, if you add a
new instruction to the Insn
type, it will force you to deal with that case in
the ALU.
Type inference
Spade has powerful type inference which gives you the benefits of static types, without all the typing.
Great Error Messages
The compiler should be your friend. Error messages give you as much information as possible
error: Match branches have incompatible type
30 +-Insn::Add(_, _, _) => rs1 + rs2,
| ^^^^^^^^^
| This branch has type int<33>
32 | Insn::Set(_, val) => val,
| ^^^ But this one has type int<32>
|
= Expected: 33 in: int<33>
= Got: 32 in: int<32>
Bad error messages are considered a bug. Please report them!
Helpful tooling
Spade comes with a great set of tools around the language
- The official build tool Swim manages
your dependencies, calls synthesis tools and runs your tests. It can even
create a new project for you with a single command!
- Of course you can put the generated Verilog into your regular build flow as well.
- Tests are written in cocotb allowing you to take advantage of python for all your testing needs. When you need higher performance tests, you can also use verilator
- VCDs coming out of tests are automatically translated to include Spade type information so you never have to decode individual bits in your waveform.
Planned features
There are also some planned features:
- Integer ranges as types.
- Generics with traits
- Clock domain information on types.
- Mixing domains without explicit synchronization is a compilation error.
- Related: clock domain inference where the domain is obvious.
- And more...
Using Spade
To get started, read the (work in progress) spade book.
For an overview of what Spade is, have a look at this talk from OSDA 2023. Documentation is very much a work in progress, but some is available in our book.
Spade is in its early stages, so everything is subject to change. You can build things with it but be prepared for bugs and missing features.
Learn more
Feel free to follow the development either on Gitlab or in our Discord community server.
Publications
To cite Spade itself, use the zenodo record.
- Frans Skarman, Lucas Klemmer Oscar Gustafsson Daniel Große. Enhancing Compiler-Driven HDL Design with Automatic Waveform Analysis. September 2023. In: Forum on specification & Design Languages (FDL).
- Frans Skarman, Oscar Gustafsson. Spade: An Expression-Based HDL With Pipelines. April 2023. In: 3rd Workshop on Open-Source Design Automation (OSDA).
- Frans Skarman, Oscar Gustafsson. Abstraction in the Spade Hardware Description Language. March 2023. In: 3rd Workshop on Languages, Tools, and Techniques for Accelerator Design (LATTE).
- Frans Skarman, Oscar Gustafsson. Spade: An HDL Inspired by Modern Software Languages. August 2022. In: 32nd International Conference on Field-Programmable Logic and Applications (FPL).
Talks
- Frans Skarman Spade - An HDL Inspired by Modern Software Languages. May 2024. At: LatchUp 2024.
Development
Spade is currently being developed as an Open Source project at the Division of Computer Engineering, Department of Electrical Engineering, Linköping University, Sweden.
License
The Spade compiler and other tooling is licensed under the EUPL-1.2 license. The Spade standard library and this website is licensed under the terms of both the MIT license and the Apache license.