Skip to content

Commit e3de978

Browse files
Quartus_Version=26.1;Build=1;
1 parent f2a6293 commit e3de978

185 files changed

Lines changed: 189120 additions & 10666 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Holoscan Sensor Bridge
1+
# 🚀 Altera FPGA-based NVIDIA Holoscan Sensor Bridge
22

33
## Introduction
44

@@ -7,10 +7,7 @@ processing using GPUs. Peripheral device data is acquired by the FPGA and sent v
77
to the host system where ConnectX devices can write that UDP data directly into GPU
88
memory. This software package supports integrating that equipment into Holoscan
99
pipelines and provides several examples showing video processing and inference using an
10-
IMX274 camera with
11-
[Lattice Holoscan Sensor Bridge device](https://www.latticesemi.com/products/developmentboardsandkits/certuspro-nx-sensor-to-ethernet-bridge-board)
12-
or an IMX477 camera with
13-
[Microchip Holoscan Sensor Bridge](https://www.microchip.com/en-us/products/fpgas-and-plds/boards-and-kits/ethernet-sensor-bridge).
10+
IMX678 camera with [Altera Agilex™ FPGA Development Kits](./fpga/altera/README.md).
1411

1512
## Setup
1613

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.6.0-EA
1+
2.6.0-EA-altera

examples/linux_agx5_player.py

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-FileCopyrightText: Copyright (c) Altera. All rights reserved.
3+
# SPDX-License-Identifier: Apache-2.0
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# This example application demonstrates real-time camera streaming and visualization
18+
# using the Agilex 5 (AGX5) Modular Development Kit with an IMX678 MIPI camera.
19+
# It showcases the integration of Altera's Hololink sensor bridge technology with
20+
# NVIDIA's Holoscan SDK for low-latency, high-performance camera applications.
21+
# See README.md for detailed information.
22+
23+
import argparse
24+
import ctypes
25+
import logging
26+
27+
import cuda.bindings.driver as cuda
28+
import holoscan
29+
30+
import hololink as hololink_module
31+
32+
33+
class MicroApplication(holoscan.core.Application):
34+
def __init__(
35+
self,
36+
headless,
37+
fullscreen,
38+
cuda_context,
39+
cuda_device_ordinal,
40+
hololink_channel,
41+
camera,
42+
frame_limit,
43+
):
44+
logging.info("__init__")
45+
super().__init__()
46+
self._headless = headless
47+
self._fullscreen = fullscreen
48+
self._cuda_context = cuda_context
49+
self._cuda_device_ordinal = cuda_device_ordinal
50+
self._hololink_channel = hololink_channel
51+
self._camera = camera
52+
self._frame_limit = frame_limit
53+
54+
def compose(self):
55+
logging.info("compose")
56+
if self._frame_limit:
57+
self._count = holoscan.conditions.CountCondition(
58+
self,
59+
name="count",
60+
count=self._frame_limit,
61+
)
62+
condition = self._count
63+
else:
64+
self._ok = holoscan.conditions.BooleanCondition(
65+
self, name="ok", enable_tick=True
66+
)
67+
condition = self._ok
68+
69+
csi_to_bayer_pool = holoscan.resources.BlockMemoryPool(
70+
self,
71+
name="pool",
72+
# storage_type of 1 is device memory
73+
storage_type=1,
74+
block_size=self._camera._width
75+
* ctypes.sizeof(ctypes.c_uint16)
76+
* self._camera._height,
77+
num_blocks=2,
78+
)
79+
csi_to_bayer_operator = hololink_module.operators.CsiToBayerOp(
80+
self,
81+
name="csi_to_bayer",
82+
allocator=csi_to_bayer_pool,
83+
cuda_device_ordinal=self._cuda_device_ordinal,
84+
)
85+
self._camera.configure_converter(csi_to_bayer_operator)
86+
87+
frame_size = csi_to_bayer_operator.get_csi_length()
88+
89+
frame_context = self._cuda_context
90+
91+
infiniband_devices = hololink_module.infiniband_devices()
92+
# If we don't have any roce connectivity then the use linux receiver
93+
if len(infiniband_devices) == 0:
94+
receiver_operator = hololink_module.operators.LinuxReceiverOperator(
95+
self,
96+
condition,
97+
name="receiver",
98+
frame_size=frame_size,
99+
frame_context=frame_context,
100+
hololink_channel=self._hololink_channel,
101+
device=self._camera,
102+
)
103+
else:
104+
infiniband_devices = hololink_module.infiniband_devices()
105+
self._ibv_name = infiniband_devices[0]
106+
self._ibv_port = 1
107+
receiver_operator = hololink_module.operators.RoceReceiverOp(
108+
self,
109+
condition,
110+
name="receiver",
111+
frame_size=frame_size,
112+
frame_context=frame_context,
113+
ibv_name=self._ibv_name,
114+
ibv_port=self._ibv_port,
115+
hololink_channel=self._hololink_channel,
116+
device=self._camera,
117+
)
118+
119+
pixel_format = self._camera.pixel_format()
120+
bayer_format = self._camera.bayer_format()
121+
image_processor_operator = hololink_module.operators.ImageProcessorOp(
122+
self,
123+
name="image_processor",
124+
optical_black=self._camera.optical_black(),
125+
bayer_format=bayer_format.value,
126+
pixel_format=pixel_format.value,
127+
)
128+
129+
rgba_components_per_pixel = 4
130+
bayer_pool = holoscan.resources.BlockMemoryPool(
131+
self,
132+
name="pool",
133+
# storage_type of 1 is device memory
134+
storage_type=1,
135+
block_size=self._camera._width
136+
* rgba_components_per_pixel
137+
* ctypes.sizeof(ctypes.c_uint16)
138+
* self._camera._height,
139+
num_blocks=2,
140+
)
141+
demosaic = holoscan.operators.BayerDemosaicOp(
142+
self,
143+
name="demosaic",
144+
pool=bayer_pool,
145+
generate_alpha=True,
146+
alpha_value=65535,
147+
bayer_grid_pos=bayer_format.value,
148+
interpolation_mode=0,
149+
)
150+
151+
visualizer = holoscan.operators.HolovizOp(
152+
self,
153+
name="holoviz",
154+
fullscreen=self._fullscreen,
155+
headless=self._headless,
156+
framebuffer_srgb=True,
157+
)
158+
159+
#
160+
self.add_flow(receiver_operator, csi_to_bayer_operator, {("output", "input")})
161+
self.add_flow(
162+
csi_to_bayer_operator, image_processor_operator, {("output", "input")}
163+
)
164+
self.add_flow(image_processor_operator, demosaic, {("output", "receiver")})
165+
self.add_flow(demosaic, visualizer, {("transmitter", "receivers")})
166+
167+
168+
def main():
169+
parser = argparse.ArgumentParser()
170+
parser.add_argument("--headless", action="store_true", help="Run in headless mode")
171+
parser.add_argument(
172+
"--fullscreen", action="store_true", help="Run in fullscreen mode"
173+
)
174+
parser.add_argument(
175+
"--frame-limit",
176+
type=int,
177+
default=None,
178+
help="Exit after receiving this many frames",
179+
)
180+
181+
parser.add_argument(
182+
"--log-level",
183+
type=int,
184+
default=20,
185+
help="Logging level to display",
186+
)
187+
parser.add_argument(
188+
"--cam",
189+
type=int,
190+
default=0,
191+
choices=(0, 1),
192+
help="which camera to stream: 0 to stream camera connected to j14 or 1 to stream camera connected to j17 (default is 0)",
193+
)
194+
parser.add_argument(
195+
"--gain",
196+
type=int,
197+
default=32,
198+
help="Set Analog Gain, RANGE(0 to 240). Default is 32",
199+
)
200+
201+
args = parser.parse_args()
202+
hololink_module.logging_level(args.log_level)
203+
# hololink_module.set_hsb_log_level(hololink_module.HSB_LOG_LEVEL_DEBUG)
204+
logging.info("Initializing.")
205+
# Get a handle to the GPU
206+
(cu_result,) = cuda.cuInit(0)
207+
assert cu_result == cuda.CUresult.CUDA_SUCCESS
208+
cu_device_ordinal = 0
209+
cu_result, cu_device = cuda.cuDeviceGet(cu_device_ordinal)
210+
assert cu_result == cuda.CUresult.CUDA_SUCCESS
211+
cu_result, cu_context = cuda.cuDevicePrimaryCtxRetain(cu_device)
212+
assert cu_result == cuda.CUresult.CUDA_SUCCESS
213+
214+
# Update the default metadata to support 2 cameras on the AGX5
215+
uuid = "7b1fa8c7-31aa-44b6-abcc-eac134461fdc"
216+
metadata = hololink_module.Metadata()
217+
uuid_strategy = hololink_module.BasicEnumerationStrategy(
218+
metadata,
219+
total_sensors=2,
220+
total_dataplanes=1,
221+
sifs_per_sensor=1
222+
)
223+
hololink_module.Enumerator.set_uuid_strategy(uuid, uuid_strategy)
224+
225+
# Get a handle to the Hololink device
226+
channel_metadata = hololink_module.Enumerator.find_channel(
227+
channel_ip="192.168.0.2"
228+
)
229+
230+
# We don't want to enable "vsync_enable" as we do not have the VSYNC control logic on APB bus 6
231+
# Also not using ptp_enable
232+
metadata_overrides = hololink_module.Metadata({"vsync_enable": 0, "ptp_enable": 0})
233+
channel_metadata.update(metadata_overrides)
234+
235+
# Select the camera based on the command line argument
236+
camera_channel = hololink_module.Metadata(channel_metadata)
237+
hololink_module.DataChannel.use_sensor(camera_channel, args.cam)
238+
hololink_channel = hololink_module.DataChannel(camera_channel)
239+
240+
# Get a handle to the camera
241+
camera = hololink_module.sensors.agx5_imx678.agx5_imx678.FramosImx678(
242+
hololink_channel, camera_id=args.cam
243+
)
244+
245+
# Set up the application
246+
application = MicroApplication(
247+
args.headless,
248+
args.fullscreen,
249+
cu_context,
250+
cu_device_ordinal,
251+
hololink_channel,
252+
camera,
253+
args.frame_limit,
254+
)
255+
256+
# Run it.
257+
hololink = hololink_channel.hololink()
258+
hololink.start()
259+
try:
260+
hololink.reset()
261+
# Configures the camera for 3840x2160, 60fps, 10bits per pixel
262+
camera.configure(
263+
hololink_module.sensors.agx5_imx678.agx5_imx678_mode.agx5_imx678_3840_2160_60Hz_10BPP
264+
)
265+
266+
# Set a default analog gain
267+
camera.set_analog_gain_reg(args.gain)
268+
269+
application.run()
270+
finally:
271+
hololink.stop()
272+
273+
(cu_result,) = cuda.cuDevicePrimaryCtxRelease(cu_device)
274+
assert cu_result == cuda.CUresult.CUDA_SUCCESS
275+
276+
if __name__ == "__main__":
277+
main()

0 commit comments

Comments
 (0)