Spade 0.6.0
Posted 2023-12-28 by The Spade Developers
This holiday season we're releasing 0.6.0 of Spade. The focus for this release has been fixing small annoyances in the language to make it nicer to work with and more complete.
Unsigned integers🔗
For a long time, Spade has only had one integer type: int
which has been primarily
used for signed integers but has been used to emulate unsigned integers where required.
However, as it turns
out, this results in
incorrect values when additions add additional bits, as is the case for all
additions in Spade.
This finally gave us the motivation to add a proper unsigned type, uint
. It
is a drop-in replacement for the old hacky signed-as-unsigned integer system
and all the basic operators are overloaded to work with both int
and uint
.
This uses a lot of compiler infrastructure that has been added for traits,
which means that user defined operator overloading is on the horizon.
Turbofish🔗
For too long, the Spade language has not had any fish in the language itself.
That changes today! The "turbofish" ::<>
syntax can be
used to specify type parameters for units when they cannot be inferred, for example
trunc::<int<8>, int<6>>(a)
.
Cast functions🔗
The standard library now contains several functions for casting between bit-like types such as integers and arrays of bools.
The star of the show is the new std::conv::unsafe::unsafe_cast
which simply
re-interprets the bits. As the name implies, the guarantees this provides for
most types are pretty much "lmao, good luck", so it is primarily to be used
internally in std::conv::int_to_bits
and std::conv::bits_to_int
which are
safe to call.
For those times where you want to do cursed things with clocks, there is also
std::conv::unsafe::clock_to_bool
and std::conv::unsafe::bool_to_clock
.
Finally, there is now a std::conv::flip_bits
which reverses the bits in an
array, since that is hard to express neatly in Spade without a builtin
function.
Enum variant ergonomics🔗
In rust, if you instantiate an enum variant that has no parameters, like None
, you don't have to call it like a function, i.e. None()
.
The same thing now applies in Spade, making the language feel more familiar to
rust users, and a bit more consistent, since the parentheses were already not
needed in match patterns.
To avoid massive breaking changes, the old style is also supported
Array range indexing🔗
You can now index arrays by ranges, as long as the ranges are statically known. For example: the following code now compiles and runs without error
let a = [1, 2, 3, 4];
assert a[1:3] == [2, 3];
Block comments🔗
3 years in, Spade finally has block comments naturally denoted by /*
and
*/
. Of course, block comments can be nested, unlike some languages
Call by name🔗
A internal change has been made to the way the compiler instantiates units. In the resulting Verilog code, arguments were previously passed positionally like this:
subunit unit_0(arg1, arg2, arg3);
In 0.6.0 they are now instead instantiated by name
subunit unit_0(.param1(arg1), .param2(arg2), .param3(arg3));
While this makes no difference for Spade code where the compiler knows the argument order, it did cause problems when instantiating external Verilog like vendor primitives, or VHDL code where argument order is less well known.
As an example, the following code now works as expected whereas it would behave incorrectly before
#[no_mangle]
fn external_verilog_module(first_arg: bool, second_arg: bool, result: &mut bool) __builtin__
module external_verilog_module(input second_arg_i, input first_arg_i, output result_o);
// ...
endmodule