[BSV] Types and Combinational Circuits
Variables in BSV
A variable in BSV (Bluespec SystemVerilog), like in Verilog or other HDLs, represent a wire in the circuit. Variable assignment expressions like b=10; and bsq = b*b; describe the data flow in the circuit. Note that BSV is not a computer programming language. In describing data flow, there is nothing dynamic; you just describe the static structure of the circuit. For example, when you reassign values to a variable, like:
b = 10; b = b*b; a = 1; for(int k = 0; k < 10; k=k+1) a = a+k;
You don't 'update' the value of the wire. Rather, you create a new wire and newly assign it to the variable name. In other words, the code's behavior is more like:
b0 = 10; b1 = b0*b0; a0 = 1; k0 = 0; a1 = a0+k0; k1 = k0+1; ... a10 = a9+k9;
In writing in BSV, we should always think how the code will be implemented into the hardware structure.
Types
An expression belongs to its type, and the type is unique for every expression. BSV's strong typing redcing bugs, and makes the hardware description more readible. A type is a grouping of values. Bit is the most basic type, which carries 0 or 1.
In BSV, types can be parameterized. Parameterized types are indicated by the '#' symbol. Bit#(n) type represents a group of n bits. The parameterized type is instantiated by specifying n, like Bit#(1), Bit#(7), Bit#(64), ... . There are also types for signed and unsigned integer: UInt#(n) and Int#(n). These types carry no different values from Bits#(n), but provides abstraction, which leads to less bugs.
There are also expressions that represent groups of expressions. Tuple is one of them. For example, a pair of integer is expressed with Tuple2#(Integer, Integer) type.
When declaring and assigning a variable, you may use the let keyword instead of the concrete type. It infers the type from the expression and determines the type of variable, if the expression is not vague.
Functions
A function in BSV is an abstraction of a combinational expression. A function is specified with its name, inputs(arguments), outputs, and their types. The data flow is described inside the function block. Note that functions in BSV are not like those in programming languages. There aren't any concept of 'call and return'. A function is merely a (combinational) part of a larger circuit, and its output value is determined instantly. Following is an example of a function, which represents a full adder circuit.
function Bit#(2) fa(Bit#(1) a, Bit#(1) b, Bit#(1) c_in);
Bit#(1) s = (a ^ b)^ c_in;
Bit#(1) c_out = (a & b) | (c_in & (a ^ b));
return {c_out,s};
endfunction
combining multiple instances of the fa function above, we can construct a ripple-carry adder, a larger combinational circuit.
function Bit#(TAdd#(w,1)) addN(Bit#(w) x, Bit#(w) y, Bit#(1) c0);
Bit#(w) s; Bit#(TAdd#(w,1)) c=0;
c[0] = c0;
for(Integer i=0; i<valueOf(w); i=i+1) begin
let cs = fa(x[i],y[i],c[i]);
c[i+1] = cs[1];
s[i] = cs[0];
end
return {c[valueOf(w)],s};
endfunction
Static Elaboration of BSV
When writing a BSV code, we use high-level types and constructs like loops. They are useful in representing the hardware intuitively, but they can't be translated directly into hardware. At compilation stage, the Bluespec compiler replaces these constructs with static expressions that have direct hardware meaning. This stage is called static elaboration, or static computation.
Note that the Integer type is different from Int#(n) type. From the example code above, the Integer type variable i does not carry any data on the physical circuit, unlike Int#(n) type variables which correspond to specific wires on the circuit. Integer type variables carry values only to describe structure of the loop. So, those values can be 'statically' computed before synthesis, and are removed at static elaboration stage. Only the variables with hardware meanings survive.
Type Synonyms and Enumerated Types
We can use typedef to define type synonyms, as follows.
typedef Bit#(8) Byte;
typedef Bit#(64) Word;
typedef Tuple2#(a,a) Pair#(type a);
Byte byte = 0'b00100001;
Also, we can use enum to define enumerated types.
typedef enum {Red, Blue, Green}
Color deriving(Bits, Eq);
typedef enum {Eq, Neq, Le, Lt, Ge, Gt, AT, NT}
BrFunc deriving(Bits, Eq);
Color c =
deriving(Bits, Eq) means that defined type Color and BrFunc derives the type class Bits and Eq. We write that to tell the compiler that each expression in the brace has its unique bit pattern (automatically assigned by compiler), and that equality(==) is defined among these expressions.
The next post will cover register types and sequential circuits.
References
- Rishiyur. S. Nikhil, Kathy Czeck. "BSV by Example". Bluespec(2010).
- Bluespec SystemVerilog Reference Guide
- Class materials of SNU CSE (2025)