Learning BSV‎ > ‎Advanced Bluespec‎ > ‎

Viewing Complex Data Structures

Advanced data structures, such as vectors, lists, and structures, are difficult to view during simulation and debugging since they are converted into bits during compilation. You can use the Probe package which is provided in the Bluespec library, to ensure that signals of interest are not optimized away by the compiler and are given a known name. The Probe primitive is used just like a register, except that only a write method exists. Since reads are not available, a Probe does not impact scheduling.

The first step is to define a specialized probe module based on the module mkProbe provided in the BSV library. In this example we'll call our module mkS_Probe. This module instantiates a mkProbe module for each field of the structure and defines the write method to include each field of the structure.

The specialized module, mkS_Probe, has to be kept in sync with the structure definition. For each field added to the structure, two lines must be added to the mkS_Probe module definition; one for the instantiation and one for the write. A unique module must be written for each unique data structure, but they all use the same basic techniques.

// Probe library is needed for this technique
import Probe :: * ;

// Define a struct, for which we want to see the internals
typedef struct {
Bit#(sx) xx;
Bit#(st) tt;
Bool zz ;
} MyS #( type sx, type st )
deriving ( Bits, Eq );

// Here we define a module specific to the MyS structure above. This module provides the
// Probe interface, and hence works exactly like the mkProbe module.
// This module should be keep in sync with the structure definition above. 2 lines are need
// for each new field added - one to instantiate the mkProbe module and one for the write.

module mkS_Probe( Probe#(MyS#(x,y)) ) ;

// We need to instantiate a mkProbe module for each field of the structure which we wish to probe.
// By using the let "type", we leave the type determination to the bsc compiler.

let xx <- mkProbe ;
let tt <- mkProbe ; // new fields added in structure above
let zz <- mkProbe ; // must be added here

// The Probe interface has one method, which we must provide in this module.
// A _write action causes all the sub-probes to be written as well.
method Action _write( s ) ;
xx <= s.xx ;
tt <= s.tt ; // new fields added in structure above
zz <= s.zz ; // must be added here

Once we've defined the mkS_Probe module, we can use it to view the signals in the structure. The remainder of this code shows an example of using mkProbe and mkS_Probe.

// Define an interface  and module for the example.
interface Foo ;
method Bit#(4) mx;
method Bit#(7) mt ;
method MyS#(4,7) ms ;

(* synthesize *)
module mkTest( Foo ) ;

let init_s = MyS{ xx: 0, tt:1, zz: True } ;

// Create a register to hold the structure listed above.
Reg#(MyS#(4,7)) r <- mkReg( init_s ) ;

// Define a regular probe and the specialized probe modules
let rprobe <- mkProbe ;
let rprobe_current <- mkS_Probe ;
let rprobe_new <- mkS_Probe ;

// Probe can only be written in an action context, -- e.g. a rule
// or an action method.
rule r1 ( True );
rprobe <= r ; // Write the regular probe
rprobe_current <= r ; // Write the specialized probe with the current value

let ns = MyS{xx: r.xx + 5,
tt: r.tt + 3,
zz: r.zz } ;
rprobe_new <= ns ; // Write the specialized probe with the "new" value
r <= ns ;

method mx = r.xx ;
method mt = r.tt ;
method ms = r ;

After compiling, the generated Verilog will contain signals named <instance_name>$PROBE, however no actual Probe instance will be created. The only side effects of a BSV Probe instantiation relate to the naming and retention of the associated signal in the generated Verilog.

In the generated Verilog code, the following code segment will be found:

 // probes
assign rprobe$PROBE = r ;

assign rprobe_current_xx$PROBE = r[11:8] ;
assign rprobe_current_tt$PROBE = r[7:1] ;
assign rprobe_current_zz$PROBE = r[0] ;

assign rprobe_new_xx$PROBE = x__h365 ;
assign rprobe_new_tt$PROBE = x__h385 ;
assign rprobe_new_zz$PROBE = r[0] ;

The first line corresponds to the rprobe, and the is assigned the current value of r[11:0]. The structure information is not present. The next 3 lines show therprobe_current instance of mkS_Probe module, while the last 3 lines show the rprobe_new instance. Note that within each of these instances the specific structure fields are presented, which will aid in the observation of the design. For net-list synthesis, these signals will be removed, since they are not connected to further down-stream logic.