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::state_gen;
use lib::output_gen;
use lib::OutputControl;
use lib::Timing;
use lib::Color;
#[no_mangle(all)]
entity demo(clk: clock, led_pin: inv &bool) {
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<uint<2>> = inst state_gen(clk, rst, 2, t);
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) => {
OutputControl::Led$(payload: colors[led_num], bit, duration)
},
};
set led_pin = &output_gen(with_color, t);
}
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 things we defined are defined directly in the lib:: namespace.
If we had put files in another file insrc, they would have been inlib::filename_without_extension::.
Since our top module, demo, is going to connect to the external world, we’ll mark it as #[no_mangle(all)]. 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 so if more than one demo unit is defined in the project, there will be a collision. In this case, we will only have one demo, and none of the names we used are keywords in Verilog so things will work just fine.
For the output, we take an inv &bool. inv & is an inverted wire, i.e. a wire where we can set a value using a set statement. We also need to map this and the clock pin to a physical pin on the FPGA, which is done with a pin mapping file. The file type and format of these files varies between FPGAs.
For the families supported by swim, these files are
- ice40:
.pcf - ecp5:
.lpf - gowin:
.cst
When you initialized the project, you should have received one of these files, update it to map the led_pin pin to the pin you intend to use.
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. Your FPGA likely runs at a different frequency, to compute the timing values, use the formula
f_clk * t
To go from color index to led value, we store the colors in an array and look up the 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.
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](https://gitlab.com/TheZoq2/ws2812-spade)