Learning BSV‎ > ‎Interfaces‎ > ‎

Interface Design Tips

When defining interfaces, the prototypes for the methods are declared.  However, it is up to the module which provides the interface to define the methods.  This allows different modules to provide the same interfaces, yet produce very different implementations for these interfaces.  This section describes some guidelines for defining methods for interfaces.

Within a module, communication between separate methods, separate rules, or between rules and methods occurs through module level Bluespec SystemVerilog elements, such as registers, FIFOs, or other modules.  To ascertain that redundant flops are not added and latency is kept to a minimum, the following guidelines should be kept in mind.

1.  Combine all combinational logic from the ports to the first stage of state elements into the interface methods. 

eg. x1 and y1 are input ports and sum is the first stage of state elements

  Reg#(a)   sum <- mkReg (0) ;   // instantiate an register interface

  // Add the two inputs storing result in register sum.
  method Action calc( x1, y1 );
          sum <=  x1 + y1;
  // note: types for x1 and y1 are taken from the interface definition.

2. Define rules for transitions in the state elements. i.e. for designs with multiple stages of flip-flops, all the intermediate logic can  be defined in terms of rules.  

e.g. stage1 and stage2 are registers. Depending upon an opcode  which is registered, stage2 gets the value of stage1 either as it is or inverted

  rule calc (True)
          3'b001: begin
            stage2 <= stage1;
          3'b010: begin
            stage2 <= 1 - stage1;

3. Good design requires that output ports are directly registered or derived only from state elements.  The output or Mealy logic should be placed in the methods as well.

e.g. z1 is an output port which is high if all bits of  register product are high

    Reg#(Int#(16))  product <- mkRegU ;
    method Bit#(1) z1();
        z1 = & product;                // reduction and

Sharing Signals in an Interface

Consider an interface to a memory system as follows:

interface Memory_ifc ;
// read returns data at address
method Data_t read ( Addr_t addr ) ;
// write datain at address
method Action write ( Addr_t addr, Data_t datain ) ;

In this definition, there would be four buses (read_addr, read (return value from read method), write_addr, and write_datain) plus three handshaking signals (RDY_read, RDY_write, and EN_write). It may be desirable to have the address bus shared between the two methods. This section shows some styles on how this can be done.

Using a Common Method

The read and write methods may be combined into a single method.

interface Memory_ifc ;
method Action read_write( Bool mode, Addr_t addr, Data_t datain ) ;
method Data_t data_out() ;

In this example, there are three buses (data_out, read_write_addr, and read_write_datain ) plus three handshaking signals (RDY_read_write, RDY_data_out, and EN_read_write).

Using a Separate Method for the Shared Signals

A separate action method is added which takes in the address for both the read and write methods. The implementation of the addr method may require some special techniques, such as using an RWire.

interface Memory_ifc ;
method Action addr( Addr_t addr ) ;
method Data_t read ( ) ;
method Action write ( Data_t datain ) ;

In this example, there are three buses (addr_addr, read, and write_datain) plus five handshaking signals (RDY_addr, RDY_read, RDY_write, EN_addr and EN_write).

Combining Interfaces

Consider that interfaces are treated like structures (collections) of methods, then it is possible to combine separate interfaces into one, (i.e., an interface composed of other interfaces).

interface Part1_ifc ; 
interface Combined_ifc ;
interface Part1_ifc part1 ;
interface Part1_ifc part2 ;
interface Part2_ifc part3 ;