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 as extern
. For example,
struct Output {
valid: bool,
value: int<16>
}
extern entity external_module(clk: clock, x: int<8>) -> Output;
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(all)]
to the entity:
#[no_mangle(all)]
extern entity external_module(
clk: clock,
x: int<8>,
output: inv &Output
);
Now, the resulting Verilog signature is
module external_module(
input clk_i,
input[7:0] x_i,
output[16:0] output
);
Spade currently does not define the packing of the structs, so we need to do something about the output__
name that might be changed later.
Therefore, #[no_mangle(all)]
refuses to accept units with return types.
The solution is to use inverted wires to generate Verilog output
s.
Note that you could have also written the module with the less economical
#[no_mangle] extern entity external_module( #[no_mangle] clk: clock, #[no_mangle] x: int<8> ) -> Output;
i.e., manually apply #[no_mangle] to all the parameters.
This has the advantage of allowing a return type, but it's completely useless for the reason stated above, so just use
#[no_mangle(all)]
!
Changing our module to
#[no_mangle(all)]
extern entity external_module(
clk: clock,
x: int<8>
output_valid: inv &bool,
output_value: int<16>,
);
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 verilog {
#[no_mangle(all)]
extern entity external_module(
clk: clock,
x: int<8>
output_valid: inv &bool,
output_value: int<16>,
);
}
struct Output {
valid: bool,
value: int<16>
}
entity external_module(clk: clock, x: int<8>) -> Output {
let (valid, valid_inv) = port;
let (value, value_inv) = port;
let _ = inst verilog::external_module$(clk, x, output_valid: valid_inv, output_value: value_inv);
Output {
valid,
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 verilog
at the root or verilog
in the synthesis section.
[verilog]
include = []
sources = []
[synthesis.verilog]
include = []
sources = []
sources
takes a list of globs that get synthesized with the rest of the project.include
takes a list of directories for Verilog search paths.
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(all)]
. Prefer using inv &
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.