blob: 3037d6db3ab8c61a0fa4ac50a208ecd05193ab40 [file] [view]
# OpenTitan Earlgrey Integration Tests
This directory contains integration tests for the OpenTitan Earlgrey target, running on the Pigweed Maize microkernel.
## Test Directory Structure
Each subdirectory here represents a test suite or a specific test target:
* **[`drivers/gpio`](drivers/gpio)**: Tests the GPIO driver functionality, verifying that pins can be configured, read, written, and toggled.
* **[`eflash`](eflash)**: Tests the embedded flash driver operations (erase, program, read) using a secure `flash_server` userspace process.
* **[`ipc/user`](ipc/user)**: Tests userspace IPC channel communication between different processes on the microkernel.
* **[`logging`](logging)**: Tests the microkernel logging subsystems and `pw_log` routing.
* **[`threads/kernel`](threads/kernel)**: Tests kernel-level threading, context switching, and scheduling invariants.
* **[`uart`](uart)**: Tests UART driver initialization and loopback communication (using interrupt-driven RX/TX).
* **[`unittest_runner`](unittest_runner)**: Runs the upstream Pigweed kernel unit and integration test suite on the target hardware.
* **[`usbdev`](usbdev)**: Tests basic USB device controller initialization and host-side enumeration.
* **[`usbdfu`](usbdfu)**: Tests USB Device Firmware Upgrade (DFU) protocol, including loopback download/upload verification and certificate reading.
* **[`usbserial`](usbserial)**: Tests USB CDC-ACM (Virtual Serial Port) bidirectional data transmission using an echo loop.
---
## Guide for Writing New Tests
When writing new integration tests, you can choose between two main patterns depending on whether the test requires host-side interaction (like USB or JTAG) or is self-contained on the target.
### 1. Self-Contained Tests
Self-contained tests run entirely on the Ibex core and report their results via the UART console. The Bazel test runner (`opentitan_test` macro) monitors the console output to determine success or failure.
* **Success Criteria**: By default, the test runner looks for the string `PASS\n` in the console output. Ensure your firmware prints this (e.g., `pw_log::info!("✅ TEST PASS")` or similar) when all assertions succeed.
* **Failure Criteria**: By default, the test runner matches the regex `FAIL: .+\n` for failures. If an assertion fails or a panic occurs, ensure the output matches this pattern.
* **Customization**: You can override these defaults in the `opentitan_test` rule using `exit_success` and `exit_failure` attributes.
Example firmware structure:
```rust
#[entry]
fn entry() -> Result<(), Error> {
pw_log::info!("🔄 MY_TEST START");
let result = run_my_test_logic();
match result {
Ok(()) => {
pw_log::info!("PASS"); // Triggers Bazel test success
Ok(())
}
Err(e) => {
pw_log::error!("FAIL: {:?}", e); // Triggers Bazel test failure
Err(Error::Unknown)
}
}
}
```
### 2. Tests with Host-Based Test Harnesses
If your test involves external interfaces (e.g., verifying USB enumeration, DFU transfers, or JTAG operations), you must write a host-based test harness that runs on the host machine and coordinates with the target.
* **Harness Definition**: Define the host harness as a Rust binary using `opentitan_rust_binary` (which applies the necessary transition for `opentitanlib` compatibility) and link it via the `test_harness` attribute of `opentitan_test`.
* **Target Reset**: The host harness should start by resetting the target (e.g., `transport.reset(UartRx::Clear)?`) to ensure a clean boot and capture early boot logs.
* **Device Correlation (Best Practice)**: On systems with multiple connected devices, it is critical to ensure the host harness talks to the correct target.
* **Firmware**: Read the unique 256-bit Device ID from the Lifecycle Controller (`lc_ctrl`) and print it to the console (e.g., `Serial Number: <hex>`) early in the boot sequence. If using USB, also use this Device ID to populate the USB Serial Number string descriptor.
* **Harness**: Monitor the UART console first, capture the printed serial number using regex, and then use that serial number to look up and open the correct USB/JTAG device (e.g., `device_by_id_with_timeout(vid, pid, Some(serial), timeout)`).
* **Console Monitoring**: Use `UartConsole::wait_for` to synchronize host actions with target states (e.g., waiting for `🔄 RUNNING` before starting USB transfers).