[sv-bc] FW: SV interfaces: How to parameterize and synthesize

From: Peter Jensen <peter@syosil.dk>
Date: Mon Jul 26 2004 - 04:20:35 PDT

Dear SV-BC,

recently a request was posted on this thread for "real" experiences using SV
with state-of-the-art EDA tools. This emails reflects a number of
experiences with synthesizing SV variables.

I have attached a case study of using synthesizeable SV interfaces, in which
some SV language related difficulties arise. I believe these questions
should be clarified to enable synthesis of higher abstraction level
SystemVerilog.

Thanks goes to Jonathan Bromley, Doulos, for valuable input to this case
study.

Kind regards,
Peter Jensen

Peter Jensen
SyoSil Consulting
Himmelev Bygade 53
DK-4000 Roskilde
Tel +45 46 36 11 32
Fax +45 46 36 11 22
Mob +45 40 58 10 00
mailto:peter@syosil.dk
http://www.syosil.dk

 ----------------------------

 The objective of this text is to ask for clarifications around a few items
in the SV3.1a interface construct, when trying to model a synthesizeable,
generic, parameterizable, abstract bus interface.

Consider a bus system with one master and N slaves. A subset of the signals
are:

Signal Master Slaves
req output input
ack [N-1:0] input output: ack[i]
 An interface representing this would be:

interface intf ();
   parameter int no_slaves = 0;
   bit req;
   bit [no_slaves-1:0] ack;
   // ....
endinterface

To make this synthesizeable, we need to add modports. As the ack bus is
driven by multiple instances, we use the "modport expressions" for the
slave:

modport master (
   output req,
   input ack
);
modport slave0 (
   output .ack_slice(ack[0]),
   input req
);
modport slave1 (
   output .ack_slice(ack[1]),
   input req
);
   // ...
modport slaveN (
   output .ack_slice(ack[N]),
   input req
);

Although the interface is intended to be generic, this solution does not
scale because each modport must be coded explicitly.

The SV3.1a BNF allows modports to be inside a generate statement:

genvar i;
generate for (i=0; i<N; i++) : generate_loop
   modport slave (
      output .ack_slice(ack[i]),
      input req
   );
end endgenerate

CLARIFICATION ISSUE #1: Besides allowed by the BNF, the LRM does not discuss
modports inside generate statements. Is it the intention that the code above
should work? What will be the name of the resulting modport(s)?

Assuming that the modport was generateable, we can design a bus sytem as
follows, using the parameterizable interface.

module master(interface myintf, ....);
   // drive myintf.req, use myintf.ack[no_slaves-1:0]
endmodule
module slave (interface myintf, ....);
   // drive myintf.ack_slice, use myintf.req
endmodule
module bussystem();
   intf #(.no_slaves(8)) intf_i ();
   master master_i (.*, .myintf(intf_i.master));
   slave slave_i0 (.*, .myintf(intf_i.slave_generate_loop_0)); // modport
name ?
   slave slave_...
   slave slave_i7 ... // The 8 slaves can also be created in a generate
statement
endmodule

We now reached out first objective: A parameterizable, synthesizeable bus
system, based on the assumption that modports can be generated.

However, assume we would like the interface clients (master, slaves) to
access the interface variables by using functions exported by the interface.
These functions defined within the interface should access the req and ack
objects on the interface directly without having these in their argument
list. However, this is not possible, as ack[i] is renamed to ack_slice in
each slave modport, we therefore need to transfer the acknowledge
explicitly:

function void check_req_send_ack(output bit ack); // defined in interface
intf
   ack = req; // directly access req in the interface
endfunction

genvar i;
generate for (i=0; i<N; i++) : generate_loop
   modport slave (
      output .ack_slice(ack[i]),
      input req,
      import function check_req_send_ack()
   );
end endgenerate

The slave would need to use this function as follows:

module slave (interface myintf, ...);
   always_comb check_req_send_ack(.ack(myintf.ack_slice));
endmodule

CLARIFICATION ISSUE #2: It is great that the SV3.1a "modport expressions"
now enables the distribution of slices of vectors to different modports,
this opens up for construction of generic interfaces. However, the renaming
done by "modport expressions" has the drawback, that direct access by
functions exported from the interface to the interface clients now cannot
access interface variables directly!

This drawback should be eliminated to allow use of interface methods in
conjunction with "modport expressions". I can come up with a few
suggestions, however these are up for discussion.

A. Make it legal to access the renamed objects in the interface functions.
For instance, the function check_req_send_ack above should be allowed to
write to a variable called ack_slice (the modport alias for ack[i]).

function void check_req_send_ack(); // defined in interface intf
   ack_slice = req; // directly access req and ack[i] in the interface
endfunction

This might impose serious difficulties. The function is a member of the
interface, not the modport, and therefore the name "ack_slice" is unknown in
the scope where the function is defined. Also, the meaning of "ack_slice"
must change, depending on which modport the function was called through.

B. Include the function in the generate block where the slave modports are
generated. This would create unique function names for each slave to use,
and that would inhibit use of generic slaves / reusable slaves. Perhaps this
could be solved by aliasing such function names using the "modport
expressions".

genvar i;
generate for (i=0; i<N; i++) : generate_loop
   function void check_req_send_ack(); // defined in interface intf
      ack[i] = req; // directly access req in the interface
   endfunction
   modport slave (
      output .ack_slice(ack[i]),
      input req,
      import function
.check_req_send_ack(check_req_send_ack_slave_generate_loop_[i]())
   );
end endgenerate

C. A different possibility would be as follows, idea and example donated by
Jonathan Bromley, Doulos :

Instead of using generated modports, use generated nested interfaces. Each
of these interfaces can have a single modport...

interface intf ();
     parameter int no_slaves = 0;
     bit req;
     bit [no_slaves-1:0] ack;

     interface slave_intf ();
        parameter which_slave = 0;
        function void check_send;
           ack[which_slave] = req;
        endfunction
        modport slave_modport (
           output .ack_slice(ack[which_slave]),
           input req,
           import function check_send()
        );
     endinterface // slave_intf

     modport master...

  genvar i;
   generate for (i=0; i< no_slaves ; i++) : slave_ports
       slave_intf #(.which_slave(i)) slave ();
  endgenerate

 endinterface // intf

Now a slave module can be instantiated...

intf #(num_slaves) intf_i ();
slave_module slave_i0 (.*,
myintf(intf_i.slave_ports[0].slave.slave_modport ));

and within the slave_module, a call to myintf.check_send() will have the
required effect. Assuming the tool support was available, this is a viable
solution, however quite verbose. Also note that the instantiation right
above requires hierarhical referencing of the nested interface, which may
not be supported by a synthesis tool.
Received on Mon Jul 26 04:20:51 2004

This archive was generated by hypermail 2.1.8 : Mon Jul 26 2004 - 04:21:21 PDT