> ## Documentation Index
> Fetch the complete documentation index at: https://docs.veecle.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# run

> Run compiled firmware on a chip-accurate virtual board and read what it printed, with no hardware on your desk.

**You**

Chiplab executes your firmware on a chip-accurate virtual board and returns the UART output you asked to capture, no physical hardware required.
Cross-compilation happens on your side.
Chiplab runs the ELF as-is.
The simulation platform today is [Renode](https://renode.io), with [QEMU](https://www.qemu.org) and more planned.

## When to use it

Use a run when you want the printed output. When you need a pass or fail for each test, use [test](/tools/test).

## Ask your agent

```text theme={null}
Run this firmware on the STM32F4 Discovery and capture usart2.
```

> [!TIP]
>
> Not sure which board fits your firmware? Ask Chiplab about individual board specs; board recommendation tooling is on the way.

## What you get back

You get the captured UART output. `succeeded` means Chiplab completed the run. It does not mean your firmware behaved: a firmware that panics or faults still returns `succeeded`. Check the `uart` output for what you expect.

A run lasts up to 5 seconds of virtual time.

## Chip accuracy

Each virtual board runs your exact binary against a chip-accurate model of the target's peripherals, memory map, and interrupt controller.

> [!NOTE]
>
> Peripheral coverage keeps growing. If your firmware depends on a peripheral or timing behavior Chiplab doesn't model yet, ask Chiplab for the current coverage of your board.

## Knowledge corpus

Every run deposits observed behavior into Chiplab's [knowledge corpus](/introduction#knowledge-corpus). Your firmware code and binaries are not part of it.

**Agents**

The agent workflow is a short sequence: resolve a project, upload the ELF, start the run, wait for the job, then download artifacts.
Your agent's MCP client discovers the exact tool names and parameters live; the shapes below match the platform's current workflow.

Cross-compilation happens on your side.
Chiplab runs the ELF as-is.

## Project

A project holds the uploads and runs, so the next session can continue the same work. Everyone with access to the project sees it.
The id is server-minted. Your agent resolves it once, from context, from `list_projects`, or by calling `create_project`, and passes that same id on every later call.

* `list_projects` returns each project's `project_id`, `name`, and `description`.
* `create_project` takes a `name` and an optional `description`.
* `update_project` changes the `name` or the `description`. Omit either field to leave it unchanged. The id stays the same.
* `delete_project` removes that project for everyone who can access it.

## Upload

`issue_upload_ticket` takes the `project_id` and returns:

* `upload_url`: a presigned URL, valid for 10 minutes. Your agent `PUT`s the raw ELF to it. The URL needs no auth header.
* `artifact_id`: the identifier to pass to `run`.

The binary must be a compiled ELF for the target chip.
Use an `artifact_id` with the same `project_id` it was issued under.

## Starting a run

**project\_id** `string` *required*

The project this run belongs to, from `create_project`. Ids look like `project_...`.

**artifact\_id** `string` *required*

The firmware to run, from `issue_upload_ticket`.

**board** `object` *required*

The virtual board to boot. This is an object, not a string. `model` is one of the board slugs on [Boards](/hardware/boards). `uarts` is required: the list of UART names to capture, for example `["usart2"]`. Only those peripherals are written into the captured output.

`run` returns as soon as the simulation has started.

**run\_id** `string`

Identifier for this simulation run. Later calls refer to it as a `job_id`.

```json theme={null}
{
  "project_id": "project_01...",
  "artifact_id": "artifact_01...",
  "board": { "model": "stm32f4_discovery", "uarts": ["usart2"] }
}
```

```json theme={null}
{ "run_id": "run_01..." }
```

## Waiting for the job

UART output is ready once the job finishes.
Your agent calls `wait_for_job` with the `run_id` as `job_id`.

**job\_id** `string` *required*

The `run_id` returned by `run`, or a job id from an earlier listing.

**timeout** `number`

How many seconds to wait. The server caps this value.

**status** `string`

`running`, `succeeded`, or `failed`. `succeeded` means Chiplab completed the run. It does not mean your firmware behaved: a firmware that panics or faults still returns `succeeded`. Check the `uart` output for what you expect.

**input** `object`

The artifact and board the job was started with, so a run from an earlier session can be recognized without the call that started it.

**result** `object`

Present when `status` is `succeeded`. Includes an `artifacts` list naming the files the run produced, such as `uart`.

**error** `string`

Present when `status` is `failed`. What went wrong.

**timed\_out** `boolean`

True when `status` is still `running` because the wait ended before the job finished.

A response with `status: "running"` and `timed_out: true` means the run is still going.
`wait_for_job` is read-only, so calling it again on a finished run returns the same response immediately.

```json theme={null}
{
  "status": "succeeded",
  "result": { "artifacts": ["uart"] }
}
```

## Reading the output

To read an artifact, your agent calls `issue_download_ticket` with the `job_id` and an `artifact_name` from that `artifacts` list.
The response is a presigned `download_url`.
A `GET` of that URL returns the artifact bytes, with no auth header.

`uart` is the captured UART output from the peripherals named in `board.uarts`.

## Earlier runs

Earlier runs stay reachable in a later session.
`list_jobs` takes the `project_id` and optionally:

**kind** `string`

`run` to list only runs. Omit it to list every job, including test suite runs.

**created\_before** `string`

An RFC 3339 timestamp. Only jobs started before this time are returned. When `more_available` is true, pass the `created_at` of the last job in the page to continue.

Each entry has a `job_id`, a `status`, and a `created_at`, most recently started first.
Pass the `job_id` to `wait_for_job` to read what that run produced.

## Limits

A run lasts up to 5 seconds of virtual time.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.