RE: [vhdl-200x] Heterogeneous Interfaces in VHDL

From: Ernst Christen <christen.1858@comcast.net>
Date: Mon Jun 29 2015 - 07:15:15 PDT
Hi Daniel,

 

Thank you for taking a look at what I put together. I read through your email, and I have a number of observations, comments and questions. See below. I have not yet looked through the twiki page you created; I'll do this ASAP and will respond separately.

 

Regards.

Ernst

 

From: owner-vhdl-200x@eda.org [mailto:owner-vhdl-200x@eda.org] On Behalf Of Daniel Kho
Sent: Friday, June 26, 2015 4:02 PM
To: vhdl-200x@eda.org
Subject: Re: [vhdl-200x] Heterogeneous Interfaces in VHDL

 

Hi Ernst,

Thank you for taking your time to come up with such an excellent white paper.

Of late, I have been working with some SystemVerilog interfaces. One thing I found different was that in SVI, one needs to "instantiate" the interface before interconnections can be made between several modules. E.g. (adapted from Twiki example on SVInterfaces):

module top;

    logic clk = 0;

    

    // "instantiate" the interfaces - well these look like instances to me.

    // Assume these interfaces are different (ifc1 and ifc2).

    ifc1 #(.DWIDTH(16)) wide_intf1(.clk(clk), .reset(reset));
    ifc2 #(.DWIDTH(16)) wide_intf2(.clk(clk), .reset(reset));

[EC] Indeed, ifc1 and ifc2 refer to different interface declarations, each of which has two formal ports named clk and reset, respectively. wide_intf1 and wide_intf2 are names of the corresponding interface instances. The port list associates the local signals clk and reset with the formal ports of the same name. They may now be referenced with their interface-based names anywhere the interface is available.
    ...

    // instantiate the modules.

    block1 u_block1(.reset(reset), .clk(clk), .ifc(wide_intf1));

    block2 u_block2(.reset(reset), .clk(clk), .ifc(wide_intf2));

[EC] The example skips over the specification of the modes, which is possible in SV (it's at best advisory) but has to be addressed in VHDL.

Also, it isn't necessary to have ports for clk and reset since these are also brought into the instances of block1 and block2 through the ifc port, but it's not wrong.
    

    // make the connections.

    assign wide_intf2.data = wide_intf1.data;
    ...

endmodule

I still prefer the existing VHDL way, just declare the interface instead of "instantiating" it. Currently, one would do the following (assuming we use record structures with custom-resolved members):

entity top is end entity top;

architecture sim of top is

    signal ifc1: wide_intf1;
    signal ifc2: wide_intf2;

begin

    -- instantiate the components

    u_block1: entity work.block1(rtl) port map(reset=>reset, clk=>clk, ifc=>wide_intf1);
    u_block2: entity work.block2(rtl) port map(reset=>reset, clk=>clk, ifc=>wide_intf2);
    

    -- make the connections.

    wide_intf2.data <= wide_intf1.data;
    ...

end architecture sim;

[EC] This doesn't seem to line up. In the declarations the signal names are ifc1 and ifc2, but I surmise ifc1 and ifc2 should refer to the interface declarations and wide_intf1 and wide_intf2 to the signal names. That is, the declarations should probably read

                signal wide_intf1 : ifc1;

                signal wide_intf2 : ifc2;

This could work for a simple interface, but the meaning here is different from the SV code above because clk and reset are not made part of the interface. The capability to include external signals in an interface was considered important at the last WG meeting I attended, so I believe we have to find a way to make such associations possible.

 

For the component instantiation statement I surmise the modes for the ifc ports of block1 and block2 are available in the corresponding entity declarations. Also, for the concurrent signal assignment statement I assume that the data members of ifc1 and ifc2 have the same type.

 

I feel declaring the interfaces the usual VHDL way is simpler than what is used in SVI. However, SVI has the concept of modports. What we usually do is to write custom resolution functions to ensure there are no multiple drivers on the interface.

The modport concept is perhaps a bit too bulky IMO, as one usually needs to define a pair of modports for a pair of blocks to interconnect with each other. This makes the concept rather prone to human error, as one could define one of the pairs incorrectly. I still like the concept of conjugated ports for this purpose.

I feel it necessary to define mixed modes within an interface, so the conjugated port concept fits nicely with interfaces having members with different modes (again shamelessly adapted from the Bundles Twiki):

-- I prefer to maintain the syntax to be consistent with records.

-- Also, a signal declaration expects a type and an optional resolution 
-- function preceding the type, e.g.:

--    signal s: resolved some_type;

-- so defining bundles as a type makes some sense.

type b is bundle

    generic (type t);

    port (
        signal clk, reset: in std_ulogic;

        -- master to slave direction.

        signal data:        mst_to_slv t;
        
        -- both master and slave can drive at 

        -- different times.

        signal serial:       inout  std_ulogic;
        

        -- slave to master direction.

        signal resp:        slv_to_mst t;

        signal ack:         slv_to_mst std_ulogic;

        signal busy:       slv_to_mst std_ulogic
    );

    signal s1: std_ulogic := '0';    -- Permanent objects of the bundle

    signal s2: std_ulogic_vector(ub downto 0) := (others => '0');

    shared variable sv : my_protected_type;

end bundle b;

[EC] Are you suggesting to deal with the mode as something like a macro, to be replaced in one situation with in, in another with out? If so, I don't understand how a situation like the following would be handled:

            architecture rtl of dut is

            -- some declaration of an interface named ifc

            begin

                        m: master port map (ifc);

                        s: slave port map (ifc);

            end;

Of course you and I know what the intent is, but does the compiler?

 

There is another issue with the bundle declaration: It seems to merge the functionality of the SV interface declaration and the modport declaration into one. I believe that doing so may reduce what a user has to write in simple cases, but it will stand in the way of more advanced uses. I'll keep it in mind but will remain skeptical of this approach.

 

Here, mst_to_slv and slv_to_mst are conjugated modes. I was thinking in the lines of:

package modes is

    mode mst_to_slv is default;    -- or perhaps, just "mode mst_to_slv"?

    mode slv_to_mst is conjugated of mst_to_slv;

end package modes;

[EC] What/who is deciding what "default" means?

Internally, the implementation should use resolution functions which errors out whenever there are multiple drivers driving the same net. Also, in the implementation of bundles, should we require users to always use unresolved types for all signals? Is there a use case where multiple drivers are allowed in a bus interface (I mean, multiple drivers at the same instance of time)?

For example, in the declaration:
        signal resp:        slv_to_mst t;

I was thinking that a custom resolution function be attached implicitly to each member of the bundle, e.g.:
        signal resp:        resolved slv_to_mst t;

where the "resolved" function is written such that multiple drivers are not allowed:

[EC] I don't quite follow your thought here. I would argue that the definition of a bundle should ensure that the bundle is not treated as a composite type, but rather that each element of the bundle stands by itself. In this case the resolved type issue you are referring to is the same as if each element of the bundle had been part of the port list individually. 

[Example resolution function for std_logic]:

    function resolve(s: std_ulogic_vector) return std_logic is
        variable result: std_logic;
    begin
        for i in s'range loop
            if is_LH01(s(i)) then    -- checks for 'L', 'H', '0', or '1'.
                assert not is_LH01(result) report "Multiple driving signals detected." severity error;
     
                if is_LH01(result) then
                    result := 'X';
                else result := s(i);
                end if; 
            end if; 
        end loop;
        return result;
    end function resolve;

 

Let me know your thoughts. Have a great weekend ahead!

 

Best regards,

Daniel

 


-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
Received on Mon Jun 29 07:15:59 2015

This archive was generated by hypermail 2.1.8 : Mon Jun 29 2015 - 07:16:52 PDT