Tuesday, April 1, 2014

The future of PSHDL (part 1)

With the PSHDL board campaign running, I think it is important to take a look at the future of PSHDL. In a series of posts I will show what I am working on right now and what can be expected to be realized within the next few month.

PSHDL Language features for V0.2

While I am busy with fixing the bugs that are being reported, I am also thinking about the next language features that I want to implement. In this blog entry I want to give a little preview of what I have in mind. Everything mentioned here is work in progress and subject to change, but I would be interested in what you think.

Any width types

One of the things that I see rather frequently are code snippets like this:

in bit<16> addr;
out uint<16> bla;
bla=(uint)addr;

This code does not do what the author intended it do to. It transforms the 16 bit value into a 32 bit integer, and then back to a 16 bit integer. Fortunately the synthesis is smart enough to ignore this, but when you replace 16 with 64, things can get ugly. So a new type will be introduced, the any width type, which allows to write something like this:

in bit<16> addr;
out uint<16> bla;
int<> temp=bla;
bla=(uint<>)addr;

The new type takes the width of the right-hand side and simple changes the value interpretation. It can also be used to create temporary new signals, but those signals are only allowed to be written exactly once, with the declaration.

Records or structs

Sometimes it makes sense to keep things together that belong together. For example an SPI Bus can have an interface like this:

interface SPI {
    in bit miso;
    out bit mosi;
    out bit sclk;
    out bit ss_n;
}

Now, if you want to conect some SPI busses internally, you would need to write something like this:

testbench SPITest {
    SPIMaster dut;
    SPISlave dummy;
    dut.miso=dummy.miso;
    dummy.mosi=dut.mosi;
    dummy.sclk=dut.sclk;
    dummy.ss_n=dut.ss_n;
}

With a record you could do something like this:

testbench SPITest {
    SPIMaster dut;
    SPISlave dummy;
    record SPI bus;
    bus.connectTo(dut);
    dummy.connectTo(bus);
}

Only signals with the same type, width and name are connected. The direction of all signals has to be the same or the opposite of the record. With that rule one might actually write:

testbench SPITest {
    SPIMaster dut;
    SPISlave dummy;
    dut.connectTo(dummy);
}

With the records another new feature can be implemented...

Conditional instances

When you design a library, for example a clock divider IP core, chances are that you will have to use a vendor specific IP core.

interface IClockDivider {
    in bit clk;
    out bit scaledClk;
}

module ClockDivider {
    export record IClockDivider div;
    switch (vendor)
        case Xilinx:
            import xilinx.*;
            PLL pll;
            pll.clock=div.clk;
            div.scaledClk=pll.clkX;
        default:
            assert("Only supporting Xilinx");
    }
}

The export keyword would make the record appear as regular signals on the module. The vendor is an enum, that is defined in pshdl.* namespace, whose value is specified via synthesis settings to the compiler.

Combined declaration and instantiation

Another simplification is that an enum can be declared in and instantiated at the same time. This eases the default case when you want to use your enum for a state-machine immediately.

register enum X {A,B} inst;
interface VHDL.work.BlĂ­nk {
    in bit clock;
    in bit reset;
    out bit led;
} blink;

To the future and beyond!

Another very important feature that is being worked on are re-usable modules. This is something that dedicates its own chapter and will be posted in the future.

No comments:

Post a Comment