.. _module_axi_data_width_converter:
Module axi_data_width_converter
===============================
This document contains technical documentation for the ``axi_data_width_converter`` module.
This module provides small footprint blocks for AXI4 and AXI3
data width conversion.
Throughout this module, the ``left`` port is the port where the
width converter is a slave, and the ``right`` port is where the width converter
is a master.
The naming convention is analogous to how block designs typically are pictured.
.. digraph:: my_graph
graph [ dpi = 300 ];
rankdir="LR";
cloud [ label="Your module\nAXI master" shape=none, image="cloud_700.png"];
cloud -> axi_width_converter [label="32" dir="both"];
axi_width_converter [ shape=box label=<
Left |
AXI data width converter |
Right |
>];
axi_width_converter -> ddr [label="64" dir="both"];
ddr [ label="DDR" shape=box];
The example block diagram above illustrates `upconversion`.
This is when the ``right`` port, the DDR side, is wider than the ``left`` port, the user module side.
This terminology is used for both read and write.
In the opposite case, when the ``left`` port is wider than the ``right`` port, a `downconversion` will occur.
.. digraph:: my_graph
graph [ dpi = 300 ];
rankdir="LR";
cloud [ label="Your module\nAXI master" shape=none, image="cloud_700.png"];
cloud -> axi_width_converter [label="64" dir="both"];
axi_width_converter [ shape=box label=<
Left |
AXI data width converter |
Right |
>];
axi_width_converter -> ddr [label="32" dir="both"];
ddr [ label="DDR" shape=box];
The user would typically instantiate :ref:`axi_data_width_converter.axi_read_data_width_converter`
or :ref:`axi_data_width_converter.axi_write_data_width_converter`.
These entities have the following generics:
- ``left_width``: Sets the data width on the left port
- ``right_width``: Sets the data width on the right port.
- ``id_width``: Sets the width of the ID field. Smaller values reduce resource utilization.
- ``max_burst_length_beats``: Typical (and maximum) values are 256 for AXI4 and 16 for AXI3.
Specification
-------------
1. The module provides data width upconversion and downconversion for AXI read and write transactions.
2. Apart from limitations below, it is fully compliant with the AMBA AXI4 Specification.
Limitations
___________
1. These AXI4 signals are not included in the interfaces, and are assumed
to be constant:
- Lock type: ``AxLOCK``
- Memory type: ``AxCACHE``
- Protection type: ``AxPROT``
- Quality of service: ``AxQOS``
- Region identifier: ``AxREGION``
- User-defined signaling: ``AxUSER`` and ``xUSER``
- Write ID (only used by AXI3): ``WID``
2. If ``AxLOCK`` is asserted, the user must take care that a downconversion
does not result in burst splitting.
3. Always set ``AxCACHE`` to be modifiable. During downconversion,
the burst must be modifiable to set the new burst size.
During upconversion, the implementation of this module requires
that the burst is modifiable to allow data to be read as efficiently as possible.
4. Only ``INCR`` bursts may be used, that is ``AxBURST`` must be set to ``b01``.
5. The module does not support ``AxSIZE`` in the general case.
The ``SIZE`` s used must be either ``left_width`` or ``right_width``.
6. AXI standard demands there be no combinatorial paths between input and output handshake signals (``ready`` and ``valid``).
This rule is not honored in this module, since it increases logic footprint and is not necessary to reach timing.
7. The module does not have any reset functionality.
The design targets modern SRAM-based FPGAs, where initial values can be used and there is no need for reset.
.. _axi_data_width_converter.axi_data_width_converter_pkg:
axi_data_width_converter_pkg.vhd
--------------------------------
Package with utility functions used in the rest of the module.
.. _axi_data_width_converter.axi_read_data_width_converter:
axi_read_data_width_converter.vhd
---------------------------------
.. symbolator::
component axi_read_data_width_converter is
generic (
left_width : positive;
right_width : positive;
id_width : natural range 0 to axi_id_sz;
max_burst_length_beats : positive
);
port (
clk : in std_ulogic;
--# {{}}
left_m2s : in axi_read_m2s_t;
left_s2m : out axi_read_s2m_t;
--
right_m2s : out axi_read_m2s_t;
right_s2m : in axi_read_s2m_t
);
end component;
Wrapper module, instantiating either an
:ref:`upconverter `
or :ref:`downconverter `
depending on the bus widths.
A throttling of different IDs is performed when width converting AXI reads.
Once a burst has been negotiated, upcoming bursts from the ``left`` side will be blocked
until the previous burst has finished, if they have different IDs.
See :ref:`axi_data_width_converter.burst_adapter` for more information.
Resource utilization
____________________
This entity has `netlist builds `__ set up with
`automatic size checkers `__
in ``module_axi_data_width_converter.py``.
The following table lists the resource utilization for the entity, depending on
generic configuration.
.. list-table:: Resource utilization for axi_read_data_width_converter.vhd netlist builds.
:header-rows: 1
* - Generics
- Total LUTs
- FFs
* - left_width = 32
right_width = 64
id_width = 0
max_burst_length_beats = 256
- 91
- 225
* - left_width = 64
right_width = 32
id_width = 0
max_burst_length_beats = 256
- 177
- 352
* - left_width = 32
right_width = 64
id_width = 24
max_burst_length_beats = 256
- 123
- 231
* - left_width = 64
right_width = 32
id_width = 24
max_burst_length_beats = 256
- 242
- 454
.. _axi_data_width_converter.axi_read_data_width_downconverter:
axi_read_data_width_downconverter.vhd
-------------------------------------
.. symbolator::
component axi_read_data_width_downconverter is
generic (
left_width : positive;
right_width : positive;
id_width : natural range 0 to axi_id_sz;
max_burst_length_beats : positive
);
port (
clk : in std_ulogic;
--# {{}}
left_m2s : in axi_read_m2s_t;
left_s2m : out axi_read_s2m_t;
--
right_m2s : out axi_read_m2s_t;
right_s2m : in axi_read_s2m_t
);
end component;
When downconverting, one input burst received on the ``left`` port may result
in two output bursts to be sent on the ``right`` port.
This module keeps track of and combines the responses in this case.
When combining different responses, this is the prioritization:
1. ``DECERR``
2. ``SLVERR``
3. ``OKAY``
4. ``EXOKAY``
For example, if one transaction resulted in ``OKAY`` and one in ``DECERR``, the
combined response will be ``DECERR``.
.. _axi_data_width_converter.axi_read_data_width_upconverter:
axi_read_data_width_upconverter.vhd
-----------------------------------
.. symbolator::
component axi_read_data_width_upconverter is
generic (
left_width : positive;
right_width : positive;
id_width : natural range 0 to axi_id_sz;
max_burst_length_beats : positive
);
port (
clk : in std_ulogic;
--# {{}}
left_m2s : in axi_read_m2s_t;
left_s2m : out axi_read_s2m_t;
--
right_m2s : out axi_read_m2s_t;
right_s2m : in axi_read_s2m_t
);
end component;
When upconverting, the base address and burst length on the ``left`` side might
result in transfers that are not aligned with the natural boundaries on the ``right``
side. That means that in some scenarios we will have to read more data than we actually
want from the ``right`` side.
Any padding from the ``right`` side, at the beginning or the end, will be removed before
passing on to the ``left`` side.
The last data beat on the ``right`` port will have the ``last``
flag set, which might be a padding word. The padding removal state machine takes care
of forwarding the ``last`` flag on the last non-padding beat.
Which beat in the burst this is depends on the burst length and base address.
.. _axi_data_width_converter.axi_write_data_width_converter:
axi_write_data_width_converter.vhd
----------------------------------
.. symbolator::
component axi_write_data_width_converter is
generic (
left_width : positive;
right_width : positive;
id_width : natural range 0 to axi_id_sz;
max_burst_length_beats : positive
);
port (
clk : in std_ulogic;
--# {{}}
left_m2s : in axi_write_m2s_t;
left_s2m : out axi_write_s2m_t;
--
right_m2s : out axi_write_m2s_t;
right_s2m : in axi_write_s2m_t
);
end component;
Wrapper module, instantiating either an
:ref:`upconverter `
or :ref:`downconverter `
depending on the bus widths.
Resource utilization
____________________
This entity has `netlist builds `__ set up with
`automatic size checkers `__
in ``module_axi_data_width_converter.py``.
The following table lists the resource utilization for the entity, depending on
generic configuration.
.. list-table:: Resource utilization for axi_write_data_width_converter.vhd netlist builds.
:header-rows: 1
* - Generics
- Total LUTs
- FFs
* - left_width = 32
right_width = 64
id_width = 0
max_burst_length_beats = 256
- 93
- 236
* - left_width = 64
right_width = 32
id_width = 0
max_burst_length_beats = 256
- 209
- 355
* - left_width = 32
right_width = 64
id_width = 24
max_burst_length_beats = 256
- 93
- 236
* - left_width = 64
right_width = 32
id_width = 24
max_burst_length_beats = 256
- 245
- 385
.. _axi_data_width_converter.axi_write_data_width_downconverter:
axi_write_data_width_downconverter.vhd
--------------------------------------
.. symbolator::
component axi_write_data_width_downconverter is
generic (
left_width : positive;
right_width : positive;
id_width : natural range 0 to axi_id_sz;
max_burst_length_beats : positive
);
port (
clk : in std_ulogic;
--# {{}}
left_m2s : in axi_write_m2s_t;
left_s2m : out axi_write_s2m_t;
--
right_m2s : out axi_write_m2s_t;
right_s2m : in axi_write_s2m_t
);
end component;
When downconverting, one input burst received on the left port may result
in two output bursts to be sent on the right port.
This module keeps track of and combines the responses in this case.
When downconverting a throttling of different IDs is performed.
Once a burst has been negotiated, upcoming bursts from the ``left`` side will be blocked
until the previous burst has finished, if they have different IDs.
See :ref:`axi_data_width_converter.burst_adapter` for more information.
.. _axi_data_width_converter.axi_write_data_width_upconverter:
axi_write_data_width_upconverter.vhd
------------------------------------
.. symbolator::
component axi_write_data_width_upconverter is
generic (
left_width : positive;
right_width : positive;
id_width : natural range 0 to axi_id_sz;
max_burst_length_beats : positive
);
port (
clk : in std_ulogic;
--# {{}}
left_m2s : in axi_write_m2s_t;
left_s2m : out axi_write_s2m_t;
--
right_m2s : out axi_write_m2s_t;
right_s2m : in axi_write_s2m_t
);
end component;
When upconverting, the base address and burst length on the ``left`` side might
result in transfers that are not aligned with the natural boundaries on the ``right``
side. In this case some padding must be added at the start or end of the transfers
on the ``right`` side. The padding will have it's write strobe zero'd out, so it will
not actually be written to memory.
The ``W`` (write data) channel will be blocked until a a burst has been negotiated
(the ``AW`` channel). This is done since we must know how much padding to prepend on the
write data channel before sending ``left`` data to the ``right``. When all data from the
``left`` side has been written any padding needed at the end will be appended.
.. _axi_data_width_converter.burst_adapter:
burst_adapter.vhd
-----------------
.. symbolator::
component burst_adapter is
generic (
left_width : positive;
right_width : positive;
id_width : natural range 0 to axi_id_sz;
max_burst_length_beats : positive;
-- Note that for downconversion the ID throttling is always enabled
upconversion_id_throttling : boolean
);
port (
clk : in std_ulogic;
--# {{}}
left_m2s : in axi_m2s_a_t;
left_s2m : out axi_s2m_a_t;
--
right_m2s : out axi_m2s_a_t;
right_s2m : in axi_s2m_a_t;
--# {{}}
burst_info_valid : out std_ulogic;
burst_info_ready : in std_ulogic;
burst_info : out burst_info_t
);
end component;
Adapts AXI bursts when changing bus data width.
The ``left`` port receives input bursts, and the ``right`` port forwards them
after manipulation. The ``burst_info`` interface supplies information for each
burst such as the amount of padding needed.
The AMBA AXI protocol specification allows transactions with different IDs to be
handled in any order by a slave. Say for example that a read with ID 0 is negotiated
and after that a read with ID 1 is negotiated. The data with ID 1 may very well arrive
before the data with ID 0. This poses some problems for a data width converter. Below
we explain the problems and how they are handled.
For transfers with the same ID, an AXI slave must send the responses in the order
the transfers were negotiated.
Upconversion
____________
For upconversion we calculate the amount of padding data needed at the beginning and end
of the outgoing burst. The padding is necessary to handle the cases where the
start or end addresses of the burst received on the ``left`` port are not aligned to the
wider ``right`` port's address steps. When the padding is known, the output burst length
is calculated as well.
For writes, the fact that write responses may return out of order does not pose an issue,
since the write response is simply passed to the ``left``. So outstanding bursts are
allowed to have different IDs, which can improve performance. To enable this behavior,
set ``upconversion_id_throttling`` to ``false``.
For read upconversion, the upconverter must remember how much padding a burst used
since any padding bytes must be removed before passing on to the ``left``.
As this information is stored in a FIFO, the read data must be returned in
the order the bursts were negotiated. This is achieved by only allowing
multiple outstanding bursts if they have the same ID. To enable this behavior,
set ``upconversion_id_throttling`` to ``true``.
Downconversion
______________
For downconversion we split an incoming address transaction if it would otherwise exceed
the maximum burst length on the ``right`` side.
When downconverting the ID throttling is always enabled. For writes we have to know
for each write response if it comes from a burst that was split or not. If not then the
write response shall be forwarded to the ``left`` side. If it comes from a split burst
only the last write response shall be forwarded. For reads the read responses from the
``right`` are packed together to form a wider data word for the ``left`` bus. To handle
these cases efficiently the IDs have to be throttled so the responses are guaranteed to
arrive in order.