Add a "Bus" port mode for bidirectional port signals

Proposal:


Add a new mode in addition to (in, out, buffer, inout, linkage), tentatively called "bus".

"bus" is already a reserved word... perhaps a rarely used one.
I can see no ambiguity if it is overloaded with a second usage as the name for a new mode, as its use in a port or parameter list cannot be confused with its other uses.

Semantics of mode "bus"

If a complex port (array or record) on an entity has mode "bus", each member of that port will be treated individually to determine its actual mode, which remains implicit until the design is elaborated.

  • If it is undriven by the entity, its mode shall have the same effect as mode In.
  • If it is driven by the entity, its mode shall have the same effect as mode Out.
  • If it is both driven and read, its mode shall have the same effect as mode Inout.

Additionally, restrictions are applied such that in a complete elaborated design, each unresolved component of a signal to which a "bus" port or parameter is connected, must have exactly one driver; i.e. one "bus" port driven by an entity, or one signal assignment, or one Output/Buffer/Inout port connected to the same signal.

(Components with a resolved type may have multiple drivers, resolved in the usual way)

Bus ports may be connected via signals to Bus ports, or In, Out, Inout or Buffer ports, and there may be drivers on these signals, provided the above restrictions are met.

In other words a Bus port maps to a signal which may connect to any number of Bus ports, plus at most one Out port, or at most one signal assignment, or at most one Bus port on an entity where it is internally connected to an Out port or signal assignment.

The Bus mode is also legal for a subprogram parameter, to allow common BFM techniques to be used.

Implementability:

Few changes are required to a VHDL tool; the changes are roughly common to synthesis and simulation.

1) Wherever port or parameter modes are parsed or handled, the new mode name is allowed.
This extends an existing parser structure, requiring no new one.

2) When elaboration is complete, a post-elaboration check takes place.
At that point, each member of each bus signal must have:
exactly one driver if unresolved;
at least one driver if resolved.
(POSSIBLY : zero drivers permissible if there is an initialisation?)

This driver is either a signal assignment, or an OUT port, (or Inout or Buffer port), or a Bus port on an entity already known to contain one of the former.

Allowing the driver to be an INOUT port requires thought in case it turns out to be a mistake. The intent is not to allow multiple drivers on an unresolved Bus member, even if all but one are driving 'Z'. However it should be OK to accept an INOUT port as the single driver on unresolved types.

As the above rules would require an INOUT to be the sole driver on an unresolved signal, and the INOUT port must have a driver, this satisfies the "exactly one driver" rule. If the associated architecture fails to drive it

Failure to meet these conditions is an elaboration error which must be corrected before simulation or synthesis can proceed..

3) Having satisfied these tests, the tool may safely represent or even rewrite the design in terms of "in" "out" etc modes for the individual components and implement as normal; as long as such rewrite does not make diagnostics or debugging confusing to the user. This rewrite can be combined with the constraint check or immediately follow it.

In other words if a component has a "bus" record of which it drives one member, the effect is to make that member an "out" port and the others "in" ports, determined at elaboration time (including the evaluation of generics, and following satisfactory test for unique drivers)

For diagnostics or debugging it would be useful if messages displayed both the port mode and its "resolved mode" as "bus(in)" or "bus(out)"etc rather than the original "bus" or rewritten "out"

NOTES :


This would also encourage std_ulogic use where a single driver is the design intent.


Does this conflict with anything fundamental in determining array drivers and/or record drivers?

Apparently not as long as the driven elements are statically determinable - including determinable by generics. There are use cases for determining the driven element by generics, especially if that generic determines the identity of a slave. Thus "locally static" would be too restrictive.

It is desirable to allow an array port to have mode "bus" in addition to a record port, in which case each element of the array is treated as a separate "bus" signal in this respect. (Wording allows array of array, or array of record, or record of array, to be decomposed into individual components.)

Then for example, each bus slave can drive an individual bit of a "Request" or "Ack" vector. If that bit can be selected by evaluating a generic, it is easy to configure multiple slaves on a bus.

ADVANTAGES :

1) Simplicity

Allow a single Record type to be used as a bus, with different components driven in different directions. No changes required to Record declaration syntax.

Simplicity compared to proposals http://www.eda.org/twiki/bin/view.cgi/P1076/BlockInterfaces

or http://www.eda.org/twiki/bin/view.cgi/P1076/InterfaceConstructandPortModeConfigurations

2) Implementability

Little or no disruption to syntax, overloading an existing(rarely used?) keyword in a non-ambiguous manner; changes can be handled by parser and post-elaboration.

It is possible for a tool to parse a design using "bus" ports and rewrite it in VHDL-2008 in an exactly equivalent low-level form, e.g. with unambiguous individual "in"/"out"/"inout" ports for each member of a bus port. Such a tool could be a useful method to verify the proposal.

3) Safety

The required checks are performed at elaboration time and difficult to fool.

4) Generality

The "Bus" mode is not restricted to records; any type may be used this way, with Arrays (either alone or as Record components) being potentially useful.

DISADVANTAGES:

1) Checks are deferred until elaboration time.


Because there is not much visibility in the source code, code review will not easily discover the actual topology of the circuit.
Arguably a minor disadvantage, since elaboration lays it bare. Good tools can help here by displaying the actual elaborated structure.

2) Architecture details are exposed through the entity

Because the "Bus" mode forms a contract which remains flexible (or invisible) until elaboration, changes to an architecture (implementation) can modify the meaning of the entity (specification).

Mistakenly adding a driver in an architecture to a signal intended as an input, changes the meaning of the contract. Thus the error has potential effects outside the architecture in which it was made. The change is only detected when the design is elaborated.

Having been so detected, the design is in error and cannot proceed until corrected.

Nevertheless, this can be seen as breaking the strong principles of abstraction underlying VHDL's original design.

An alternative view is that "bus" is an abstraction which specifies a flexible contract. Therefore such flexibility is part of the abstraction, not a hole in it. In other words "the meaning of the entity" is : this port's actual mode is determined at elaboration.

This flexibility is safe (as far as I can see) because mistakes in the concretization (? reverse of abstraction) cannot escape the compiler checks; these checks therefore guarantee the abstraction.

The alternative proposals above add syntax such as "mode" fields to a record declaration to formalise a contract that cannot be accidentally rewritten in this way.

I am not convinced that the Record type declaration is the right place to apply these, and wonder if something lighter weight can specify (or even hint at) the intended contract in this proposal.

3) Assignments can be to a record element or an entire record, but not to a partial record.

Therefore an assignment ignoring some record members is not possible.

Bus_IO <= (Address => X"1234, Wr_Data => X"5555", Wr => '1', En => '1');   -- ACK, Rd_Data unwritten

Assignments to each individual component are obviously possible, but undesirably verbose.

Bus_IO.Address <= X"1234; 
Bus_IO.Wr_Data <= X"5555;

Two possible solutions exist without adding anything to THIS proposal:

1) Partial record updates:

Bus_IO <= (Address => X"1234, Wr_Data => X"5555", Wr => '1', En => '1', others => others);

where the "others" clause (or equivalent syntax) signifies that unspecified members keep their current value. This could be implemented as a separate proposal and there may already be such proposals. In this instance it would be an error to drive an "in" member, (i.e. all "in" members should be covered by the "others" clause) but any "out" member in the others clause would remain driven with its existing value. In this way, two independent new features add together to provide a compact syntax.

2) The Bus_IO record can itself contain a record as a component, all of whose components are of mode Out; a full assignment to this component would provide compact notation with no further language changes.This record component is identical to the current "Out" mode record in the present two-record solution.

Example of "bus" mode use.

Package declaring the bus_type record:

package definitions is

  type id_type is range 0 to 3;
  type id_array is array(id_type) of boolean;

  subtype wdata is std_ulogic_vector(31 downto 0);   -- UNresolved
  subtype rdata is std_logic_vector(31 downto 0);      -- Resolved across multiple slaves
  subtype addr is natural range 0 to 255;
  
  type bus_type is record
     Wr_Data : wdata;
     Rd_Data : rdata;
     Address : addr;
     En     : boolean;
     Wr     : boolean;
     Rd     : boolean;
     Rq     : id_array;
     Ack    : id_array;
  end record;

end definitions;

Bus master with "bus" mode port:

use work.definitions.all;

entity master is
port ( IO : bus bus_type );
end master;

architecture Behavioral of master is
begin

   -- Drive bus signals
   -- IO.Rd_Data and IO.Ack undriven
   IO.Wr_Data <= (others => '0');
   IO.Address  <= (others => '0');
   IO.En     <= false;
   IO.Wr     <= false;
   IO.Rd     <= false;
   IO.Rq     <= (others => False);

end Behavioral;

Slave with "bus" mode port, and generic to identify it:

use work.definitions.all;

entity slave is
generic ( id : id_type := id_type'left );
port ( IO : bus bus_type );
end slave;

architecture Behavioral of slave is
begin

   -- Drive bus signals; only drive selected bit of Ack
   IO.Rd_Data <= (others => '0') when IO.Rq(id) and IO.En and IO.Rd
                            else (others => 'Z');
   IO.Ack(id) <= IO.Rq(id) and IO.En and IO.Rd;

end Behavioral;

Top level design instantiating a master and two slaves.

entity toplevel is
end toplevel;
use work.definitions.all;

architecture Behavioral of toplevel is    
   signal Main_Bus : bus_type;
begin

 Bus_Master : entity work.master 
   port map ( IO => Main_Bus );  

 First_Slave : entity work.slave 
   generic map ( id => 1 ) 
   port map ( IO => Main_Bus );  

 Second_Slave : entity work.slave 
    generic map ( id => 2 ) 
    port map ( IO => Main_Bus );  

end Behavioral;

End of example.

Topic revision: r3 - 2016-11-15 - 18:23:42 - BrentHahoe
 
Copyright © 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback