Testing in hardware
We are finally at a point where we think the code is correct, and all the pieces are implemented. It's time to test it on hardware.
To do so, we need to set up a demo entity which instantiates the state and
output generators, and selects a nice color for them. We'll do this in a
separate file, called src/hw_test.spade
use lib::main::state_gen;
use lib::main::output_gen;
use lib::main::OutputControl;
use lib::main::Timing;
use lib::main::Color;
#[no_mangle]
entity demo(#[no_mangle] clk: clock, #[no_mangle] pmod0: &mut int<6>) {
reg(clk) rst initial(true) = false;
// Our target FPGA, the ecpix5 has a 100 MHz clock.
let t = Timing$(
us280: 28000,
us0_4: 40,
us0_8: 80,
us0_45: 45,
us0_85: 85,
us1_25: 125,
);
let ctrl: OutputControl<int<4>> = inst state_gen(clk, rst, 4, t);
reg(clk) timer: int<32> reset(rst: 0) = if timer > 100_000_000 {
0
}
else {
trunc(timer+1)
};
reg(clk) offset: int<2> reset(rst: 0) = if timer == 0 {
trunc(offset+1)
}
else {
offset
};
let brightness = 64;
let colors = [
Color(brightness, 0, 0),
Color(0, brightness, 0),
Color(0, 0, brightness),
Color(0, brightness, brightness),
];
let with_color = match ctrl {
OutputControl::Ret => OutputControl::Ret(),
OutputControl::Led$(payload: led_num, bit, duration) => {
let led_num = trunc(led_num + sext(offset));
OutputControl::Led$(payload: colors[led_num], bit, duration)
},
};
let pin = output_gen(with_color, t);
set pmod0 = if pin {0b1} else {0};
}
There is not much going on here. Since we're in a different file, we need to
include the stuff defined in the other file. lib
refers to the library we are
currently building, and since our code is in main.spade
, the items are put in
the main
namespace
Since our top module, demo
, is going to connect to the external world,
we'll mark both it and its parameters as #[no_mangle]
. This tells the spade
compiler to name things exactly what they are called in
the Spade code. The downside of this is that we might collide with Verilog
keywords, and the module demo
will not have a namespaced name.
For the output, we also use a &mut int<6>
. &mut
is a mutable wire, i.e. a
wire where we can set a value using set
. It is an int<6>
because the IO
port pmod0
on the ecpix5
board we've been using as an example is 6 bits
wide. The physical pins pmod0
is mapped to is specified in the lpf
file.
The line reg(clk) rst initial(true) = false;
generates a reset signal that is
active the first clock cycle after the FPGA has started.
To generate the output, we create our timing struct, this time with correct timings for the 100 MHz FPGA we're targeting. We use an array to look up color values for each of the LEDs we're going to test, and output those signals.
Then we instantiate everything, and finally set the output pin to the resulting value. Here the LED strip is connected to the first pin of pmod0
We also need to tell the synthesis tool what entity should be our top module; to do so, change the synthesis.top
value in swim.toml
to demo
[synthesis]
top = "demo"
With all that done, we can run swim upload
, and look at our new RGB LEDs.
The pattern is static and boring at the moment, so this is a great opportunity to play around a bit and make the LEDs do something more interesting!
All the code for this project can be found at https://gitlab.com/TheZoq2/ws2812-spade