Holoscan sensor bridge software architecture
Holoscan sensor bridge devices provide a high-speed interface between sensor equipment and GPU accelerated Holoscan applications. Control of peripherals connected to a sensor bridge device are provided through I2C, SPI, or local bus interfaces on the sensor bridge board. Interactions with these peripherals take place using network messages that are referred to as the control plane. Data acquired from high-speed sensors is gathered by the FPGA and forwarded via UDP back to the host, using messages referred to as the data plane.
Holoscan sensor bridge host software provides objects that manage the control plane messages used to control I2C, SPI, and local bus transactions. Other sensor bridge host software objects provide network receiver operators which configure and direct data received by data plane traffic. For systems with ConnectX SmartNIC devices, received data plane traffic can be transparently written to GPU memory using RDMA. For systems without ConnectX devices, there is a network receiver object which uses the Linux Sockets API to provide the same functionality but without the high performance that offloading packet reception offers.
Holoscan sensor bridge software also includes additional operators for image format conversion and image signal processing.
As an introduction to the architecture in the sensor bridge host software, we’ll step through the major aspects of examples/imx274_player.py. In this example, an IMX274 stereo camera unit provides a live video feed for 4k or 1080p video running at 60FPS.
Applications interact with APIs on sensor objects
Applications typically instantiate and use sensor objects which provide device specific APIs. For example, a camera object can provide an API for setting its exposure:
camera.set_exposure(1000)
To instantiate a camera object, application code will typically
Use
HololinkEnumerator.find_channel
to enumerate the sensor bridge devices visible to the local system.find_channel
accepts arguments that filter received messages; when an enumeration message that matches the given critera is found, a dict is returned with metadata about the enumerated device.channel_metadata = hololink_module.HololinkEnumerator.find_channel(channel_ip=args.hololink)
When enumeration data is observed from the given IP address, information about the found device is returned into the
channel_metadata
variable.Construct a
HololinkDataChannel
object usingchannel_metadata
. This object connects received data with a GPU memory buffer.hololink_channel = hololink_module.HololinkDataChannel(channel_metadata)
Construct our camera sensor object using
hololink_channel
:camera = hololink_module.sensors.imx274.dual_imx274.Imx274Cam(hololink_channel, ...)
Constructing the camera instance does not actually interact with the sensor bridge device– we just store device communication information for later use. The camera instance is necessary for our HoloscanApplication’s constructor to run; so we’re motivated to create this object relatively early.
A single sensor bridge device usually has multiple
HololinkDataChannel
instances; many APIs affect all data channel instances associated with a specific sensor bridge device. In order to reset the sensor bridge device–the only way to guarantee that the device is in a known state–we’ll get a handle to the underlyingHololink
instance. AllHololinkDataChannel
instances on this board will return the sameHololink
instance here. The call tohololink.reset
will reset all attached data channel instances.hololink = hololink_channel.hololink() hololink.reset()
In our sample application, we need initialize the camera clock and configure the image format the camera will transmit. Note that in the IMX274 stereo camera, the same clock is used to drive both camera devices, so care must be taken to ensure you don’t initialize the camera while the other is in use.
camera.setup_clock() camera_mode = imx274_mode.Imx274_Mode.IMX274_MODE_3840X2160_60FPS camera.configure(camera_mode)
In our IMX274 demo,
camera.setup_clock
andcamera.configure
call the sensor bridge device’s I2C controller objects to write the proper set of device registers.Now we’re ready to start our application pipeline.
application.run()
Unless the pipeline is explicitly stopped, this call to
application.run
will never return.application.run
starts with a call to each operator’sstart
method.The
start
method of bothRoceReceiverOperator
andLinuxReceiverOperator
will callcamera.start
(where camera is the device object passed into the constructor’sdevice
parameter). The camera, on a call tostart
, will be configured to start sending video data.application.run
then goes into a loop, executing the pipeline, calling each operator’scompute
method. The network receiver operatorcompute
method blocks until a whole data frame is received into the memory block it was initialized with.
Our camera object works with device registers by reads and writes on an I2C bus present
in the sensor bridge device. Suppose our camera is connected to a sensor bridge I2C
controller at local bus address 0x04000200. (We’ll store that address in the constant
hololink_module.CAM_I2C_CTRL
.) The camera object can fetch a handle to an
Hololink.I2c
object, with APIs for generating I2C transactions, by calling
hololink.get_i2c
. If the camera itself responds to an I2C bus address of 0x34 (which
we’ll call CAM_I2C_ADDRESS
), then it can support a camera.set_register
method this
way:
class Imx274Cam:
def __init__(
self,
hololink_channel,
i2c_controller_address=hololink_module.CAM_I2C_CTRL,
...
):
self._hololink = hololink_channel.hololink()
self._i2c = self._hololink.get_i2c(i2c_controller_address)
...
def set_register(self, register, value):
...
self._i2c.i2c_transaction(
CAM_I2C_ADDRESS,
write_data,
read_byte_count,
)
def set_exposure(self, value):
self.set_register(..., value)
Sensor objects of all types can be supported this way: interfaces for I2C, SPI, or
sensor bridge local bus can all be controlled by APIs present on the Hololink
instance.
HololinkDataChannel enumeration and IP address configuration
Once per second, each data plane instance in a sensor bridge device sends out two UDP
packets; the host uses these to enumerate visible devices. One packet is called the
enumeration packet, the other is the bootp request packet. The
HololinkEnumerator.find_channel
method gathers and decodes both of these messages and
uses that to generate the dictionary passed back as channel_metadata
. Holoscan sensor
bridge sends these packets using the local broadcast MAC ID (FF:FF:FF:FF:FF:FF). Routers
are not allowed to forward these messages to other networks, so only locally connected
hosts will receive these. Your host must be connected to the same network as the sensor
bridge device in order to communicate.
While the enumeration message is intended to announce the presence of a sensor bridge
data channel; the bootp request presents a request that the host can reply to with an
IP address reconfiguration command. If the host wishes to reconfigure the IP address of
the device, it sends a bootp reply message with a new IP address to be assigned to
that data plane controller. The sensor bridge demo container includes a command line
tool called hololink
that can be used to assign new IP addresses to sensor bridge
devices:
$ hololink set-ip b0:4f:13:e0:20:4c 192.168.100.250
Your MAC-ID and IP addresses will be different; a list with any number of mac-id and ip-address pairs can be given. By default, this starts a process that runs forever; on receipt of a bootp request with any IP address other than the configured value, a bootp reply response is sent that assigns the configured address to that data channel. Running this as a daemon is important when resetting the sensor bridge device:
Application code establishes a connection at the new IP address
Application executes
hololink.reset
The device resets and reverts back to the default IP address
Application code sees enumeration with the default IP address–which it ignores
When
hololink set-ip
sees the bootp request with something besides the new IP address, it’ll send a bootp reply with the new IP address configurationHoloscan sensor bridge updates its IP address. Enumeration data will now be sent using that new address
Application code then sees enumeration at the new IP address
Application reconnects and completes the reset request
bootp request and bootp reply packet contents follow the specification given in RFC951 with the exception that bootp request is sent by the sensor bridge device on UDP port 12267 and bootp reply is sent by the host to UDP port 12268.
Specific information about host network configuration can be found here..
Holoscan sensor bridge data channel uses RoCE v2 RDMA write and RDMA write immediate requests
ConnectX SmartNIC firmware has support for handling authenticated
RoCE v2 requests without
CPU intervention. Sensor bridge devices leverage this by generating
RDMA write and RDMA write with immediate requests
to send data plane content to the host. HololinkDataChannel configures the sensor bridge
device with target network addressing, authentication keys, and individual-packet and
overall data-frame sizes. Once configured, the sensor bridge device will send received
sensor data in RDMA write requests with a payload size given by the individual-packet
size value. These requests, on receipt by ConnectX, are written directly into GPU or
system memory–these writes are completely offloaded from the CPU. When the total number
of received bytes reaches the data-frame size, that packet is sent using an RDMA
write-immediate request. Sending the last packet once the data-frame size is reached,
and not waiting to reach the individual-packet size, accounts for aggergate data
payloads that are not an even multiple of the individual packet size. The RDMA
write-immediate request has the same functionality as an RDMA write request with an
extra flag that is passed to the CPU with an interrupt. This interrupt is used to
indicate the end-of-frame and is what RoceReceiverOperator.compute
waits for on a call
to get_next_frame.