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 instanciates 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