Re: Fwd: RE: Data Channels


Subject: Re: Fwd: RE: Data Channels
From: Tom Fitzpatrick (fitz@co-design.com)
Date: Thu Aug 01 2002 - 08:40:27 PDT


Hi Everyone,

Here's a more detailed explanation of interfaces from one of our AEs,
Christian Burisch. I'm hoping that this explanation, combined with the email
I sent out on 7/19/02 ("Fitz's Actions for 7/22 Meeting") will clear up any
misunderstandings about interfaces.

Please let me know if there are still questions about what interfaces can or
cannot do.
Thanks,
-Tom

  Subject: RE: Data Channels
  Date: Fri, 26 Jul 2002 11:22:32 -0700
  From: "Stuart Swan" <stuart@cadence.com>
  To: "Kevin Cameron x3251" <Kevin.Cameron@nsc.com>
  Cc: <sv-ec@server.eda.org>

  For example, a channel in SystemC can have embedded modules, and can
implement interface
  methods that convert abstract transactions modeled as function calls
  into low-level signal interactions with such
  embedded modules. I don't believe this is possible with SystemVerilog
"interfaces".
  I'm still trying to figure out some of the specific characteristics of
SystemVerilog
  "interfaces" and such. As can be expected, when you dive into the details
they are
  quite different from SystemC channels. It's not even clear to me at the
moment
  whether each instantiation of an interface in SystemVerilog truely
represents
  a unique instance with instance-specific data, as occurs with module
instantiation.

Hi,

Let me explain:

The data inside an interface is instance specific. The trick is that the
interface spans the modules that it connects.

You can start writing very abstract interfaces, later refining them to
bus functional models and then still later refining them to RTL.

You can swap between these different versions of interfaces, WITHOUT having
to edit the interconnecting modules. The module headers of different
levels of abstractions are the same.

Modules can contain interface instances and can be connected via interfaces.
Interfaces cannot instance modules.
Interfaces can instance other interfaces.

Let me show you an example. Here is a sender and a receiver connected by
an interface, called channel_i:

module top;
  bit clk;
  channel_i channel(clk);

  always #10 clk = ~clk;

  sender s(clk, channel);
  receiver r(clk, channel);

endmodule

module sender (input bit clk, channel_i channel);

initial
  begin
    channel.transmit('hF0);
    channel.transmit('h42);
    channel.transmit('h27);
    #50 $finish();
  end

endmodule

module receiver (input bit clk, channel_i channel);

always
  begin
    logic [7:0] data;
    channel.receive(data);
    $display("Received %h", data);
  end

endmodule

We can start simulating to make sure that the communication works
using an interface that just shuffles the data across:

interface channel_i(input bit clk);
  bit valid;
  bit [7:0] data;

 task transmit(input bit [7:0] d);
    wait(valid == 0);
    data = d;
    valid = 1;
  endtask

  task receive(output bit [7:0] d);
    wait(valid == 1);
    d = data;
    valid = 0;
  endtask

endinterface

This interface is only useful to check functionality. If we want
to make tradeoff decisions and need more accurate timing, we need
a cycle accurate interface. Here is one that send the data serially:

interface channel_i(input bit clk);
  logic ready=0;
  logic valid=0;
  logic data;

  modport rtl_transmitmode(output valid, data, input ready);
  modport rtl_receivemode( input valid, data, output ready);

  task transmit(input logic [7:0] d);
    while (ready !== 1) @(negedge clk) ;
    valid <= 1;
    for (int i = 7; i >= 0; i--)
      begin
        data <= d[i];
        @(negedge clk);
      end
    data<= 'x;
    valid <= 0;
  endtask

  task receive(output logic [7:0] d);
    ready <= 1;
    while (valid !== 1) @(posedge clk) ;
    ready <= 0;
    for (int i = 7; i >= 0; i--)
      begin
        d[i] <= data;
        @(posedge clk);
      end
  endtask

endinterface

The clk is not actually part of the interface, but it passed in because
the receive and transmit methods need it.

To use this interface NO modification to top, sender or receiver is
necessary!

The receiver uses the receive method in the interface, so it may not be
synthesizable. If we want to rewrite it at RTL, then we may want to
implement the receive functionality as a FSM and it needs to use
the actual signals inside the interface, instead of the task:

module receiver (input bit clk,
                 channel_i.rtl_receivemode channel);

logic [7:0] data;
logic [7:0] index;
enum {wait_for_valid, receiving, done} st;

always @(posedge clk)
  case (st)
    wait_for_valid:
      begin
        channel.ready <= 1;
        index <= 6;
        if (channel.valid==1)
          begin
            data[7] <= channel.data;
            st <= receiving;
          end
      end

    receiving:
      begin
        channel.ready <= 0;
        index <= index - 1;
        if ((index >= 0) && (index <= 6))
          data[index] <= channel.data;
        else
          st <= done;
      end

    done:
      begin
        st <= wait_for_valid;
        $display("Received %h", data);
      end
  endcase

endmodule

Note that this change in abstraction of the receiver module requires no
change to the sender module because the abstraction difference is masked by
the interface. So I am sure that you see the power of this approach. This
allows some interesting methodologies, for example:

*) step-wise refinement from architecture to RTL
*) verifying interfaces seperate from the design
*) reuse of interfaces
*) separating communication from the processing functionality
*) huge code compaction



This archive was generated by hypermail 2b28 : Thu Aug 01 2002 - 08:42:33 PDT