# Xtensa chips

Install Espressif's Rust fork for the ESP32, S2 and S3, and build Xtensa projects with espx or cargo.

The ESP32, ESP32-S2 and ESP32-S3 use Xtensa cores. Stable Rust has no Xtensa backend, so these chips need Espressif's fork of the Rust compiler, which rustup cannot install by itself. espx installs it through espup, only when you ask for an Xtensa chip.

| Chip      | Rust target               |
| --------- | ------------------------- |
| `esp32`   | `xtensa-esp32-none-elf`   |
| `esp32s2` | `xtensa-esp32s2-none-elf` |
| `esp32s3` | `xtensa-esp32s3-none-elf` |

## Install the fork

```sh
espx setup --chip esp32s3
espx setup --chip esp32,esp32s2,esp32s3
```

When the chip list holds an Xtensa chip, `setup` also downloads espup 0.17.1 into `~/.espx/bin` and runs:

```sh
espup install --targets esp32s3 --export-file ~/.espx/export-esp.sh
```

espup installs the fork as a rustup toolchain named `esp`, with an Xtensa-capable clang and GCC linker, about 1 GB in all:

| What                                                      | Where                      |
| --------------------------------------------------------- | -------------------------- |
| The `esp` toolchain                                       | `~/.rustup/toolchains/esp` |
| espup's own files                                         | `~/.espup`                 |
| The export file (`LIBCLANG_PATH` and `PATH` for the fork) | `~/.espx/export-esp.sh`    |

`setup` skips espup when the `esp` toolchain and the export file both exist. Run `espx doctor` to see them: espup, the `esp` toolchain and the export file are optional rows, dim when absent, never a failure.

## Create and build

```sh
espx new radar --chip esp32s3
cd radar
espx build
```

esp-generate writes `channel = "esp"` in the project's `rust-toolchain.toml`. espx sources `~/.espx/export-esp.sh` before every cargo command it runs, so `espx build`, `flash` and `run` work with no change to your shell.

If the `esp` toolchain is missing, `espx new` for an Xtensa chip and any build of a project on the `esp` channel stop before cargo runs:

```text
error: the esp32s3 needs the `esp` toolchain (Espressif's Rust fork for Xtensa chips) and rustup has no toolchain named `esp`. Run `espx setup --chip esp32s3`.
```

rustup's own error for a missing toolchain does not say how to get it, which is why espx checks first.

## Plain cargo

To run `cargo build --release` or `cargo run --release` outside espx, load the fork's environment into your shell:

```sh
eval "$(espx env)"
```

`espx env` prints the `PATH` line for `~/.espx/bin` followed by the lines of the export file. `espx setup --shell` adds `eval "$(espx env)"` to `~/.zshrc` or `~/.bashrc` so every new shell has it.

> **Note**
>
> cargo sets `RUSTUP_TOOLCHAIN` for every process it starts, and that variable overrides a project's `rust-toolchain.toml`. espx removes it from the environment it gives cargo and rustup, so an Xtensa project builds with the `esp` toolchain even when espx itself was started from inside cargo.

## On the board

Two things differ from the RISC-V chips when you monitor an Xtensa board:

- The original ESP32 prints an older ROM banner (`ets …`) whose reset codes mean different things. espx reads the banner and names the boot reason correctly for either family.
- A CPU exception's backtrace on Xtensa starts inside the exception handler before it reaches the faulting function. The `exception` event's first frame is the faulting instruction either way.

The ESP32-S3 DevKitC-1 has two USB connectors. The one labelled USB is the chip's native USB; the other goes through a USB serial bridge. If the bridge is a WCH chip on macOS without WCH's driver, flashing fails; see [Driver warnings](https://www.espx.dev/docs/cli/boards#driver-warnings).

## Remove the fork

```sh
espx uninstall --xtensa
```

This runs `espup uninstall` and removes `~/.espup` and the export file. The RISC-V setup stays.

## Related

- [Installation](https://www.espx.dev/docs/installation)
- [Chips and boards](https://www.espx.dev/docs/reference/chips)
