この記事は、GROOVE Xアドベントカレンダー2025 の8日目の記事です。
こんにちはfmyです。皆さんはJetsonを使っていますか?
LOVOTではJetson Orin NXシリーズを採用しています。 Jetsonは公式のライブラリやリファレンスが充実しており扱いやすいのですが、 開発キットそのままを動かすことはできても、カメラをつないだり開発キット以外のキャリアボードを使うと未知の挙動に悩まされることがあります。 ここではJetsonを使うアプリケーション開発者が知っておくと役に立つかもしれない話をします。
Jetson LinuxのBoot Flow
アプリケーション開発者にとっては起動後に何するかが重要ですが、Jetson Linuxは起動前に色々しており、アプリケーション側にも大きく関係します。
Jetson AGX Orin, Orin NX and Orin Nano Boot Flow The following diagram shows the flow of control in the boot software
上はJetson Linuxのソフトウェアリファレンスから引用したブートソフトウェアの制御フローの図です。 大まかに3ステージに分かれており、BPMPではJetsonのデバイスを利用可能にし、PSCはLinuxのセキュリティを担当し、最後にCPUが起動してLinuxカーネルを起動します。 起動しなかったりデバイスが認識されないときには正しくflashできているかを確認しましょう。
BPMP設定例: Super Mode
2024/11/17にOrin Nano 開発キットの"Super" Boostが発表され、JetPack 6.1.1で有効化されました。 この機能はどのように実現されているのでしょうか?
JetsonはnvpmodelでCPUやGPUなどのクロックの設定をできるようになっています。
特定の電力制限モードではこのファイルに上限値を設定してますが、MAX_Nモードの場合は-1で別のところで定義された設定可能な最大値を指定するようになっています。
この最大値がBPMPのデバイスツリーによって設定されています。
Jetson Linux 36.4で追加されているtegra234-bpmp-3767-0000-3768-super-maxn.dtbがまさにこのSuperモード向けの設定値が書かれたデバイスツリーなのです。
ということは、SuperMode対応以前のL4T 35.6.2でも、開発キットflash時に*super-maxn.dtbを使うことでDLAを1.2GHzで動作させることができます。
以下の例はL4T 35.6.2でSuperModeと同じ周波数で動作させている例です。DLAは本来614.4MHzですが1.2GHzとなっています。

キャリアボードのEEPROM
BPMPではSoCだけではなくキャリアボードもみます。 Jetson EEPROM Layoutに書かれている情報です。 MACアドレスの指定を行うことを想定されており、CRC-8検証が通らなければJetsonは起動しないようになっています。
もしも何らかの理由でキャリアボードのEEPROMにアクセスできない場合はEEPROM ModificationsにあるようにEEPROMを無視させることで起動できるようになります。
# Linux_for_Tegra/bootloader/t186ref/BCT/tegra234-mb2-bct-misc-p3767-0000.dts - cvb_eeprom_read_size = <0x100> + cvb_eeprom_read_size = <0x0>
Timerの話
Jetsonを使いたい人の多くは何らかのセンサー、特に画像のようなデータを処理したいケースが多いでしょう。 そしてセンサー値を扱うにはデータの時刻が重要ですね。 Jetsonはそのような用途のためにTimerの実装がされています。
次の図はOrin SoCのマニュアルにあるTimerの階層です。
DP-10508-002_v1.2 | Page 5785 Timer Hierarchy
Jetson SoCではTSCというタイマーを元にしてCPUやカメラ関係のブロックにSecondary TSCの名前で38.4 MHz(26.04ns解像度)の信号を、NV関係はNV Timerの名前で1 MHz(1us解像度)でクロックが供給されています。 カメラ関係に付与されている各タイムスタンプはSecondary TSCで付与された値になります。 また、L4T 36.xではLIC IRQやGPIOの変化についてもTSCのタイムスタンプを得る方法をHTE Driverとして提供されています。
TSCというのは Timer's System Counterの略称で、より一般的にはTimeStamp Counterと呼ばれる機能と同じものになります。
一定周期でカウントアップするもので、これ自身は何らかのリファレンス時計と同期をしているものではありません。
このカウンタ値をUnix Timeなどに変換するには、現在のTSCとセンサー値の差分を現在時刻から引くことで求めます。
またSecondary TSCはArmのGlobal Timerとしても供給されているため、LinuxからもアクセスできてMONOTONIC_RAWに対しては固定のオフセットがある値になります。
TSCの取得はArmのCNTVCT_EL0命令を使います。
L4T 35.xまではTSC - MONOTONIC_RAWの値であるoffset_nsという値がsysに出力されていましたが、L4T 36.xからはCPU命令での取得を推奨されています。
何らかの時刻に変換するなら、TSCと同時に時刻を取りセンサー値との差分を時刻から引けばよいです。 可能な限り同時に時刻を取るならforumと同様の実装で実現できます。 以下はRustでの実装例です。
pub fn get_ts_with_clock(clock_id: nix::time::ClockId) -> nix::Result<(u64, TimeSpec)> { // Conversion constants for cycles to nanoseconds const CYCLES_MULTIPLIER: u64 = 100; const FRQ_DIVISOR: u64 = 10000; // Jetson Orin TSC frequency is Fixed at 31,250,000 Hz const NS_MULTIPLIER: u64 = 1000; // Conversion formula: unsafe { let mut cycles: u64; let mut frq: u64; std::arch::asm!("mrs {0}, cntfrq_el0", out(reg) frq); std::arch::asm!("mrs {0}, cntvct_el0", out(reg) cycles); let r = clock_id.now()?; let tsc_ns = (cycles * CYCLES_MULTIPLIER / (frq / FRQ_DIVISOR)) * NS_MULTIPLIER; Ok((tsc_ns, r)) } }
カメラのタイムスタンプ
カメラ画像には2つのタイムスタンプ、SoFとEoFが付与されています。 ArgusAPIからはSoF, EoFが取得でき、V4L2ではSoFがタイムスタンプとして用いられます。
vb->vb2_buf.timestamp = descr->status.sof_timestamp;
SoFやEoFの時刻は以下のようなイメージで、露光後転送したデータを受信した時点のTSCとなっています。 ローリングシャッターなら最初のラインの露光が終わって露光と転送をしている途中の時刻となります。

V4Lのストライドの話
V4Lの場合はArgusAPIと比較してアクセスできる情報も少なく、画像についてもArgusAPI経由とは異なる制約があります。
V4LバッファーはVIのATOMP(Atomメモリパッカー)が書き込みを行っており、このブロックは1atom=64Bのアライメントで動作しています。
画像の1ラインの長さがこのアライメントと合わない場合、受信した画像データは欠損したりずれることがあります。
この問題を回避するにはV4Lのコントロールにあるpreferred_strideを32の倍数(1pixel=2Bytesの場合)に拡張することでパディングを含んだ形で正常なデータが受信でき、後で範囲外のデータを削除する必要があります。(forum)
おわりに
Jetsonと周辺機器を使うときに躓きやすい要素を紹介しました。
最後になりますが、GROOVE X ではロボット開発したい人材を募集しています。ぜひ以下のリンクからご応募ください!













