The use of a component inside another component is called instantiation. The component that is instantiated, is the instance.
A complex design becomes manageable by subdividing it into smaller subcomponents. Recursively subdividing these subcomponents results in a hierarchical decomposition of the design. Instantiation is a key mechanism for the creation of hierarchical descriptions.
The example below shows how the register, as presented in the explanation of a component, is instantiated twice.
# subcomponent component reg word_length: generic integer = 8 T_IO : generic type = bitvector(word_length) data_in : in T_IO data_out : out T_IO register storage : T_IO = 0 begin storage = data_in data_out = storage end # top-level component component top word_length: generic integer = 12 T_topIO : generic type = bitvector(word_length) data_in : in T_topIO data_out : out T_topIO variable data_internal: T_topIO generate r1: reg T_IO = T_topIO data_in => data_in data_out => data_internal r2: reg word_length = word_length data_in => data_internal data_out => data_out begin # no functionality at this level end
The example illustrates the following points:
reg
is the register that is instatiated twice in top-level component top
.generate
.r1
and r2
. =
with a generic (type) at its left and a value at its right. Here, it is the intention to use bitvectors of length 12, overriding the default length of 8. For the first instance r1
, this is done by binding generic type T_IO
to T_IOtop
. In r2
, the same effect is achieved by binding generic word_length
of the instance to word_length
in the instaniating component.=>
with a local port name at its left and a wire of the instantiating environment at its right. In this case, the data_in
input of r1
is wired to the data_in
of top
and data_out
of r1
is wired to the wire data_internal
.
The example below is a slight variant of the example above. It shows
how instances of subcomponents can be created that use different types.
The
default generic type of the register is now signed
instead of bitvector
. The
type is changed to bitvector
when instantiating r1
.
Instantiation of r2
leaves the signed
type intact, but only
changes the vector length. The reinterpret
function is used to
make the design consistent (see the page on data types
for a detailed discussion of the Arx data types and reinterpretation).
Note that the generated VHDL description of this circuit will use two different entities for the two register instances.
# subcomponent component reg word_length: generic integer = 8 T_IO : generic type = signed(word_length) data_in : in T_IO data_out : out T_IO register storage : T_IO = 0 begin storage = data_in data_out = storage end # top-level component component top word_length: generic integer = 12 T_topIO : generic type = bitvector(word_length) data_in : in T_topIO data_out : out T_topIO type T_num: signed(word_length) variable data_internal: T_topIO r2_in, r2_out: T_num generate r1: reg T_IO = T_topIO data_in => data_in data_out => data_internal r2: reg word_length = word_length data_in => r2_in data_out => r2_out begin r2_in = reinterpret(T_num, data_internal) data_out = reinterpret(T_topIO, r2_out) end
The Arx code generators can only process a single Arx file. The current version of Arx does not have an include command to compose a design from sources distributed across multiple files.
An alternative design strategy is to describe different parts of a design with separate Arx files and generate C++ and VHDL for each of them. The different parts of the design can then be combined at the C++ or VHDL level. This is, actually, the only way to deal with multiple clock domains. In the current version of Arx, multiple instantiations of the same Arx component in a C++ simulation does not work due to name conflicts. A possible workaround is to create a wrapper model in Arx that instantiates a component as often as needed.