Bundles
Proposal Details
- Who Updates: LievenLemiengre
- Date Proposed: 2016-04-12
- Date Last Updated: 2016-04-12
- Priority: high
- Complexity: high
- Focus: rtl, testbench
Motivation
This proposal explores a method to abstract over port lists in VHDL.
Current Situation
Large parts of port lists are often duplicated multiple times across a design. The repetition occurs in port list definitions (in entities and components) and in port maps (in instantiations and configurations).
This code duplication is a code smell, changing the name, type or position of a port may require changes to port definitions and port maps across the entire design. The repetition also causes excessively long instantiations that make the code harder to maintain.
Workarounds
Records
There are two options
- Split the record based on port mode
- Many designs use this approach
- This approach doesn't scale when you nest records
- Make the record inout
Packages
A package with signals can be passed as a generic to an entity.
- No port modes
- An unnatural code style because the package is passed as a generic
Goals
- Maintainability
- Manage port names & types in one place
- Reuse parts of a port list to build bigger port lists
- Readability
- Reduce the verbosity of VHDL
- Testability
- A bundle can make it easier to write reusable tests.
- New abstractions
- Reusable operations that operate on bundles (use bundles in procedures & functions)
- Reusable mapping operations (map functions)
Non-Goals
This feature shouldn't change the way the simulator works. After elaboration, bundles should be expanded into ports and signals.
Success factors and risks
Port lists are used very frequently in VHDL. The payoff of this function could be very big. The impact on the language will be big at the same time there should be no friction with existing features. The benefits should outweigh the added complexity and increased learning curve of vhdl.
Requirements
A general exploration of the requirements can be found
here.
TODO: list a reduced set of requirements specific for bundles, other requirements must be moved to separate enhancement requiests.
Design exploration
This is my interpretation of the requirements and how they lead to the design. These questions are more or less in-order, one question leads to the next.
What is the contents of a bundle?
A bundle can contain the same things as a port_list. A port list element has a
kind, a
mode and a
type. Because a bundle has multiple kinds of information we call it heterogeneous. Kinds:
- ports: references to signals
- signals
- other bundles (but it can't contain itself)
- shared variables: future work, not part of this proposal
- vhdl-ams objects
Modes: (only relevant for ports?)
- for ports: in, out, inout, buffer
- for bundles: modport
- for shared variables: ???
- for vhdl-ams objects: ???
Type: see port_list rules
Can we build this feature on top of records?
I suspect that there is a vastly simpler proposal using records
if we drop the requirement that bundles contain
heterogeneous data. TODO: explore this design (basically records + modports), other object can be passed via a generic package?
Differences between bundles and records:
- Records can have resolution functions, bundles can't
- Bundles are a pure elaboration-time construct
- You can have signals with a record-type and variables with a record type, bundles can contain both & thus they are neither variables nor signals.
What is the impact on signal resolution and the event model?
- No resolution functions for bundles, the elements may or may not be resolved
- No additional delta cycles when bundles are connected, this should work like ports
- To get this behavior, all bundle connections need to be resolved at elaboration time
Should bundles have a type?
- Subtypes of bundles: you can constrain the elements of a bundle like you can constrain the elements of a record
- Arrays of bundles: the array element has a type
- Subprograms with bundle parameters: a type for overloading
What variants/subtypes are there of a bundles?
- Subtyping: constraining element types
- modport
- determine port modes
- export a subset of bundle elements
What attributes should bundles have?
- The same attributes as records?
- new attributes?:
- 'kind: is_signal, is_bundle, is_variable
- 'mode: is_in, is_out, is_inout, …
- I don't have practical use cases for these
Why 'bundle' and not 'interface' or something else?
I suspect 'interface' is a often used as an identifier, so for backwards compatibility 'bundle' may be a better choice.
Proposal
Bundle declaration
Declaration
A simple bundle declaration:
type bundle_t is bundle
signal sig1 : std_logic;
sig2 : std_logic; -- the default kind is signal
signal sig3 : std_logic_vector;
signal sig4 : std_logic_vector(7 downto 0);
variable var : shared_integer; -- a shared variable (future: not part of this proposal)
end bundle bundle_t;
- In a bundle declaration we declare the element kind and type
- Bundle elements initially don't need a mode, nor do they need to be constrained
- The only useful place to declare bundles is in a package but I see no reason to restrict the use.
A nested bundle declaration:
type nested_t is bundle
signal sig1 : std_logic;
signal sig2 : std_logic_vector(7 downto 0);
signal sig3 : std_logic_vector;
bundle nested : bundle_t; -- a nested bundle
end bundle nested_t;
- Bundles can be nested
- Nested bundles don't have a modport!
Grammar:
[§ 6.5.2] interface_object_declaration ::=
interface_constant_declaration
| interface_signal_declaration
| interface_variable_declaration
| interface_file_declaration
| interface_bundle_declaration
[§ ?.?.?] interface_bundle_declaration ::=
bundle identifier_list : subtype_indication
[§ 5.3.1] composite_type_definition ::=
array_type_definition
| record_type_definition
| bundle_type_definition
[§ ?.?.?] bundle_type_definition ::=
bundle
?? generic_list and/or discriminant_list ??
port_list
end [bundle] [bundle_type_simple_name]
Bundle subtype declaration
subtype nested_subtype_t is nested_t(sig2(7 downto 0));
We reuse the syntax used for record subtyping.
Modport declaration
A modport is applied to a bundle
- It sets the mode of individual elements
- It doesn't have to include all elements
How should modports work though?
- A modport is bound to a specific bundle!
- Where should it be defined?
- Outside of the bundle
- My choice
- Alows to create other modports separate of the bundle definition, for example in another package
- Inside of the bundle
- The alternative
- The person who defines de bundle defines all possible modports
modport master_of_bundle of bundle_t is
sig : out;
end modport master_bundle_t;
modport master_nested of nested_t is
sig1, sig2 : out;
nested : master_of_bundle;
end modport master_nested;
modport master_slave of nested_t is
sig1, sig2 : in; -- not all elements are exported
end modport master_nested;
grammar:
TODO
Bundle instantiation
Bundles are neither signals, variable or files so we need a new kind of declaration During instantiation we can choose to map objects into the bundle or create new objects
Raw bundle instantiation:
signal clk, rst : std_logic;
bundle b : bundle_t := (clk => clk, rst => rst, others => new);
You can't connect ports into a raw bundle instantiation
bundle invalid : nested_t := (
sig1 => input_port, -- not valid: ports have a mode, you can't turn ports into signals
others => new -- create the other elements
)
Bundle with modport instantiation can connect ports:
bundle valid : master_nested(nested_t) := (
sig1 => input_port, -- valid if the modport is respected in -> in
others => new -- create the other elements
)
Array of bundles declaration
We use the normal array type declaration to create an array of bundles.
type bundle_array is array(0 to 10) of bundle_t;
Note: no modport
Usage in a port list
In a port_interface_list
Bundle usage in port lists (found in entity declarations, component declarations & block statements)
A modport is required now! You can apply the modport to an array!
entity e is | component c is | blk : block is
port (
signal clk : in std_logic; -- already legal
rst : in std_logic; -- short form
bundle bnd : master(bundle_t); -- bundle in port-list
bundle bnd_array : master(bundle_array)
);
end entity e; | end component c; | begin end block blk;
In a parameter_interface_list
Bundle usage in subprograms (functions and procedures)
A modport is required now!
procedure p (
signal sig : in std_logic;
bundle bnd : master_nested(nested_t)
);
function f (
signal sig : in std_logic;
bundle bnd : master_nested(nested_t)
) return boolean;
Instantiation
Bundle usage with a port map --
To avoid nested mapping so I added a map function
architecture top of e is
component c is
port(
clk : in std_logic;
rst : in std_logic;
bundle bundle_v1 : master(bundle_t);
bundle bundle_v2 : master(bundle_t);
bundle bundle_v3 : master(bundle_t);
bundle bundle_ar : master(bundle_array_t)
);
end component c;
-- note: don?t have a modport!
bundle bundle_v : bundle_t := (clk => clk, rst => rst, others => new);
bundle bundle_ar : bundle_array_t; -- incomplete initialisation is an elaboration issue?
bundle bundle_slv : slave(bundle_t) := ( /*only bind slave part*/ )
map function select (
i : natural;
bundle b : master(bundle_array_t)
) to master(bundle_t) is
map (
a => b(i).a,
b => b(i).b,
);
end map function select;
begin
for_gen : for i in bnd_array'range generate
b_arr(i) <= (clk => clk, others => new);
end generate generate_label;
intst : component c
port map(
clk => clk,
rst => rst,
bundle_v1 => bundle_v,
bundle_v2 => (bundle_v.a, bundle_v.b),
bundle_v3 => select(1, bundle_v),
bundle_ar => bundle_ar
);
end architecture top;
Map functions (optional)
Elaboration-time functions that help mapping signals & bundles to another bundle
Used to avoid nested mapping (map ( x => y map (...) )
Reusable & improves readability: (example select_slave(i, master_bus))
map function mapper (
signal clk : in std_logic;
constant c : natural;
bundle bnd1 : master(bundle_t_1);
bundle bnd2 : slave(bundle_t_2);
) to master(bundle_t_3) is
map (
clk => clk,
foo => bnd1.foo,
baz => bnd2.baz
);
end map function mapper;
Changes to LRM
This is a placeholder, haven't looked these up yet, the grammar rules are from memory
- New keywords
- New *_declarative_items (package, entity, architecture, ..?)
- bundle_type_declaration
- bundle_declaration
- modport_declaration
- map_function
- Modified grammar rules
- port _interface_list
- parameter _interface_list
- a new primary rule '*new*' to support (others => new)
- Type system changes
- a new type 'bundle'
- part of composite types?
- can't be part of a record, protected and access type
Reflection
Identified 3 more or less orthogonal work areas:
- The type
- heterogenious data or homogenious data (records)
- Options
- We create a new kind of type: bundle
- a lot of duplication between record & bundle: as a good engineer I hate special cases
- weird interaction with records: bundles can't be part of a record
- requires new kind of data "bundle b : bundle_t := (...)"
- do we really need this data container?
- where does it fit in the type classification? Container or something else?
- We change the definition of records
- signal foo : record_type => what does this mean when record_type contains signals & variables?
- Split this off in another proposal
- The modport
- Creates a port structure from a composite type like record or an array of records
- Only applies to ports (signals): sortof clashes with bundles
- Uses a record or bundle as a template, the result is an "exploded" record, you can't go back to the underlying record
- Clean solution: reuse existing types
- How do we encode assignability between modports?
- some modports are directly compatible, for example assign to a modport with only "in" elements
- How do we apply a modport
- modport(record_t)
- record_t.modport
- record_t'modport
- <modport> record_t
- keyword(modport) record_t
- bundle some_name : modport record_t -- looking at the whole declaration bundle requires modport
- The mapping
- Convert one modport into another, without introducing delta cycles
- do type conversions
- apply selected names
- check access rules
- Necessary to avoid nested mapping in instantiations
Use Cases
Arguments FOR
Arguments AGAINST
General Comments
I think the idea of separating modports from the heterogeneity requirement of bundles makes a lot of sense, and I think I'll just talk about modports for the moment, and for the sake of argument it'll be modports on records of signals, which I think is the dominant use case.
We're currently asking modports to perform two different jobs; direction and adaptation. Let's talk first only about direction; all signals in the record go from in to out. In this case, the "record of signals" is akin to a bunch of wires, and the record structure is nothing more than a zip tie around those wires. If all a modport is asked to do is direction, then it's the equivalent of a keyed connector. The male connector goes on the record, the female on a bulkhead on the entity, and now you can only connect the record to the entity in a single "way" (these are in, those are out). Inside the entity you've just got the solder tails of the connector; they're just signals again.
If all you had was direction, you could handle adaptation with entities. They'd have two bulkhead connectors on the entity, one that fit one type of record and one that fit another. Nothing would inherently relate the two record types, you've just plugged in an adapter between the two cable bundles. (Ignore the delta cycle issue).
So now you expand the concept of a modport. It's not just a keyed connector (which is itself a physical entity). It's a keyed connector that may have some wiring inside, it moves some signals around and leaves some others out. And it can be bulkhead mounted, so you can put it directly on the sidewall of another entity and get one (keyed) record type on the outside of the entity, and solder tails for a different record type on the inside.
-- Rob Gaddi - 2016-04-28
Supporters
--
Lieven Lemiengre - 2016-04-12