An automated test bench for firmware

AI agents test your firmware on real hardware.

A hundred iterations, unattended. You review the one that passed.
oracova bench full-screen →
$ oracova task "implement and test the gyro driver"
plan MPU6000 gyro/accel · SPI1 · DUT STM32F411, real silicon
world mpu6000 model v1.2, validated against real silicon
synth → bench FPGA ................ ok · 4 m 12 s
bench DUT board seated
 
netlist parse DUT board PCBA: SPI1 → PA5 SCK · PA6 MISO · PA7 MOSI · PA4 nCS
map DUT pins → base connector → FPGA balls ............ 4 nets routed
bench internal logic analyzer armed on SPI1
 
wiretest gpio loopback, no protocol: fabric drives each net, reads each pin back over SWD
PA5 PA6 PA7 PA4 4/4 nets toggle · 0 shorts · 0 opens
bench wiring is ground truth: every failure after this line is firmware
 
iter 1 write driver: spi init · read WHO_AM_I @ 12.5 MHz · t+7 min
build ok flash ok test running
who_am_i .................................. FAIL read 0x9C then 0xE3, expected 0x68
internal logic analyzer · spi1 · iter 1
nCSSCLKMISO12.5 MHz on a 1 MHz register bus
Config read clocked at 12.5 MHz. This part caps register reads at 1 MHz; 20 MHz is for sensor data only.
la register reads cap at 1 MHz on this part; data reads may run to 20 MHz
fix config clock → ÷128 = 0.78 MHz, under the cap · sensor burst 12.5 MHz → rebuild
 
iter 2 flash ok · test running · t+21 min
who_am_i .................................. PASS 0x68
gyro_x .................................... FAIL reads 0x0000, always
internal logic analyzer · spi1 · iter 2
MOSI00111011bit 7addr byte = 0x3B
bit 7 = 0 → that's a write, not a read. The 0x80 read flag is missing.
la addr 0x3B sent without the 0x80 read bit
fix set the 0x80 read flag in the burst path → rebuild
 
iter 3 flash ok · test running · t+34 min
gyro_x .................................... data flows
rate check: world spins the table 90 °/s, ground truth · driver reports 11.3
fix driver set FS_SEL=3 (±2000 °/s) but scaled with the ±250 default: 131 → 16.4
 
iter 4 flash ok · full suite · t+43 min
who_am_i .................................. PASS
self test ................................. PASS
rate step 90 °/s .......................... PASS
burst read @ 8 kHz sustained .............. PASS
 
iter 5 improve: burst reads over DMA, get the CPU out of the read path · t+49 min
flash ok · test running
burst read @ 8 kHz ........................ FAIL transfer never starts
regs NDTR frozen: driver armed DMA1, but SPI1 requests only route to DMA2
fix retarget SPI1 DMA → DMA2 streams 0/3 · CR2 TXDMAEN|RXDMAEN → rebuild
 
iter 6 flash ok · test running · t+58 min
burst read @ 8 kHz ........................ FAIL stream armed, zero requests
regs stream 0 CHSEL=0. SPI1_RX is channel 3 on this stream
fix CHSEL → 3 on both streams → rebuild
 
iter 7 flash ok · test running · t+65 min
gyro_x .................................... data flows, shifted by one byte
la 14 bytes clocked per frame, 15 needed (addr + 14 data): NDTR missed the address byte
fix NDTR = 15: count the address byte, discard rx[0] → rebuild
 
iter 8 flash ok · full suite · t+70 min
who_am_i .................................. PASS
self test ................................. PASS
rate step 90 °/s .......................... PASS
burst read @ 8 kHz over DMA ............... PASS
cpu cycles moving gyro bytes .............. 0
 
result gyro driver implemented · DMA-accelerated · tested · 8 iterations · 1 h 12 min unattended
checker deterministic, fixed thresholds. The agent never grades its own work
task · implement and test the gyro driver replay · recreated · sped up · not live video
gyro driver implemented · DMA-accelerated · tested8 iterations1 h 12 min unattended
How it works

Every firmware PR gets its own bench and its own agent.

01
The agent builds, the world proves
One prompt covers implement and test: the agent writes the driver, flashes real silicon, and reads the world's verdicts.
02
Iteration is cheap when nobody has to watch it
Flash, run, read, fix, repeat until PASS.
03
Tests come from expected behavior
Written from what the part should do, never from the code under test: a pass means the part behaved, not that the code agreed with itself. They run on the bench, not in a mock.
04
Every test keeps running
Tests and thresholds are files in the test repo, reviewed like code, and stay in the suite, so regressions get caught on every future PR.
The world

Close your control loop on real-time physics emulation.

Motors spin, packs heat up, pressure builds, so your unmodified firmware reaches every test point on its own. No test hooks, no forced states, no special builds.

Motor control

inverter switching · saturation · current ripple

The FPGA and real-time MCU have the compute and latency to play your inverter and motor for real. Fast analog plays the current feedback your loop samples, so your fastest loop closes at full rate, at the real control point.

BMS

internal resistance · imbalance · thermal drift

A pack model built for your product, behind a cell monitor emulated at its digital interface. Protection logic trips by reaching the fault, and ship-mode drain is measured at 31 nA resolution.

Power and thermal

thermal mass · transport delay · converter ripple

A plant model built for your loop's dynamics. Loops settle, overshoot and recover against physics that pushes back, at their real update rate.

The hardware

Augur One

One board plays that world: your microcontroller sits on a small DUT board and plugs in; the sensors, loads, buses and faults come from the FPGA and live physics.

Evidence

What the bench does that a mock cannot.

Both ran on real silicon against a live emulated world, on Oracova development hardware. Verdicts come from fixed thresholds, not from the agent.

the simplest demonstration of oracova A flight simulator: unmodified Betaflight flies against the bench.
gyro + accel over SPI · 4 motor outputs decoded · ANGLE mode · 8 kHz loop

To the firmware, it is flying. Gyro and accel come from live physics; its motor commands move that world.

the bug a green build hid Zero edges on the output pin.
build green · DShot600 configured · static checks passed · output: zero edges

A DMA allocation failure every static check missed. Only the closed loop caught it.

Straight answers

Against the ways you test today.

swipe → manual bench · simulator · HIL rig
oracovamanual benchsimulatorHIL rig
who writes the teststhe agentyouyouyou
who scores the runsAI-free checkeryouyour assertsscripted checks
runs unattendedagent-drivennoyesscripted
a bench per PRyesnoyesno
runs on real siliconyesyesnoyes
faults on the chipby physicshand-wiredin a modelscripted
entry cost$2,000 a bench →engineer-hours"free"$9,900+

Running today on Oracova development hardware: STM32F411 under test, a BLDC + hall fault library, unmodified Betaflight in closed loop at 8 kHz. Augur One puts all of it on one board and is in fabrication.

Price your bench →

Why this exists

Credibility is the regressions caught before your customers ever meet them.

I was the manual bench. I shipped motor controllers and battery systems, and every change meant me, a board, and an afternoon. I missed one corner case, and it cost the company a fortune. So I built Oracova. If you know the pain, I want 15 minutes.

Pouya Mansouri, founder

engineer to engineer
Book 15 minutes
You'll watch a live run, not a deck.