blob: e52cbe355143ec4f20b1049f5d257f1b89d2329b [file] [log] [blame] [view]
# Crossbar Generation Tool
The crossbar tool `tlgen.py` is used to build the TL-UL crossbar RTL.
It can be used standalone or invoked as part of top module generation process (details of top generation forthcoming).
The RTL files found `hw/top_earlgrey/ip/xbar/rtl/autogen` are generated with the crossbar tool.
This document does not specify the details of the internal blocks.
See the [bus specification](../../hw/ip/tlul/README.md) for details on the protocol and the components used in the crossbar.
## Standalone tlgen.py
The standalone utility `tlgen.py` is a Python script to read a crossbar Hjson configuration file and generate a crossbar package, crossbar RTL, and DV test collateral for checking the connectivity.
The `--help` flag is provided for details on how to run the tool.
### Example and Results
An example of the crossbar Hjson is given in `util/example/tlgen/xbar_main.hjson`.
The package file and RTL can be created by the command below:
```console
$ util/tlgen.py -t util/example/tlgen/xbar_main.hjson -o /tmp/
```
This creates files in `/tmp/{rtl|dv}`.
While generating the RTL, the tool adds detailed connection information in the form of comments to the RTL header.
## Configuration File Format
The `tlgen` script reads an Hjson file containing the crossbar connections and the host and device information.
The two main sections of the configuration file are the list of nodes and the list of connections.
Together, these describe a generic Directed Acyclic Graph (DAG) with some additional clock information and steering information.
If the tool is used in the process of top generation (`topgen.py`, details forthcoming), a few fields are derived from the top Hjson configuration module structure.
A description of Hjson and the recommended style is in the [Hjson Usage and Style Guide](../../doc/contributing/style_guides/hjson_usage_style.md).
An item in the `nodes` list corresponds to a host or device interface on an instantiated IP block.
The name of such a node should be of the form `<instance_name>.<interface_name>`, so the `my_if` interface on the instance `my_instance` would be denoted `my_instance.my_if`.
However, many instances have a single, unnamed, device interface.
For these devices, the node should just be named with the name of the instance (just `my_instance` in the example above).
For details of how interfaces are defined using the register tool, see the [Bus Interfaces](../../doc/contributing/hw/comportability/README.md#bus-interfaces) section of the Comportability Specification.
Edges in the DAG are specified in the `connections` map.
This uses an adjacency list format.
The keys are the names of host nodes.
The value at a host a list of the names of device nodes which that host should be able to see.
### Configuration file syntax
The tables below describe the keys for each context.
The tool raises an error if *Required* keys are missing.
*Optional* keys may be provided in the input files.
The tool also may insert the optional keys with default value.
<!-- BEGIN AUTOGEN python3 util/selfdoc.py tlgen -->
The tables describe each key and the type of the value. The following
types are used:
Type | Description
---- | -----------
int | integer (binary 0b, octal 0o, decimal, hex 0x)
xint | x for undefined otherwise int
bitrange | bit number as decimal integer, or bit-range as decimal integers msb:lsb
list | comma separated list enclosed in `[]`
name list | comma separated list enclosed in `[]` of one or more groups that have just name and dscr keys. e.g. `{ name: "name", desc: "description"}`
name list+ | name list that optionally contains a width
parameter list | parameter list having default value optionally
group | comma separated group of key:value enclosed in `{}`
list of group | comma separated group of key:value enclosed in `{}` the second entry of the list is the sub group format
string | string, typically short
text | string, may be multi-line enclosed in `'''` may use `**bold**`, `*italic*` or `!!Reg` markup
tuple | tuple enclosed in ()
python int | Native Python type int (generated)
python Bool | Native Python type Bool (generated)
python list | Native Python type list (generated)
python enum | Native Python type enum (generated)
### Top configuration
Crossbar configuration format.
Field | Kind | Type | Description
----- | ---- | ---- | ------------
name | required | string | Name of the crossbar
clock | required | string | Main clock. Internal components use this clock. If not specified, it is assumed to be in main clock domain
reset | required | string | Main reset
connections | required | group | List of edge. Key is host, entry in value list is device
clock_connections | required | group | list of clocks
nodes | required | list of group | List of nodes group
type | optional | string | Indicate Hjson type. "xbar" always if exist
clock_group | optional | string | Remnant from auto-generation scripts. Ignore.
clock_srcs | optional | group | Remnant from auto-generation scripts. Ignore.
domain | optional | string | Power domain for the crossbar
reset_connections | added by tool | group | Generated by topgen. Key is the reset signal inside IP and value is the top reset signal
### Node configuration
Crossbar node description. It can be host, device, or internal nodes.
Field | Kind | Type | Description
----- | ---- | ---- | ------------
name | required | string | Module instance name
stub | required | python Bool | Real node or stub. Stubs only occupy address ranges
type | required | string | Module type: {"host", "device", "async", "socket_1n", "socket_m1"}
clock | optional | string | main clock of the port
reset | optional | string | main reset of the port
pipeline | optional | python Bool | If true, pipeline is added in front of the port
req_fifo_pass | optional | python Bool | If true, pipeline fifo has passthrough behavior on req
rsp_fifo_pass | optional | python Bool | If true, pipeline fifo has passthrough behavior on rsp
inst_type | optional | string | Instance type
xbar | optional | python Bool | If true, the node is connected to another Xbar
addr_range | optional | list of group | List of addr_range group
### Address configuration
Device Node address configuration. It contains the base address and the size in bytes.
Field | Kind | Type | Description
----- | ---- | ---- | ------------
base_addr | required | int | Base address of the device. It is required for the device
size_byte | required | int | Memory space of the device. It is required for the device
<!-- END AUTOGEN -->
## Fabrication process
The tool fabricates a sparse crossbar from the given Hjson configuration file.
In the first step, the tool creates Vertices (Nodes) and Edges.
Then it creates internal building blocks such as Asynchronous FIFO, Socket `1:N`, or Socket `M:1` at the elaboration stage.
Please refer to `util/tlgen/elaborate.py` for details.
### Traversing DAG
The tool, after building Nodes and Edges, traverses downstream from every Host node downstream during elaboration.
In the process of traversal, it adds internal blocks as necessary.
Once all Nodes are visited, the tool completes traversing then moves to RTL generation stage.
1. Generates Nodes and Edges from the Hjson.
Start node should be Host and end node should be Device.
2. (`for loop`) Visit every host
3. If a node has different clock from main clock and not Async FIFO:
1. (New Node) Create Async FIFO Node.
2. If the Node is host, revise every edge from the node to have start node in Async FIFO.
(New Edge) Create an Edge from the Node to Async FIFO.
Then go to Step 3 with Async FIFO Node.
Eventually, the connection is changed to `host -> async_fifo -> downstream` from `host -> downstream`.
3. Revise every Edge to the Node to have Async FIFO as a downstream port.
(New Edge) Create an Edge from the Async FIFO to the Node.
4. If it is not Device, raise Error.
If it is, repeat from Step 2 with next item.
4. If a Node has multiple Edges pointing to it as a downstream port, set `nodes -> this node` and create Socket `M:1`,
1. (New Node) Create Socket `M:1`.
2. Revise every Edge to the Node to point to this Socket `M:1` as a downstream port
3. (New Edge) Create an Edge from Socket `M:1` to the Node.
The new connection appears as `nodes -> socket_m1 -> this node`.
4. Repeat from Step 3 with the Node.
5. If a Node has multiple Edges and is not already a Socket `1:N`,
1. (New Node) Create Socket `1:N` Node.
2. Revise every Edge from the Node to point to Socket `1:N` as an upstream port
3. (New Edge) Create an Edge from the Node to Socket `1:N`.
4. (for loop) Repeat from Step 3 with Socket `1:N`'s downstream Nodes.
Below shows an example of 2 Hosts and 2 Devices connectivity.
![Example Topology](./doc/crossbar_example_1.svg)
Each circle represents a Node and an arrow represents an Edge that has downward direction.
The tool starts from `H0` Node.
As the Node has two downstream Edges and not Socket `1:N`, the tool creates Socket `1:N` based on the condition #5 above.
Then repeat the process from Socket `1:N` Node's children Nodes, `D0` and `D1`.
![Socket 1:N instantiated](./doc/crossbar_example_2.svg)
For `D0`, the tool creates Socket `M:1` based on the condition #4.
It then visit its downstream Node, `D0` again.
In this case, it doesn't create an Async FIFO as the clock is same as main clock.
So it reached the terminal Node.
Then it visits `D1`.
It repeats the same step (condition #4) as `D0`, which creates another Socket `M:1`.
![Socket M:1 instantiated to D0, D1](./doc/crossbar_example_3.svg)
As all Nodes from `H0` have been visited, the tool repeats all steps from `H1`.
It applies condition #3 above as `H1` has a peripheral clock rather than main clock.
So the tool creates an Async FIFO and moves the pointer to the Node and repeats.
![Async FIFO instantiated to H1](./doc/crossbar_example_4.svg)
The tool applies rule #5 as Async FIFO has multiple downstream Nodes (Edges) and it is not Socket `1:N`.
The tool creates a Socket `1:N` and visits every downstream Node.
![Socket 1:N instantiated to `AS_a`](./doc/crossbar_example_5.svg)
Both Nodes have been processed, so no condition is hit.
The tool completes traversing.
### Numbering
After the traversing is completed, Hosts and Devices only have one Edge and internal Sockets have multiple Edges.
The tool assigns increasing numbers to each Edge starting from 0.
This helps the tool to connect between Nodes.
### Propagating the steering information (Address)
After the numbering is done, the tool propagates the steering information, addressing every Device Node to the upstream node until it hits a Socket `1:N`.
Socket `1:N` is the only module that requires `base_addr` and `size_bytes` information.
It is determined that at most one Socket `1:N` exists on the path from a Host to a Device within a crossbar.
If any SoC requires a multi-tiered crossbar design, it should create multiple crossbars to communicate with each other.
This condition does not exist in the current design.
### Connection information
The tool creates DAG connections when it creates RTL to help understanding the fabric.
The information is put as a comment in the header of the RTL.
For instance, with the above 2x2 example, the following information is created.
```console
$ util/tlgen.py -t util/example/tlgen/xbar_2x2.hjson -o /tmp/
$ cat /tmp/rtl/xbar_2x2.sv
// ...
// Interconnect
// h0
// -> s1n_4
// -> sm1_5
// -> d0
// -> sm1_6
// -> d1
// h1
// -> asf_7
// -> s1n_8
// -> sm1_5
// -> d0
// -> sm1_6
// -> d1
// ...
```