Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,20 @@ test:virt_ast10x0 --run_under="//target/ast10x0/harness:qemu_runner \
# Mirrors k_common's test_tag_filters and adds -hardware (physical-board-only tests).
# Combined into one flag to avoid the Bazel 'expanded from multiple configs' warning
# that fires when --test_tag_filters is set by both k_common and this config.
test:virt_ast10x0 --test_tag_filters=-integration,-do_not_build,-do_not_run_test,-kernel_doc_test,-hardware
test:virt_ast10x0 --test_tag_filters=-integration,-do_not_build,-do_not_run_test,-kernel_doc_test,-hardware

# Stress test configs — same platform/build flags as the base configs but with
# --timeout 0 in --run_under so the runner blocks indefinitely until FAIL.
common:stress_virt_ast10x0 --platforms=//target/ast10x0
common:stress_virt_ast10x0 --//target/ast10x0:qemu=true
build:stress_virt_ast10x0 --build_tag_filters=-do_not_build,-kernel_doc_test
run:stress_virt_ast10x0 --run_under="//target/ast10x0/harness:qemu_runner \
--timeout 0 --cpu cortex-m4 --machine ast1030-evb --image "
test:stress_virt_ast10x0 --run_under="//target/ast10x0/harness:qemu_runner \
--timeout 0 --cpu cortex-m4 --machine ast1030-evb --image "
common:stress_k_ast1060_evb --platforms=//target/ast10x0
build:stress_k_ast1060_evb --build_tag_filters=-do_not_build,-kernel_doc_test
test:stress_k_ast1060_evb --local_test_jobs=1
test:stress_k_ast1060_evb --run_under="//target/ast10x0/harness:test_runner \
--timeout 0 "
test:stress_k_ast1060_evb --test_env=AST1060_EVB_PI_HOST
18 changes: 14 additions & 4 deletions target/ast10x0/harness/qemu_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@

PASS_SENTINEL = b"TEST_RESULT:PASS"
FAIL_SENTINEL = b"TEST_RESULT:FAIL"
TIMEOUT_SECONDS = 30


def _parse_args():
Expand All @@ -55,6 +54,12 @@ def _parse_args():
parser.add_argument(
"--qemu-args", nargs="*", help="Extra arguments to pass to qemu"
)
parser.add_argument(
"--timeout",
type=int,
default=30,
help="Seconds to wait for a test result sentinel (0 = no timeout, default: 30)",
)
return parser.parse_args()


Expand Down Expand Up @@ -138,7 +143,9 @@ def _main(args) -> None:
result = [None] # 0 = pass, 1 = fail, None = no sentinel found

with tempfile.NamedTemporaryFile() as f:
with subprocess.Popen(args=qemu_args, stdout=f) as proc:
with subprocess.Popen(
args=qemu_args, stdout=f, stdin=subprocess.DEVNULL
) as proc:
qemu_finished = threading.Event()
sentinel_thread = threading.Thread(
target=_sentinel_watcher,
Expand All @@ -154,11 +161,14 @@ def _main(args) -> None:
stdout_thread.start()

try:
proc.wait(timeout=TIMEOUT_SECONDS)
proc.wait(timeout=args.timeout if args.timeout > 0 else None)
except KeyboardInterrupt:
proc.kill()
proc.wait()
except subprocess.TimeoutExpired:
_LOG.error(
"Test timed out after %ds — no sentinel detected",
TIMEOUT_SECONDS,
args.timeout,
)
proc.kill()
proc.wait()
Expand Down
40 changes: 40 additions & 0 deletions target/ast10x0/harness/stress_image_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0
"""Bazel rule for stress test images.

Stress tests loop forever and only exit on TEST_RESULT:FAIL. This rule
produces a test target that relies on --run_under to supply the runner,
exactly like system_image_test. Use the stress_virt_ast10x0 or
stress_k_ast1060_evb configs which pass --no-timeout to the runner.
"""

load("@pigweed//pw_kernel/tooling:system_image.bzl", "SystemImageInfo")

def _stress_image_test_impl(ctx):
executable_symlink = ctx.actions.declare_file(ctx.label.name)
ctx.actions.symlink(
output = executable_symlink,
target_file = ctx.attr.image[SystemImageInfo].elf,
)

return [
DefaultInfo(
executable = executable_symlink,
runfiles = ctx.attr.image[DefaultInfo].default_runfiles,
),
]

stress_image_test = rule(
implementation = _stress_image_test_impl,
test = True,
attrs = {
"image": attr.label(
doc = "The system_image target to test.",
mandatory = True,
providers = [DefaultInfo, SystemImageInfo],
executable = True,
cfg = "target",
),
},
doc = "Defines a stress test for a system_image. Use with stress_virt_ast10x0 or stress_k_ast1060_evb config.",
)
4 changes: 4 additions & 0 deletions target/ast10x0/tests/stress/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

package(default_visibility = ["//visibility:public"])
37 changes: 37 additions & 0 deletions target/ast10x0/tests/stress/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# AST10x0 Stress Tests

Stress tests run forever and only exit on `TEST_RESULT:FAIL`. They are excluded
from CI and must be run manually using one of the configs below.

## Tests

| Target | What it stresses |
|---|---|
| `stress/mutex/kernel:mutex_stress_test` | Mutex contention across three kernel threads |
| `stress/ipc/user:ipc_stress_test` | IPC channel between initiator and handler processes |
| `stress/process_termination/user:process_termination_stress_test` | Repeated process termination and restart |

## Running on QEMU

```
# Open-ended soak — runs until Ctrl-C or FAIL, no timeout.
# Target the system_image directly, not the _stress_test rule.
bazel run --config=stress_virt_ast10x0 //target/ast10x0/tests/stress/mutex/kernel:mutex

# Timed run — Bazel kills after 3600s if no FAIL detected.
bazel test --config=stress_virt_ast10x0 //target/ast10x0/tests/stress/<test>:*_stress_test
```

Use `bazel run` on the `system_image` target (`:mutex`, `:ipc`, `:process_termination`)
for open-ended soak testing — this runs QEMU directly with no wrappers.
`bazel test` targets the `_stress_test` rule and is bounded by Bazel's 3600s ceiling.

## Running on hardware (EVB via Pi fixture)

```
AST1060_EVB_PI_HOST=<pi-hostname> bazel test --config=stress_k_ast1060_evb //target/ast10x0/tests/stress/<test>
```

`bazel run` is not supported for EVB — the Pi fixture is only wired into
`bazel test`. Tests run one at a time (`--local_test_jobs=1` is inherited from
`k_ast1060_evb`) and have no runner timeout.
79 changes: 79 additions & 0 deletions target/ast10x0/tests/stress/ipc/user/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

load("@pigweed//pw_kernel/tooling:system_image.bzl", "system_image")
load("@pigweed//pw_kernel/tooling:target_codegen.bzl", "target_codegen")
load("@pigweed//pw_kernel/tooling:target_linker_script.bzl", "target_linker_script")
load("@pigweed//pw_kernel/tooling/panic_detector:rust_binary_no_panics_test.bzl", "rust_binary_no_panics_test")
load("@rules_rust//rust:defs.bzl", "rust_binary")
load("//target/ast10x0:defs.bzl", "TARGET_COMPATIBLE_WITH")
load("//target/ast10x0/harness:stress_image_test.bzl", "stress_image_test")

system_image(
name = "ipc",
apps = [
"@pigweed//pw_kernel/tests/stress/ipc/user:initiator",
"@pigweed//pw_kernel/tests/stress/ipc/user:handler",
],
kernel = ":target",
platform = "//target/ast10x0",
system_config = ":system_config",
tags = ["kernel"],
target_compatible_with = TARGET_COMPATIBLE_WITH,
)

rust_binary_no_panics_test(
name = "no_panics_test",
binary = ":ipc",
tags = ["kernel"],
)

stress_image_test(
name = "ipc_stress_test",
timeout = "eternal",
image = ":ipc",
tags = [
"do_not_run_test",
"kernel",
],
target_compatible_with = TARGET_COMPATIBLE_WITH,
)

filegroup(
name = "system_config",
srcs = ["system.json5"],
)

target_codegen(
name = "codegen",
arch = "@pigweed//pw_kernel/arch/arm_cortex_m:arch_arm_cortex_m",
system_config = ":system_config",
target_compatible_with = TARGET_COMPATIBLE_WITH,
)

target_linker_script(
name = "linker_script",
system_config = ":system_config",
tags = ["kernel"],
target_compatible_with = TARGET_COMPATIBLE_WITH,
template = "//target/ast10x0:linker_script_template",
)

rust_binary(
name = "target",
srcs = ["target.rs"],
edition = "2024",
tags = ["kernel"],
target_compatible_with = TARGET_COMPATIBLE_WITH,
deps = [
":codegen",
":linker_script",
"//target/ast10x0:entry",
"@pigweed//pw_kernel/arch/arm_cortex_m:arch_arm_cortex_m",
"@pigweed//pw_kernel/kernel",
"@pigweed//pw_kernel/subsys/console:console_backend",
"@pigweed//pw_kernel/target:target_common",
"@pigweed//pw_kernel/userspace",
"@pigweed//pw_log/rust:pw_log",
],
)
75 changes: 75 additions & 0 deletions target/ast10x0/tests/stress/ipc/user/system.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

// AST10x0 IPC Stress Test Configuration
// Two separate apps: initiator sends characters over IPC, handler uppercases
// and echoes them back. Both loop indefinitely.
//
// Memory layout:
// 0x00000000 - 0x00000500: Vector table (1280 bytes)
// 0x00000500 - 0x00020000: Kernel code (~126KB, ends at 128KB boundary)
// 0x00020000 - 0x00040000: initiator app flash (128KB)
// 0x00040000 - 0x00060000: handler app flash (128KB)
// 0x00060000 - 0x00080000: Kernel RAM (128KB)
// 0x00080000 - 0x000A0000: App RAM (2 x 64KB)
{
arch: {
type: "armv7m",
vector_table_start_address: 0x00000000,
vector_table_size_bytes: 1280, // 0x500
},
kernel: {
flash_start_address: 0x00000500,
flash_size_bytes: 129792, // ~126KB (ends at 0x00020000)
ram_start_address: 0x00060000,
ram_size_bytes: 131072, // 128KB
},
apps: [
{
name: "initiator",
flash_size_bytes: 131072, // 128KB
processes: [
{
name: "initiator_process",
ram_size_bytes: 65536, // 64KB
objects: [
{
name: "ipc",
type: "channel_initiator",
handler_process: "handler_process",
handler_object_name: "ipc",
},
],
threads: [
{
name: "initiator_thread",
kernel_stack_size_bytes: 2048,
},
],
},
],
},
{
name: "handler",
flash_size_bytes: 131072, // 128KB
processes: [
{
name: "handler_process",
ram_size_bytes: 65536, // 64KB
objects: [
{
name: "ipc",
type: "channel_handler",
},
],
threads: [
{
name: "handler_thread",
kernel_stack_size_bytes: 2048,
},
],
},
],
},
],
}
31 changes: 31 additions & 0 deletions target/ast10x0/tests/stress/ipc/user/target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

#![no_std]
#![no_main]

use console_backend::console_backend_write_all;
use entry as _;
use target_common::{TargetInterface, declare_target};

pub struct Target {}

impl TargetInterface for Target {
const NAME: &'static str = "AST10x0 IPC Stress";

fn main() -> ! {
codegen::start();
#[expect(clippy::empty_loop)]
loop {}
}

fn shutdown(code: u32) -> ! {
if code != 0 {
let _ = console_backend_write_all(b"TEST_RESULT:FAIL\n");
}
#[expect(clippy::empty_loop)]
loop {}
}
}

declare_target!(Target);
Loading