#pagebreak()

= Grouping <grouping>

Grouping is a feature of the FBDL used to inform a compiler that particular functionalities might be accessed together, and their register location must meet additional constraints.
This is achieved using the group functionality described in @group.
The following functionalities can be grouped:
- config,
- group,
- irq,
- mask,
- param,
- return,
- static,
- satus.
However, not all groupable functionalities can belong to the same group.
For example, a config shall not be grouped with a param, as param is an inner functionality of a proc, and config instantiation within a proc is invalid.

#block(breakable: false)[

The following snippet presents three grouped configs.

#pad(left: 1em)[
```fbd
Main bus
  Config_Group group
    type cfg_t; width = 8;
    A cfg_t
    B cfg_t
    C cfg_t
```
] 
]

Any FBDL compliant compiler must place all three configs (`A`, `B`, `C`) in the same register.

== Single register group

A single register group is a group of functionalities that fit a single register.
The overall width of all functionalities is not greater than the single register width.
In such a case, all functionalities must be placed in the same register.
The specification does not impose any specific order of the functionalities within the register, and it is left to the compiler
implementation.
The following listing presents an example bus description with three single register groups.

#block(breakable: false)[
#pad(left: 1em)[
```fbd
Main bus
  Read_Write group
    C0 config; width = 16
    M0 mask;   width = 15
  Mixed group
    C1 config;  width = 16
    S11 static; width = 8
    S12 status; width = 8
  Read_Only group
    S21 status; width = 4
    S22 status; width = 7
```
]
]

All functionalities of the `Read_Write` group can be both read and written.
The code generated by a compiler for the requester must provide means for reading/writing the whole group as well as for reading/writing particular functionalities of the group.

The `Mixed` group contains functionality that can be read and written (`C1`), as well as functionalities that can only be read (`S11`, `S12`).
The code generated by a compiler for the requester must provide a means for reading all readable functionalities and writing all writable functionalities.
It is valid even if the group has single readable or single writable functionality.
The compiler must also generate means for reading/writing particular functionalities of the group.
In the case of `Mixed` group this will result in two means doing exactly the same (writing the `C1` config).
However, it is up to the user to decide which of the means should be used.
If it makes sense, it is perfectly valid to use both of them in different contexts.

All functionalities of the `Read_Only` group are read-only.
In this case, the compiler must generate a mean only for reading the group.
It must also generate means for reading particular functionalities.

== Multi register group

A multi register group is a group with functionalities that overall width is greater than the width of a single register.
The specification does not impose any order of functionalities or registers in such cases, and it is left to the compiler implementation.
However, the compiler must not split functionalities narrower or equal to the register width into multiple registers.
This implies that any functionality with a width not greater than the register width is always read or written using single read or write access.
The following snippet presents a bus description with one multi register group.

#block(breakable: false)[
#pad(left: 1em)[
```fbd
Main bus
  Multireg group
    C config;  width = 10
    M mask;    width = 10
    SC static; width = 10
    SS status; width = 10
```
]
]

The compiler must generate code for the requester allowing to write all writable functionalities of the group as well
as the code allowing reading all readable functionalities of the group.
It must also generate means for reading or writing particular functionalities.

There are multiple ways to place functionalities from the above example into registers.
The following snippet presents one possible way.

#block(breakable: false)[
#pad(left: 1em)[
```
        Nth register              Nth + 1 register
-----------------------------  ----------------------
|| C | M | SC | 2 bits gap ||  || SS | 22 bits gap ||
-----------------------------  ----------------------
```
]
]

However, the above arrangement might not be optimal if there is a need to read both `SC` and `SS` at the same time as it would require reading two registers not a single one.
The below listing presents how to group elements within the group using subgroups.

#block(breakable: false)[
#pad(left: 1em)[
```fbd
Main bus
  User group
    RW group
      C config; width = 10
      M mask;   width = 10
    Read_Only group
      SC static; width = 10
      SS status; width = 10
```
]
]

The set of possible functionalities placements within the registers is now limited as the `RW` and `Read_Only` subgruops are registerified before the `User` group.
The below snippet shows a possible arrangement.

#block(breakable: false)[
#pad(left: 1em)[
```
       Nth register              Nth + 1 register
-------------------------  ---------------------------
|| C | M | 12 bits gap ||  || SC | SS | 12 bits gap ||
-------------------------  ---------------------------
```
]
]

This time reading both `SC` and `SS` requires reading only one register, while reading the whole `User` group still requires reading two registers.

== Array groups

An array group is a group with all functionalities being arrays.
The functionalities do not necessarily have the same number of elements.
 
The code generated by a compiler, for an array group, for the requester must provide a means for writing an arbitrary number of elements for all writable functionalities starting from an arbitrary index.
It must also provide a mean for reading an arbitrary number of elements for all readable functionalities starting from an arbitrary index.

The specification does not define what happens on access to the elements with an index greater than the length of some arrays.
This is because some of the target languages support special data types indicating that the value is absent (for example, `None` - Python, `Option` - Rust), while others use for this purpose completely valid values (`0` - C, Go).

=== Single register array group

The single register array group is an array group with overall single elements width not greater than the width of a single register.
The below listing presents an example bus description with a single register array group.

#block(breakable: false)[
#pad(left: 1em)[
```fbd
Main bus
  Single_Reg_Array group
    type cfg_t config; width = 8;
    A [1]cfg_t
    B [2]cfg_t
    C [3]cfg_t
    D [3]status; width = 8
```
]
]

In the case of a single register array group all elements with corresponding indices must be placed in the same register.
Elements with consecutive indexes must be placed in consecutive registers.
The below snippet presents a possible arrangement of elements for the example bus.

#block(breakable: false)[
#pad(left: 1em)[
```
         Nth register
-------------------------------
|| D[0] | C[0] | B[0] | A[0] ||
-------------------------------
         Nth + 1 register
-------------------------------------
|| D[1] | C[1] | B[1] | 8 bits gap ||
-------------------------------------
         Nth + 2 register
-------------------------------
|| D[2] | C[2] | 16 bits gap ||
-------------------------------

```
]
]

=== Mutli register array group

The multi register array group ia an array group with overall single elements width greater than the width of a single register.
The below listing presents an example bus description with a multi register array group.

#block(breakable: false)[
#pad(left: 1em)[
```fbd
Main bus
  Multi_Reg_Array group
    A [1]config; width = 16
    B [2]config; width = 12
    C [2]config; width = 12
```
]
]

In the case of multi register array group all elements with corresponding indices must be placed in consecutive registers.
Also all elements with consecutive indexes must be placed in consecutive registers.
Such a requirement guarantees that block access can always be used.
The below snippet presents possible arrangement of elements for the example bus.

#block(breakable: false)[
#pad(left: 1em)[
```
         Nth register                Nth + 1 register
------------------------------   ------------------------
|| C[0] | B[0] | 8 bits gap ||   || A[0] | 16 bits gap ||
------------------------------   ------------------------
       Nth + 2 register                 Nth + 3 register
------------------------------   ------------------------------
|| C[1] | B[1] | 8 bits gap ||   || C[2] | B[2] | 8 bits gap ||
------------------------------   ------------------------------
```
]
]

=== Mixed group

A mixed group is a group with both single functionalities and array functionalities.
The below listing presents an example bus description with a mixed group.

#block(breakable: false)[
#pad(left: 1em)[
```fbd
Main bus
  Mixed group
    C config; width = 10
    M mask;   width = 7
    S status; width = 8
    CA [3]config; width = 10
    SA [3]config; width = 12
```
]
]

In case of a mixed group, array functionalities shall be registerified as the first ones assuming a pure array group.
Single functionalities shall be later placed in the gaps created during array registerification.
If there are no gaps, or gaps are not wide enough, then all reamining single functionalities shall be registerified as single register group or multi register group.
If the gaps are wide enough to place single functionalities there, but for some reason it is not desired, then subgroup can be defined to group single functionalities of the mixed group as the first ones.
The below snippet presents a possible arrangement of elements for the example bus.

#block(breakable: false)[
#pad(left: 1em)[
```
      Nth register                  Nth + 1 register
-----------------------   ------------------------------------
|| CA[0] | SA[0] | C ||   || CA[1] | SA[1] | M | 3 bits gap ||
-----------------------   ------------------------------------
          Nth + 2 register
------------------------------------
|| CA[2] | SA[2] | S | 2 bits gap ||
------------------------------------
```
]
]

== Irq group

The irq group is used for interrupt grouping.
Grouped irqs have a common interrupt consumer signal.
Irqs belonging to the same group might have different values of the producer trigger (in-trigger property), but all of them must have the same value for the consumer trigger (out-trigger property).
In the case of level-triggered interrupt consumer the information on the interrupt source can be read from the interrupt group flag register.

#block(breakable: false)[

The below snippet shows an example of an irq group for level-sensitive interrupt consumer.

#pad(left: 1em)[
```fbd
Main bus
  IRQ group
    type irq_t irq; add-enable = true
    IRQ0 irq_t
    IRQ1 irq_t; clear = "On Read"
    IRQ2 irq_t; in-trigger = "Edge"
    IRQ3 irq_t; in-trigger = "Edge"; clear = "On Read"

```
]
]

The picture below presents a possible logical block diagram of the `IRQ` group with level trigger for the interrupt consumer and enable register.
The `"Clear On Read"` signal is driven on every Flag Register read.
The `"Explicit Clear"` signal must be driven when the requester calls a means for clearing given interrupt flags.
Probably the easiest form of the `"Explicit Clear"` implementation is clear on Flag Register write, where the clear bit-mask is the value of the data bus.
The Flag Register is to some extent a virtual register, as it has an address, but it does not have any storage elements.
The flag is stored in the interrupt producer in case of a level-triggered producer or in the Edge Detector in case of an edge-triggered producer.

#set align(center)
#image("images/irq-group.png", width: 70%)
#set align(left)

== Param and return groups

Param and return groups are used to group proc or stream parameters or returns.
Such a kind of grouping may be necessary for performance optimizations, as the requester may store parameters or returns in a single list or in multiple distinct lists.
Param and return groups help to avoid data reshuffling before or after the access.
Param and return groups are independent.
The below snippet presents a valid description with a single proc with one param and one return group.

#block(breakable: false)[
#pad(left: 1em)[
```fbd
Main bus
  P proc
    Params group
      p1 param
      p2 param
    Returns group
      r1 return
      r2 return
```
]
]

Param and return groups may contain subgroups.
The below snippet presents examples of two invalid and two valid params and returns grouping.
#block(breakable: false)[
#pad(left: 1em)[
```fbd
Main bus
  # Param and return groups must have distinct names.
  Invalid1 proc
    G group
      p1 param
      p2 param
    G group
      r1 return
      r2 return
  # Param and return must not be placed in the same group.
  Invalid2 proc
    G group
      p param
      r return
  # Param and return groups might have subgroups.
  Valid1
    Params group
      p1 param
      Subgroup group
        p2 param
        p3 param
  # Params and returns in different groups might have the same names.
  Valid2
    Params group
      x param
      y param
    Returns group
      x return
      y return
```
]
]
