Learning BSV‎ > ‎Data Types‎ > ‎

Hints for Using Types

This page describes some common issues and restrictions involving data types when using Bluespec.  Moreover, some often overlooked
conveniences of Bluespec are also described.

Local variables and =

Local and temporary variables are assigned with the symbol  =.  This can be thought of like a verilog block assignment, but is really just a wire assign.  For example:

  Int#(32) data = 0;

This can be used in function, rules, or in modules where you want to generate combination logic. For instance, you can create a value from defined registers using this method.

  module ...
    Reg#(Int#(32)) data1 <- mkReg;
    Reg#(Int#(32)) data2 <- mkReg;
    Bool isSame = (data1 == data2);

This variable is just a wire driven to (data1 == data2) - a comparitor.  The scope of this variable is the same as the scope it is defined in.

In some other cases, = can be used to do a assign of one valid type to the same type. This can create a  reference pointer similar to the use in C++/C.  Modules and methods can be assigned with = in some cases.

  FIFO#(Int#(32)) outQ <- mkSizedFIFO(8);
  FIFO#(Int#(32)) inQ = outQ;

In this example, inQ and outQ are the same queue.  The <- is explained in the next section;

Reg#(type) and <=

Register are not inferred in Bluespec, they must be explicitly defined.  Bluespec will not generate registers or state variables, and it will not delete them either. This is useful to know for debugging purposes;  Reg's will always be present in the generated verilog code.

  Reg#( Int#(32) ) dataReg1 <- mkRegU;
  Reg#( Int#(32) ) dataReg2 <- mkReg(0);
  Reg#( Int#(32) ) dataReg3 <- mkRegA(0);

These three registers are defined as 32 bit, but with slightly different ways of being reset.

The <- operator is an action assign.  This is used when instantiating a module (such as we see here).

  • mkRegU - no reset or initialization
  • mkReg  - synchronous reset, reset to 0 (can be any value that is valid for that type)
  • mkRegA - asynchronous reset, reset to 0 (can be any value that is valid for that type)
The verilog code that gets generated is:

  always @(posedge CLK)
    if(enable) dataReg1 <= dataReg1\$DIN;

  always @(posedge CLK)
    if (!RST\_N)
      dataReg2 <= 0;
      if(enable) dataReg2 <= dataReg2\$DIN;
  always @(posedge CLK or negedge RST\_N)
    if (!RST\_N)
      dataReg3 <= 0;
      if (enable) dataReg3 <= dataReg2\$DIN;

We assume that a synthesis tool such as magma will then generate the proper registers.

Identifier Names

A current restriction in Bluespec requires that type names begin with an uppercase, and that variable names begin with lower case letters.
Type names include typedefs, interfaces, and structures. Variable names include module names, module instance names, interface
instantiations, methods names, formal and actual argument names.

If the first character of an instance name is an underscore, _, the compiler will not generate the name.  This is used extensively in the Bluespec libraries as it allows layers of modules inside libraries to be hidden from users of the libraries.  

If a user doesn't want a module instantiation to appear in the hierarchical naming, he can start the instance name with _.  This tells the compiler not to generate the name.  For example, a module hierarchy of foo calling bar which instantiates readme will generate foo_bar_readme.  If instead, the instantiation is named _readme,  the final generated name will be foo_bar

The unintended consequence may be that there are unamed objects generated.  For example, if in the BSV code the register names start with an underscore, then they will be converted into unnamed registers.  To avoid this, instance names should not start with an underscore, unless the desired behavior is to not generate the instance name.

Use let Variables

let definitions are a short hand step which directs the compiler to resolve and type check variable types. let variable are
useful to give meaningful names to intermediate values and also to share intermediate values.

      let norm1 = normalize( bas_res ) ;
      let rounded = round ( norm1 ) ;

as opposed to explicitly declaring the types

      FP_I#(8,24) norm1 = normalize( bas_res ) ;
      FP_I#(8,25) rounded = round ( norm1 ) ;

One caveat, if too many let declarations are used, BSC may not be able to determine some intermediate types.  This is especially true
when there are truncate and extension operations surrounding these declarations.

Using types instead of `defines

The following code fragment show how typedef and variables can be used in lieu of Verilog Pre-processor commands.

typedef Bit#(32)   DataT;
typedef Bit#(30)   Cntr;

typedef enum { CmdAdd, CmdSub, CmdMul, CmdDiv } DOp deriving(Bits,Eq);
typedef struct {
                  Cntr     counter ;
                  DataT    data1;
                  DataT    data2;
                  DOp      opcode}  Cmd
                                    deriving(Bits) ;
typedef Tuple5#(Cntr,DataT,DataT,DOp,Cntr) CmdCnt;

Integer numAdd = 5;
Integer numMul = 3;
Integer numDiv = 1;
Integer numTotal = numAdd + numMul + numDiv;

These definitions can be specified in a file and accessed via the import syntax.