Sequencer Configuration#

The heart of the HSB Native Latency Tool lies in its precise sequencer configuration, which synchronizes LED triggers with camera frame events at the hardware level. Different camera sensor types require different timing strategies due to their distinct capture mechanisms.

The HSB Native Latency Tool provides complete example implementations for each sensor type, demonstrating the specific timing adjustments and GPIO control sequences required for accurate latency measurement.

The HSB Native Latency Tool includes two example applications in the examples/ directory:

  • e2e_imx274_latency.py - Rolling shutter camera implementation (IMX274)

  • e2e_vb1940_latency.py - Global shutter camera implementation (VB1940)

Each implementation showcases the hardware sequencer programming, event handling, and timing synchronization specific to that sensor type, providing a complete reference for implementing latency measurement with your own camera sensors.

Rolling Shutter Sequencer Setup#

The HSB Native Latency Tool uses hardware sequencers to precisely control GPIO events synchronized to camera frame timing. The following code demonstrates how to configure the sequencer for rolling shutter cameras.

Event Sequencer Implementation#

The code snippets below are from the rolling shutter application e2e_imx274_latency.py.

def setup_sequencer(self):
    # GPIO register addresses
    GPIO_DIRECTION_REG_BASE = 0x0000002C
    # GPIO module control registers
    GPIO_DELAYED_IO_OUTPUT_REG = 0x70000014
    GPIO_MODULE_CLEAR_REG = 0x7000080C
    GPIO_MODULE_ENABLE_REG = 0x700008F0
    GPIO_DELAY_REG = 0x70000804
    GPIO_DELAYED_SET_REG = 0x70000814

    self._hololink.write_uint32(
        GPIO_DELAYED_IO_OUTPUT_REG, 0x00000100
    )  # Enable DELAYED_IO output on GPIO

    # GPIO Pin 0 will be set HIGH on Frame Start event
    sequencer = self._hololink.sif0_frame_start_sequencer()
    sequencer.write_uint32(GPIO_MODULE_CLEAR_REG, 0x1)  # Clear all
    sequencer.write_uint32(
        GPIO_MODULE_ENABLE_REG, 0x1
    )  # To enable the whole module
    sequencer.write_uint32(GPIO_DELAY_REG, 0x0)  # 0ms delay
    sequencer.write_uint32(GPIO_DELAYED_SET_REG, 0x1)  # Delayed set
    sequencer.write_uint32(GPIO_DIRECTION_REG_BASE, 0x2)  # Set pin 1 as Input
    sequencer.enable()

    # Setting up sequencer to retrieve timestamp on LED_TRIGGER aka GPIO0
    sequencer = self._hololink.gpio0_sequencer()
    sequencer.enable()

    # When the PD_ACTIVE aka GPIO1 goes HIGH, timestamp is retrieved to calculate end to end measurements.
    # Also LED_TRIGGER aka GPIO0 is set to LOW to ACK that PD_ACTIVE went HIGH.
    sequencer = self._hololink.gpio1_sequencer()
    sequencer.write_uint32(
        GPIO_MODULE_ENABLE_REG, 0x1
    )  # To enable the whole module
    sequencer.write_uint32(GPIO_MODULE_CLEAR_REG, 0x1)  # To clear all
    sequencer.enable()

Key Configuration Parameters#

  • GPIO_DELAYED_IO_OUTPUT_REG (0x70000014) - Enables DELAYED_IO output on the GPIO pin

  • GPIO_MODULE_CLEAR_REG (0x7000080C) - Clears all previous sequencer states

  • GPIO_MODULE_ENABLE_REG (0x700008F0) - Enables the sequencer module

  • GPIO_DELAY_REG (0x70000804) - Sets the LED trigger delay (0 for rolling shutter; set to sensor_readout_time for global shutter)

  • GPIO_DELAYED_SET_REG (0x70000814) - Enables delayed set functionality

Event Acknowledgment for Continuous Measurement#

To enable continuous latency measurements, events must be acknowledged after each measurement cycle. The following MultiEventAcknowledgmentOperator class is implemented in both applications e2e_imx274_latency.py and e2e_vb1940_latency.py:

class MultiEventAcknowledgmentOperator(holoscan.core.Operator):
    """
    Operator that acknowledges events after a specified frame count.
    """

    def __init__(self, fragment, hololink, frame_threshold=50, *args, **kwargs):
        super().__init__(fragment, *args, **kwargs)
        self._hololink = hololink
        self._frame_threshold = frame_threshold
        self._frame_count = 0

    def setup(self, spec):
        spec.input("input")
        spec.output("output")

    def compute(self, op_input, op_output, context):
        in_message = op_input.receive("input")

        self._frame_count += 1

        if self._frame_count >= self._frame_threshold:
            logging.info(f"Acknowledging events after {self._frame_count} frames")
            self._hololink.write_uint32(0x0000020C, 0xFFFFFFFF)  # Set flag
            self._hololink.write_uint32(0x0000020C, 0x0)  # Clear flag
            # Reset frame counter for next acknowledgment cycle
            self._frame_count = 0

        op_output.emit(in_message, "output")

Sequencer Operation Flow#

  1. Frame Start Detection - HSB FPGA detects camera frame start signal

  2. Immediate LED Trigger - GPIO pin 0 set high with 0ms delay (rolling shutter)

  3. Event Processing - LED_TRIGGER and PD_ACTIVE events are processed

  4. Frame Counting - MultiEventAcknowledgmentOperator tracks frame count

  5. Event Acknowledgment - After N frames, events are acknowledged for next cycle

  6. Cycle Repeat - Process repeats for continuous measurement

Global Shutter Modifications#

For global shutter cameras (like VB1940), the sequencer configuration in e2e_vb1940_latency.py modifies the delay parameter to account for sensor readout time:

# Global shutter configuration
sequencer.write_uint32(GPIO_DELAY_REG, sensor_readout_time)  # Add readout time delay

Where sensor_readout_time is the specific readout time for your sensor mode.

Run Latency Tool app#

Once your HSB system is operational, run the app using:

# Navigate to latency measurement examples
# for IMX274
python3 examples/e2e_imx274_latency.py --fullscreen
# for VB1940
python3 examples/e2e_vb1940_latency.py --fullscreen