We raised a Jetson Orin Nano's GPU speed limit in its firmware. Here are the measurements.

Listening edition

Narrated · 7 chapters · about 8 minutes

Listening editions are read aloud by synthesized voices generated with ElevenLabs. The words are written and checked by people at Axiovex Systems.

THE MACHINE

A small inference box and a paperwork speed limit

  • The machine is a Jetson Orin Nano Super Developer Kit, the 8 GB module on NVIDIA's reference carrier, with a 1 TB NVMe drive, in its own enclosure with the stock heatsink and fan
  • It runs JetPack 7.2.1 and serves models with Ollama, in MAXN SUPER mode with the clocks locked
  • The question: the GPU tops out at 1020 MHz, and we wanted to know whether that limit was silicon or paperwork

TakeawayThe ceiling was a number stored in firmware, and numbers in firmware can be edited.

0:00 / 7:53 Chapter 1 of 7

We run a small fleet of machines for local AI inference work, and the smallest of them is an NVIDIA Jetson Orin Nano. It is the low power node, the one that sits there sipping watts and answering model requests all day. This article is about a question we could not leave alone: the module's GPU tops out at 1020 MHz, and we wanted to know whether that limit was silicon or paperwork.

It turned out to be paperwork. The ceiling is a number stored in the module's firmware, and numbers in firmware can be edited. What follows is what we did, what it measured, and what went wrong along the way, including 1 result that got worse.

The exact setup

Nothing in this article means anything without the setup, so here it is. The machine is an NVIDIA Jetson Orin Nano Super Developer Kit (NVIDIA part 945-13766-0000-000), which is the 8 GB Orin Nano module (P3767-0003) on NVIDIA's reference carrier board (P3768). Storage is a WD_BLACK SN7100 1 TB NVMe drive. The kit is running in its own enclosure with the stock NVIDIA active heatsink and fan it shipped with, nothing third party. It runs JetPack 7.2.1 (L4T R39.2.1, Ubuntu 24.04.5 LTS, CUDA 13.2.2, TensorRT 10.16) and serves models with Ollama 0.40.15. Its performance posture is MAXN SUPER power mode with the clocks locked by jetson_clocks and the fan profile set to cool. Peak temperature we have ever recorded under load is 77.8C, against a throttle point of 99C.

Power matters on this machine, and it gets its own section below, because the first power supply we ran it on was not a power supply at all.

The false start: the machine kept dying, and it was the plug

Before any of this, the module had a habit of dying silently. In the 15 W power mode it ran fine. At 25 W or MAXN SUPER it would lock up the moment a real GPU workload started, no error, no log, just gone. We spent a day bisecting it as a silicon or thermal problem before looking at the wall wart, and the wall wart was the problem. It was labelled as a charger for a battery powered device, made in 2019, rated at 25.2 V and 800 mA. That is a constant current lithium battery charger, about 20.2 W maximum, putting 25.2 V into a carrier board whose documented input range is 9 to 20 V. The kit's specified supply is a regulated 19 V, 45 W unit.

The arithmetic closed the case later: once the machine was on a proper supply, we measured the module alone peaking at 23.7 W during the 25 W test, which is more than the charger's entire output rating. The lesson is boring and it is the one we keep relearning. When a machine misbehaves, check the boring parts first, and read the label on the plug before you blame the silicon.

Where the speed limit actually lives

With the power sorted, the machine ran its full rate, and the original question came back. At MAXN SUPER the GPU frequency table ends at 1020 MHz. We tried the easy routes first. The running system will not let you raise it: a root write asking for 1122 MHz gets silently clamped back to 1020. The ceiling is enforced below the operating system, in the BPMP firmware. The BPMP is a small power management processor on the module, and it boots from its own device tree blob stored in a dedicated flash partition, A_bpmp-fw-dtb.

We read that blob from NVIDIA's own board support package for this exact kit. The GPU ceiling turned out to be exactly 3 properties in it (the maximum clock for the 2 graphics clusters and the GPU system clock), each set to 1020000000 Hz. There is no voltage table in the blob to edit alongside them. The firmware derives voltage from characterization data fused into each chip at the factory, so the blob carries ceilings only.

One more fact decided the target numbers. NVIDIA ships a higher performance bin of this same GPU family in the Orin NX module, and its firmware blob is the same file with the same structure. We diffed the 2 blobs. Regulators, thermal policy, and safety structures are byte for byte identical. The difference is the ceiling values: 1173 MHz in the NX file. So our steps were 1122 MHz first (one step of the chip's own 102 MHz clock grid above stock) and then 1173 MHz, NVIDIA's own number for this silicon.

How we did it without bricking the machine

The procedure, in order, because the order is the safety case:

  1. Benchmark first, change nothing. We built a fixed benchmark suite (same prompts, same seed, same settings every run) and recorded the full stock numbers at 1020 MHz before touching anything.
  2. Rehearse the recovery. Our first flash was not the modification. It was the stock firmware blob, reflashed onto the stock partition, to prove the whole path worked before we needed it.
  3. Change 12 bytes. The patch edits the 3 ceiling values in place in a copy of the stock blob. Same file size, 12 bytes different, verified by re-reading the parsed values before the file went anywhere.
  4. Flash 1 partition on 1 slot. The Jetson keeps 2 firmware slots, A and B. We flashed only slot A's bpmp-fw-dtb partition. Slot B still holds stock firmware today, untouched, as a parachute.
  5. Hash check before every write. The flash script refuses to run unless the file on disk hashes to the expected value for the mode being flashed, and refuses unless the board is actually sitting in recovery mode.
  6. Guard the first workload. After each flash, the first GPU job ran with temperature and error counters being watched, with named abort conditions. Only after it passed did the full suite run.

NVIDIA's position on this is plain and we will state it plainly too: overclocking these modules is unsupported and voids the warranty. Developer kit modules are not fused against modified firmware, which is the only reason this boots. We accepted the risk on a developer kit we can reflash from a second machine in about 4 minutes. Do not do this to a machine you cannot afford to reflash, and do not do it at all on a production module carrying a warranty you care about.

The measurements

Same suite every time: 3 language models served by Ollama (decode speed, prefill speed), a 4 way parallel run, an embedding model, and a sustained PyTorch matrix multiply loop. Median of 3 runs for decode. Here are the inch marks:

MetricStock 1020 MHzStep 1, 1122 MHzStep 2, 1173 MHz
Qwen3 4B decode18.43 tok/s18.88 tok/s19.10 tok/s
Qwen3 4B prefill660.7 tok/s714.5 tok/s734.5 tok/s
Qwen3 8B decode10.33 tok/s9.93 tok/s10.09 tok/s
Qwen3 8B prefill373.8 tok/s402.9 tok/s413.5 tok/s
Granite 3.1 MoE 3B decode55.47 tok/s58.56 tok/s60.73 tok/s
Granite 3.1 MoE 3B prefill1,664.5 tok/s1,784.0 tok/s1,847.3 tok/s
Qwen3 4B, 4 parallel streams70.57 tok/s74.26 tok/s74.69 tok/s
Embeddings (nomic-embed-text)77.7 chunks/s81.0 chunks/s81.0 chunks/s
Sustained matmul, 150 seconds1,1201,1401,100
Peak temperature in suite71.6C77.8C75.9C

The pattern in the table is consistent, and it makes sense once you split the work by what limits it:

  • Prefill is compute bound and bursty, and it gained the most: about 11% at 1173 MHz on all 3 models. In serving terms that is time to first token, the wait a user actually feels on every request.
  • Single stream decode is limited by memory bandwidth, and the memory speed did not change, so decode barely moved. The 8B model sits within noise of stock on decode, and we are calling that flat rather than spinning it.
  • The sustained matmul loop got slightly slower at 1173 MHz (1,100 against 1,140 at step 1 and 1,120 stock), measured the same way twice. Under a flat out sustained load the module's current limiter is the binding constraint, and a higher ceiling cannot express itself there. It can even cost a little. This box's job is bursty inference, not flat out sustained math, so we kept 1173. But if your workload is a sustained soak, our own numbers say the higher ceiling is not a win, and you should measure your workload and not ours.

What went wrong while measuring (and got thrown out)

2 full benchmark runs from this project are not in the table, on purpose.

Once, 2 copies of the benchmark suite ran at the same time after a launch mistake around a power cycle. They split the machine between them and produced numbers like 5.6 tok/s decode on a model that does 19. Both runs were discarded in full.

Once, the suite measured the machine with the model server running on CPU only and nobody noticed until the numbers were read closely. After a reboot, Ollama's GPU discovery raced the rest of the boot sequence, timed out, and the service quietly served everything on the CPU at about half speed, with no error anywhere a user would look. We caught it because a decode number was wrong in a way clocks could not explain, restarted the service, verified GPU rates with a probe request, and only then measured again. That failure mode now has a boot time check on the machine: after every boot, a small service times a real generation, and if the rate comes back in the CPU range (about 11 tok/s on the 4B model instead of about 19) it restarts the model service and measures again, up to 5 tries, and logs every verdict. It earned its keep the first time it ran for real. On the next reboot the discovery failed again, the check caught the slow probe, restarted the service twice, and confirmed 19.53 tok/s before it stood down.

We are including this section because it is the part of benchmarking people usually leave out, and it is the part that decides whether the table above is worth anything. A benchmark you cannot audit is a rumor. Ours has the raw logs behind every row, and the bad runs are logged too, marked as bad.

Where it stands

The module is in production at 1173 MHz, MAXN SUPER, clocks locked, fan on cool, serving the same models it served at stock, with prefill about 11% faster than the day it came out of the box. Slot B still holds the stock firmware. Rolling back is a 4 minute reflash, and every step of that path has been walked in both directions.

The honest summary is a narrow one. The silicon had headroom because NVIDIA sells the same silicon with a higher number on it, and the limit on our unit was a value in a file. We changed the value, carefully, with a parachute, and measured everything twice. That is the whole trick, and the measuring is most of the work.

If you run Jetsons at the edge and want to compare notes on any of this, we are easy to find.

Corrections and updates

No corrections or updates recorded since publication.

Likes are a rough signal only: one per browser, not a count of readers.

THE AXIOVEX SIGNAL — MONTHLY

The month, in your inbox.

What changed in Michigan industry and the field → one email, after the monthly economic releases, with the numbers, their vintages, and why they matter.

Choose what to join

Three separate lists — join only the ones you want, and leave any of them in one click, any time. We send a confirmation first — nothing arrives until you click it. No tracking pixels, no per-reader analytics. Privacy policy

Next step

Questions about this analysis?

If this analysis touches a problem you’re facing, we’d like to hear about it.

Start a conversation