.. _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.