---
title: Post-Silicon Validation Bench Automation
description: "Automate post-silicon validation on the bench: voltage and temperature corners, I/O timing, shmoo plots, register reads over JTAG or SWD, and repeatable data."
url: https://galoislabs.ai/blog/post-silicon-validation-bench
author: Alex Hernandez
author_url: https://galoislabs.ai/blog/authors/alex-hernandez
published: "2026-06-13"
topic: Industries
publisher: Galois Labs
---

# Post-silicon validation bench automation: corners, timing, shmoos and records

![A validation board seen from above with a clamshell test socket open and a chip seated in it.](https://galoislabs.ai/blog/figures/board-3.light.webp)

*FIG. 1 — DEVICE IN A TEST SOCKET*

Post-silicon validation bench automation is software that characterizes first silicon on a lab bench: SMUs step each supply rail, a thermal forcer holds the package at each temperature, scopes and pattern generators measure I/O timing, a debug probe reads on-chip registers, and the results land in shmoo plots with every point tied to its exact conditions.

The code in this guide uses PyVISA for a Keithley 2450 SMU, [PyMeasure's ThermoStream driver](https://pymeasure.readthedocs.io/en/latest/api/instruments/temptronic/temptronic_ats545.html) for the forcer, [pyOCD](https://github.com/pyocd/pyOCD) for registers, and NumPy with matplotlib for shmoo plots. Pre-silicon verification, the simulation work before tapeout, is out of scope.

## What is post-silicon validation?

Post-silicon validation answers a question simulation cannot: does this silicon meet its specification across the conditions it will see, and with how much margin? It sits between pre-silicon verification and production test, and production limits come from it.

| Stage                         | What runs                                               | Question it answers                                                       | What you can observe                         | Output                                           |
| ----------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------ |
| Pre-silicon verification      | RTL and gate-level simulation, emulation, formal checks | Does the design match its specification?                                  | Every internal signal                        | Coverage, bugs fixed before tapeout              |
| Post-silicon bench validation | First-silicon parts on evaluation boards                | Does the silicon meet specification across corners, and with what margin? | Pins, instruments, debug-reachable registers | Characterization data, errata, production limits |
| Production test               | Every shipped part on automatic test equipment          | Is this unit good?                                                        | What the test program measures               | A bin per part                                   |

Two properties make the bench stage hard. Observability is poor: the bench sees what the pins, instruments and debug registers expose, nothing more. The test space is large: every functional test multiplies across temperatures, voltages, frequencies, patterns and parts. Automation covers that space without losing track of which result came from which conditions.

## What equipment does a post-silicon validation bench need?

A characterization bench is built around an evaluation board, usually socketed, and instruments that each own one axis of the test.

| Role        | Instrument                           | Controls or measures                                    | Typical control path                     |
| ----------- | ------------------------------------ | ------------------------------------------------------- | ---------------------------------------- |
| Supplies    | SMU or precision supply per rail     | Rail voltage, current limit, supply and leakage current | SCPI over GPIB, LAN or USB               |
| Temperature | Thermal forcer or chamber            | Device temperature, soak                                | Remote commands over GPIB, LAN or serial |
| Stimulus    | Pattern generator, AWG, clock source | Input patterns, edge placement, clock frequency         | SCPI or a vendor SDK                     |
| Timing      | Oscilloscope, logic analyzer         | Edges, delays, rise and fall, jitter, eye               | SCPI plus waveform transfer              |
| Precision   | DMM                                  | Reference voltages, analog outputs                      | SCPI                                     |
| Chip access | JTAG or SWD probe, I2C or SPI bridge | Registers, on-die sensors, trims, chip ID               | OpenOCD, pyOCD, vendor libraries         |

SCPI basics, error queues and timeouts are in [SCPI instrument automation with Python](https://galoislabs.ai/blog/scpi-automation-python); this guide assumes them. Bringing up the evaluation board itself follows the [board bring-up checklist](https://galoislabs.ai/blog/board-bring-up-checklist).

## How do you run voltage and temperature corners on first silicon?

Characterization sweeps parts, ideally from process corner lots, across their rated voltage and temperature range. Three rules shape the loop.

**Put the slowest axis outermost.** A forcer takes minutes to move and settle; an SMU changes a rail in milliseconds. Settle temperature once, then sweep voltage, frequency and patterns inside it.

**Protect the part before the first point.** First silicon is scarce. Set each SMU's current limit before its output turns on, and check every requested voltage against the part's absolute maximum rating in software; the instrument does not know it.

**Measure what the part saw, not what you asked for.** Record the rail voltage read back at the board and the forcer's measured temperature beside every setpoint. With 4-wire sensing, the SMU regulates and reads the voltage where the sense leads land, so land them near the socket.

> **PVT means two things**
>
> In silicon, PVT is process, voltage and temperature, the axes of a corner sweep. In product development, PVT is production validation test, the last build before mass production. This guide uses the first sense. The second is covered in [EVT vs DVT vs PVT](https://galoislabs.ai/blog/evt-dvt-pvt-testing).

```python title="bench/corners.py"
import time
from contextlib import contextmanager

import pyvisa
from pymeasure.instruments.temptronic import ATS545

# name: (VISA resource, absolute maximum volts from the datasheet, current limit A, OVP level)
RAILS = {
    "vdd_core": ("TCPIP0::10.0.0.21::inst0::INSTR", 0.99, 0.5, "PROT2"),
    "vdd_io": ("TCPIP0::10.0.0.22::inst0::INSTR", 1.98, 0.1, "PROT5"),
}
NOMINAL_V = {"vdd_core": 0.80, "vdd_io": 1.80}
POWER_UP_ORDER = ("vdd_core", "vdd_io")  # from the design's sequencing requirement


def open_rails(rm: pyvisa.ResourceManager) -> dict:
    smus = {}
    for name, (resource, _abs_max, ilimit, ovp) in RAILS.items():
        smu = rm.open_resource(resource, read_termination="\n", write_termination="\n")
        for command in (
            "*RST",                      # also resets the current limit to 105 uA
            "SOUR:FUNC VOLT",
            "SOUR:VOLT 0",
            f"SOUR:VOLT:ILIM {ilimit}",  # current limit before the output ever turns on
            f"SOUR:VOLT:PROT {ovp}",     # hardware clamp if a sense lead opens
            'SENS:FUNC "CURR"',
            "SENS:CURR:RSEN ON",         # 4-wire: sense the voltage at the board
        ):
            smu.write(command)
        smus[name] = smu
    return smus


def set_rail(smus: dict, name: str, volts: float) -> None:
    abs_max = RAILS[name][1]
    if not 0.0 <= volts <= abs_max:
        raise ValueError(f"{name}: {volts} V is outside 0 to {abs_max} V")
    smus[name].write(f"SOUR:VOLT {volts}")


def read_rail(smu) -> tuple[float, float]:
    """One reading: measured source volts (readback) and measured amps."""
    volts, amps = smu.query_ascii_values('READ? "defbuffer1", SOUR, READ')
    return volts, amps


@contextmanager
def powered(smus: dict):
    """Rails up in order at nominal, down in reverse, on every exit path."""
    try:
        for name in POWER_UP_ORDER:
            set_rail(smus, name, NOMINAL_V[name])  # level first, then the output
            smus[name].write("OUTP ON")
            time.sleep(0.01)
        yield smus
    finally:
        for name in reversed(POWER_UP_ORDER):
            smus[name].write("OUTP OFF")


def run_corners(forcer: ATS545, smus: dict, temps_c, vdd_core_points, measure):
    """Yield one record per (temperature, voltage) point; temperature is the outer loop."""
    forcer.configure(temp_window=1, dut_type="T", soak_time=60)  # control on a T thermocouple
    forcer.start()
    try:
        for temp in temps_c:
            forcer.set_temperature(temp)
            forcer.wait_for_settling(time_limit=1800)
            if not forcer.at_temperature():  # wait_for_settling also returns on timeout
                raise RuntimeError(f"forcer did not settle at {temp} C")
            with powered(smus):  # power up at temperature: each corner is also a power-on test
                for volts in vdd_core_points:
                    set_rail(smus, "vdd_core", volts)
                    v_meas, i_meas = read_rail(smus["vdd_core"])
                    yield {
                        "t_set_c": temp,
                        "t_forcer_c": forcer.temperature,
                        "vdd_core_set_v": volts,
                        "vdd_core_meas_v": v_meas,
                        "idd_core_a": i_meas,
                        **measure(),  # functional test, register snapshot, timing
                    }
    finally:
        forcer.shutdown(head=False)


# forcer = ATS545("GPIB0::1::INSTR")
# smus = open_rails(pyvisa.ResourceManager())
# rows = list(run_corners(forcer, smus, (-40, 25, 125), (0.72, 0.80, 0.88), measure=my_test))
```

What the choices do:

- **The current limit goes in before `OUTP ON`.** After `*RST`, a [Keithley 2450](https://download.tek.com/manual/2450-901-01_D_May_2015_Ref.pdf) sources voltage with a 105 µA limit. The [Keithley SMU guide](https://galoislabs.ai/blog/keithley-smu-python) covers limits, sensing and status bits.
- **The software check and the clamp do different jobs.** If a sense lead opens, the SMU reads 0 V and raises its output; `SOUR:VOLT:PROT` clamps it, but its lowest level, 2 V, is above this example's 0.99 V core limit. `set_rail()` holds each request below the part's own rating.
- **The forcer controls on the device.** `dut_type="T"` puts the ThermoStream in DUT mode on a T-type thermocouple, and the [PyMeasure driver](https://pymeasure.readthedocs.io/en/latest/api/instruments/temptronic/temptronic_ats545.html) maps its properties to the forcer's `SETP`, `WNDW`, `SOAK`, `TEMP?` and `TECR?` commands. Its `wait_for_settling()` returns after the time limit even if the forcer never settled, so the code checks `at_temperature()` afterwards.
- **The generator cleans up.** If the loop raises or the generator is closed, the `finally` blocks turn the rails off in reverse order and stop the forcer; wrap it in `contextlib.closing()` if the caller may stop early.

At high-power corners the junction runs hotter than the package thermocouple, so read the on-die temperature sensor at every point and store it beside the forcer reading. Rail order and limits come from the design; the [power rail validation plan](https://galoislabs.ai/blog/power-rail-validation-plan) covers writing them down.

## How do you characterize I/O timing with a scope and pattern generator?

I/O characterization measures the datasheet's timing parameters on real parts: setup and hold times, clock-to-output delay, rise and fall times, duty cycle and jitter. High-speed serial links add eye height, eye width and bit error rate.

Setup and hold are found by search. The pattern generator drives clock and data, and its delay control steps the data edge relative to the clock. At each offset the bench checks whether the chip captured the data, by register readback, loopback or output compare. The smallest passing offsets on each side of the clock edge are the setup and hold times at that corner. That is a shmoo with timing on one axis, repeated at every voltage and temperature.

Four practices keep timing numbers trustworthy:

- **Deskew first.** Probes and cables add delay. Measure each channel's delay against a common edge and apply the correction before measuring any delay between channels.
- **Measure at the threshold the specification defines,** and set the scope's reference levels to match it rather than accepting defaults.
- **Use statistics, not one acquisition.** Report the mean, standard deviation and worst case over many edges; a single capture hides jitter.
- **Record the measurement setup with the data:** bandwidth limit, sample rate, probe model, reference levels and deskew values.

Run every timing measurement at every corner, not only the one expected to be worst; characterization exists to find out which corner is worst.

## How do you make a shmoo plot in Python?

A shmoo plot is a two-dimensional grid of results for one test, usually supply voltage on one axis and clock frequency or a timing offset on the other. The passing region's edge is the part's margin. Two choices matter: how many times each point runs, and whether the sweep stops early.

```python title="bench/shmoo.py"
import matplotlib.pyplot as plt
import numpy as np


def shmoo(run_point, volts, freqs_mhz, repeats=3, stop_after=3):
    """Pass fraction at each (voltage, frequency) point; NaN where a point did not run.

    run_point(v, f) returns True on pass. Within a row, the sweep stops after
    `stop_after` consecutive points with zero passes, which assumes a failing
    frequency keeps failing above it. Use stop_after=None to run the full grid.
    """
    grid = np.full((len(volts), len(freqs_mhz)), np.nan)
    for i, v in enumerate(volts):
        dead = 0
        for j, f in enumerate(freqs_mhz):
            grid[i, j] = sum(bool(run_point(v, f)) for _ in range(repeats)) / repeats
            dead = dead + 1 if grid[i, j] == 0 else 0
            if stop_after and dead >= stop_after:
                break
    return grid


def plot_shmoo(grid, volts, freqs_mhz, title):
    fig, ax = plt.subplots(figsize=(6, 4))
    mesh = ax.pcolormesh(
        freqs_mhz, volts, np.ma.masked_invalid(grid),
        cmap="gray", vmin=0, vmax=1, shading="nearest",
    )
    fig.colorbar(mesh, ax=ax, label="pass fraction")
    ax.set(xlabel="core clock (MHz)", ylabel="VDD_CORE (V)", title=title)
    return fig
```

**Repeat each point.** A point that passes twice and fails once is not a pass. The pass fraction shows a gray band along the boundary, and its width is information: a wide band points to noise, jitter or temperature; a sharp edge points to a hard timing limit.

**Stop early with care.** Skipping the rest of a row after repeated total failures saves time but assumes failures are monotonic in frequency, and it can hide a second passing region. Run the full grid on the first parts, then decide.

**Reset state between points.** A failure can leave the chip hung for the next point. Reset or power-cycle after a failure, and record it.

Make one shmoo per temperature and part, with identical axes and color scale, so they compare side by side.

## How do you read chip registers over JTAG or SWD?

On a validation bench the debug interface is an instrument. Through it a script reads the chip ID and revision, on-die sensors, PLL lock, status registers and trim or fuse values, and writes configuration such as clock dividers, drive strengths and test modes.

JTAG is the test access port defined by [IEEE 1149.1](https://standards.ieee.org/ieee/1149.1/4484/), which also covers boundary scan. Many Arm cores also offer Serial Wire Debug (SWD); the [OpenOCD manual](https://openocd.org/doc/html/About.html) notes that SWD supports only debugging, whereas JTAG also supports boundary scan. [OpenOCD](https://openocd.org/doc/html/General-Commands.html) reads and writes memory-mapped registers with `mdw` and `mww`. For Arm Cortex-M targets, pyOCD offers the same from Python; its [README](https://github.com/pyocd/pyOCD) lists CMSIS-DAP, J-Link, ST-Link and other probes, under the Apache-2.0 license.

```python title="bench/registers.py"
from pyocd.core.helpers import ConnectHelper

# Placeholder addresses: generate them from the chip's register description.
CHIP_ID = 0x5000_0000
TSENSE_RAW = 0x5000_0100
PLL_STATUS = 0x5000_0204
PLL_LOCKED = 1 << 0


def chip_snapshot(target) -> dict:
    """Identity and health registers, read at every point and stored with it."""
    return {
        "chip_id": f"0x{target.read32(CHIP_ID):08x}",
        "tsense_raw": target.read32(TSENSE_RAW),
        "pll_locked": bool(target.read32(PLL_STATUS) & PLL_LOCKED),
    }


def write_checked(target, addr: int, value: int, mask: int = 0xFFFF_FFFF) -> None:
    """Write, read back, and compare the bits the register map says are read/write."""
    target.write32(addr, value)
    readback = target.read32(addr)
    if (readback ^ value) & mask:
        raise RuntimeError(f"0x{addr:08x}: wrote 0x{value:08x}, read 0x{readback:08x}")


with ConnectHelper.session_with_chosen_probe(
    unique_id="E6616407E3646B29",  # the probe's serial, from `pyocd list`
    options={"frequency": 1_000_000, "target_override": "cortex_m"},  # 1 MHz clock, generic target
) as session:
    print(chip_snapshot(session.target))
```

Practices that save debugging time later:

- **Store the chip ID and revision with every row.** A respin or a mislabeled socket then shows up in the data.
- **Generate addresses from the design's register description.** A hand-copied address with a typo reads the wrong register without any error.
- **Start the debug clock slow** on a new board; `frequency` is the probe clock in hertz.
- **Reconnect after every power cycle,** and if the sweep moves the I/O rail, confirm the probe's signal levels follow it.

Many mixed-signal parts expose registers over I2C or SPI instead; the same rules apply.

## How much data does characterization produce?

More than a spreadsheet holds. A modest plan of 3 temperatures, 9 core voltages, 16 frequencies, 4 patterns and 3 repeats is 5,184 test executions per part, and 51,840 across 10 parts. At 2 seconds each, the 10 parts take about 29 hours of bench time before temperature transitions, part swaps or timing captures. Runs that long go unattended overnight, so the code has to fail safe and the records have to stand on their own.

Store one row per execution in a long-format table, written as Parquet or another columnar format, with waveforms and large captures saved as separate files referenced by ID. Each row carries its full context:

| Field            | Example                                                  | Why it matters                        |
| ---------------- | -------------------------------------------------------- | ------------------------------------- |
| Part identity    | Lot, wafer, die position, chip ID register               | Ties a result to one die              |
| Silicon revision | Revision register value                                  | Respins change behavior               |
| Board and socket | Board serial, socket ID                                  | Fixtures differ and wear              |
| Temperature      | Setpoint, forcer reading, on-die sensor                  | Self-heating separates them           |
| Rails            | Setpoint, measured voltage, measured current, per rail   | The readback, not the request         |
| Stimulus         | Frequency, pattern ID, random seed                       | Reproduces the exact point            |
| Result           | Pass count of repeats, raw measurement or file reference | Marginal points need counts           |
| Instruments      | Full `*IDN?` string per instrument                       | Model, serial and firmware per result |
| Code             | Script commit or sequence revision                       | Which logic produced the number       |
| Order and time   | Timestamp, point index, sweep direction                  | Drift and hysteresis show up here     |

## How do you keep post-silicon results reproducible?

A result is reproducible when another engineer, on another day, can rerun the point and get the same answer within the noise. Most failures come from conditions nobody recorded.

- **Freeze the sweep definition before the run.** Any change to points, limits or order makes a new revision, and every row records the revision it ran under.
- **Rerun a subset in reverse order.** If results depend on direction, something has memory: self-heating, forcer drift or leftover state.
- **Measure a reference part at the start and end of each session.** A shift on a known part is a bench problem.
- **Seed every random pattern** and store the seed.
- **Keep failures in the dataset.** Deleting a failing point to rerun a clean sweep erases evidence of an intermittent problem.

The broader practice of linking requirements, sequences, runs and results is covered in [hardware test traceability](https://galoislabs.ai/blog/hardware-test-traceability).

## How do you run this corner sweep in Galois with Évariste?

Évariste, the agent in the Galois platform, runs the same sweep on the same wiring through the galois-edge daemon. Open it from the app sidebar (Ctrl+Shift+E) beside the project.

**Instruments and drivers.** Ask "List connected instruments" to see the instruments on the team's edges and each profile's commands. Confirm the Keithley 2450's library profile covers the current limit (`SOUR:VOLT:ILIM`), overvoltage protection and 4-wire sense, or generate one from the [reference manual](https://download.tek.com/manual/2450-901-01_D_May_2015_Ref.pdf). For the ThermoStream, upload its programming manual as a PDF; check the profile Évariste generates against the PyMeasure driver's commands (`SETP`, `WNDW`, `SOAK`, `TEMP?`, `TECR?`) before it is deployed to the edge and bound to the forcer. Dangerous ad-hoc commands, such as setting a source voltage, wait for your confirmation.

**Prompt.** Objective and datasheet limits, in plain English:

> Create a corner sweep. ThermoStream on a T thermocouple, 1 °C window, 60 s soak, at -40, 25 and 125 °C, temperature outermost. Rails stay off while the forcer ramps: wait 30 minutes at each setpoint, then check the forcer reads within 1 °C. vdd_core: absolute maximum 0.99 V, current limit 0.5 A, overvoltage protection 2 V. vdd_io: 1.80 V, absolute maximum 1.98 V, 0.1 A, protection 5 V. 4-wire sensing; current limits before outputs turn on; vdd_core then vdd_io up at nominal (0.80 V core), down in reverse. At each temperature, step vdd_core through 0.72, 0.80 and 0.88 V and record readback voltage, current and forcer temperature.

**The draft.** Évariste builds the sequence from the profiles' named commands and saves it as a versioned YAML draft ([step types and the approval gate](https://galoislabs.ai/blog/ai-test-automation-hardware)), one step per point, so every setpoint is a line in the review. Readings are `measure` steps, which record without a verdict. Settling is a fixed 30-minute `wait`, then a `numeric_limit` check on the forcer at the setpoint ± 1 °C. Later steps still run when one fails, so a failed check flags that corner's readings rather than holding the rails off. One point:

```yaml title="corner_sweep.yaml (excerpt)"
name: "corner-sweep-vdd-core"
steps:
  # 25 C: setpoint, wait, forcer check, then both rails up in order
  - name: "25 C: vdd_core to 0.72 V"
    type: action
    config:
      instrument_id: "smu_core"
      command_name: "source_voltage"
      parameters: { value: "0.72" }

  - name: "25 C, 0.72 V: vdd_core readback"
    type: measure
    config:
      instrument_id: "smu_core"
      command_name: "measure_voltage"
      unit: "V"

  - name: "25 C, 0.72 V: idd_core"
    type: measure
    config:
      instrument_id: "smu_core"
      command_name: "measure_current"
      unit: "A"
```

**Review and approve.** The draft cannot run until an engineer approves it. Check it as [how to review an AI-generated test plan](https://galoislabs.ai/blog/review-ai-generated-test-plan) describes: every vdd_core setpoint below 0.99 V, since profile ranges guard the instrument, not the part; current limits before outputs; power order at every temperature; the wait and forcer check before the rails come up. Edit in conversation or in the sequence builder; each change is a new version with a diff, and an edit after approval needs approval again. Production-lock the sweep once it is frozen.

**Run, record, interpret.** Mount the part, land the sense leads near the socket and start the run; Monitor shows the channels live. Stopping a run partway skips its remaining steps, power-down included, so turn the outputs off after a stop. Each step records its measured value, limits, verdict, raw command and response, and instrument; the run carries the operator, the DUT serial and timestamps. Ask "Compare idd_core at 0.88 V across the three temperatures" or which checks failed; Évariste also compares runs across parts, citing runs and notes.

**Report.** "Generate a test report from the last run" produces a PDF or HTML report from a LaTeX template, editable in the report editor; add the part's lot, socket and thermocouple type there, then share it to Slack.

| Step               | Code path (this guide)                    | Galois with Évariste                          |
| ------------------ | ----------------------------------------- | --------------------------------------------- |
| Connect            | PyVISA resources, `ATS545`                | galois-edge; "List connected instruments"     |
| Forcer driver      | PyMeasure's ThermoStream driver           | Profile from the manual; review, deploy, bind |
| Define the sweep   | `RAILS`, `run_corners()` arguments        | Plain-English prompt with datasheet limits    |
| Protect the part   | `set_rail()`, limit before `OUTP ON`      | Setpoints and limit order checked in review   |
| Settle temperature | `wait_for_settling()`, `at_temperature()` | `wait` step, limit step on the forcer reading |
| Clean up           | `finally` blocks                          | Reverse power-down steps, checked in review   |
| Freeze the sweep   | Script commit                             | Versioned sequence, approval, production lock |
| Run                | Overnight script                          | galois-edge; live in Monitor                  |
| Records            | Row dictionaries, Parquet writer          | Per-step record with raw command and response |
| Report             | Your own script                           | Generated PDF or HTML report                  |

You no longer write or maintain the instrument setup, the corner loop with its settling and readback logic, the record writer or a report script. Stating the limits from the datasheet, reviewing and approving the draft, mounting the part and keeping first silicon safe stay your job.

## Where does Galois fit on a post-silicon bench?

Galois is agent-driven test engineering for hardware teams: agents generate tests and instrument drivers, run them on real benches through the open-source galois-edge daemon, and turn the results into reports and a shared engineering record.

- **One daemon for the instruments.** galois-edge is Apache-2.0 and runs on the bench PC or a Raspberry Pi. It [discovers instruments](https://docs.galoislabs.ai/guides/connecting-instruments/) on GPIB, USB, LAN, serial, Modbus and CAN, and a matched profile turns each into typed commands. Galois ships 573 instrument profiles across 135 manufacturers in its [instrument library](https://galoislabs.ai/instruments); instruments with a Python SDK instead of SCPI bind through a profile's `sdk:` block.
- **Limits in front of the part.** Profile parameters carry `min` and `max`, and the daemon validates each call's parameters against the profile. Agents reach the same commands as typed [MCP tools](https://galoislabs.ai/blog/mcp-lab-instruments), which reject out-of-range values before any SCPI reaches the instrument. The generic `send_scpi` tool bypasses profile validation, and who can reach either depends on the access path; [LLM instrument safety](https://galoislabs.ai/blog/llm-instrument-safety) covers how to scope both.
- **Ramps that survive a dropped client.** A profile can mark a setpoint command `requires_sweep`, the pattern the docs use for temperature controllers. The daemon then runs the ramp [on its own](https://docs.galoislabs.ai/guides/python-sdk/), polls its status, keeps going if a notebook or agent session drops, and on stop either holds or runs the profile's abort command.
- **Versioned sequences and per-step records.** [Galois sequences](https://galoislabs.ai/product) are versioned YAML. A sequence Évariste drafts cannot run until an engineer approves it, and each edit needs approval again. Each step stores the SCPI sent, the raw response, the measured value, its limits and the instrument; the run records the operator and the serial number. Production-locking an approved version keeps a sweep fixed across a characterization campaign.
- **Drivers and tests drafted by Évariste.** Évariste [generates profiles from programming manuals](https://galoislabs.ai/compare/labview); [the walkthrough above](https://galoislabs.ai/blog/post-silicon-validation-bench#how-do-you-run-this-corner-sweep-in-galois-with-évariste) drafts and runs this corner sweep.

Autonomous investigations, where an agent follows up a failing shmoo region with its own runs, are a direction for Galois, not a shipped feature. Pre-release silicon data is sensitive; the platform also runs as a dedicated single-tenant cloud or on-prem and air-gapped ([deployment](https://galoislabs.ai/deployment), [security](https://galoislabs.ai/security)).

If one engineer characterizes one part on one bench, the scripts in this guide and a disciplined record format are enough. The case for a platform grows with the number of benches, people and runs someone has to trust months later. To try the daemon on your own bench, start with the [quickstart](https://docs.galoislabs.ai/getting-started/quickstart/).

## Frequently asked questions

### What is the difference between pre-silicon and post-silicon validation?

Pre-silicon verification checks the design before tapeout with simulation, emulation and formal methods, where every internal signal is visible but the physics is a model. Post-silicon validation runs on real chips after first silicon returns, mounted on evaluation boards and driven by lab instruments. It measures what the models approximate: timing and power margins across process, voltage and temperature, analog behavior, and failures that need real speed and long run times to appear.

### What is a shmoo plot?

A shmoo plot is a grid of results for one test swept across two parameters, most often supply voltage against clock frequency or against an I/O timing offset. Each cell is one operating point, and the boundary between the passing and failing regions shows the margin. Running each point several times and plotting the pass fraction, rather than a single pass or fail, exposes marginal regions where results are intermittent.

### What does PVT mean in chip characterization?

Process, voltage and temperature: the three axes of variation a chip has to work across. Process corners come from deliberately skewed wafer lots, voltage from the rated supply range, and temperature from the rated operating range. It is unrelated to PVT the build stage (production validation test) in the EVT, DVT and PVT sequence used for products.

### How do you control chip temperature during bench characterization?

With a thermal forcer, which blows temperature-controlled air over the part, or with a chamber. Control on a thermocouple at the device rather than on the air temperature, wait for the reading to stay inside a window for a soak time, and record the chip's own on-die temperature sensor at every point, because self-heating at high-power corners pushes the junction above the setpoint.

### Can I run a post-silicon corner sweep without writing Python?

Yes. In Galois, Évariste generates the ThermoStream's profile from its PDF programming manual, and you review it before it is deployed to the edge and bound to the forcer. You then describe the sweep in plain English, with the temperature setpoints, the vdd_core steps and the datasheet limits for each Keithley 2450 rail, and Évariste drafts a versioned sequence from the instrument profiles. The draft cannot run until an engineer approves it. Once you start the run, galois-edge runs it on the bench: each step records its measured value and raw command and response, each check its limits and verdict, and Évariste compares the corners and generates the report when you ask.
