← julian lupu

Gemini Stimdeck

geminicomplex.com

An FPGA-based miniaturized ATE for integrated circuits, taken from conception to working product. It drives and samples logic on a 400-channel test bus to functionally test any attached device. A 12-layer triple-FPGA motherboard, a custom vector processing unit with its own ISA, a Linux board support package, and a compiler for the test pattern language. Hardware, RTL and software, solo.

Specs

Test bus I/O 400 digital channels
Vector execution speed 50 Mbps per channel
Per-channel vector burst depth 67M vectors
Latency sub-10 ns
The assembled Gemini Tester Mk.I board
Gemini Tester Mk.I, assembled.

Why

The vector engine runs on FPGA fabric with its own DDR3. No CPU is in the path from pattern to pins, so the interval between one vector and the next is set by the fabric clock rather than by software.

400 channels at 50 Mbps each, a 50 MHz vector rate, sub-10 nanosecond latency, and 67M vectors of burst depth per channel. Patterns are written in Dots, compiled, and queued from a web app that reports pass or fail per pin.

Architecture

Three FPGAs. A Zynq-7000 SoC called Agent orchestrates the boot process, services the test queue and serves the web app. Two Artix-7s, Castor and Pollux, each run a vector processing unit against their own 8GB of DDR3 tester memory. The board runs VecOS, an operating system built to do nothing but execute test patterns.

VecOS is a board support package I built up from parts: a custom Linux kernel and device tree, a root filesystem and init scripts, a U-Boot second-stage bootloader, and a set of cross-compiled binaries. There is no BIOS underneath it.

Work arrives over NFS as compiled stim binaries and prgm files, queued from the web app. Agent runs queued programs as slots open, then records pass or fail for every execution in an on-board SQLite database.

WEB APP / NFS AGENT CASTOR POLLUX 400-PIN TEST BUS DUT MEZZANINE Zynq-7000 SoC VecOS, boot, queue, results db Artix-7 GVPU 8 GB DDR3 Artix-7 GVPU 8 GB DDR3
Mechanical drawing of the Gemini Stimdeck with dimensions
Mechanical drawing. 180mm square, with Castor and Pollux and the JT4 and JT5 mezzanine connectors.

The vector processing unit

The GVPU executes a custom instruction set where one instruction is one 1024-bit word, streamed out of Artix DDR3 over AXI-Stream into an eight-deep burst FIFO. Every word carries an opcode, an operand, and the state of 200 test bus pins at once. Castor and Pollux run one GVPU each and drive half the bus apiece, which is where the 400 channels come from.

Instruction word

OP OPERAND VECTOR 1023:1016 1015:800 799:0 8 bits 216 bits 800 bits pin 199 pin 198 pin 197 . . . pin 1 pin 0 4 bits 4 bits 4 bits 4 bits 4 bits 200 pins, one subvector each

Opcodes

Opcode Value Action
VEC 0x01 Execute one vector
VECLOOP 0x02 Execute, then repeat it operand times
VECCLK 0x03 Drive a clock pin through a low then high phase
NOP 0xff Burn a cycle

Every pin slot is one of six codes in four bits, and stimulus and comparison live in the same word, so a single beat both drives the device and checks what came back. There is no separate compare pass.

Subvectors

Subvec Value Meaning
0 0000 Drive low
1 0001 Drive high
X 0010 Don't care
H 0011 Expect high
L 0100 Expect low
C 0101 Clock

A repeating vector holds its word in the pipeline and counts down the 216-bit operand locally, so a pattern that idles or clocks for thousands of cycles costs one fetch instead of thousands. Memory bandwidth stops limiting how long a test can run, and no starved cycle reaches the test bus as jitter.

On a mismatch the engine sets a bit in a 200-wide fail mask, one bit per pin, so a failure reports which pins mismatched rather than just that the test failed. A thirteen-state machine sequences it: memory burst, load and run, then test setup, run, fail reporting and cleanup.

A second mode tests the board's own memory. It writes a pattern into Artix DDR3, reads it back and CRCs it in flight: sixteen CRC64 engines run in parallel, one per 64-bit chunk of every 1024-bit beat, so a burst is checked at full width without slowing the stream. The computed CRC goes back to memory, and Linux compares it against a known value. The path runs from userspace through DMA, across the Zynq to the Artix, into DDR3.

Schematic view of the Gemini Vector Processing Unit
The Gemini Vector Processing Unit.
The GVPU design zoomed out
The same design, zoomed out.

End-to-end verification

The GVPU and DUT RTL run in Verilator behind a C++ shim that the unmodified kernel driver binds to. Driver ioctl and DMA calls resolve to direct AXI bus calls instead of silicon.

Regression then runs the whole path with no board on the bench: userspace application, through the library, through the driver, into the vector processing unit, out to the device under test.

The boards

A 12-layer triple-FPGA motherboard and an 8-layer single-FPGA device-under-test validation board, both in Altium: board architecture, component selection, schematic capture and layout. Fine-pitch BGAs with high pin counts, DDR3 fly-by routing, impedance-controlled traces calculated against stack-ups from the board house. Power design and sequencing across three FPGAs, and interfaces to bring up for each: Gigabit Ethernet, USB 2.0, JTAG, UART, I2C, SD and SPI. Manufactured by Sierra Circuits.

A 3D render of the Gemini Stimdeck board
3D render, before anything physical existed.
Beginning the PCB layout in Altium
Starting the layout.
Component placement in progress
Component placement.
Routing traces across twelve layers
Routing. Twelve layers.
The board with its major blocks annotated
The same board, annotated.

Bring-up

Debug and rework on a DMM, an oscilloscope, and a stereo microscope with a microsoldering iron.

Bare fabricated boards before assembly
Bare boards back from fab, ready for assembly.
Reels of components boxed for the assembly house
Reels of parts, shipping out for assembly.
The assembled board powered on for the first time
First power-on.
Microsoldering rework under a stereo microscope
Rework under the microscope.

Software

Test patterns are written in Dots. A Pins line names the pins and fixes their order, and every vector after it is one character per pin, in that order.

Pins RESET_B, IN, OUT

# release reset, then shift a one through
V0XX
V11L
V10H

# hold it for ten cycles
repeat 10 V10H

Those characters are the six subvector codes the hardware executes, so a Dots file is close to a literal picture of what lands on the test bus. repeat compiles to a VECLOOP with its count in the operand, which is how a thousand idle cycles cost one word of memory instead of a thousand.

Leda compiles Dots into stim binaries. How those run is a separate file, a prgm, written in Fe, a small Lisp dialect with an API for loading patterns into tester memory and executing them.

(set-profile "board_profile.json")
(run
  (load "file1.stim")
  (load "file2.stim")
  (load "file3.stim"))

run stops at the first failing pattern and returns how many ran, whether one failed, and the cycle it failed on. runc runs everything regardless. get-fail-pins returns which pins mismatched, which is the 200-bit mask coming up from the engine.

Source code

Hardware documents

Mezzanine boards

The Gemini DUT Rev 1 mezzanine board
Gemini DUT Rev 1. Single-DUT mezzanine for characterizing the tester.
The Simstim DVT Rev 1 expansion module
Simstim DVT Rev 1. Expansion module adding HDMI, UART and a second SD slot.
The Stimpac Test MK2 Rev 1 prototype board
Stimpac Test MK2 Rev 1. Prototype for a buffered channel design with VOH/VOL.

Using it

A DUT board mates to JT4 and JT5 and breaks the test bus out to whatever you are testing. You write Dots against the pins on that board, compile it with Leda, and queue a prgm from the web app. Agent runs it and returns pass or fail, the cycle it stopped on, and which pins mismatched.