Engineering February 22, 2026 25 min read

Hardware Agent - Bare Metal Ethernet UDP program on STM32F767ZI: 5 AI Models, 1 Board, Raw Register Access

We asked 5 frontier AI models to write bare-metal Ethernet UDP firmware for the Nucleo-F767ZI ; no HAL, no lwIP, no SDK. Raw register access to drive an LAN8742A PHY over RMII, build UDP packets from scratch, and handle ARP. In Compete Mode, only 2 of 4 compiled. Only 1 sent real packets on the wire. Then Sonnet 4.6 in Single Mode nailed it. Every binary flashed and verified on real silicon.

RespCode Team Engineering

Bare-metal Ethernet is where “hello world” goes to die. To get a single ping on the STM32F767ZI, you need to initialize the RMII PHY, configure MAC DMA descriptors with strict memory alignment, build Ethernet frames with CRC from raw bytes, and handle ARP request/reply cycles. Most engineers spend days debugging a silent wire. We asked frontier AI models to do it in under 60 seconds.

Hardware Validated: AI-Generated Ethernet UDP Firmware on Real Silicon
Nucleo-F767ZI board with Ethernet cable running AI-generated bare-metal UDP firmware via RespCode
AI-generated bare-metal Ethernet UDP firmware running on a physical Nucleo-F767ZI. Blue LED (PB7) active, Ethernet cable connected to LAN8742A PHY. Prompt → AI generates code → ARM cross-compilation → pyOCD flash → real UDP packets on the wire. No SDK, no HAL, no human edits.
2/4
Compiled
Compete Mode
1/4
Real UDP on Wire
Compete Mode
1/1
Real UDP on Wire
Single Mode
5
AI Models Tested
2
Working on HW
7,964B
Best Binary (Opus)
836
Lines of C (Opus)

The Challenge: Bare-Metal Ethernet Without a Safety Net

Bare-metal Ethernet on the STM32F767ZI requires getting at least 6 major subsystems correct simultaneously, all without any library, any HAL abstraction, or any example code to lean on:

This is not LED blink. This is not UART serial. This is the hardest single-prompt firmware generation test we’ve run on RespCode.

The Prompt

The Prompt
Write a bare metal Ethernet UDP program for STM32F767ZI.
The board has a LAN8742A PHY connected via RMII.
Configure static IP 192.168.0.200, send a UDP packet to
192.168.0.175 port 9000 every 2 seconds containing
"Hello from STM32F767ZI". Blink green LED on each packet sent.

One prompt. No hints about register addresses, no pin mapping, no clock configuration. The model must know ; or figure out ; every hardware detail from its training data, augmented by RespCode’s SVD register injection system.

The Agent Pipeline

This test was run on the RespCode Hardware Agent ; our autonomous firmware engineer. The agent takes a prompt, orchestrates AI models, compiles in cloud sandboxes, and in the case of the physical test, flashes to real hardware via pyOCD and captures UART output.

RespCode Autonomous Agent Pipeline
💬Prompt
Agent🧠SVD Inject
+ MCU Detect
🤖4 Models
Compete
🔨ARM
Compile
Flash via
pyOCD
📡UART
Verify
RespCode Agent: Compete Mode ; Ethernet UDP on Nucleo-F767ZI
RespCode Hardware Agent running Compete Mode with 4 AI models generating bare-metal Ethernet UDP firmware for STM32F767ZI
The full agent run: 4 models generate in parallel, 2 compile (Opus and Gemini), GPT-5.2 hits a linker error, DeepSeek fails with wrong GPIO typedef. Opus is flashed via pyOCD, UART captures real UDP packets, and the agent declares the winner ; all autonomous, no human intervention.

Compete Mode: 4 Models, 2 Compiled, 1 Works

We ran Claude Opus 4.6, GPT-5.2, Gemini 3.1 Pro, and DeepSeek Coder simultaneously in Compete Mode. Each model received the same prompt with identical SVD register injection. The results diverged dramatically.

ModelCompileBinarySource LinesFilesHardwareFailure Mode
Claude Opus 4.6 ✅ PASS 7,964 B 836 5 ✅ UDP on wire ;
Gemini 3.1 Pro ✅ PASS 2,152 B 285 5 ❌ Silent HSI 100 MHz, wrong TXD1 pin, no ARP
GPT-5.2 ⚠️ Compiled* 5,232 B 517 5 ❌ Silent TX pins on PB11/12 (wrong), no GPIOG clock
DeepSeek Coder ❌ Wrong MCU 1,004 B 219 3 ❌ Dead Generated NXP LPC55S69 code

* GPT-5.2 compiled but produced firmware that could never work on the Nucleo-F767ZI due to wrong TX pin mapping.

2/4
Compiled
for correct MCU
1/4
Working HW
UDP on wire
7,964B
Best Binary
Opus 4.6
836
Most Complete
Lines of C
1/4
Wrong MCU
DeepSeek

Deep Code Analysis: What Went Right and Wrong

🏆 Claude Opus 4.6 ; The Winner

Opus produced the most comprehensive Ethernet firmware of any model we’ve tested: 836 lines of C across 5 files (main.c, startup.c, stm32f767zi.h, linker.ld, plus compiled binaries). The code demonstrates deep hardware understanding at every layer.

Clock Configuration: Opus correctly configured the PLL chain from HSE 8 MHz to 216 MHz SYSCLK with PLLM=8, PLLN=432, PLLP=2, PLLQ=9 ; matching the reference manual exactly. It set the flash wait states for 216 MHz, configured AHB/APB prescalers (APB1=/4, APB2=/2), and initialized SysTick at 216,000 ticks per millisecond. Every parameter is correct.

RMII Pin Mapping: All 9 RMII pins are correct, including the Nucleo-F767ZI-specific routing of TX_EN to PG11, TXD0 to PG13, and TXD1 to PB13. This is the critical detail that other models got wrong ; the STM32F767ZI datasheet allows multiple pin options for TX, and only the Nucleo board schematic reveals which are actually connected.

Opus 4.6 ; RMII Pin Mapping (Correct)
/* PG11 - ETH_TX_EN (AF11) */
gpio_set_af(GPIOG, 11, 11);
/* PG13 - ETH_TXD0 (AF11) */
gpio_set_af(GPIOG, 13, 11);
/* PB13 - ETH_TXD1 (AF11) */
gpio_set_af(GPIOB, 13, 11);

MDIO Clock Divider: Opus used CR=0b100 (decimal 4, selecting HCLK/102) for the MDIO interface, producing an MDC clock of ~2.1 MHz from 216 MHz HCLK ; safely within the LAN8742A’s 2.5 MHz maximum. The PHY initialization includes soft reset, PHY ID verification, auto-negotiation with timeout, and link speed/duplex detection from the LAN8742A’s Special Control/Status Register.

Full Protocol Stack: Unlike any other model, Opus implemented a complete bidirectional Ethernet stack: ARP request generation, ARP reply handling (both responding to requests and processing replies to resolve the destination MAC), and UDP packet construction with proper IP header checksums. The DMA ring buffers use 4 TX and 4 RX descriptors, 32-byte aligned, with correct OWN bit management.

🏆 Opus 4.6: Real UDP Packets on the Wire

7,964 bytes of lean, working firmware. ARP resolution confirmed via UART debug output. UDP packets to 192.168.0.175:9000 every 2 seconds. Green LED (PB0) blinks on each successful send. First try, no human edits, no retry loop.

UART Output: Claude Opus 4.6 Ethernet UDP on Nucleo-F767ZI
UART serial output showing ARP resolution and UDP packet transmission from AI-generated STM32F767ZI firmware
Real UART capture from the Nucleo-F767ZI running Opus 4.6’s firmware. Clock at 216 MHz, PHY link up at 100 Mbps full-duplex, ARP resolution to 38:CA:84:45:87:99, then continuous UDP packets to 192.168.0.175:9000 every 2 seconds. Every line generated by AI-written bare-metal code ; no SDK, no HAL.

⚠️ Gemini 3.1 Pro ; Compiled, But Three Fatal Flaws

Gemini produced the smallest binary at 2,152 bytes and the most compact code at 285 lines. It compiled cleanly and demonstrates correct Ethernet MAC/DMA initialization structure. But three independent errors killed it on real hardware:

Flaw 1 ; HSI Instead of HSE: Gemini configured the PLL from the internal HSI oscillator at 16 MHz, targeting 100 MHz SYSCLK instead of the Nucleo’s 8 MHz HSE crystal targeting 216 MHz. The Ethernet MAC needs a 50 MHz REF_CLK from the PHY, and the MDIO clock divider was set for a 100 MHz HCLK range (CR=2). While this isn’t necessarily fatal, it deviates from the board’s intended configuration and may cause MDIO timing issues.

Flaw 2 ; Wrong TXD1 Pin: Gemini mapped ETH_RMII_TXD1 to PG14. On the Nucleo-F767ZI, TXD1 is routed to PB13. PG14 is a valid alternate function for TXD1 on the STM32F767 chip, but on the Nucleo board, it’s not connected to the PHY. This means TX data bit 1 never reaches the LAN8742A ; every transmitted frame is corrupted.

Flaw 3 ; No ARP, Broadcast Only: Instead of resolving the destination MAC via ARP, Gemini sends all UDP packets to the broadcast address FF:FF:FF:FF:FF:FF. While this can technically work on a local network, it means the firmware never handles incoming ARP requests and can’t participate in normal IP communication. Combined with the TX pin error, no packets could ever reach the wire.

❌ GPT-5.2 ; Compiled, But Wrong TX Pins

GPT-5.2 produced a capable TX-only Ethernet implementation with proper UDP checksum calculation ; a detail Opus also got right but Gemini skipped. The code includes ARP request generation and a state machine for MAC address resolution. But a critical pin mapping error makes it dead on arrival.

The Fatal Error: GPT mapped the three RMII TX pins to PB11 (TX_EN), PB12 (TXD0), and PB13 (TXD1). On the Nucleo-F767ZI, TX_EN is on PG11 and TXD0 is on PG13. Only TXD1 on PB13 is correct. Worse, GPT never enabled the GPIOG clock ; it only enabled GPIOA, GPIOB, and GPIOC. Even if the pins were remapped, GPIOG would be powered off.

GPT-5.2 ; RMII TX Pins (Wrong)
/* GPT-5.2 used generic STM32 pin mapping */
RMII_TX_EN  : PB11 (AF11)  /* ❌ Should be PG11 */
RMII_TXD0   : PB12 (AF11)  /* ❌ Should be PG13 */
RMII_TXD1   : PB13 (AF11)  /* ✅ Correct */

/* Missing: GPIOG clock not enabled */
RCC->AHB1ENR |= BIT(0) | BIT(1) | BIT(2);
/* Only GPIOA, GPIOB, GPIOC ; no BIT(6) for GPIOG */

This is a pattern we see repeatedly in AI-generated firmware: the model knows the chip’s alternate function table but not the board’s physical wiring. PB11/PB12 are valid AF11 options for ETH TX on the STM32F767, but the Nucleo-F767ZI routes those signals through PG11/PG13. Only board schematics ; or RespCode’s hardware database ; can disambiguate this.

❌ DeepSeek Coder ; Generated Code for the Wrong MCU

This is the most dramatic failure of the four. DeepSeek received a prompt explicitly requesting STM32F767ZI firmware with LAN8742A PHY over RMII. Instead, it generated NXP LPC55S69 code ; SYSCON registers at 0x40000000, IOCON pin mux, Flexcomm UART, and GPIO on port 1 pin 4. An entirely different ARM Cortex-M microcontroller family.

The code contains zero Ethernet functionality. No MAC, no DMA, no PHY, no ARP, no UDP. Instead, it outputs “UDP Packet #N: Hello from LPC55S69JBD100” over UART ; simulating UDP packets as serial text. The binary is only 1,004 bytes because there’s no Ethernet stack to compile.

💡 Why Did DeepSeek Generate LPC55S69 Code?

The metadata reveals the answer: DeepSeek was assigned to the lpc55s69-evk board target in the system, likely from a previous session configuration. The SVD injection provided LPC55S69 register data, and the model followed the hardware context over the prompt’s explicit STM32F767ZI reference. This highlights an important lesson: when system context and user prompt conflict, models follow the system context. We’ve since added validation to detect MCU mismatches between the prompt and the injection target.

Register-Level Correctness Analysis

The following table compares critical register configurations across the three models that targeted the correct MCU (Opus, Gemini, GPT). DeepSeek is excluded because it targeted a different chip entirely.

Register / ConfigCorrect ValueOpus 4.6Gemini 3.1GPT-5.2
RCC_BASE0x40023800
ETH_MAC_BASE0x40028000
ETH_DMA_BASE0x40029000
SYSCFG_BASE0x40013800
Clock SourceHSE 8 MHz✅ HSE❌ HSI 16 MHz❌ No PLL (16 MHz HSI)
SYSCLK Target216 MHz✅ 216 MHz❌ 100 MHz❌ 16 MHz (no config)
MDIO CR (clock div)4 (HCLK/102)✅ CR=4❌ CR=2 (100MHz range)✅ CR=5
ETH_RMII TX_ENPG11 AF11✅ PG11✅ PG11❌ PB11
ETH_RMII TXD0PG13 AF11✅ PG13✅ PG13❌ PB12
ETH_RMII TXD1PB13 AF11✅ PB13❌ PG14✅ PB13
GPIOG Clock EnableAHB1ENR bit 6❌ Missing
DMA Descriptor Align32-byte✅ aligned(32)✅ aligned(4)✅ aligned(4)
ARP HandlingFull req+reply✅ Full❌ Broadcast only✅ TX only
RX ProcessingPoll + handle✅ Full RX❌ None❌ TX only
LED Pin (Green)PB0✅ PB0✅ PB0✅ PB0

* GPT-5.2 omitted PLL configuration entirely. The STM32F7 always boots from the 16 MHz HSI oscillator on reset ; there is no “default” 216 MHz. Without explicit PLL setup, the firmware runs at 16 MHz, causing incorrect SysTick timing, wrong MDIO clock rates, and potentially broken Ethernet timing.

“Base addresses are the easy part ; SVD injection solves those. The hard part is board-specific pin routing, clock source selection, and peripheral initialization sequencing. That’s where models diverge and hardware fails silently.”

Single Mode: Sonnet 4.6 ; The Follow-Up That Worked

After the Compete Mode results, we ran the same prompt through Claude Sonnet 4.6 in Single Mode to see if a second Claude model could match Opus’s result. It did ; and in some ways exceeded it.

MetricClaude Opus 4.6Claude Sonnet 4.6
Binary Size7,964 B7,432 B
Source Lines836920
Header File226 lines323 lines (more complete)
Startup Vectors50 handlers220 handlers (full IRQ table)
Clock ConfigHSE → 216 MHzHSE → 216 MHz
RMII TX PinsPG11, PG13, PB13PG11, PG13, PB13
ARP HandlingFull req + replyFull req + reply
DMA Descriptors4 TX / 4 RX, aligned(32)4 TX / 4 RX, enhanced format
Memory LayoutDTCM + SRAM1DTCM (stack) + SRAM1 (DMA buffers + .data/.bss)
PHY Auto-Neg✅ With timeout✅ With PHYSCSR speed detection
Hardware Result✅ UDP on wire✅ UDP on wire

Sonnet’s code is 920 lines ; the most of any model ; with several production-quality touches. The startup file includes a 220-entry interrupt vector table covering every STM32F767 peripheral IRQ, compared to Opus’s 50 entries. The linker script correctly separates DTCM (for stack and CPU-exclusive variables) from SRAM1 (for .data, .bss, and DMA buffers). This distinction matters on the STM32F7: the DTCM at 0x20000000 is connected directly to the CPU’s TCM bus for zero-wait-state access, but the Ethernet DMA master operates on the AHB bus matrix and physically cannot access DTCM. DMA descriptors and packet buffers must live in SRAM1/SRAM2 (0x20020000+), which are AHB-accessible. Sonnet places DMA buffers in SRAM ; the correct architecture for DMA-safe Ethernet operation. The header file at 323 lines defines named constants for every PLL parameter, GPIO mode, and Ethernet register bit ; cleaner than raw magic numbers.

Sonnet also reads the LAN8742A’s PHY Special Control/Status Register (PHYSCSR) to detect actual link speed and duplex after auto-negotiation, then configures the MAC accordingly ; a detail Opus hardcodes as 100 Mbps full-duplex.

🏆 Sonnet 4.6: The Most Production-Ready Firmware

7,432 bytes, 920 lines, working UDP on the wire. Full interrupt vector table, correct DTCM/SRAM memory partitioning with DMA buffers in AHB-accessible SRAM, PHY speed auto-detection, and the cleanest header file of any model. If Opus is the engineer who ships, Sonnet is the one who ships and writes the documentation.

Code Architecture Comparison

FeatureOpus 4.6Sonnet 4.6GPT-5.2Gemini 3.1DeepSeek
Register styleDirect #define volatiletypedef struct + named constantstypedef structtypedef structLPC55S69 regs
Ethernet TXRing buffer, 4 descRing buffer, 4 descRing buffer, 4 descSingle descriptorNone
Ethernet RXFull pollingFull pollingTX onlyNoneNone
ARPRequest + Reply + ProcessRequest + Reply + ProcessRequest onlyBroadcast (no ARP)None
UDP ChecksumN/A
UART DebugUSART3 @ PD8/PD9USART3 @ PD8/PD9❌ NoneUSART3LPC UART
Linker: Memory RegionsFLASH + DTCM + SRAM1FLASH + DTCM + SRAMFLASH + RAMFLASH + RAMFLASH + RAM

The Claude models (Opus and Sonnet) stand out on protocol completeness: both implement bidirectional ARP handling, which is essential for real-world Ethernet. GPT-5.2 had the right idea with ARP request generation but couldn’t receive replies because it had no RX processing. Gemini took a shortcut with broadcast-only UDP, which avoids ARP entirely but breaks normal IP networking.

The RespCode Hardware Agent: What Made This Possible

This test was run entirely by the RespCode Hardware Agent ; an autonomous CLI tool that orchestrates the entire firmware development lifecycle. Here’s what the agent handles automatically:

respcode-agent ; Ethernet UDP Demo
Configured targets: (1) Local Machine [bare-metal · arm64 · pyOCD] (2) NUCLEO-F767ZI [bare-metal · arm32 · pyOCD · stm32f767zi] > 2 Board: NUCLEO-F767ZI (STM32F767ZI) Flash: pyocd → stm32f767zi @ 0x08000000 UART: /dev/ttyACM0 @ 115200 baud > Write a bare metal Ethernet UDP program for STM32F767ZI... [generate] Claude Opus 4.6 ; 836 lines, 5 files [compile] arm-none-eabi-gcc ; 7,964 bytes [flash] pyOCD → stm32f767zi @ 0x08000000 [verify] UART capture: [OK] System clock: 216 MHz [OK] Ethernet GPIO configured (RMII) [OK] PHY soft reset complete [OK] PHY ID: 0007C130 (LAN8742A) [OK] Link up: 100 Mbps Full Duplex [OK] ARP resolved: 192.168.0.175 → c8:3a:35:xx:xx:xx [TX] UDP → 192.168.0.175:9000 "Hello from STM32F767ZI" [TX] UDP → 192.168.0.175:9000 "Hello from STM32F767ZI"

The entire process ; from prompt to verified UDP packets on the wire ; takes under 60 seconds. No manual editing, no copy-pasting code, no debugging register addresses. The agent handles MCU detection, SVD injection, compilation, flashing, and UART verification autonomously.

📂 Full Source Code & Binaries

Every file from all 5 models is published unmodified: source code, headers, linker scripts, and compiled firmware binaries. Inspect the register addresses yourself, disassemble the ELFs, or flash them to your own Nucleo-F767ZI.

github.com/RespCodeAI/stm32f767-ethernet-ai-firmware →

Try It Yourself

Open RespCode, select ARM32 Baremetal, choose Compete Mode, and try:

Write a bare metal Ethernet UDP program for STM32F767ZI.
The board has a LAN8742A PHY connected via RMII.

Or install the agent and flash directly to your Nucleo board:

curl -fsSL https://respcode.com/agent/install.sh | bash
respcode-agent

Generate Real Firmware with AI

Multi-model code generation with SVD register injection and autonomous hardware verification

Get 500 Free Credits

No credit card required • 2,914 MCUs supported • 11 AI models