Skip to content

platform: integrate FreeRTOS+TCP networking on Zynq-A9 QEMU#70

Merged
zevorn merged 8 commits intomainfrom
feat/zynq-network
Mar 18, 2026
Merged

platform: integrate FreeRTOS+TCP networking on Zynq-A9 QEMU#70
zevorn merged 8 commits intomainfrom
feat/zynq-network

Conversation

@zevorn
Copy link
Copy Markdown
Owner

@zevorn zevorn commented Mar 18, 2026

Summary

Integrate FreeRTOS+TCP networking stack with Xilinx Cadence GEM driver on the Zynq-A9 QEMU platform. Full network stack from DHCP to HTTP verified end-to-end.

Verified: End-to-End HTTP Communication

[test] testing HTTP POST to AI API...
[test] HTTP status=401 len=130
[test] response: {"type":"error","error":{"type":"authentication_error",
  "message":"invalid x-api-key"}}

HTTP POST to Anthropic API via api-proxy.py returns valid JSON response (401 = test key, expected). Full path: FreeRTOS+TCP → GEM DMA TX → QEMU SLIRP → Host proxy → Anthropic API → response back through the same path.

Issues Fixed (7 commits)

Issue Root Cause Fix
PHY autoneg hangs QEMU MDIO too slow Skip Phy_Setup, force 100Mbps
Tick never fires vApplicationFPUSafeIRQHandler double-reads GICC_IAR Use port-provided ulICCIAR directly
DHCP can't allocate buffers 32 RX DMA descriptors exhaust all 32 buffers Increase to 60 buffers
Data Abort in ARP cache Unaligned struct access + QEMU strict alignment -mno-unaligned-access flag
RX/TX handlers never called GEM ISR is read-clear; BSP reads it twice (2nd = 0) Override XEmacPs_IntrHandler, read once
No DHCP response PHY link wait blocks 7s per attempt Force ulPHYLinkStates = LINK_UP
claw_net stub No HTTP client for standalone FreeRTOS Full FreeRTOS+TCP socket implementation

Components

  • FreeRTOS+TCP submodule (vendor/lib/freertos-plus-tcp)
  • Xilinx EMAC PS driver (vendor/bsp/xilinx/drivers/emacps/)
  • Patched NetworkInterface.c (platform/zynq-a9/drivers/) — QEMU-specific ISR, PHY, link fixes
  • HTTP client (osal/freertos/claw_net_freertos.c) — FreeRTOS+TCP socket API

Build & Run

make build-zynq-a9-qemu
# Terminal 1: start proxy
python3 scripts/api-proxy.py https://api.anthropic.com 8888
# Terminal 2: run QEMU
RTCLAW_AI_API_URL="http://10.0.2.2:8888/v1/messages" make run-zynq-a9-qemu

Test Plan

  • Compile (117/117 targets)
  • DHCP: IP 10.0.2.15 obtained from QEMU SLIRP
  • TCP connect to host proxy (10.0.2.2:8888)
  • HTTP POST with JSON body → 401 response with valid JSON
  • make test-smoke-zynq boot test passes
  • AI conversation with real API key

zevorn added 8 commits March 18, 2026 15:20
Add FreeRTOS+TCP networking stack and Xilinx EMAC PS driver to the
Zynq-A9 QEMU platform.  The TCP/IP stack initializes and attempts
DHCP negotiation via the QEMU-emulated Cadence GEM ethernet MAC.

Components added:
- FreeRTOS+TCP as git submodule (vendor/lib/freertos-plus-tcp)
- Xilinx EMAC PS driver (vendor/bsp/xilinx/drivers/emacps)
- FreeRTOSIPConfig.h with DHCP, DNS, TCP/UDP configuration
- xparameters_ps.h with GEM register addresses and SLCR dividers
- Network initialization in main.c (FreeRTOS_IPInit_Multi)
- Application hooks: random number, hostname, network event

Current status: TCP/IP stack initializes, GEM driver loads, DHCP
socket opens.  PHY link negotiation returns speed 0 on QEMU — needs
further investigation for full DHCP/HTTP connectivity.

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
- Force 100 Mbps link speed (ipconfigNIC_LINKSPEED100) since QEMU
  Cadence GEM does not emulate PHY autonegotiation
- Set configEMAC_TASK_STACK_SIZE for the GEM handler task

Note: DMA-level packet TX/RX is not yet functional on QEMU.  The
Cadence GEM DMA buffer descriptor ring requires further debugging
to achieve DHCP and HTTP connectivity.

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
QEMU's Cadence GEM emulation makes MDIO PHY operations extremely
slow (each register read/write takes many QEMU cycles).  The full
Phy_Setup sequence took longer than DHCP timeouts, preventing any
network activity.

Changes:
- Copy Zynq NetworkInterface.c to platform/zynq-a9/drivers/ to
  avoid modifying the FreeRTOS+TCP submodule
- Skip Phy_Setup entirely, force 100 Mbps operating speed
- Force ulPHYLinkStates = LINK_UP, skip prvGMACWaitLS (7s wait)
- Enable FreeRTOS+TCP debug printf for network diagnostics
- Reduce uncached memory from 1MB to 64KB (QEMU has no cache)

Current status: GEM driver initializes, EMAC handler task starts,
DHCP socket opens and timer schedules.  DHCP discover TX is the
next step to debug — DMA buffer descriptors may need further
tuning for QEMU's GEM DMA model.

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
FreeRTOS ARM_CA9 port reads GICC_IAR (acknowledge) before calling
vApplicationFPUSafeIRQHandler(ulICCIAR).  The handler was ignoring
the passed IRQ ID and calling XScuGic_InterruptHandler which reads
GICC_IAR again, getting 0x3FF (spurious) every time.

Fix: use the ulICCIAR value from the port to look up and call the
registered handler directly from the XScuGic handler table.

Result: tick interrupt (IRQ 29, SCU Timer) now fires correctly.
xTickCount advances, DHCP timer triggers, DHCP Discover attempts
begin.  Remaining issue: DMA TX send fails ("Send failed during
eWaitingSendFirstDiscover").

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
Two fixes:
1. Increase ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS from 32 to 60.
   The Zynq GEM DMA uses 32 descriptors for RX, leaving 0 for TX
   and DHCP.  With 60, there are 28 buffers available for TX/DHCP.

2. Fix GIC IRQ dispatch (previous commit) enabled the tick interrupt,
   allowing FreeRTOS timers to fire.  DHCP state machine now runs
   and successfully calls prvSendDHCPDiscover.

Current status:
- FreeRTOS tick works (xTickCount advances)
- DHCP Discover message constructed and sent via xNetworkInterfaceOutput
- No DHCP Offer received — DMA TX may not be delivering frames to
  QEMU SLIRP, or DMA RX may not be receiving replies

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
Data Abort (alignment fault) occurred in eARPGetCacheEntry when
accessing packed network structures at non-aligned addresses.
FreeRTOS+TCP uses 2-byte packet filler (ipconfigPACKET_FILLER_SIZE)
causing misaligned struct field accesses.

Root cause: QEMU enforces strict alignment even with SCTLR.A=0.
Fix: add -mno-unaligned-access to cross.ini and FreeRTOS+TCP build,
making GCC generate byte-access sequences for unaligned data.

Also:
- Clear SCTLR.A bit in startup.S (defense in depth)
- Add Data Abort handler with DFAR/DFSR diagnostic printf
- GEM TX debug output confirms DHCP Discover packets (309 bytes)
  and ARP packets (42 bytes) are being sent

DHCP Discover is now transmitted but no Offer received yet —
DMA-to-QEMU-SLIRP data path under investigation.

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
Root cause: Xilinx BSP's XEmacPs_IntrHandler reads the GEM ISR
register twice — once as RegISR and once as RegQiISR[0] (which maps
to the same offset 0x24 for Queue 0).  Cadence GEM ISR is read-clear,
so the second read always returns 0.  This prevented emacps_recv_handler
and emacps_send_handler from ever being called.

Fix: override XEmacPs_IntrHandler in the platform's NetworkInterface.c
with a version that reads ISR only once and dispatches based on that
single read value.

Result: Full DHCP cycle works on QEMU Zynq-A9:
  - DHCP Discover sent via GEM DMA
  - DHCP Offer received from QEMU SLIRP
  - DHCP Request/ACK completed
  - IP address 10.0.2.15 obtained

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
Replace the claw_net_freertos.c stub with a full HTTP POST/GET
implementation using FreeRTOS+TCP socket API:
  - FreeRTOS_socket/connect/send/recv/closesocket
  - FreeRTOS_gethostbyname for DNS resolution
  - Manual HTTP/1.1 request construction and response parsing
  - 60-second timeout on send/recv operations

The implementation mirrors the RT-Thread POSIX socket version
(claw_net_rtthread.c) with FreeRTOS+TCP API equivalents.

No TLS support — use scripts/api-proxy.py for HTTPS termination,
same as all other QEMU platforms.

Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
@zevorn zevorn force-pushed the feat/zynq-network branch from db6b382 to ffeb987 Compare March 18, 2026 07:21
@zevorn zevorn merged commit 814783b into main Mar 18, 2026
8 checks passed
@zevorn zevorn deleted the feat/zynq-network branch March 19, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant