Skip to content

Implementing RoCEv2 - #198

Open
Skinbow wants to merge 11 commits into
enjoy-digital:masterfrom
Skinbow:rdma_pr
Open

Implementing RoCEv2#198
Skinbow wants to merge 11 commits into
enjoy-digital:masterfrom
Skinbow:rdma_pr

Conversation

@Skinbow

@Skinbow Skinbow commented Mar 31, 2026

Copy link
Copy Markdown

This PR adds RoCEv2 (RDMA over Converged Ethernet v2 with RDMA being Remote Direct Memory Access) functionality to LiteEth.

In particular, this pull request is built to be compliant with the InfiniBand Architecture Specification Volume 1 (Release 1.2.1).
It implements the Transport Layer (Chapter 9) of the InfiniBand protocol as well as the CM (Communication Management) protocol (Chapter 12) and part of the IP CM protocol (Annex A11) for standard connection establishment.
This is done on top of the UDP/IP (similarly to the LiteEthUDPIPCore) to comply with the Annex 17 - RoCEv2 of the InfiniBand specification.

The changes are gathered in the LiteEthRoCEv2Core which includes the RDMA on top of the IP/UDP stack (which remains usable for normal UDP connections through ports other than 4791, which is the IANA assigned port for the RoCEv2 protocol).

Both the responder and requester side of the protocol is implemented, meaning that the FPGA can function as the passive or active TCA (Target Channel Adapter) which can have one QP (Queue Pair) in RC (Reliable Connection) mode, as well as a Special QP (QP1) used for CM in UD (Unreliable Datagram) mode.

Only the responder side of the CM protocol is implemented here, so the connection establishment must be done by the other device.

Thus, an HCA (Host Channel Adapter) can establish a connection and send RDMA Read and Write operations, as well as Send operations to this module, and receive acknowledgements as specified in the protocol. Additionally, the module can send RDMA Read and Write requests as well as Send requests, to which the HCA has to respond. This allows existing hardware such as Mellanox ConnectX cards to communicate with the FPGA through the use of Infiniband's Verbs API as well as other calls to the rdma-core library. Regular networking cards can also communicate through RoCEv2 with Software ROCE, also implemented in the rdma-core library.

Note that for the FPGA to be the requester in this connection, for it to send RDMA requests, the HCA has to transfer its memory regions' keys to the FPGA. The way of doing this is not specified in the protocol. The LiteEthKeyExchanger module shows an example of how it can be done.

Tests

This module has been tested using a 1000BASE-T and 1000BASE-LX connection to a Mellanox ConnectX-4 Lx, as well as a regular networking card using Software ROCE.

It can be tested using the test.c script I am sharing through this gist.

Here are some tests results that I obtained with the Mellanox card:

Test of a RDMA_WRITE followed by an RDMA READ on the same memory region (twice):

$ ./test -C 2 -c -a 192.170.1.50 -S 32 -V
Sent:
\x05\x0c\xd4\x74\xe5\xd3\x27\x44\x45\x64\x29\x56\x09\x49\x69\x46\x39\x80\xcc\x48\xd5\x1b\xf0\xce\xf3\xe4\x7a\x95\x02\x79\x37\xf8
Received:
\x05\x0c\xd4\x74\xe5\xd3\x27\x44\x45\x64\x29\x56\x09\x49\x69\x46\x39\x80\xcc\x48\xd5\x1b\xf0\xce\xf3\xe4\x7a\x95\x02\x79\x37\xf8

Data matches
Sent:
\xdc\xc2\x6b\x56\x2b\x8f\xe9\x2b\x60\x63\x9b\xf0\xd0\x58\x2f\x28\x1d\x3b\xd0\xe1\xf3\xc6\xe1\x09\x68\xc9\x2b\xc3\xc8\xc9\x8b\x44
Received:
\xdc\xc2\x6b\x56\x2b\x8f\xe9\x2b\x60\x63\x9b\xf0\xd0\x58\x2f\x28\x1d\x3b\xd0\xe1\xf3\xc6\xe1\x09\x68\xc9\x2b\xc3\xc8\xc9\x8b\x44

Data matches

with the transmission showing in Wireshark:
Screenshot from 2026-04-02 15-03-45

Benchmarks with RDMA_WRITEs:

$ ./test -b -w -C 1000 -c -a 192.170.1.50 -S 1024
Sent 8 1024-byte RDMA_WRITEs 1000 times in 71.88ms
This amounts to a transfer rate of MB/s: 113.962990
$ ./test -b -w -C 1000 -c -a 192.170.1.50 -S 2048
Sent 8 2048-byte RDMA_WRITEs 1000 times in 142.63ms
This amounts to a transfer rate of MB/s: 114.872999
$ ./test -b -w -C 1000 -c -a 192.170.1.50 -S 32768
Sent 8 32768-byte RDMA_WRITEs 1000 times in 2.27s
This amounts to a transfer rate of MB/s: 115.677083

Benchmarks with RDMA_READs

$ ./test -b -r -C 1000 -c -a 192.170.1.50 -S 1024
Sent 8 1024-byte RDMA_READs 1000 times in 71.22ms
This amounts to a transfer rate of MB/s: 115.028594
$ ./test -b -r -C 1000 -c -a 192.170.1.50 -S 2048
Sent 8 2048-byte RDMA_READs 1000 times in 142.70ms
This amounts to a transfer rate of MB/s: 114.817430
$ ./test -b -r -C 1000 -c -a 192.170.1.50 -S 32768
Sent 8 32768-byte RDMA_READs 1000 times in 2.27s
This amounts to a transfer rate of MB/s: 115.701172

Benchmarks with RDMA_READs and RDMA_WRITEs in quick succession:

$ ./test -b -rw -C 1000 -c -a 192.170.1.50 -S 2048 -B
Sent 8 2048-byte RDMA_WRITEs and RDMA_READs 1000 times in 76.81ms
This amounts to a bidirectional transfer rate of MB/s: 213.298131

Related changes

This pull request relies on a couple of changes throughout the LiteX, such as an additional last signal in the LiteDRAMDMAWriter of LiteDRAM's frontend/dma.py (see "Added last signal to LiteDRAMWriter#377").
It also relies on a small change to the Header class in LiteX's soc/interconnect/packet.py (see "Allow for changing starting position of header#2441").

@Skinbow

Skinbow commented Apr 22, 2026

Copy link
Copy Markdown
Author

Added new changes (More versatile memory regions) and rebased on master + some minor bug fixes.
Plus made some additional effort to close timings.

@Skinbow
Skinbow marked this pull request as ready for review April 29, 2026 15:22
@Skinbow
Skinbow marked this pull request as draft May 26, 2026 03:12
@Skinbow
Skinbow marked this pull request as ready for review June 23, 2026 05:48
@Skinbow Skinbow changed the title Implementing RoCEv2 passive receiver Implementing RoCEv2 Jun 26, 2026
@Skinbow
Skinbow force-pushed the rdma_pr branch 3 times, most recently from 4db3994 to 8364246 Compare August 12, 2026 13:04
Mikhail Iakimenko and others added 11 commits August 29, 2026 13:44
Introduces various headers from the RoCEv2 protocol for use in the IBT as well as the MAD layer for CM
Adds IBT and MAD-specific sink/source layout descriptions.
Adds layouts and constants for send, receive and completion queues.
Adds enums representing IBT and MAD opcodes for better readability.
Sets RoCEv2-related constants.
Adds several helper functions.
RoCEv2 requires the don't fragment flag to be set to 1 in the IP header
Introduces numerous submodules useful for the IBT and the MAD CM
Adds cq.py:
    RDMA uses CQ to transmit notifications about request completions to
    upper layers.
Adds icrc.py:
    Treats the Invariable Cyclic Redundancy Check required by the
    IBT. The submodule will need to be placed between UDP and IBT
    output and will listen to IP's output to calculate the CRC.
Adds pad.py:
    The IBT requires all packets to be aligned to a size of 4 bytes,
    and since all IBT headers have a size that is a multiple of 4,
    the alignment needs to be insured using the payload's size.
Adds qp.py:
    RDMA uses QPs to establish communication. The current
    implementation will only allow one generic (RC) and one special
     (QP1 UD) QPs to exist.
Adds mr.py:
    Memory regions to be handled by QPs.
These submodules are essential for the RDMA implementation as they add
a variable timer, specialized fifos (WaitPipe) and a modified
packetizer and depacketizer.

The variable timer is needed because the CM protocol sets delay times
during connection establishment.

The packetizer and depacketizer needed changing to accommodate for
variable opcode-dependent headers in the IBT and MAD layers.

The need for a specialized fifo comes from the need to be able to pipe
entire packets and instantly dump them in case of an error.
The MAD CM layer is used for communication establishment and QP configuration.

This allows to simplify the client-side API calls through the usage of
the libmacm library (a standard in rdma_core).

Also adds IPCM, a protocol a layer above CM made specifically for CM
through the IP protocol. The CM gives the IPCM the ability to refuse a
connection request. The latter also pipes requests private data to upper
layers.
The bulk of the logic, the requester and responder are each split into
two blocks between reception and transmission.

The requester handles requests made by the FPGA's send queue as well
as resend logic and checking for response validity.
The responder handles requests made by the other machine, checks their
validity and responds to them.
Adds the IBT layer of RoCEv2 and connects it to the requester, the
responder and MAD CM (through the responder).

The validation handles the validity of incoming packets regardless
of whether they are a request or a response.

The reception dispatches the incoming request to the requester or
responder.

The transmission alternates between requester and responder and
arbitrates their use of the wire.
Adds the entire stack connected together, similar to the UDPIPCore, but
with the IBT and MAD CM layers on top.
These are frontend modules that allow to pass a stream to rdma.

The key exchanger handles a SEND request that the other machine must
send after connection establishment to be able to make requests to its
memory regions (it sends multple mrs' r_keys and vas).

The streamer then uses the passed keys to alternate its writes and reads
between local and remote mrs.
Adds tests for various submodules, as well as the entire RoCEv2 core.
Adding dont_fragment field to the IP packet requires a change in the packet model,
which needs to be changed in several tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants