This commit is contained in:
elwrcl committed 2026-06-06 13:25:15 +03:00
1 parent 04d4dc8b09
commit 818cab85dc
12511 files changed
+6156641

No files matched your search

+134
View File
@@ -0,0 +1,134 @@
External Hardware Documentation and Resources
=============================================
Information about hardware behavior comes from a mix of official and
reverse-engineered sources.
Command buffers
^^^^^^^^^^^^^^^
* `NVIDIA open-gpu-doc repository`_ is official documentation from NVIDIA that
has been released to the public. The majority of this documentation comes in
the form of class headers which describe the class state registers.
* `NVIDIA open-gpu-kernel-modules repository`_ is the open-source kernel mode
driver that NVIDIA ships on Turing+ GPUs with GSP. The code here can provide
examples of how to use some hardware features. If open-gpu-doc is missing a
class header, sometimes there will be one here.
* Reverse-engineered command names from `envytools`_ are available in mesa
under eg. ``src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h``. These are no
longer updated. nvk instead uses the open-gpu-doc headers
* `envyhooks`_ is the modern way to dump command sequences from the proprietary
driver
* ``nv_push_dump`` is part of mesa and can disassemble command sequences (build
with ``-D tools=nouveau``, run ``src/nouveau/headers/nv_push_dump`` from the
build dir)
.. _NVIDIA open-gpu-doc repository: https://github.com/NVIDIA/open-gpu-doc
.. _NVIDIA open-gpu-kernel-modules repository: https://github.com/NVIDIA/open-gpu-kernel-modules
.. _envyhooks: https://gitlab.freedesktop.org/nouveau/envyhooks
Shader ISA
^^^^^^^^^^
* `NVIDIA PTX documentation`_ is NVIDIA documentation for CUDA's
intermediate representation. We don't use PTX directly, but this often has
hints about how underlying hardware instructions work. For example, the PTX
`redux` instruction is pretty much identical to the hardware instruction of
the same name.
* `CUDA Binary Utilities`_ is documentation for CUDA's disassembler,
`nvdisasm`. It includes a brief description of most hardware instructions.
There's also an `older version`_ that has older architectures (Kepler through
Volta).
* Kuter Dinel has reverse-engineered instruction encodings for the `Hopper
ISA`_ and `Ada ISA`_ which are autogenerated from his `nv_isa_solver`_
project.
* `nv-shader-tools`_ has some additional tools for disassembling and fuzzing
the hardware ISA
* Mel has dumped a `list of avaiable instructions`_ and their opcodes on recent
architectures by scraping nvdisasm error messages.
* The `Volta whitepaper`_ section "Independent Thread Scheduling" has an
overview of the control flow model used on Volta+ GPUs.
* `Dissecting the NVidia Turing T4 GPU via Microbenchmarking`_ has
reverse-engineered info about the Turing instruction encoding. See especially
section "2.1 Control information" for an overview of compiler-inserted delays
and waits on Maxwell and later.
* `Analyzing Modern NVIDIA GPU cores`_ has additional reverse-engineered info
about the semantics of compiler-inserted delays and waits.
* `Control Flow Management in Modern GPUs`_ has more detail about control flow
reconvergence on Volta+
* `maxas`_ has some reverse-engineered info on the Maxwell ISA
* `asfermi`_ has some reverse-engineered info on the older Fermi ISA
* Red Hat has some NDA'd documentation on instruction latencies from NVIDIA.
Bother karolherbst or airlied on irc if you're missing a latency class for an
instruction on recent architectures.
* Behavior of instructions are tested using the hardware tests in
``src/nouveau/compiler/nak/hw_tests.rs`` and the corresponding ``Foldable``
implementations in ``src/nouveau/compiler/nak/ir.rs`` (build with ``-D
build-tests=true`` and run ``src/nouveau/compiler/nak hw_tests`` from the
build dir)
* NAK's instruction encodings are tested against nvdisasm using
``src/nouveau/compiler/nak/nvdisasm_tests.rs`` (build with ``-D
build-tests=true`` and run ``src/nouveau/compiler/nak nvdisasm_tests`` from
the build dir)
* The old GL driver's compiler, under ``src/gallium/drivers/nouveau/codegen``,
has some information. This is especially useful for graphics-only
instructions, which are often not covered by other sources.
* `Compiler explorer`_ is a convenient tool to see what assembly NVIDIA
generates for a given CUDA program.
.. _NVIDIA PTX documentation: https://docs.nvidia.com/cuda/parallel-thread-execution/index.html
.. _CUDA Binary Utilities: https://docs.nvidia.com/cuda/cuda-binary-utilities/index.html#instruction-set-reference
.. _older version: https://docs.nvidia.com/cuda/archive/11.8.0/cuda-binary-utilities/index.html#instruction-set-ref
.. _Hopper ISA: https://kuterdinel.com/nv_isa/
.. _Ada ISA: https://kuterdinel.com/nv_isa_sm89/
.. _nv_isa_solver: https://github.com/kuterd/nv_isa_solver
.. _nv-shader-tools: https://gitlab.freedesktop.org/nouveau/nv-shader-tools
.. _list of avaiable instructions: https://gitlab.freedesktop.org/mhenning/re/-/tree/main/opclass?ref_type=heads
.. _Volta whitepaper: https://images.nvidia.com/content/volta-architecture/pdf/volta-architecture-whitepaper.pdf
.. _Dissecting the NVidia Turing T4 GPU via Microbenchmarking: https://arxiv.org/pdf/1903.07486
.. _Analyzing Modern NVIDIA GPU cores: https://arxiv.org/pdf/2503.20481
.. _Control Flow Management in Modern GPUs: https://arxiv.org/pdf/2407.02944
.. _maxas: https://github.com/NervanaSystems/maxas/wiki
.. _asfermi: https://github.com/hyqneuron/asfermi/wiki
.. _Compiler explorer: https://godbolt.org/z/1jrfhq5G7
Misc
^^^^
* `envytools`_ has reverse-engineered documentation for maxwell and earlier
hardware.
* The nvidia architecture whitepapers give a basic overview of what has changed
between hardware revisions. See eg. the `Blackwell whitepaper`_
* The nvidia architecture tuning guides often mention how details of a hardware
generation has changed, often with information about the memory subsystem or
occupancy. See eg. the `Blackwell tuning guide`_
* `The Nouveau wiki's CodeNames page`_ is useful for mapping NVIDIA marketing
names to engineering names
* `Matching CUDA arch and CUDA gencode for various NVIDIA architectures`_ has a
useful table comparing SM versions to engineering names
.. _envytools: https://envytools.readthedocs.io/en/latest/hw/index.html
.. _Blackwell whitepaper: https://images.nvidia.com/aem-dam/Solutions/geforce/blackwell/nvidia-rtx-blackwell-gpu-architecture.pdf
.. _Blackwell tuning guide: https://docs.nvidia.com/cuda/blackwell-tuning-guide/index.html
.. _The Nouveau wiki's CodeNames page: https://nouveau.freedesktop.org/CodeNames.html
.. _Matching CUDA arch and CUDA gencode for various NVIDIA architectures: https://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/
+126
View File
@@ -0,0 +1,126 @@
Hardware docs
=============
Command buffers
---------------
Command format
^^^^^^^^^^^^^^
Each command sent to the GPU contains a method and some data. Method names are
documented in corresponding header files copied from NVIDIA, eg. the fermi
graphics methods are in src/nouveau/headers/nvidia/classes/cl9097.h
P_IMMD
""""""
A lot of the time, you will want to issue a single method with its data, which
can be done with P_IMMD::
P_IMMD(p, NV9097, WAIT_FOR_IDLE, 0);
P_IMMD will emit either a single `immediate-data method`_, which takes a single
word, or a pair of words that's equivalent to P_MTHD + the provided data. Code
must count P_IMMD as possibly 2 words as a result.
.. _immediate-data method: https://github.com/NVIDIA/open-gpu-doc/blob/87ba53e0c385285a3aa304b864dccb975a9a0dd4/manuals/turing/tu104/dev_ram.ref.txt#L1214
P_MTHD
""""""
`P_MTHD`_ is a convenient way to execute multiple consecutive methods without
repeating the method header. For example, the code::
P_MTHD(p, NV9097, SET_REPORT_SEMAPHORE_A);
P_NV9097_SET_REPORT_SEMAPHORE_A(p, addr >> 32);
P_NV9097_SET_REPORT_SEMAPHORE_B(p, addr);
P_NV9097_SET_REPORT_SEMAPHORE_C(p, value);
generates four words - one word for the method header (defaulting to 1INC) and
then the next three words for data. 1INC will automatically increment the method
id by one word for each data value, which is why the example can advance from
SET_REPORT_SEMAPHORE_A to B to C.
.. _P_MTHD: https://github.com/NVIDIA/open-gpu-doc/blob/87ba53e0c385285a3aa304b864dccb975a9a0dd4/manuals/turing/tu104/dev_ram.ref.txt#L1042
P_0INC
""""""
`0INC`_ will issue the same method repeatedly for each following data word.
.. _0INC: https://github.com/NVIDIA/open-gpu-doc/blob/87ba53e0c385285a3aa304b864dccb975a9a0dd4/manuals/turing/tu104/dev_ram.ref.txt#L1096
P_1INC
""""""
`1INC`_ will increment after one word and then issue the following method
repeatedly. For example, the code::
P_1INC(p, NV9097, CALL_MME_MACRO(NVK_MME_SET_PRIV_REG));
P_INLINE_DATA(p, 0);
P_INLINE_DATA(p, BITFIELD_BIT(3));
issues one NV9097_CALL_MME_MACRO command, then increments the method and issues
two NV9097_CALL_MME_DATA commands.
.. _1INC: https://github.com/NVIDIA/open-gpu-doc/blob/87ba53e0c385285a3aa304b864dccb975a9a0dd4/manuals/turing/tu104/dev_ram.ref.txt#L1149
Execution barriers
^^^^^^^^^^^^^^^^^^
Commands within a command buffer can be synchronized in a few different ways.
* Explicit WFI - Idles all engines before executing the next command eg. via
`NVA16F_WFI` or `NV9097_WAIT_FOR_IDLE`
* Semaphores - Delay execution based on values in a memory location. See
`open-gpu-doc on semaphores`_
* A subchannel switch - Causes the hardware to execute an implied WFI
.. _open-gpu-doc on semaphores: https://github.com/NVIDIA/open-gpu-doc/blob/master/manuals/turing/tu104/dev_pbdma.ref.txt#L3231
Subchannel switches
"""""""""""""""""""
A subchannel switch occurs when the hardware receives a command for a different
subchannel than the one that it's currently executing. For example, if the
hardware is currently executing commands on the 3D engine (`SUBC_NV9097 == 0`), a
command executed on the compute engine (`SUBC_NV90C0 == 1`) will cause a
subchannel switch. Host methods (class \*6F) are an exception to this - they can
be issued to any subchannel and will not trigger a subchannel switch
[#fhostsubcswitch]_ [#foverlapnvk]_.
Subchannel switches act the same way that an explicit WFI does - they fully idle
the channel before issuing commands to the next engine [#fnsight]_
[#foverlapnvk]_.
This works the same on Blackwell. Some NVIDIA documentation contradicts this:
"On NVIDIA Blackwell Architecture GPUs and newer, subchannel switches do not
occur between 3D and compute workloads"[#fnsight]_. This documentation appears
to be wrong or inapplicable for some reason - tests do not reproduce this
behavior [#foverlapnvk]_ [#foverlapprop]_, and the blob does not change its
event implementation for blackwell [#feventprop]_.
.. [#fnsight] https://docs.nvidia.com/nsight-graphics/UserGuide/gpu-trace-ui.html#subchannel-switch-overlay
.. [#foverlapnvk] Based on reverse engineering by running
https://gitlab.freedesktop.org/mhenning/re/-/tree/main/vk_test_overlap_exec
under nvk, possibly with alterations to the way nvk generates commands for
pipeline barriers.
.. [#foverlapprop] By running
https://gitlab.freedesktop.org/mhenning/re/-/tree/main/vk_test_overlap_exec
under the proprietary driver and examining the main output (eg. execution
overlaps without pipeline barriers)
.. [#feventprop] For example, the event commands generated here only wait on a
single engine
https://gitlab.freedesktop.org/mhenning/re/-/blob/6ce8c860da65fbf2ab26d124d25f907dea2cf33a/vk_event/blackwell.out#L20612-20793
.. [#fhostsubcswitch] https://github.com/NVIDIA/open-gpu-doc/blob/87ba53e0c385285a3aa304b864dccb975a9a0dd4/manuals/turing/tu104/dev_ram.ref.txt#L1009
Copy engine
^^^^^^^^^^^
The copy engine's `PIPELINED` mode allows a new transfer to start before the
previous transfer finishes, while `NON_PIPELINED` acts as an execution barrier
between the current copy and the previous one [#fpipelined]_.
.. [#fpipelined] https://github.com/NVIDIA/open-gpu-kernel-modules/blob/2b436058a616676ec888ef3814d1db6b2220f2eb/src/common/sdk/nvidia/inc/ctrl/ctrl0050.h#L75-L83
+66
View File
@@ -0,0 +1,66 @@
ISBE Layout
=============================================
The ISBE is a piece of staging memory shared between the :abbr:`PE (Primitive Engine)`
and :abbr:`SM (Streaming Multiprocessor)` containing the attributes for
:abbr:`VTG (Vertex, Tesselation, Geometry)` stages.
In normal cases, no shader needs to access it and instead use ``ALD`` or ``AST``
but in some some cases (like ``ALD`` vtx handling or mesh shaders) direct access to
it is necessary.
This document the observations made about the layout of it.
The ISBE "space" is separated in multiple regions (map, patch, primitive and attribute)
and can be accessed with ``ISBERD`` and ``ISBEWR`` instructions.
There is always two ISBE "space" present: one for inputs and one for outputs.
Input and output ISBE memory can be shared by setting bit 25 of the
:abbr:`SPH (Shader Program Header)`.
**NOTE**: Before Turing, only ``ISBERD`` was supported with the ``.MAP`` flag.
Map region
""""""""""
The Map region contains all the vertex indices for all primitives.
When used as output, it starts with the primitives count.
==================================================== ==================== ===================================================
Byte Range Name Note
==================================================== ==================== ===================================================
0x0 primitive_count 32-bit, Only present as output ISBE
0x0?..(0x0? + primitive_count * $vtx_per_prim_count) primitive_indices[i] 8-bit, Offset is 0x04 as output ISBE otherwise 0x00
==================================================== ==================== ===================================================
Attribute region
""""""""""""""""
The Attribute region contains all attributes and is allocated based on the
:abbr:`SPH (Shader Program Header)` input/output mask and :doc:`mesh shader methods <mesh_shading_notes>`.
With the ``SKEW`` flag applied on ``ISBERD`` and ``ISBEWR``, each type of attribute is packed with
32 values of the same type forming "memory lines" of 128 bytes each.
Additionally, the order of the attribute packs is determined by the unique attribute id.
(see `nak_attr` or :abbr:`SPH (Shader Program Header)` headers definitions for the values)
Finally, if more than 32 values are needed, the layout repeat itself.
Here is an example for 256 vertices being defined with ``ATTR_POINT_SIZE`` (0x6C) and
``ATTR_POSITION_X`` (0x70) active:
==================================================== ========================
Byte Range Name
==================================================== ========================
0x000..0x080 ATTR_POINT_SIZE[0..31]
0x000..0x100 ATTR_POSITION_X[0..31]
0x180..0x200 ATTR_POINT_SIZE[32..63]
0x200..0x280 ATTR_POSITION_X[32..63]
0x280..0x300 ATTR_POINT_SIZE[63..95]
0x300..0x380 ATTR_POSITION_X[63..95]
... ...
0x700..0x780 ATTR_POINT_SIZE[224..255]
0x780..0x800 ATTR_POSITION_X[224..255]
==================================================== ========================
+159
View File
@@ -0,0 +1,159 @@
Mesh Shading Notes
=============================================
Mesh shaders support is present on Turing and later.
It reuses the 3D engine and regular graphics stages.
Draw
""""
When performing a draw with a mesh shader bound, ``SET_DRAW_CONTROL_A`` needs to
be used while ignoring the base indices.
This can be done with the following commands:
.. code-block::
NVC597_SET_VERTEX_ID_BASE
.V = (0x0)
NVC597_SET_DRAW_CONTROL_A
.TOPOLOGY = POINTS
.PRIMITIVE_ID = FIRST
.INSTANCE_ID = FIRST
.SPLIT_MODE = NORMAL_BEGIN_NORMAL_END
.INSTANCE_ITERATE_ENABLE = FALSE
.IGNORE_GLOBAL_BASE_VERTEX_INDEX = TRUE
.IGNORE_GLOBAL_BASE_INSTANCE_INDEX = TRUE
NVC597_DRAW_VERTEX_ARRAY_BEGIN_END_A
.START = (0x0)
NVC597_DRAW_VERTEX_ARRAY_BEGIN_END_B
.COUNT = ($groupCount)
Where:
- ``$groupCount`` is the number of local workgroups to dispatch.
**NOTE**: The topology of ``SET_DRAW_CONTROL_A`` will be ignored and
``SET_MESH_SHADER_A`` topology will be used.
Stages
""""""
Before binding a task or mesh shader, ``SET_MESH_CONTROL`` needs to be set.
Shared Memory
^^^^^^^^^^^^^
Shared memory is allocated in the output :doc:`ISBE Attribute region <isbe_layout>` after all attributes.
Part of the shared memory can be made accessible to the next stage
as part of the input :doc:`ISBE Attribute region <isbe_layout>` using ``.OUTPUT_TO_M_S_LINES``.
As such, the task payload is the first part of the shared memory on the task stage.
Task
^^^^
The task shader is always bound as a vertex shader.
Additionally, ``SET_MESH_INIT_SHADER`` is used to set the number of local invocations
to use and the size used for shared memory.
This can be done with the following command:
::
NVC597_SET_MESH_INIT_SHADER
.THREAD_COUNT = ($thread_count)
.LOCAL_BUFFER_LINES = ($local_buffer_lines)
.OUTPUT_TO_M_S_LINES = ($output_to_m_s_lines)
Where:
- ``$thread_count`` is the number of local invocations (up to 32).
- ``$local_buffer_lines`` is the total amount of shared memory lines (including task payload)
to allocate in the output :doc:`ISBE Attribute region <isbe_layout>` after all attributes.
- ``$output_to_m_s_lines`` is the total amount of shared memory lines that will be available
to the next stage.
**NOTE**: The size of a memory line is 128 bytes.
The workgroup_index is implemented using the ``VERTEX_ID`` read from the input :doc:`ISBE Attribute region <isbe_layout>` (with SKEW applied).
``EmitMeshTasksEXT`` is lowered to the equivalent pseudo code:
::
void EmitMeshTasksEXT(uint x, uint y, uint z) {
uint taskCount = x * y * z;
ISBEWR.O.ATTR.32 [0x04], taskCount;
ISBEWR.O.ATTR.32 [0x08], x;
ISBEWR.O.ATTR.32 [0x0C], y;
ISBEWR.O.ATTR.32 [0x10], z;
}
Mesh
^^^^
The mesh shader is bound to the vertex stage if no task shader is present
or the tess control stage otherwise.
Attributes are stored in the output :doc:`ISBE Attribute region <isbe_layout>` **with SKEW applied**.
If any per primitive attributes are in use, they are stored after all per vertex
attributes and the geometry stage will be enabled in passthrough mode with only
its program header present.
For more details about the ISBE Attribute or Map layout, see the dedicated :doc:`ISBE <isbe_layout>` page.
Additionally, ``SET_MESH_SHADER_A`` and ``SET_MESH_SHADER_B`` are used
to set the number of local invocations to use, the size used for shared memory
and topology details.
This can be done with the following command:
::
NVC597_SET_MESH_SHADER_A
.OUTPUT_TOPOLOGY = $topology
.MAX_VERTEX = ($max_vertex)
.MAX_PRIMITIVE = ($max_primitive)
NVC597_SET_MESH_SHADER_B
.SHARED_MEM_LINES = ($shared_mem_lines)
.THREAD_COUNT = ($thread_count)
Where:
- ``$topology`` is the topology to use.
- ``$max_vertex`` is the max count of vertices used by the mesh shader.
- ``$max_primitive`` is the max count primitives used by the mesh shader.
- ``$shared_mem_lines`` is the total amount of shared memory line to allocate
in the output :doc:`ISBE Attribute region <isbe_layout>` after all attributes.
- ``$thread_count`` is the number of local invocations (up to 32).
**NOTE**: The size of a memory line is 128 bytes.
The workgroup_index is implemented in the following way:
- If a task shader is present, the value is read from the input :doc:`ISBE Attribute region <isbe_layout>` **without SKEW applied**.
- Otherwise, it is implemented using the ``VERTEX_ID`` read from the input :doc:`ISBE Attribute region <isbe_layout>` **with SKEW applied**.
``SetMeshOutputsEXT`` is lowered to the equivalent pseudo code:
::
void SetMeshOutputsEXT(uint vertexCount, uint primitiveCount) {
// vertexCount is unused
ISBEWR.O.MAP.32 [0x3], primitiveCount;
}
**NOTE**: This is effectively a write to offset 0x0 but the output :doc:`ISBE Map region <isbe_layout>` process writes in reverse.
All primitive indices are stored as 8-bit indices starting at offset 0x4 in the ouptut :doc:`ISBE Map region <isbe_layout>`.
Hardware limitations
""""""""""""""""""""
* Only up to 32 local invocations are supported.
* The shared memory being part of the :doc:`ISBE Attribute region <isbe_layout>` makes it that
we do not have any atomics for it.
* Task / mesh invocations need to be counted inside the shader.