feat(xteink): support direct emulator workflow
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/GNUmakefile
|
||||
/build/
|
||||
/build-native/
|
||||
/dist/
|
||||
/subprojects/.wraplock
|
||||
/.cache/
|
||||
/.vscode/
|
||||
|
||||
@@ -26,12 +26,16 @@ quiet-command-run = $(if $(V),,$(if $2,printf " %-7s %s\n" $2 $3 && ))$1
|
||||
quiet-@ = $(if $(V),,@)
|
||||
quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3)
|
||||
|
||||
UNCHECKED_GOALS := TAGS gtags cscope ctags dist \
|
||||
UNCHECKED_GOALS := TAGS gtags cscope ctags dist xteink \
|
||||
help check-help print-% \
|
||||
docker docker-% lcitool-refresh vm-help vm-test vm-build-%
|
||||
|
||||
all:
|
||||
.PHONY: all clean distclean recurse-all dist msi FORCE
|
||||
.PHONY: all clean distclean recurse-all dist msi xteink FORCE
|
||||
|
||||
xteink:
|
||||
@mkdir -p dist
|
||||
nix build .#qemu-native-xteink --builders '' --out-link dist/qemu-native
|
||||
|
||||
# Don't try to regenerate Makefile or configure
|
||||
# We don't generate any of them
|
||||
|
||||
+3
-2
@@ -26,10 +26,11 @@ nix develop
|
||||
ninja -C build qemu-system-riscv32
|
||||
```
|
||||
|
||||
Or build the native xteink target directly:
|
||||
Or build the native xteink target into `dist/qemu-native` for direct script use:
|
||||
|
||||
```sh
|
||||
nix build .#qemu-native-xteink
|
||||
make xteink
|
||||
uv run --script scripts/xteink-emu.py --help
|
||||
```
|
||||
|
||||
### Agent emulator
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
export XTEINK_QEMU=${qemu-native-xteink}/bin/qemu-system-riscv32
|
||||
export XTEINK_BIOS=${qemu-native-xteink}/share/qemu
|
||||
export XTEINK_BOOTLOADER=${./scripts/assets/esp32c3-bootloader.bin}
|
||||
export XTEINK_PARTITIONS=${./scripts/assets/crosspoint-partitions.bin}
|
||||
export XTEINK_PARTITIONS=${./scripts/assets/xteink-partitions.bin}
|
||||
exec uv run --script ${./scripts/xteink-emu.py} "$@"
|
||||
'';
|
||||
};
|
||||
|
||||
+30
-4
@@ -20,6 +20,13 @@ FLASH_SIZE = 16 * 1024 * 1024
|
||||
APP_OFFSET = 0x10000
|
||||
PARTITIONS_OFFSET = 0x8000
|
||||
RELEASED_ADC = 4095
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
||||
DEFAULT_PATHS = {
|
||||
"XTEINK_QEMU": PROJECT_ROOT / "dist/qemu-native/bin/qemu-system-riscv32",
|
||||
"XTEINK_BIOS": PROJECT_ROOT / "dist/qemu-native/share/qemu",
|
||||
"XTEINK_BOOTLOADER": PROJECT_ROOT / "scripts/assets/esp32c3-bootloader.bin",
|
||||
"XTEINK_PARTITIONS": PROJECT_ROOT / "scripts/assets/xteink-partitions.bin",
|
||||
}
|
||||
BUTTONS = {
|
||||
"left": ("/machine/adc", "adci[2]", 2242, RELEASED_ADC),
|
||||
"right": ("/machine/adc", "adci[2]", 5, RELEASED_ADC),
|
||||
@@ -40,10 +47,10 @@ def state_dir(args):
|
||||
|
||||
|
||||
def require_env(name):
|
||||
value = os.environ.get(name)
|
||||
if not value:
|
||||
raise RuntimeError(f"{name} is not set; run xteink-emu through its Nix app")
|
||||
return Path(value)
|
||||
path = Path(os.environ.get(name, DEFAULT_PATHS[name]))
|
||||
if not path.exists():
|
||||
raise RuntimeError(f"{path} does not exist; run 'make xteink'")
|
||||
return path
|
||||
|
||||
|
||||
def pid_from(state):
|
||||
@@ -174,6 +181,13 @@ def start(args):
|
||||
raise RuntimeError(f"QEMU exited during startup\n{message}")
|
||||
try:
|
||||
qmp_command(state, "query-status")
|
||||
if args.power_hold_ms:
|
||||
path, prop, pressed, released = BUTTONS["power"]
|
||||
qmp_command(state, "qom-set", {"path": path, "property": prop, "value": pressed})
|
||||
try:
|
||||
time.sleep(args.power_hold_ms / 1000)
|
||||
finally:
|
||||
qmp_command(state, "qom-set", {"path": path, "property": prop, "value": released})
|
||||
print(f"started PID {process.pid}")
|
||||
print(f"state: {state}")
|
||||
print(f"serial log: {state / 'serial.log'}")
|
||||
@@ -238,6 +252,12 @@ def button(args):
|
||||
state = state_dir(args)
|
||||
require_running(state)
|
||||
path, prop, pressed, released = BUTTONS[args.name]
|
||||
if args.down:
|
||||
qmp_command(state, "qom-set", {"path": path, "property": prop, "value": pressed})
|
||||
return
|
||||
if args.up:
|
||||
qmp_command(state, "qom-set", {"path": path, "property": prop, "value": released})
|
||||
return
|
||||
qmp_command(state, "qom-set", {"path": path, "property": prop, "value": pressed})
|
||||
try:
|
||||
time.sleep(args.hold_ms / 1000)
|
||||
@@ -265,6 +285,7 @@ def parser():
|
||||
add_state(command)
|
||||
command.add_argument("--firmware", required=True)
|
||||
command.add_argument("--sdcard", required=True)
|
||||
command.add_argument("--power-hold-ms", type=int, default=2000)
|
||||
command.set_defaults(handler=start)
|
||||
|
||||
command = commands.add_parser("stop")
|
||||
@@ -281,6 +302,9 @@ def parser():
|
||||
add_state(command)
|
||||
command.add_argument("name", choices=BUTTONS)
|
||||
command.add_argument("--hold-ms", type=int, default=100)
|
||||
button_mode = command.add_mutually_exclusive_group()
|
||||
button_mode.add_argument("--down", action="store_true", help="press and hold")
|
||||
button_mode.add_argument("--up", action="store_true", help="release")
|
||||
command.set_defaults(handler=button)
|
||||
|
||||
command = commands.add_parser("screenshot")
|
||||
@@ -296,6 +320,8 @@ def main():
|
||||
raise RuntimeError("--timeout must not be negative")
|
||||
if getattr(args, "hold_ms", 0) < 0:
|
||||
raise RuntimeError("--hold-ms must not be negative")
|
||||
if getattr(args, "power_hold_ms", 0) < 0:
|
||||
raise RuntimeError("--power-hold-ms must not be negative")
|
||||
args.handler(args)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user