Interfacing with Verilog
It is often desirable to interface with existing Verilog, either instantiating
a Verilog module inside a Spade project, or including a Spade module as a
component of a larger Verilog project. Both are quite easy to do as long as you
have no generics on the Spade side, and no parameter
s on the Verilog side.
Generics and parameters may be supported in the future.
Instantiating a Verilog module
If you have a Verilog module that you want to instantiate from Spade, you need
to add a stub for it in your Spade project. This is done by defining a
function, entity or pipeline1 but using __builtin__
instead of the body
of the unit. For example,
struct Output {
valid: bool,
value: int<16>
}
entity external_module(clk: clock, x: int<8>) -> Output __builtin__
While this works, Spade will "mangle" names to avoid namespace collisions and collisions with keywords, so this would in practice look for a module like
module \your_project::your_file::external_module(
input clk_i,
input[7:0] x_i,
output[16:0] output__
);
Changing your module to follow this signature would work, but is not very convenient, the more convenient thing is to add #[no_mangle]
to both the module and its inputs in order to use the raw names instead of the mangled names:
#[no_mangle]
entity external_module(
#[no_mangle] clk: clock,
#[no_mangle] x: int<8>
) -> Output __builtin__
Now, the resulting Verilog signature is
module external_module(
input clk_i,
input[7:0] x_i,
output[16:0] output__
);
As you can see it still has a single output__
which is both inconvenient if
you can't change the signature, and annoying since you need to know how Spade
packs struct
s in order to generate the correct signals. Spade currently does
not even define the packing of the structs, so we need to do something about
this. The solution is to use mutable wires to generate Verilog output
s
Changing our module to
#[no_mangle]
entity external_module(
#[no_mangle] clk: clock,
#[no_mangle] x: int<8>
#[mo_mangle] output_valid: &mut bool,
#[no_mangle] output_value: int<16>,
) __builtin__
results in
module external_module(
input clk_i,
input[7:0] x_i,
output output_valid,
output[15:0] output_value
);
which is a normal looking Verilog signature.
One downside of this however, is that the interface to this module isn't very Spadey, so typically you will want to define a wrapper around the external module that provides a more Spade-like interface
use std::ports::new_mut_wire;
use std::ports::read_mut_wire;
// Put the wrapper inside a `mod` to allow defining a spade-native unit of the same name.
mod extern {
#[no_mangle]
entity external_module(
#[no_mangle] clk: clock,
#[no_mangle] x: int<8>
#[mo_mangle] output_valid: &mut bool,
#[no_mangle] output_value: int<16>,
) __builtin__
}
struct Output {
valid: bool,
value: int<16>
}
entity external_module(clk: clock, x: int<8>) -> Output {
let valid = inst new_mut_wire();
let value = inst new_mut_wire();
let _ = inst extern::external_module$(clk, x, output_valid: valid, output_value: value);
Output {
valid: inst read_mut_wire(valid),
value: inst read_mut_wire(value),
}
}
With this, we have the best of both worlds. A canonical spade-entity on the Spade side, and a canonical Verilog module on the other.
Finally, to use the Verilog module in a Spade project, the Verilog file containing the implementation must be specified in swim.toml
under extra_verilog
at the root, or extra_verilog
in the synthesis section. This takes a list of globs that get synthesized with the rest of the project.
See the documentation for units for more
details. Most of the time, you probably want to use entity
for external
Verilog.
Instantiating Spade in a Verilog project
Instantiating Spade in a larger Verilog project is similar to going the other
way around as just described. Mark the Spade unit you want to expose as
#[no_mangle]
on both the unit itself and its inputs. Prefer using &mut
instead of returning output values, as that results in a more Verilog-friendly
interface.
To get the Verilog code, run swim build
, which will generate build/spade.sv
which contains all the Verilog code for the Spade project, including your
exposed module.