JetPack 6 Hardware Timestamp Engine (HTE)
|
Introduction
The Hardware Timestamp Engine (HTE) on NVIDIA Jetson platforms provides precise, deterministic timestamps at the exact moment a hardware event occurs such as GPIO transitions and interrupt triggers. Unlike software-based timestamping, which is subject to kernel scheduling latency, interrupt handling delays, and CPU load, HTE records the event time directly in hardware, eliminating jitter introduced by the operating system. This capability is essential for applications that require accurate time correlation and low-latency measurement, such as sensor synchronization, trigger alignment, latency characterization, real-time signal monitoring, and system debugging.
HTE is particularly useful in systems that require:
- High-precision sensor synchronization
- Multi-camera systems
- Time-aligned data acquisition systems
- External trigger synchronization
- Low-latency interrupt measurement
- Measuring interrupt latency
- Real-time performance analysis
- Edge AI systems requiring precise event correlation
Hardware Timestamp Engine HTE Driver
The information below contains the steps to how to evaluate the Hardware Timestamp Engine (HTE).
Build and install packages
Download packages
Click link https://developer.nvidia.com/embedded/jetson-linux-r363
Download the packages
- Driver Package (BSP) Sources
- Bootlin Toolchain gcc 11.3
Confirm version:
sha1sum aarch64--glibc--stable-2022.08-1.tar.bz2 public_sources.tbz2
Result
289b5a2f1e759a9c86032fbb45c40292b5e3ad9e public_sources.tbz2 a53ce1e738bac038ba6d7ad4e78bf9779b405958 aarch64--glibc--stable-2022.08-1.tar.bz2
Extracting the Toolchain
To extract the toolchain, enter these commands:
mkdir $HOME/l4t-gcc cd $HOME/l4t-gcc mv ~/Downloads/aarch64—glibc—stable-2022.08-1.tar.bz2 . tar -xvf aarch64—glibc—stable-2022.08-1.tar.bz2
Extracting kernel sources
cd $JP6_INSTALL_PATH tar -xf public_sources.tbz2 cd Linux_for_Tegra/source tar xf kernel_src.tbz2 tar xf kernel_oot_modules_src.tbz2 tar xf nvidia_kernel_display_driver_source.tbz2
Build kernel and device tree
Install dependencies
sudo apt install build-essential bc flex bison
Apply patch to enable HTE test driver
diff --git a/hardware/nvidia/t23x/nv-public/tegra234.dtsi b/hardware/nvidia/t23x/nv-public/tegra234.dtsi
index f909b93..e3271ed 100644
--- a/hardware/nvidia/t23x/nv-public/tegra234.dtsi
+++ b/hardware/nvidia/t23x/nv-public/tegra234.dtsi
@@ -9,6 +9,7 @@
#include <dt-bindings/power/tegra234-powergate.h>
#include <dt-bindings/reset/tegra234-reset.h>
#include <dt-bindings/thermal/tegra234-bpmp-thermal.h>
+#include <dt-bindings/pinctrl/pinctrl-tegra.h>
/ {
compatible = "nvidia,tegra234";
@@ -1766,6 +1766,18 @@
nvidia,gpio-controller = <&gpio_aon>;
#timestamp-cells = <1>;
};
+#define GPIO_CAN0_DOUT TEGRA234_AON_GPIO(AA, 0)
+#define GPIO_CAN0_DIN TEGRA234_AON_GPIO(AA, 1)
+
+ hte_test {
+ status = "okay";
+ compatible = "nvidia,tegra194-hte-test";
+ in-gpios = <&gpio_aon GPIO_CAN0_DIN GPIO_ACTIVE_HIGH>;
+ out-gpios = <&gpio_aon GPIO_CAN0_DOUT GPIO_ACTIVE_HIGH>;
+ timestamps = <&hte_aon GPIO_CAN0_DIN>,<&hte_lic 0x19>;
+ timestamp-names = "hte-gpio", "hte-i2c-irq";
+ };
@@ -1853,6 +1889,25 @@
pinmux_aon: pinmux@c300000 {
compatible = "nvidia,tegra234-pinmux-aon";
reg = <0x0 0xc300000 0x0 0x4000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&jetson_io_pinmux_aon>;
+ jetson_io_pinmux_aon: exp-header-pinmux {
+ can0_dout_paa0 {
+ nvidia,pins = "can0_dout_paa0";
+ nvidia,function = "rsvd1";
+ nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+ nvidia,tristate = <TEGRA_PIN_DISABLE>;
+ nvidia,enable-input = <TEGRA_PIN_DISABLE>;
+ };
+
+ can1_stb_pbb0 {
+ nvidia,pins = "can1_stb_pbb0";
+ nvidia,function = "rsvd1";
+ nvidia,pull = <TEGRA_PIN_PULL_NONE>;
+ nvidia,tristate = <TEGRA_PIN_DISABLE>;
+ nvidia,enable-input = <TEGRA_PIN_DISABLE>;
+ };
+ };
};
Build kernel and device tree
cd $JP6_INSTALL_PATH/Linux_for_Tegra/source export CROSS_COMPILE=$HOME/l4t-gcc/aarch64--glibc--stable-2022.08-1/bin/aarch64-buildroot-linux-gnu- make -C kernel export KERNEL_HEADERS=$PWD/kernel/kernel-jammy-src make dtbs
Installation
1. Install new device tree in developer kit using ssh
AGX devkit:
sudo touch /boot/tegra234-p3737-0000+p3701-0000.dtb sudo chown nvidia:nvidia /boot/tegra234-p3737-0000+p3701-0000.dtb
Host Laptop:
scp \ ./nvidia-oot/device-tree/platform/generic-dts/dtbs/tegra234-p3737-0000+p3701-0000.dtb \ nvidia@$DEVKIT_IP:/boot/
1. Edit extlinux.conf to add a new boot option, apply the changes below.
--- /boot/extlinux/extlinux.conf 2024-08-16 15:40:25.478615127 +0000
+++ /boot/extlinux/extlinux.conf 2024-08-14 15:19:14.661687235 +0000
@@ -1,5 +1,5 @@
TIMEOUT 30
-DEFAULT primary
+DEFAULT secondary
MENU TITLE L4T boot options
@@ -9,6 +9,15 @@
INITRD /boot/initrd
APPEND ${cbootargs} root=PARTUUID=4615c8f6-0345-4360-97de-3fde3079d086 rw rootwait rootfstype=ext4 mminit_loglevel=4 console=ttyTCU0,115200 console=ttyAMA0,115200 firmware_class.path=/etc/firmware fbcon=map:0 net.ifnames=0 nospectre_bhb video=efifb:off console=tty0 nv-auto-config
+
+LABEL secondary
+ MENU LABEL primary kernel
+ LINUX /boot/Image_dev
+ FDT /boot/tegra234-p3737-0000+p3701-0000.dtb
+ INITRD /boot/initrd
+ APPEND ${cbootargs} root=PARTUUID=4615c8f6-0345-4360-97de-3fde3079d086 rw rootwait rootfstype=ext4 mminit_loglevel=4 console=ttyTCU0,115200 console=ttyAMA0,115200 firmware_class.path=/etc/firmware fbcon=map:0 net.ifnames=0 nospectre_bhb video=efifb:off console=tty0 nv-auto-config
+
# When testing a custom kernel, it is recommended that you create a backup of
# the original kernel and add a new entry to this file so that the device can
# fallback to the original kernel. To do this:
1. Reboot the AGX Orin devkit
Hardware Timestamp Engine HTE Driver
Driver code
This driver controls a devices that have built in hardware timestamping engines which can monitor sets of system signals, lines, etc... in realtime for state change; upon detecting the change they can automatically store the timestamp at the moment of occurrence.
Driver file: kernel/kernel-jammy-src/drivers/hte/hte-tegra194.c
This driver is compatible with T194 and T234 and there 2 types of the driver: GPIO GTE and LIC IRQ GTE
Description from the documentation:
File: kernel-jammy-src/Documentation/driver-api/hte/tegra-hte.rst
GPIO GTE -------- This GTE instance timestamps GPIO in real time. For that to happen GPIO needs to be configured as input. Only the always on (AON) GPIO controller instance supports timestamping GPIOs in real time as it is tightly coupled with the GPIO GTE. To support this, GPIOLIB adds two optional APIs as mentioned below. The GPIO GTE code supports both kernel and userspace consumers. The kernel space consumers can directly talk to HTE subsystem while userspace consumers timestamp requests go through GPIOLIB CDEV framework to HTE subsystem. LIC (Legacy Interrupt Controller) IRQ GTE ----------------------------------------- This GTE instance timestamps LIC IRQ lines in real time. The hte devicetree binding described at ``Documentation/devicetree/bindings/timestamp`` provides an example of how a consumer can request an IRQ line. Since it is a one-to-one mapping with IRQ GTE provider, consumers can simply specify the IRQ number that they are interested in. There is no userspace consumer support for this GTE instance in the HTE framework.
There is only one driver for T194 and T234 and it is able to manage GPIO GTE and LIC IRQ GTE, the only difference is the driver data.
file: hte-tegra194.c: line 652
static const struct of_device_id tegra_hte_of_match[] = {
{ .compatible = "nvidia,tegra194-gte-lic", .data = &t194_lic_hte},
{ .compatible = "nvidia,tegra194-gte-aon", .data = &t194_aon_hte},
{ .compatible = "nvidia,tegra234-gte-lic", .data = &t234_lic_hte},
{ .compatible = "nvidia,tegra234-gte-aon", .data = &t234_aon_hte},
{ }
};
Device tree
Tegra SoC has two instances of generic hardware timestamping engines (GTE) known as GTE GPIO and GTE IRQ, which can monitor subset of GPIO and on chip IRQ lines for the state change respectively.
The 2 instances are in the device tree:
Device tree file: ./hardware/nvidia/t23x/nv-public/tegra234.dtsi
File: tegra234.dtsi: line: 1212
hte_lic: hardware-timestamp@3aa0000 {
compatible = "nvidia,tegra234-gte-lic";
reg = <0x0 0x3aa0000 0x0 0x10000>;
interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
nvidia,int-threshold = <1>;
#timestamp-cells = <1>;
};
File: tegra234.dtsi: line: 1761
hte_aon: hardware-timestamp@c1e0000 {
compatible = "nvidia,tegra234-gte-aon";
reg = <0x0 0xc1e0000 0x0 0x10000>;
interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
nvidia,int-threshold = <1>;
nvidia,gpio-controller = <&gpio_aon>;
#timestamp-cells = <1>;
};
This is an example of a consumer using both drivers, AON and LIC
File: kernel-jammy-src/Documentation/devicetree/bindings/timestamp/hte-consumer.yaml
hte_tegra_consumer {
timestamps = <&tegra_hte_aon 0x9>, <&tegra_hte_lic 0x19>;
timestamp-names = "hte-gpio", "hte-i2c";
};
But in the test example below our consumer is going to use hte-gpio only.
HTE Test Driver
This driver demonstrates HTE API usage for both IRQ and GPIO GTE. The driver enable the hardware timestamping on specific GPIO. The purpose is to record timestamps of events that occur on an input GPIO pin and I2C bus 1.
Driver code
Driver file: kernel/kernel-jammy-src/drivers/hte/hte-tegra194-test.c
The Test driver uses HTE framework APIs for the consumers from kernel/kernel-jammy-src/drivers/hte/hte.c.
API functions:
- hte_init_line_attr
- hte_ts_get
- hte_ts_put devm_hte_request_ts_ns
- hte_request_ts_ns hte_enable_ts
- hte_disable_ts of_hte_req_count
- hte_get_clk_src_info
Device tree
The DT test driver node need to be added (see installation instruction above), The driver need 2 GPIOs, An output pin the will be used to produce a signal to the input pin.
#define GPIO_OUT TEGRA234_AON_GPIO(AA, 0)
#define GPIO_IN TEGRA234_AON_GPIO(AA, 1)
hte_test {
status = "okay";
compatible = "nvidia,tegra194-hte-test";
in-gpios = <&gpio_aon GPIO_IN GPIO_ACTIVE_HIGH>;
out-gpios = <&gpio_aon GPIO_OUT GPIO_ACTIVE_HIGH>;
timestamps = <&hte_aon GPIO_CAN0_DIN>,<&hte_lic 0x19>;
timestamp-names = "hte-gpio", "hte-i2c-irq";
};
The GTE instance will timestamps this input GPIO in real time. For that to happen GPIO only the always on (AON) GPIO controller instance supports timestamping GPIOs in real time.
Confirm GPIO configuration with the comment:
nvidia@tegra-ubuntu:~$ sudo cat /sys/kernel/debug/gpio | head -n 3 gpiochip1: GPIOs 316-347, parent: platform/c2f0000.gpio, tegra234-gpio-aon: gpio-316 (PAA.00 |out ) out lo gpio-317 (PAA.01 |in ) in hi IRQ
Driver GPIO test description
The operation of the test driver is very simple, a GPIO is configured as an output, this GPIO will produce a periodic signal, the output GPIO must be connected to the input GPIO, the input GPIO will be registered and every time a level change is detected the timestamp of the event is read from the GTE and it is printed.
The driver starts getting the input and output GPIO from the DT:
hte.gpio_in = gpiod_get(&pdev->dev, "in", 0);
if (IS_ERR(hte.gpio_in)) {
dev_err(&pdev->dev, "failed to get gpio in\n");
ret = -EINVAL;
goto free_gpio_out;
}
hte.gpio_out = gpiod_get(&pdev->dev, "out", 0);
if (IS_ERR(hte.gpio_out)) {
dev_err(&pdev->dev, "failed to get gpio out\n");
ret = -EINVAL;
goto out;
}
The GPIO are configured as an input and output respectively.
The driver gets I2C IRQ from the DT:
timestamps = <&hte_aon GPIO_CAN0_DIN>,<&hte_lic 0x19>;
The driver get the number of entities to timestamp, for this example we have 2 for the GPIO input and I2C IRQ.
cnt = of_hte_req_count(hte.pdev);
if (cnt < 0)
goto free_irq;
Driver information message:
[ 3601.281953] tegra_hte_test bus@0:hte_test: Total requested lines:2
Initialize hte line attributes. An HTE description need to be provided and the GPIO In handler.
ret = hte_init_line_attr(&hte.desc[i], 0, 0, NULL,
hte.gpio_in);
Initialize the consumer provided HTE descriptor.
ret = hte_ts_get(hte.pdev, &hte.desc[i], i);
if (ret) {
Enable the hardware timestamp in nanoseconds.
ret = devm_hte_request_ts_ns(hte.pdev, &hte.desc[i],
process_hw_ts, NULL,
&hte.desc[i]);
The Callback process_hw_ts will be the function called for each timestamp event, for this test, the timestamp will be printed.
static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
{
. . .
pr_info("HW timestamp(%u: %llu): %llu, edge: %s\n",
desc->attr.line_id, ts->seq, ts->tsc,
(ts->raw_level >= 0) ? ((ts->raw_level == 0) ?
"falling" : "rising") : edge);
In order to generate an output signal, The GPIO output is configured to change the voltage level periodically.
GPIO timer configuration There is a timer that is set a callback
timer_setup(&hte.timer, gpio_timer_cb, 0);
The callback change the GPIO output level each 8 seconds
static void gpio_timer_cb(struct timer_list *t)
{
(void)t;
gpiod_set_value(hte.gpio_out, !gpiod_get_value(hte.gpio_out));
mod_timer(&hte.timer, jiffies + msecs_to_jiffies(8000));
}
Set first timer time out.
/* Modify a timer's timeout */
mod_timer(&hte.timer, jiffies + msecs_to_jiffies(5000));
Results GPIO
Each time is detected a rising level the driver prints the timestamp
nvidia@tegra-ubuntu:~$ sudo dmesg | grep "HW timestamp" [ 12.767845] HW timestamp(1: 1): 38507387040, edge: rising [ 29.151851] HW timestamp(1: 2): 54891386272, edge: rising [ 45.535847] HW timestamp(1: 3): 71275386240, edge: rising [ 61.919844] HW timestamp(1: 4): 87659378848, edge: rising [ 78.303843] HW timestamp(1: 5): 104043384768, edge: rising [ 94.687834] HW timestamp(1: 6): 120427378912, edge: rising [ 111.071833] HW timestamp(1: 7): 136811378880, edge: rising [ 127.455830] HW timestamp(1: 8): 153195381728, edge: rising [ 143.839841] HW timestamp(1: 9): 169579383296, edge: rising [ 160.223843] HW timestamp(1: 10): 185963378688, edge: rising
Results I2C IRQ
For the LIC IRQ line, it uses 0x19 interrupt which is i2c controller 1. Run i2cdetect command to generate i2c bus transactions which creates timestamp data.
sudo i2cdetect -y 1 1> /dev/null
Timestamp log output
sudo dmesg | grep HW | tail -n 44 [ 2777.258055] HW timestamp(25: 1): 2803008926464, edge: Unknown [ 2777.258113] HW timestamp(25: 2): 2803009004736, edge: Unknown [ 2777.259070] HW timestamp(25: 3): 2803009958112, edge: Unknown . . . [ 2777.273185] HW timestamp(25: 42): 2803024080352, edge: Unknown [ 2777.273547] HW timestamp(25: 43): 2803024443552, edge: Unknown [ 2777.273581] HW timestamp(25: 44): 2803024472288, edge: Unknown
Userspace consumer HTE Test
This application demonstrates HTE API usage for GPIO GTE. The driver enable the hardware timestamping on specific GPIO. The purpose is to record timestamps of events that occur on an input GPIO pin.
Installation
Copy the sources into AGX Orin.
cd $JP6_INSTALL_PATH cd Linux_for_Tegra/source scp -r kernel/kernel-jammy-src/tools/gpio \ nvidia@$DEVKIT_IP:/home/nvidia/
For userspace consumers, GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE flag must be specified during IOCTL calls to returns the timestamp in nanoseconds. Apply the patch below to add the flag.
--- /usr/include/linux/gpio.h 2024-08-23 21:29:28.136459261 +0000
+++ /usr/include/linux/gpio.h 2024-08-23 21:29:28.136459261 +0000
@@ -66,6 +66,8 @@
@GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN: line has pull-down bias enabled
@GPIO_V2_LINE_FLAG_BIAS_DISABLED: line has bias disabled
@GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME: line events contain REALTIME timestamps
+ * @GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE: line events contain timestamps from
+ * hardware timestamp engine
/
enum gpio_v2_line_flag {
GPIO_V2_LINE_FLAG_USED = _BITULL(0),
@@ -80,6 +82,7 @@
GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN = _BITULL(9),
GPIO_V2_LINE_FLAG_BIAS_DISABLED = _BITULL(10),
GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME = _BITULL(11),
+ GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE = _BITULL(12),
};
/*
Compilation
Compile the application
gcc gpio-utils.c gpio-event-mon.c -o gpio-event-mon -std=gnu99 -D_GNU_SOURCE
Code description
The application has 2 main functions:
- main: it is the entry function and has a parser to compute all parameters.
- monitor_device: set the configuration to get the event and and timestamp detected in the input GPIO.
main function description
Initialize gpio_v2_line_config structure
struct gpio_v2_line_config config;
memset(&config, 0, sizeof(config));
Parsing the the user parameters:
while ((c = getopt(argc, argv, "c:n:o:b:dsrfwt?")) != -1) {
Set flags configuration based on the user parameters, This flag is required to use Hardware timestamp engine
config.flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE;
Configure GPIO and handle all the events
int monitor_device(const char *device_name,
unsigned int *lines,
unsigned int num_lines,
struct gpio_v2_line_config *config,
unsigned int loops)
- device_name: GPIO chip device: example gpiochip1
- lines: An array desired lines, specified by offset index for the associated GPIO device.
- num_lines: the number of lines to request.
- config: The new config for requested gpio.
- loops: wait for <n> loops
monitor_device function description
Open GPIO chip device: /dev/gpiochip1
cfd = open(chrdev_name, 0);
Request gpio lines selected by the user in the gpiochip
ret = gpiotools_request_line(device_name, lines, num_lines, config,
"gpio-event-mon");
- device_name: gpiochip without prefix "/dev/",
- lines: An array desired lines, specified by offset index for the associated GPIO device.
- num_lines: The number of lines to request.
- config: The new config for requested gpio.
- consumer:The name of consumer, such as "gpio-event-mon"
Read the current line values
ret = gpiotools_get_values(lfd, &values);
After to read the initial values, this information is printed:
Monitoring line 1 on gpiochip1 Initial line value: 0
Now the application is ready to wait for an event.
while (1) {
struct gpio_v2_line_event event;
ret = read(lfd, &event, sizeof(event));
Print the information of all the event received, for example GPIO EVENT at 22948895457984 on line 1 (1|1) rising edge.
fprintf(stdout, "GPIO EVENT at %" PRIu64 " on line %d (%d|%d) ",
(uint64_t)event.timestamp_ns, event.offset, event.line_seqno,
event.seqno);
switch (event.id) {
case GPIO_V2_LINE_EVENT_RISING_EDGE:
fprintf(stdout, "rising edge");
break;
case GPIO_V2_LINE_EVENT_FALLING_EDGE:
fprintf(stdout, "falling edge");
break;
Run the application
To run sample HTE tests, you must short pins 29 and 31 on the carrier board’s 40-pin header, where pin 29 is AON GPIO AA.1 configured as input, and pin 31 is AON GPIO AA.0 configured as output.
This application register a GPIO and every time a level change is detected the timestamp of the event is printed.
We need to know the GPIO device and offset of the input and output GPIOs
Run the command below the get the device name and offset of GPIO PAA.0 and PAA.1.
nvidia@tegra-ubuntu:~/gpio$ sudo gpiofind PAA.00 gpiochip1 0 nvidia@tegra-ubuntu:~/gpio$ sudo gpiofind PAA.01 gpiochip1 1
Now using the input GPIO device and offset, run the application with the command below:
sudo ./gpio-event-mon -n gpiochip1 -o 1 -r -b 10000 -t
The parameters are:
- -n gpiochip1 GPIO named device
- -o 1 Offset of line to monitor
- -r Listen for rising edges
- -t Report the hardware timestamp for events (GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)
- -b 1000 Debounce the line with period n microseconds
Use the output GPIO to produce an event, You can use the command below
sudo gpioset --mode=wait gpiochip1 0=1
Result
Each time is detected a rising level the application prints the timestamp
nvidia@tegra-ubuntu:~/gpio$ sudo ./gpio-event-mon -n gpiochip1 -o 1 -r -b 10000 -t Monitoring line 1 on gpiochip1 Initial line value: 0 GPIO EVENT at 22948895457984 on line 1 (1|1) rising edge GPIO EVENT at 22957495457984 on line 1 (2|2) rising edge GPIO EVENT at 22959911457984 on line 1 (3|3) rising edge GPIO EVENT at 22963127457984 on line 1 (4|4) rising edge
For direct inquiries, please refer to the contact information available on our Contact page. Alternatively, you may complete and submit the form provided at the same link. We will respond to your request at our earliest opportunity.
Links to RidgeRun Resources and RidgeRun Artificial Intelligence Solutions can be found in the footer below.