Interface Construct and Port Mode Configurations

Proposal Details

Current Situation

Currently, objects of composite types can only be connected through ports with a common port mode for all elements (of the composite type).

Requirement

A proposal to provide a formal VHDL Interface construct for composite types with the aim of enabling a user defined port mode configuration capability.

Competing Issues:

Interfaces / Record IO - a proposal to alter record types to include mode functionality.

Implementation details

Interface Construct:

interface_declaration ::=

interface identifier of composite_subtype_name is

interface_declarative_part

end interface [ interface_simple_name ] ;

Apart from port configurations, the interface could contain various decalarations pertaining to the Interface.

At first I thought that the interface should not infer any form of hardware, mainly being used to provide simplified connectivity and functions/procedures used for abstract test monitoring /stimulus of protocols and transactions. However, maybe within the object entity (as opposed to the structural interconnect side) it could be used to generate the interface I/O functions.

Port Configuration Construct:

port_configuration_declaration ::=

port configuration identifier is

[ formal_generic_clause ]

formal_composite_port_clause

end port configuration [ port_confguration_simple_name] ;

composite_port_clause ::=

port ( composite_port_list ) ;

composite_port_list ::= composite_interface_list

composite_interface_list ::=

composite_interface_element { ; composite_interface_element }

composite_interface_element ::= composite_interface_declaration

composite_interface_declaration ::=

element_identifier_list : composite_mode_declaration ;

composite_mode_declaration ::=

mode [ := static_expression ]

Mode Additions

Two new modes and a custom named mode are required as shown:

mode ::= in | out | inout | buffer | linkage | composite_mode | null | custom_mode

Composite mode

This mode provides a hierarchical mode structure for composite types.

composite_mode ::=

composite ( composite_mode_clause )

composite_mode_clause ::= composite_interface_declaration

Null mode

This mode is required for interface (sub-)connections that are not used within the connected entity. It provides connectivity/termination at the port, but can neither be driven nor read by the entity.

It is used within composite mode structures.

Custom mode

This mode provides a custom named mode structure for composite types.

Because procedures don't need to specify a parameter mode (defaulting to 'in') we need a means of disambiguating the custom mode name.

A suggestion is a new attribute 'mode' which will qualify the name as a mode.

custom_mode ::= mode'port_configured_composite_mode

port_configured_composite_mode::=

port_configuration_simple_name [ ( generic_association_list ) ]

Use Cases

A simple CPU Bus example - define stuff in a package first:

CPU Bus Package

  package CPU_BUS_pkg is

    -- address & data subtypes
    subtype  ADDR_vst          is Std_Logic_Vector(15 downto 0);
    subtype  DATA_vst          is Std_Logic_Vector(15 downto 0);

    -- slave array definitions
    constant SLAVE_MAX_jc       : Positive := 8;
    subtype  SEL_jst           is Natural range SLAVE_MAX_jc-1 downto 0;
    subtype  SEL_vst           is Std_Logic_Vector(SEL_jst);

    -- the interface sub-records and arrays
    type MASTER_r is                      -- Master record
      record
        ADDR_vl : ADDR_vst;               -- Address bus to slave devices
        DATA_vl : DATA_vst;               -- Data bus from master to slave
        WE_l    : Std_Logic;              -- Write enable from master
        SEL_v   : SEL_vst;                -- Slave enables from master

      end record MASTER_r;

    type SLAVE_r is                       -- Slave record
      record
        DATA_vl : DATA_vst;               -- Data bus from slave to master
        ACK_l   : Std_Logic;              -- Acknowledge to master

      end record SLAVE_r;

    type SLAVE_at is
      array(SLAVE_IF_SEL_jst) of SLAVE_r; -- Array of slave return records

    -- the main interface record
    type CPU_BUS_r is                     -- Bus interface record
      record
        MASTER_rl : MASTER_r;           -- Master record to slaves
        SLAVE_al  : SLAVE_at;           -- Array of slave records to master

      end record CPU_BUS_r;

    -- the interface declaration
    interface CPU_BUS_if of CPU_BUS_r is

      -- these configurations define custom modes which can be 
      -- used on a port mapping of a 'CPU_BUS_r' record type

      -- for a master mapping:
      --   the master element (a record) is an output
      --   the slave element (an array) is an input
      port configuration MASTER_pcfg is
        port(
            MASTER_rl : buffer;
            SLAVE_al  : in
          );
      end port configuration MASTER_pcfg;

      -- for a slave mapping:
      --   the master element (a record) is an input
      --   the slave element (an array) is a composite mode
      --     the generic selects the array element to drive as an output
      --     the other array elements are set to null mode (not driven or read)
      port configuration SLAVE_pcfg is
        generic(
            SEL_jg : SEL_jst
          );
        port(
            MASTER_rl : in;
            SLAVE_al  : composite(
              SEL_jg    : buffer;
              others    : null
            )
          );
      end port configuration SLAVE_pcfg;
  end package CPU_BUS_pkg;

The interface statement pertains to the given composite type, in this case 'CPU_BUS_r'.

The port configurations define custom port mode declarations for the composite type of the given interface (CPU_BUS_if ).

Master entity declaration:

  use work.CPU_BUS_pkg.all
  entity MASTER is
    port(
        CLK_i      : in        Std_Logic;
        CPU_BUS_cp : mode'CPU_BUS_if.MASTER_pcfg
                               CPU_BUS_r;  -- Master port to slaves
        RST_i      : in        Std_Logic
      );
  end entity MASTER;

The 'master' entity uses the 'master' port configuration to connect to the record (CPU_BUS_r).

The record type (CPU_BUS_r) could be ommited from the port declaration, as the port configurartion constrains the port (CPU_BUS_cp) to be of this type.

Slave entity declaration:

  use work.CPU_BUS_pkg.all
  entity SLAVE is
    generic(
        SLAVE_SEL_jg : SEL_jst
      );
    port(
        CLK_i      : in        Std_Logic;
        CPU_BUS_cp : mode'CPU_BUS_if.SLAVE_pcfg(
                         SEL_jg => SLAVE_SEL_jg
                       )       CPU_BUS_r;
        RST_i      : in        Std_Logic
      );
  end entity SLAVE;

The 'slave' port configuration uses the generic to map to the selected array element of the port configuartion, the others are mapped as 'null' port modes.

As with the master entity decalaration above, the record type (CPU_BUS_r) could be ommited.

Master & Slave Instantiations:

    signal CLK_s,
           RST_s      : Std_Logic;
    signal CPU_BUS_rs : CPU_BUS_r;

  begin
    MASTER_i0 : MASTER
      port map (
        CLK_i      => CLK_s,
        CPU_BUS_cp => CPU_BUS_rs, 
        RST_i      => RST_s
      );
    SLAVE_i2 : SLAVE
      generic map (
        SLAVE_SEL_jg => 2           -- slave instance 2
      )
      port map (
        CLK_i      => CLK_i,
        CPU_BUS_cp => CPU_BUS_cs,   -- is mapped through to CPU_BUS_cs.SLAVE_al(2)
        RST_i      => RST_i
      );
    SLAVE_i4 : SLAVE
      generic map (
        SLAVE_SEL_jg => 4           -- slave instance 4
      )
      port map (
        CLK_i      => CLK_i,
        CPU_BUS_cp => CPU_BUS_cs,   -- is mapped through to CPU_BUS_cs.SLAVE_al(4)
        RST_i      => RST_i
      );

The 'slave' instantiations have the 'select' generics mapped to their respective values. The port configuration generics have already been mapped in the 'slave' entity declaration.

References

Relevant Email threads:

Relevant Proposals:

Relevant Documents:

Comments

-- ErnstChristen - 2015-01-27

There are about 6 proposals related to improving the management of ports:

It would be worthwhile to derive generic requirements for port/interface management instead of looking at them in isolation.

Arguments FOR

  • Complex - powerful construct?
  • The port configuration construct could have a declaration region to include procedures used to define the interface protocol for master and slave instances.

Port Mode Configurations :

At first, I thought these customizable port modes should be limited to use within the new Interface Construct, but then I stumbled on a somewhat trivial but useful example to counter this presumption.

In my FPGA designs a rather anoying problem with 'unsigned' types is when an input bus (e.g. address) becomes tri-state. The internal bus becomes unknown, i.e. 'X', even when target input buffers are instantiated. This in turn generates many warnings of the type 'metavalue detected in function...' when performing address range comparisons.

Many FPGA architectures have the ability to enable weak pull-ups or -downs on the pin side of their input buffers and these can be instantiated directly on these inputs via their target library components. However, as the resister model is an output only component, the input ports of the FPGA need to be redefined as 'inout' ports.

The new port mode confguration could be used to define a mode of 'in_pu' or 'in_pd' as 'inout'. Trivial as I said, but it would maintain a level of design intent for the port.

Arguments Against

  • Complex - difficult to use?
  • Port configuration protocol procedures would perhaps infer the need for an 'interface' body construct and 'port configuration' body constructs?
  • Are the extra port modes useful extensions or an unnecessary complexity?

Email Reflector Comments

Supporters

Add your signature here to indicate your support for the proposal

-- Brent Hayhoe -2013-05-15

Analysis

The LCS implementation adds a mode view construct (was the port configuration construct) with the new interface mode null, for use within a mode view of an interface object associated with a composite type.

The proposal for bundle objects (and interface objects) along with the map function have been left as a future enhancement.

The LCS has been split into three:

  • LCS-2016-045a deals with the new mode view declaration for composite interface objects.
  • LCS-2016-045b deals with a new null mode used primarily for disconnecting elements of composite objects at an interface instantiation.
  • LCS-2016-045c deals with new attributes of named instances of a mode view declaration.

LCS-2016-045a - Mode View Construct

Mode view constructs provide a mechanism to individually declare the mode for elements of an interface object having a composite type.

The declared mode of a composite interface object's element can be replaced by a named mode view if its declared subtype indication is that of a composite type.

Mode Views of Record Structures

A mode view can define the interface element modes for an interface object associated with a record type.

Given a record type - 'MyRecordType':


type MyRecordType is
   record
      ElementA : ElementAType;
      ElementB : ElementBType;
      ElementC : ElementCType;
      ElementD,
      ElementE,
      ElementF : ElementDEFType;
   end record MyRecordType;

A view (MyRecordView) defining a particular mode definition for a given interface object of record type 'MyRecordType':


view MyRecordView of MyRecordType is
      ElementA : ElementAMode;
      ElementB : ElementBMode;
      ElementC;                -- default 'ElementC' mode!
      ElementD,
      ElementE,
      ElementF : ElementDEFMode;
   end view MyRecordView;

Mode Views of Array Structures

The array mode view defines the interface mode view for an interface object associated with a array type.

Given a constrained array type - 'MyArrayType':


type MyArrayType is
   array(MyIndex)
      of ElementType;

A view (MyArrayView) defining a particular mode definition for a given interface object of array type 'MyArrayType':


view MyArrayView of MyArrayType is
      MyIndexRangeZ : TypeRangeZMode;
      MyIndexRangeY;                 -- default 'MyIndexRangeY' mode!
      MyIndexRangeX : TypeRangeXMode;
   end view MyArrayView;

LCS-2016-045b - Null Mode

A new mode null is added to provide the ability to disconnect an element of such a composite interface object defined within a mode view.

As such, a composite interface object element 'actual', cannot be driven or read by its 'formal' (of mode null) in an interface map instantiation.

If one envisages the interface map of an entity (or subprogram, or block) as a physical IC plugged into a socket on a PCB, then each pin of the IC represents each interface object (constant, signal, or variable) and/or each element/sub-element of any composite interface objects.

The functionality of each pin is controlled by its associated mode, in, out, or inout.

The new null mode can be envisioned as a pin element of a composite object that is cut (or broken) and therefore cannot affect the PCB composite interface element in the PCB net list. This is what the new null mode achieves.

LCS-2016-045c - Mode Attributes

A mode view declaration in BNF terms is an 'entity class' and as such is a named construct which may have attibutes defined for it.

An attribute 'CONVERSE' is defined which when used to decorate a mode view returns an identical structured view with its modes reversed:

  • an in transforms to an out
  • an out transforms to an in
  • an inout remains as an inout
  • a null remains as a null
  • a mode view remains as a mode view

Examples

Some general examples of usage.

Given a simple peripheral interface bus, we define a record type for the master, to drive the slaves, and an array to group the slave responses from each slave's response bus.

Each Std_uLogic_Vector element and array element is unconstrained in the type declaration and are constrained in the mode view by means of generics:


type MasterType is
   record
      Address       : Std_uLogic_Vector;
      Read_Write    : Std_uLogic;
      WriteData     : Std_uLogic_Vector;
      SlaveResponse : SlaveResponseArray;
   end record MasterType;

type SlaveResponseArray is
   array (Natural range <>)
      of SlaveResponseType;

type SlaveResponseType is
   record
      Select    : Std_uLogic;
      DataValid : Std_uLogic;
      ReadData  : Std_uLogic_Vector;
   end record SlaveResponseType;

Now we can define the mode views for the master and slave nodes of the interface.

With generics constraining the composite type of the mode view, then the mode views for the master are:


view MasterResponseView
   generic (
      DataLength : Positive)
   of SlaveResponseType(
      ReadData(DataLength - 1 downto 0))
   is
   Select    : out;
   DataValid : in;
   ReadData  : in;
end view MasterResponseView;

view MasterArrayView
   generic (
      DataLength          : Positive;
      SlaveResponseLength : Positive)
   of SlaveResponseArray(SlaveResponseLength - 1 downto 0))
   is
   others : view MasterResponseView generic map (
                                             DataLength => DataLength);
end view MasterArrayView;

view MasterBusView
   generic (
      AddressLength       : Positive;
      DataLength          : Positive;
      SlaveResponseLength : Positive)
   of MasterType(
      Address(AddressLength - 1 downto 0),
      WriteData(DataLength - 1 downto 0),
      SlaveResponse(SlaveResponseLength - 1 downto 0))
   is
   Address       : out;
   Read_Write    : out;
   WriteData     : out;
   SlaveResponse : view MasterArrayView generic map (
                                  DataLength          => DataLength,
                                  SlaveResponseLength => SlaveResponseLength);
end view MasterBusView;

Then, mode views for each slave:


view SlaveResponseView
   generic (
      DataLength : Positive)
   of SlaveResponseType(
      ReadData(DataLength - 1 downto 0))
   is
   Select    : in;
   DataValid : out;
   ReadData  : out;
end view SlaveResponseView;

view SlaveArrayView
   generic (
      DataLength          : Positive;
      SlaveResponseLength : Positive;
      SlaveResponseID     : Natural)
   of SlaveResponseArray(SlaveResponseLength - 1 downto 0))
   is
   SlaveResponseID : view SlaveResponseView generic map (
                                             DataLength => DataLength);
   others          : null;
end view SlaveArrayView;

view SlaveBusView of
   generic (
      AddressLength       : Positive;
      DataLength          : Positive;
      SlaveResponseLength : Positive;
      SlaveResponseID     : Natural)
   of MasterType(
      Address(AddressLength - 1 downto 0),
      WriteData(DataLength - 1 downto 0),
      SlaveResponse(SlaveResponseLength - 1 downto 0))
   is
   Address       : in;
   Read_Write    : in;
   WriteData     : in;
   SlaveResponse : view SlaveArrayView generic map (
                                  DataLength          => DataLength,
                                  SlaveResponseLength => SlaveResponseLength,
                                  SlaveResponseID     => SlaveResponseID);
end view SlaveBusView;

Bundles (not included within LCS-2016-045x) - Thoughts?

We need bundle objects to provide interconnect and bundle interface objects to define entity structure interfaces.

With a normal object we have:

object_class object_name : subtype indication ;

e.g.

signal MySignal : Natural ;

We need a similar syntax for a bundle:

bundle MyBundle : ????? ;

The association with a bundle is not a type because it has a composite object structure, so let's call it a group type. The only valid format for this(???) is record based:


group type MyGrype is
   record
      signal      ElementA : ElementAType;
      signal      ElementB : ElementBType;
      shared
         variable ElementC : ElementCType;
      signal      ElementD,
      signal      ElementE,
      signal      ElementF : ElementDEFType;
   end record MyGrype;

A bundle can only be declared in a concurrent code region?

But what about passing into a subprogram. Then we may want variables and constant classes in the bundle group type as well.

This may imply a view of a group type should be able to modify the object class structure??

A bundle of signal declarations may need to be passed into a function as a bundle of constant declarations.

Map Views (not included within LCS-2016-045x) - Thoughts?

A lot of discussion has gone into the map function. However, this concept does not comply with our current understanding of a function.

Program languages in general define a function as a subprogram with input values and a single return value.

The map function defines a set of left interface objects and a single right interface object, but both sides will have (in general) a mix of modes defined by views. The mechanism provided by this 'map function' is to provide a mapping association instance to the right side parameters side to the left side return value.

So let us postulate it as a new construct and call it a map view. We start with a simple record type for a system bus::


type SysBusRecordType is
   record
      Select      : Std_uLogic;
      Rd_Wr       : Std_uLogic;
      Addr        : Std_uLogic_Vector(31 downto 0);
      RdData      : Std_uLogic_Vector(31 downto 0);
      RdDataValid : Std_uLogic;
      WrData      : Std_uLogic_Vector(31 downto 0);
   end record SysBusRecordType;

view SysBusRecordView of SysBusRecordType is
      Select      : in;
      Rd_Wr       : in;
      Addr        : in;
      RdData      : out;
      RdDataValid : out;
      WrData      : in;
   end view SysBusRecordView;

We then generate a map view subprogram(?) to re-map and generate a new view for the slave:


map view MySlaveMapView (
      Rst_i        : in   Std_uLogic;
      Clk_i        : in   Std_uLogic;
      SysBusRecord : view SysBusRecordView
   ) return MySlaveView is
begin
   return : view (
     MySlaveView.Rst    => Rst_i,
     MySlaveView.Clk    => Clk_i,
     null               => SysBusRecordView.Addr(31 downto 16),
     MySlaveView.Addr   => SysBusRecordView.Addr(15 downto  0),
     null               => SysBusRecordView.RdData(31 downto 8),
     MySlaveView.RdData => SysBusRecordView.RdData(7 downto 0),
     null               => SysBusRecordView.WrData(31 downto 8),
     MySlaveView.WrData => SysBusRecordView.WrData(7 downto 0),
     others             => others
   );
end map view MySlaveMapView;

Already defined is a slave record type and associated view and these differ from the system bus by having clock and reset included within the bus plus smaller address and data elements:


type MySlaveRecordType is
   record
      Rst         : Std_uLogic;
      Clk         : Std_uLogic;
      Select      : Std_uLogic;
      Rd_Wr       : Std_uLogic;
      Addr        : Std_uLogic_Vector(15 downto 0);
      RdData      : Std_uLogic_Vector( 8 downto 0);
      RdDataValid : Std_uLogic;
      WrData      : Std_uLogic_Vector( 8 downto 0);
   end record MySlaveRecordType;

view MySlaveView of MySlaveRecordType is
      Rst         : in;
      Clk         : in;
      Select      : in;
      Rd_Wr       : in;
      Addr        : in;
      RdData      : out;
      RdDataValid : out;
      WrData      : in;
   end view MySlaveView;

These are used in the entity declaration and the adaption to the system bus is done in the instance mapping by calling this new map view:


entity MySlave is (
      MySlaveBus : MySlaveView
   );
begin
   ...
end view MySlaveView;

MySlave_inst : MySlave (
   MySlaveBus => MySlaveMapView(Rst_s, Clk_s, SysBusRecord_s)
);

Comments

Topic revision: r25 - 2020-02-17 - 15:34:34 - JimLewis
 
Copyright © 2008-2025 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback